The parser converts a sequence of tokens into an abstract syntax tree (AST). Type definitions for the AST are found in ast.ts. See also the MJr language grammar.
The parser is hand-written, to avoid a separate stage of converting a parse tree into an AST, and hopefully to give more helpful syntax error messages. It is a top-down recursive descent parser without backtracking (also known as a predictive parser), with a single-token lookahead; expressions with unary and binary operators are parsed by an operator precedence parser.
For AST nodes with “arguments” (name/value pairs in braces), the parseArgs
method is used to avoid repetition of code. The constant ARGS
specifies the argument names for nodes which have arguments, and maps each argument name to a boolean indicating whether that argument is required.
The following table shows the precedences of operators, plus some other syntactic constructs which are similar to operators and which occur at the boundaries of some expressions.
Operator | Description | Associativity |
---|---|---|
( …) , [ …] , { …} |
Highest precedence | — |
x.attr |
Not an operator; x must be a simple name, keyword name or attribute access expression |
Left |
load , randint , sum |
Function expressions | — |
+x , -x |
Unary positive, unary negative | — |
* , / , // , % |
Multiplication, division, floor-division, modulo | Left |
+ , - |
Addition and subtraction | Left |
== , != , < , <= , > , >= |
Comparisons | Neither |
not |
Logical negation | — |
and |
Logical conjunction | Left |
or |
Logical disjunction | Left |
-> |
Not an operator; delimits patterns in rules | — |
… if … else … |
Conditional expression | Right |
= |
Not an operator | — |
, |
Not an operator; delimits name/value pairs | — |
Declaration in … |
Lowest precedence. Not an operator; may only occur in bracketed expressions | Right |
The comparison operators are neither left- nor right-associative. This is intended to catch mistakes like a < b < c
in the parser, so that a specific syntax error message can be emitted instead of a less helpful type error. Additionally, by not allowing otherwise-valid expressions like a == b == c
(where c
is a bool
), this design decision also leaves open the possibility to later add chained comparisons to the language without breaking existing programs.