Skip to content
Matt Bierner edited this page Nov 9, 2015 · 32 revisions

Execution

run(g, ud, random = Math.random)

g.run(ud, random = Math.random)

Run a generator to completion, combining results into a string.

  • g - Generator to run.
  • ud - Optional, user data.
  • r - Optional, custom random number generator.
const p = pep.seq('a', pep.choice('b', 'c'));

p.run() === 'ac';
p.run() === 'ac';
p.run() === 'ab';

fold(f, z, g, ud, random = Math.random)

g.fold(f, z, ud, random = Math.random)

Left fold over a generator.

  • f - Accumulation function, passed accumulated value and current value.
  • z - Initial value.
  • g - Generator to run.
  • ud - Optional, user data.
  • r - Optional, custom random number generator.
const p = pep.seq('a', 'b', 'c');

pep.fold((p, c) => p.concat(c), [], p) === ['a', 'b', 'c'];

begin(g, ud, random = Math.random)

g.begin(ud, random = Math.random)

Begin the execution of a generator. Returns a Javascript iterator.

  • g - Generator to run.
  • ud - Optional, user data.
  • r - Optional, custom random number generator.

Values are lazily produced. Javascript iterators are stateful, so you can only iterate over the result of begin once.

const p = pep.seq('a', 'b', 'c');
for (const x of p.begin())
    console.log(x);

Declaration

declare(self => generator)

Declare a generator for self reference or late bindings. This has a number of interesting applications.

Anonymous self reference:

// Yield 'a' followed by one or more 'b's.
// In practice, use `many1` for this.
const p = pep.seq('a', pep.declare(self => pep.seq('b', pep.opt(self))));

Use of generators before they are declared:

// this expands to `pep.seq(undefined, self)`
// since `m` is not defined yet.
let ms = pep.seq(m, self);

// But we fix this with `declare`.
let ms = pep.declare(() =>
    pep.seq(m, self));
   
// And then define `m` later
const m = pep.lit('m');

Forward declaration:

// Declare that the some generator `ms` will exist.
let ms = pep.declare(() => ms);

// Use `ms` in any expression.
const p = pep.seq('a', ms);

...

// Actually define `ms` sometime later.
ms = pep.str('abc');

Also can be used to introduce simple, scoped state:

const counter = pep.declare(() => {
    // declare some variables local to this block.
    let sum = 0;

    return pep.seq(
        pep.seq(pep.lit(1), pep.lit(2), pep.lit(3))
            .map(x => {
                // Update the state in an expression.
                sum += i;
                return x;
            }),
        // and use the state sometime later.
        // Declare is used to make sure the current value of `i` is
        // always returned.
        pep.declare(() => pep.lit(i)));
});

Value Generators

lit(x)

Yields x.

pep.lit('abc').run() === 'abc';
pep.lit(5).begin().next().value === 5;

Use lit to force a generator to yield a non string value.

// Normally, `seq` will attempt to convert `myObj` to a string.
Array.from(pep.seq('a', myObj, 'c')) === ['a', '[Object object]', 'c'];

// But `lit`forces the object to be treated as a literal value.
Array.from(pep.seq('a', pep.lit(myObj), 'c')) === ['a', myObj, 'c'];

str(x)

Yields x as a string. x is converted to a string when str is first called.

pep.str('abc').run() === 'abc';
pep.str(5).begin().next().value === '5';
pep.str({}).begin().next().value === '[Object object]';

Note that pep.str('') or pep.str() is different from pep.empty. The former yield the value '' while the latter does not yield any values.

empty

Generator that does not yield any values. This can be used with choice to add the possibility that nothing will be output.

const p = pep.seq('a', pep.empty, 'b');
Array.from(p.begin()) === ['a', 'b'];

For most cases, empty is the preferred way to indicate that no value is yielded, rather than pep.str('').

Basic Combinators

seq(...generators)

g.seq(...generators)

Run a sequence of generators from left to right.

const p = pep.seq(pep.str('a'), pep.str('bc'), pep.str('c'));
Array.from(p.begin()) === ['a', 'bc', 'd'];

Any non generator arguments are wrapped in pep.str:

pep.seq('a', pep.choice(...), 3) === pep.seq(pep.str('a'), pep.choice(...), pep.str(3))

map(g, f)

g.map(f)

Map function f over each element produced by p.

const p = pep.seq('a', 'b', 'c').map(x => x + x)

p.run() === 'aabbcc'
Array.from(p.begin()) === ['aa', 'bb', 'cc']

Note that f is run on every value produced by g, not on the entire result of g. If you need to run on the entire result of g, use join or combine to merge all the results into a single value first.

const p = pep.join(pep.seq('a', 'b', 'c')).map(x => x + x)

p.run() === 'abcabc'
Array.from(p.begin()) === ['abcabc']

chain(g, f)

g.chain(f)

For each element produced by p, run function f and then run the generator that f returns.

  • g - Generator to run.
  • f - Function that maps values to generators
const p = pep.chain(pep.seq('a', 'b', 'c'), x => pep.seq(x, x));

p.run() === 'aabbcc';
Array.from(p.begin()) === ['a', 'a', 'b', 'b', 'c', 'c']

Note how the yielded values in this case differ from map.

Like with map, if you need to run on the entire result of g, use join or combine to merge all the results into a single value first.

combine(f, z, ...generators)

g.combine(f, z, ...generators)

Left fold over the results of generators, combining the results with f. Yields only a single value.

  • g - Generator to run.
  • f - Function that maps values to generators
  • z - Initial value.
const p = pep.combine(
    (p, c) => [p, c],
    'x',
    pep.seq('a', pep.seq('b', 'c'), 'd'));

Array.from(p.begin()) === [[[[['x', 'a'], 'b'], 'c'], 'd']]

join(...generators)

g.join(...generators)

Combining all results of generators into a single string value.

const p = pep.join('a', pep.seq('b', 'c'), 'd');

Array.from(p.begin()) === ['abcd']

Choice

choice(...generators)

Randomly choose from along one or more generators. Each element has the same weight.

const p = pep.choice('a', 'b', c);

p.run() === 'c';
p.run() === 'a';
p.run() === 'c';
p.run() === 'b';

Use empty to indicate the generator may choose to produce no value.

weightedChoice(weightMap)

Randomly choose from along one or more generators, each with its own relative weight.

// Produce 'b' 70% of the time, 'a' 10% and 'c' 20%.
const p = weightedChoice([
    [0.1, 'a'],
    [0.7, 'b'],
    [0.2, 'c']);

p.run() === 'b';
p.run() === 'b';
p.run() === 'c';
p.run() === 'b';

Use empty to indicate the generator may choose to produce no value.

noop(...generators)

Consumes all yielded values from generators without yielding any values itself. Useful for updating states.

opt(g, prob = 0.5)

Mark a value as optional.

  • g - Generator.
  • prob - value between [0, 1] that g will be run. 1 means that g is run infinity, while 0 means that g is never run.
const p = pep.opt('a');

p.run() === '';
p.run() === 'a';
p.run() === 'a';

Iteration

many(g, prob = 0.5)

Run g zero or more times.

  • g - Generator.
  • prob - Number between [0, 1] that g will be run at each step. 1 means that g is run infinity, while 0 means that g is never run.
const p = pep.many(pep.seq('a', 'b'));

p.run() === 'abab';
p.run() === '';
p.run() === 'ab';

Array.from(p.begin()) === ['a', 'b', 'a', 'b', 'a', 'b'];

many1(g, prob = 0.5)

Run g one or more times. Same behavior as many expect prob only applies after the first value is produced.

const p = pep.many1(pep.seq('a', 'b'));

p.run() === 'abab';
p.run() === 'ab';
p.run() === 'abab';

Array.from(p.begin()) === ['a', 'b', 'a', 'b', 'a', 'b'];

State Interaction

get(name, def ='')

Lookup a stored variable.

  • name - Key of the var.
  • def - Value returned if the variable does not exist.

set(name, value)

Set the value of a stored variable. Does not yield any value.

  • name - Key of the var.
  • value - New Value to store.
const p = pep.seq('a', pep.set('x', 10), 'b', pep.get('x'), 'c');

p.run() === 'ab10c'

modify(name, f)

Update the value of a stored variable. Does not yield any value.

  • name - Key of the var.
  • f - Function mapping old value to new value. Called with undefined if value was previously not set.

getUd

Yield the current user data object.

const p = pep.seq('a', pep.getUd, 'c');

p.run('b') === 'abc';
p.run(123) === 'a123c';

setUd(ud)

Set the value of the user data. Does not yield any value.

modifyUd(f)

Modify the value of the user data object. Does not yield any value.