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;
|
||||
pub mod chain;
|
||||
pub mod definition;
|
||||
pub mod error;
|
||||
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;
|
||||
mod block;
|
||||
pub(crate) mod bytearray;
|
||||
mod chained;
|
||||
mod if_else;
|
||||
mod int;
|
||||
mod list;
|
||||
|
@ -17,10 +18,10 @@ mod tuple;
|
|||
mod var;
|
||||
pub mod when;
|
||||
|
||||
use anonymous_binop::parser as anonymous_binop;
|
||||
pub use anonymous_function::parser as anonymous_function;
|
||||
pub use block::parser as block;
|
||||
pub use bytearray::parser as bytearray;
|
||||
pub use chained::parser as chained;
|
||||
pub use if_else::parser as if_else;
|
||||
pub use int::parser as int;
|
||||
pub use list::parser as list;
|
||||
|
@ -32,195 +33,22 @@ pub use tuple::parser as tuple;
|
|||
pub use var::parser as var;
|
||||
pub use when::parser as when;
|
||||
|
||||
use crate::{
|
||||
ast::{self, Span},
|
||||
expr::{FnStyle, UntypedExpr},
|
||||
};
|
||||
|
||||
use super::{error::ParseError, token::Token};
|
||||
use crate::{ast, expr::UntypedExpr};
|
||||
|
||||
pub fn parser(
|
||||
sequence: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
recursive(|expression| {
|
||||
let field_access_constructor = 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,
|
||||
}),
|
||||
});
|
||||
|
||||
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 {
|
||||
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((
|
||||
|
@ -244,7 +72,7 @@ pub fn parser(
|
|||
let unary = op
|
||||
.map_with_span(|op, span| (op, span))
|
||||
.repeated()
|
||||
.then(debug)
|
||||
.then(chained_debugged)
|
||||
.foldr(|(un_op, span), value| UntypedExpr::UnOp {
|
||||
op: un_op,
|
||||
location: span.union(value.location()),
|
||||
|
|
Loading…
Reference in New Issue