forked from katzen-cafe/iowo
lang: add pipelines and rename parser to lst_parser
This commit is contained in:
parent
db2643359c
commit
30f17773a8
18 changed files with 108 additions and 47 deletions
81
crates/lang/src/lst_parser/grammar/expression.rs
Normal file
81
crates/lang/src/lst_parser/grammar/expression.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use crate::lst_parser::{error::SyntaxError, syntax_kind::SyntaxKind::*, CompletedMarker, Parser};
|
||||
|
||||
use self::{collection::collection, instruction::instr, lit::literal, pipeline::PIPES};
|
||||
|
||||
mod collection;
|
||||
mod instruction;
|
||||
mod lit;
|
||||
mod pipeline {
|
||||
use enumset::enum_set;
|
||||
|
||||
use crate::lst_parser::{
|
||||
error::SyntaxError,
|
||||
syntax_kind::{SyntaxKind::*, TokenSet},
|
||||
CompletedMarker, Parser,
|
||||
};
|
||||
|
||||
use super::expression;
|
||||
|
||||
pub fn pipeline(p: &mut Parser, start_expr: CompletedMarker) -> Option<CompletedMarker> {
|
||||
if !pipe(p) {
|
||||
return Some(start_expr);
|
||||
}
|
||||
let pipeline_marker = start_expr.precede(p, "pipeline_start");
|
||||
|
||||
loop {
|
||||
if expression(p, true).is_none() {
|
||||
return Some(pipeline_marker.complete_err(p, SyntaxError::PipelineNeedsSink));
|
||||
}
|
||||
if !pipe(p) {
|
||||
return Some(pipeline_marker.complete(p, PIPELINE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const PIPES: TokenSet = enum_set!(PIPE | MAPPING_PIPE | NULL_PIPE);
|
||||
|
||||
fn pipe(p: &mut Parser) -> bool {
|
||||
if PIPES.contains(p.current()) {
|
||||
p.do_bump();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expression(p: &mut Parser, in_pipe: bool) -> Option<CompletedMarker> {
|
||||
let expr = p.start("expr");
|
||||
|
||||
if atom(p).or_else(|| instr(p)).is_none() {
|
||||
expr.abandon(p);
|
||||
return None;
|
||||
}
|
||||
|
||||
let r = expr.complete(p, EXPR);
|
||||
|
||||
if PIPES.contains(p.current()) && !in_pipe {
|
||||
pipeline::pipeline(p, r)
|
||||
} else {
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn atom(p: &mut Parser) -> Option<CompletedMarker> {
|
||||
literal(p)
|
||||
.or_else(|| collection(p))
|
||||
.or_else(|| parenthesized_expr(p))
|
||||
}
|
||||
|
||||
pub fn parenthesized_expr(p: &mut Parser) -> Option<CompletedMarker> {
|
||||
if p.eat(L_PAREN) {
|
||||
let par_expr = p.start("parenthesized");
|
||||
expression(p, false);
|
||||
if !p.eat(R_PAREN) {
|
||||
return Some(par_expr.complete_err(p, SyntaxError::Expected(vec![R_PAREN])));
|
||||
}
|
||||
|
||||
return Some(par_expr.complete(p, PARENTHESIZED_EXPR));
|
||||
}
|
||||
None
|
||||
}
|
25
crates/lang/src/lst_parser/grammar/expression/collection.rs
Normal file
25
crates/lang/src/lst_parser/grammar/expression/collection.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use enumset::enum_set;
|
||||
|
||||
use crate::lst_parser::{
|
||||
syntax_kind::{SyntaxKind::*, TokenSet},
|
||||
CompletedMarker, Parser,
|
||||
};
|
||||
|
||||
use self::{attr_set::attr_set, vec::vec_matrix_list};
|
||||
|
||||
mod attr_set;
|
||||
mod vec;
|
||||
|
||||
const COLLECTION_START: TokenSet = enum_set!(L_BRACK | L_BRACE);
|
||||
|
||||
pub fn collection(p: &mut Parser) -> Option<CompletedMarker> {
|
||||
if !COLLECTION_START.contains(p.current()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(match p.current() {
|
||||
L_BRACK => vec_matrix_list(p),
|
||||
L_BRACE => attr_set(p),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
use crate::lst_parser::{
|
||||
error::SyntaxError,
|
||||
grammar::expression::{atom, expression},
|
||||
CompletedMarker, Marker, Parser,
|
||||
SyntaxKind::*,
|
||||
};
|
||||
|
||||
pub fn attr_set(p: &mut Parser) -> CompletedMarker {
|
||||
let start = p.start("attr_set_start");
|
||||
assert!(p.eat(L_BRACE));
|
||||
|
||||
loop {
|
||||
if attr(p).is_some() {
|
||||
// TODO: handle others
|
||||
if p.eat(COMMA) {
|
||||
continue;
|
||||
} else if p.eat(R_BRACE) {
|
||||
return start.complete(p, ATTR_SET);
|
||||
}
|
||||
// TODO: check for newline and stuff following that for recov of others
|
||||
} else if p.eat(R_BRACE) {
|
||||
return start.complete(p, ATTR_SET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn attr(p: &mut Parser) -> Option<CompletedMarker> {
|
||||
if p.at(IDENT) {
|
||||
let attr_start = p.start("attr");
|
||||
let attr_name_start = p.start("attr_name");
|
||||
p.do_bump();
|
||||
attr_name_start.complete(p, ATTR_NAME);
|
||||
|
||||
// TODO: handle comma, expr/atom, other
|
||||
p.eat(COLON);
|
||||
|
||||
// TODO: handle failed expr parser too
|
||||
let attr_value = p.start("attr_value");
|
||||
let _ = expression(p, false);
|
||||
attr_value.complete(p, ATTR_VALUE);
|
||||
Some(attr_start.complete(p, ATTR))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
use crate::lst_parser::{
|
||||
error::SyntaxError, grammar::expression::atom, CompletedMarker, Marker, Parser, SyntaxKind::*,
|
||||
};
|
||||
|
||||
pub fn vec_matrix_list(p: &mut Parser) -> CompletedMarker {
|
||||
let start = p.start("vec_matrix_list_start");
|
||||
assert!(p.eat(L_BRACK));
|
||||
let row_start = p.start("matrix_row_start");
|
||||
if let Some(item) = atom(p) {
|
||||
item.precede(p, "coll_item_start")
|
||||
.complete(p, COLLECTION_ITEM);
|
||||
|
||||
if p.at(COMMA) {
|
||||
row_start.abandon(p);
|
||||
return finish_list(p, start);
|
||||
}
|
||||
|
||||
finish_mat_or_vec(p, start, row_start)
|
||||
} else if p.eat(R_BRACK) {
|
||||
start.complete(p, LIST)
|
||||
} else {
|
||||
start.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK]))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle semicolons, other wrong toks
|
||||
fn finish_list(p: &mut Parser, list_start: Marker) -> CompletedMarker {
|
||||
loop {
|
||||
if p.eat(COMMA) {
|
||||
if let Some(item) = atom(p) {
|
||||
item.precede(p, "coll_item_start")
|
||||
.complete(p, COLLECTION_ITEM);
|
||||
} else if p.eat(R_BRACK) {
|
||||
return list_start.complete(p, LIST);
|
||||
}
|
||||
} else if p.eat(R_BRACK) {
|
||||
return list_start.complete(p, LIST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle commas, general other wrong toks
|
||||
fn finish_mat_or_vec(p: &mut Parser, coll_start: Marker, mut row_start: Marker) -> CompletedMarker {
|
||||
let mut is_matrix = false;
|
||||
let mut row_item_count = 1;
|
||||
loop {
|
||||
if let Some(item) = atom(p) {
|
||||
item.precede(p, "coll_item_start")
|
||||
.complete(p, COLLECTION_ITEM);
|
||||
row_item_count += 1;
|
||||
} else if p.at(SEMICOLON) {
|
||||
is_matrix = true;
|
||||
row_start.complete(p, MAT_ROW);
|
||||
p.eat(SEMICOLON);
|
||||
row_start = p.start("matrix_row_start");
|
||||
row_item_count = 0;
|
||||
} else if p.at(R_BRACK) {
|
||||
if is_matrix && row_item_count == 0 {
|
||||
row_start.abandon(p);
|
||||
p.eat(R_BRACK);
|
||||
return coll_start.complete(p, MATRIX);
|
||||
} else if is_matrix {
|
||||
row_start.complete(p, MAT_ROW);
|
||||
p.eat(R_BRACK);
|
||||
return coll_start.complete(p, MATRIX);
|
||||
} else {
|
||||
row_start.abandon(p);
|
||||
p.eat(R_BRACK);
|
||||
return coll_start.complete(p, VEC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
crates/lang/src/lst_parser/grammar/expression/instruction.rs
Normal file
34
crates/lang/src/lst_parser/grammar/expression/instruction.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use crate::lst_parser::{syntax_kind::SyntaxKind::*, CompletedMarker, Parser};
|
||||
|
||||
use super::{atom, lit::literal};
|
||||
|
||||
pub fn instr(p: &mut Parser) -> Option<CompletedMarker> {
|
||||
if !p.at(IDENT) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let instr = p.start("instr");
|
||||
|
||||
instr_name(p);
|
||||
instr_params(p);
|
||||
|
||||
Some(instr.complete(p, INSTR))
|
||||
}
|
||||
|
||||
fn instr_name(p: &mut Parser) {
|
||||
let instr_name = p.start("instr_name");
|
||||
|
||||
while p.at(IDENT) {
|
||||
p.do_bump();
|
||||
}
|
||||
|
||||
instr_name.complete(p, INSTR_NAME);
|
||||
}
|
||||
|
||||
fn instr_params(p: &mut Parser) {
|
||||
if let Some(start) = atom(p) {
|
||||
while atom(p).is_some() {}
|
||||
|
||||
start.precede(p, "params_start").complete(p, INSTR_PARAMS);
|
||||
}
|
||||
}
|
59
crates/lang/src/lst_parser/grammar/expression/lit.rs
Normal file
59
crates/lang/src/lst_parser/grammar/expression/lit.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use enumset::enum_set;
|
||||
use indoc::indoc;
|
||||
|
||||
use crate::lst_parser::{
|
||||
grammar::check_parser,
|
||||
syntax_kind::{SyntaxKind::*, TokenSet},
|
||||
CompletedMarker, Parser,
|
||||
};
|
||||
|
||||
const LIT_TOKENS: TokenSet = enum_set!(INT_NUM | FLOAT_NUM | STRING);
|
||||
|
||||
pub fn literal(p: &mut Parser) -> Option<CompletedMarker> {
|
||||
if !LIT_TOKENS.contains(p.current()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let lit = p.start("lit");
|
||||
|
||||
p.do_bump();
|
||||
|
||||
Some(lit.complete(p, LITERAL))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_lst_lit() {
|
||||
check_parser(
|
||||
"42",
|
||||
|p| {
|
||||
literal(p);
|
||||
},
|
||||
indoc! {r#"
|
||||
LITERAL {
|
||||
INT_NUM "42";
|
||||
}
|
||||
"#},
|
||||
);
|
||||
check_parser(
|
||||
"3.14",
|
||||
|p| {
|
||||
literal(p);
|
||||
},
|
||||
indoc! {r#"
|
||||
LITERAL {
|
||||
FLOAT_NUM "3.14";
|
||||
}
|
||||
"#},
|
||||
);
|
||||
check_parser(
|
||||
r#""Meow""#,
|
||||
|p| {
|
||||
literal(p);
|
||||
},
|
||||
indoc! {r#"
|
||||
LITERAL {
|
||||
STRING "\"Meow\"";
|
||||
}
|
||||
"#},
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue