1mod bin_op;
2pub mod context;
3mod error;
4#[cfg(feature = "math")]
5mod math;
6#[cfg(any(feature = "string", feature = "array"))]
7mod method;
8#[cfg(feature = "semver-support")]
9mod semver_wrapper;
10mod unary;
11mod util;
12
13use anyhow::{anyhow, bail, Result};
14use bin_op::{
15 addition, bitwise_operation, compare, division, equality, exponential, multiplication,
16 remainder, subtraction, unsigned_right_shift,
17};
18use context::ContextEntry;
19use oxc::{
20 allocator::Allocator,
21 ast::ast::{
22 ArrayExpression, BinaryExpression, BinaryOperator, CallExpression, ChainElement,
23 ChainExpression, ConditionalExpression, Expression, LogicalExpression, LogicalOperator,
24 ObjectExpression, ObjectPropertyKind, Statement, StaticMemberExpression, UnaryExpression,
25 UnaryOperator,
26 },
27 parser::Parser,
28 span::SourceType,
29};
30use serde_json::{to_string, Map, Value};
31use std::collections::HashMap;
32use unary::{unary_bitwise_not, unary_negation, unary_plus};
33use util::number_from_f64;
34
35use crate::error::EvaluatorError;
36
37pub struct Evaluator {
38 context: HashMap<String, ContextEntry>,
39}
40
41impl Evaluator {
42 pub fn new(context: HashMap<String, ContextEntry>) -> Self {
43 Self { context }
44 }
45
46 pub fn evaluate(&self, expression: &str) -> Result<Value> {
47 let allocator = Allocator::default();
48 let parser = Parser::new(&allocator, expression, SourceType::cjs());
49 let parsed = parser.parse();
50 if parsed.errors.len() > 0 {
51 let errors = parsed
52 .errors
53 .iter()
54 .map(|d| d.message.to_string())
55 .collect::<String>();
56 bail!("Parsing error: {:?}", errors)
57 }
58 let program = parsed.program;
59 let stmt = &program.body.first();
60 match stmt {
61 Some(Statement::ExpressionStatement(expr)) => self.evaluate_expr(&expr.expression),
62 Some(stmt) => bail!("Unsupported statement: {:?}", stmt),
63 None => {
64 let directive = &program.directives.first();
65 if let Some(directive) = directive {
66 return Ok(Value::String(directive.directive.into_string()));
67 }
68 bail!("No statements found")
69 }
70 }
71 }
72
73 fn evaluate_expr(&self, expr: &Expression) -> Result<Value> {
74 match expr {
75 Expression::BooleanLiteral(expr) => Ok(Value::Bool(expr.value)),
76 Expression::NullLiteral(_) => Ok(Value::Null),
77 Expression::NumericLiteral(expr) => Ok(Value::Number(number_from_f64(expr.value)?)),
78 Expression::StringLiteral(expr) => Ok(Value::String(expr.value.into_string())),
81 Expression::Identifier(expr) => self.evaluate_by_name(&expr.name),
83 Expression::ArrayExpression(expr) => self.evaluate_array(expr),
86 Expression::BinaryExpression(expr) => self.evaluate_binary(expr),
90 Expression::CallExpression(expr) => self.evaluate_call(expr),
91 Expression::ChainExpression(expr) => self.evaluate_chain(expr),
92 Expression::ConditionalExpression(expr) => self.evaluate_conditional(expr),
94 Expression::LogicalExpression(expr) => self.evaluate_logical(expr),
97 Expression::ObjectExpression(expr) => self.evaluate_object(expr),
99 Expression::ParenthesizedExpression(expr) => self.evaluate_expr(&expr.expression),
100 Expression::StaticMemberExpression(expr) => self.evaluate_static_member(expr),
102 Expression::UnaryExpression(expr) => self.evaluate_unary(expr),
105 _ => bail!("Unsupported expression: {:?}", expr),
109 }
110 }
111
112 fn evaluate_array(&self, expr: &ArrayExpression) -> Result<Value> {
113 let result = expr
114 .elements
115 .iter()
116 .map(|f| self.evaluate_expr(f.to_expression()).unwrap())
117 .collect::<Vec<Value>>();
118 Ok(Value::Array(result))
119 }
120 fn evaluate_binary(&self, expr: &BinaryExpression) -> Result<Value> {
121 let left = self.evaluate_expr(&expr.left)?;
122 let right = self.evaluate_expr(&expr.right)?;
123
124 #[cfg(feature = "semver-support")]
125 {
126 use semver_wrapper::SemverWrapper;
127 use serde::Deserialize;
128 if let (Ok(left), Ok(right)) = (
129 SemverWrapper::deserialize(&left),
130 SemverWrapper::deserialize(&right),
131 ) {
132 let cmp = left.version.cmp_precedence(&right.version);
133 let result = match expr.operator {
134 BinaryOperator::Equality | BinaryOperator::StrictEquality => cmp.is_eq(),
135 BinaryOperator::Inequality | BinaryOperator::StrictInequality => !cmp.is_eq(),
136 BinaryOperator::LessThan => cmp.is_lt(),
137 BinaryOperator::LessEqualThan => cmp.is_le(),
138 BinaryOperator::GreaterThan => cmp.is_gt(),
139 BinaryOperator::GreaterEqualThan => cmp.is_gt(),
140 _ => bail!(
141 "Unsupported binary operator for semver: {:?}",
142 expr.operator
143 ),
144 };
145 return Ok(Value::Bool(result));
146 }
147 }
148
149 match expr.operator {
150 BinaryOperator::Equality => Ok(Value::Bool(equality(&left, &right, false))),
151 BinaryOperator::Inequality => Ok(Value::Bool(!equality(&left, &right, false))),
152 BinaryOperator::StrictEquality => Ok(Value::Bool(equality(&left, &right, true))),
153 BinaryOperator::StrictInequality => Ok(Value::Bool(!equality(&left, &right, true))),
154 BinaryOperator::LessThan => Ok(Value::Bool(compare(&left, &right, |l, r| l < r))),
155 BinaryOperator::LessEqualThan => Ok(Value::Bool(compare(&left, &right, |l, r| l <= r))),
156 BinaryOperator::GreaterThan => Ok(Value::Bool(compare(&left, &right, |l, r| l > r))),
157 BinaryOperator::GreaterEqualThan => {
158 Ok(Value::Bool(compare(&left, &right, |l, r| l >= r)))
159 }
160 BinaryOperator::Addition => addition(left, right),
161 BinaryOperator::Subtraction => subtraction(left, right),
162 BinaryOperator::Multiplication => multiplication(left, right),
163 BinaryOperator::Division => division(left, right),
164 BinaryOperator::Remainder => remainder(left, right),
165 BinaryOperator::Exponential => exponential(left, right),
166 BinaryOperator::ShiftLeft => bitwise_operation(left, right, |l, r| l << (r & 0x1F)),
167 BinaryOperator::ShiftRight => bitwise_operation(left, right, |l, r| l >> (r & 0x1F)),
168 BinaryOperator::ShiftRightZeroFill => unsigned_right_shift(left, right),
169 BinaryOperator::BitwiseOR => bitwise_operation(left, right, |l, r| l | r),
170 BinaryOperator::BitwiseXOR => bitwise_operation(left, right, |l, r| l ^ r),
171 BinaryOperator::BitwiseAnd => bitwise_operation(left, right, |l, r| l & r),
172 _ => bail!("Unsupported binary operator: {:?}", expr.operator),
173 }
174 }
175 fn evaluate_call(&self, expr: &CallExpression) -> Result<Value> {
176 let args = expr
177 .arguments
178 .iter()
179 .map(|f| self.evaluate_expr(f.to_expression()).unwrap())
180 .collect::<Vec<Value>>();
181 match &expr.callee {
182 Expression::Identifier(expr) => {
183 let callee_name = expr.name.to_string();
184
185 #[cfg(feature = "semver-support")]
186 {
187 use semver_wrapper::SemverWrapper;
188 if callee_name == "semver" {
189 return Ok(serde_json::json!(SemverWrapper::from_values(args)?));
190 }
191 }
192
193 match self.context.get(&callee_name) {
194 Some(ContextEntry::Function(f)) => Ok(f(args)),
195 _ => {
196 #[cfg(feature = "string")]
197 {
198 if let Some(Value::String(callee)) = args.get(0) {
199 return match Evaluator::evaluate_str_method(
200 callee,
201 &callee_name,
202 args[1..].to_vec(),
203 ) {
204 Ok(value) => Ok(value),
205 _ => {
206 #[cfg(feature = "math")]
207 {
208 if let Ok(result) = Evaluator::evaluate_math_function(
209 &callee_name,
210 args,
211 ) {
212 return Ok(result);
213 }
214 }
215 bail!("{:?} not found in function context", callee_name)
216 }
217 };
218 }
219 }
220 #[cfg(feature = "array")]
221 {
222 if let Some(Value::Array(callee)) = args.get(0) {
223 return match Evaluator::evaluate_array_method(
224 callee,
225 &callee_name,
226 args[1..].to_vec(),
227 ) {
228 Ok(value) => Ok(value),
229 _ => bail!("{:?} not found in function context", callee_name),
230 };
231 }
232 }
233 #[cfg(feature = "math")]
234 {
235 if let Some(Value::Number(_)) = args.get(0) {
236 return Evaluator::evaluate_math_function(&callee_name, args);
237 }
238 }
239 bail!("{:?} not found in function context", callee_name)
240 }
241 }
242 }
243 _ => {
244 let callee = self.evaluate_expr(&expr.callee)?;
245
246 if let Value::String(callee) = &callee {
247 #[cfg(feature = "string")]
248 {
249 let callee_name = expr.callee_name().unwrap_or_default();
250 return Evaluator::evaluate_str_method(callee, callee_name, args);
251 }
252 #[cfg(not(feature = "string"))]
253 bail!("'string' feature is not enabled. callee: {:?}", callee)
254 } else if let Value::Array(callee) = &callee {
255 #[cfg(feature = "array")]
256 {
257 let callee_name = expr.callee_name().unwrap_or_default();
258 return Evaluator::evaluate_array_method(callee, callee_name, args);
259 }
260 #[cfg(not(feature = "array"))]
261 bail!("'array' feature is not enabled. callee: {:?}", callee)
262 }
263
264 bail!("Unsupported method for {:?}", callee);
265 }
266 }
267 }
268 fn evaluate_chain(&self, expr: &ChainExpression) -> Result<Value> {
269 let ex = &expr.expression;
270 match ex {
271 ChainElement::CallExpression(expr) => self.evaluate_call(expr),
272 ChainElement::StaticMemberExpression(expr) => self.evaluate_static_member(expr),
273 _ => bail!("Unsupported ChainExpression: {:?}", ex),
274 }
275 }
276 fn evaluate_conditional(&self, expr: &ConditionalExpression) -> Result<Value> {
277 let test = self.evaluate_value(&self.evaluate_expr(&expr.test)?);
278 let expr = match test {
279 true => &expr.consequent,
280 false => &expr.alternate,
281 };
282 self.evaluate_expr(expr)
283 }
284 fn evaluate_logical(&self, expr: &LogicalExpression) -> Result<Value> {
285 let operator = expr.operator;
286 let left = &self.evaluate_expr(&expr.left)?;
287 let right = &self.evaluate_expr(&expr.right)?;
288 match operator {
289 LogicalOperator::And => Ok(Value::Bool(
290 self.evaluate_value(left) && self.evaluate_value(right),
291 )),
292 LogicalOperator::Coalesce => match left {
293 Value::Null => Ok(right.clone()),
294 _ => Ok(left.clone()),
295 },
296 LogicalOperator::Or => match self.evaluate_value(left) {
297 true => Ok(left.clone()),
298 false => Ok(right.clone()),
299 },
300 }
301 }
302 fn evaluate_object(&self, expr: &ObjectExpression) -> Result<Value> {
303 let properties = &expr.properties;
304 let mut map = Map::new();
305 for property in properties {
306 match property {
307 ObjectPropertyKind::ObjectProperty(object_property) => {
308 let key = match object_property.key.as_expression() {
309 Some(v) => v,
310 _ => bail!("Object key is not an expression"),
311 };
312 let key = self.evaluate_expr(key)?;
313 let key = to_string(&key)?;
314 let value = self.evaluate_expr(&object_property.value)?;
315 map.insert(key, value);
316 }
317 _ => bail!("Unsupported ObjectPropertyKind: {:?}", property),
318 }
319 }
320 Ok(Value::Object(map))
321 }
322 fn evaluate_static_member(&self, expr: &StaticMemberExpression) -> Result<Value> {
323 let obj = if expr.optional {
324 match self.evaluate_expr(&expr.object) {
325 Ok(value) => value,
326 Err(e) => {
327 if e.downcast_ref::<EvaluatorError>().map_or(false, |err| {
328 matches!(err, EvaluatorError::VariableNotFound(_))
329 }) {
330 return Ok(Value::Null);
331 }
332 return Err(e);
333 }
334 }
335 } else {
336 self.evaluate_expr(&expr.object)?
337 };
338 let property = expr.property.name.to_string();
339 match &obj {
340 Value::Object(map) => {
341 let value = map.get(&property);
342 if let Some(value) = value {
343 return Ok(value.clone());
344 }
345 if expr.optional {
346 return Ok(Value::Null);
347 }
348 Err(anyhow!(EvaluatorError::PropertyNotFound(
349 obj.to_string(),
350 property.clone(),
351 )))
352 }
353 _ => Ok(obj),
354 }
355 }
356 fn evaluate_unary(&self, expr: &UnaryExpression) -> Result<Value> {
357 let operator = expr.operator;
358 let value = self.evaluate_expr(&expr.argument)?;
359 match operator {
360 UnaryOperator::UnaryPlus => unary_plus(value),
361 UnaryOperator::UnaryNegation => unary_negation(value),
362 UnaryOperator::BitwiseNot => unary_bitwise_not(value),
363 UnaryOperator::LogicalNot => Ok(Value::Bool(!self.evaluate_value(&value))),
364 _ => {
365 bail!("Unsupported UnaryOperator {:?}", operator)
366 }
367 }
368 }
369
370 fn evaluate_by_name(&self, name: &str) -> Result<Value> {
371 match self.context.get(name) {
372 Some(ContextEntry::Variable(value)) => Ok(value.clone()),
373 _ => Err(anyhow!(EvaluatorError::VariableNotFound(name.to_string()))),
374 }
375 }
376 fn evaluate_value(&self, value: &Value) -> bool {
377 match value {
378 Value::Array(arr) => !arr.is_empty(),
379 Value::Bool(bool) => *bool,
380 Value::Null => false,
381 Value::Number(number) => number.as_f64().is_some_and(|value| value != 0.0),
382 Value::Object(_) => true,
383 Value::String(str) => !str.is_empty(),
384 }
385 }
386 #[cfg(feature = "string")]
387 fn evaluate_str_method(callee: &str, callee_name: &str, args: Vec<Value>) -> Result<Value> {
388 use method::string::StringMethod;
389
390 let str_method = StringMethod::new(args);
391 match callee_name {
392 "replace" => str_method.replace(callee),
393 "contains" => str_method.contains(callee),
394 "split" => str_method.split(callee),
395 "indexOf" => str_method.index_of(callee),
396 "lastIndexOf" => str_method.last_index_of(callee),
397 "toUpperCase" => str_method.to_upper_case(callee),
398 "toLowerCase" => str_method.to_lower_case(callee),
399 "substring" => str_method.substring(callee),
400 "startsWith" => str_method.starts_with(callee),
401 "endsWith" => str_method.ends_with(callee),
402 "regexReplace" => str_method.regex_replace(callee),
403 "length" => str_method.length(callee),
404 "trim" => str_method.trim(callee),
405 _ => {
406 bail!("Unknown string method: {}", callee_name);
407 }
408 }
409 }
410 #[cfg(feature = "array")]
411 fn evaluate_array_method(
412 callee: &Vec<Value>,
413 callee_name: &str,
414 args: Vec<Value>,
415 ) -> Result<Value> {
416 use method::array::ArrayMethod;
417
418 let array_method = ArrayMethod::new(args);
419 match callee_name {
420 "join" => array_method.join(callee),
421 _ => bail!("Unknown array method: {}", callee_name),
422 }
423 }
424 #[cfg(feature = "math")]
425 fn evaluate_math_function(callee_name: &str, mut args: Vec<Value>) -> Result<Value> {
426 let value = args.remove(0).take();
427 match args.len() {
428 1 => match callee_name {
429 "floor" => math::unary_function(value, f64::floor),
430 "ceil" => math::unary_function(value, f64::ceil),
431 "round" => math::unary_function(value, f64::round),
432 "sin" => math::unary_function(value, f64::sin),
433 "cos" => math::unary_function(value, f64::cos),
434 "tan" => math::unary_function(value, f64::tan),
435 "asin" => math::unary_function(value, f64::asin),
436 "acos" => math::unary_function(value, f64::acos),
437 "atan" => math::unary_function(value, f64::atan),
438 "sqrt" => math::unary_function(value, f64::sqrt),
439 "abs" => math::unary_function(value, f64::abs),
440 "clamp" => math::unary_function(value, |x| x.clamp(0.0, 1.0)),
441 "bitwiseNot" => unary_bitwise_not(value),
442 _ => bail!("{:?} not found in function context", callee_name),
443 },
444 2 => {
445 let second = args.remove(0).take();
446 match callee_name {
447 "atan2" => math::binary_function(value, second, f64::atan2),
448 "min" => math::binary_function(value, second, f64::min),
449 "max" => math::binary_function(value, second, f64::max),
450 "mod" => remainder(value, second),
451 "pow" => math::binary_function(value, second, f64::powf),
452 "bitwiseAnd" => bitwise_operation(value, second, |l, r| l & r),
453 "bitwiseOr" => bitwise_operation(value, second, |l, r| l | r),
454 "bitwiseLeft" => bitwise_operation(value, second, |l, r| l << (r & 0x1F)),
455 "bitwiseRight" => bitwise_operation(value, second, |l, r| l >> (r & 0x1F)),
456 _ => bail!("{:?} not found in function context", callee_name),
457 }
458 }
459 _ => bail!("{:?} not found in function context", callee_name),
460 }
461 }
462}