use super::lex::{StrPart, Tok, Token};
use super::path::{self, Path};
use super::{ops, prec_climb};
use alloc::{boxed::Box, vec::Vec};
use core::fmt::{self, Debug};
pub type Error<S, T = S> = (Expect<S>, T);
pub type TError<'t, S> = Error<S, Option<&'t Token<S>>>;
#[derive(Debug)]
#[non_exhaustive]
pub enum Expect<S> {
Just(S),
Var,
Pattern,
ElseOrEnd,
CommaOrRBrack,
CommaOrRBrace,
SemicolonOrRParen,
Term,
Key,
Ident,
Arg,
Str,
Nothing,
}
impl<'a> Expect<&'a str> {
pub fn as_str(&self) -> &'a str {
match self {
Self::Just(s) => s,
Self::Var => "variable",
Self::Pattern => "pattern",
Self::ElseOrEnd => "else or end",
Self::CommaOrRBrack => "comma or right bracket",
Self::CommaOrRBrace => "comma or right brace",
Self::SemicolonOrRParen => "semicolon or right parenthesis",
Self::Term => "term",
Self::Key => "key",
Self::Ident => "identifier",
Self::Arg => "argument",
Self::Str => "string",
Self::Nothing => "nothing",
}
}
}
pub type Result<'s, 't, T> = core::result::Result<T, TError<'t, &'s str>>;
pub struct Parser<'s, 't> {
i: core::slice::Iter<'t, Token<&'s str>>,
e: Vec<TError<'t, &'s str>>,
}
#[derive(Debug, Default)]
pub enum Term<S> {
#[default]
Id,
Recurse,
Num(S),
Str(Option<S>, Vec<StrPart<S, Self>>),
Arr(Option<Box<Self>>),
Obj(Vec<(Self, Option<Self>)>),
Neg(Box<Self>),
BinOp(Box<Self>, BinaryOp<S>, Box<Self>),
Label(S, Box<Self>),
Break(S),
Fold(S, Box<Self>, Pattern<S>, Vec<Self>),
TryCatch(Box<Self>, Option<Box<Self>>),
IfThenElse(Vec<(Self, Self)>, Option<Box<Self>>),
Def(Vec<Def<S, Self>>, Box<Self>),
Call(S, Vec<Self>),
Var(S),
Path(Box<Self>, Path<Self>),
}
#[derive(Debug)]
pub enum Pattern<S> {
Var(S),
Arr(Vec<Self>),
Obj(Vec<(Term<S>, Self)>),
}
#[derive(Debug)]
pub enum BinaryOp<S> {
Pipe(Option<Pattern<S>>),
Comma,
Alt,
Or,
And,
Math(ops::Math),
Cmp(ops::Cmp),
Assign,
Update,
UpdateMath(ops::Math),
UpdateAlt,
}
impl<S> Term<S> {
#[cfg(feature = "std")]
pub(crate) fn as_str(&self) -> Option<&S> {
if let Term::Str(None, s) = self {
if let [StrPart::Str(s)] = &s[..] {
return Some(s);
}
}
None
}
pub(crate) fn from_str(s: S) -> Self {
Self::Str(None, [StrPart::Str(s)].into())
}
pub(crate) fn empty() -> Self {
let path = (path::Part::Range(None, None), path::Opt::Essential);
let obj = Term::Obj(Vec::new());
Term::Path(obj.into(), Path(Vec::from([path])))
}
fn climb(self, tail: &mut impl Iterator<Item = (BinaryOp<S>, Self)>) -> Self {
let tail = core::iter::from_fn(|| {
tail.next().map(|(op, tm)| match op {
BinaryOp::Pipe(Some(_)) => (op, tm.climb(tail)),
_ => (op, tm),
})
});
prec_climb::climb(self, tail)
}
}
impl<S> Pattern<S> {
pub(crate) fn vars(&self) -> Box<dyn Iterator<Item = &S> + '_> {
match self {
Pattern::Var(x) => Box::new(core::iter::once(x)),
Pattern::Arr(a) => Box::new(a.iter().flat_map(|p| p.vars())),
Pattern::Obj(o) => Box::new(o.iter().flat_map(|(_k, p)| p.vars())),
}
}
}
impl<'s, 't> Parser<'s, 't> {
#[must_use]
pub fn new(i: &'t [Token<&'s str>]) -> Self {
Self {
i: i.iter(),
e: Vec::new(),
}
}
pub fn parse<T: Default, F>(mut self, f: F) -> core::result::Result<T, Vec<TError<'t, &'s str>>>
where
F: FnOnce(&mut Self) -> Result<'s, 't, T>,
{
let y = self.finish("", f);
if self.e.is_empty() {
Ok(y)
} else {
Err(self.e)
}
}
fn verify_last(&mut self, last: &'static str) -> Result<'s, 't, ()> {
match (self.i.as_slice(), last) {
([], "") => Ok(()),
([Token(c, _)], last) if *c == last => Ok(()),
([], _) => Err((Expect::Just(last), None)),
([next, ..], "") => Err((Expect::Nothing, Some(next))),
([next, ..], _) => Err((Expect::Just(last), Some(next))),
}
}
fn with_tok<T>(&mut self, tokens: &'t [Token<&'s str>], f: impl FnOnce(&mut Self) -> T) -> T {
let i = core::mem::replace(&mut self.i, tokens.iter());
let y = f(self);
self.i = i;
y
}
fn finish<T: Default, F>(&mut self, last: &'static str, f: F) -> T
where
F: FnOnce(&mut Self) -> Result<'s, 't, T>,
{
f(self)
.and_then(|y| {
self.verify_last(last)?;
Ok(y)
})
.unwrap_or_else(|e| {
self.e.push(e);
T::default()
})
}
fn with<T: Default, F>(&mut self, tokens: &'t [Token<&'s str>], last: &'static str, f: F) -> T
where
F: FnOnce(&mut Self) -> Result<'s, 't, T>,
{
self.with_tok(tokens, |p| p.finish(last, f))
}
fn maybe<T>(&mut self, f: impl Fn(&mut Self) -> Option<T>) -> Option<T> {
let i = self.i.clone();
let y = f(self);
if y.is_none() {
self.i = i;
}
y
}
fn try_maybe<T, F>(&mut self, f: F) -> Result<'s, 't, Option<T>>
where
F: Fn(&mut Self) -> Result<'s, 't, Option<T>>,
{
let i = self.i.clone();
let y = f(self)?;
if y.is_none() {
self.i = i;
}
Ok(y)
}
fn many1<T>(
&mut self,
f: impl Fn(&mut Self) -> Result<'s, 't, T>,
sep: &'s str,
allow_trailing_sep: bool,
last: &'s str,
expect: Expect<&'s str>,
) -> Result<'s, 't, Vec<T>> {
let mut y = Vec::from([f(self)?]);
let get_last = |p: &mut Self| p.i.next().filter(|Token(s, _)| *s == last);
let next_last = |p: &mut Self| allow_trailing_sep && p.maybe(get_last).is_some();
loop {
match self.i.next() {
Some(Token(s, _)) if *s == last => break,
Some(Token(s, _)) if *s == sep && next_last(self) => break,
Some(Token(s, _)) if *s == sep => y.push(f(self)?),
next => return Err((expect, next)),
}
}
Ok(y)
}
fn obj_items<T, F>(&mut self, f: F) -> Result<'s, 't, Vec<T>>
where
F: Fn(&mut Self) -> Result<'s, 't, T>,
{
self.many1(f, ",", true, "}", Expect::CommaOrRBrace)
}
fn args<T>(&mut self, f: fn(&mut Self) -> Result<'s, 't, T>) -> Vec<T> {
self.maybe(|p| match p.i.next() {
Some(Token(full, Tok::Block(tokens))) if full.starts_with('(') => {
Some(p.with(tokens, "", |p| {
p.many1(f, ";", false, ")", Expect::SemicolonOrRParen)
}))
}
_ => None,
})
.unwrap_or_default()
}
fn op(&mut self, with_comma: bool) -> Result<'s, 't, Option<BinaryOp<&'s str>>> {
use ops::{Cmp, Math};
self.try_maybe(|p| match p.i.next() {
Some(Token(s, _)) => Ok(Some(match *s {
"|" => BinaryOp::Pipe(None),
"as" => {
let x = p.pattern()?;
p.just("|")?;
BinaryOp::Pipe(Some(x))
}
"," if with_comma => BinaryOp::Comma,
"+" => BinaryOp::Math(Math::Add),
"-" => BinaryOp::Math(Math::Sub),
"*" => BinaryOp::Math(Math::Mul),
"/" => BinaryOp::Math(Math::Div),
"%" => BinaryOp::Math(Math::Rem),
"=" => BinaryOp::Assign,
"|=" => BinaryOp::Update,
"+=" => BinaryOp::UpdateMath(Math::Add),
"-=" => BinaryOp::UpdateMath(Math::Sub),
"*=" => BinaryOp::UpdateMath(Math::Mul),
"/=" => BinaryOp::UpdateMath(Math::Div),
"%=" => BinaryOp::UpdateMath(Math::Rem),
"<" => BinaryOp::Cmp(Cmp::Lt),
">" => BinaryOp::Cmp(Cmp::Gt),
"<=" => BinaryOp::Cmp(Cmp::Le),
">=" => BinaryOp::Cmp(Cmp::Ge),
"==" => BinaryOp::Cmp(Cmp::Eq),
"!=" => BinaryOp::Cmp(Cmp::Ne),
"//" => BinaryOp::Alt,
"//=" => BinaryOp::UpdateAlt,
"or" => BinaryOp::Or,
"and" => BinaryOp::And,
_ => return Ok(None),
})),
None => Ok(None),
})
}
fn char0(&mut self, c: char) -> Option<&'s str> {
self.maybe(|p| match p.i.next() {
Some(Token(s, _)) if s.chars().eq([c]) => Some(*s),
_ => None,
})
}
fn dot(&mut self) -> Option<&'s str> {
self.maybe(|p| match p.i.next() {
Some(Token(c, _)) if *c != ".." => c.strip_prefix('.'),
_ => None,
})
}
fn terminated<T, F>(&mut self, f: F) -> Result<'s, 't, T>
where
F: FnOnce(&mut Self) -> Result<'s, 't, T>,
{
let y = f(self)?;
self.just(";")?;
Ok(y)
}
fn just(&mut self, c: &'static str) -> Result<'s, 't, &'s str> {
match self.i.next() {
Some(Token(s, _)) if *s == c => Ok(*s),
next => Err((Expect::Just(c), next)),
}
}
fn var(&mut self) -> Result<'s, 't, &'s str> {
match self.i.next() {
Some(Token(x, Tok::Var)) => Ok(*x),
next => Err((Expect::Var, next)),
}
}
fn pattern(&mut self) -> Result<'s, 't, Pattern<&'s str>> {
match self.i.next() {
Some(Token(x, Tok::Var)) => Ok(Pattern::Var(*x)),
next @ Some(Token(full, Tok::Block(tokens))) => match &full[..1] {
"[" => Ok(Pattern::Arr(self.with(tokens, "", |p| {
p.many1(Self::pattern, ",", false, "]", Expect::CommaOrRBrack)
}))),
"{" => Ok(Pattern::Obj(
self.with(tokens, "", |p| p.obj_items(Self::pat_obj_entry)),
)),
_ => Err((Expect::Pattern, next)),
},
next => Err((Expect::Pattern, next)),
}
}
fn term_with_comma(&mut self, with_comma: bool) -> Result<'s, 't, Term<&'s str>> {
let head = self.atom()?;
let mut tail = Vec::new();
while let Some(op) = self.op(with_comma)? {
tail.push((op, self.atom()?))
}
Ok(head.climb(&mut tail.into_iter()))
}
fn atom(&mut self) -> Result<'s, 't, Term<&'s str>> {
let tm = match self.i.next() {
Some(Token("-", _)) => Term::Neg(Box::new(self.atom()?)),
Some(Token("def", _)) => {
let head = self.def_tail()?;
let tail = self.defs()?;
let tm = self.term()?;
Term::Def(core::iter::once(head).chain(tail).collect(), Box::new(tm))
}
Some(Token("if", _)) => {
let if_then = |p: &mut Self| -> Result<_> {
let if_ = p.term()?;
p.just("then")?;
Ok((if_, p.term()?))
};
let mut if_thens = Vec::from([if_then(self)?]);
let else_ = loop {
match self.i.next() {
Some(Token("elif", _)) => if_thens.push(if_then(self)?),
Some(Token("else", _)) => {
let else_ = self.term()?;
self.just("end")?;
break Some(else_);
}
Some(Token("end", _)) => break None,
next => return Err((Expect::ElseOrEnd, next)),
}
};
Term::IfThenElse(if_thens, else_.map(Box::new))
}
Some(Token("try", _)) => {
let try_ = self.atom()?;
let catch = self.try_maybe(|p| match p.i.next() {
Some(Token("catch", _)) => Ok(Some(p.atom()?)),
_ => Ok(None),
})?;
Term::TryCatch(Box::new(try_), catch.map(Box::new))
}
Some(Token("label", _)) => {
let x = self.var()?;
self.just("|")?;
let tm = self.term()?;
Term::Label(x, Box::new(tm))
}
Some(Token("break", _)) => Term::Break(self.var()?),
Some(Token(fold @ ("reduce" | "foreach"), _)) => {
let xs = self.atom()?;
self.just("as")?;
let x = self.pattern()?;
let args = self.args(Self::term);
Term::Fold(*fold, Box::new(xs), x, args)
}
Some(Token(id, Tok::Var)) => Term::Var(*id),
Some(Token(id, Tok::Fmt)) => {
let s = self.maybe(|p| match p.i.next() {
Some(Token(_, Tok::Str(parts))) => Some(p.str_parts(parts)),
_ => None,
});
match s {
None => Term::Call(*id, self.args(Self::term)),
Some(parts) => Term::Str(Some(*id), parts),
}
}
Some(Token(id, Tok::Word)) => Term::Call(*id, self.args(Self::term)),
Some(Token("..", _)) => Term::Recurse,
Some(Token(c, Tok::Sym)) if c.starts_with('.') => {
let key = if c.len() > 1 {
Some(Term::from_str(&c[1..]))
} else {
self.maybe(|p| p.str_key().ok())
};
if let Some(key) = key {
let head = (path::Part::Index(key), self.opt());
let path = core::iter::once(head).chain(self.path()?.0).collect();
Term::Path(Box::new(Term::Id), Path(path))
} else {
Term::Id
}
}
Some(Token(n, Tok::Num)) => Term::Num(*n),
Some(Token(full, Tok::Block(tokens))) => match &full[..1] {
"[" if matches!(tokens[..], [Token("]", _)]) => Term::Arr(None),
"{" if matches!(tokens[..], [Token("}", _)]) => Term::Obj(Vec::new()),
"[" => Term::Arr(Some(Box::new(self.with(tokens, "]", Self::term)))),
"{" => self.with(tokens, "", |p| p.obj_items(Self::obj_entry).map(Term::Obj)),
"(" => self.with(tokens, ")", Self::term),
_ => panic!(),
},
Some(Token(_, Tok::Str(parts))) => Term::Str(None, self.str_parts(parts)),
next => return Err((Expect::Term, next)),
};
let tm = match self.opt() {
path::Opt::Optional => Term::TryCatch(Box::new(tm), None),
path::Opt::Essential => tm,
};
let path = self.path()?;
Ok(if path.0.is_empty() {
tm
} else {
Term::Path(Box::new(tm), path)
})
}
pub fn term(&mut self) -> Result<'s, 't, Term<&'s str>> {
self.term_with_comma(true)
}
fn pat_obj_entry(&mut self) -> Result<'s, 't, (Term<&'s str>, Pattern<&'s str>)> {
let i = self.i.clone();
let key = match self.i.next() {
Some(Token(x, Tok::Var)) => return Ok((Term::from_str(&x[1..]), Pattern::Var(x))),
Some(Token(full, Tok::Block(tokens))) if full.starts_with('(') => {
self.with(tokens, ")", Self::term)
}
Some(Token(id, Tok::Word)) if !id.contains("::") => Term::from_str(*id),
_ => {
self.i = i;
self.str_key()?
}
};
self.just(":")?;
Ok((key, self.pattern()?))
}
fn obj_entry(&mut self) -> Result<'s, 't, (Term<&'s str>, Option<Term<&'s str>>)> {
let i = self.i.clone();
let key = match self.i.next() {
Some(Token(full, Tok::Block(tokens))) if full.starts_with('(') => {
let k = self.with(tokens, ")", Self::term);
self.just(":")?;
return Ok((k, Some(self.term_with_comma(false)?)));
}
Some(Token(id, Tok::Var)) => Term::Var(*id),
Some(Token(id, Tok::Word)) if !id.contains("::") => Term::from_str(*id),
_ => {
self.i = i;
self.str_key()?
}
};
let v = self.char0(':').map(|_| self.term_with_comma(false));
Ok((key, v.transpose()?))
}
fn str_parts(
&mut self,
parts: &'t [StrPart<&'s str, Token<&'s str>>],
) -> Vec<StrPart<&'s str, Term<&'s str>>> {
let parts = parts.iter().map(|part| match part {
StrPart::Str(s) => StrPart::Str(*s),
StrPart::Term(Token(full, Tok::Block(tokens))) if full.starts_with('(') => {
StrPart::Term(self.with(tokens, ")", Self::term))
}
StrPart::Term(_) => unreachable!(),
StrPart::Char(c) => StrPart::Char(*c),
});
parts.collect()
}
fn path(&mut self) -> Result<'s, 't, Path<Term<&'s str>>> {
let mut path: Vec<_> = core::iter::from_fn(|| self.path_part_opt()).collect();
while let Some(key) = self.dot() {
let key = if key.is_empty() {
self.str_key()?
} else {
Term::from_str(key)
};
path.push((path::Part::Index(key), self.opt()));
path.extend(core::iter::from_fn(|| self.path_part_opt()));
}
Ok(Path(path))
}
fn path_part(&mut self) -> Result<'s, 't, path::Part<Term<&'s str>>> {
use path::Part::{Index, Range};
let done = |p: &Self| matches!(p.i.as_slice(), [Token("]", _)]);
Ok(if done(self) {
Range(None, None)
} else if self.char0(':').is_some() {
Range(None, Some(self.term()?))
} else {
let tm = self.term()?;
if self.char0(':').is_some() {
if done(self) {
Range(Some(tm), None)
} else {
Range(Some(tm), Some(self.term()?))
}
} else {
Index(tm)
}
})
}
fn path_part_opt(&mut self) -> Option<(path::Part<Term<&'s str>>, path::Opt)> {
let part = self.maybe(|p| match p.i.next() {
Some(Token(full, Tok::Block(tokens))) if full.starts_with('[') => {
Some(p.with(tokens, "]", Self::path_part))
}
_ => None,
})?;
Some((part, self.opt()))
}
fn str_key(&mut self) -> Result<'s, 't, Term<&'s str>> {
match self.i.next() {
Some(Token(id, Tok::Fmt)) => match self.i.next() {
Some(Token(_, Tok::Str(parts))) => Ok(Term::Str(Some(*id), self.str_parts(parts))),
next => Err((Expect::Str, next)),
},
Some(Token(_, Tok::Str(parts))) => Ok(Term::Str(None, self.str_parts(parts))),
next => Err((Expect::Key, next)),
}
}
fn opt(&mut self) -> path::Opt {
let mut opt = path::Opt::Essential;
while self.char0('?').is_some() {
opt = path::Opt::Optional;
}
opt
}
pub fn defs(&mut self) -> Result<'s, 't, Vec<Def<&'s str>>> {
let head = |p: &mut Self| p.just("def").ok();
core::iter::from_fn(|| self.maybe(head).map(|_| self.def_tail())).collect()
}
fn def_tail(&mut self) -> Result<'s, 't, Def<&'s str, Term<&'s str>>> {
let name = match self.i.next() {
Some(Token(w, Tok::Word | Tok::Fmt)) if !w.contains("::") => w,
next => return Err((Expect::Ident, next)),
};
let args = self.args(|p| match p.i.next() {
Some(Token(w, Tok::Word | Tok::Var)) if !w.contains("::") => Ok(*w),
next => Err((Expect::Arg, next)),
});
self.just(":")?;
let body = self.term()?;
self.just(";")?;
Ok(Def { name, args, body })
}
fn bare_str(&mut self) -> Result<'s, 't, &'s str> {
match self.i.next() {
next @ Some(Token(_, Tok::Str(parts))) => match parts[..] {
[StrPart::Str(s)] => Ok(s),
_ => Err((Expect::Str, next)),
},
next => Err((Expect::Str, next)),
}
}
fn dep(&mut self, import: bool) -> Result<'s, 't, Dep<&'s str>> {
let path = self.bare_str()?;
let name = import.then(|| {
self.just("as")?;
match self.i.next() {
Some(Token(v, Tok::Word | Tok::Var)) => Ok(*v),
next => Err((Expect::Ident, next)),
}
});
let name = name.transpose()?;
let meta = !matches!(self.i.as_slice(), [Token(";", _), ..]);
let meta = meta.then(|| self.term()).transpose()?;
Ok((path, name, meta))
}
pub(crate) fn module<B, F>(&mut self, f: F) -> Result<'s, 't, Module<&'s str, B>>
where
F: FnOnce(&mut Self) -> Result<'s, 't, B>,
{
let meta = self
.maybe(|p| match p.i.next() {
Some(Token("module", _)) => Some(p.terminated(Self::term)),
_ => None,
})
.transpose()?;
let deps = core::iter::from_fn(|| {
self.maybe(|p| match p.i.next() {
Some(Token("include", _)) => Some(p.terminated(|p| p.dep(false))),
Some(Token("import", _)) => Some(p.terminated(|p| p.dep(true))),
_ => None,
})
})
.collect::<Result<_>>()?;
let body = f(self)?;
Ok(Module { meta, deps, body })
}
}
type Dep<S> = (S, Option<S>, Option<Term<S>>);
#[derive(Debug, Default)]
pub(crate) struct Module<S, B> {
pub meta: Option<Term<S>>,
pub deps: Vec<Dep<S>>,
pub body: B,
}
pub struct Def<S = &'static str, F = Term<S>> {
pub name: S,
pub args: Vec<S>,
pub body: F,
}
impl<S: Debug, F: Debug> Debug for Def<S, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({:?}, {:?}, {:?})", self.name, self.args, self.body)
}
}
impl<S, F> Def<S, F> {
pub(crate) fn new(name: S, args: Vec<S>, body: F) -> Self {
Self { name, args, body }
}
}
impl<S> prec_climb::Op for BinaryOp<S> {
fn precedence(&self) -> usize {
use ops::{Cmp, Math};
match self {
Self::Pipe(None) => 0,
Self::Comma => 1,
Self::Pipe(Some(_)) => 2,
Self::Assign | Self::Update | Self::UpdateMath(_) | Self::UpdateAlt => 3,
Self::Alt => 4,
Self::Or => Self::Alt.precedence() + 1,
Self::And => Self::Or.precedence() + 1,
Self::Cmp(Cmp::Eq | Cmp::Ne) => Self::And.precedence() + 1,
Self::Cmp(Cmp::Lt | Cmp::Gt | Cmp::Le | Cmp::Ge) => Self::And.precedence() + 2,
Self::Math(Math::Add | Math::Sub) => Self::And.precedence() + 3,
Self::Math(Math::Mul | Math::Div) => Self::Math(Math::Add).precedence() + 1,
Self::Math(Math::Rem) => Self::Math(Math::Mul).precedence() + 1,
}
}
fn associativity(&self) -> prec_climb::Associativity {
use prec_climb::Associativity;
match self {
Self::Pipe(_) | Self::Assign | Self::Update | Self::UpdateMath(_) | Self::UpdateAlt => {
Associativity::Right
}
_ => Associativity::Left,
}
}
}
impl<S> prec_climb::Expr<BinaryOp<S>> for Term<S> {
fn from_op(lhs: Self, op: BinaryOp<S>, rhs: Self) -> Self {
Self::BinOp(Box::new(lhs), op, Box::new(rhs))
}
}