92 lines
3.8 KiB
JavaScript
92 lines
3.8 KiB
JavaScript
{
|
|
/*
|
|
|
|
This is a per-parser initialization block. It runs whenever the parser is invoked. Any variables declared here are
|
|
available in any javascript blocks in the rest of the grammar.
|
|
|
|
In addition to the parser generated by peggy, we allow for certain parts of the process to be delegated to client
|
|
code. A class implementing this 'parser' API may be passed-in here as an option when the peggy parser is invoked,
|
|
otherwise we use the one configured at CONFIG.Dice.parser.
|
|
|
|
*/
|
|
|
|
const Parser = options.parser ?? CONFIG.Dice.parser;
|
|
const parser = new Parser(input);
|
|
}
|
|
|
|
Expression = _ leading:(_ @Additive)* _ head:Term tail:(_ @Operators _ @Term)* _ {
|
|
|
|
/*
|
|
|
|
The grammar rules are matched in order of precedence starting at the top of the file, so the rules that match the
|
|
largest portions of a string should generally go at the top, with matches for smaller substrings going at the bottom.
|
|
|
|
Here we have a rule that matches the overall roll formula. If a given formula does not match this rule, it means that
|
|
it is an invalid formula and will throw a parsing error.
|
|
|
|
Prefixing a pattern with 'foo:' is a way to give a name to the sub-match in the associated javascript code. We use
|
|
this fairly heavily since we want to forward these sub-matches onto the 'parser'. We can think of them like named
|
|
capture groups.
|
|
|
|
Prefixing a pattern with '@' is called 'plucking', and is used to identify sub-matches that should be assigned to the
|
|
overall capture name (like 'foo:'), ignoring any that are not 'plucked'.
|
|
|
|
For example 'tail:(_ @Operators _ @Term)*' matches operators followed by terms, with any amount of whitespace
|
|
in-between, however only the operator and term matches are assigned to the 'tail' variable, the whitespace is ignored.
|
|
|
|
The 'head:A tail:(Delimiter B)*' pattern is a way of representing a string of things separated by a delimiter,
|
|
like 'A + B + C' for formulas, or 'A, B, C' for Pool and Math terms.
|
|
|
|
In each of these cases we follow the same pattern: We match a term, then we call 'parser.on*', and that method is
|
|
responsible for taking the raw matched sub-strings and returning a 'parse node', i.e. a plain javascript object that
|
|
contains all the information we need to instantiate a real `RollTerm`.
|
|
|
|
*/
|
|
|
|
return parser._onExpression(head, tail, leading, text(), error);
|
|
}
|
|
|
|
Term = FunctionTerm / DiceTerm / NumericTerm / PoolTerm / Parenthetical / StringTerm
|
|
|
|
FunctionTerm = fn:FunctionName "(" _ head:Expression? _ tail:(_ "," _ @Expression)* _ ")" flavor:Flavor? {
|
|
return parser._onFunctionTerm(fn, head, tail, flavor, text());
|
|
}
|
|
|
|
DiceTerm = number:(Parenthetical / Constant)? [dD] faces:([a-z]i / Parenthetical / Constant) modifiers:Modifiers? flavor:Flavor? {
|
|
return parser._onDiceTerm(number, faces, modifiers, flavor, text());
|
|
}
|
|
|
|
NumericTerm = number:Constant flavor:Flavor? !StringTerm { return parser._onNumericTerm(number, flavor); }
|
|
|
|
PoolTerm = "{" _ head:Expression tail:("," _ @Expression)* "}" modifiers:Modifiers? flavor:Flavor? {
|
|
return parser._onPoolTerm(head, tail, modifiers, flavor, text());
|
|
}
|
|
|
|
Parenthetical = "(" _ term:Expression _ ")" flavor:Flavor? { return parser._onParenthetical(term, flavor, text()); }
|
|
|
|
StringTerm = term:(ReplacedData / PlainString) flavor:Flavor? { return parser._onStringTerm(term, flavor); }
|
|
|
|
ReplacedData = $("$" $[^$]+ "$")
|
|
|
|
PlainString = $[^ (){}[\]$,+\-*%/]+
|
|
|
|
FunctionName = $([a-z$_]i [a-z$_0-9]i*)
|
|
|
|
Modifiers = $([^ (){}[\]$+\-*/,]+)
|
|
|
|
Constant = _ [0-9]+ ("." [0-9]+)? { return Number(text()); }
|
|
|
|
Operators = MultiplicativeFirst / AdditiveOnly
|
|
|
|
MultiplicativeFirst = head:Multiplicative tail:(_ @Additive)* { return [head, ...tail]; }
|
|
|
|
AdditiveOnly = head:Additive tail:(_ @Additive)* { return [null, head, ...tail]; }
|
|
|
|
Multiplicative = "*" / "/" / "%"
|
|
|
|
Additive = "+" / "-"
|
|
|
|
Flavor = "[" @$[^[\]]+ "]"
|
|
|
|
_ "whitespace" = [ ]*
|