Create each main parser only once

This commit is contained in:
Joscha 2022-11-19 20:50:26 +01:00
parent b291619d10
commit ea7518b183
10 changed files with 346 additions and 222 deletions

View file

@ -1,11 +1,19 @@
//! Parse the ast over at [`crate::ast`].
//!
//! Rules of thumb:
//! - Everything `pub` should return a [`BoxedParser`].
//! - Everything not used outside a module should not be `pub`. It can always be
//! made public later.
//! # Rules
//!
//! - Public parser functions must return [`basic::EParser`].
//! - Public parser functions must receive public subparsers via their arguments.
//! - Each public parser function must be called exactly once, inside this file.
//! - Non-public parser functions may receive and return `impl Parser<...>`.
//!
//! # Warning
//!
//! `clippy::redundant_clone` has stopped working in this module and its
//! submodules. I have no idea why.
// TODO Turn multiple calls to subparsers into clone-s
// TODO https://github.com/rust-lang/rust/issues/63063
// TODO Find out why clippy::redundant_clone stopped working
mod basic;
mod expr;
@ -24,5 +32,45 @@ use crate::ast::Expr;
use self::basic::Error;
pub fn parser() -> impl Parser<char, Expr, Error = Error> {
recursive(expr::expr).padded().then_ignore(end())
let space = basic::space();
let ident = basic::ident();
let local = basic::local(space.clone());
let table_pattern = table_destr::table_pattern(space.clone(), ident.clone());
let expr = recursive(|expr| {
let expr = expr.boxed();
let table_lit_elem = lit::table_lit_elem(space.clone(), ident.clone(), expr.clone());
let lit = lit::lit(space.clone(), table_lit_elem.clone());
let var = var::var(space.clone(), ident.clone(), local.clone(), expr.clone());
let table_constr = table_constr::table_constr(space.clone(), table_lit_elem, expr.clone());
let table_destr = table_destr::table_destr(
space.clone(),
local.clone(),
table_pattern.clone(),
expr.clone(),
);
let func_def = func_defs::func_def(
space.clone(),
ident.clone(),
local,
table_pattern,
expr.clone(),
);
let atom = expr::atom(
space.clone(),
lit,
var,
table_constr.clone(),
table_destr,
func_def,
expr.clone(),
);
let suffixed = suffix::suffixed(space.clone(), ident, table_constr, atom, expr);
let prefixed = prefix::prefixed(space.clone(), suffixed);
expr::expr(space, prefixed)
});
expr.padded().then_ignore(end())
}

View file

@ -7,8 +7,7 @@ use crate::ast::{Ident, Line, Space};
use crate::span::Span;
pub type Error = Simple<char, Span>;
// TODO https://github.com/rust-lang/rust/issues/63063
pub type EParser<O> = BoxedParser<'static, char, O, Error>;
fn inline() -> impl Parser<char, (), Error = Error> {
filter(|c: &char| c.is_whitespace() && *c != '\n')
@ -32,7 +31,7 @@ fn line() -> impl Parser<char, Line, Error = Error> {
empty.or(comment)
}
pub fn space() -> BoxedParser<'static, char, Space, Error> {
pub fn space() -> EParser<Space> {
inline()
.ignore_then(line())
.repeated()
@ -41,7 +40,7 @@ pub fn space() -> BoxedParser<'static, char, Space, Error> {
.boxed()
}
pub fn ident() -> BoxedParser<'static, char, Ident, Error> {
pub fn ident() -> EParser<Ident> {
text::ident()
.try_map(|name, span| {
if matches!(
@ -56,6 +55,6 @@ pub fn ident() -> BoxedParser<'static, char, Ident, Error> {
.boxed()
}
pub fn local() -> BoxedParser<'static, char, Option<Space>, Error> {
text::keyword("local").ignore_then(space()).or_not().boxed()
pub fn local(space: EParser<Space>) -> EParser<Option<Space>> {
text::keyword("local").ignore_then(space).or_not().boxed()
}

View file

@ -2,25 +2,19 @@
use chumsky::prelude::*;
use crate::ast::{BinOp, Expr};
use crate::ast::{BinOp, Expr, FuncDef, Lit, Space, TableConstr, TableDestr, Var};
use crate::span::HasSpan;
use super::basic::{space, Error};
use super::func_defs::func_def;
use super::lit::lit;
use super::prefix::prefixed;
use super::suffix::suffixed;
use super::table_constr::table_constr;
use super::table_destr::table_destr;
use super::var::var;
use super::basic::{EParser, Error};
fn atom_paren(
expr: impl Parser<char, Expr, Error = Error> + Clone,
space: EParser<Space>,
expr: EParser<Expr>,
) -> impl Parser<char, Expr, Error = Error> + Clone {
just('(')
.ignore_then(space())
.ignore_then(space.clone())
.then(expr)
.then(space())
.then(space)
.then_ignore(just(')'))
.map_with_span(|((s0, inner), s1), span| Expr::Paren {
s0,
@ -30,33 +24,39 @@ fn atom_paren(
})
}
fn atom(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> 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);
let table_destr = table_destr(expr.clone()).map(Expr::TableDestr);
let func_def = func_def(expr.clone()).map(Expr::FuncDef);
let paren = atom_paren(expr.clone());
pub fn atom(
space: EParser<Space>,
lit: EParser<Lit>,
var: EParser<Var>,
table_constr: EParser<TableConstr>,
table_destr: EParser<TableDestr>,
func_def: EParser<FuncDef>,
expr: EParser<Expr>,
) -> EParser<Expr> {
let lit = lit.map(Expr::Lit);
let var = var.map(Expr::Var);
let table_constr = table_constr.map(Expr::TableConstr);
let table_destr = table_destr.map(Expr::TableDestr);
let func_def = func_def.map(Expr::FuncDef);
let paren = atom_paren(space, expr);
let base = lit
.or(paren)
lit.or(paren)
.or(table_destr)
.or(table_constr)
.or(func_def)
.or(var);
prefixed(suffixed(base, expr))
.or(var)
.boxed()
}
fn left_assoc(
space: EParser<Space>,
op: impl Parser<char, BinOp, Error = Error> + 'static,
over: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, Expr, Error> {
let op_over = space()
) -> EParser<Expr> {
let op_over = space
.clone()
.then(op)
.then(space())
.then(space)
.then(over.clone())
.map(|(((s0, op), s1), right)| (s0, op, s1, right));
@ -73,14 +73,15 @@ fn left_assoc(
}
fn right_assoc(
space: EParser<Space>,
op: impl Parser<char, BinOp, Error = Error> + 'static,
over: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, Expr, Error> {
let over_op = over
.clone()
.then(space())
.then(space.clone())
.then(op)
.then(space())
.then(space)
.map(|(((left, s0), op), s1)| (left, s0, op, s1));
over_op
@ -97,9 +98,7 @@ fn right_assoc(
.boxed()
}
pub fn expr(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, Expr, Error> {
pub fn expr(space: EParser<Space>, prefixed: EParser<Expr>) -> EParser<Expr> {
// * / %
let op_prec_4 = (just('*').to(BinOp::Mul))
.or(just('/').to(BinOp::Div))
@ -123,12 +122,19 @@ pub fn expr(
let op_prec_0 = text::keyword("or").to(BinOp::Or);
right_assoc(
space.clone(),
op_prec_0,
right_assoc(
space.clone(),
op_prec_1,
left_assoc(
space.clone(),
op_prec_2,
left_assoc(op_prec_3, left_assoc(op_prec_4, atom(expr))),
left_assoc(
space.clone(),
op_prec_3,
left_assoc(space, op_prec_4, prefixed),
),
),
),
)

View file

@ -1,19 +1,21 @@
// TODO Rename this module to func_def for consistency
use chumsky::prelude::*;
use crate::ast::{Expr, FuncDef};
use crate::ast::{Expr, FuncDef, Ident, Space, TablePattern};
use super::basic::{ident, local, space, Error};
use super::table_destr::table_pattern;
use super::basic::{EParser, Error};
fn func_def_anon_no_arg(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
expr: EParser<Expr>,
) -> impl Parser<char, FuncDef, Error = Error> {
text::keyword("function")
.ignore_then(space())
.ignore_then(space.clone())
.then_ignore(just('('))
.then(space())
.then(space.clone())
.then_ignore(just(')'))
.then(space())
.then(space)
.then(expr)
.map_with_span(|(((s0, s1), s2), body), span| FuncDef::AnonNoArg {
s0,
@ -25,16 +27,18 @@ fn func_def_anon_no_arg(
}
fn func_def_anon_arg(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
ident: EParser<Ident>,
expr: EParser<Expr>,
) -> impl Parser<char, FuncDef, Error = Error> {
text::keyword("function")
.ignore_then(space())
.ignore_then(space.clone())
.then_ignore(just('('))
.then(space())
.then(ident())
.then(space())
.then(space.clone())
.then(ident)
.then(space.clone())
.then_ignore(just(')'))
.then(space())
.then(space)
.then(expr)
.map_with_span(
|(((((s0, s1), arg), s2), s3), body), span| FuncDef::AnonArg {
@ -50,12 +54,14 @@ fn func_def_anon_arg(
}
fn func_def_anon_destr(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
table_pattern: EParser<TablePattern>,
expr: EParser<Expr>,
) -> impl Parser<char, FuncDef, Error = Error> {
text::keyword("function")
.ignore_then(space())
.then(table_pattern())
.then(space())
.ignore_then(space.clone())
.then(table_pattern)
.then(space)
.then(expr)
.map_with_span(|(((s0, pattern), s1), body), span| FuncDef::AnonDestr {
s0,
@ -67,17 +73,20 @@ fn func_def_anon_destr(
}
fn func_def_named_no_arg(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
ident: EParser<Ident>,
local: EParser<Option<Space>>,
expr: EParser<Expr>,
) -> impl Parser<char, FuncDef, Error = Error> {
local()
local
.then_ignore(text::keyword("function"))
.then(space())
.then(ident())
.then(space())
.then(space.clone())
.then(ident)
.then(space.clone())
.then_ignore(just('('))
.then(space())
.then(space.clone())
.then_ignore(just(')'))
.then(space())
.then(space)
.then(expr)
.map_with_span(
|((((((local, s0), name), s1), s2), s3), body), span| FuncDef::NamedNoArg {
@ -94,19 +103,22 @@ fn func_def_named_no_arg(
}
fn func_def_named_arg(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
ident: EParser<Ident>,
local: EParser<Option<Space>>,
expr: EParser<Expr>,
) -> impl Parser<char, FuncDef, Error = Error> {
local()
local
.then_ignore(text::keyword("function"))
.then(space())
.then(ident())
.then(space())
.then(space.clone())
.then(ident.clone())
.then(space.clone())
.then_ignore(just('('))
.then(space())
.then(ident())
.then(space())
.then(space.clone())
.then(ident)
.then(space.clone())
.then_ignore(just(')'))
.then(space())
.then(space)
.then(expr)
.map_with_span(
|((((((((local, s0), name), s1), s2), arg), s3), s4), body), span| FuncDef::NamedArg {
@ -125,15 +137,19 @@ fn func_def_named_arg(
}
fn func_def_named_destr(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
ident: EParser<Ident>,
local: EParser<Option<Space>>,
table_pattern: EParser<TablePattern>,
expr: EParser<Expr>,
) -> impl Parser<char, FuncDef, Error = Error> {
local()
local
.then_ignore(text::keyword("function"))
.then(space())
.then(ident())
.then(space())
.then(table_pattern())
.then(space())
.then(space.clone())
.then(ident)
.then(space.clone())
.then(table_pattern)
.then(space)
.then(expr)
.map_with_span(|((((((local, s0), name), s1), pattern), s2), body), span| {
FuncDef::NamedDestr {
@ -150,13 +166,35 @@ fn func_def_named_destr(
}
pub fn func_def(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, FuncDef, Error> {
func_def_anon_no_arg(expr.clone())
.or(func_def_anon_arg(expr.clone()))
.or(func_def_anon_destr(expr.clone()))
.or(func_def_named_no_arg(expr.clone()))
.or(func_def_named_arg(expr.clone()))
.or(func_def_named_destr(expr))
space: EParser<Space>,
ident: EParser<Ident>,
local: EParser<Option<Space>>,
table_pattern: EParser<TablePattern>,
expr: EParser<Expr>,
) -> EParser<FuncDef> {
let anon_no_arg = func_def_anon_no_arg(space.clone(), expr.clone());
let anon_arg = func_def_anon_arg(space.clone(), ident.clone(), expr.clone());
let anon_destr =
func_def_anon_destr(space.clone(), table_pattern.clone(), expr.clone().clone());
let named_no_arg = func_def_named_no_arg(
space.clone(),
ident.clone(),
local.clone(),
expr.clone().clone(),
);
let named_arg = func_def_named_arg(
space.clone(),
ident.clone(),
local.clone(),
expr.clone().clone(),
);
let named_destr = func_def_named_destr(space, ident, local, table_pattern, expr);
anon_no_arg
.or(anon_arg)
.or(anon_destr)
.or(named_no_arg)
.or(named_arg)
.or(named_destr)
.boxed()
}

View file

@ -2,10 +2,10 @@
use chumsky::prelude::*;
use crate::ast::{Expr, Lit, NumLit, NumLitStr, StringLit, TableLit, TableLitElem};
use crate::ast::{Expr, Ident, Lit, NumLit, NumLitStr, Space, StringLit, TableLit, TableLitElem};
use crate::builtin::Builtin;
use super::basic::{ident, space, Error};
use super::basic::{EParser, Error};
fn builtin_lit() -> impl Parser<char, Builtin, Error = Error> {
just('\'').ignore_then(choice((
@ -84,16 +84,18 @@ fn string_lit() -> impl Parser<char, StringLit, Error = Error> {
}
pub fn table_lit_elem(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, TableLitElem, Error> {
space: EParser<Space>,
ident: EParser<Ident>,
expr: EParser<Expr>,
) -> EParser<TableLitElem> {
let positional = expr
.clone()
.map(|value| TableLitElem::Positional(Box::new(value)));
let named = ident()
.then(space())
let named = ident
.then(space.clone())
.then_ignore(just(':'))
.then(space())
.then(space)
.then(expr)
.map_with_span(|(((name, s0), s1), value), span| TableLitElem::Named {
name,
@ -107,14 +109,16 @@ pub fn table_lit_elem(
}
fn table_lit(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
space: EParser<Space>,
table_lit_elem: EParser<TableLitElem>,
) -> impl Parser<char, TableLit, Error = Error> {
let elem = space()
.then(table_lit_elem(expr))
.then(space())
let elem = space
.clone()
.then(table_lit_elem)
.then(space.clone())
.map(|((s0, elem), s1)| (s0, elem, s1));
let trailing_comma = just(',').ignore_then(space()).or_not();
let trailing_comma = just(',').ignore_then(space).or_not();
let elems = elem.separated_by(just(',')).then(trailing_comma);
@ -128,16 +132,14 @@ fn table_lit(
})
}
pub fn lit(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, Lit, Error> {
pub fn lit(space: EParser<Space>, table_lit_elem: EParser<TableLitElem>) -> EParser<Lit> {
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#false = text::keyword("false").map_with_span(|_, span| Lit::Bool(false, span));
let builtin = builtin_lit().map_with_span(Lit::Builtin);
let num = num_lit().map(Lit::Num);
let string = string_lit().map(Lit::String);
let table = table_lit(expr).map(Lit::Table);
let table = table_lit(space, table_lit_elem).map(Lit::Table);
nil.or(r#true)
.or(r#false)

View file

@ -5,7 +5,7 @@ use chumsky::prelude::*;
use crate::ast::{Expr, Space};
use crate::span::{HasSpan, Span};
use super::basic::{space, Error};
use super::basic::{EParser, Error};
enum Prefix {
/// See [`Expr::Neg`].
@ -35,25 +35,23 @@ impl Prefix {
}
}
fn prefix_neg() -> impl Parser<char, Prefix, Error = Error> {
fn prefix_neg(space: EParser<Space>) -> impl Parser<char, Prefix, Error = Error> {
just('-')
.map_with_span(|_, span| span)
.then(space())
.then(space)
.map(|(minus, s0)| Prefix::Neg { minus, s0 })
}
fn prefix_not() -> impl Parser<char, Prefix, Error = Error> {
fn prefix_not(space: EParser<Space>) -> impl Parser<char, Prefix, Error = Error> {
text::keyword("not")
.map_with_span(|_, span| span)
.then(space())
.then(space)
.map(|(not, s0)| Prefix::Not { not, s0 })
}
pub fn prefixed(
suffixed: impl Parser<char, Expr, Error = Error> + 'static,
) -> BoxedParser<'static, char, Expr, Error> {
let prefix = prefix_neg()
.or(prefix_not())
pub fn prefixed(space: EParser<Space>, suffixed: EParser<Expr>) -> EParser<Expr> {
let prefix = prefix_neg(space.clone())
.or(prefix_not(space))
.map_with_span(|prefix, span| (prefix, span));
prefix

View file

@ -5,8 +5,7 @@ use chumsky::prelude::*;
use crate::ast::{Call, Expr, Field, Ident, Space, TableConstr};
use crate::span::{HasSpan, Span};
use super::basic::{ident, space, Error};
use super::table_constr::table_constr;
use super::basic::{EParser, Error};
enum Suffix {
/// See [`Call::Arg`].
@ -131,13 +130,15 @@ impl Suffix {
}
fn suffix_call_arg(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
expr: EParser<Expr>,
) -> impl Parser<char, Suffix, Error = Error> {
space()
space
.clone()
.then_ignore(just('('))
.then(space())
.then(space.clone())
.then(expr)
.then(space())
.then(space)
.then_ignore(just(')'))
.map(|(((s0, s1), arg), s2)| Suffix::CallArg {
s0,
@ -147,30 +148,34 @@ fn suffix_call_arg(
})
}
fn suffix_call_no_arg() -> impl Parser<char, Suffix, Error = Error> {
space()
fn suffix_call_no_arg(space: EParser<Space>) -> impl Parser<char, Suffix, Error = Error> {
space
.clone()
.then_ignore(just('('))
.then(space())
.then(space)
.then_ignore(just(')'))
.map(|(s0, s1)| Suffix::CallNoArg { s0, s1 })
}
fn suffix_call_constr(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
space: EParser<Space>,
table_constr: EParser<TableConstr>,
) -> impl Parser<char, Suffix, Error = Error> {
space()
.then(table_constr(expr))
space
.then(table_constr)
.map(|(s0, constr)| Suffix::CallConstr { s0, constr })
}
fn suffix_field_access(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
expr: EParser<Expr>,
) -> impl Parser<char, Suffix, Error = Error> {
space()
space
.clone()
.then_ignore(just('['))
.then(space())
.then(space.clone())
.then(expr)
.then(space())
.then(space)
.then_ignore(just(']'))
.map(|(((s0, s1), index), s2)| Suffix::FieldAccess {
s0,
@ -181,17 +186,19 @@ fn suffix_field_access(
}
fn suffix_field_assign(
expr: impl Parser<char, Expr, Error = Error> + Clone,
space: EParser<Space>,
expr: EParser<Expr>,
) -> impl Parser<char, Suffix, Error = Error> {
space()
space
.clone()
.then_ignore(just('['))
.then(space())
.then(space.clone())
.then(expr.clone())
.then(space())
.then(space.clone())
.then_ignore(just(']'))
.then(space())
.then(space.clone())
.then_ignore(just('='))
.then(space())
.then(space)
.then(expr)
.map(
|((((((s0, s1), index), s2), s3), s4), value)| Suffix::FieldAssign {
@ -206,24 +213,31 @@ fn suffix_field_assign(
)
}
fn suffix_field_access_ident() -> impl Parser<char, Suffix, Error = Error> {
space()
fn suffix_field_access_ident(
space: EParser<Space>,
ident: EParser<Ident>,
) -> impl Parser<char, Suffix, Error = Error> {
space
.clone()
.then_ignore(just('.'))
.then(space())
.then(ident())
.then(space)
.then(ident)
.map(|((s0, s1), ident)| Suffix::FieldAccessIdent { s0, s1, ident })
}
fn suffix_field_assign_ident(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
ident: EParser<Ident>,
expr: EParser<Expr>,
) -> impl Parser<char, Suffix, Error = Error> {
space()
space
.clone()
.then_ignore(just('.'))
.then(space())
.then(ident())
.then(space())
.then(space.clone())
.then(ident)
.then(space.clone())
.then_ignore(just('='))
.then(space())
.then(space)
.then(expr)
.map(
|(((((s0, s1), ident), s2), s3), value)| Suffix::FieldAssignIdent {
@ -238,16 +252,19 @@ fn suffix_field_assign_ident(
}
pub fn suffixed(
atom: impl Parser<char, Expr, Error = Error> + 'static,
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, Expr, Error> {
let call_arg = suffix_call_arg(expr.clone());
let call_no_arg = suffix_call_no_arg();
let call_constr = suffix_call_constr(expr.clone());
let field_access = suffix_field_access(expr.clone());
let field_assign = suffix_field_assign(expr.clone());
let field_access_ident = suffix_field_access_ident();
let field_assign_ident = suffix_field_assign_ident(expr);
space: EParser<Space>,
ident: EParser<Ident>,
table_constr: EParser<TableConstr>,
atom: EParser<Expr>,
expr: EParser<Expr>,
) -> EParser<Expr> {
let call_arg = suffix_call_arg(space.clone(), expr.clone());
let call_no_arg = suffix_call_no_arg(space.clone());
let call_constr = suffix_call_constr(space.clone(), table_constr);
let field_access = suffix_field_access(space.clone(), expr.clone());
let field_assign = suffix_field_assign(space.clone(), expr.clone());
let field_access_ident = suffix_field_access_ident(space.clone(), ident.clone());
let field_assign_ident = suffix_field_assign_ident(space, ident, expr);
let suffix = call_arg
.or(call_no_arg)

View file

@ -2,24 +2,25 @@
use chumsky::prelude::*;
use crate::ast::{Expr, TableConstr, TableConstrElem};
use crate::ast::{Expr, Space, TableConstr, TableConstrElem, TableLitElem};
use super::basic::{space, Error};
use super::lit::table_lit_elem;
use super::basic::{EParser, Error};
fn table_constr_elem(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
space: EParser<Space>,
table_lit_elem: EParser<TableLitElem>,
expr: EParser<Expr>,
) -> impl Parser<char, TableConstrElem, Error = Error> {
let lit = table_lit_elem(expr.clone()).map(TableConstrElem::Lit);
let lit = table_lit_elem.map(TableConstrElem::Lit);
let indexed = just('[')
.ignore_then(space())
.ignore_then(space.clone())
.then(expr.clone())
.then(space())
.then(space.clone())
.then_ignore(just(']'))
.then(space())
.then(space.clone())
.then_ignore(just(':'))
.then(space())
.then(space)
.then(expr)
.map_with_span(
|(((((s0, index), s1), s2), s3), value), span| TableConstrElem::Indexed {
@ -37,14 +38,17 @@ fn table_constr_elem(
}
pub fn table_constr(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, TableConstr, Error> {
let elem = space()
.then(table_constr_elem(expr))
.then(space())
space: EParser<Space>,
table_lit_elem: EParser<TableLitElem>,
expr: EParser<Expr>,
) -> EParser<TableConstr> {
let elem = space
.clone()
.then(table_constr_elem(space.clone(), table_lit_elem, expr))
.then(space.clone())
.map(|((s0, elem), s1)| (s0, elem, s1));
let trailing_comma = just(',').ignore_then(space()).or_not();
let trailing_comma = just(',').ignore_then(space).or_not();
let elems = elem.separated_by(just(',')).then(trailing_comma);

View file

@ -2,18 +2,22 @@
use chumsky::prelude::*;
use crate::ast::{Expr, TableDestr, TablePattern, TablePatternElem};
use crate::ast::{Expr, Ident, Space, TableDestr, TablePattern, TablePatternElem};
use super::basic::{ident, space, Error};
use super::basic::{EParser, Error};
fn table_pattern_elem() -> impl Parser<char, TablePatternElem, Error = Error> {
let positional = ident().map(TablePatternElem::Positional);
fn table_pattern_elem(
space: EParser<Space>,
ident: EParser<Ident>,
) -> impl Parser<char, TablePatternElem, Error = Error> {
let positional = ident.clone().map(TablePatternElem::Positional);
let named = ident()
.then(space())
let named = ident
.clone()
.then(space.clone())
.then_ignore(just(':'))
.then(space())
.then(ident())
.then(space)
.then(ident)
.map_with_span(|(((name, s0), s1), ident), span| TablePatternElem::Named {
name,
s0,
@ -25,13 +29,14 @@ fn table_pattern_elem() -> impl Parser<char, TablePatternElem, Error = Error> {
named.or(positional)
}
pub fn table_pattern() -> BoxedParser<'static, char, TablePattern, Error> {
let elem = space()
.then(table_pattern_elem())
.then(space())
pub fn table_pattern(space: EParser<Space>, ident: EParser<Ident>) -> EParser<TablePattern> {
let elem = space
.clone()
.then(table_pattern_elem(space.clone(), ident))
.then(space.clone())
.map(|((s0, elem), s1)| (s0, elem, s1));
let trailing_comma = just(',').ignore_then(space()).or_not();
let trailing_comma = just(',').ignore_then(space).or_not();
let elems = elem.separated_by(just(',')).then(trailing_comma);
@ -47,15 +52,16 @@ pub fn table_pattern() -> BoxedParser<'static, char, TablePattern, Error> {
}
pub fn table_destr(
expr: impl Parser<char, Expr, Error = Error> + 'static,
) -> BoxedParser<'static, char, TableDestr, Error> {
let local = text::keyword("local").ignore_then(space()).or_not();
space: EParser<Space>,
local: EParser<Option<Space>>,
table_pattern: EParser<TablePattern>,
expr: EParser<Expr>,
) -> EParser<TableDestr> {
local
.then(table_pattern())
.then(space())
.then(table_pattern)
.then(space.clone())
.then_ignore(just('='))
.then(space())
.then(space)
.then(expr)
.map_with_span(|((((local, pattern), s0), s1), value), span| TableDestr {
local,

View file

@ -2,17 +2,15 @@
use chumsky::prelude::*;
use crate::ast::{Expr, Var};
use crate::ast::{Expr, Ident, Space, Var};
use super::basic::{ident, local, space, Error};
use super::basic::{EParser, Error};
fn var_access(
expr: impl Parser<char, Expr, Error = Error>,
) -> impl Parser<char, Var, Error = Error> {
fn var_access(space: EParser<Space>, expr: EParser<Expr>) -> impl Parser<char, Var, Error = Error> {
just('[')
.ignore_then(space())
.ignore_then(space.clone())
.then(expr)
.then(space())
.then(space)
.then_ignore(just(']'))
.map_with_span(|((s0, index), s1), span| Var::Access {
s0,
@ -23,17 +21,19 @@ fn var_access(
}
fn var_assign(
expr: impl Parser<char, Expr, Error = Error> + Clone,
space: EParser<Space>,
local: EParser<Option<Space>>,
expr: EParser<Expr>,
) -> impl Parser<char, Var, Error = Error> {
local()
local
.then_ignore(just('['))
.then(space())
.then(space.clone())
.then(expr.clone())
.then(space())
.then(space.clone())
.then_ignore(just(']'))
.then(space())
.then(space.clone())
.then_ignore(just('='))
.then(space())
.then(space)
.then(expr)
.map_with_span(
|((((((local, s0), index), s1), s2), s3), value), span| Var::Assign {
@ -50,13 +50,16 @@ fn var_assign(
}
fn var_assign_ident(
expr: impl Parser<char, Expr, Error = Error>,
space: EParser<Space>,
ident: EParser<Ident>,
local: EParser<Option<Space>>,
expr: EParser<Expr>,
) -> impl Parser<char, Var, Error = Error> {
local()
.then(ident())
.then(space())
local
.then(ident)
.then(space.clone())
.then_ignore(just('='))
.then(space())
.then(space)
.then(expr)
.map_with_span(
|((((local, name), s0), s1), value), span| Var::AssignIdent {
@ -71,12 +74,15 @@ fn var_assign_ident(
}
pub fn var(
expr: impl Parser<char, Expr, Error = Error> + Clone + 'static,
) -> BoxedParser<'static, char, Var, Error> {
let access = var_access(expr.clone());
let assign = var_assign(expr.clone());
let access_ident = ident().map(Var::AccessIdent);
let assign_ident = var_assign_ident(expr);
space: EParser<Space>,
ident: EParser<Ident>,
local: EParser<Option<Space>>,
expr: EParser<Expr>,
) -> EParser<Var> {
let access = var_access(space.clone(), expr.clone());
let assign = var_assign(space.clone(), local.clone(), expr.clone());
let access_ident = ident.clone().map(Var::AccessIdent);
let assign_ident = var_assign_ident(space, ident, local, expr);
assign.or(access).or(assign_ident).or(access_ident).boxed()
}