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

@ -20,6 +20,14 @@ pub enum BinOp {
Eq, Eq,
/// `!=` /// `!=`
Neq, Neq,
/// `>`
Gt,
/// `>=`
Ge,
/// `<`
Lt,
/// `<=`
Le,
/// `and` /// `and`
And, And,
/// `or` /// `or`

View file

@ -1,3 +1,5 @@
// TODO Turn multiple calls to subparsers into clone-s
mod basic; mod basic;
mod expr; mod expr;
mod lit; mod lit;

View file

@ -10,17 +10,17 @@ pub type Error = Simple<char, Span>;
// TODO https://github.com/rust-lang/rust/issues/63063 // TODO https://github.com/rust-lang/rust/issues/63063
pub fn inline() -> impl Parser<char, (), Error = Error> { pub fn inline() -> impl Parser<char, (), Error = Error> + Clone {
filter(|c: &char| c.is_whitespace() && *c != '\n') filter(|c: &char| c.is_whitespace() && *c != '\n')
.repeated() .repeated()
.to(()) .to(())
} }
pub fn newline() -> impl Parser<char, (), Error = Error> { pub fn newline() -> impl Parser<char, (), Error = Error> + Clone {
just('\n').to(()) just('\n').to(())
} }
pub fn line() -> impl Parser<char, Line, Error = Error> { pub fn line() -> impl Parser<char, Line, Error = Error> + Clone {
let empty = newline().to(Line::Empty); let empty = newline().to(Line::Empty);
let comment = just('#') let comment = just('#')
@ -32,7 +32,7 @@ pub fn line() -> impl Parser<char, Line, Error = Error> {
empty.or(comment) empty.or(comment)
} }
pub fn space() -> impl Parser<char, Space, Error = Error> { pub fn space() -> impl Parser<char, Space, Error = Error> + Clone {
inline() inline()
.ignore_then(line()) .ignore_then(line())
.repeated() .repeated()
@ -40,7 +40,7 @@ pub fn space() -> impl Parser<char, Space, Error = Error> {
.map_with_span(|lines, span| Space { lines, span }) .map_with_span(|lines, span| Space { lines, span })
} }
pub fn ident() -> impl Parser<char, Ident, Error = Error> { pub fn ident() -> impl Parser<char, Ident, Error = Error> + Clone {
text::ident().try_map(|name, span| { text::ident().try_map(|name, span| {
if matches!( if matches!(
&name as &str, &name as &str,

View file

@ -2,7 +2,8 @@
use chumsky::prelude::*; use chumsky::prelude::*;
use crate::ast::Expr; use crate::ast::{BinOp, Expr};
use crate::span::HasSpan;
use super::basic::{space, Error}; use super::basic::{space, Error};
use super::lit::lit; use super::lit::lit;
@ -14,7 +15,7 @@ use super::var::var;
fn atom_paren( fn atom_paren(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Expr, Error = Error> { ) -> impl Parser<char, Expr, Error = Error> + Clone {
just('(') just('(')
.ignore_then(space()) .ignore_then(space())
.then(expr) .then(expr)
@ -30,7 +31,7 @@ fn atom_paren(
fn atom( fn atom(
expr: impl Parser<char, Expr, Error = Error> + Clone, 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 lit = lit(expr.clone()).map(Expr::Lit);
let var = var(expr.clone()).map(Expr::Var); let var = var(expr.clone()).map(Expr::Var);
let table_constr = table_constr(expr.clone()).map(Expr::TableConstr); let table_constr = table_constr(expr.clone()).map(Expr::TableConstr);
@ -41,8 +42,57 @@ fn atom(
prefixed(suffixed(base, expr)) 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( pub fn expr(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Expr, Error = Error> { ) -> 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)))),
),
)
} }

View file

@ -7,7 +7,7 @@ use crate::builtin::Builtin;
use super::basic::{ident, space, Error}; use super::basic::{ident, space, Error};
fn builtin_lit() -> impl Parser<char, Builtin, Error = Error> { fn builtin_lit() -> impl Parser<char, Builtin, Error = Error> + Clone {
just('\'').ignore_then(choice(( just('\'').ignore_then(choice((
text::keyword("get").to(Builtin::Get), text::keyword("get").to(Builtin::Get),
text::keyword("set").to(Builtin::Set), text::keyword("set").to(Builtin::Set),
@ -78,14 +78,14 @@ pub fn num_lit() -> impl Parser<char, NumLit, Error = Error> + Clone {
.map_with_span(|(value, str), span| NumLit { value, str, span }) .map_with_span(|(value, str), span| NumLit { value, str, span })
} }
pub fn string_lit() -> impl Parser<char, StringLit, Error = Error> { pub fn string_lit() -> impl Parser<char, StringLit, Error = Error> + Clone {
// TODO Parse string literals // TODO Parse string literals
filter(|_| false).map(|_| unreachable!()) filter(|_| false).map(|_| unreachable!())
} }
pub fn table_lit_elem( pub fn table_lit_elem(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, TableLitElem, Error = Error> { ) -> impl Parser<char, TableLitElem, Error = Error> + Clone {
let positional = expr let positional = expr
.clone() .clone()
.map(|value| TableLitElem::Positional(Box::new(value))); .map(|value| TableLitElem::Positional(Box::new(value)));
@ -108,7 +108,7 @@ pub fn table_lit_elem(
pub fn table_lit( pub fn table_lit(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, TableLit, Error = Error> { ) -> impl Parser<char, TableLit, Error = Error> + Clone {
let elem = space() let elem = space()
.then(table_lit_elem(expr)) .then(table_lit_elem(expr))
.then(space()) .then(space())
@ -130,7 +130,7 @@ pub fn table_lit(
pub fn lit( pub fn lit(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Lit, Error = Error> { ) -> impl Parser<char, Lit, Error = Error> + Clone {
let nil = text::keyword("nil").map_with_span(|_, span| Lit::Nil(span)); let nil = text::keyword("nil").map_with_span(|_, span| Lit::Nil(span));
let r#true = text::keyword("true").map_with_span(|_, span| Lit::Bool(true, span)); let r#true = text::keyword("true").map_with_span(|_, span| Lit::Bool(true, span));
let r#false = text::keyword("false").map_with_span(|_, span| Lit::Bool(false, span)); let r#false = text::keyword("false").map_with_span(|_, span| Lit::Bool(false, span));

View file

@ -35,14 +35,14 @@ impl Prefix {
} }
} }
fn prefix_neg() -> impl Parser<char, Prefix, Error = Error> { fn prefix_neg() -> impl Parser<char, Prefix, Error = Error> + Clone {
just('-') just('-')
.map_with_span(|_, span| span) .map_with_span(|_, span| span)
.then(space()) .then(space())
.map(|(minus, s0)| Prefix::Neg { minus, s0 }) .map(|(minus, s0)| Prefix::Neg { minus, s0 })
} }
fn prefix_not() -> impl Parser<char, Prefix, Error = Error> { fn prefix_not() -> impl Parser<char, Prefix, Error = Error> + Clone {
text::keyword("not") text::keyword("not")
.map_with_span(|_, span| span) .map_with_span(|_, span| span)
.then(space()) .then(space())
@ -50,8 +50,8 @@ fn prefix_not() -> impl Parser<char, Prefix, Error = Error> {
} }
pub fn prefixed( pub fn prefixed(
suffixed: impl Parser<char, Expr, Error = Error>, suffixed: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Expr, Error = Error> { ) -> impl Parser<char, Expr, Error = Error> + Clone {
let prefix = prefix_neg() let prefix = prefix_neg()
.or(prefix_not()) .or(prefix_not())
.map_with_span(|prefix, span| (prefix, span)); .map_with_span(|prefix, span| (prefix, span));

View file

@ -131,8 +131,8 @@ impl Suffix {
} }
fn suffix_call_arg( fn suffix_call_arg(
expr: impl Parser<char, Expr, Error = Error>, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Suffix, Error = Error> { ) -> impl Parser<char, Suffix, Error = Error> + Clone {
space() space()
.then_ignore(just('(')) .then_ignore(just('('))
.then(space()) .then(space())
@ -147,7 +147,7 @@ fn suffix_call_arg(
}) })
} }
fn suffix_call_no_arg() -> impl Parser<char, Suffix, Error = Error> { fn suffix_call_no_arg() -> impl Parser<char, Suffix, Error = Error> + Clone {
space() space()
.then_ignore(just('(')) .then_ignore(just('('))
.then(space()) .then(space())
@ -157,7 +157,7 @@ fn suffix_call_no_arg() -> impl Parser<char, Suffix, Error = Error> {
fn suffix_call_constr( fn suffix_call_constr(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Suffix, Error = Error> { ) -> impl Parser<char, Suffix, Error = Error> + Clone {
space() space()
.then(table_constr(expr)) .then(table_constr(expr))
.map(|(s0, constr)| Suffix::CallConstr { s0, constr }) .map(|(s0, constr)| Suffix::CallConstr { s0, constr })
@ -165,7 +165,7 @@ fn suffix_call_constr(
fn suffix_field_access( fn suffix_field_access(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Suffix, Error = Error> { ) -> impl Parser<char, Suffix, Error = Error> + Clone {
space() space()
.then_ignore(just('[')) .then_ignore(just('['))
.then(space()) .then(space())
@ -182,7 +182,7 @@ fn suffix_field_access(
fn suffix_field_assign( fn suffix_field_assign(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Suffix, Error = Error> { ) -> impl Parser<char, Suffix, Error = Error> + Clone {
space() space()
.then_ignore(just('[')) .then_ignore(just('['))
.then(space()) .then(space())
@ -206,7 +206,7 @@ fn suffix_field_assign(
) )
} }
fn suffix_field_access_ident() -> impl Parser<char, Suffix, Error = Error> { fn suffix_field_access_ident() -> impl Parser<char, Suffix, Error = Error> + Clone {
space() space()
.then_ignore(just('.')) .then_ignore(just('.'))
.then(space()) .then(space())
@ -215,8 +215,8 @@ fn suffix_field_access_ident() -> impl Parser<char, Suffix, Error = Error> {
} }
fn suffix_field_assign_ident( fn suffix_field_assign_ident(
expr: impl Parser<char, Expr, Error = Error>, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Suffix, Error = Error> { ) -> impl Parser<char, Suffix, Error = Error> + Clone {
space() space()
.then_ignore(just('.')) .then_ignore(just('.'))
.then(space()) .then(space())
@ -238,9 +238,9 @@ fn suffix_field_assign_ident(
} }
pub fn suffixed( pub fn suffixed(
atom: impl Parser<char, Expr, Error = Error>, atom: impl Parser<char, Expr, Error = Error> + Clone,
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Expr, Error = Error> { ) -> impl Parser<char, Expr, Error = Error> + Clone {
let call_arg = suffix_call_arg(expr.clone()); let call_arg = suffix_call_arg(expr.clone());
let call_no_arg = suffix_call_no_arg(); let call_no_arg = suffix_call_no_arg();
let call_constr = suffix_call_constr(expr.clone()); let call_constr = suffix_call_constr(expr.clone());

View file

@ -9,7 +9,7 @@ use super::lit::table_lit_elem;
pub fn table_constr_elem( pub fn table_constr_elem(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, TableConstrElem, Error = Error> { ) -> impl Parser<char, TableConstrElem, Error = Error> + Clone {
let lit = table_lit_elem(expr.clone()).map(TableConstrElem::Lit); let lit = table_lit_elem(expr.clone()).map(TableConstrElem::Lit);
let indexed = just('[') let indexed = just('[')
@ -38,7 +38,7 @@ pub fn table_constr_elem(
pub fn table_constr( pub fn table_constr(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, TableConstr, Error = Error> { ) -> impl Parser<char, TableConstr, Error = Error> + Clone {
let elem = space() let elem = space()
.then(table_constr_elem(expr)) .then(table_constr_elem(expr))
.then(space()) .then(space())

View file

@ -6,7 +6,7 @@ use crate::ast::{Expr, TableDestr, TablePattern, TablePatternElem};
use super::basic::{ident, space, Error}; use super::basic::{ident, space, Error};
pub fn table_pattern_elem() -> impl Parser<char, TablePatternElem, Error = Error> { pub fn table_pattern_elem() -> impl Parser<char, TablePatternElem, Error = Error> + Clone {
let positional = ident().map(TablePatternElem::Positional); let positional = ident().map(TablePatternElem::Positional);
let named = ident() let named = ident()
@ -25,7 +25,7 @@ pub fn table_pattern_elem() -> impl Parser<char, TablePatternElem, Error = Error
named.or(positional) named.or(positional)
} }
pub fn table_pattern() -> impl Parser<char, TablePattern, Error = Error> { pub fn table_pattern() -> impl Parser<char, TablePattern, Error = Error> + Clone {
let elem = space() let elem = space()
.then(table_pattern_elem()) .then(table_pattern_elem())
.then(space()) .then(space())
@ -46,8 +46,8 @@ pub fn table_pattern() -> impl Parser<char, TablePattern, Error = Error> {
} }
pub fn table_destr( pub fn table_destr(
expr: impl Parser<char, Expr, Error = Error>, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, TableDestr, Error = Error> { ) -> impl Parser<char, TableDestr, Error = Error> + Clone {
let local = text::keyword("local").ignore_then(space()).or_not(); let local = text::keyword("local").ignore_then(space()).or_not();
local local

View file

@ -8,7 +8,7 @@ use super::basic::{ident, space, Error};
fn var_access( fn var_access(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Var, Error = Error> { ) -> impl Parser<char, Var, Error = Error> + Clone {
just('[') just('[')
.ignore_then(space()) .ignore_then(space())
.then(expr) .then(expr)
@ -24,7 +24,7 @@ fn var_access(
fn var_assign( fn var_assign(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Var, Error = Error> { ) -> impl Parser<char, Var, Error = Error> + Clone {
let local = text::keyword("local").ignore_then(space()).or_not(); let local = text::keyword("local").ignore_then(space()).or_not();
local local
@ -53,7 +53,7 @@ fn var_assign(
fn var_assign_ident( fn var_assign_ident(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Var, Error = Error> { ) -> impl Parser<char, Var, Error = Error> + Clone {
let local = text::keyword("local").ignore_then(space()).or_not(); let local = text::keyword("local").ignore_then(space()).or_not();
local local
@ -76,7 +76,7 @@ fn var_assign_ident(
pub fn var( pub fn var(
expr: impl Parser<char, Expr, Error = Error> + Clone, expr: impl Parser<char, Expr, Error = Error> + Clone,
) -> impl Parser<char, Var, Error = Error> { ) -> impl Parser<char, Var, Error = Error> + Clone {
let access = var_access(expr.clone()); let access = var_access(expr.clone());
let assign = var_assign(expr.clone()); let assign = var_assign(expr.clone());
let access_ident = ident().map(Var::AccessIdent); let access_ident = ident().map(Var::AccessIdent);