1use crate::parser::{BinOp, Node};
4use crate::value::Value;
5use crate::{Error, Result};
6use std::collections::HashMap;
7use std::sync::Arc;
8
9#[derive(Debug, Clone, Default)]
18pub struct Context {
19 values: HashMap<Arc<str>, Value>,
20}
21
22impl Context {
23 pub fn new() -> Context {
24 Context::default()
25 }
26
27 pub fn set(&mut self, name: impl Into<Arc<str>>, value: impl Into<Value>) -> &mut Context {
29 self.values.insert(name.into(), value.into());
30 self
31 }
32
33 pub fn get(&self, name: &str) -> Option<&Value> {
34 self.values.get(name)
35 }
36
37 pub fn contains(&self, name: &str) -> bool {
38 self.values.contains_key(name)
39 }
40
41 pub fn remove(&mut self, name: &str) -> Option<Value> {
42 self.values.remove(name)
43 }
44
45 pub fn clear(&mut self) {
46 self.values.clear();
47 }
48
49 pub fn len(&self) -> usize {
50 self.values.len()
51 }
52
53 pub fn is_empty(&self) -> bool {
54 self.values.is_empty()
55 }
56
57 pub fn names(&self) -> impl Iterator<Item = &str> {
59 self.values.keys().map(AsRef::as_ref)
60 }
61}
62
63impl<'a> FromIterator<(&'a str, Value)> for Context {
64 fn from_iter<T: IntoIterator<Item = (&'a str, Value)>>(iter: T) -> Self {
65 Context {
66 values: iter
67 .into_iter()
68 .map(|(name, value)| (Arc::from(name), value))
69 .collect(),
70 }
71 }
72}
73
74pub(crate) fn eval(nodes: &[Node], root: u32, context: &Context) -> Result<Value> {
75 eval_node(nodes, root, context)
76}
77
78fn eval_node(nodes: &[Node], index: u32, context: &Context) -> Result<Value> {
79 match &nodes[index as usize] {
80 Node::Null => Ok(Value::Null),
81 Node::Bool(b) => Ok(Value::Bool(*b)),
82 Node::Int(i) => Ok(Value::Int(*i)),
83 Node::Float(f) => Ok(Value::Float(*f)),
84 Node::Str(s) => Ok(Value::Str(s.clone())),
85 Node::Ident(name) => context.get(name).cloned().ok_or_else(|| Error::Unknown {
86 name: name.to_string(),
87 }),
88 Node::Member(target, name) => {
89 let value = eval_node(nodes, *target, context)?;
90 let map = value.as_map().ok_or_else(|| type_error(".", &value))?;
91
92 map.get(name.as_ref())
93 .cloned()
94 .ok_or_else(|| Error::Missing {
95 name: name.to_string(),
96 })
97 }
98 Node::Index(target, key) => {
99 let container = eval_node(nodes, *target, context)?;
100 let key = eval_node(nodes, *key, context)?;
101
102 match (&container, &key) {
103 (Value::List(items), Value::Int(i)) => {
104 let len = items.len();
105 let position = usize::try_from(*i)
106 .ok()
107 .filter(|position| *position < len)
108 .ok_or(Error::OutOfRange { index: *i, len })?;
109
110 Ok(items[position].clone())
111 }
112 (Value::Map(map), Value::Str(name)) => {
113 map.get(name.as_ref())
114 .cloned()
115 .ok_or_else(|| Error::Missing {
116 name: name.to_string(),
117 })
118 }
119 _ => Err(type_error("[]", &container)),
120 }
121 }
122 Node::Call(name, args) => {
123 let function = context.get(name).cloned().ok_or_else(|| Error::Unknown {
124 name: name.to_string(),
125 })?;
126 let Value::Function(function) = function else {
127 return Err(type_error("call", &function));
128 };
129
130 function(&arguments(nodes, args, context)?)
131 }
132 Node::Method(receiver, name, args) => {
133 let receiver = eval_node(nodes, *receiver, context)?;
134
135 let own = receiver
141 .as_map()
142 .and_then(|map| map.get(name.as_ref()))
143 .filter(|value| value.is_function())
144 .cloned();
145
146 let function = match own {
147 Some(function) => function,
148 None => match context.get(name).cloned() {
149 Some(function) => function,
150 None => {
151 let args = arguments(nodes, args, context)?;
152
153 return crate::methods::call(name, &receiver, &args)?.ok_or_else(|| {
154 Error::UnknownMethod {
155 name: name.to_string(),
156 }
157 });
158 }
159 },
160 };
161 let Value::Function(function) = function else {
162 return Err(Error::UnknownMethod {
163 name: name.to_string(),
164 });
165 };
166
167 let mut call_args = Vec::with_capacity(args.len() + 1);
168 call_args.push(receiver);
169 call_args.extend(arguments(nodes, args, context)?);
170
171 function(&call_args)
172 }
173 Node::Not(operand) => {
174 let value = eval_node(nodes, *operand, context)?;
175 let boolean = boolean_of(&value, "!")?;
176
177 Ok(Value::Bool(!boolean))
178 }
179 Node::Neg(operand) => {
180 let value = eval_node(nodes, *operand, context)?;
181
182 match value {
183 Value::Int(i) => i
184 .checked_neg()
185 .map(Value::Int)
186 .ok_or(Error::Overflow { operation: "-" }),
187 Value::Float(f) => Ok(Value::Float(-f)),
188 other => Err(type_error("-", &other)),
189 }
190 }
191 Node::Binary(op, left, right) => binary(nodes, *op, *left, *right, context),
192 }
193}
194
195fn binary(nodes: &[Node], op: BinOp, left: u32, right: u32, context: &Context) -> Result<Value> {
198 match op {
199 BinOp::And => {
200 let left = eval_node(nodes, left, context)?;
201 if !boolean_of(&left, op.symbol())? {
202 return Ok(Value::Bool(false));
203 }
204 let right = eval_node(nodes, right, context)?;
205
206 return Ok(Value::Bool(boolean_of(&right, op.symbol())?));
207 }
208 BinOp::Or => {
209 let left = eval_node(nodes, left, context)?;
210 if boolean_of(&left, op.symbol())? {
211 return Ok(Value::Bool(true));
212 }
213 let right = eval_node(nodes, right, context)?;
214
215 return Ok(Value::Bool(boolean_of(&right, op.symbol())?));
216 }
217 _ => {}
218 }
219
220 let left = eval_node(nodes, left, context)?;
221 let right = eval_node(nodes, right, context)?;
222
223 match op {
224 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div | BinOp::Rem => {
225 arithmetic(op, &left, &right)
226 }
227 BinOp::Eq => Ok(Value::Bool(left.equals(&right))),
228 BinOp::Ne => Ok(Value::Bool(!left.equals(&right))),
229 BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
230 let ordering = left
231 .compare(&right)
232 .ok_or_else(|| type_error(op.symbol(), blame(&left, &right)))?;
233
234 Ok(Value::Bool(match op {
235 BinOp::Lt => ordering.is_lt(),
236 BinOp::Le => ordering.is_le(),
237 BinOp::Gt => ordering.is_gt(),
238 _ => ordering.is_ge(),
239 }))
240 }
241 BinOp::And | BinOp::Or => unreachable!("handled above"),
242 }
243}
244
245fn arithmetic(op: BinOp, left: &Value, right: &Value) -> Result<Value> {
251 let symbol = op.symbol();
252
253 if let (Value::Int(a), Value::Int(b)) = (left, right) {
256 let (a, b) = (*a, *b);
257
258 let result = match op {
259 BinOp::Add => a.checked_add(b),
260 BinOp::Sub => a.checked_sub(b),
261 BinOp::Mul => a.checked_mul(b),
262 BinOp::Div | BinOp::Rem => {
263 if b == 0 {
264 return Err(Error::DivideByZero { operation: symbol });
265 }
266 if op == BinOp::Div {
267 a.checked_div(b)
268 } else {
269 a.checked_rem(b)
270 }
271 }
272 _ => unreachable!("only arithmetic reaches here"),
273 };
274
275 return result
276 .map(Value::Int)
277 .ok_or(Error::Overflow { operation: symbol });
278 }
279
280 if let (Some(a), Some(b)) = (left.as_i128(), right.as_i128()) {
283 let result = match op {
284 BinOp::Add => a.checked_add(b),
285 BinOp::Sub => a.checked_sub(b),
286 BinOp::Mul => a.checked_mul(b),
287 BinOp::Div | BinOp::Rem => {
288 if b == 0 {
289 return Err(Error::DivideByZero { operation: symbol });
290 }
291 if op == BinOp::Div {
292 a.checked_div(b)
293 } else {
294 a.checked_rem(b)
295 }
296 }
297 _ => unreachable!("only arithmetic reaches here"),
298 };
299
300 let result = result.ok_or(Error::Overflow { operation: symbol })?;
301
302 return match (i64::try_from(result), u64::try_from(result)) {
303 (Ok(value), _) => Ok(Value::Int(value)),
304 (Err(_), Ok(value)) => Ok(Value::UInt(value)),
305 (Err(_), Err(_)) => Err(Error::Overflow { operation: symbol }),
306 };
307 }
308
309 let (Some(a), Some(b)) = (left.as_number(), right.as_number()) else {
310 if op == BinOp::Add
312 && let (Value::Str(a), Value::Str(b)) = (left, right)
313 {
314 let mut text = String::with_capacity(a.len() + b.len());
315 text.push_str(a);
316 text.push_str(b);
317
318 return Ok(Value::Str(Arc::from(text)));
319 }
320
321 return Err(type_error(symbol, blame(left, right)));
322 };
323
324 if matches!(op, BinOp::Div | BinOp::Rem) && b == 0.0 {
325 return Err(Error::DivideByZero { operation: symbol });
326 }
327
328 Ok(Value::Float(match op {
329 BinOp::Add => a + b,
330 BinOp::Sub => a - b,
331 BinOp::Mul => a * b,
332 BinOp::Div => a / b,
333 BinOp::Rem => a % b,
334 _ => unreachable!("only arithmetic reaches here"),
335 }))
336}
337
338fn arguments(nodes: &[Node], args: &[u32], context: &Context) -> Result<Vec<Value>> {
339 args.iter()
340 .map(|arg| eval_node(nodes, *arg, context))
341 .collect()
342}
343
344fn boolean_of(value: &Value, operation: &'static str) -> Result<bool> {
345 value.as_bool().ok_or_else(|| type_error(operation, value))
346}
347
348fn blame<'a>(left: &'a Value, right: &'a Value) -> &'a Value {
351 if left.is_number() || left.as_str().is_some() {
352 right
353 } else {
354 left
355 }
356}
357
358fn type_error(operation: &'static str, value: &Value) -> Error {
359 Error::Type {
360 operation,
361 got: value.type_name(),
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368 use crate::Expr;
369
370 fn eval(source: &str, context: &Context) -> Result<Value> {
371 Expr::compile(source).unwrap().eval(context)
372 }
373
374 fn error(source: &str, context: &Context) -> Error {
375 eval(source, context).expect_err(source)
376 }
377
378 #[test]
379 fn context_get_answers_both_ways() {
380 let mut context = Context::new();
381 assert_eq!(context.get("a"), None);
382 context.set("a", 1);
383 assert_eq!(context.get("a"), Some(&Value::Int(1)));
384 }
385
386 #[test]
387 fn index_errors_name_the_missing_key_and_the_wrong_key_kind() {
388 let mut context = Context::new();
389 context
390 .set("obj", Value::map([("k", 1)]))
391 .set("items", Value::list([1]));
392
393 assert_eq!(
394 error("obj['nope']", &context),
395 Error::Missing {
396 name: "nope".to_string()
397 }
398 );
399 assert_eq!(
400 error("items[true]", &context),
401 Error::Type {
402 operation: "[]",
403 got: "list"
404 }
405 );
406 assert_eq!(
407 error("obj[0]", &context),
408 Error::Type {
409 operation: "[]",
410 got: "map"
411 }
412 );
413 assert_eq!(
414 error("null[0]", &context),
415 Error::Type {
416 operation: "[]",
417 got: "null"
418 }
419 );
420 assert_eq!(
422 error("items[missing]", &context),
423 Error::Unknown {
424 name: "missing".to_string()
425 }
426 );
427 }
428
429 #[test]
430 fn member_access_requires_a_map() {
431 let mut context = Context::new();
432 context
433 .set("obj", Value::map([("k", 1)]))
434 .set("items", Value::list([1]));
435
436 assert_eq!(
437 error("items.k", &context),
438 Error::Type {
439 operation: ".",
440 got: "list"
441 }
442 );
443 assert_eq!(
444 error("obj.missing", &context),
445 Error::Missing {
446 name: "missing".to_string()
447 }
448 );
449 }
450
451 #[test]
452 fn calls_check_the_name_is_a_function() {
453 let mut context = Context::new();
454 context
455 .set("add", Value::function(|args| Ok(Value::from(args.len()))))
456 .set("value", 1);
457
458 assert_eq!(eval("add()", &context).unwrap(), Value::from(0));
459 assert_eq!(eval("add(1, 2)", &context).unwrap(), Value::from(2));
460 assert_eq!(
461 error("value()", &context),
462 Error::Type {
463 operation: "call",
464 got: "int"
465 }
466 );
467 assert_eq!(
468 error("missing()", &context),
469 Error::Unknown {
470 name: "missing".to_string()
471 }
472 );
473 }
474
475 #[test]
476 fn a_method_falls_through_to_unknown() {
477 let mut context = Context::new();
478 context.set("obj", Value::map([("k", 1)])).set("value", 2);
479
480 assert_eq!(
482 error("obj.k()", &context),
483 Error::UnknownMethod {
484 name: "k".to_string()
485 }
486 );
487 assert_eq!(
489 error("obj.value()", &context),
490 Error::UnknownMethod {
491 name: "value".to_string()
492 }
493 );
494 }
495
496 #[test]
497 fn unary_operators_evaluate_both_types() {
498 assert_eq!(eval("-1", &Context::new()).unwrap(), Value::Int(-1));
499 assert_eq!(eval("-1.5", &Context::new()).unwrap(), Value::Float(-1.5));
500 assert_eq!(eval("!false", &Context::new()).unwrap(), Value::Bool(true));
501 assert_eq!(
502 error("-'s'", &Context::new()),
503 Error::Type {
504 operation: "-",
505 got: "string"
506 }
507 );
508 assert_eq!(
509 error("!'s'", &Context::new()),
510 Error::Type {
511 operation: "!",
512 got: "string"
513 }
514 );
515 }
516
517 #[test]
518 fn logic_evaluates_the_right_side_and_checks_both() {
519 let context = Context::new();
520 assert_eq!(eval("true && true", &context).unwrap(), Value::Bool(true));
521 assert_eq!(eval("true && false", &context).unwrap(), Value::Bool(false));
522 assert_eq!(eval("false || true", &context).unwrap(), Value::Bool(true));
523 assert_eq!(
524 eval("false || false", &context).unwrap(),
525 Value::Bool(false)
526 );
527
528 assert_eq!(
529 error("true && 1", &context),
530 Error::Type {
531 operation: "&&",
532 got: "int"
533 }
534 );
535 assert_eq!(
536 error("1 || true", &context),
537 Error::Type {
538 operation: "||",
539 got: "int"
540 }
541 );
542 assert_eq!(
543 error("false || 1", &context),
544 Error::Type {
545 operation: "||",
546 got: "int"
547 }
548 );
549 }
550
551 #[test]
552 fn arithmetic_handles_every_operator_on_uints() {
553 let mut context = Context::new();
554 context.set("big", u64::MAX);
555
556 assert_eq!(
557 eval("big + 1", &context),
558 Err(Error::Overflow { operation: "+" })
559 );
560 assert_eq!(
561 eval("big / 0", &context),
562 Err(Error::DivideByZero { operation: "/" })
563 );
564 assert_eq!(
565 eval("big % 0", &context),
566 Err(Error::DivideByZero { operation: "%" })
567 );
568 assert_eq!(
569 eval("big / 2", &context).unwrap(),
570 Value::UInt(u64::MAX / 2)
571 );
572 assert_eq!(eval("big - big", &context).unwrap(), Value::Int(0));
573 assert_eq!(
574 eval("big - 1.0", &context).unwrap(),
575 Value::Float(u64::MAX as f64 - 1.0)
576 );
577 assert_eq!(
578 error("big + true", &context),
579 Error::Type {
580 operation: "+",
581 got: "bool"
582 }
583 );
584 }
585
586 #[test]
587 fn integer_overflow_and_float_division_are_errors() {
588 let context = Context::new();
589 assert_eq!(
590 eval("9223372036854775807 + 1", &context),
591 Err(Error::Overflow { operation: "+" })
592 );
593 assert_eq!(
594 eval("1.0 / 0.0", &context),
595 Err(Error::DivideByZero { operation: "/" })
596 );
597 assert_eq!(
598 eval("1.0 % 0.0", &context),
599 Err(Error::DivideByZero { operation: "%" })
600 );
601 }
602
603 #[test]
604 fn comparison_blames_the_operand_without_an_overload() {
605 let context = Context::new();
606 assert_eq!(
607 error("true < 1", &context),
608 Error::Type {
609 operation: "<",
610 got: "bool"
611 }
612 );
613 assert_eq!(
614 error("'a' < 1", &context),
615 Error::Type {
616 operation: "<",
617 got: "int"
618 }
619 );
620 assert_eq!(eval("1 < 2.5", &context).unwrap(), Value::Bool(true));
621 }
622}