Parse binary operators

For now, they're all left associative.
This commit is contained in:
Joscha 2022-11-19 12:40:49 +01:00
parent 63f8026007
commit 408219073a
10 changed files with 99 additions and 39 deletions

View file

@ -2,7 +2,8 @@
use chumsky::prelude::*;
use crate::ast::Expr;
use crate::ast::{BinOp, Expr};
use crate::span::HasSpan;
use super::basic::{space, Error};
use super::lit::lit;
@ -14,7 +15,7 @@ use super::var::var;
fn atom_paren(
expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Expr, Error = Error> {
) -> impl Parser<char, Expr, Error = Error> + Clone {
just('(')
.ignore_then(space())
.then(expr)
@ -30,7 +31,7 @@ fn atom_paren(
fn atom(
expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Expr, Error = Error> {
) -> impl Parser<char, Expr, Error = Error> + Clone {
let lit = lit(expr.clone()).map(Expr::Lit);
let var = var(expr.clone()).map(Expr::Var);
let table_constr = table_constr(expr.clone()).map(Expr::TableConstr);
@ -41,8 +42,57 @@ fn atom(
prefixed(suffixed(base, expr))
}
fn left_assoc(
op: impl Parser<char, BinOp, Error = Error> + Clone,
over: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Expr, Error = Error> + Clone {
let op_over = space()
.then(op)
.then(space())
.then(over.clone())
.map(|(((s0, op), s1), right)| (s0, op, s1, right));
over.then(op_over.repeated())
.foldl(|left, (s0, op, s1, right)| Expr::BinOp {
span: left.span().join(right.span()),
left: Box::new(left),
s0,
op,
s1,
right: Box::new(right),
})
}
pub fn expr(
expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Expr, Error = Error> {
atom(expr)
// * / %
let prec0 = (just('*').to(BinOp::Mul))
.or(just('/').to(BinOp::Div))
.or(just('%').to(BinOp::Mod));
// + -
let prec1 = (just('+').to(BinOp::Add)).or(just('-').to(BinOp::Sub));
// == != > >= < <=
let prec2 = (just("==").to(BinOp::Eq))
.or(just("!=").to(BinOp::Neq))
.or(just('>').to(BinOp::Gt))
.or(just(">=").to(BinOp::Ge))
.or(just('<').to(BinOp::Lt))
.or(just("<=").to(BinOp::Le));
// and
let prec3 = text::keyword("and").to(BinOp::And);
// or
let prec4 = text::keyword("or").to(BinOp::Or);
left_assoc(
prec4,
left_assoc(
prec3,
left_assoc(prec2, left_assoc(prec1, left_assoc(prec0, atom(expr)))),
),
)
}