48 lines
2.3 KiB
Plaintext
48 lines
2.3 KiB
Plaintext
// first define our whitespace chars:
|
|
ignore:ws = " " | [\t] | [\r] | [\n];
|
|
|
|
// define the format of input numbers. With the :nows flag we declare that we don't expect ignored spaces
|
|
// between the digits and the delimiters. We support integers, floating point numbers, and floating point
|
|
// numbers with their exponential notation. We don't support arbitrary leading zeros to avoid confusion with the
|
|
// octal representation of numbers, which is not supported here.
|
|
num:nows = "-"? ("0" | [1-9][0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?;
|
|
|
|
// define the supported operators:
|
|
add = "+";
|
|
sub = "-";
|
|
mul = "*";
|
|
div = "/";
|
|
|
|
// let's define grouping. Any expression can be grouped. The definition of the expression can be found further
|
|
// down in the syntax document. This usage of the expression reference is also a good example for recursive
|
|
// definitions. Using the :alias flag prevents generating a separate node in the resulting AST.
|
|
group:alias = "(" expression ")";
|
|
|
|
// we group the operators by precedence. This is necessary to parse the expressions like a * b + c in a structure
|
|
// that is equivalent to (a * b) + c.
|
|
op0:alias = mul | div;
|
|
op1:alias = add | sub;
|
|
|
|
// we also define which operands can be used at which precedence level. Notice, how operand1 also allows binary0
|
|
// expressions.
|
|
operand0:alias = num | group;
|
|
operand1:alias = operand0 | binary0;
|
|
|
|
// using the prioritized operators, we can define the prioritized binary expressions. We support a + b + c, and
|
|
// not only a + b.
|
|
binary0 = operand0 (op0 operand0)+;
|
|
binary1 = operand1 (op1 operand1)+;
|
|
binary:alias = binary0 | binary1;
|
|
|
|
// let's define, what an expression can be. Notice the recursion along expression and group.
|
|
expression:alias = num | group | binary;
|
|
|
|
// finally, define the root of the parser, the result of the arithmetic expression. It can be any expression,
|
|
// but since we used the :alias flag for the expression definition, we need to add a non-alias parser that will
|
|
// represent the root of the resulting AST. This also allows us to define an "exit" token, which can be used
|
|
// exit from the REPL loop of our application.
|
|
//
|
|
// Note that we don't need to use the :root flag here, because it is our last definition, and this means that
|
|
// the expression is the root parser of the syntax.
|
|
result = expression | "exit"
|