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 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> {

View File

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

View File

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

View File

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

View File

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

View File

@ -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()),