Move chain and chained parsing into their own submodule
Alleviate a bit more the top-level expression parser. Note that we probably need a bit more disciplined in what we export and at what level because there doesn't seem to be much logic as for whether a parser is private, exported to the crate only or to the wide open. I'd be in favor of exporting everything by default.
This commit is contained in:
parent
4f6defcf3e
commit
a306d6e9f2
|
@ -1,4 +1,5 @@
|
||||||
mod annotation;
|
mod annotation;
|
||||||
|
pub mod chain;
|
||||||
pub mod definition;
|
pub mod definition;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
use super::{Chain, ParserArg};
|
||||||
|
use crate::{
|
||||||
|
ast,
|
||||||
|
expr::UntypedExpr,
|
||||||
|
parser::{token::Token, ParseError},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) fn parser<'a>(
|
||||||
|
expression: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
|
) -> impl Parser<Token, Chain, Error = ParseError> + 'a {
|
||||||
|
choice((
|
||||||
|
select! { Token::Name { name } => name }
|
||||||
|
.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,
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
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,
|
||||||
|
label,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
.separated_by(just(Token::Comma))
|
||||||
|
.allow_trailing()
|
||||||
|
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||||
|
.map_with_span(Chain::Call)
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
use super::Chain;
|
||||||
|
use crate::{
|
||||||
|
expr::UntypedExpr,
|
||||||
|
parser::{token::Token, ParseError},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) fn parser() -> impl Parser<Token, Chain, Error = ParseError> {
|
||||||
|
just(Token::Dot)
|
||||||
|
.ignore_then(select! {
|
||||||
|
Token::Name { name } => name,
|
||||||
|
})
|
||||||
|
.map_with_span(Chain::FieldAccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn constructor() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
|
||||||
|
select! {Token::Name { name } => name}
|
||||||
|
.map_with_span(|module, span| (module, span))
|
||||||
|
.then_ignore(just(Token::Dot))
|
||||||
|
.then(select! {Token::UpName { name } => name})
|
||||||
|
.map_with_span(|((module, m_span), name), span| UntypedExpr::FieldAccess {
|
||||||
|
location: span,
|
||||||
|
label: name,
|
||||||
|
container: Box::new(UntypedExpr::Var {
|
||||||
|
location: m_span,
|
||||||
|
name: module,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
use crate::ast::{self, Span};
|
||||||
|
use crate::expr::UntypedExpr;
|
||||||
|
|
||||||
|
pub(crate) mod call;
|
||||||
|
pub(crate) mod field_access;
|
||||||
|
pub(crate) mod tuple_index;
|
||||||
|
|
||||||
|
pub(crate) enum Chain {
|
||||||
|
Call(Vec<ParserArg>, 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>,
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
use super::Chain;
|
||||||
|
use crate::parser::{token::Token, ParseError};
|
||||||
|
|
||||||
|
pub(crate) fn parser() -> impl Parser<Token, Chain, Error = ParseError> {
|
||||||
|
just(Token::Dot)
|
||||||
|
.ignore_then(select! {
|
||||||
|
Token::Ordinal { index } => index,
|
||||||
|
})
|
||||||
|
.validate(|index, span, emit| {
|
||||||
|
if index < 1 {
|
||||||
|
emit(ParseError::invalid_tuple_index(
|
||||||
|
span,
|
||||||
|
index.to_string(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
Chain::TupleIndex(0, span)
|
||||||
|
} else {
|
||||||
|
Chain::TupleIndex(index as usize - 1, span)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
use super::anonymous_binop::parser as anonymous_binop;
|
||||||
|
use super::anonymous_function::parser as anonymous_function;
|
||||||
|
use super::assignment;
|
||||||
|
use super::block::parser as block;
|
||||||
|
use super::bytearray::parser as bytearray;
|
||||||
|
use super::if_else::parser as if_else;
|
||||||
|
use super::int::parser as int;
|
||||||
|
use super::list::parser as list;
|
||||||
|
use super::record::parser as record;
|
||||||
|
use super::record_update::parser as record_update;
|
||||||
|
use super::string::parser as string;
|
||||||
|
use super::tuple::parser as tuple;
|
||||||
|
use super::var::parser as var;
|
||||||
|
use super::when::parser as when;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::{self, Span},
|
||||||
|
expr::{FnStyle, UntypedExpr},
|
||||||
|
parser::{
|
||||||
|
chain::{
|
||||||
|
call::parser as call, field_access, tuple_index::parser as tuple_index, Chain,
|
||||||
|
ParserArg,
|
||||||
|
},
|
||||||
|
error::ParseError,
|
||||||
|
token::Token,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parser<'a>(
|
||||||
|
sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
|
expression: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
|
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
|
||||||
|
let chain = choice((
|
||||||
|
tuple_index(),
|
||||||
|
field_access::parser(),
|
||||||
|
call(expression.clone()),
|
||||||
|
));
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chain_start<'a>(
|
||||||
|
sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
|
expression: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
|
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
|
||||||
|
choice((
|
||||||
|
string(),
|
||||||
|
int(),
|
||||||
|
record_update(expression.clone()),
|
||||||
|
record(expression.clone()),
|
||||||
|
field_access::constructor(),
|
||||||
|
var(),
|
||||||
|
tuple(expression.clone()),
|
||||||
|
bytearray(),
|
||||||
|
list(expression.clone()),
|
||||||
|
anonymous_function(sequence.clone()),
|
||||||
|
anonymous_binop(),
|
||||||
|
block(sequence.clone()),
|
||||||
|
when(expression.clone()),
|
||||||
|
assignment::let_(expression.clone()),
|
||||||
|
assignment::expect(expression.clone()),
|
||||||
|
if_else(sequence, expression.clone()),
|
||||||
|
))
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ pub mod anonymous_function;
|
||||||
pub mod assignment;
|
pub mod assignment;
|
||||||
mod block;
|
mod block;
|
||||||
pub(crate) mod bytearray;
|
pub(crate) mod bytearray;
|
||||||
|
mod chained;
|
||||||
mod if_else;
|
mod if_else;
|
||||||
mod int;
|
mod int;
|
||||||
mod list;
|
mod list;
|
||||||
|
@ -17,10 +18,10 @@ mod tuple;
|
||||||
mod var;
|
mod var;
|
||||||
pub mod when;
|
pub mod when;
|
||||||
|
|
||||||
use anonymous_binop::parser as anonymous_binop;
|
|
||||||
pub use anonymous_function::parser as anonymous_function;
|
pub use anonymous_function::parser as anonymous_function;
|
||||||
pub use block::parser as block;
|
pub use block::parser as block;
|
||||||
pub use bytearray::parser as bytearray;
|
pub use bytearray::parser as bytearray;
|
||||||
|
pub use chained::parser as chained;
|
||||||
pub use if_else::parser as if_else;
|
pub use if_else::parser as if_else;
|
||||||
pub use int::parser as int;
|
pub use int::parser as int;
|
||||||
pub use list::parser as list;
|
pub use list::parser as list;
|
||||||
|
@ -32,195 +33,22 @@ pub use tuple::parser as tuple;
|
||||||
pub use var::parser as var;
|
pub use var::parser as var;
|
||||||
pub use when::parser as when;
|
pub use when::parser as when;
|
||||||
|
|
||||||
use crate::{
|
|
||||||
ast::{self, Span},
|
|
||||||
expr::{FnStyle, UntypedExpr},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{error::ParseError, token::Token};
|
use super::{error::ParseError, token::Token};
|
||||||
|
use crate::{ast, expr::UntypedExpr};
|
||||||
|
|
||||||
pub fn parser(
|
pub fn parser(
|
||||||
sequence: Recursive<'_, Token, UntypedExpr, ParseError>,
|
sequence: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||||
recursive(|expression| {
|
recursive(|expression| {
|
||||||
let field_access_constructor = select! {Token::Name { name } => name}
|
let chained_debugged = chained(sequence, expression)
|
||||||
.map_with_span(|module, span| (module, span))
|
.then(just(Token::Question).or_not())
|
||||||
.then_ignore(just(Token::Dot))
|
.map_with_span(|(value, token), location| match token {
|
||||||
.then(select! {Token::UpName { name } => name})
|
|
||||||
.map_with_span(|((module, m_span), name), span| UntypedExpr::FieldAccess {
|
|
||||||
location: span,
|
|
||||||
label: name,
|
|
||||||
container: Box::new(UntypedExpr::Var {
|
|
||||||
location: m_span,
|
|
||||||
name: module,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
let expr_unit_parser = choice((
|
|
||||||
string(),
|
|
||||||
int(),
|
|
||||||
record_update(expression.clone()),
|
|
||||||
record(expression.clone()),
|
|
||||||
field_access_constructor,
|
|
||||||
var(),
|
|
||||||
tuple(expression.clone()),
|
|
||||||
bytearray(),
|
|
||||||
list(expression.clone()),
|
|
||||||
anonymous_function(sequence.clone()),
|
|
||||||
anonymous_binop(),
|
|
||||||
block(sequence.clone()),
|
|
||||||
when(expression.clone()),
|
|
||||||
assignment::let_(expression.clone()),
|
|
||||||
assignment::expect(expression.clone()),
|
|
||||||
if_else(sequence, expression.clone()),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Parsing a function call into the appropriate structure
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ParserArg {
|
|
||||||
Arg(Box<ast::CallArg<UntypedExpr>>),
|
|
||||||
Hole {
|
|
||||||
location: Span,
|
|
||||||
label: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Chain {
|
|
||||||
Call(Vec<ParserArg>, Span),
|
|
||||||
FieldAccess(String, Span),
|
|
||||||
TupleIndex(usize, Span),
|
|
||||||
}
|
|
||||||
|
|
||||||
let field_access_parser = just(Token::Dot)
|
|
||||||
.ignore_then(select! {
|
|
||||||
Token::Name { name } => name,
|
|
||||||
})
|
|
||||||
.map_with_span(Chain::FieldAccess);
|
|
||||||
|
|
||||||
let tuple_index_parser = just(Token::Dot)
|
|
||||||
.ignore_then(select! {
|
|
||||||
Token::Ordinal { index } => index,
|
|
||||||
})
|
|
||||||
.validate(|index, span, emit| {
|
|
||||||
if index < 1 {
|
|
||||||
emit(ParseError::invalid_tuple_index(
|
|
||||||
span,
|
|
||||||
index.to_string(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
Chain::TupleIndex(0, span)
|
|
||||||
} else {
|
|
||||||
Chain::TupleIndex(index as usize - 1, span)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let call_parser = choice((
|
|
||||||
select! { Token::Name { name } => name }
|
|
||||||
.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,
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
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,
|
|
||||||
label,
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
.separated_by(just(Token::Comma))
|
|
||||||
.allow_trailing()
|
|
||||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
|
||||||
.map_with_span(Chain::Call);
|
|
||||||
|
|
||||||
let chain = choice((tuple_index_parser, field_access_parser, call_parser));
|
|
||||||
|
|
||||||
let chained = expr_unit_parser
|
|
||||||
.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),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let debug = chained.then(just(Token::Question).or_not()).map_with_span(
|
|
||||||
|(value, token), location| match token {
|
|
||||||
Some(_) => UntypedExpr::TraceIfFalse {
|
Some(_) => UntypedExpr::TraceIfFalse {
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
location,
|
location,
|
||||||
},
|
},
|
||||||
None => value,
|
None => value,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// Negate
|
// Negate
|
||||||
let op = choice((
|
let op = choice((
|
||||||
|
@ -244,7 +72,7 @@ pub fn parser(
|
||||||
let unary = op
|
let unary = op
|
||||||
.map_with_span(|op, span| (op, span))
|
.map_with_span(|op, span| (op, span))
|
||||||
.repeated()
|
.repeated()
|
||||||
.then(debug)
|
.then(chained_debugged)
|
||||||
.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