forked from katzen-cafe/iowo
191 lines
5.8 KiB
Rust
191 lines
5.8 KiB
Rust
use enumset::enum_set;
|
|
|
|
use crate::lst_parser::{
|
|
error::SyntaxError,
|
|
grammar::expression::expression,
|
|
syntax_kind::{SyntaxKind::*, TokenSet},
|
|
CompletedMarker, Parser,
|
|
};
|
|
|
|
const TOP_LEVEL_ITEM_START: TokenSet = enum_set!(DEF_KW | MOD_KW | USE_KW);
|
|
|
|
pub fn mod_body(p: &mut Parser) {
|
|
loop {
|
|
if top_level_item(p).is_none() {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn mod_decl(p: &mut Parser) -> Option<CompletedMarker> {
|
|
let mod_start = p.start("module");
|
|
if !p.eat(MOD_KW) {
|
|
mod_start.abandon(p);
|
|
return None;
|
|
}
|
|
|
|
let mod_name = p.start("module_name");
|
|
if p.eat(IDENT) {
|
|
mod_name.complete(p, MODULE_NAME);
|
|
} else {
|
|
mod_name.error(p, SyntaxError::Expected(vec![IDENT]));
|
|
}
|
|
|
|
let mod_body_marker = p.start("mod_body");
|
|
if p.eat(SEMICOLON) {
|
|
mod_body_marker.abandon(p);
|
|
Some(mod_start.complete(p, MODULE))
|
|
} else if p.eat(L_BRACE) {
|
|
mod_body(p);
|
|
if !p.eat(R_BRACE) {
|
|
mod_body_marker
|
|
.complete(p, MODULE_BODY)
|
|
.precede(p, "unclosed_mod_body_err")
|
|
.error(p, SyntaxError::UnclosedModuleBody);
|
|
} else {
|
|
mod_body_marker.complete(p, MODULE_BODY);
|
|
}
|
|
Some(mod_start.complete(p, MODULE))
|
|
} else {
|
|
Some(mod_start.error(p, SyntaxError::Expected(vec![MODULE_BODY])))
|
|
}
|
|
}
|
|
|
|
pub fn top_level_item(p: &mut Parser) -> Option<CompletedMarker> {
|
|
if !TOP_LEVEL_ITEM_START.contains(p.current()) {
|
|
return None;
|
|
}
|
|
def(p).or_else(|| mod_decl(p)).or_else(|| r#use(p))
|
|
}
|
|
|
|
fn def(p: &mut Parser) -> Option<CompletedMarker> {
|
|
let def_start = p.start("top_level_def");
|
|
if !p.eat(DEF_KW) {
|
|
def_start.abandon(p);
|
|
return None;
|
|
}
|
|
|
|
let def_name = p.start("def_name");
|
|
if p.eat(IDENT) {
|
|
def_name.complete(p, DEF_NAME);
|
|
} else {
|
|
def_name.error(p, SyntaxError::Expected(vec![IDENT]));
|
|
}
|
|
|
|
let maybe_expected_eq = p.start("maybe_expect_eq");
|
|
if !p.eat(EQ) {
|
|
maybe_expected_eq.error(p, SyntaxError::Expected(vec![EQ]));
|
|
} else {
|
|
maybe_expected_eq.abandon(p);
|
|
}
|
|
|
|
let body = p.start("def_body");
|
|
if expression(p, false).is_some() {
|
|
body.complete(p, DEF_BODY);
|
|
} else {
|
|
body.error(p, SyntaxError::Expected(vec![DEF_BODY]));
|
|
}
|
|
|
|
Some(if p.eat(SEMICOLON) {
|
|
def_start.complete(p, DEF)
|
|
} else if TOP_LEVEL_ITEM_START.contains(p.current()) || p.at(EOF) {
|
|
def_start
|
|
.complete(p, DEF)
|
|
.precede(p, "unterminated_tl_item")
|
|
.error(p, SyntaxError::UnterminatedTopLevelItem)
|
|
} else {
|
|
def_start
|
|
.complete(p, DEF)
|
|
.precede(p, "err_unexpected")
|
|
.error(p, SyntaxError::Expected(vec![SEMICOLON]))
|
|
})
|
|
}
|
|
|
|
fn r#use(p: &mut Parser) -> Option<CompletedMarker> {
|
|
let use_start = p.start("use_start");
|
|
if !p.eat(USE_KW) {
|
|
use_start.abandon(p);
|
|
return None;
|
|
}
|
|
|
|
if use_pat(p).is_none() {
|
|
p.start("expected_use_pat")
|
|
.error(p, SyntaxError::Expected(vec![USE_PAT]));
|
|
}
|
|
|
|
let use_item = use_start.complete(p, USE);
|
|
Some(if p.eat(SEMICOLON) {
|
|
use_item
|
|
} else if TOP_LEVEL_ITEM_START.contains(p.current()) || p.at(EOF) {
|
|
use_item
|
|
.precede(p, "unterminated_tl_item")
|
|
.error(p, SyntaxError::UnterminatedTopLevelItem)
|
|
} else {
|
|
use_item
|
|
.precede(p, "err_unexpected")
|
|
.error(p, SyntaxError::Expected(vec![SEMICOLON]))
|
|
})
|
|
}
|
|
|
|
fn use_pat(p: &mut Parser) -> Option<CompletedMarker> {
|
|
let use_pat_marker = p.start("use_pat");
|
|
if !p.eat(IDENT) {
|
|
return None;
|
|
}
|
|
|
|
loop {
|
|
if p.eat(PATH_SEP) {
|
|
if pat_item(p).is_none() {
|
|
break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath));
|
|
}
|
|
} else if p.at(SEMICOLON) && p.nth_at(1, COLON) {
|
|
let broken_sep = p.start("broken_path_sep");
|
|
let wrong_semi = p.start("semi_typo");
|
|
p.eat(SEMICOLON);
|
|
wrong_semi.error(p, SyntaxError::PathSepContainsSemicolon);
|
|
p.eat(COLON);
|
|
broken_sep.complete(p, PATH_SEP);
|
|
if pat_item(p).is_none() {
|
|
break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath));
|
|
}
|
|
} else if p.at(COLON) && p.nth_at(1, SEMICOLON) {
|
|
let broken_sep = p.start("broken_path_sep");
|
|
p.eat(COLON);
|
|
let wrong_semi = p.start("semi_typo");
|
|
p.eat(SEMICOLON);
|
|
wrong_semi.error(p, SyntaxError::PathSepContainsSemicolon);
|
|
broken_sep.complete(p, PATH_SEP);
|
|
if pat_item(p).is_none() {
|
|
break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath));
|
|
}
|
|
} else if p.at(SEMICOLON) && p.nth_at(1, SEMICOLON) {
|
|
let broken_sep = p.start("broken_path_sep");
|
|
p.eat(SEMICOLON);
|
|
p.eat(SEMICOLON);
|
|
broken_sep
|
|
.complete(p, PATH_SEP)
|
|
.precede(p, "semi_typo_err")
|
|
.error(p, SyntaxError::PathSepContainsSemicolon);
|
|
if pat_item(p).is_none() {
|
|
break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath));
|
|
}
|
|
} else if p.eat(SEMICOLON) {
|
|
break Some(use_pat_marker.complete(p, USE_PAT));
|
|
} else {
|
|
break Some(use_pat_marker.error(p, SyntaxError::Expected(vec![PATH_SEP, SEMICOLON])));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn pat_item(p: &mut Parser) -> Option<CompletedMarker> {
|
|
let item_start = p.start("pat_item_start");
|
|
if p.eat(IDENT) {
|
|
Some(item_start.complete(p, PAT_ITEM))
|
|
} else if p.eat(STAR) {
|
|
Some(item_start.complete(p, PAT_GLOB))
|
|
} else if p.eat(L_BRACE) {
|
|
todo!("write PAT_GROUPs")
|
|
} else {
|
|
None
|
|
}
|
|
}
|