-
Notifications
You must be signed in to change notification settings - Fork 5
- Execution - Running generators.
- Declaration - Declaring a generator for forward and self reference.
- Value Generators - Basic generators that produce a value.
- Basic Combinators - Primitive sequencing combinators.
- Choice Combinators - Randomly selecting a generator to run.
- Iteration Combinators - Running a generator multiple times.
- State Interaction Generators - Generators that interact with state.
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';
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 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);
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)));
});
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'];
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.
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('')
.
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 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']
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.
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']]
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']
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.
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.
Consumes all yielded values from generators
without yielding any values itself. Useful for updating states.
Mark a value as optional.
-
g
- Generator. -
prob
- value between [0, 1] thatg
will be run. 1 means thatg
is run infinity, while 0 means thatg
is never run.
const p = pep.opt('a');
p.run() === '';
p.run() === 'a';
p.run() === 'a';
Run g
zero or more times.
-
g
- Generator. -
prob
- Number between [0, 1] thatg
will be run at each step. 1 means thatg
is run infinity, while 0 means thatg
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'];
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'];
Lookup a stored variable.
-
name
- Key of the var. -
def
- Value returned if the variable does not exist.
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'
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.
Yield the current user data object.
const p = pep.seq('a', pep.getUd, 'c');
p.run('b') === 'abc';
p.run(123) === 'a123c';
Set the value of the user data. Does not yield any value.
Modify the value of the user data object. Does not yield any value.