forked from katzen-cafe/iowo
lang: finish module/top level syntax
This commit is contained in:
parent
946ac879a7
commit
0de076ace1
4 changed files with 86 additions and 3 deletions
|
@ -10,4 +10,6 @@ pub enum SyntaxError {
|
||||||
CommaInMatOrVec,
|
CommaInMatOrVec,
|
||||||
UnterminatedTopLevelItem,
|
UnterminatedTopLevelItem,
|
||||||
UnclosedModuleBody,
|
UnclosedModuleBody,
|
||||||
|
UnfinishedPath,
|
||||||
|
PathSepContainsSemicolon,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub fn mod_body(p: &mut Parser) {
|
||||||
fn mod_decl(p: &mut Parser) -> Option<CompletedMarker> {
|
fn mod_decl(p: &mut Parser) -> Option<CompletedMarker> {
|
||||||
let mod_start = p.start("module");
|
let mod_start = p.start("module");
|
||||||
if !p.eat(MOD_KW) {
|
if !p.eat(MOD_KW) {
|
||||||
|
mod_start.abandon(p);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ pub fn top_level_item(p: &mut Parser) -> Option<CompletedMarker> {
|
||||||
if !TOP_LEVEL_ITEM_START.contains(p.current()) {
|
if !TOP_LEVEL_ITEM_START.contains(p.current()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
def(p).or_else(|| mod_decl(p))
|
def(p).or_else(|| mod_decl(p)).or_else(|| r#use(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn def(p: &mut Parser) -> Option<CompletedMarker> {
|
fn def(p: &mut Parser) -> Option<CompletedMarker> {
|
||||||
|
@ -98,9 +99,81 @@ fn def(p: &mut Parser) -> Option<CompletedMarker> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn r#use(p: &mut Parser) -> Option<CompletedMarker> {
|
fn r#use(p: &mut Parser) -> Option<CompletedMarker> {
|
||||||
|
let use_start = p.start("use_start");
|
||||||
if !p.eat(USE_KW) {
|
if !p.eat(USE_KW) {
|
||||||
|
use_start.abandon(p);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
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, DEF);
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
} 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);
|
||||||
|
} else if p.at(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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,14 +27,20 @@ pub enum SyntaxKind {
|
||||||
#[token("in")]
|
#[token("in")]
|
||||||
IN_KW,
|
IN_KW,
|
||||||
LET_IN,
|
LET_IN,
|
||||||
|
#[token("::")]
|
||||||
|
PATH_SEP,
|
||||||
#[token("mod")]
|
#[token("mod")]
|
||||||
MOD_KW,
|
MOD_KW,
|
||||||
MODULE,
|
MODULE,
|
||||||
MODULE_NAME,
|
MODULE_NAME,
|
||||||
MODULE_BODY,
|
MODULE_BODY,
|
||||||
|
USE,
|
||||||
#[token("use")]
|
#[token("use")]
|
||||||
USE_KW,
|
USE_KW,
|
||||||
USE_PAT,
|
USE_PAT,
|
||||||
|
PAT_ITEM,
|
||||||
|
PAT_GLOB,
|
||||||
|
PAT_GROUP,
|
||||||
#[regex("[\\d]+")]
|
#[regex("[\\d]+")]
|
||||||
INT_NUM,
|
INT_NUM,
|
||||||
#[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)")]
|
#[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)")]
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
def hello_world = meow [ 1 2 ]
|
use hello::meow;
|
||||||
|
|
||||||
|
def hello_world = meow [ 1 2 ];
|
||||||
|
|
||||||
def test
|
def test
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue