(*
the indentation level is only recorded for the first token on each line,
for example:
if true:
print "test"
would produce the token stream:
Token { kind: If, indent: Some(0), lexeme: "if" }
Token { kind: True, indent: None, lexeme: "true" }
Token { kind: Colon, indent: None, lexeme: ":" }
Token { kind: Print, indent: Some(2), lexeme: "print" }
Token { kind: String, indent: None, lexeme: "\"test\"" }
the parser maintains a stack of indentation levels,
and supports the following operations on that stack:
{+} -> expects indentation == Some(indent_stack[0] + N), and pushes N onto the indent_stack
{-} -> expects indentation == Some(indent_stack[1]), and pops the top of the indent_stack
{=} -> expects indentation == Some(indent_stack[0])
{0} -> expects indentation == Some(0)
{_} -> expects indentation == None
in certain contexts (such as a grouping expression, or call args), the indentation
is ignored, meaning that any indentation checks always pass, and the indentation levels
don't change.
*)
module = ({0} top_level_stmt)* ;
top_level_stmt = {=} stmt ;
stmt = scoped_stmt | simple_stmt ;
simple_stmt =
| pass_stmt
| return_stmt
| continue_stmt
| break_stmt
| yield_stmt
| print_stmt
| assign_stmt
;
scoped_stmt =
| import_stmt
| if_stmt
| for_stmt
| while_stmt
| loop_stmt
| fn_stmt
| class_stmt
;
pass_stmt = "pass" ;
return_stmt = "return" ({_} expr)? ;
continue_stmt = "continue" ;
break_stmt = "break" ;
yield_stmt = "yield" ({_} expr)? ;
print_stmt = "print" {_} expr ({_} "," {_} expr)? ;
assign_stmt = assign_target {_} assign_op {_} expr ;
assign_target =
| var_expr
| field_expr
| index_expr
;
assign_op =
| ":="
| "="
| "+="
| "-="
| "*="
| "/="
| "%="
| "**="
| "??="
;
import_stmt =
| "import" {_} import_path ({_} "as" identifier)?
| "from" {_} import_path {_} "import" {_} import_symbol_list
;
import_path = identifier ({_} "." {_} identifier)* ;
import_symbol_list = indentifier ({_} "," {_} identifier)* ;
if_stmt =
"if" {_} expr {_} ":" block
({=} "elif" {_} expr {_} ":" block)*
({=} "else" ":" block)?
;
for_stmt = "for" {_} identifier {_} "in" {_} for_iter {_} ":" block ;
for_iter =
| expr {_} ".." {_} expr (* range *)
| expr (* iterable *)
;
while_stmt = "while" {_} expr {_} ":" block ;
loop_stmt = "loop" {_} ":" block ;
fn_stmt = "fn" {_} identifier {_} "(" (param ("," param)*)? ")" {_} ":" block ;
param = identifier ({_} "=" {_} expr)? ;
class_stmt = "class" {_} identifier ({_} "(" identifier ")") {_} ":" class_members ;
class_members =
| {_} pass_stmt
| {+} class_member_list {-}
;
class_member_list =
(class_field ({=} class_field)*)?
(class_method ({=} class_method)*)?
;
class_field = identifier "=" expr ;
class_method = fn_stmt ;
block =
| {_} simple_stmt
| {+} stmt ({=} stmt)*
;
expr = maybe_expr ;
maybe_expr = or_expr ({_} "??" {_} or_expr)* ;
or_expr = and_expr ({_} "||" {_} and_expr)* ;
and_expr = eq_expr ({_} "&&" {_} eq_expr)* ;
eq_expr = comp_expr ({_} ("==" | "!=") {_} comp_expr)* ;
comp_expr = add_expr ({_} ("<" | "<=" | ">" | ">=") {_} add_expr)* ;
add_expr = mul_expr ({_} ("+" | "-") {_} mul_expr)* ;
mul_expr = pow_expr ({_} ("*" | "/" | "%") {_} pow_expr)* ;
pow_expr = unary_expr ({_} "**" {_} unary_expr)* ;
unary_expr = ("-" | "+" | "!" | "?") {_} (unary_expr | postfix_expr) ;
postfix_expr = call_expr | index_expr | field_expr | primary_expr ;
call_expr = postfix_expr {_} "(" (expr ("," expr)*)? ")" ;
index_expr = postfix_expr {_} "[" expr "]" ;
field_expr = postfix_expr {_} "." {_} identifier ;
primary_expr =
| none_expr
| bool_expr
| int_expr
| float_expr
| string_expr
| list_expr
| table_expr
| self_expr
| super_expr
| var_expr
| group_expr
;
none_expr = "none" ;
bool_expr = "true" | "false" ;
(* NOTE: `int_expr` takes precedence over `float_expr` *)
int_expr = (* regex *) "[0-9]([0-9_]*[0-9])?" ;
float_expr = (* regex *) "[0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)?" ;
string_expr = "\"" (* regex *) "([^\"\\]|\\.)*" "\"" ;
list_expr = "[" (expr ("," expr)*)? "]" ;
table_expr = "{" (table_field ("," table_field)*)? "}" ;
table_field = table_key ":" expr ;
table_key =
| identifier
| "[" expr "]"
;
self_expr = "self" ;
super_expr = "super" ;
var_expr = identifier ;
group_expr = "(" expr ")" ;
identifier = (* regex *) "[a-zA-Z_][a-zA-Z0-9_]*" ;