use crate::SyntaxKind::{
ASSIGNMENT, AWAIT_STMT, BREAK_STMT, CONTINUE_STMT, DOT, ELSE_CLAUSE, EOF, EQ, EXPR_STMT,
FIELD_ACCESS_EXPR, FOR_STMT, IDENT, IDENTIFIER, IF_STMT, INDEX_EXPR, KW_ELSE, KW_RETURN,
KW_TEMP, L_BRACE, L_BRACKET, LOGIC_LINE, MINUS_EQ, NEWLINE, PLUS_EQ, R_BRACE, R_BRACKET,
RETURN_STMT, STMT_BLOCK, TEMP_DECL, WHILE_STMT,
};
use super::Parser;
use super::types::{at_type_annotation, type_annotation};
pub(crate) fn logic_line(p: &mut Parser<'_, '_>) {
p.start_node(LOGIC_LINE);
p.bump(); p.skip_ws();
if p.current() == L_BRACE {
stmt_block(p);
p.skip_ws();
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
return;
}
match p.current() {
KW_RETURN => return_statement(p),
KW_TEMP => temp_declaration(p),
IDENT if p.at_kw_text("await") && !is_assignment_ahead(p) => await_statement(p),
IDENT if is_assignment_ahead(p) => assignment(p),
_ => {
super::expression::expression(p);
}
}
p.skip_ws();
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
}
fn is_assignment_ahead(p: &Parser<'_, '_>) -> bool {
let mut i = 1; while p.nth(i) == DOT && p.nth(i + 1) == IDENT {
i += 2;
}
loop {
if p.nth(i) == L_BRACKET {
i += 1;
let mut depth = 1i32;
loop {
match p.nth(i) {
L_BRACKET => depth += 1,
R_BRACKET => depth -= 1,
EOF | NEWLINE => return false, _ => {}
}
i += 1;
if depth == 0 {
break;
}
}
continue;
}
if p.nth(i) == DOT && p.nth(i + 1) == IDENT {
i += 2;
continue;
}
break;
}
let next = p.nth(i);
matches!(next, EQ | PLUS_EQ | MINUS_EQ) && !(next == EQ && p.nth(i + 1) == EQ)
}
fn return_statement(p: &mut Parser<'_, '_>) {
p.start_node(RETURN_STMT);
p.bump(); p.skip_ws();
if !matches!(p.current(), NEWLINE | EOF | R_BRACE) {
super::expression::expression(p);
}
p.finish_node();
}
fn await_statement(p: &mut Parser<'_, '_>) {
p.start_node(AWAIT_STMT);
p.bump(); p.skip_ws();
if matches!(p.current(), NEWLINE | EOF | R_BRACE) {
p.error("expected a condition expression after `await`".into());
} else {
super::expression::expression(p);
}
p.finish_node();
}
fn temp_declaration(p: &mut Parser<'_, '_>) {
p.start_node(TEMP_DECL);
p.bump(); p.skip_ws();
p.start_node(IDENTIFIER);
p.expect(IDENT);
p.finish_node();
if at_type_annotation(p) {
type_annotation(p);
}
p.skip_ws();
assignment_op(p);
p.skip_ws();
super::expression::expression(p);
p.finish_node();
}
fn assignment(p: &mut Parser<'_, '_>) {
p.start_node(ASSIGNMENT);
indexable_lvalue(p);
p.skip_ws();
assignment_op(p);
p.skip_ws();
super::expression::expression(p);
p.finish_node();
}
fn indexable_lvalue(p: &mut Parser<'_, '_>) {
let checkpoint = p.checkpoint();
super::divert::path(p);
loop {
if p.current() == L_BRACKET {
p.start_node_at(checkpoint, INDEX_EXPR);
super::expression::index_bracket(p);
p.finish_node();
continue;
}
if p.current() == DOT && p.nth(1) == IDENT {
p.start_node_at(checkpoint, FIELD_ACCESS_EXPR);
p.bump(); p.skip_ws();
p.start_node(IDENTIFIER);
p.bump(); p.finish_node();
p.finish_node();
continue;
}
break;
}
}
fn assignment_op(p: &mut Parser<'_, '_>) {
match p.current() {
PLUS_EQ | MINUS_EQ | EQ => {
p.bump();
}
_ => {
p.error("expected assignment operator".into());
}
}
}
pub(super) fn stmt_block(p: &mut Parser<'_, '_>) {
p.start_node(STMT_BLOCK);
p.skip_ws();
p.bump();
if p.at_depth_limit() {
p.error("nesting depth limit exceeded".into());
let mut depth = 1u32;
while !p.at_eof() && depth > 0 {
match p.current() {
L_BRACE => {
depth += 1;
p.bump();
}
R_BRACE => {
depth -= 1;
if depth > 0 {
p.bump();
}
}
_ => p.bump(),
}
}
if p.current() == R_BRACE {
p.bump();
}
p.finish_node();
return;
}
p.depth += 1;
loop {
p.skip_ws();
while p.at(NEWLINE) {
p.bump();
p.skip_ws();
}
if p.at(R_BRACE) || p.at_eof() {
break;
}
let before = p.pos();
block_stmt(p);
if p.pos() == before {
p.error_recover("unexpected token in block");
}
p.skip_ws();
if p.at(NEWLINE) {
p.bump();
}
}
p.skip_ws();
p.expect(R_BRACE);
p.depth -= 1;
p.finish_node();
}
fn block_stmt(p: &mut Parser<'_, '_>) {
match p.current() {
KW_RETURN => return_statement(p),
KW_TEMP => temp_declaration(p),
IDENT if p.at_kw_text("await") && !is_assignment_ahead(p) => await_statement(p),
IDENT if p.at_kw_text("if") => if_stmt(p),
IDENT if p.at_kw_text("while") => while_stmt(p),
IDENT if p.at_kw_text("for") => for_stmt(p),
IDENT if p.at_kw_text("break") => {
p.start_node(BREAK_STMT);
p.bump();
p.finish_node();
}
IDENT if p.at_kw_text("continue") => {
p.start_node(CONTINUE_STMT);
p.bump();
p.finish_node();
}
IDENT if is_assignment_ahead(p) => assignment(p),
_ => expr_stmt(p),
}
}
fn if_stmt(p: &mut Parser<'_, '_>) {
p.start_node(IF_STMT);
p.bump(); p.skip_ws();
super::expression::expression(p);
p.skip_ws();
stmt_block(p);
p.skip_ws();
if p.at(KW_ELSE) {
p.start_node(ELSE_CLAUSE);
p.bump(); p.skip_ws();
if p.at_kw_text("if") {
if_stmt(p); } else {
stmt_block(p);
}
p.finish_node();
}
p.finish_node();
}
fn while_stmt(p: &mut Parser<'_, '_>) {
p.start_node(WHILE_STMT);
p.bump(); p.skip_ws();
if p.at_kw_text("await") && p.nth(1) != L_BRACE {
p.bump(); p.skip_ws();
}
super::expression::expression(p);
p.skip_ws();
stmt_block(p);
p.finish_node();
}
fn for_stmt(p: &mut Parser<'_, '_>) {
p.start_node(FOR_STMT);
p.bump(); p.skip_ws();
p.start_node(IDENTIFIER);
p.expect(IDENT);
p.finish_node();
p.skip_ws();
if p.at_kw_text("in") {
p.bump();
} else {
p.error("expected 'in'".into());
}
p.skip_ws();
super::expression::expression(p);
p.skip_ws();
stmt_block(p);
p.finish_node();
}
fn expr_stmt(p: &mut Parser<'_, '_>) {
p.start_node(EXPR_STMT);
super::expression::expression(p);
p.finish_node();
}