lang: add pipelines and rename parser to lst_parser

This commit is contained in:
Schrottkatze 2024-04-30 12:21:06 +02:00
parent db2643359c
commit 30f17773a8
Signed by: schrottkatze
SSH key fingerprint: SHA256:hXb3t1vINBFCiDCmhRABHX5ocdbLiKyCdKI4HK2Rbbc
18 changed files with 108 additions and 47 deletions

View 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
}

View 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!(),
})
}

View file

@ -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
}
}

View file

@ -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);
}
}
}
}

View 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);
}
}

View 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\"";
}
"#},
);
}