aiken/crates/aiken-lang/src/parser/expr/mod.rs

238 lines
6.2 KiB
Rust

use chumsky::prelude::*;
use vec1::Vec1;
mod and_or_chain;
mod anonymous_binop;
pub mod anonymous_function;
pub mod assignment;
mod block;
pub(crate) mod bytearray;
mod chained;
mod fail_todo_trace;
mod if_else;
mod int;
mod list;
mod record;
mod record_update;
mod sequence;
pub mod string;
mod tuple;
mod var;
pub mod when;
pub use and_or_chain::parser as and_or_chain;
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 fail_todo_trace::parser as fail_todo_trace;
pub use if_else::parser as if_else;
pub use int::parser as int;
pub use list::parser as list;
pub use record::parser as record;
pub use record_update::parser as record_update;
pub use sequence::parser as sequence;
pub use string::parser as string;
pub use tuple::parser as tuple;
pub use var::parser as var;
pub use when::parser as when;
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| {
choice((
fail_todo_trace(expression.clone(), sequence.clone()),
pure_expression(sequence, expression),
))
})
}
pub fn pure_expression<'a>(
sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
expression: Recursive<'a, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
// Negate
let op = choice((
just(Token::Bang).to(ast::UnOp::Not),
choice((just(Token::Minus), just(Token::NewLineMinus)))
// NOTE: Prevent conflict with usage for '-' as a standalone binary op.
// This will make '-' parse when used as standalone binop in a function call.
// For example:
//
// foo(a, -, b)
//
// but it'll fail in a let-binding:
//
// let foo = -
//
// which seems acceptable.
.then_ignore(just(Token::Comma).not().rewind())
.to(ast::UnOp::Negate),
));
let unary = op
.map_with_span(|op, span| (op, span))
.repeated()
.then(chained(sequence, expression))
.foldr(|(un_op, span), value| UntypedExpr::UnOp {
op: un_op,
location: span.union(value.location()),
value: Box::new(value),
})
.boxed();
// Product
let op = choice((
just(Token::Star).to(ast::BinOp::MultInt),
just(Token::Slash).to(ast::BinOp::DivInt),
just(Token::Percent).to(ast::BinOp::ModInt),
));
let product = unary
.clone()
.then(op.then(unary).repeated())
.foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()),
name: op,
left: Box::new(a),
right: Box::new(b),
})
.boxed();
// Sum
let op = choice((
just(Token::Plus).to(ast::BinOp::AddInt),
just(Token::Minus).to(ast::BinOp::SubInt),
));
let sum = product
.clone()
.then(op.then(product).repeated())
.foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()),
name: op,
left: Box::new(a),
right: Box::new(b),
})
.boxed();
// Comparison
let op = choice((
just(Token::EqualEqual).to(ast::BinOp::Eq),
just(Token::NotEqual).to(ast::BinOp::NotEq),
just(Token::Less).to(ast::BinOp::LtInt),
just(Token::Greater).to(ast::BinOp::GtInt),
just(Token::LessEqual).to(ast::BinOp::LtEqInt),
just(Token::GreaterEqual).to(ast::BinOp::GtEqInt),
));
let comparison = sum
.clone()
.then(op.then(sum).repeated())
.foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()),
name: op,
left: Box::new(a),
right: Box::new(b),
})
.boxed();
// Conjunction
let op = just(Token::AmperAmper).to(ast::BinOp::And);
let conjunction = comparison
.clone()
.then(op.then(comparison).repeated())
.foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()),
name: op,
left: Box::new(a),
right: Box::new(b),
})
.boxed();
// Disjunction
let op = just(Token::VbarVbar).to(ast::BinOp::Or);
let disjunction = conjunction
.clone()
.then(op.then(conjunction).repeated())
.foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()),
name: op,
left: Box::new(a),
right: Box::new(b),
})
.boxed();
// Pipeline
disjunction
.clone()
.then(
choice((just(Token::Pipe), just(Token::NewLinePipe)))
.then(disjunction)
.repeated(),
)
.foldl(|l, (pipe, r)| {
if let UntypedExpr::PipeLine {
mut expressions,
one_liner,
} = l
{
expressions.push(r);
return UntypedExpr::PipeLine {
expressions,
one_liner,
};
}
let mut expressions = Vec1::new(l);
expressions.push(r);
UntypedExpr::PipeLine {
expressions,
one_liner: pipe != Token::NewLinePipe,
}
})
}
#[cfg(test)]
mod tests {
use crate::assert_expr;
#[test]
fn plus_binop() {
assert_expr!("a + 1");
}
#[test]
fn pipeline() {
assert_expr!(
r#"
a + 2
|> add_one
|> add_one
"#
);
}
#[test]
fn field_access() {
assert_expr!("user.name");
}
#[test]
fn function_invoke() {
assert_expr!(
r#"
let x = add_one(3)
let map_add_x = list.map(_, fn (y) { x + y })
map_add_x([ 1, 2, 3 ])
"#
);
}
}