-
Notifications
You must be signed in to change notification settings - Fork 5
Wrapping
The builtin Apep combinators all automatically convert non-generator arguments into generators values. This allows for more expressive grammars.
// Same behavior as in the README
const tvHackerQuip = pep.declare(() =>
pep.seq(exlaimation, ' ',
pep.choice(
['The ', subject, " is ", somethingBad, "!"],
[evilDoer, ' ', doingSomthingBad, " the ", target, "!"])));
Note how ' '
and other string literals are not generators. Also, Javascript arrays are used as shorthand for pep.seq
.
When evaluating an argument, Apep uses the following mapping:
- Generators are passed through unmodified. You can yield a generator itself with
pep.lit(generator)
or force one to be converted to a string withpep.str(generator)
. - Arrays are converted to
pep.seq(...)
. -
undefined
triggers an exception. This is almost always a sign that something is wrong with your grammar. If you want to yield anundefined
value, usepep.lit(undefined)
, orpep.str(undefined)
if you want the string representation ofundefined
. - Any remaining values are passed to
pep.str(...)
. This includes numbers, strings, objects, booleans, null, functions, and every other class of Javascript object except forundefined
.
All builtin Apep generators wrap all of their generator arguments. A few key points however:
-
Returned values are NOT wrapped.
pep.chain
for example does not attempt to wrap the result of the mapping function, and neither doespep.declare
. You must always explicitly return a generator in these cases. -
Method chaining does not work on unwrapped values.
pep.seq(a, b)
will always wrap botha
andb
, whilea.seq(b)
will fail ifa
is not a real generator.
It is safe to use autowrapping if no one will be able to tell the difference. Temporary generators that exist only as arguments to other generators are one example of this.
const tvHackerQuip = pep.declare(() =>
pep.seq(exlaimation, ' ',
pep.choice(
['The ', subject, " is ", somethingBad, "!"],
[evilDoer, ' ', doingSomthingBad, " the ", target, "!"])));
The argument values cannot be accessed after they are passed, so it is safe to use ' '
and the other string literals here, along with the arrays to express pep.seq
. No external code can ever tell the difference.
ALWAYS use a true generator if external code can access the variable.
// Never do this.
const action = 'hop'
const person = pep.choice('pop');
const title = [action, 'on', person];
This only works if action
and title
are used in specific ways:
pep.chain(title, x => ...) // ok
title.chain(x => ...) // CRASH
pep.map(title, x => ...) // ok
title.map(x => ...) // Not what you expect, calls `Array.prototype.map` instead of `pep.map`
Array.from(pep.begin(title))) // OK
Array.from(title) // Not what you expect
When writing a combinator, follow these rules to make sure it works correctly with both generators and values that can be converted to generators:
- ALWAYS use
pep.FN(g1, ...)
inside of the combinator instead ofg1.FN(...)
. Ifg1
is an unwrapped value,g1.FN
will not exist. - NEVER try to inspect the generator values passed into the combinator to tell if it is a generators or a value. You will get it wrong. Just assume they are good and forward them to Apep.