1use rslint_parser::{
2 ast::{BinExpr, BinOp, CondExpr, DotExpr, Expr, Name, NameRef, UnaryExpr, UnaryOp},
3 parse_text, AstNode, SyntaxKind, SyntaxNode,
4};
5
6use anyhow::Result;
7use thiserror::Error;
8
9use serde_json::Value;
10
11use std::collections::HashMap;
12
13#[derive(Error, Debug)]
14#[error("Evaluation error")]
15pub struct EvaluationError {
16 #[from]
17 source: NodeError,
18}
19
20#[derive(Error, Debug)]
21#[error("Node error {message}, node: {node:?}")]
22pub struct NodeError {
23 message: String,
24 node: Option<SyntaxNode>,
25}
26
27#[cfg(feature = "logging")]
28use scribe_rust::Logger;
29#[cfg(feature = "logging")]
30use std::sync::Arc;
31
32pub struct Evaluator {
33 context: HashMap<String, serde_json::Value>,
34 #[cfg(feature = "logging")]
35 logger: Arc<Logger>,
36}
37
38impl Evaluator {
39 pub fn new(
40 context: HashMap<String, serde_json::Value>,
41 #[cfg(feature = "logging")] logger: Arc<Logger>,
42 ) -> Self {
43 Evaluator {
44 context,
45 #[cfg(feature = "logging")]
46 logger,
47 }
48 }
49
50 pub fn evaluate(&self, expression: &str) -> Result<Value> {
51 let ast = parse_text(expression, 0).syntax();
52 let untyped_expr_node = match ast.first_child() {
53 Some(node) => node,
54 None => {
55 return Err(NodeError {
56 message: "Empty expression".to_string(),
57 node: None,
58 }
59 .into())
60 }
61 };
62
63 #[cfg(feature = "logging")]
64 self.logger.trace(&format!(
65 "Expression AST:\n\n{:#?}\n-----------------",
66 untyped_expr_node
67 ));
68
69 let result = self.evaluate_node(&untyped_expr_node)?;
70
71 #[cfg(feature = "logging")]
72 self.logger.trace(&format!("Result: {}", result));
73
74 Ok(result)
75 }
76
77 fn evaluate_node(&self, node: &SyntaxNode) -> Result<Value, NodeError> {
78 #[cfg(feature = "logging")]
79 self.logger.trace(&format!(
80 "Evaluating NodeKind: {:#?}, {:?}",
81 node.kind(),
82 node.to_string()
83 ));
84
85 let res = match node.kind() {
86 SyntaxKind::EXPR_STMT => {
87 let expr = node.first_child().ok_or_else(|| NodeError {
88 message: "[Empty expression]".to_string(),
89 node: None,
90 })?;
91 self.evaluate_node(&expr)
92 }
93 SyntaxKind::DOT_EXPR => self.evaluate_dot_expr(&DotExpr::cast(node.clone()).unwrap()),
94 SyntaxKind::NAME_REF => self.evaluate_name_ref(&NameRef::cast(node.clone()).unwrap()),
95 SyntaxKind::NAME => self.evaluate_name(&Name::cast(node.clone()).unwrap()),
96 SyntaxKind::BIN_EXPR => self.evaluate_bin_expr(&BinExpr::cast(node.clone()).unwrap()),
97 SyntaxKind::LITERAL => self.evaluate_literal(&Expr::cast(node.clone()).unwrap()),
98 SyntaxKind::COND_EXPR => {
99 self.evaluate_cond_expr(&CondExpr::cast(node.clone()).unwrap())
100 }
101 SyntaxKind::IDENT => self.evaluate_identifier(&Expr::cast(node.clone()).unwrap()),
102 SyntaxKind::UNARY_EXPR => {
103 self.evaluate_prefix_expr(&UnaryExpr::cast(node.clone()).unwrap())
104 }
105 _ => Err(NodeError {
106 message: format!("Unsupported syntax kind: {:?}", node.kind()),
107 node: Some(node.clone()),
108 }),
109 };
110
111 #[cfg(feature = "logging")]
112 self.logger.trace(&format!(
113 "NodeKind: {:?} => {:#?}",
114 node.kind(),
115 res.as_ref()
116 ));
117
118 res
119 }
120
121 fn evaluate_bin_expr(&self, bin_expr: &BinExpr) -> Result<Value, NodeError> {
122 #[cfg(feature = "logging")]
123 self.logger.trace(&format!(
124 "Evaluating Binary Expression: {:#?}",
125 bin_expr.to_string()
126 ));
127
128 let left = bin_expr.lhs().ok_or_else(|| NodeError {
129 message: "[Empty BinExpr Left Expression]".to_string(),
130 node: Some(bin_expr.syntax().clone()),
131 })?;
132 let right = bin_expr.rhs().ok_or_else(|| NodeError {
133 message: "[Empty BinExpr Right Expression]".to_string(),
134 node: Some(bin_expr.syntax().clone()),
135 })?;
136
137 let left_value = self.evaluate_node(left.syntax())?;
138 let right_value = self.evaluate_node(right.syntax())?;
139
140 let op = bin_expr.op_details();
141
142 #[cfg(feature = "logging")]
143 self.logger
144 .trace(&format!("BinaryOp left_value {:?}", left_value));
145
146 #[cfg(feature = "logging")]
147 self.logger
148 .trace(&format!("BinaryOp right_value {:?}", right_value));
149
150 #[cfg(feature = "logging")]
151 self.logger.trace(&format!("BinaryOp op_details {:?}", op));
152
153 let result = match op {
154 Some((_, BinOp::Plus)) => self.add_values(left_value, right_value),
155 Some((_, BinOp::Minus)) => self.subtract_values(left_value, right_value),
156 Some((_, BinOp::Times)) => self.multiply_values(left_value, right_value),
157 Some((_, BinOp::Divide)) => self.divide_values(left_value, right_value),
158 Some((_, BinOp::Remainder)) => self.modulo_values(left_value, right_value),
159 Some((_, BinOp::LogicalAnd)) => Ok(Value::Bool(
160 self.to_boolean(&left_value)? && self.to_boolean(&right_value)?,
161 )),
162 Some((_, BinOp::LogicalOr)) => Ok(Value::Bool(
163 self.to_boolean(&left_value)? || self.to_boolean(&right_value)?,
164 )),
165 Some((_, BinOp::Equality)) | Some((_, BinOp::StrictEquality)) => Ok(Value::Bool(
166 self.abstract_equality(&left_value, &right_value),
167 )),
168 Some((_, BinOp::Inequality)) | Some((_, BinOp::StrictInequality)) => Ok(Value::Bool(
169 !self.abstract_equality(&left_value, &right_value),
170 )),
171 Some((_, BinOp::GreaterThan)) => {
172 self.compare_values(&left_value, &right_value, |a, b| a > b)
173 }
174 Some((_, BinOp::LessThan)) => {
175 self.compare_values(&left_value, &right_value, |a, b| a < b)
176 }
177 Some((_, BinOp::GreaterThanOrEqual)) => {
178 self.compare_values(&left_value, &right_value, |a, b| a >= b)
179 }
180 Some((_, BinOp::LessThanOrEqual)) => {
181 self.compare_values(&left_value, &right_value, |a, b| a <= b)
182 }
183 _ => Err(NodeError {
184 message: "Unsupported binary operator".to_string(),
185 node: Some(bin_expr.syntax().clone()),
186 }),
187 }?;
188
189 #[cfg(feature = "logging")]
190 self.logger.trace(&format!("Binary Result: {:?}", result));
191
192 Ok(result)
193 }
194
195 fn add_values(&self, left: Value, right: Value) -> Result<Value, NodeError> {
196 match (left.clone(), right.clone()) {
197 (Value::Number(l), Value::Number(r)) => {
198 let sum = l.as_f64().unwrap() + r.as_f64().unwrap();
199 Ok(Value::Number(serde_json::Number::from_f64(sum).unwrap()))
200 }
201 (Value::String(l), Value::String(r)) => Ok(Value::String(l + &r)),
202 (Value::String(l), r) => Ok(Value::String(l + &self.value_to_string(&r))),
203 (l, Value::String(r)) => Ok(Value::String(self.value_to_string(&l) + &r)),
204 _ => {
205 let l_str = self.value_to_string(&left);
207 let r_str = self.value_to_string(&right);
208 Ok(Value::String(l_str + &r_str))
209 }
210 }
211 }
212
213 fn subtract_values(&self, left: Value, right: Value) -> Result<Value, NodeError> {
214 let l_num = self.to_number(&left)?;
215 let r_num = self.to_number(&right)?;
216 Ok(Value::Number(
217 serde_json::Number::from_f64(l_num - r_num).unwrap(),
218 ))
219 }
220
221 fn multiply_values(&self, left: Value, right: Value) -> Result<Value, NodeError> {
222 let l_num = self.to_number(&left)?;
223 let r_num = self.to_number(&right)?;
224 Ok(Value::Number(
225 serde_json::Number::from_f64(l_num * r_num).unwrap(),
226 ))
227 }
228
229 fn divide_values(&self, left: Value, right: Value) -> Result<Value, NodeError> {
230 let l_num = self.to_number(&left)?;
231 let r_num = self.to_number(&right)?;
232 if r_num == 0.0 {
233 return Err(NodeError {
234 message: "Division by zero".to_string(),
235 node: None,
236 });
237 }
238 Ok(Value::Number(
239 serde_json::Number::from_f64(l_num / r_num).unwrap(),
240 ))
241 }
242
243 fn modulo_values(&self, left: Value, right: Value) -> Result<Value, NodeError> {
244 let l_num = self.to_number(&left)?;
245 let r_num = self.to_number(&right)?;
246 Ok(Value::Number(
247 serde_json::Number::from_f64(l_num % r_num).unwrap(),
248 ))
249 }
250
251 fn compare_values<F>(&self, left: &Value, right: &Value, cmp: F) -> Result<Value, NodeError>
252 where
253 F: Fn(f64, f64) -> bool,
254 {
255 let l_num = self.to_number(left)?;
256 let r_num = self.to_number(right)?;
257 Ok(Value::Bool(cmp(l_num, r_num)))
258 }
259
260 fn evaluate_prefix_expr(&self, prefix_expr: &UnaryExpr) -> Result<Value, NodeError> {
261 #[cfg(feature = "logging")]
262 self.logger.trace(&format!(
263 "Evaluating Prefix Expression: {:#?}",
264 prefix_expr.to_string()
265 ));
266 let expr = prefix_expr.expr().ok_or_else(|| NodeError {
267 message: "[Empty PrefixExpr Expression]".to_string(),
268 node: Some(prefix_expr.syntax().clone()),
269 })?;
270 let expr_value = self.evaluate_node(expr.syntax())?;
271
272 let op = prefix_expr.op_details();
273
274 let result = match op {
275 Some((_, UnaryOp::LogicalNot)) => Value::Bool(!self.to_boolean(&expr_value)?),
276 Some((_, UnaryOp::Minus)) => {
277 let num = self.to_number(&expr_value)?;
278 Value::Number(serde_json::Number::from_f64(-num).unwrap())
279 }
280 Some((_, UnaryOp::Plus)) => {
281 let num = self.to_number(&expr_value)?;
282 Value::Number(serde_json::Number::from_f64(num).unwrap())
283 }
284 _ => {
285 return Err(NodeError {
286 message: "Unsupported unary operator".to_string(),
287 node: Some(prefix_expr.syntax().clone()),
288 })
289 }
290 };
291
292 #[cfg(feature = "logging")]
293 self.logger.trace(&format!("Prefix Result: {:?}", result));
294
295 Ok(result)
296 }
297
298 fn evaluate_cond_expr(&self, cond_expr: &CondExpr) -> Result<Value, NodeError> {
299 #[cfg(feature = "logging")]
300 self.logger.trace(&format!(
301 "Evaluating Conditional Expression: {:#?}",
302 cond_expr.to_string()
303 ));
304 let cond = cond_expr.test().ok_or_else(|| NodeError {
305 message: "[Empty CondExpr Test Expression]".to_string(),
306 node: Some(cond_expr.syntax().clone()),
307 })?;
308 let true_expr = cond_expr.cons().ok_or_else(|| NodeError {
309 message: "[Empty CondExpr Consequent Expression]".to_string(),
310 node: Some(cond_expr.syntax().clone()),
311 })?;
312 let false_expr = cond_expr.alt().ok_or_else(|| NodeError {
313 message: "[Empty CondExpr Alternate Expression]".to_string(),
314 node: Some(cond_expr.syntax().clone()),
315 })?;
316
317 let cond_value = self.evaluate_node(cond.syntax())?;
318 let cond_bool = self.to_boolean(&cond_value)?;
319
320 let result = if cond_bool {
321 self.evaluate_node(true_expr.syntax())?
322 } else {
323 self.evaluate_node(false_expr.syntax())?
324 };
325
326 #[cfg(feature = "logging")]
327 self.logger
328 .trace(&format!("Conditional Result: {:?}", result));
329
330 Ok(result)
331 }
332
333 fn evaluate_dot_expr(&self, dot_expr: &DotExpr) -> Result<Value, NodeError> {
334 #[cfg(feature = "logging")]
335 self.logger
336 .trace(&format!("Evaluating Dot Expression: {:#?}", dot_expr));
337
338 let mut current_expr = dot_expr.clone();
340 let mut property_chain = Vec::new();
341
342 loop {
344 let prop = current_expr.prop();
345 let obj = current_expr.object();
346
347 if let Some(prop) = prop {
348 let prop_name = prop.syntax().text().to_string();
349 property_chain.push(prop_name);
350 } else {
351 return Err(NodeError {
352 message: "Missing property in dot expression".to_string(),
353 node: Some(current_expr.syntax().clone()),
354 });
355 }
356
357 if let Some(obj_expr) = obj {
358 let obj_syntax = obj_expr.syntax().clone();
359 if let Some(prev_dot_expr) = DotExpr::cast(obj_syntax.clone()) {
360 current_expr = prev_dot_expr;
361 } else if let Some(name_ref) = NameRef::cast(obj_syntax.clone()) {
362 let obj_name = name_ref.syntax().text().to_string();
363 property_chain.push(obj_name);
364 break;
365 } else if let Some(name) = Name::cast(obj_syntax.clone()) {
366 let obj_name = name.syntax().text().to_string();
367 property_chain.push(obj_name);
368 break;
369 } else {
370 return Err(NodeError {
371 message: "Unsupported object type in dot expression".to_string(),
372 node: Some(obj_expr.syntax().clone()),
373 });
374 }
375 } else {
376 break;
377 }
378 }
379
380 property_chain.reverse();
382
383 #[cfg(feature = "logging")]
384 self.logger
385 .trace(&format!("Property Chain: {:?}", property_chain));
386
387 let mut value = self
389 .context
390 .get(&property_chain[0])
391 .cloned()
392 .unwrap_or(Value::Null);
393
394 for prop in &property_chain[1..] {
396 match &value {
397 Value::Object(map) => {
398 value = map.get(prop).cloned().unwrap_or(Value::Null);
399 }
400 _ => {
401 value = Value::Null;
403 break;
404 }
405 }
406 }
407
408 Ok(value)
409 }
410
411 fn abstract_equality(&self, left: &Value, right: &Value) -> bool {
413 match (left, right) {
414 (Value::Null, Value::Null) => true,
415 (Value::Number(l), Value::Number(r)) => l.as_f64() == r.as_f64(),
416 (Value::String(l), Value::String(r)) => l == r,
417 (Value::Bool(l), Value::Bool(r)) => l == r,
418 _ => false,
419 }
420 }
421
422 fn evaluate_by_name(&self, identifier_name: String) -> Result<Value, NodeError> {
423 let identifier_value = self.context.get(&identifier_name);
424
425 #[cfg(feature = "logging")]
426 self.logger
427 .trace(&format!("Identifier Value: {:#?}", identifier_value));
428
429 match identifier_value {
430 Some(value) => Ok(value.clone()),
431 None => Err(NodeError {
432 message: format!("Identifier '{}' not found in context.", identifier_name),
433 node: None,
434 }),
435 }
436 }
437
438 fn evaluate_name(&self, name: &Name) -> Result<Value, NodeError> {
439 #[cfg(feature = "logging")]
440 self.logger
441 .trace(&format!("Evaluating Name: {:#?}", name.to_string()));
442 let identifier_name = name
443 .ident_token()
444 .ok_or_else(|| NodeError {
445 message: "[Empty Name]".to_string(),
446 node: Some(name.syntax().clone()),
447 })?
448 .to_string();
449
450 self.evaluate_by_name(identifier_name)
451 }
452
453 fn evaluate_name_ref(&self, name_ref: &NameRef) -> Result<Value, NodeError> {
454 #[cfg(feature = "logging")]
455 self.logger.trace(&format!(
456 "Evaluating Name Reference: {:#?}",
457 name_ref.to_string()
458 ));
459 let identifier_name = name_ref
460 .ident_token()
461 .ok_or_else(|| NodeError {
462 message: "[Empty NameRef]".to_string(),
463 node: Some(name_ref.syntax().clone()),
464 })?
465 .to_string();
466
467 self.evaluate_by_name(identifier_name)
468 }
469
470 fn evaluate_identifier(&self, identifier: &Expr) -> Result<Value, NodeError> {
471 #[cfg(feature = "logging")]
472 self.logger.trace(&format!(
473 "Evaluating Identifier: {:#?}",
474 identifier.to_string()
475 ));
476 let identifier_name = identifier.to_string();
477
478 self.evaluate_by_name(identifier_name)
479 }
480
481 fn evaluate_literal(&self, literal: &Expr) -> Result<Value, NodeError> {
482 #[cfg(feature = "logging")]
483 self.logger
484 .trace(&format!("Evaluating Literal: {:#?}", literal.to_string()));
485
486 let literal_str = literal.to_string();
487
488 if let Ok(number) = literal_str.parse::<f64>() {
490 return Ok(Value::Number(serde_json::Number::from_f64(number).unwrap()));
491 }
492
493 if literal_str.starts_with('"') || literal_str.starts_with('\'') {
495 let unquoted = literal_str
496 .trim_matches(|c| c == '"' || c == '\'')
497 .to_string();
498 return Ok(Value::String(unquoted));
499 }
500
501 match literal_str.as_str() {
503 "true" => return Ok(Value::Bool(true)),
504 "false" => return Ok(Value::Bool(false)),
505 "null" => return Ok(Value::Null),
506 _ => {}
507 }
508
509 Err(NodeError {
510 message: format!("Unknown literal type: {}", literal_str),
511 node: Some(literal.syntax().clone()),
512 })
513 }
514
515 fn to_number(&self, value: &Value) -> Result<f64, NodeError> {
516 match value {
517 Value::Number(n) => Ok(n.as_f64().unwrap()),
518 Value::String(s) => s.parse::<f64>().map_err(|_| NodeError {
519 message: format!("Cannot convert string '{}' to number", s),
520 node: None,
521 }),
522 Value::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }),
523 Value::Null => Ok(0.0),
524 _ => Err(NodeError {
525 message: "Cannot convert value to number".to_string(),
526 node: None,
527 }),
528 }
529 }
530
531 fn to_boolean(&self, value: &Value) -> Result<bool, NodeError> {
532 let result = match value {
533 Value::Bool(b) => *b,
534 Value::Null => false,
535 Value::Number(n) => {
536 let num = n.as_f64().unwrap();
537 num != 0.0 && !num.is_nan()
538 }
539 Value::String(s) => !s.is_empty(),
540 Value::Array(a) => !a.is_empty(),
541 Value::Object(o) => !o.is_empty(),
542 };
543 Ok(result)
544 }
545
546 fn value_to_string(&self, value: &Value) -> String {
547 match value {
548 Value::String(s) => s.clone(),
549 Value::Number(n) => n.to_string(),
550 Value::Bool(b) => b.to_string(),
551 Value::Null => "null".to_string(),
552 Value::Array(_) => "[Array]".to_string(),
553 Value::Object(_) => "[Object]".to_string(),
554 }
555 }
556}