Initial
This commit is contained in:
91
resources/app/client-esm/dice/grammar.pegjs
Normal file
91
resources/app/client-esm/dice/grammar.pegjs
Normal file
@@ -0,0 +1,91 @@
|
||||
{
|
||||
/*
|
||||
|
||||
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" = [ ]*
|
||||
Reference in New Issue
Block a user