expr/
parser.rs

1use crate::ast::node::Node;
2use crate::ast::program::Program;
3use crate::functions::{array, string, ExprCall, Function};
4use crate::{ExprPest, Rule};
5use crate::{bail, Context, Error, Result, Value};
6use indexmap::IndexMap;
7use pest::Parser as PestParser;
8use std::fmt;
9use std::fmt::{Debug, Formatter};
10
11/// Main struct for parsing and evaluating expr programs
12///
13/// Example:
14///
15/// ```
16/// use expr::{Context, Parser};
17/// let ctx = Context::from_iter([("foo", 1), ("bar", 2)]);
18/// let p = Parser::new();
19/// assert_eq!(p.eval("foo + bar", &ctx).unwrap().to_string(), "3");
20/// ```
21#[derive(Default)]
22pub struct Parser<'a> {
23    pub(crate) functions: IndexMap<String, Function<'a>>,
24}
25
26impl Debug for Parser<'_> {
27    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
28        f.debug_struct("ExprParser").finish()
29    }
30}
31
32impl<'a> Parser<'a> {
33    /// Create a new parser with default set of functions
34    pub fn new() -> Self {
35        let mut p = Self {
36            functions: IndexMap::new(),
37        };
38        string::add_string_functions(&mut p);
39        array::add_array_functions(&mut p);
40        p
41    }
42
43    /// Add a function for expr programs to call
44    ///
45    /// Example:
46    /// ```
47    /// use std::collections::HashMap;
48    /// use expr::{Context, Parser, Value};
49    ///
50    /// let mut p = Parser::new();
51    /// let ctx = Context::default();
52    /// p.add_function("add", |c| {
53    ///   let mut sum = 0;
54    ///     for arg in c.args {
55    ///       if let Value::Number(n) = arg {
56    ///         sum += n;
57    ///        } else {
58    ///          panic!("Invalid argument: {arg:?}");
59    ///        }
60    ///     }
61    ///   Ok(sum.into())
62    /// });
63    /// assert_eq!(p.eval("add(1, 2, 3)", &ctx).unwrap().to_string(), "6");
64    /// ```
65    pub fn add_function<F>(&mut self, name: &str, f: F)
66    where
67        F: Fn(ExprCall) -> Result<Value> + 'a + Sync + Send,
68    {
69        self.functions.insert(name.to_string(), Box::new(f));
70    }
71
72    /// Parse an expr program to be run later
73    pub fn compile(&self, code: &str) -> Result<Program> {
74        #[cfg(debug_assertions)]
75        pest::set_error_detail(true);
76        let pairs = ExprPest::parse(Rule::full, code).map_err(|e| Error::PestError(Box::new(e)))?;
77        Ok(pairs.into())
78    }
79
80    /// Run a compiled expr program
81    pub fn run(&self, program: Program, ctx: &Context) -> Result<Value> {
82        let mut ctx = ctx.clone();
83        ctx.insert("$env".to_string(), Value::Map(ctx.0.clone()));
84        for (id, expr) in program.lines {
85            ctx.insert(id, self.eval_expr(&ctx, expr)?);
86        }
87        self.eval_expr(&ctx, program.expr)
88    }
89
90    /// Compile and run an expr program in one step
91    ///
92    /// Example:
93    /// ```
94    /// use std::collections::HashMap;
95    /// use expr::{Context, Parser};
96    /// let p = Parser::default();
97    /// let ctx = Context::default();
98    /// assert_eq!(p.eval("1 + 2", &ctx).unwrap().to_string(), "3");
99    /// ```
100    pub fn eval(&self, code: &str, ctx: &Context) -> Result<Value> {
101        let program = self.compile(code)?;
102        self.run(program, ctx)
103    }
104
105    pub fn eval_expr(&self, ctx: &Context, node: Node) -> Result<Value> {
106        let value = match node {
107            Node::Value(value) => value,
108            Node::Ident(id) => {
109                if let Some(value) = ctx.get(&id) {
110                    value.clone()
111                } else if let Some(item) = ctx
112                    .get("#")
113                    .and_then(|o| o.as_map())
114                    .and_then(|m| m.get(&id))
115                {
116                    item.clone()
117                } else {
118                    bail!("Unknown variable: {id}")
119                }
120            }
121            Node::Func {
122                ident,
123                args,
124                predicate,
125            } => {
126                let args = args
127                    .into_iter()
128                    .map(|e| self.eval_expr(ctx, e))
129                    .collect::<Result<_>>()?;
130                self.exec_func(ctx, ident, args, predicate.map(|l| *l))?
131            },
132            Node::Operation {
133                left,
134                operator,
135                right,
136            } => self.eval_operator(ctx, operator, *left, *right)?,
137            Node::Unary { operator, node } => self.eval_unary_operator(ctx, operator, *node)?,
138            Node::Postfix { operator, node } => self.eval_postfix_operator(ctx, operator, *node)?,
139            Node::Array(a) => Value::Array(
140                a.into_iter()
141                    .map(|e| self.eval_expr(ctx, e))
142                    .collect::<Result<_>>()?,
143            ), // node => bail!("unexpected node: {node:?}"),
144            Node::Range(start, end) => match (self.eval_expr(ctx, *start)?, self.eval_expr(ctx, *end)?) {
145                (Value::Number(start), Value::Number(end)) => {
146                    Value::Array((start..=end).map(Value::Number).collect())
147                }
148                (start, end) => bail!("Invalid range: {start:?}..{end:?}"),
149            }
150        };
151        Ok(value)
152    }
153}