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

@ -475,6 +475,7 @@ impl Constant {
}
pub type TypedCallArg = CallArg<TypedExpr>;
pub type ParsedCallArg = CallArg<Option<UntypedExpr>>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CallArg<A> {

View File

@ -4,9 +4,9 @@ use vec1::Vec1;
use crate::{
ast::{
Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
DefinitionLocation, IfBranch, Pattern, RecordUpdateSpread, Span, TraceKind, TypedClause,
TypedRecordUpdateArg, UnOp, UntypedClause, UntypedRecordUpdateArg,
self, Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
DefinitionLocation, IfBranch, ParsedCallArg, Pattern, RecordUpdateSpread, Span, TraceKind,
TypedClause, TypedRecordUpdateArg, UnOp, UntypedClause, UntypedRecordUpdateArg,
},
builtins::void,
parser::token::Base,
@ -564,6 +564,88 @@ impl UntypedExpr {
}
}
pub fn tuple_index(self, index: usize, location: Span) -> Self {
UntypedExpr::TupleIndex {
location: self.location().union(location),
index,
tuple: Box::new(self),
}
}
pub fn field_access(self, label: String, location: Span) -> Self {
UntypedExpr::FieldAccess {
location: self.location().union(location),
label,
container: Box::new(self),
}
}
pub fn call(self, args: Vec<ParsedCallArg>, location: Span) -> Self {
let mut holes = Vec::new();
let args = args
.into_iter()
.enumerate()
.map(|(index, a)| match a {
CallArg {
value: Some(value),
label,
location,
} => CallArg {
value,
label,
location,
},
CallArg {
value: None,
label,
location,
} => {
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: self.location().union(location),
fun: Box::new(self),
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,
}
}
}
pub fn append_in_sequence(self, next: Self) -> Self {
let location = Span {
start: self.location().start,

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 {
.map_with_span(|(label, value), location| CallArg {
label,
location: span,
value,
}))
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),
},
}
}
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),
})
.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),
.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()),