use std::cell::RefCell;
use derive_more::IsVariant;
use derive_new::new;
use itertools::Itertools;
use pest::Parser;
use pest::iterators::Pairs;
use pest::pratt_parser::{PrattParser, Op, Assoc};
use pest_derive::Parser;
use thiserror::Error;
use super::eval::{Context, Error as EvalError};
use super::model::{Expr, Ident, Literal, UnaryOp, BinaryOp, Value};
#[derive(Parser)]
#[grammar = "grammar.pest"]
struct Grammar;
pub trait Handler<R> {
type Term;
fn bool(&mut self, raw: &str) -> Self::Term;
fn int(&mut self, raw: &str) -> Self::Term;
fn float(&mut self, raw: &str) -> Self::Term;
fn symbol(&mut self, name: &str) -> Self::Term;
fn ident(&mut self, raw: &str) -> Self::Term;
#[cfg(glam)]
fn vector(&mut self, items: impl IntoIterator<Item=Self::Term>) -> Self::Term;
fn parens(&mut self, inner: Self::Term) -> Self::Term {
inner
}
fn unary_expr(&mut self, op: UnaryOp, arg: Self::Term) -> Self::Term;
fn binary_expr(&mut self, lhs: Self::Term, op: BinaryOp, rhs: Self::Term) -> Self::Term;
fn call(&mut self, target: Self::Term, args: impl IntoIterator<Item=Self::Term>) -> Self::Term;
fn subscript(&mut self, target: Self::Term, index: Self::Term) -> Self::Term;
fn access(&mut self, target: Self::Term, member: &str) -> Self::Term;
}
pub struct Driver<H: Handler<Rule>> {
pratt: PrattParser<Rule>,
handler: RefCell<H>,
}
impl<H: Handler<Rule>> Driver<H> {
pub fn new(handler: H) -> Self {
let pratt = PrattParser::new()
.op(Op::prefix(Rule::neg) | Op::prefix(Rule::not))
.op(Op::infix(Rule::and, Assoc::Left) | Op::infix(Rule::or, Assoc::Left))
.op(
Op::infix(Rule::eq, Assoc::Left) | Op::infix(Rule::not_eq, Assoc::Left) |
Op::infix(Rule::less, Assoc::Left) | Op::infix(Rule::less_eq, Assoc::Left) |
Op::infix(Rule::greater, Assoc::Left) | Op::infix(Rule::greater_eq, Assoc::Left))
.op(Op::infix(Rule::add, Assoc::Left) | Op::infix(Rule::sub, Assoc::Left))
.op(Op::infix(Rule::mul, Assoc::Left) | Op::infix(Rule::div, Assoc::Left))
.op(Op::infix(Rule::pow, Assoc::Right))
.op(Op::postfix(Rule::call) | Op::postfix(Rule::subscript) | Op::postfix(Rule::access))
;
Self { pratt, handler: RefCell::new(handler) }
}
}
impl<H: Handler<Rule>> Driver<H> {
pub fn process(&self, input: impl AsRef<str>) -> Result<H::Term, Error> {
let mut expr = Grammar::parse(Rule::input, input.as_ref())?;
let pairs = expr.next().unwrap().into_inner();
self.validate_optional_features(&pairs)?;
Ok(self.handle_expr(pairs))
}
fn validate_optional_features(&self, pairs: &Pairs<Rule>) -> Result<(), Error> {
if cfg!(glam) {
for pair in pairs.clone().find_tagged("glam") {
match pair.as_rule() {
Rule::vector => {
let len = pair.into_inner().len();
if ![2, 3, 4].contains(&len) {
return Err(Error::Unsupported(format!("vector of length {}", len)));
}
},
_ => unreachable!(),
}
}
} else if pairs.find_first_tagged("glam").is_some() {
return Err(Error::Unsupported("vector syntax".into()));
}
Ok(())
}
fn handle_expr(&self, pairs: Pairs<Rule>) -> H::Term {
self.pratt
.map_primary(|primary| match primary.as_rule() {
Rule::r#bool => self.handler.borrow_mut().bool(primary.as_str()),
Rule::int => self.handler.borrow_mut().int(primary.as_str()),
Rule::float => self.handler.borrow_mut().float(primary.as_str()),
Rule::symbol => {
let name = primary.into_inner().next().unwrap().as_str();
self.handler.borrow_mut().symbol(name)
},
Rule::ident => self.handler.borrow_mut().ident(primary.as_str()),
#[cfg(glam)]
Rule::vector => {
let items: Vec<_> = primary.into_inner()
.map(|elem| self.handle_expr(elem.into_inner()))
.collect();
self.handler.borrow_mut().vector(items)
},
Rule::expr => {
let inner = self.handle_expr(primary.into_inner());
self.handler.borrow_mut().parens(inner)
},
_ => unreachable!(),
})
.map_prefix(|op, rhs| {
let op = match op.as_rule() {
Rule::neg => UnaryOp::Neg,
Rule::not => UnaryOp::Not,
_ => unreachable!(),
};
self.handler.borrow_mut().unary_expr(op, rhs)
})
.map_infix(|left, op, right| {
let op = match op.as_rule() {
Rule::and => BinaryOp::And,
Rule::or => BinaryOp::Or,
Rule::eq => BinaryOp::Eq,
Rule::not_eq => BinaryOp::NotEq,
Rule::less => BinaryOp::Less,
Rule::less_eq => BinaryOp::LessOrEq,
Rule::greater => BinaryOp::Greater,
Rule::greater_eq => BinaryOp::GreaterOrEq,
Rule::add => BinaryOp::Add,
Rule::sub => BinaryOp::Sub,
Rule::mul => BinaryOp::Mul,
Rule::div => BinaryOp::Div,
Rule::pow => BinaryOp::Pow,
_ => unreachable!(),
};
self.handler.borrow_mut().binary_expr(left, op, right)
})
.map_postfix(|lhs, op| match op.as_rule() {
Rule::call => {
let args: Vec<_> = op.into_inner()
.map(|a| self.handle_expr(a.into_inner()))
.collect();
self.handler.borrow_mut().call(lhs, args)
},
Rule::subscript => {
let index = self.handle_expr(op.into_inner().next().unwrap().into_inner());
self.handler.borrow_mut().subscript(lhs, index)
},
Rule::access => {
let member = op.into_inner().next().unwrap();
self.handler.borrow_mut().access(lhs, member.as_str())
},
_ => unreachable!(),
})
.parse(pairs)
}
}
pub(crate) struct AstHandler;
impl Handler<Rule> for AstHandler {
type Term = Expr;
fn bool(&mut self, raw: &str) -> Self::Term {
Expr::literal(raw.parse::<bool>().unwrap())
}
fn int(&mut self, raw: &str) -> Self::Term {
Expr::literal(raw.parse::<i64>().unwrap())
}
fn float(&mut self, raw: &str) -> Self::Term {
Expr::literal(raw.parse::<f32>().unwrap())
}
fn symbol(&mut self, name: &str) -> Self::Term {
Expr::Literal(Literal::symbol(name))
}
fn ident(&mut self, raw: &str) -> Self::Term {
Expr::ref_(raw.to_owned())
}
#[cfg(glam)]
fn vector(&mut self, items: impl IntoIterator<Item=Self::Term>) -> Self::Term {
Expr::vector(items)
}
fn unary_expr(&mut self, op: UnaryOp, arg: Self::Term) -> Self::Term {
Expr::unary(op, arg)
}
fn binary_expr(&mut self, lhs: Self::Term, op: BinaryOp, rhs: Self::Term) -> Self::Term {
Expr::binary(op, lhs, rhs)
}
fn call(&mut self, target: Self::Term, args: impl IntoIterator<Item=Self::Term>) -> Self::Term {
Expr::call(target, args)
}
fn subscript(&mut self, target: Self::Term, index: Self::Term) -> Self::Term {
Expr::subscript(target, index)
}
fn access(&mut self, target: Self::Term, member: &str) -> Self::Term {
Expr::access(target, member.to_owned())
}
}
#[derive(new)]
pub(crate) struct EvalHandler<'h, 'ctx: 'h> {
context: &'h mut Context<'ctx>,
}
impl<'h, 'ctx> Handler<Rule> for EvalHandler<'h, 'ctx> {
type Term = Result<Value, EvalError>;
fn bool(&mut self, raw: &str) -> Self::Term {
Ok(Value::Bool(raw.parse().unwrap()))
}
fn int(&mut self, raw: &str) -> Self::Term {
Ok(Value::Integer(raw.parse().unwrap()))
}
fn float(&mut self, raw: &str) -> Self::Term {
Ok(Value::Float(raw.parse().unwrap()))
}
fn symbol(&mut self, name: &str) -> Self::Term {
Ok(Value::symbol(name))
}
fn ident(&mut self, raw: &str) -> Self::Term {
self.context.resolve(raw).map(|v| v.clone())
}
#[cfg(glam)]
fn vector(&mut self, items: impl IntoIterator<Item=Self::Term>) -> Self::Term {
let items: Result<Vec<_>, _> = items.into_iter().collect();
self.context.eval_vector(&items?)
}
fn unary_expr(&mut self, op: UnaryOp, arg: Self::Term) -> Self::Term {
self.context.eval_unary_expr(op, &arg?)
}
fn binary_expr(&mut self, lhs: Self::Term, op: BinaryOp, rhs: Self::Term) -> Self::Term {
self.context.eval_binary_expr(op, &lhs?, &rhs?)
}
fn call(&mut self, target: Self::Term, args: impl IntoIterator<Item=Self::Term>) -> Self::Term {
let callable = target?.try_to_callable()?;
let args: Result<Vec<_>, _> = args.into_iter().collect();
self.context.eval_call(callable, &args?)
}
fn subscript(&mut self, target: Self::Term, index: Self::Term) -> Self::Term {
self.context.eval_subscript(&target?, &index?)
}
fn access(&mut self, target: Self::Term, member: &str) -> Self::Term {
self.context.eval_access(&target?, &Ident::from(member.to_owned()))
}
}
#[derive(Default, new)]
pub(crate) struct FormatHandler {
format: Format,
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub(crate) enum Format {
#[default]
Pretty,
Minified,
}
impl Handler<Rule> for FormatHandler {
type Term = String;
fn bool(&mut self, raw: &str) -> Self::Term {
raw.to_owned()
}
fn int(&mut self, raw: &str) -> Self::Term {
raw.to_owned()
}
fn float(&mut self, raw: &str) -> Self::Term {
raw.to_owned()
}
fn symbol(&mut self, name: &str) -> Self::Term {
format!("@{}", name)
}
fn ident(&mut self, raw: &str) -> Self::Term {
raw.to_owned()
}
#[cfg(glam)]
fn vector(&mut self, items: impl IntoIterator<Item=Self::Term>) -> Self::Term {
format!("[{}]", items.into_iter().format(self.comma()))
}
fn parens(&mut self, inner: Self::Term) -> Self::Term {
format!("({})", inner)
}
fn unary_expr(&mut self, op: UnaryOp, arg: Self::Term) -> Self::Term {
format!("{}{}", op, arg)
}
fn binary_expr(&mut self, lhs: Self::Term, op: BinaryOp, rhs: Self::Term) -> Self::Term {
if self.format == Format::Minified || op == BinaryOp::Pow {
format!("{}{}{}", lhs, op, rhs)
} else {
format!("{} {} {}", lhs, op, rhs)
}
}
fn call(&mut self, target: Self::Term, args: impl IntoIterator<Item=Self::Term>) -> Self::Term {
format!("{}({})", target, args.into_iter().format(self.comma()))
}
fn subscript(&mut self, target: Self::Term, index: Self::Term) -> Self::Term {
format!("{}[{}]", target, index)
}
fn access(&mut self, target: Self::Term, member: &str) -> Self::Term {
format!("{}.{}", target, member)
}
}
impl FormatHandler {
fn comma(&self) -> &str {
match self.format {
Format::Pretty => ", ",
Format::Minified => ",",
}
}
}
#[derive(Debug, Error, Eq, IsVariant, PartialEq)]
pub enum Error {
#[error("syntax error: {0}")]
Syntax(#[from] pest::error::Error<Rule>),
#[error("unsupported feature: {0}")]
Unsupported(String),
}