use crate::SyntaxKind::{
COLON, COMMA, CONST_DECL, EQ, EXTERNAL_DECL, FILE_PATH, FUNCTION_PARAM_LIST, HASH, IDENT,
IDENTIFIER, IMPORT_ITEM, IMPORT_LIST, IMPORT_MODULE, IMPORT_STMT, INCLUDE_STMT, INTEGER,
KW_CONST, KW_EXTERNAL, KW_IMPORT, KW_INCLUDE, KW_LIST, KW_VAR, L_BRACE, L_PAREN, LIST_DECL,
LIST_DEF, LIST_MEMBER, LIST_MEMBER_OFF, LIST_MEMBER_ON, NEWLINE, R_BRACE, R_PAREN, STRUCT_DECL,
STRUCT_FIELD_DECL, VAR_DECL,
};
use super::Parser;
use super::expression::skip_balanced;
use super::types::{at_type_annotation, type_annotation, type_expr};
pub(crate) fn include_statement(p: &mut Parser<'_, '_>) {
p.start_node(INCLUDE_STMT);
p.bump(); p.skip_ws();
p.start_node(FILE_PATH);
if p.at_eof() || p.nth_raw(0) == NEWLINE {
p.error("expected file path".into());
}
while !p.at_eof() && p.nth_raw(0) != NEWLINE {
p.bump();
}
p.finish_node();
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
}
pub(crate) fn import_statement(p: &mut Parser<'_, '_>) {
p.start_node(IMPORT_STMT);
p.bump(); p.skip_ws();
if p.at(L_BRACE) {
import_list(p);
p.skip_ws();
if p.at_kw_text("FROM") {
p.bump(); } else {
p.error("expected `FROM` after import list".into());
}
p.skip_ws();
import_module(p);
} else {
import_module(p);
}
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
}
fn import_list(p: &mut Parser<'_, '_>) {
p.start_node(IMPORT_LIST);
p.bump(); p.skip_ws();
if !p.at(R_BRACE) {
import_item(p);
loop {
p.skip_ws();
if !p.eat(COMMA) {
break;
}
p.skip_ws();
if p.at(R_BRACE) {
break; }
import_item(p);
}
}
p.skip_ws();
p.expect(R_BRACE);
p.finish_node();
}
fn import_item(p: &mut Parser<'_, '_>) {
p.start_node(IMPORT_ITEM);
p.start_node(IDENTIFIER);
p.expect_ident_or_keyword();
p.finish_node();
p.skip_ws();
if p.at_kw_text("AS") {
p.bump(); p.skip_ws();
p.start_node(IDENTIFIER);
p.expect_ident_or_keyword();
p.finish_node();
}
p.finish_node();
}
fn import_module(p: &mut Parser<'_, '_>) {
p.start_node(IMPORT_MODULE);
p.start_node(IDENTIFIER);
p.expect_ident_or_keyword();
p.finish_node();
p.finish_node();
}
pub(crate) fn external_declaration(p: &mut Parser<'_, '_>) {
p.start_node(EXTERNAL_DECL);
p.bump(); p.skip_ws();
p.start_node(IDENTIFIER);
p.expect_ident_or_keyword();
p.finish_node();
p.skip_ws();
p.expect(L_PAREN);
p.skip_ws();
if p.at_ident_or_keyword() {
function_param_list(p);
}
p.skip_ws();
p.expect(R_PAREN);
p.skip_ws();
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
}
fn function_param_list(p: &mut Parser<'_, '_>) {
p.start_node(FUNCTION_PARAM_LIST);
p.start_node(IDENTIFIER);
p.bump(); p.finish_node();
loop {
p.skip_ws();
if !p.eat(COMMA) {
break;
}
p.skip_ws();
p.start_node(IDENTIFIER);
p.expect_ident_or_keyword();
p.finish_node();
}
p.finish_node();
}
pub(crate) fn var_declaration(p: &mut Parser<'_, '_>) {
p.start_node(VAR_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();
p.expect(EQ);
p.skip_ws();
super::expression::expression(p);
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
}
pub(crate) fn const_declaration(p: &mut Parser<'_, '_>) {
p.start_node(CONST_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();
p.expect(EQ);
p.skip_ws();
super::expression::expression(p);
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
}
pub(crate) fn list_declaration(p: &mut Parser<'_, '_>) {
p.start_node(LIST_DECL);
p.bump(); p.skip_ws();
p.start_node(IDENTIFIER);
p.expect(IDENT);
p.finish_node();
p.skip_ws();
p.expect(EQ);
p.skip_ws();
list_definition(p);
p.skip_ws();
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
}
fn list_definition(p: &mut Parser<'_, '_>) {
p.start_node(LIST_DEF);
list_member(p);
loop {
p.skip_ws();
if !p.eat(COMMA) {
break;
}
p.skip_ws();
list_member(p);
}
p.finish_node();
}
fn list_member(p: &mut Parser<'_, '_>) {
p.start_node(LIST_MEMBER);
if p.current() == L_PAREN {
p.start_node(LIST_MEMBER_ON);
p.bump(); p.skip_ws();
p.expect_ident_or_keyword();
p.skip_ws();
if p.eat(EQ) {
p.skip_ws();
p.expect(INTEGER);
}
p.skip_ws();
p.expect(R_PAREN);
p.finish_node();
} else {
p.start_node(LIST_MEMBER_OFF);
p.expect_ident_or_keyword();
p.skip_ws();
if p.eat(EQ) {
p.skip_ws();
p.expect(INTEGER);
}
p.finish_node();
}
p.finish_node();
}
pub(crate) fn struct_declaration(p: &mut Parser<'_, '_>) {
p.start_node(STRUCT_DECL);
p.bump(); p.skip_ws();
p.start_node(IDENTIFIER);
p.expect(IDENT);
p.finish_node();
p.skip_ws();
p.expect(EQ);
p.skip_ws();
p.expect(HASH);
p.skip_ws();
p.bump_assert(L_BRACE);
if p.at_depth_limit() {
p.error("nesting depth limit exceeded".into());
skip_balanced(p, L_BRACE, R_BRACE);
p.finish_node();
return;
}
p.depth += 1;
skip_struct_body_trivia(p);
if p.current() != R_BRACE {
struct_field_decl(p);
loop {
skip_struct_body_trivia(p);
if !p.eat(COMMA) {
break;
}
skip_struct_body_trivia(p);
if p.current() == R_BRACE {
break; }
struct_field_decl(p);
}
}
skip_struct_body_trivia(p);
p.expect(R_BRACE);
p.depth -= 1;
p.skip_ws();
if p.at(NEWLINE) {
p.bump();
}
p.finish_node();
}
fn skip_struct_body_trivia(p: &mut Parser<'_, '_>) {
loop {
p.skip_ws();
if p.current() == NEWLINE {
p.bump();
} else {
break;
}
}
}
fn struct_field_decl(p: &mut Parser<'_, '_>) {
p.start_node(STRUCT_FIELD_DECL);
p.start_node(IDENTIFIER);
p.expect_ident_or_keyword();
p.finish_node();
p.skip_ws();
p.expect(COLON);
p.skip_ws();
type_expr(p);
p.finish_node();
}
pub(crate) fn at_struct_decl(p: &Parser<'_, '_>) -> bool {
p.at_kw_text("STRUCT")
&& p.nth(1) == IDENT
&& p.nth(2) == EQ
&& p.nth(3) == HASH
&& p.nth(4) == L_BRACE
}
pub(crate) fn at_declaration(p: &Parser<'_, '_>) -> bool {
matches!(
p.current(),
KW_INCLUDE | KW_IMPORT | KW_EXTERNAL | KW_VAR | KW_CONST | KW_LIST
)
}
pub(crate) fn at_inline_declaration(p: &Parser<'_, '_>) -> bool {
matches!(p.current(), KW_VAR | KW_CONST | KW_LIST)
}
pub(crate) fn declaration(p: &mut Parser<'_, '_>) {
match p.current() {
KW_INCLUDE => include_statement(p),
KW_IMPORT => import_statement(p),
KW_EXTERNAL => external_declaration(p),
KW_VAR => var_declaration(p),
KW_CONST => const_declaration(p),
KW_LIST => list_declaration(p),
_ => {
p.error("expected declaration".into());
}
}
}