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:
KtorZ 2023-07-05 15:18:07 +02:00
parent 4f6defcf3e
commit a306d6e9f2
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
7 changed files with 255 additions and 180 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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