1pub mod builtins;
19pub mod cast;
20pub mod environment;
21pub mod error;
22pub mod format;
23pub mod methods;
24pub mod pattern;
25pub mod value;
26
27pub use environment::Environment;
28pub use error::RuntimeError;
29pub use value::Value;
30
31use std::collections::HashMap;
32
33use crate::ast::*;
34
35pub struct Interpreter {
40 pub env: Environment,
41 struct_defs: HashMap<String, Vec<(String, Type)>>,
42}
43
44impl Default for Interpreter {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl Interpreter {
51 pub fn new() -> Self {
53 let mut interp = Interpreter {
54 env: Environment::new(),
55 struct_defs: HashMap::new(),
56 };
57 interp
59 .env
60 .define("None".to_string(), Value::Option(None), false);
61 interp
62 }
63
64 pub fn run(&mut self, program: Vec<Expr>) -> Result<Value, RuntimeError> {
66 let mut result = Value::Unit;
67 for expr in program {
68 result = self.eval(&expr)?;
69 if let Value::Return(val) = result {
70 return Ok(val.map(|v| *v).unwrap_or(Value::Unit));
71 }
72 }
73 Ok(result)
74 }
75
76 pub fn eval(&mut self, expr: &Expr) -> Result<Value, RuntimeError> {
80 match expr {
81 Expr::IntLiteral(n) => Ok(Value::Int(*n)),
83 Expr::FloatLiteral(n) => Ok(Value::Float(*n)),
84 Expr::StringLiteral(s) => Ok(Value::String(s.clone())),
85 Expr::CharLiteral(c) => Ok(Value::Char(*c)),
86 Expr::BoolLiteral(b) => Ok(Value::Bool(*b)),
87 Expr::Unit => Ok(Value::Unit),
88
89 Expr::Ident(name) => self.eval_ident(name),
91
92 Expr::BinaryOp { left, op, right } => {
94 let lv = self.eval(left)?;
95 let rv = self.eval(right)?;
96 self.eval_binary_op(&lv, op, &rv)
97 }
98 Expr::UnaryOp { op, expr } => {
99 let val = self.eval(expr)?;
100 self.eval_unary_op(op, &val)
101 }
102
103 Expr::Let {
105 name,
106 mutable,
107 value,
108 ..
109 } => self.eval_let(name, *mutable, value.as_deref()),
110
111 Expr::Assign { target, value } => {
112 let val = self.eval(value)?;
113 self.assign_to(target, val)?;
114 Ok(Value::Unit)
115 }
116 Expr::CompoundAssign { target, op, value } => {
117 let current = self.eval(target)?;
118 let rhs = self.eval(value)?;
119 let result = self.eval_binary_op(¤t, op, &rhs)?;
120 self.assign_to(target, result)?;
121 Ok(Value::Unit)
122 }
123
124 Expr::Block(stmts) => self.eval_block(stmts),
126 Expr::If {
127 condition,
128 then_block,
129 else_block,
130 } => self.eval_if(condition, then_block, else_block.as_deref()),
131 Expr::While { condition, body } => self.eval_while(condition, body),
132 Expr::Loop { body } => self.eval_loop(body),
133 Expr::For {
134 var,
135 iterator,
136 body,
137 } => self.eval_for(var, iterator, body),
138 Expr::Range {
139 start,
140 end,
141 inclusive,
142 } => self.eval_range(start.as_deref(), end.as_deref(), *inclusive),
143 Expr::Break(val) => {
144 let v = val.as_ref().map(|e| self.eval(e)).transpose()?;
145 Ok(Value::Break(v.map(Box::new)))
146 }
147 Expr::Continue => Ok(Value::Continue),
148 Expr::Return(val) => {
149 let v = val.as_ref().map(|e| self.eval(e)).transpose()?;
150 Ok(Value::Return(v.map(Box::new)))
151 }
152
153 Expr::FnDef {
155 name, params, body, ..
156 } => self.eval_fn_def(name, params, body),
157
158 Expr::FnCall { name, args } => self.eval_fn_call(name, args),
159
160 Expr::MethodCall {
161 object,
162 method,
163 args,
164 } => {
165 let obj = self.eval(object)?;
166 let evaluated_args = self.eval_slice(args)?;
167 self.call_method(obj, method, evaluated_args)
168 }
169 Expr::MacroCall { name, args } => self.call_macro(name, args),
170
171 Expr::ArrayLiteral(elements) => {
173 let values = self.eval_slice(elements)?;
174 Ok(Value::Array(values))
175 }
176 Expr::ArrayRepeat { value, count } => self.eval_array_repeat(value, count),
177 Expr::TupleLiteral(elements) => {
178 let values = self.eval_slice(elements)?;
179 Ok(Value::Tuple(values))
180 }
181 Expr::VecMacro(elements) => {
182 let values = self.eval_slice(elements)?;
183 Ok(Value::Array(values))
184 }
185
186 Expr::Index { object, index } => self.eval_index(object, index),
188 Expr::FieldAccess { object, field } => self.eval_field_access(object, field),
189
190 Expr::StructDef { name, fields } => {
192 self.struct_defs.insert(name.clone(), fields.clone());
193 Ok(Value::Unit)
194 }
195 Expr::StructInit { name, fields } => self.eval_struct_init(name, fields),
196
197 Expr::Match { expr, arms } => self.eval_match(expr, arms),
199
200 Expr::TypeCast { expr, target_type } => {
202 let val = self.eval(expr)?;
203 cast::type_cast(val, target_type)
204 }
205
206 Expr::Closure { params, body } => Ok(Value::Closure {
208 params: params.clone(),
209 body: body.clone(),
210 env: self.env.clone(),
211 }),
212 Expr::Ref { expr, .. } | Expr::Deref(expr) => self.eval(expr),
213 }
214 }
215
216 fn eval_ident(&self, name: &str) -> Result<Value, RuntimeError> {
219 if name == "Some" {
220 return Ok(Value::Function {
221 name: "Some".to_string(),
222 params: vec![("value".to_string(), Type::Inferred)],
223 body: Box::new(Expr::Ident("value".to_string())),
224 closure_env: None,
225 });
226 }
227 self.env
228 .get(name)
229 .cloned()
230 .ok_or_else(|| RuntimeError::new(format!("Undefined variable: '{name}'")))
231 }
232
233 fn eval_let(
234 &mut self,
235 name: &str,
236 mutable: bool,
237 value: Option<&Expr>,
238 ) -> Result<Value, RuntimeError> {
239 let val = match value {
240 Some(expr) => {
241 let v = self.eval(expr)?;
242 if let Value::Return(_) = &v {
243 return Ok(v);
244 }
245 v
246 }
247 None => Value::Unit,
248 };
249 self.env.define(name.to_string(), val, mutable);
250 Ok(Value::Unit)
251 }
252
253 fn eval_block(&mut self, stmts: &[Expr]) -> Result<Value, RuntimeError> {
254 self.env.push_scope();
255 let mut result = Value::Unit;
256 for stmt in stmts {
257 result = self.eval(stmt)?;
258 if matches!(&result, Value::Return(_) | Value::Break(_) | Value::Continue) {
259 self.env.pop_scope();
260 return Ok(result);
261 }
262 }
263 self.env.pop_scope();
264 Ok(result)
265 }
266
267 fn eval_if(
268 &mut self,
269 condition: &Expr,
270 then_block: &Expr,
271 else_block: Option<&Expr>,
272 ) -> Result<Value, RuntimeError> {
273 let cond = self.eval(condition)?;
274 if cond.is_truthy() {
275 self.eval(then_block)
276 } else if let Some(else_b) = else_block {
277 self.eval(else_b)
278 } else {
279 Ok(Value::Unit)
280 }
281 }
282
283 fn eval_while(&mut self, condition: &Expr, body: &Expr) -> Result<Value, RuntimeError> {
284 loop {
285 if !self.eval(condition)?.is_truthy() {
286 break;
287 }
288 match self.eval(body)? {
289 Value::Break(v) => return Ok(v.map(|v| *v).unwrap_or(Value::Unit)),
290 Value::Continue => continue,
291 Value::Return(v) => return Ok(Value::Return(v)),
292 _ => {}
293 }
294 }
295 Ok(Value::Unit)
296 }
297
298 fn eval_loop(&mut self, body: &Expr) -> Result<Value, RuntimeError> {
299 loop {
300 match self.eval(body)? {
301 Value::Break(v) => return Ok(v.map(|v| *v).unwrap_or(Value::Unit)),
302 Value::Continue => continue,
303 Value::Return(v) => return Ok(Value::Return(v)),
304 _ => {}
305 }
306 }
307 }
308
309 fn eval_for(
310 &mut self,
311 var: &str,
312 iterator: &Expr,
313 body: &Expr,
314 ) -> Result<Value, RuntimeError> {
315 let iter_val = self.eval(iterator)?;
316 let items = value_to_iterator(iter_val)?;
317 for item in items {
318 self.env.push_scope();
319 self.env.define(var.to_string(), item, true);
320 let result = self.eval(body)?;
321 self.env.pop_scope();
322 match result {
323 Value::Break(v) => return Ok(v.map(|v| *v).unwrap_or(Value::Unit)),
324 Value::Continue => continue,
325 Value::Return(v) => return Ok(Value::Return(v)),
326 _ => {}
327 }
328 }
329 Ok(Value::Unit)
330 }
331
332 fn eval_range(
333 &mut self,
334 start: Option<&Expr>,
335 end: Option<&Expr>,
336 inclusive: bool,
337 ) -> Result<Value, RuntimeError> {
338 let start_val = match start {
339 Some(e) => expect_int(self.eval(e)?, "Range start")?,
340 None => 0,
341 };
342 match end {
343 Some(e) => {
344 let end_val = expect_int(self.eval(e)?, "Range end")?;
345 let items: Vec<Value> = if inclusive {
346 (start_val..=end_val).map(Value::Int).collect()
347 } else {
348 (start_val..end_val).map(Value::Int).collect()
349 };
350 Ok(Value::Array(items))
351 }
352 None => Ok(Value::Array(Vec::new())),
353 }
354 }
355
356 fn eval_fn_def(
359 &mut self,
360 name: &str,
361 params: &[(String, Type)],
362 body: &Expr,
363 ) -> Result<Value, RuntimeError> {
364 let func = Value::Function {
365 name: name.to_string(),
366 params: params.to_vec(),
367 body: Box::new(body.clone()),
368 closure_env: None,
369 };
370 self.env.define(name.to_string(), func, false);
371 Ok(Value::Unit)
372 }
373
374 fn eval_fn_call(&mut self, name: &str, args: &[Expr]) -> Result<Value, RuntimeError> {
375 let evaluated_args = self.eval_slice(args)?;
376
377 if let Some(result) = self.call_builtin(name, &evaluated_args)? {
379 return Ok(result);
380 }
381
382 let func = self
383 .env
384 .get(name)
385 .cloned()
386 .ok_or_else(|| RuntimeError::new(format!("Undefined function: '{name}'")))?;
387
388 self.call_function(func, evaluated_args)
389 }
390
391 pub(crate) fn call_function(
393 &mut self,
394 func: Value,
395 args: Vec<Value>,
396 ) -> Result<Value, RuntimeError> {
397 match func {
398 Value::Function {
399 params,
400 body,
401 closure_env,
402 ..
403 } => {
404 let saved_env = self.env.clone();
405 if let Some(env) = closure_env {
406 self.env = env;
407 }
408 self.env.push_scope();
409 for (i, (param_name, _)) in params.iter().enumerate() {
410 let val = args.get(i).cloned().unwrap_or(Value::Unit);
411 self.env.define(param_name.clone(), val, true);
412 }
413 let result = self.eval(&body)?;
414 self.env.pop_scope();
415 self.env = saved_env;
416 unwrap_return(result)
417 }
418 Value::Closure {
419 params, body, env, ..
420 } => {
421 let saved_env = self.env.clone();
422 self.env = env;
423 self.env.push_scope();
424 for (i, (param_name, _)) in params.iter().enumerate() {
425 let val = args.get(i).cloned().unwrap_or(Value::Unit);
426 self.env.define(param_name.clone(), val, true);
427 }
428 let result = self.eval(&body)?;
429 self.env.pop_scope();
430 self.env = saved_env;
431 unwrap_return(result)
432 }
433 _ => Err(RuntimeError::new(format!(
434 "'{}' is not callable",
435 func.type_name()
436 ))),
437 }
438 }
439
440 fn eval_array_repeat(
443 &mut self,
444 value: &Expr,
445 count: &Expr,
446 ) -> Result<Value, RuntimeError> {
447 let val = self.eval(value)?;
448 let cnt = expect_int(self.eval(count)?, "Array repeat count")? as usize;
449 Ok(Value::Array(vec![val; cnt]))
450 }
451
452 fn eval_index(&mut self, object: &Expr, index: &Expr) -> Result<Value, RuntimeError> {
453 let obj = self.eval(object)?;
454 let idx = self.eval(index)?;
455
456 match (&obj, &idx) {
457 (Value::Array(arr), Value::Int(i)) => {
458 let index = normalize_index(*i, arr.len());
459 arr.get(index).cloned().ok_or_else(|| {
460 RuntimeError::new(format!(
461 "Index {i} out of bounds for array of length {}",
462 arr.len()
463 ))
464 })
465 }
466 (Value::String(s), Value::Int(i)) => {
467 let index = normalize_index(*i, s.len());
468 s.chars().nth(index).map(Value::Char).ok_or_else(|| {
469 RuntimeError::new(format!(
470 "Index {i} out of bounds for string of length {}",
471 s.len()
472 ))
473 })
474 }
475 (Value::Tuple(elems), Value::Int(i)) => {
476 elems.get(*i as usize).cloned().ok_or_else(|| {
477 RuntimeError::new(format!(
478 "Index {i} out of bounds for tuple of length {}",
479 elems.len()
480 ))
481 })
482 }
483 _ => Err(RuntimeError::new(format!(
484 "Cannot index {} with {}",
485 obj.type_name(),
486 idx.type_name()
487 ))),
488 }
489 }
490
491 fn eval_field_access(&mut self, object: &Expr, field: &str) -> Result<Value, RuntimeError> {
492 let obj = self.eval(object)?;
493 match &obj {
494 Value::Struct { fields, .. } => {
495 fields.get(field).cloned().ok_or_else(|| {
496 RuntimeError::new(format!("No field '{field}' on struct"))
497 })
498 }
499 Value::Tuple(elements) => {
500 let idx: usize = field.parse().map_err(|_| {
501 RuntimeError::new(format!("Invalid tuple field: {field}"))
502 })?;
503 elements.get(idx).cloned().ok_or_else(|| {
504 RuntimeError::new(format!("Tuple index {idx} out of bounds"))
505 })
506 }
507 _ => Err(RuntimeError::new(format!(
508 "Cannot access field '{field}' on {}",
509 obj.type_name()
510 ))),
511 }
512 }
513
514 fn eval_struct_init(
515 &mut self,
516 name: &str,
517 fields: &[(String, Expr)],
518 ) -> Result<Value, RuntimeError> {
519 let mut field_values = HashMap::new();
520 for (fname, fexpr) in fields {
521 field_values.insert(fname.clone(), self.eval(fexpr)?);
522 }
523 Ok(Value::Struct {
524 name: name.to_string(),
525 fields: field_values,
526 })
527 }
528
529 fn eval_match(&mut self, expr: &Expr, arms: &[MatchArm]) -> Result<Value, RuntimeError> {
530 let val = self.eval(expr)?;
531 for arm in arms {
532 if self.match_pattern(&arm.pattern, &val)? {
533 self.env.push_scope();
534 self.bind_pattern(&arm.pattern, &val)?;
535 let result = self.eval(&arm.body)?;
536 self.env.pop_scope();
537 return Ok(result);
538 }
539 }
540 Err(RuntimeError::new("Non-exhaustive match"))
541 }
542
543 fn assign_to(&mut self, target: &Expr, value: Value) -> Result<(), RuntimeError> {
546 match target {
547 Expr::Ident(name) => self.env.set(name, value),
548 Expr::Index { object, index } => self.assign_to_index(object, index, value),
549 Expr::FieldAccess { object, field } => self.assign_to_field(object, field, value),
550 _ => Err(RuntimeError::new("Invalid assignment target")),
551 }
552 }
553
554 fn assign_to_index(
555 &mut self,
556 object: &Expr,
557 index: &Expr,
558 value: Value,
559 ) -> Result<(), RuntimeError> {
560 let Expr::Ident(name) = object else {
561 return Err(RuntimeError::new("Complex index assignment not supported"));
562 };
563 let idx = expect_int(self.eval(index)?, "Index")? as usize;
564 let mut arr = self
565 .env
566 .get(name)
567 .cloned()
568 .ok_or_else(|| RuntimeError::new(format!("Undefined variable: '{name}'")))?;
569 let Value::Array(ref mut elements) = arr else {
570 return Err(RuntimeError::new("Cannot index non-array"));
571 };
572 if idx >= elements.len() {
573 return Err(RuntimeError::new(format!("Index {idx} out of bounds")));
574 }
575 elements[idx] = value;
576 self.env.set(name, arr)
577 }
578
579 fn assign_to_field(
580 &mut self,
581 object: &Expr,
582 field: &str,
583 value: Value,
584 ) -> Result<(), RuntimeError> {
585 let Expr::Ident(name) = object else {
586 return Err(RuntimeError::new("Complex field assignment not supported"));
587 };
588 let mut obj = self
589 .env
590 .get(name)
591 .cloned()
592 .ok_or_else(|| RuntimeError::new(format!("Undefined variable: '{name}'")))?;
593 let Value::Struct { ref mut fields, .. } = obj else {
594 return Err(RuntimeError::new("Cannot set field on non-struct"));
595 };
596 fields.insert(field.to_string(), value);
597 self.env.set(name, obj)
598 }
599
600 pub(crate) fn eval_binary_op(
603 &self,
604 left: &Value,
605 op: &BinOp,
606 right: &Value,
607 ) -> Result<Value, RuntimeError> {
608 if let (Value::String(a), BinOp::Add, Value::String(b)) = (left, op, right) {
610 return Ok(Value::String(format!("{a}{b}")));
611 }
612
613 match (left, right) {
614 (Value::Int(a), Value::Int(b)) => eval_int_op(*a, op, *b),
615 (Value::Float(a), Value::Float(b)) => eval_float_op(*a, op, *b),
616 (Value::Int(a), Value::Float(b)) => eval_float_op(*a as f64, op, *b),
617 (Value::Float(a), Value::Int(b)) => eval_float_op(*a, op, *b as f64),
618 (Value::Bool(a), Value::Bool(b)) => eval_bool_op(*a, op, *b),
619 (Value::String(a), Value::String(b)) => eval_string_cmp(a, op, b),
620 _ => Err(RuntimeError::new(format!(
621 "Cannot apply {op:?} to {} and {}",
622 left.type_name(),
623 right.type_name()
624 ))),
625 }
626 }
627
628 fn eval_unary_op(&self, op: &UnaryOp, val: &Value) -> Result<Value, RuntimeError> {
629 match (op, val) {
630 (UnaryOp::Neg, Value::Int(n)) => Ok(Value::Int(-n)),
631 (UnaryOp::Neg, Value::Float(n)) => Ok(Value::Float(-n)),
632 (UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
633 (UnaryOp::Not, Value::Int(n)) => Ok(Value::Int(!n)),
634 _ => Err(RuntimeError::new(format!(
635 "Cannot apply {op:?} to {}",
636 val.type_name()
637 ))),
638 }
639 }
640
641 pub(crate) fn values_equal(&self, left: &Value, right: &Value) -> bool {
643 matches!(
644 self.eval_binary_op(left, &BinOp::Eq, right),
645 Ok(Value::Bool(true))
646 )
647 }
648
649 pub(crate) fn format_string(
651 &self,
652 fmt: &str,
653 args: &[Value],
654 ) -> Result<String, RuntimeError> {
655 format::format_string(fmt, args)
656 }
657}
658
659fn value_to_iterator(val: Value) -> Result<Vec<Value>, RuntimeError> {
663 match val {
664 Value::Array(items) => Ok(items),
665 Value::String(s) => Ok(s.chars().map(Value::Char).collect()),
666 _ => Err(RuntimeError::new(format!(
667 "Cannot iterate over {}",
668 val.type_name()
669 ))),
670 }
671}
672
673fn unwrap_return(result: Value) -> Result<Value, RuntimeError> {
675 match result {
676 Value::Return(Some(v)) => Ok(*v),
677 Value::Return(None) => Ok(Value::Unit),
678 other => Ok(other),
679 }
680}
681
682fn expect_int(val: Value, context: &str) -> Result<i64, RuntimeError> {
684 match val {
685 Value::Int(n) => Ok(n),
686 other => Err(RuntimeError::new(format!(
687 "{context} must be integer, got {}",
688 other.type_name()
689 ))),
690 }
691}
692
693fn normalize_index(i: i64, len: usize) -> usize {
695 if i < 0 {
696 (len as i64 + i) as usize
697 } else {
698 i as usize
699 }
700}
701
702fn eval_int_op(a: i64, op: &BinOp, b: i64) -> Result<Value, RuntimeError> {
705 match op {
706 BinOp::Add => Ok(Value::Int(a.wrapping_add(b))),
707 BinOp::Sub => Ok(Value::Int(a.wrapping_sub(b))),
708 BinOp::Mul => Ok(Value::Int(a.wrapping_mul(b))),
709 BinOp::Div => {
710 if b == 0 {
711 Err(RuntimeError::new("Division by zero"))
712 } else {
713 Ok(Value::Int(a / b))
714 }
715 }
716 BinOp::Mod => {
717 if b == 0 {
718 Err(RuntimeError::new("Modulo by zero"))
719 } else {
720 Ok(Value::Int(a % b))
721 }
722 }
723 BinOp::Eq => Ok(Value::Bool(a == b)),
724 BinOp::NotEq => Ok(Value::Bool(a != b)),
725 BinOp::Lt => Ok(Value::Bool(a < b)),
726 BinOp::LtEq => Ok(Value::Bool(a <= b)),
727 BinOp::Gt => Ok(Value::Bool(a > b)),
728 BinOp::GtEq => Ok(Value::Bool(a >= b)),
729 BinOp::BitAnd => Ok(Value::Int(a & b)),
730 BinOp::BitOr => Ok(Value::Int(a | b)),
731 BinOp::BitXor => Ok(Value::Int(a ^ b)),
732 BinOp::Shl => Ok(Value::Int(a << b)),
733 BinOp::Shr => Ok(Value::Int(a >> b)),
734 _ => Err(RuntimeError::new(format!(
735 "Unsupported operation {op:?} on integers"
736 ))),
737 }
738}
739
740fn eval_float_op(a: f64, op: &BinOp, b: f64) -> Result<Value, RuntimeError> {
741 match op {
742 BinOp::Add => Ok(Value::Float(a + b)),
743 BinOp::Sub => Ok(Value::Float(a - b)),
744 BinOp::Mul => Ok(Value::Float(a * b)),
745 BinOp::Div => Ok(Value::Float(a / b)),
746 BinOp::Mod => Ok(Value::Float(a % b)),
747 BinOp::Eq => Ok(Value::Bool(a == b)),
748 BinOp::NotEq => Ok(Value::Bool(a != b)),
749 BinOp::Lt => Ok(Value::Bool(a < b)),
750 BinOp::LtEq => Ok(Value::Bool(a <= b)),
751 BinOp::Gt => Ok(Value::Bool(a > b)),
752 BinOp::GtEq => Ok(Value::Bool(a >= b)),
753 _ => Err(RuntimeError::new(format!(
754 "Unsupported operation {op:?} on floats"
755 ))),
756 }
757}
758
759fn eval_bool_op(a: bool, op: &BinOp, b: bool) -> Result<Value, RuntimeError> {
760 match op {
761 BinOp::And => Ok(Value::Bool(a && b)),
762 BinOp::Or => Ok(Value::Bool(a || b)),
763 BinOp::Eq => Ok(Value::Bool(a == b)),
764 BinOp::NotEq => Ok(Value::Bool(a != b)),
765 _ => Err(RuntimeError::new(format!(
766 "Unsupported operation {op:?} on booleans"
767 ))),
768 }
769}
770
771fn eval_string_cmp(a: &str, op: &BinOp, b: &str) -> Result<Value, RuntimeError> {
772 match op {
773 BinOp::Eq => Ok(Value::Bool(a == b)),
774 BinOp::NotEq => Ok(Value::Bool(a != b)),
775 BinOp::Lt => Ok(Value::Bool(a < b)),
776 BinOp::LtEq => Ok(Value::Bool(a <= b)),
777 BinOp::Gt => Ok(Value::Bool(a > b)),
778 BinOp::GtEq => Ok(Value::Bool(a >= b)),
779 _ => Err(RuntimeError::new(format!(
780 "Unsupported operation {op:?} on strings"
781 ))),
782 }
783}