// 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"