1use crate::env::Environment;
4use crate::error::{EvalError, EvalResult};
5use pepl_stdlib::modules::{convert, core, json, list, math, record, string, time, timer};
6use pepl_stdlib::{StdlibModule, Value, ResultValue};
7use pepl_types::ast::*;
8use std::collections::BTreeMap;
9
10pub struct Evaluator {
12 pub env: Environment,
14 pub gas: u64,
16 pub gas_limit: u64,
18 pub log_output: Vec<String>,
20 pub action_names: Vec<String>,
22 pub mock_responses: Vec<(String, String, Value)>,
25}
26
27impl Evaluator {
28 pub fn new(gas_limit: u64) -> Self {
30 Self {
31 env: Environment::new(),
32 gas: 0,
33 gas_limit,
34 log_output: Vec::new(),
35 action_names: Vec::new(),
36 mock_responses: Vec::new(),
37 }
38 }
39
40 fn tick(&mut self) -> EvalResult<()> {
42 self.gas += 1;
43 if self.gas > self.gas_limit {
44 Err(EvalError::GasExhausted)
45 } else {
46 Ok(())
47 }
48 }
49
50 pub fn eval_expr(&mut self, expr: &Expr) -> EvalResult<Value> {
56 self.tick()?;
57 match &expr.kind {
58 ExprKind::NumberLit(n) => Ok(Value::Number(*n)),
59 ExprKind::StringLit(s) => Ok(Value::String(s.clone())),
60 ExprKind::BoolLit(b) => Ok(Value::Bool(*b)),
61 ExprKind::NilLit => Ok(Value::Nil),
62
63 ExprKind::StringInterpolation(parts) => self.eval_string_interpolation(parts),
64 ExprKind::ListLit(elems) => self.eval_list_literal(elems),
65 ExprKind::RecordLit(entries) => self.eval_record_literal(entries),
66
67 ExprKind::Identifier(name) => self.eval_identifier(name),
68
69 ExprKind::Call { name, args } => self.eval_call(&name.name, args),
70 ExprKind::QualifiedCall {
71 module,
72 function,
73 args,
74 } => self.eval_qualified_call(&module.name, &function.name, args),
75 ExprKind::FieldAccess { object, field } => self.eval_field_access(object, &field.name),
76 ExprKind::MethodCall {
77 object,
78 method,
79 args,
80 } => self.eval_method_call(object, &method.name, args),
81
82 ExprKind::Binary { left, op, right } => self.eval_binary(left, *op, right),
83 ExprKind::Unary { op, operand } => self.eval_unary(*op, operand),
84 ExprKind::ResultUnwrap(inner) => self.eval_result_unwrap(inner),
85 ExprKind::NilCoalesce { left, right } => self.eval_nil_coalesce(left, right),
86
87 ExprKind::If(if_expr) => self.eval_if_expr(if_expr),
88 ExprKind::For(for_expr) => self.eval_for_expr(for_expr),
89 ExprKind::Match(match_expr) => self.eval_match_expr(match_expr),
90 ExprKind::Lambda(lambda) => self.eval_lambda(lambda),
91 ExprKind::Paren(inner) => self.eval_expr(inner),
92 }
93 }
94
95 fn eval_string_interpolation(&mut self, parts: &[StringPart]) -> EvalResult<Value> {
98 let mut result = String::new();
99 for part in parts {
100 match part {
101 StringPart::Literal(s) => result.push_str(s),
102 StringPart::Expr(expr) => {
103 let val = self.eval_expr(expr)?;
104 result.push_str(&self.value_to_display_string(&val));
105 }
106 }
107 }
108 Ok(Value::String(result))
109 }
110
111 fn eval_list_literal(&mut self, elems: &[Expr]) -> EvalResult<Value> {
112 let mut values = Vec::with_capacity(elems.len());
113 for elem in elems {
114 values.push(self.eval_expr(elem)?);
115 }
116 Ok(Value::List(values))
117 }
118
119 fn eval_record_literal(&mut self, entries: &[RecordEntry]) -> EvalResult<Value> {
120 let mut fields = BTreeMap::new();
121 for entry in entries {
122 match entry {
123 RecordEntry::Field { name, value } => {
124 let val = self.eval_expr(value)?;
125 fields.insert(name.name.clone(), val);
126 }
127 RecordEntry::Spread(expr) => {
128 let val = self.eval_expr(expr)?;
129 if let Value::Record { fields: rf, .. } = val {
130 for (k, v) in rf {
131 fields.insert(k, v);
132 }
133 } else {
134 return Err(EvalError::TypeMismatch(
135 format!("spread requires record, got {}", val.type_name()),
136 ));
137 }
138 }
139 }
140 }
141 Ok(Value::Record {
142 type_name: None,
143 fields,
144 })
145 }
146
147 fn eval_identifier(&self, name: &str) -> EvalResult<Value> {
150 self.env
151 .get(name)
152 .cloned()
153 .ok_or_else(|| EvalError::UndefinedVariable(name.to_string()))
154 }
155
156 fn eval_call(&mut self, name: &str, args: &[Expr]) -> EvalResult<Value> {
160 if let Some(Value::Function(f)) = self.env.get(name).cloned() {
162 let mut arg_vals = Vec::with_capacity(args.len());
163 for arg in args {
164 arg_vals.push(self.eval_expr(arg)?);
165 }
166 return f.0(arg_vals).map_err(|e| EvalError::StdlibError(e.to_string()));
167 }
168 Err(EvalError::UnknownFunction(format!(
170 "unknown function '{name}'"
171 )))
172 }
173
174 pub fn eval_qualified_call(
176 &mut self,
177 module: &str,
178 function: &str,
179 args: &[Expr],
180 ) -> EvalResult<Value> {
181 let mut arg_vals = Vec::with_capacity(args.len());
182 for arg in args {
183 arg_vals.push(self.eval_expr(arg)?);
184 }
185 self.call_stdlib(module, function, arg_vals)
186 }
187
188 fn eval_field_access(&mut self, object: &Expr, field: &str) -> EvalResult<Value> {
189 let obj = self.eval_expr(object)?;
190 match &obj {
191 Value::Record { fields, .. } => fields
192 .get(field)
193 .cloned()
194 .ok_or_else(|| {
195 EvalError::Runtime(format!("record has no field '{field}'"))
196 }),
197 Value::Nil => Err(EvalError::NilAccess(format!(
198 "cannot access field '{field}' on nil"
199 ))),
200 _ => Err(EvalError::TypeMismatch(format!(
201 "cannot access field '{field}' on {}",
202 obj.type_name()
203 ))),
204 }
205 }
206
207 fn eval_method_call(
208 &mut self,
209 object: &Expr,
210 method: &str,
211 args: &[Expr],
212 ) -> EvalResult<Value> {
213 let obj = self.eval_expr(object)?;
214 let mut all_args = vec![obj];
215 for arg in args {
216 all_args.push(self.eval_expr(arg)?);
217 }
218 let module = match &all_args[0] {
220 Value::List(_) => "list",
221 Value::String(_) => "string",
222 _ => {
223 return Err(EvalError::TypeMismatch(format!(
224 "cannot call method '{method}' on {}",
225 all_args[0].type_name()
226 )));
227 }
228 };
229 self.call_stdlib(module, method, all_args)
230 }
231
232 fn eval_binary(&mut self, left: &Expr, op: BinOp, right: &Expr) -> EvalResult<Value> {
235 if op == BinOp::And {
237 let lv = self.eval_expr(left)?;
238 return if !lv.is_truthy() {
239 Ok(Value::Bool(false))
240 } else {
241 let rv = self.eval_expr(right)?;
242 Ok(Value::Bool(rv.is_truthy()))
243 };
244 }
245 if op == BinOp::Or {
246 let lv = self.eval_expr(left)?;
247 return if lv.is_truthy() {
248 Ok(Value::Bool(true))
249 } else {
250 let rv = self.eval_expr(right)?;
251 Ok(Value::Bool(rv.is_truthy()))
252 };
253 }
254
255 let lv = self.eval_expr(left)?;
256 let rv = self.eval_expr(right)?;
257
258 match op {
259 BinOp::Add => self.eval_add(&lv, &rv),
260 BinOp::Sub => self.eval_arith(&lv, &rv, |a, b| a - b, "-"),
261 BinOp::Mul => self.eval_arith(&lv, &rv, |a, b| a * b, "*"),
262 BinOp::Div => {
263 if let (Value::Number(a), Value::Number(b)) = (&lv, &rv) {
264 if *b == 0.0 {
265 return Err(EvalError::ArithmeticTrap("division by zero".into()));
266 }
267 let result = a / b;
268 if result.is_nan() || result.is_infinite() {
269 return Err(EvalError::ArithmeticTrap("division produced NaN/Infinity".into()));
270 }
271 Ok(Value::Number(result))
272 } else {
273 Err(EvalError::TypeMismatch(format!(
274 "cannot divide {} by {}",
275 lv.type_name(),
276 rv.type_name()
277 )))
278 }
279 }
280 BinOp::Mod => {
281 if let (Value::Number(a), Value::Number(b)) = (&lv, &rv) {
282 if *b == 0.0 {
283 return Err(EvalError::ArithmeticTrap("modulo by zero".into()));
284 }
285 Ok(Value::Number(a % b))
286 } else {
287 Err(EvalError::TypeMismatch(format!(
288 "cannot modulo {} by {}",
289 lv.type_name(),
290 rv.type_name()
291 )))
292 }
293 }
294 BinOp::Eq => Ok(Value::Bool(self.structural_eq(&lv, &rv))),
295 BinOp::NotEq => Ok(Value::Bool(!self.structural_eq(&lv, &rv))),
296 BinOp::Less => self.eval_comparison(&lv, &rv, |a, b| a < b),
297 BinOp::Greater => self.eval_comparison(&lv, &rv, |a, b| a > b),
298 BinOp::LessEq => self.eval_comparison(&lv, &rv, |a, b| a <= b),
299 BinOp::GreaterEq => self.eval_comparison(&lv, &rv, |a, b| a >= b),
300 BinOp::And | BinOp::Or => unreachable!("handled above"),
301 }
302 }
303
304 fn eval_add(&self, lv: &Value, rv: &Value) -> EvalResult<Value> {
305 match (lv, rv) {
306 (Value::Number(a), Value::Number(b)) => {
307 let result = a + b;
308 if result.is_nan() || result.is_infinite() {
309 Err(EvalError::ArithmeticTrap("addition produced NaN/Infinity".into()))
310 } else {
311 Ok(Value::Number(result))
312 }
313 }
314 (Value::String(a), Value::String(b)) => {
315 Ok(Value::String(format!("{a}{b}")))
316 }
317 _ => Err(EvalError::TypeMismatch(format!(
318 "cannot add {} and {}",
319 lv.type_name(),
320 rv.type_name()
321 ))),
322 }
323 }
324
325 fn eval_arith(
326 &self,
327 lv: &Value,
328 rv: &Value,
329 op: fn(f64, f64) -> f64,
330 symbol: &str,
331 ) -> EvalResult<Value> {
332 if let (Value::Number(a), Value::Number(b)) = (lv, rv) {
333 let result = op(*a, *b);
334 if result.is_nan() || result.is_infinite() {
335 Err(EvalError::ArithmeticTrap(format!("{symbol} produced NaN/Infinity")))
336 } else {
337 Ok(Value::Number(result))
338 }
339 } else {
340 Err(EvalError::TypeMismatch(format!(
341 "cannot apply '{symbol}' to {} and {}",
342 lv.type_name(),
343 rv.type_name()
344 )))
345 }
346 }
347
348 fn eval_comparison(
349 &self,
350 lv: &Value,
351 rv: &Value,
352 op: fn(f64, f64) -> bool,
353 ) -> EvalResult<Value> {
354 match (lv, rv) {
355 (Value::Number(a), Value::Number(b)) => Ok(Value::Bool(op(*a, *b))),
356 (Value::String(a), Value::String(b)) => Ok(Value::Bool(op(
357 a.len() as f64,
358 b.len() as f64,
359 ))),
360 _ => Err(EvalError::TypeMismatch(format!(
361 "cannot compare {} and {}",
362 lv.type_name(),
363 rv.type_name()
364 ))),
365 }
366 }
367
368 fn eval_unary(&mut self, op: UnaryOp, operand: &Expr) -> EvalResult<Value> {
369 let val = self.eval_expr(operand)?;
370 match op {
371 UnaryOp::Neg => {
372 if let Value::Number(n) = val {
373 Ok(Value::Number(-n))
374 } else {
375 Err(EvalError::TypeMismatch(format!(
376 "cannot negate {}",
377 val.type_name()
378 )))
379 }
380 }
381 UnaryOp::Not => Ok(Value::Bool(!val.is_truthy())),
382 }
383 }
384
385 fn eval_result_unwrap(&mut self, inner: &Expr) -> EvalResult<Value> {
386 let val = self.eval_expr(inner)?;
387 match val {
388 Value::Result(r) => match *r {
389 ResultValue::Ok(v) => Ok(v),
390 ResultValue::Err(e) => Err(EvalError::UnwrapError(format!(
391 "unwrap on Err: {}",
392 self.value_to_display_string(&e)
393 ))),
394 },
395 _ => Err(EvalError::TypeMismatch(format!(
396 "'?' requires Result, got {}",
397 val.type_name()
398 ))),
399 }
400 }
401
402 fn eval_nil_coalesce(&mut self, left: &Expr, right: &Expr) -> EvalResult<Value> {
403 let lv = self.eval_expr(left)?;
404 if lv == Value::Nil {
405 self.eval_expr(right)
406 } else {
407 Ok(lv)
408 }
409 }
410
411 pub fn eval_if_expr(&mut self, if_expr: &IfExpr) -> EvalResult<Value> {
414 let cond = self.eval_expr(&if_expr.condition)?;
415 if cond.is_truthy() {
416 self.eval_block(&if_expr.then_block)
417 } else if let Some(else_branch) = &if_expr.else_branch {
418 match else_branch {
419 ElseBranch::ElseIf(elif) => self.eval_if_expr(elif),
420 ElseBranch::Block(block) => self.eval_block(block),
421 }
422 } else {
423 Ok(Value::Nil)
424 }
425 }
426
427 fn eval_for_expr(&mut self, for_expr: &ForExpr) -> EvalResult<Value> {
428 let iterable = self.eval_expr(&for_expr.iterable)?;
429 let items = match iterable {
430 Value::List(items) => items,
431 _ => {
432 return Err(EvalError::TypeMismatch(format!(
433 "for loop requires list, got {}",
434 iterable.type_name()
435 )));
436 }
437 };
438
439 self.env.push_scope();
440 let mut last = Value::Nil;
441 for (i, item) in items.iter().enumerate() {
442 self.env.define(&for_expr.item.name, item.clone());
443 if let Some(idx) = &for_expr.index {
444 self.env.define(&idx.name, Value::Number(i as f64));
445 }
446 last = self.eval_block(&for_expr.body)?;
447 }
448 self.env.pop_scope();
449 Ok(last)
450 }
451
452 fn eval_match_expr(&mut self, match_expr: &MatchExpr) -> EvalResult<Value> {
453 let subject = self.eval_expr(&match_expr.subject)?;
454
455 for arm in &match_expr.arms {
456 if let Some(bindings) = self.match_pattern(&arm.pattern, &subject) {
457 self.env.push_scope();
458 for (name, val) in bindings {
459 self.env.define(&name, val);
460 }
461 let result = self.eval_match_arm_body(&arm.body);
462 self.env.pop_scope();
463 return result;
464 }
465 }
466 Ok(Value::Nil)
468 }
469
470 fn match_pattern(&self, pattern: &Pattern, value: &Value) -> Option<Vec<(String, Value)>> {
473 match pattern {
474 Pattern::Wildcard(_) => Some(vec![]),
475 Pattern::Variant { name, bindings } => {
476 if let Value::Result(r) = value {
478 match (name.name.as_str(), r.as_ref()) {
479 ("Ok", ResultValue::Ok(v)) => {
480 let mut b = Vec::new();
481 if let Some(binding) = bindings.first() {
482 b.push((binding.name.clone(), v.clone()));
483 }
484 Some(b)
485 }
486 ("Err", ResultValue::Err(v)) => {
487 let mut b = Vec::new();
488 if let Some(binding) = bindings.first() {
489 b.push((binding.name.clone(), v.clone()));
490 }
491 Some(b)
492 }
493 _ => None,
494 }
495 }
496 else if let Value::SumVariant {
498 variant, fields, ..
499 } = value
500 {
501 if variant == &name.name {
502 let mut b = Vec::new();
503 for (binding, field) in bindings.iter().zip(fields.iter()) {
504 b.push((binding.name.clone(), field.clone()));
505 }
506 Some(b)
507 } else {
508 None
509 }
510 }
511 else if bindings.is_empty() {
513 if let Value::String(s) = value {
515 if s == &name.name {
516 return Some(vec![]);
517 }
518 }
519 None
520 } else {
521 None
522 }
523 }
524 }
525 }
526
527 fn eval_match_arm_body(&mut self, body: &MatchArmBody) -> EvalResult<Value> {
528 match body {
529 MatchArmBody::Expr(expr) => self.eval_expr(expr),
530 MatchArmBody::Block(block) => self.eval_block(block),
531 }
532 }
533
534 pub fn eval_lambda(&mut self, lambda: &LambdaExpr) -> EvalResult<Value> {
535 let captured_env = self.env.clone();
537 let params: Vec<String> = lambda.params.iter().map(|p| p.name.name.clone()).collect();
538 let body = lambda.body.clone();
539
540 let closure = pepl_stdlib::StdlibFn(std::sync::Arc::new(move |args: Vec<Value>| {
541 let mut eval = Evaluator::new(100_000);
543 eval.env = captured_env.clone();
544 eval.env.push_scope();
545 for (param, arg) in params.iter().zip(args.into_iter()) {
546 eval.env.define(param, arg);
547 }
548 let result = eval
549 .eval_block(&body)
550 .map_err(|e| pepl_stdlib::StdlibError::RuntimeError(e.to_string()))?;
551 eval.env.pop_scope();
552 Ok(result)
553 }));
554
555 Ok(Value::Function(closure))
556 }
557
558 pub fn eval_block(&mut self, block: &Block) -> EvalResult<Value> {
564 let mut last = Value::Nil;
565 for stmt in &block.stmts {
566 last = self.eval_stmt(stmt)?;
567 }
568 Ok(last)
569 }
570
571 pub fn eval_stmt(&mut self, stmt: &Stmt) -> EvalResult<Value> {
573 self.tick()?;
574 match stmt {
575 Stmt::Set(set) => self.eval_set(set),
576 Stmt::Let(binding) => self.eval_let(binding),
577 Stmt::If(if_expr) => self.eval_if_expr(if_expr),
578 Stmt::For(for_expr) => self.eval_for_expr(for_expr),
579 Stmt::Match(match_expr) => self.eval_match_expr(match_expr),
580 Stmt::Return(ret) => {
581 let _ = ret;
582 Err(EvalError::Return(Value::Nil))
584 }
585 Stmt::Assert(assert) => self.eval_assert(assert),
586 Stmt::Expr(expr_stmt) => self.eval_expr(&expr_stmt.expr),
587 }
588 }
589
590 fn eval_set(&mut self, set: &SetStmt) -> EvalResult<Value> {
591 let value = self.eval_expr(&set.value)?;
592
593 if set.target.len() == 1 {
594 let name = &set.target[0].name;
596 if !self.env.set(name, value) {
597 return Err(EvalError::UndefinedVariable(name.clone()));
598 }
599 } else {
600 self.eval_nested_set(&set.target, value)?;
602 }
603 Ok(Value::Nil)
604 }
605
606 fn eval_nested_set(&mut self, target: &[Ident], value: Value) -> EvalResult<()> {
608 let root_name = &target[0].name;
609 let root = self
610 .env
611 .get(root_name)
612 .cloned()
613 .ok_or_else(|| EvalError::UndefinedVariable(root_name.clone()))?;
614
615 let new_root = self.set_nested_field(&root, &target[1..], value)?;
616 self.env.set(root_name, new_root);
617 Ok(())
618 }
619
620 fn set_nested_field(
621 &self,
622 current: &Value,
623 path: &[Ident],
624 value: Value,
625 ) -> EvalResult<Value> {
626 if path.is_empty() {
627 return Ok(value);
628 }
629
630 let field_name = &path[0].name;
631 match current {
632 Value::Record {
633 type_name, fields, ..
634 } => {
635 let mut new_fields = fields.clone();
636 if path.len() == 1 {
637 new_fields.insert(field_name.clone(), value);
638 } else {
639 let inner = fields
640 .get(field_name)
641 .ok_or_else(|| {
642 EvalError::Runtime(format!("record has no field '{field_name}'"))
643 })?;
644 let new_inner = self.set_nested_field(inner, &path[1..], value)?;
645 new_fields.insert(field_name.clone(), new_inner);
646 }
647 Ok(Value::Record {
648 type_name: type_name.clone(),
649 fields: new_fields,
650 })
651 }
652 _ => Err(EvalError::TypeMismatch(format!(
653 "cannot set field '{}' on {}",
654 field_name,
655 current.type_name()
656 ))),
657 }
658 }
659
660 fn eval_let(&mut self, binding: &LetBinding) -> EvalResult<Value> {
661 let value = self.eval_expr(&binding.value)?;
662 if let Some(name) = &binding.name {
663 self.env.define(&name.name, value);
664 }
665 Ok(Value::Nil)
667 }
668
669 fn eval_assert(&mut self, assert: &AssertStmt) -> EvalResult<Value> {
670 let val = self.eval_expr(&assert.condition)?;
671 if !val.is_truthy() {
672 let msg = assert
673 .message
674 .clone()
675 .unwrap_or_else(|| "assertion failed".into());
676 return Err(EvalError::AssertionFailed(msg));
677 }
678 Ok(Value::Nil)
679 }
680
681 pub fn call_stdlib(
687 &mut self,
688 module: &str,
689 function: &str,
690 args: Vec<Value>,
691 ) -> EvalResult<Value> {
692 if module == "core" && function == "log" {
694 if let Some(val) = args.first() {
695 self.log_output
696 .push(self.value_to_display_string(val));
697 }
698 return Ok(Value::Nil);
699 }
700
701 let result = match module {
703 "core" => core::CoreModule.call(function, args),
704 "math" => math::MathModule.call(function, args),
705 "string" => string::StringModule.call(function, args),
706 "list" => list::ListModule.call(function, args),
707 "record" => record::RecordModule.call(function, args),
708 "time" => time::TimeModule.call(function, args),
709 "convert" => convert::ConvertModule.call(function, args),
710 "json" => json::JsonModule.call(function, args),
711 "timer" => timer::TimerModule.call(function, args),
712 "http" | "storage" | "location" | "notifications" | "clipboard" | "share" => {
714 if let Some(response) = self.find_mock_response(module, function) {
716 Ok(response)
717 } else {
718 Ok(Value::Result(Box::new(ResultValue::Err(Value::String(
719 format!("unmocked capability call: {module}.{function}"),
720 )))))
721 }
722 }
723 _ => {
724 return Err(EvalError::UnknownFunction(format!(
725 "unknown module '{module}'"
726 )));
727 }
728 };
729
730 result.map_err(|e| EvalError::StdlibError(e.to_string()))
731 }
732
733 fn find_mock_response(&self, module: &str, function: &str) -> Option<Value> {
739 self.mock_responses
740 .iter()
741 .find(|(m, f, _)| m == module && f == function)
742 .map(|(_, _, v)| v.clone())
743 }
744
745 pub fn structural_eq(&self, a: &Value, b: &Value) -> bool {
751 match (a, b) {
752 (Value::Number(x), Value::Number(y)) => {
753 if x.is_nan() || y.is_nan() {
755 false
756 } else {
757 x == y
758 }
759 }
760 (Value::String(x), Value::String(y)) => x == y,
761 (Value::Bool(x), Value::Bool(y)) => x == y,
762 (Value::Nil, Value::Nil) => true,
763 (Value::List(x), Value::List(y)) => {
764 x.len() == y.len() && x.iter().zip(y.iter()).all(|(a, b)| self.structural_eq(a, b))
765 }
766 (
767 Value::Record { fields: fa, .. },
768 Value::Record { fields: fb, .. },
769 ) => {
770 fa.len() == fb.len()
771 && fa
772 .iter()
773 .all(|(k, v)| fb.get(k).is_some_and(|v2| self.structural_eq(v, v2)))
774 }
775 (Value::Result(a), Value::Result(b)) => match (a.as_ref(), b.as_ref()) {
776 (ResultValue::Ok(a), ResultValue::Ok(b)) => self.structural_eq(a, b),
777 (ResultValue::Err(a), ResultValue::Err(b)) => self.structural_eq(a, b),
778 _ => false,
779 },
780 (
781 Value::SumVariant {
782 variant: va,
783 fields: fa,
784 ..
785 },
786 Value::SumVariant {
787 variant: vb,
788 fields: fb,
789 ..
790 },
791 ) => {
792 va == vb
793 && fa.len() == fb.len()
794 && fa
795 .iter()
796 .zip(fb.iter())
797 .all(|(a, b)| self.structural_eq(a, b))
798 }
799 (Value::Function(_), _) | (_, Value::Function(_)) => false,
801 _ => false,
802 }
803 }
804
805 pub fn value_to_display_string(&self, val: &Value) -> String {
811 match val {
812 Value::Number(n) => {
813 if n.fract() == 0.0 && n.is_finite() {
814 format!("{}", *n as i64)
815 } else {
816 format!("{n}")
817 }
818 }
819 Value::String(s) => s.clone(),
820 Value::Bool(b) => if *b { "true" } else { "false" }.to_string(),
821 Value::Nil => "nil".to_string(),
822 Value::List(items) => {
823 let parts: Vec<String> = items
824 .iter()
825 .map(|v| self.value_to_display_string(v))
826 .collect();
827 format!("[{}]", parts.join(", "))
828 }
829 Value::Record { fields, .. } => {
830 let parts: Vec<String> = fields
831 .iter()
832 .map(|(k, v)| format!("{k}: {}", self.value_to_display_string(v)))
833 .collect();
834 format!("{{ {} }}", parts.join(", "))
835 }
836 Value::Result(r) => match r.as_ref() {
837 ResultValue::Ok(v) => format!("Ok({})", self.value_to_display_string(v)),
838 ResultValue::Err(v) => format!("Err({})", self.value_to_display_string(v)),
839 },
840 Value::SumVariant {
841 variant, fields, ..
842 } => {
843 if fields.is_empty() {
844 variant.clone()
845 } else {
846 let parts: Vec<String> = fields
847 .iter()
848 .map(|v| self.value_to_display_string(v))
849 .collect();
850 format!("{variant}({})", parts.join(", "))
851 }
852 }
853 Value::Function(_) => "<function>".to_string(),
854 Value::Color { r, g, b, a } => format!("color({r}, {g}, {b}, {a})"),
855 }
856 }
857
858 pub fn string_comparison(&self, _a: &str, _b: &str) -> std::cmp::Ordering {
862 std::cmp::Ordering::Equal
864 }
865}