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:
parent
549cf22cdd
commit
346df47232
|
@ -475,6 +475,7 @@ impl Constant {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TypedCallArg = CallArg<TypedExpr>;
|
pub type TypedCallArg = CallArg<TypedExpr>;
|
||||||
|
pub type ParsedCallArg = CallArg<Option<UntypedExpr>>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct CallArg<A> {
|
pub struct CallArg<A> {
|
||||||
|
|
|
@ -4,9 +4,9 @@ use vec1::Vec1;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
|
self, Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
|
||||||
DefinitionLocation, IfBranch, Pattern, RecordUpdateSpread, Span, TraceKind, TypedClause,
|
DefinitionLocation, IfBranch, ParsedCallArg, Pattern, RecordUpdateSpread, Span, TraceKind,
|
||||||
TypedRecordUpdateArg, UnOp, UntypedClause, UntypedRecordUpdateArg,
|
TypedClause, TypedRecordUpdateArg, UnOp, UntypedClause, UntypedRecordUpdateArg,
|
||||||
},
|
},
|
||||||
builtins::void,
|
builtins::void,
|
||||||
parser::token::Base,
|
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 {
|
pub fn append_in_sequence(self, next: Self) -> Self {
|
||||||
let location = Span {
|
let location = Span {
|
||||||
start: self.location().start,
|
start: self.location().start,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use chumsky::prelude::*;
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
use super::{Chain, ParserArg};
|
use super::Chain;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast,
|
ast::CallArg,
|
||||||
expr::UntypedExpr,
|
expr::UntypedExpr,
|
||||||
parser::{token::Token, ParseError},
|
parser::{token::Token, ParseError},
|
||||||
};
|
};
|
||||||
|
@ -15,20 +15,19 @@ pub(crate) fn parser(
|
||||||
.then_ignore(just(Token::Colon))
|
.then_ignore(just(Token::Colon))
|
||||||
.or_not()
|
.or_not()
|
||||||
.then(expression)
|
.then(expression)
|
||||||
.map_with_span(|(label, value), span| {
|
.map_with_span(|(label, value), location| CallArg {
|
||||||
ParserArg::Arg(Box::new(ast::CallArg {
|
label,
|
||||||
label,
|
location,
|
||||||
location: span,
|
value: Some(value),
|
||||||
value,
|
|
||||||
}))
|
|
||||||
}),
|
}),
|
||||||
select! { Token::Name { name } => name }
|
select! { Token::Name { name } => name }
|
||||||
.then_ignore(just(Token::Colon))
|
.then_ignore(just(Token::Colon))
|
||||||
.or_not()
|
.or_not()
|
||||||
.then_ignore(select! {Token::DiscardName {name} => name })
|
.then_ignore(select! {Token::DiscardName {name} => name })
|
||||||
.map_with_span(|label, span| ParserArg::Hole {
|
.map_with_span(|label, location| CallArg {
|
||||||
location: span,
|
location,
|
||||||
label,
|
label,
|
||||||
|
value: None,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
.separated_by(just(Token::Comma))
|
.separated_by(just(Token::Comma))
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
use crate::ast::{self, Span};
|
use crate::ast::{ParsedCallArg, Span};
|
||||||
use crate::expr::UntypedExpr;
|
|
||||||
|
|
||||||
pub(crate) mod call;
|
pub(crate) mod call;
|
||||||
pub(crate) mod field_access;
|
pub(crate) mod field_access;
|
||||||
pub(crate) mod tuple_index;
|
pub(crate) mod tuple_index;
|
||||||
|
|
||||||
pub(crate) enum Chain {
|
pub(crate) enum Chain {
|
||||||
Call(Vec<ParserArg>, Span),
|
Call(Vec<ParsedCallArg>, Span),
|
||||||
FieldAccess(String, Span),
|
FieldAccess(String, Span),
|
||||||
TupleIndex(usize, 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>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,13 +16,9 @@ use super::var::parser as var;
|
||||||
use super::when::parser as when;
|
use super::when::parser as when;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{self, Span},
|
expr::UntypedExpr,
|
||||||
expr::{FnStyle, UntypedExpr},
|
|
||||||
parser::{
|
parser::{
|
||||||
chain::{
|
chain::{call::parser as call, field_access, tuple_index::parser as tuple_index, Chain},
|
||||||
call::parser as call, field_access, tuple_index::parser as tuple_index, Chain,
|
|
||||||
ParserArg,
|
|
||||||
},
|
|
||||||
error::ParseError,
|
error::ParseError,
|
||||||
token::Token,
|
token::Token,
|
||||||
},
|
},
|
||||||
|
@ -40,71 +36,17 @@ pub fn parser<'a>(
|
||||||
chain_start(sequence, expression)
|
chain_start(sequence, expression)
|
||||||
.then(chain.repeated())
|
.then(chain.repeated())
|
||||||
.foldl(|expr, chain| match chain {
|
.foldl(|expr, chain| match chain {
|
||||||
Chain::Call(args, span) => {
|
Chain::Call(args, span) => expr.call(args, span),
|
||||||
let mut holes = Vec::new();
|
Chain::FieldAccess(label, span) => expr.field_access(label, span),
|
||||||
|
Chain::TupleIndex(index, span) => expr.tuple_index(index, span),
|
||||||
let args = args
|
})
|
||||||
.into_iter()
|
.then(just(Token::Question).or_not())
|
||||||
.enumerate()
|
.map_with_span(|(value, token), location| match token {
|
||||||
.map(|(index, a)| match a {
|
Some(_) => UntypedExpr::TraceIfFalse {
|
||||||
ParserArg::Arg(arg) => *arg,
|
value: Box::new(value),
|
||||||
ParserArg::Hole { location, 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: 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),
|
|
||||||
},
|
},
|
||||||
|
None => value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,16 +53,6 @@ pub fn pure_expression<'a>(
|
||||||
sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
|
sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
expression: Recursive<'a, Token, UntypedExpr, ParseError>,
|
expression: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
|
) -> 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
|
// Negate
|
||||||
let op = choice((
|
let op = choice((
|
||||||
just(Token::Bang).to(ast::UnOp::Not),
|
just(Token::Bang).to(ast::UnOp::Not),
|
||||||
|
@ -85,7 +75,7 @@ pub fn pure_expression<'a>(
|
||||||
let unary = op
|
let unary = op
|
||||||
.map_with_span(|op, span| (op, span))
|
.map_with_span(|op, span| (op, span))
|
||||||
.repeated()
|
.repeated()
|
||||||
.then(chained_debugged)
|
.then(chained(sequence, expression))
|
||||||
.foldr(|(un_op, span), value| UntypedExpr::UnOp {
|
.foldr(|(un_op, span), value| UntypedExpr::UnOp {
|
||||||
op: un_op,
|
op: un_op,
|
||||||
location: span.union(value.location()),
|
location: span.union(value.location()),
|
||||||
|
|
Loading…
Reference in New Issue