Refactor chain parser

The main goal is to make the parser more reusable to be used for when-clauses, instead of the expression parser. A side goal has been to make it more readable by moving the construction of some untyped expression as method on UntypedExpr. Doing so, I got rid of the extra temporary 'ParseArg' type and re-used the generic 'CallArg' instead by simply using an Option<UntypedExpr> as value to get the same semantic as 'ParseArg' (which would distinguish between plain call args and holes). Now the chained parser is in a bit more reusable state.
This commit is contained in:
KtorZ
2023-07-06 14:11:32 +02:00
committed by Lucas
parent 549cf22cdd
commit 346df47232
6 changed files with 110 additions and 107 deletions

View File

@@ -1,8 +1,8 @@
use chumsky::prelude::*;
use super::{Chain, ParserArg};
use super::Chain;
use crate::{
ast,
ast::CallArg,
expr::UntypedExpr,
parser::{token::Token, ParseError},
};
@@ -15,20 +15,19 @@ pub(crate) fn parser(
.then_ignore(just(Token::Colon))
.or_not()
.then(expression)
.map_with_span(|(label, value), span| {
ParserArg::Arg(Box::new(ast::CallArg {
label,
location: span,
value,
}))
.map_with_span(|(label, value), location| CallArg {
label,
location,
value: Some(value),
}),
select! { Token::Name { name } => name }
.then_ignore(just(Token::Colon))
.or_not()
.then_ignore(select! {Token::DiscardName {name} => name })
.map_with_span(|label, span| ParserArg::Hole {
location: span,
.map_with_span(|label, location| CallArg {
location,
label,
value: None,
}),
))
.separated_by(just(Token::Comma))

View File

@@ -1,22 +1,11 @@
use crate::ast::{self, Span};
use crate::expr::UntypedExpr;
use crate::ast::{ParsedCallArg, Span};
pub(crate) mod call;
pub(crate) mod field_access;
pub(crate) mod tuple_index;
pub(crate) enum Chain {
Call(Vec<ParserArg>, Span),
Call(Vec<ParsedCallArg>, Span),
FieldAccess(String, Span),
TupleIndex(usize, Span),
}
// Parsing a function call into the appropriate structure
#[derive(Debug)]
pub(crate) enum ParserArg {
Arg(Box<ast::CallArg<UntypedExpr>>),
Hole {
location: Span,
label: Option<String>,
},
}

View File

@@ -16,13 +16,9 @@ use super::var::parser as var;
use super::when::parser as when;
use crate::{
ast::{self, Span},
expr::{FnStyle, UntypedExpr},
expr::UntypedExpr,
parser::{
chain::{
call::parser as call, field_access, tuple_index::parser as tuple_index, Chain,
ParserArg,
},
chain::{call::parser as call, field_access, tuple_index::parser as tuple_index, Chain},
error::ParseError,
token::Token,
},
@@ -40,71 +36,17 @@ pub fn parser<'a>(
chain_start(sequence, expression)
.then(chain.repeated())
.foldl(|expr, chain| match chain {
Chain::Call(args, span) => {
let mut holes = Vec::new();
let args = args
.into_iter()
.enumerate()
.map(|(index, a)| match a {
ParserArg::Arg(arg) => *arg,
ParserArg::Hole { location, label } => {
let name = format!("{}__{index}", ast::CAPTURE_VARIABLE);
holes.push(ast::Arg {
location: Span::empty(),
annotation: None,
arg_name: ast::ArgName::Named {
label: name.clone(),
name,
location: Span::empty(),
is_validator_param: false,
},
tipo: (),
});
ast::CallArg {
label,
location,
value: UntypedExpr::Var {
location,
name: format!("{}__{index}", ast::CAPTURE_VARIABLE),
},
}
}
})
.collect();
let call = UntypedExpr::Call {
location: expr.location().union(span),
fun: Box::new(expr),
arguments: args,
};
if holes.is_empty() {
call
} else {
UntypedExpr::Fn {
location: call.location(),
fn_style: FnStyle::Capture,
arguments: holes,
body: Box::new(call),
return_annotation: None,
}
}
}
Chain::FieldAccess(label, span) => UntypedExpr::FieldAccess {
location: expr.location().union(span),
label,
container: Box::new(expr),
},
Chain::TupleIndex(index, span) => UntypedExpr::TupleIndex {
location: expr.location().union(span),
index,
tuple: Box::new(expr),
Chain::Call(args, span) => expr.call(args, span),
Chain::FieldAccess(label, span) => expr.field_access(label, span),
Chain::TupleIndex(index, span) => expr.tuple_index(index, span),
})
.then(just(Token::Question).or_not())
.map_with_span(|(value, token), location| match token {
Some(_) => UntypedExpr::TraceIfFalse {
value: Box::new(value),
location,
},
None => value,
})
}

View File

@@ -53,16 +53,6 @@ pub fn pure_expression<'a>(
sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
expression: Recursive<'a, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
let chained_debugged = chained(sequence, expression)
.then(just(Token::Question).or_not())
.map_with_span(|(value, token), location| match token {
Some(_) => UntypedExpr::TraceIfFalse {
value: Box::new(value),
location,
},
None => value,
});
// Negate
let op = choice((
just(Token::Bang).to(ast::UnOp::Not),
@@ -85,7 +75,7 @@ pub fn pure_expression<'a>(
let unary = op
.map_with_span(|op, span| (op, span))
.repeated()
.then(chained_debugged)
.then(chained(sequence, expression))
.foldr(|(un_op, span), value| UntypedExpr::UnOp {
op: un_op,
location: span.union(value.location()),