use crate::error::SyntaxError;
#[allow(clippy::enum_glob_use)]
use crate::kind::SyntaxKind::{self, *};
use crate::lexer::{Token, tokenize};
use crate::text::TextRange;
use crate::tree::{Checkpoint, SyntaxTree, TreeBuilder};
#[must_use]
pub fn parse(source: &str) -> SyntaxTree {
let lexed = tokenize(source);
Parser {
source,
tokens: lexed.tokens,
pos: 0,
builder: TreeBuilder::new(),
errors: lexed.errors,
bracket_depth: 0,
in_pattern: false,
fuel: 0,
}
.run()
}
struct Parser<'a> {
source: &'a str,
tokens: Vec<Token>,
pos: usize,
builder: TreeBuilder,
errors: Vec<SyntaxError>,
bracket_depth: u32,
in_pattern: bool,
fuel: u32,
}
const CLASS_MEMBER_START: &[SyntaxKind] = &[
At,
VarKw,
ConstKw,
FuncKw,
ClassKw,
ClassNameKw,
ExtendsKw,
SignalKw,
EnumKw,
StaticKw,
];
const STATEMENT_START: &[SyntaxKind] = &[
VarKw,
ConstKw,
IfKw,
ElifKw,
ElseKw,
ForKw,
WhileKw,
MatchKw,
ReturnKw,
PassKw,
BreakKw,
ContinueKw,
BreakpointKw,
AssertKw,
AwaitKw,
FuncKw,
ClassKw,
SignalKw,
EnumKw,
StaticKw,
];
impl Parser<'_> {
fn run(mut self) -> SyntaxTree {
self.builder.start_node(SourceFile);
while !self.at(Eof) {
let before = self.pos;
self.parse_class_member();
while self.eat(Semicolon) {}
self.ensure_progress(before, CLASS_MEMBER_START);
}
self.skip_trivia();
self.bump_raw();
self.builder.finish_node();
self.builder.finish(self.source.to_string(), self.errors)
}
fn parse_class_member(&mut self) {
let checkpoint = self.builder.checkpoint();
let mut is_abstract = false;
while self.at(At) {
is_abstract |= self.parse_annotation();
}
match self.current() {
ClassNameKw => self.parse_class_name(),
ExtendsKw => self.parse_extends(),
VarKw => self.parse_var_decl(checkpoint),
ConstKw => self.parse_const_decl(checkpoint),
SignalKw => self.parse_signal_decl(checkpoint),
EnumKw => self.parse_enum_decl(checkpoint),
FuncKw => self.parse_func_decl(checkpoint, is_abstract),
ClassKw => self.parse_inner_class(checkpoint),
StaticKw => match self.nth(1) {
VarKw => self.parse_var_decl(checkpoint),
FuncKw => self.parse_func_decl(checkpoint, is_abstract),
_ => self.error_and_recover(
"expected `var` or `func` after `static`",
CLASS_MEMBER_START,
),
},
PassKw => self.simple_statement(PassStmt),
Str => self.parse_expr_statement(),
At | Eof | Dedent => {}
_ => self.error_and_recover("expected a declaration", CLASS_MEMBER_START),
}
}
fn parse_annotation(&mut self) -> bool {
self.builder.start_node(Annotation);
self.bump(); let mut is_abstract = false;
if self.current().is_ident_like() {
is_abstract = self.current_text() == "abstract";
self.bump();
} else {
self.error("expected an annotation name after `@`");
}
if self.at(LParen) {
self.parse_arg_list();
}
self.builder.finish_node();
is_abstract
}
fn parse_class_name(&mut self) {
self.builder.start_node(ClassNameDecl);
self.bump(); self.expect_name("expected a class name");
if self.at(ExtendsKw) {
self.parse_extends();
}
self.builder.finish_node();
}
fn parse_extends(&mut self) {
self.builder.start_node(ExtendsDecl);
self.bump(); if self.at(Str) {
self.bump();
while self.at(Dot) {
self.bump();
self.expect_name("expected a name after `.`");
}
} else {
self.parse_type();
}
self.builder.finish_node();
}
fn parse_signal_decl(&mut self, checkpoint: Checkpoint) {
self.builder.start_node_at(checkpoint, SignalDecl);
self.bump(); self.expect_name("expected a signal name");
if self.at(LParen) {
self.parse_param_list();
}
self.builder.finish_node();
}
fn parse_enum_decl(&mut self, checkpoint: Checkpoint) {
self.builder.start_node_at(checkpoint, EnumDecl);
self.bump(); if self.at_name() {
self.eat_name();
}
if self.at(LBrace) {
self.builder.start_node(EnumBody);
self.bump(); self.enter_brackets();
while !self.at(RBrace) && !self.at(Eof) {
let before = self.pos;
self.builder.start_node(EnumVariant);
self.expect_name("expected an enum member name");
if self.eat(Eq) {
self.parse_expr();
}
self.builder.finish_node();
if !self.eat(Comma) {
break;
}
self.ensure_progress(before, &[RBrace]);
}
self.expect(RBrace, "expected `}` to close the enum");
self.leave_brackets();
self.builder.finish_node();
} else {
self.error("expected `{` to open the enum body");
}
self.builder.finish_node();
}
fn parse_const_decl(&mut self, checkpoint: Checkpoint) {
self.builder.start_node_at(checkpoint, ConstDecl);
self.bump(); self.expect_name("expected a constant name");
if !self.parse_type_and_initializer() {
self.error("a constant must be initialised");
}
self.builder.finish_node();
}
fn parse_var_decl(&mut self, checkpoint: Checkpoint) {
self.builder.start_node_at(checkpoint, VarDecl);
self.eat(StaticKw);
self.bump(); self.expect_name("expected a variable name");
self.parse_type_and_initializer();
if self.at(Colon) {
self.parse_accessors();
}
self.builder.finish_node();
}
fn parse_type_and_initializer(&mut self) -> bool {
if self.at(ColonEq) || (self.at(Colon) && self.nth(1) == Eq) {
self.builder.start_node(Initializer);
self.bump(); self.eat(Eq); self.parse_expr();
self.builder.finish_node();
return true;
}
let names_accessor = matches!(self.nth_text(1), "set" | "get");
if self.at(Colon) && (self.nth_is_name(1) || self.nth(1) == VoidKw) && !names_accessor {
self.builder.start_node(TypeHint);
self.bump(); self.parse_type();
self.builder.finish_node();
}
if self.at(Eq) {
self.parse_initializer();
return true;
}
false
}
fn parse_initializer(&mut self) {
self.builder.start_node(Initializer);
self.bump(); self.parse_expr();
self.builder.finish_node();
}
fn parse_accessors(&mut self) {
self.builder.start_node(Accessors);
self.bump(); if self.at(Indent) {
self.bump();
while !self.at(Dedent) && !self.at(Eof) {
let before = self.pos;
self.parse_one_accessor();
self.eat(Comma);
self.ensure_progress(before, &[Dedent]);
}
self.eat(Dedent);
} else {
loop {
self.parse_one_accessor();
if !self.eat(Comma) {
break;
}
}
}
self.builder.finish_node();
}
fn parse_one_accessor(&mut self) {
if !self.at(Ident) {
self.error_and_recover("expected `set` or `get`", &[Dedent, Comma]);
return;
}
let node = match self.current_text() {
"set" => Setter,
"get" => Getter,
_ => {
self.error_and_recover("expected `set` or `get`", &[Dedent, Comma]);
return;
}
};
self.builder.start_node(node);
self.bump();
if self.at(LParen) {
self.parse_param_list();
}
if self.eat(Eq) {
self.parse_expr();
} else if self.eat(Colon) {
self.parse_block();
} else {
self.error("expected `=` or `:` after the accessor");
}
self.builder.finish_node();
}
fn parse_func_decl(&mut self, checkpoint: Checkpoint, allow_no_body: bool) {
self.builder.start_node_at(checkpoint, FuncDecl);
self.eat(StaticKw);
self.bump(); self.expect_name("expected a function name");
if self.at(LParen) {
self.parse_param_list();
} else {
self.error("expected `(` to open the parameter list");
}
if self.at(Arrow) {
self.builder.start_node(ReturnType);
self.bump();
self.parse_type();
self.builder.finish_node();
}
if self.eat(Colon) {
self.parse_block();
} else if !allow_no_body {
self.error("expected `:` to open the function body");
}
self.builder.finish_node();
}
fn parse_inner_class(&mut self, checkpoint: Checkpoint) {
self.builder.start_node_at(checkpoint, ClassDecl);
self.bump(); self.expect_name("expected a class name");
if self.at(ExtendsKw) {
self.parse_extends();
}
if self.eat(Colon) {
self.parse_class_block();
} else {
self.error("expected `:` to open the class body");
}
self.builder.finish_node();
}
fn parse_class_block(&mut self) {
self.builder.start_node(Block);
if self.at(Indent) {
self.bump();
while !self.at(Dedent) && !self.at(Eof) {
let before = self.pos;
self.parse_class_member();
while self.eat(Semicolon) {}
self.ensure_progress(before, CLASS_MEMBER_START);
}
self.eat(Dedent);
} else {
self.parse_class_member();
}
self.builder.finish_node();
}
fn parse_param_list(&mut self) {
self.builder.start_node(ParamList);
self.bump(); self.enter_brackets();
while !self.at(RParen) && !self.at(Eof) {
let before = self.pos;
self.builder.start_node(Param);
self.eat(Ellipsis);
self.expect_name("expected a parameter name");
self.parse_type_and_initializer();
self.builder.finish_node();
if !self.eat(Comma) {
break;
}
self.ensure_progress(before, &[RParen]);
}
self.expect(RParen, "expected `)` to close the parameter list");
self.leave_brackets();
self.builder.finish_node();
}
fn parse_arg_list(&mut self) {
self.builder.start_node(ArgList);
self.bump(); self.enter_brackets();
while !self.at(RParen) && !self.at(Eof) {
let before = self.pos;
self.parse_expr();
if !self.eat(Comma) {
break;
}
self.ensure_progress(before, &[RParen]);
}
self.expect(RParen, "expected `)` to close the argument list");
self.leave_brackets();
self.builder.finish_node();
}
fn parse_type(&mut self) {
if self.at(VoidKw) {
self.bump();
return;
}
if !self.at_name() {
self.error("expected a type name");
return;
}
self.eat_name();
while self.at(Dot) {
self.bump();
self.expect_name("expected a name after `.`");
}
if self.at(LBracket) {
self.bump();
self.enter_brackets();
while !self.at(RBracket) && !self.at(Eof) {
let before = self.pos;
self.parse_type();
if !self.eat(Comma) {
break;
}
self.ensure_progress(before, &[RBracket]);
}
self.expect(RBracket, "expected `]` to close the type parameters");
self.leave_brackets();
}
}
fn parse_block(&mut self) {
self.builder.start_node(Block);
if self.at(Indent) {
let enclosing_brackets = std::mem::take(&mut self.bracket_depth);
self.bump();
while !self.at(Dedent) && !self.at(Eof) {
let before = self.pos;
self.parse_statement();
while self.eat(Semicolon) {}
self.ensure_progress(before, STATEMENT_START);
}
self.eat(Dedent);
self.bracket_depth = enclosing_brackets;
} else {
loop {
self.parse_statement();
if !self.eat(Semicolon) || self.newline_ahead() {
break;
}
if self.at(Eof) || self.at(Dedent) {
break;
}
}
}
self.builder.finish_node();
}
#[allow(clippy::too_many_lines)]
fn parse_statement(&mut self) {
match self.current() {
PassKw => self.simple_statement(PassStmt),
BreakKw => self.simple_statement(BreakStmt),
ContinueKw => self.simple_statement(ContinueStmt),
BreakpointKw => self.simple_statement(BreakpointStmt),
ReturnKw => {
self.builder.start_node(ReturnStmt);
self.bump();
if !self.at_statement_end() {
self.parse_expr();
}
self.builder.finish_node();
}
AssertKw => {
self.builder.start_node(AssertStmt);
self.bump();
if self.at(LParen) {
self.parse_arg_list();
} else {
self.error("expected `(` after `assert`");
}
self.builder.finish_node();
}
VarKw => {
let checkpoint = self.builder.checkpoint();
self.parse_var_decl(checkpoint);
}
ConstKw => {
let checkpoint = self.builder.checkpoint();
self.parse_const_decl(checkpoint);
}
StaticKw if self.nth(1) == VarKw => {
let checkpoint = self.builder.checkpoint();
self.parse_var_decl(checkpoint);
}
IfKw => self.parse_if_statement(),
WhileKw => {
self.builder.start_node(WhileStmt);
self.bump();
self.parse_expr();
if self.eat(Colon) {
self.parse_block();
} else {
self.error("expected `:` to open the loop body");
}
self.builder.finish_node();
}
ForKw => self.parse_for_statement(),
MatchKw => self.parse_match_statement(),
At => {
let checkpoint = self.builder.checkpoint();
while self.at(At) {
self.parse_annotation();
}
match self.current() {
VarKw => self.parse_var_decl(checkpoint),
ConstKw => self.parse_const_decl(checkpoint),
Dedent | Eof => {}
_ => self.parse_statement(),
}
}
ClassKw | SignalKw | EnumKw => self.parse_class_member(),
Eof | Dedent => {
self.error("unexpected end of block");
}
_ => self.parse_expr_statement(),
}
}
fn simple_statement(&mut self, kind: SyntaxKind) {
self.builder.start_node(kind);
self.bump();
self.builder.finish_node();
}
fn parse_if_statement(&mut self) {
self.builder.start_node(IfStmt);
self.bump(); self.parse_expr();
if self.eat(Colon) {
self.parse_block();
} else {
self.error("expected `:` to open the branch body");
}
while self.at(ElifKw) {
self.builder.start_node(ElifClause);
self.bump();
self.parse_expr();
if self.eat(Colon) {
self.parse_block();
} else {
self.error("expected `:` to open the branch body");
}
self.builder.finish_node();
}
if self.at(ElseKw) {
self.builder.start_node(ElseClause);
self.bump();
if self.eat(Colon) {
self.parse_block();
} else {
self.error("expected `:` to open the branch body");
}
self.builder.finish_node();
}
self.builder.finish_node();
}
fn parse_for_statement(&mut self) {
self.builder.start_node(ForStmt);
self.bump(); self.expect_name("expected a loop variable name");
if self.at(Colon) && (self.nth_is_name(1) || self.nth(1) == VoidKw) {
self.builder.start_node(TypeHint);
self.bump();
self.parse_type();
self.builder.finish_node();
}
if !self.eat(InKw) {
self.error("expected `in` after the loop variable");
}
self.parse_expr();
if self.eat(Colon) {
self.parse_block();
} else {
self.error("expected `:` to open the loop body");
}
self.builder.finish_node();
}
fn parse_match_statement(&mut self) {
self.builder.start_node(MatchStmt);
self.bump(); self.parse_expr();
if !self.eat(Colon) {
self.error("expected `:` after the match subject");
}
if self.at(Indent) {
self.bump();
while !self.at(Dedent) && !self.at(Eof) {
let before = self.pos;
self.parse_match_arm();
self.ensure_progress(before, &[Dedent]);
}
self.eat(Dedent);
} else {
self.error("expected an indented block of match arms");
}
self.builder.finish_node();
}
fn parse_match_arm(&mut self) {
self.builder.start_node(MatchArm);
self.in_pattern = true;
loop {
self.parse_expr();
if !self.eat(Comma) {
break;
}
if self.at(Colon) || self.at(WhenKw) || self.at(Eof) {
break;
}
}
self.in_pattern = false;
if self.at(WhenKw) {
self.builder.start_node(MatchGuard);
self.bump();
self.parse_expr();
self.builder.finish_node();
}
if self.eat(Colon) {
self.parse_block();
} else {
self.error("expected `:` after the match pattern");
}
self.builder.finish_node();
}
fn parse_expr_statement(&mut self) {
let checkpoint = self.builder.checkpoint();
self.builder.start_node(ExprStmt);
self.parse_expr();
if is_assign_op(self.current()) {
self.builder.finish_node();
self.builder.start_node_at(checkpoint, AssignStmt);
self.bump();
self.parse_expr();
}
self.builder.finish_node();
}
fn parse_expr(&mut self) {
self.parse_expr_bp(0);
}
fn parse_expr_bp(&mut self, min_bp: u8) {
let checkpoint = self.builder.checkpoint();
self.parse_prefix();
loop {
if self.at_line_break() {
break;
}
let kind = self.current();
if kind == IfKw && min_bp <= TERNARY_BP {
self.builder.start_node_at(checkpoint, TernaryExpr);
self.bump(); self.parse_expr_bp(TERNARY_BP + 1);
if !self.eat(ElseKw) {
self.error("expected `else` to complete the conditional expression");
}
self.parse_expr_bp(TERNARY_BP);
self.builder.finish_node();
continue;
}
if kind == NotKw && self.nth(1) == InKw {
let (left_bp, right_bp) = NOT_IN_BP;
if left_bp < min_bp {
break;
}
self.builder.start_node_at(checkpoint, BinaryExpr);
self.bump(); self.bump(); self.parse_expr_bp(right_bp);
self.builder.finish_node();
continue;
}
if kind == AsKw && CAST_BP >= min_bp {
self.builder.start_node_at(checkpoint, CastExpr);
self.bump();
self.parse_type();
self.builder.finish_node();
continue;
}
let Some((left_bp, right_bp)) = infix_binding_power(kind) else {
break;
};
if left_bp < min_bp {
break;
}
self.builder.start_node_at(checkpoint, BinaryExpr);
self.bump();
self.parse_expr_bp(right_bp);
self.builder.finish_node();
}
}
fn parse_prefix(&mut self) {
match self.current() {
NotKw | Bang => {
self.builder.start_node(UnaryExpr);
self.bump();
self.parse_expr_bp(NOT_BP);
self.builder.finish_node();
}
Minus | Plus | Tilde => {
self.builder.start_node(UnaryExpr);
self.bump();
self.parse_expr_bp(UNARY_BP);
self.builder.finish_node();
}
AwaitKw => {
self.builder.start_node(AwaitExpr);
self.bump();
self.parse_expr_bp(UNARY_BP);
self.builder.finish_node();
}
_ => self.parse_postfix(),
}
}
fn parse_postfix(&mut self) {
let checkpoint = self.builder.checkpoint();
self.parse_atom();
loop {
if self.at_line_break() {
break;
}
match self.current() {
LParen => {
self.builder.start_node_at(checkpoint, CallExpr);
self.parse_arg_list();
self.builder.finish_node();
}
LBracket => {
self.builder.start_node_at(checkpoint, SubscriptExpr);
self.bump();
self.enter_brackets();
self.parse_expr();
self.expect(RBracket, "expected `]` to close the subscript");
self.leave_brackets();
self.builder.finish_node();
}
Dot => {
self.builder.start_node_at(checkpoint, AttributeExpr);
self.bump();
if self.at_name() {
self.eat_name();
} else {
self.error("expected a member name after `.`");
}
self.builder.finish_node();
}
_ => break,
}
}
}
#[allow(clippy::too_many_lines)]
fn parse_atom(&mut self) {
match self.current() {
Int | Float | Str | StringName | NodePath | GetNode | UniqueNode | TrueKw | FalseKw
| NullKw => {
self.builder.start_node(Literal);
self.bump();
self.builder.finish_node();
}
Ident | SelfKw | SuperKw | MatchKw | WhenKw => {
self.builder.start_node(NameRef);
if !self.eat_name() {
self.bump();
}
self.builder.finish_node();
}
PreloadKw => {
self.builder.start_node(PreloadExpr);
self.bump();
if self.at(LParen) {
self.parse_arg_list();
} else {
self.error("expected `(` after `preload`");
}
self.builder.finish_node();
}
LParen => {
self.builder.start_node(ParenExpr);
self.bump();
self.enter_brackets();
if !self.at(RParen) {
self.parse_expr();
}
self.expect(RParen, "expected `)` to close the group");
self.leave_brackets();
self.builder.finish_node();
}
LBracket => {
self.builder.start_node(ArrayExpr);
self.bump();
self.enter_brackets();
while !self.at(RBracket) && !self.at(Eof) {
let before = self.pos;
self.parse_expr();
if !self.eat(Comma) {
break;
}
self.ensure_progress(before, &[RBracket]);
}
self.expect(RBracket, "expected `]` to close the array");
self.leave_brackets();
self.builder.finish_node();
}
LBrace => self.parse_dict(),
VarKw if self.in_pattern => {
self.builder.start_node(NameRef);
self.bump();
self.expect_name("expected a name after `var` in a pattern");
self.builder.finish_node();
}
DotDot if self.in_pattern => {
self.builder.start_node(Literal);
self.bump();
self.builder.finish_node();
}
FuncKw => {
self.builder.start_node(LambdaExpr);
self.bump();
if self.at_name() {
self.eat_name();
}
if self.at(LParen) {
self.parse_param_list();
} else {
self.error("expected `(` to open the lambda parameters");
}
if self.at(Arrow) {
self.builder.start_node(ReturnType);
self.bump();
self.parse_type();
self.builder.finish_node();
}
if self.eat(Colon) {
self.parse_block();
} else {
self.error("expected `:` to open the lambda body");
}
self.builder.finish_node();
}
_ => self.error_and_recover("expected an expression", STATEMENT_START),
}
}
fn parse_dict(&mut self) {
self.builder.start_node(DictExpr);
self.bump(); self.enter_brackets();
while !self.at(RBrace) && !self.at(Eof) {
let before = self.pos;
self.builder.start_node(DictEntry);
if self.in_pattern && self.at(DotDot) {
self.bump();
} else if self.at_name() && self.nth(1) == Eq {
self.bump(); self.bump(); self.parse_expr();
} else {
self.parse_expr();
if self.eat(Colon) {
self.parse_expr();
} else if !self.in_pattern {
self.error("expected `:` between the key and value");
}
}
self.builder.finish_node();
if !self.eat(Comma) {
break;
}
self.ensure_progress(before, &[RBrace]);
}
self.expect(RBrace, "expected `}` to close the dictionary");
self.leave_brackets();
self.builder.finish_node();
}
fn current(&self) -> SyntaxKind {
self.nth(0)
}
fn nth(&self, n: usize) -> SyntaxKind {
self.tokens[self.pos..]
.iter()
.filter(|token| !token.kind.is_trivia())
.nth(n)
.map_or(Eof, |token| token.kind)
}
fn current_text(&self) -> &str {
self.nth_text(0)
}
fn nth_text(&self, n: usize) -> &str {
self.tokens[self.pos..]
.iter()
.filter(|token| !token.kind.is_trivia())
.nth(n)
.map_or("", |token| token.text(self.source))
}
fn at(&self, kind: SyntaxKind) -> bool {
self.current() == kind
}
fn eat(&mut self, kind: SyntaxKind) -> bool {
if self.at(kind) {
self.bump();
true
} else {
false
}
}
fn expect(&mut self, kind: SyntaxKind, message: &str) {
if !self.eat(kind) {
self.error(message);
}
}
fn at_name(&self) -> bool {
self.current().is_name()
}
fn nth_is_name(&self, n: usize) -> bool {
self.nth(n).is_name()
}
fn eat_name(&mut self) -> bool {
if !self.at_name() {
return false;
}
self.skip_trivia();
let mut token = self.tokens[self.pos];
token.kind = Ident;
self.builder.token(token);
if self.tokens[self.pos].kind != Eof {
self.pos += 1;
}
true
}
fn expect_name(&mut self, message: &str) {
if !self.eat_name() {
self.error(message);
}
}
fn bump(&mut self) {
self.skip_trivia();
self.bump_raw();
}
fn bump_raw(&mut self) {
let token = self.tokens[self.pos];
self.builder.token(token);
if token.kind != Eof {
self.pos += 1;
}
}
fn skip_trivia(&mut self) {
while self.tokens[self.pos].kind.is_trivia() {
self.bump_raw();
}
}
fn newline_ahead(&self) -> bool {
self.tokens[self.pos..]
.iter()
.take_while(|token| token.kind.is_trivia())
.any(|token| token.kind == Newline)
}
fn at_line_break(&self) -> bool {
self.bracket_depth == 0 && self.newline_ahead()
}
fn enter_brackets(&mut self) {
self.bracket_depth += 1;
}
fn leave_brackets(&mut self) {
self.bracket_depth = self.bracket_depth.saturating_sub(1);
}
fn at_statement_end(&self) -> bool {
self.newline_ahead() || matches!(self.current(), Semicolon | Dedent | Eof)
}
fn error(&mut self, message: &str) {
let range = self.tokens[self.pos..]
.iter()
.find(|token| !token.kind.is_trivia())
.map_or_else(|| self.tokens[self.pos].range, |token| token.range);
if self
.errors
.last()
.is_some_and(|last| last.range().start() == range.start())
{
return;
}
self.errors
.push(SyntaxError::new(TextRange::empty(range.start()), message));
}
fn error_and_recover(&mut self, message: &str, recovery: &[SyntaxKind]) {
self.error(message);
self.builder.start_node(Error);
if !self.at(Eof) {
self.bump();
}
while !self.at(Eof) && !recovery.contains(&self.current()) && !self.at(Dedent) {
if self.newline_ahead() {
break;
}
self.bump();
}
self.builder.finish_node();
}
fn ensure_progress(&mut self, before: usize, recovery: &[SyntaxKind]) {
if self.pos != before {
self.fuel = 0;
return;
}
self.fuel += 1;
if self.fuel > 1 {
self.fuel = 0;
self.error_and_recover("unexpected token", recovery);
}
}
}
const TERNARY_BP: u8 = 1;
const CAST_BP: u8 = 9;
const NOT_BP: u8 = 7;
const UNARY_BP: u8 = 27;
const NOT_IN_BP: (u8, u8) = (11, 12);
fn infix_binding_power(kind: SyntaxKind) -> Option<(u8, u8)> {
Some(match kind {
OrKw | PipePipe => (3, 4),
AndKw | AmpAmp => (5, 6),
IsKw => (9, 10),
InKw => (11, 12),
Lt | LtEq | Gt | GtEq | EqEq | BangEq => (13, 14),
Pipe => (15, 16),
Caret => (17, 18),
Amp => (19, 20),
Shl | Shr => (21, 22),
Plus | Minus => (23, 24),
Star | Slash | Percent => (25, 26),
StarStar => (30, 29),
_ => return None,
})
}
fn is_assign_op(kind: SyntaxKind) -> bool {
matches!(
kind,
Eq | PlusEq
| MinusEq
| StarEq
| StarStarEq
| SlashEq
| PercentEq
| AmpEq
| PipeEq
| CaretEq
| ShlEq
| ShrEq
)
}