1extern crate core;
2
3use std::{convert::TryFrom, sync::Arc};
4
5use thiserror::Error;
6
7mod macros;
8
9pub mod common;
10pub mod context;
11pub mod parser;
12
13pub use common::ast::IdedExpr;
14use common::ast::SelectExpr;
15pub use context::Context;
16pub use functions::FunctionContext;
17pub use objects::{ResolveResult, Value};
18use parser::{Expression, ExpressionReferences, Parser};
19pub use parser::{ParseError, ParseErrors};
20pub mod functions;
21mod magic;
22pub mod objects;
23mod resolvers;
24
25#[cfg(feature = "time")]
26mod duration;
27
28#[cfg(feature = "ip")]
29pub use ser::Ip;
30#[cfg(feature = "time")]
31pub use ser::{Duration, Timestamp};
32
33mod ser;
34pub use ser::{SerializationError, to_value};
35
36#[cfg(feature = "json")]
37mod json;
38#[cfg(feature = "json")]
39pub use json::ConvertToJsonError;
40use magic::FromContext;
41
42pub mod extractors {
43 pub use crate::magic::{Arguments, Identifier, This};
44}
45
46#[derive(Error, Clone, Debug, PartialEq)]
47#[non_exhaustive]
48pub enum ExecutionError {
49 #[error("Invalid argument count: expected {expected}, got {actual}")]
50 InvalidArgumentCount { expected: usize, actual: usize },
51 #[error("Invalid argument type: {:?}", .target)]
52 UnsupportedTargetType { target: Value },
53 #[error("Method '{method}' not supported on type '{target:?}'")]
54 NotSupportedAsMethod { method: String, target: Value },
55 #[error("Unable to use value '{0:?}' as a key")]
58 UnsupportedKeyType(Value),
59 #[error("Unexpected type: got '{got}', want '{want}'")]
60 UnexpectedType { got: String, want: String },
61 #[error("No such key: {0}")]
64 NoSuchKey(Arc<String>),
65 #[error("No such overload")]
68 NoSuchOverload,
69 #[error("Undeclared reference to '{0}'")]
72 UndeclaredReference(Arc<String>),
73 #[error("Missing argument or target")]
76 MissingArgumentOrTarget,
77 #[error("{0:?} can not be compared to {1:?}")]
79 ValuesNotComparable(Value, Value),
80 #[error("Unsupported unary operator '{0}': {1:?}")]
82 UnsupportedUnaryOperator(&'static str, Value),
83 #[error("Unsupported binary operator '{0}': {1:?}, {2:?}")]
86 UnsupportedBinaryOperator(&'static str, Value, Value),
87 #[error("Cannot use value as map index: {0:?}")]
89 UnsupportedMapIndex(Value),
90 #[error("Cannot use value as list index: {0:?}")]
92 UnsupportedListIndex(Value),
93 #[error("Cannot use value {0:?} to index {1:?}")]
95 UnsupportedIndex(Value, Value),
96 #[error("Unsupported function call identifier type: {0:?}")]
99 UnsupportedFunctionCallIdentifierType(Expression),
100 #[error("Unsupported fields construction: {0:?}")]
103 UnsupportedFieldsConstruction(SelectExpr),
104 #[error("Error executing function '{function}': {message}")]
106 FunctionError { function: String, message: String },
107 #[error("Division by zero of {0:?}")]
108 DivisionByZero(Value),
109 #[error("Remainder by zero of {0:?}")]
110 RemainderByZero(Value),
111 #[error("Overflow from binary operator '{0}': {1:?}, {2:?}")]
112 Overflow(&'static str, Value, Value),
113}
114
115impl ExecutionError {
116 pub fn no_such_key(name: &str) -> Self {
117 ExecutionError::NoSuchKey(Arc::new(name.to_string()))
118 }
119
120 pub fn undeclared_reference(name: &str) -> Self {
121 ExecutionError::UndeclaredReference(Arc::new(name.to_string()))
122 }
123
124 pub fn invalid_argument_count(expected: usize, actual: usize) -> Self {
125 ExecutionError::InvalidArgumentCount {
126 expected,
127 actual,
128 }
129 }
130
131 pub fn function_error<E: ToString>(function: &str, error: E) -> Self {
132 ExecutionError::FunctionError {
133 function: function.to_string(),
134 message: error.to_string(),
135 }
136 }
137
138 pub fn unsupported_target_type(target: Value) -> Self {
139 ExecutionError::UnsupportedTargetType {
140 target,
141 }
142 }
143
144 pub fn not_supported_as_method(method: &str, target: Value) -> Self {
145 ExecutionError::NotSupportedAsMethod {
146 method: method.to_string(),
147 target,
148 }
149 }
150
151 pub fn unsupported_key_type(value: Value) -> Self {
152 ExecutionError::UnsupportedKeyType(value)
153 }
154
155 pub fn missing_argument_or_target() -> Self {
156 ExecutionError::MissingArgumentOrTarget
157 }
158}
159
160#[derive(Debug, Clone)]
161pub struct Program {
162 expression: Expression,
163}
164
165impl Program {
166 pub fn compile(source: &str) -> Result<Program, ParseErrors> {
167 let parser = Parser::default();
168 parser.parse(source).map(|expression| Program {
169 expression,
170 })
171 }
172
173 pub fn execute(&self, context: &Context) -> ResolveResult {
174 Value::resolve(&self.expression, context)
175 }
176
177 pub fn references(&self) -> ExpressionReferences<'_> {
189 self.expression.references()
190 }
191
192 pub fn expression(&self) -> &Expression {
194 &self.expression
195 }
196}
197
198impl TryFrom<&str> for Program {
199 type Error = ParseErrors;
200
201 fn try_from(value: &str) -> Result<Self, Self::Error> {
202 Program::compile(value)
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use std::{collections::HashMap, convert::TryInto};
209
210 use crate::{
211 ExecutionError, Program,
212 context::Context,
213 objects::{ResolveResult, Value},
214 };
215
216 pub(crate) fn test_script(script: &str, ctx: Option<Context>) -> ResolveResult {
218 let program = match Program::compile(script) {
219 Ok(p) => p,
220 Err(e) => panic!("{}", e),
221 };
222 program.execute(&ctx.unwrap_or_default())
223 }
224
225 #[test]
226 fn parse() {
227 Program::compile("1 + 1").unwrap();
228 }
229
230 #[test]
231 fn from_str() {
232 let input = "1.1";
233 let _p: Program = input.try_into().unwrap();
234 }
235
236 #[test]
237 fn variables() {
238 fn assert_output(script: &str, expected: ResolveResult) {
239 let mut ctx = Context::default();
240 ctx.add_variable_from_value("foo", HashMap::from([("bar", 1i64)]));
241 ctx.add_variable_from_value("arr", vec![1i64, 2, 3]);
242 ctx.add_variable_from_value("str", "foobar".to_string());
243 assert_eq!(test_script(script, Some(ctx)), expected);
244 }
245
246 assert_output("length([1, 2, 3]) == 3", Ok(true.into()));
248 assert_output("length([length([42]), 2, 3]) == 3", Ok(true.into()));
249 assert_output("length([]) == 3", Ok(false.into()));
250
251 assert_output("foo.bar == 1", Ok(true.into()));
253
254 assert_output("arr[0] == 1", Ok(true.into()));
256
257 assert_output("str[0] == \"f\"", Ok(true.into()));
259 }
260
261 #[test]
262 fn references() {
263 let p = Program::compile("[1, 1].map(x, x * 2)").unwrap();
264 assert!(p.references().has_variable("x"));
265 assert_eq!(p.references().variables().len(), 1);
266 }
267
268 #[test]
269 fn test_execution_errors() {
270 let tests = vec![
271 ("no such key", "foo.baz.bar == 1", ExecutionError::no_such_key("baz")),
272 (
273 "undeclared reference",
274 "missing == 1",
275 ExecutionError::undeclared_reference("missing"),
276 ),
277 (
278 "undeclared method",
279 "1.missing()",
280 ExecutionError::undeclared_reference("missing"),
281 ),
282 (
283 "undeclared function",
284 "missing(1)",
285 ExecutionError::undeclared_reference("missing"),
286 ),
287 (
288 "unsupported key type",
289 "{null: true}",
290 ExecutionError::unsupported_key_type(Value::Null),
291 ),
292 ];
293
294 for (name, script, error) in tests {
295 let mut ctx = Context::default();
296 ctx.add_variable_from_value("foo", HashMap::from([("bar", 1)]));
297 let res = test_script(script, Some(ctx));
298 assert_eq!(res, error.into(), "{name}");
299 }
300 }
301}