1use crate::ast::*;
2use crate::env::Env;
3use crate::error::{ErrorKind, IonError};
4use crate::host_types::TypeRegistry;
5use crate::stdlib::OutputHandler;
6use crate::value::{IonFn, Value};
7use indexmap::IndexMap;
8use std::sync::Arc;
9
10enum Signal {
12 Return(Value),
13 Break {
14 label: Option<String>,
15 value: Value,
16 },
17 Continue {
18 label: Option<String>,
19 },
20}
21
22fn signal_targets_loop(sig_label: &Option<String>, loop_label: &Option<String>) -> bool {
26 match sig_label {
27 None => true,
28 Some(want) => loop_label.as_deref() == Some(want.as_str()),
29 }
30}
31
32fn unmatched_label_msg(keyword: &str, label: Option<String>) -> String {
33 match label {
34 Some(name) => format!("{keyword} with unknown label '{name}"),
35 None => format!("{keyword} outside of loop"),
36 }
37}
38
39type IonResult = Result<Value, IonError>;
40type SignalResult = Result<Value, SignalOrError>;
41
42enum SignalOrError {
43 Signal(Signal),
44 Error(IonError),
45}
46
47impl From<IonError> for SignalOrError {
48 fn from(e: IonError) -> Self {
49 SignalOrError::Error(e)
50 }
51}
52
53impl From<Signal> for SignalOrError {
54 fn from(s: Signal) -> Self {
55 SignalOrError::Signal(s)
56 }
57}
58
59#[derive(Clone)]
60pub struct Limits {
61 pub max_call_depth: usize,
62 pub max_loop_iters: usize,
63}
64
65impl Default for Limits {
66 fn default() -> Self {
67 Self {
68 max_call_depth: 512,
69 max_loop_iters: 1_000_000,
70 }
71 }
72}
73
74pub struct Interpreter {
75 pub env: Env,
76 pub limits: Limits,
77 pub types: TypeRegistry,
78 call_depth: usize,
79 #[cfg(feature = "concurrency")]
80 nursery: Option<crate::async_rt::Nursery>,
81 #[cfg(feature = "concurrency")]
82 pub(crate) cancel_flag: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
83}
84
85impl Default for Interpreter {
86 fn default() -> Self {
87 Self::new()
88 }
89}
90
91impl Interpreter {
92 pub fn new() -> Self {
93 Self::with_output(crate::stdlib::missing_output_handler())
94 }
95
96 pub fn with_output(output: Arc<dyn OutputHandler>) -> Self {
97 let mut env = Env::new();
98 register_builtins(&mut env, output);
99 Self {
100 env,
101 limits: Limits::default(),
102 types: TypeRegistry::new(),
103 call_depth: 0,
104 #[cfg(feature = "concurrency")]
105 nursery: None,
106 #[cfg(feature = "concurrency")]
107 cancel_flag: None,
108 }
109 }
110
111 pub fn eval_program(&mut self, program: &Program) -> IonResult {
112 match self.eval_stmts(&program.stmts) {
113 Ok(v) => Ok(v),
114 Err(SignalOrError::Error(e)) if e.kind == ErrorKind::PropagatedErr => {
115 Ok(Value::Result(Err(Box::new(Value::Str(e.message.clone())))))
116 }
117 Err(SignalOrError::Error(e)) if e.kind == ErrorKind::PropagatedNone => {
118 Ok(Value::Option(None))
119 }
120 Err(SignalOrError::Error(e)) => Err(e),
121 Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
122 Err(SignalOrError::Signal(Signal::Break { label, .. })) => {
123 Err(IonError::runtime(unmatched_label_msg("break", label), 0, 0))
124 }
125 Err(SignalOrError::Signal(Signal::Continue { label })) => Err(IonError::runtime(
126 unmatched_label_msg("continue", label),
127 0,
128 0,
129 )),
130 }
131 }
132
133 pub fn with_env(env: Env) -> Self {
135 Self {
136 env,
137 limits: Limits::default(),
138 types: TypeRegistry::new(),
139 call_depth: 0,
140 #[cfg(feature = "concurrency")]
141 nursery: None,
142 #[cfg(feature = "concurrency")]
143 cancel_flag: None,
144 }
145 }
146
147 pub fn take_env(self) -> Env {
149 self.env
150 }
151
152 pub fn eval_block(&mut self, stmts: &[Stmt]) -> IonResult {
154 match self.eval_stmts(stmts) {
155 Ok(v) => Ok(v),
156 Err(SignalOrError::Error(e)) => Err(e),
157 Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
158 Err(SignalOrError::Signal(Signal::Break { value, .. })) => Ok(value),
159 Err(SignalOrError::Signal(Signal::Continue { .. })) => Ok(Value::Unit),
160 }
161 }
162
163 pub fn eval_single_expr(&mut self, expr: &Expr) -> IonResult {
165 match self.eval_expr(expr) {
166 Ok(v) => Ok(v),
167 Err(SignalOrError::Error(e)) => Err(e),
168 Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
169 Err(SignalOrError::Signal(Signal::Break { value, .. })) => Ok(value),
170 Err(SignalOrError::Signal(Signal::Continue { .. })) => Ok(Value::Unit),
171 }
172 }
173
174 #[cfg(feature = "concurrency")]
175 fn check_cancelled(&self, line: usize, col: usize) -> Result<(), SignalOrError> {
176 if let Some(flag) = &self.cancel_flag {
177 if flag.load(std::sync::atomic::Ordering::Relaxed) {
178 return Err(IonError::runtime(ion_str!("task cancelled"), line, col).into());
179 }
180 }
181 Ok(())
182 }
183
184 fn eval_stmts(&mut self, stmts: &[Stmt]) -> SignalResult {
185 let mut last = Value::Unit;
186 for (i, stmt) in stmts.iter().enumerate() {
187 let is_last = i == stmts.len() - 1;
188 match &stmt.kind {
189 StmtKind::ExprStmt { expr, has_semi } => {
190 let val = self.eval_expr(expr)?;
191 if is_last && !has_semi {
192 last = val;
193 } else {
194 last = Value::Unit;
195 }
196 }
197 _ => {
198 self.eval_stmt(stmt)?;
199 last = Value::Unit;
200 }
201 }
202 }
203 Ok(last)
204 }
205
206 fn eval_stmt(&mut self, stmt: &Stmt) -> SignalResult {
207 match &stmt.kind {
208 StmtKind::Let {
209 mutable,
210 pattern,
211 type_ann,
212 value,
213 } => {
214 let val = self.eval_expr(value)?;
215 if let Some(ann) = type_ann {
216 Self::check_type_ann(&val, ann, stmt.span)?;
217 }
218 self.bind_pattern(pattern, &val, *mutable, stmt.span)?;
219 Ok(Value::Unit)
220 }
221 StmtKind::FnDecl { name, params, body } => {
222 let captures = self.env.capture();
223 let func = Value::Fn(IonFn::new(
224 name.clone(),
225 params.clone(),
226 body.clone(),
227 captures,
228 ));
229 self.env.define(name.clone(), func, false);
230 Ok(Value::Unit)
231 }
232 StmtKind::ExprStmt { expr, .. } => {
233 self.eval_expr(expr)?;
234 Ok(Value::Unit)
235 }
236 StmtKind::For {
237 label,
238 pattern,
239 iter,
240 body,
241 } => {
242 let iter_val = self.eval_expr(iter)?;
243 let items = self.value_to_iter(&iter_val, iter.span)?;
244 for item in items {
245 #[cfg(feature = "concurrency")]
246 self.check_cancelled(stmt.span.line, stmt.span.col)?;
247 self.env.push_scope();
248 self.bind_pattern(pattern, &item, false, iter.span)?;
249 match self.eval_stmts(body) {
250 Ok(_) => {}
251 Err(SignalOrError::Signal(Signal::Break {
252 label: sig_label,
253 value,
254 })) => {
255 self.env.pop_scope();
256 if signal_targets_loop(&sig_label, label) {
257 break;
258 }
259 return Err(Signal::Break {
260 label: sig_label,
261 value,
262 }
263 .into());
264 }
265 Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
266 self.env.pop_scope();
267 if signal_targets_loop(&sig_label, label) {
268 continue;
269 }
270 return Err(Signal::Continue { label: sig_label }.into());
271 }
272 Err(e) => {
273 self.env.pop_scope();
274 return Err(e);
275 }
276 }
277 self.env.pop_scope();
278 }
279 Ok(Value::Unit)
280 }
281 StmtKind::While { label, cond, body } => {
282 let mut iters = 0usize;
283 loop {
284 #[cfg(feature = "concurrency")]
285 self.check_cancelled(stmt.span.line, stmt.span.col)?;
286 let c = self.eval_expr(cond)?;
287 if !c.is_truthy() {
288 break;
289 }
290 iters += 1;
291 if iters > self.limits.max_loop_iters {
292 return Err(IonError::runtime(
293 ion_str!("maximum loop iterations exceeded").to_string(),
294 stmt.span.line,
295 stmt.span.col,
296 )
297 .into());
298 }
299 self.env.push_scope();
300 match self.eval_stmts(body) {
301 Ok(_) => {}
302 Err(SignalOrError::Signal(Signal::Break {
303 label: sig_label,
304 value,
305 })) => {
306 self.env.pop_scope();
307 if signal_targets_loop(&sig_label, label) {
308 break;
309 }
310 return Err(Signal::Break {
311 label: sig_label,
312 value,
313 }
314 .into());
315 }
316 Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
317 self.env.pop_scope();
318 if signal_targets_loop(&sig_label, label) {
319 continue;
320 }
321 return Err(Signal::Continue { label: sig_label }.into());
322 }
323 Err(e) => {
324 self.env.pop_scope();
325 return Err(e);
326 }
327 }
328 self.env.pop_scope();
329 }
330 Ok(Value::Unit)
331 }
332 StmtKind::WhileLet {
333 label,
334 pattern,
335 expr,
336 body,
337 } => {
338 let mut iters = 0usize;
339 loop {
340 #[cfg(feature = "concurrency")]
341 self.check_cancelled(stmt.span.line, stmt.span.col)?;
342 let val = self.eval_expr(expr)?;
343 if !self.pattern_matches(pattern, &val) {
344 break;
345 }
346 iters += 1;
347 if iters > self.limits.max_loop_iters {
348 return Err(IonError::runtime(
349 ion_str!("maximum loop iterations exceeded").to_string(),
350 stmt.span.line,
351 stmt.span.col,
352 )
353 .into());
354 }
355 self.env.push_scope();
356 self.bind_pattern(pattern, &val, false, expr.span)?;
357 match self.eval_stmts(body) {
358 Ok(_) => {}
359 Err(SignalOrError::Signal(Signal::Break {
360 label: sig_label,
361 value,
362 })) => {
363 self.env.pop_scope();
364 if signal_targets_loop(&sig_label, label) {
365 break;
366 }
367 return Err(Signal::Break {
368 label: sig_label,
369 value,
370 }
371 .into());
372 }
373 Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
374 self.env.pop_scope();
375 if signal_targets_loop(&sig_label, label) {
376 continue;
377 }
378 return Err(Signal::Continue { label: sig_label }.into());
379 }
380 Err(e) => {
381 self.env.pop_scope();
382 return Err(e);
383 }
384 }
385 self.env.pop_scope();
386 }
387 Ok(Value::Unit)
388 }
389 StmtKind::Loop { label, body } => {
390 let mut iters = 0usize;
391 let result = loop {
392 iters += 1;
393 if iters > self.limits.max_loop_iters {
394 return Err(IonError::runtime(
395 ion_str!("maximum loop iterations exceeded").to_string(),
396 stmt.span.line,
397 stmt.span.col,
398 )
399 .into());
400 }
401 self.env.push_scope();
402 match self.eval_stmts(body) {
403 Ok(_) => {}
404 Err(SignalOrError::Signal(Signal::Break {
405 label: sig_label,
406 value,
407 })) => {
408 self.env.pop_scope();
409 if signal_targets_loop(&sig_label, label) {
410 break value;
411 }
412 return Err(Signal::Break {
413 label: sig_label,
414 value,
415 }
416 .into());
417 }
418 Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
419 self.env.pop_scope();
420 if signal_targets_loop(&sig_label, label) {
421 continue;
422 }
423 return Err(Signal::Continue { label: sig_label }.into());
424 }
425 Err(e) => {
426 self.env.pop_scope();
427 return Err(e);
428 }
429 }
430 self.env.pop_scope();
431 };
432 Ok(result)
433 }
434 StmtKind::Break { label, value } => {
435 let v = match value {
436 Some(expr) => self.eval_expr(expr)?,
437 None => Value::Unit,
438 };
439 Err(Signal::Break {
440 label: label.clone(),
441 value: v,
442 }
443 .into())
444 }
445 StmtKind::Continue { label } => Err(Signal::Continue {
446 label: label.clone(),
447 }
448 .into()),
449 StmtKind::Return { value } => {
450 let v = match value {
451 Some(expr) => self.eval_expr(expr)?,
452 None => Value::Unit,
453 };
454 Err(Signal::Return(v).into())
455 }
456 StmtKind::Assign { target, op, value } => {
457 let rhs = self.eval_expr(value)?;
458 match target {
459 AssignTarget::Ident(name) => {
460 let final_val = match op {
461 AssignOp::Eq => rhs,
462 _ => {
463 let lhs = self
464 .env
465 .get(name)
466 .ok_or_else(|| {
467 IonError::name(
468 format!("{}{}", ion_str!("undefined variable: "), name),
469 stmt.span.line,
470 stmt.span.col,
471 )
472 })?
473 .clone();
474 self.apply_compound_op(*op, &lhs, &rhs, stmt.span)?
475 }
476 };
477 self.env
478 .set(name, final_val)
479 .map_err(|msg| IonError::runtime(msg, stmt.span.line, stmt.span.col))?;
480 }
481 AssignTarget::Index(obj_expr, index_expr) => {
482 let var_name = match &obj_expr.kind {
483 ExprKind::Ident(name) => name.clone(),
484 _ => {
485 return Err(IonError::runtime(
486 ion_str!("index assignment only supported on variables"),
487 stmt.span.line,
488 stmt.span.col,
489 )
490 .into())
491 }
492 };
493 let mut container = self
494 .env
495 .get(&var_name)
496 .ok_or_else(|| {
497 IonError::name(
498 format!("{}{}", ion_str!("undefined variable: "), var_name),
499 stmt.span.line,
500 stmt.span.col,
501 )
502 })?
503 .clone();
504 let index = self.eval_expr(index_expr)?;
505 let final_val = match op {
506 AssignOp::Eq => rhs,
507 _ => {
508 let old = self.index_access(&container, &index, stmt.span)?;
509 let old = match old {
511 Value::Option(Some(v)) => *v,
512 other => other,
513 };
514 self.apply_compound_op(*op, &old, &rhs, stmt.span)?
515 }
516 };
517 match (&mut container, &index) {
518 (Value::List(items), Value::Int(i)) => {
519 let idx = if *i < 0 { items.len() as i64 + i } else { *i } as usize;
520 if idx >= items.len() {
521 return Err(IonError::runtime(
522 format!(
523 "{}{}{}",
524 ion_str!("index "),
525 i,
526 ion_str!(" out of range")
527 ),
528 stmt.span.line,
529 stmt.span.col,
530 )
531 .into());
532 }
533 items[idx] = final_val;
534 }
535 (Value::Dict(map), Value::Str(key)) => {
536 map.insert(key.clone(), final_val);
537 }
538 _ => {
539 return Err(IonError::type_err(
540 format!(
541 "{}{}",
542 ion_str!("cannot set index on "),
543 container.type_name()
544 ),
545 stmt.span.line,
546 stmt.span.col,
547 )
548 .into())
549 }
550 }
551 self.env
552 .set(&var_name, container)
553 .map_err(|msg| IonError::runtime(msg, stmt.span.line, stmt.span.col))?;
554 }
555 AssignTarget::Field(obj_expr, field) => {
556 let var_name = match &obj_expr.kind {
557 ExprKind::Ident(name) => name.clone(),
558 _ => {
559 return Err(IonError::runtime(
560 ion_str!("field assignment only supported on variables"),
561 stmt.span.line,
562 stmt.span.col,
563 )
564 .into())
565 }
566 };
567 let mut container = self
568 .env
569 .get(&var_name)
570 .ok_or_else(|| {
571 IonError::name(
572 format!("{}{}", ion_str!("undefined variable: "), var_name),
573 stmt.span.line,
574 stmt.span.col,
575 )
576 })?
577 .clone();
578 let final_val = match op {
579 AssignOp::Eq => rhs,
580 _ => {
581 let old = self.field_access(&container, field, stmt.span)?;
582 self.apply_compound_op(*op, &old, &rhs, stmt.span)?
583 }
584 };
585 match &mut container {
586 Value::Dict(map) => {
587 map.insert(field.clone(), final_val);
588 }
589 Value::HostStruct { fields, .. } => {
590 if fields.contains_key(field.as_str()) {
591 fields.insert(field.clone(), final_val);
592 } else {
593 return Err(IonError::runtime(
594 format!(
595 "{}{}{}",
596 ion_str!("field '"),
597 field,
598 ion_str!("' not found")
599 ),
600 stmt.span.line,
601 stmt.span.col,
602 )
603 .into());
604 }
605 }
606 _ => {
607 return Err(IonError::type_err(
608 format!(
609 "{}{}",
610 ion_str!("cannot set field on "),
611 container.type_name()
612 ),
613 stmt.span.line,
614 stmt.span.col,
615 )
616 .into())
617 }
618 }
619 self.env
620 .set(&var_name, container)
621 .map_err(|msg| IonError::runtime(msg, stmt.span.line, stmt.span.col))?;
622 }
623 }
624 Ok(Value::Unit)
625 }
626 StmtKind::Use { path, imports } => {
627 let root = self.env.get(&path[0]).ok_or_else(|| {
629 SignalOrError::Error(IonError::name(
630 format!("{}{}", ion_str!("undefined module: "), &path[0]),
631 stmt.span.line,
632 stmt.span.col,
633 ))
634 })?;
635 let mut module_val = root.clone();
636 for seg in &path[1..] {
637 match &module_val {
638 Value::Dict(map) => {
639 module_val = map.get(seg).cloned().ok_or_else(|| {
640 SignalOrError::Error(IonError::name(
641 format!(
642 "{}{}{}{}",
643 ion_str!("'"),
644 seg,
645 ion_str!("' not found in module "),
646 &path[0]
647 ),
648 stmt.span.line,
649 stmt.span.col,
650 ))
651 })?;
652 }
653 _ => {
654 return Err(IonError::type_err(
655 format!(
656 "{}{}{}",
657 ion_str!("'"),
658 seg,
659 ion_str!("' is not a module")
660 ),
661 stmt.span.line,
662 stmt.span.col,
663 )
664 .into())
665 }
666 }
667 }
668 match imports {
670 UseImports::Glob => {
671 if let Value::Dict(map) = &module_val {
672 for (name, val) in map {
673 self.env.define(name.clone(), val.clone(), false);
674 }
675 } else {
676 return Err(IonError::type_err(
677 ion_str!("use target is not a module"),
678 stmt.span.line,
679 stmt.span.col,
680 )
681 .into());
682 }
683 }
684 UseImports::Names(names) => {
685 if let Value::Dict(map) = &module_val {
686 for name in names {
687 let val = map.get(name).ok_or_else(|| {
688 SignalOrError::Error(IonError::name(
689 format!(
690 "{}{}{}",
691 ion_str!("'"),
692 name,
693 ion_str!("' not found in module")
694 ),
695 stmt.span.line,
696 stmt.span.col,
697 ))
698 })?;
699 self.env.define(name.clone(), val.clone(), false);
700 }
701 } else {
702 return Err(IonError::type_err(
703 ion_str!("use target is not a module"),
704 stmt.span.line,
705 stmt.span.col,
706 )
707 .into());
708 }
709 }
710 UseImports::Single(name) => {
711 if let Value::Dict(map) = &module_val {
712 let val = map.get(name).ok_or_else(|| {
713 SignalOrError::Error(IonError::name(
714 format!(
715 "{}{}{}",
716 ion_str!("'"),
717 name,
718 ion_str!("' not found in module")
719 ),
720 stmt.span.line,
721 stmt.span.col,
722 ))
723 })?;
724 self.env.define(name.clone(), val.clone(), false);
725 } else {
726 return Err(IonError::type_err(
727 ion_str!("use target is not a module"),
728 stmt.span.line,
729 stmt.span.col,
730 )
731 .into());
732 }
733 }
734 }
735 Ok(Value::Unit)
736 }
737 }
738 }
739
740 fn eval_expr(&mut self, expr: &Expr) -> SignalResult {
741 let span = expr.span;
742 match &expr.kind {
743 ExprKind::Int(n) => Ok(Value::Int(*n)),
744 ExprKind::Float(n) => Ok(Value::Float(*n)),
745 ExprKind::Bool(b) => Ok(Value::Bool(*b)),
746 ExprKind::Str(s) => Ok(Value::Str(s.clone())),
747 ExprKind::Bytes(b) => Ok(Value::Bytes(b.clone())),
748 ExprKind::None => Ok(Value::Option(None)),
749 ExprKind::Unit => Ok(Value::Unit),
750
751 ExprKind::FStr(parts) => {
752 let mut result = String::new();
753 for part in parts {
754 match part {
755 FStrPart::Literal(s) => result.push_str(s),
756 FStrPart::Expr(e) => {
757 let val = self.eval_expr(e)?;
758 result.push_str(&val.to_string());
759 }
760 }
761 }
762 Ok(Value::Str(result))
763 }
764
765 ExprKind::Ident(name) => self.env.get(name).cloned().ok_or_else(|| {
766 IonError::name(
767 format!("{}{}", ion_str!("undefined variable: "), name),
768 span.line,
769 span.col,
770 )
771 .into()
772 }),
773
774 ExprKind::ModulePath(segments) => {
775 let root = self.env.get(&segments[0]).ok_or_else(|| {
777 SignalOrError::Error(IonError::name(
778 format!("{}{}", ion_str!("undefined module: "), &segments[0]),
779 span.line,
780 span.col,
781 ))
782 })?;
783 let mut current = root.clone();
784 for seg in &segments[1..] {
785 match ¤t {
786 Value::Dict(map) => {
787 current = map.get(seg).cloned().ok_or_else(|| {
788 SignalOrError::Error(IonError::name(
789 format!(
790 "{}{}{}{}",
791 ion_str!("'"),
792 seg,
793 ion_str!("' not found in module "),
794 &segments[0]
795 ),
796 span.line,
797 span.col,
798 ))
799 })?;
800 }
801 _ => {
802 return Err(IonError::type_err(
803 format!(
804 "{}{}{}",
805 ion_str!("cannot access '"),
806 seg,
807 ion_str!("' on non-module value")
808 ),
809 span.line,
810 span.col,
811 )
812 .into())
813 }
814 }
815 }
816 Ok(current)
817 }
818
819 ExprKind::SomeExpr(e) => {
820 let val = self.eval_expr(e)?;
821 Ok(Value::Option(Some(Box::new(val))))
822 }
823 ExprKind::OkExpr(e) => {
824 let val = self.eval_expr(e)?;
825 Ok(Value::Result(Ok(Box::new(val))))
826 }
827 ExprKind::ErrExpr(e) => {
828 let val = self.eval_expr(e)?;
829 Ok(Value::Result(Err(Box::new(val))))
830 }
831
832 ExprKind::List(items) => {
833 let mut vals = Vec::new();
834 for entry in items {
835 match entry {
836 ListEntry::Elem(expr) => vals.push(self.eval_expr(expr)?),
837 ListEntry::Spread(expr) => match self.eval_expr(expr)? {
838 Value::List(sub) => vals.extend(sub),
839 other => {
840 return Err(IonError::type_err(
841 format!(
842 "{}{}",
843 ion_str!("spread requires a list, got "),
844 other.type_name()
845 ),
846 span.line,
847 span.col,
848 )
849 .into())
850 }
851 },
852 }
853 }
854 Ok(Value::List(vals))
855 }
856 ExprKind::Dict(entries) => {
857 let mut map = IndexMap::new();
858 for entry in entries {
859 match entry {
860 DictEntry::KeyValue(k, v) => {
861 let key = self.eval_expr(k)?;
862 let key_str = match key {
863 Value::Str(s) => s,
864 _ => {
865 return Err(IonError::type_err(
866 ion_str!("dict keys must be strings").to_string(),
867 span.line,
868 span.col,
869 )
870 .into())
871 }
872 };
873 let val = self.eval_expr(v)?;
874 map.insert(key_str, val);
875 }
876 DictEntry::Spread(expr) => {
877 let val = self.eval_expr(expr)?;
878 match val {
879 Value::Dict(other) => {
880 for (k, v) in other {
881 map.insert(k, v);
882 }
883 }
884 _ => {
885 return Err(IonError::type_err(
886 ion_str!("spread requires a dict").to_string(),
887 span.line,
888 span.col,
889 )
890 .into())
891 }
892 }
893 }
894 }
895 }
896 Ok(Value::Dict(map))
897 }
898 ExprKind::Tuple(items) => {
899 let mut vals = Vec::new();
900 for item in items {
901 vals.push(self.eval_expr(item)?);
902 }
903 Ok(Value::Tuple(vals))
904 }
905
906 ExprKind::ListComp {
907 expr,
908 pattern,
909 iter,
910 cond,
911 } => {
912 let iter_val = self.eval_expr(iter)?;
913 let items = self.value_to_iter(&iter_val, span)?;
914 let mut result = Vec::new();
915 for item in items {
916 self.env.push_scope();
917 self.bind_pattern(pattern, &item, false, span)?;
918 let include = if let Some(c) = cond {
919 let v = self.eval_expr(c)?;
920 v.is_truthy()
921 } else {
922 true
923 };
924 if include {
925 result.push(self.eval_expr(expr)?);
926 }
927 self.env.pop_scope();
928 }
929 Ok(Value::List(result))
930 }
931 ExprKind::DictComp {
932 key,
933 value,
934 pattern,
935 iter,
936 cond,
937 } => {
938 let iter_val = self.eval_expr(iter)?;
939 let items = self.value_to_iter(&iter_val, span)?;
940 let mut map = IndexMap::new();
941 for item in items {
942 self.env.push_scope();
943 self.bind_pattern(pattern, &item, false, span)?;
944 let include = if let Some(c) = cond {
945 let v = self.eval_expr(c)?;
946 v.is_truthy()
947 } else {
948 true
949 };
950 if include {
951 let k = self.eval_expr(key)?;
952 let k_str = match k {
953 Value::Str(s) => s,
954 _ => {
955 return Err(IonError::type_err(
956 ion_str!("dict comp keys must be strings").to_string(),
957 span.line,
958 span.col,
959 )
960 .into())
961 }
962 };
963 let v = self.eval_expr(value)?;
964 map.insert(k_str, v);
965 }
966 self.env.pop_scope();
967 }
968 Ok(Value::Dict(map))
969 }
970
971 ExprKind::BinOp { left, op, right } => {
972 if matches!(op, BinOp::And) {
974 let l = self.eval_expr(left)?;
975 if !l.is_truthy() {
976 return Ok(Value::Bool(false));
977 }
978 let r = self.eval_expr(right)?;
979 return Ok(Value::Bool(r.is_truthy()));
980 }
981 if matches!(op, BinOp::Or) {
982 let l = self.eval_expr(left)?;
983 if l.is_truthy() {
984 return Ok(Value::Bool(true));
985 }
986 let r = self.eval_expr(right)?;
987 return Ok(Value::Bool(r.is_truthy()));
988 }
989 let l = self.eval_expr(left)?;
990 let r = self.eval_expr(right)?;
991 self.eval_binop(*op, &l, &r, span)
992 }
993
994 ExprKind::UnaryOp { op, expr } => {
995 let val = self.eval_expr(expr)?;
996 match op {
997 UnaryOp::Neg => match val {
998 Value::Int(n) => Ok(Value::Int(-n)),
999 Value::Float(n) => Ok(Value::Float(-n)),
1000 _ => Err(IonError::type_err(
1001 format!("{}{}", ion_str!("cannot negate "), val.type_name()),
1002 span.line,
1003 span.col,
1004 )
1005 .into()),
1006 },
1007 UnaryOp::Not => Ok(Value::Bool(!val.is_truthy())),
1008 }
1009 }
1010
1011 ExprKind::Try(inner) => {
1012 let val = self.eval_expr(inner)?;
1013 match val {
1014 Value::Result(Ok(v)) => Ok(*v),
1015 Value::Result(Err(e)) => {
1016 Err(IonError::propagated_err(e.to_string(), span.line, span.col).into())
1017 }
1018 Value::Option(Some(v)) => Ok(*v),
1019 Value::Option(None) => {
1020 Err(IonError::propagated_none(span.line, span.col).into())
1021 }
1022 _ => Err(IonError::type_err(
1023 format!(
1024 "{}{}",
1025 ion_str!("? applied to non-Result/Option: "),
1026 val.type_name()
1027 ),
1028 span.line,
1029 span.col,
1030 )
1031 .into()),
1032 }
1033 }
1034
1035 ExprKind::PipeOp { left, right } => {
1036 let lval = self.eval_expr(left)?;
1037 match &right.kind {
1039 ExprKind::Call { func, args } => {
1040 let mut new_args = vec![CallArg {
1041 name: None,
1042 value: Expr {
1043 kind: ExprKind::Int(0),
1044 span, },
1046 }];
1047 new_args.extend(args.iter().cloned());
1048 let func_val = self.eval_expr(func)?;
1049 let mut arg_vals = vec![lval];
1050 for arg in args {
1051 arg_vals.push(self.eval_expr(&arg.value)?);
1052 }
1053 self.call_value(&func_val, &arg_vals, span)
1054 }
1055 ExprKind::Ident(_) => {
1056 let func_val = self.eval_expr(right)?;
1058 self.call_value(&func_val, &[lval], span)
1059 }
1060 _ => Err(IonError::runtime(
1061 ion_str!("right side of |> must be a function call").to_string(),
1062 span.line,
1063 span.col,
1064 )
1065 .into()),
1066 }
1067 }
1068
1069 ExprKind::FieldAccess { expr, field } => {
1070 let val = self.eval_expr(expr)?;
1071 self.field_access(&val, field, span)
1072 }
1073
1074 ExprKind::Index { expr, index } => {
1075 let val = self.eval_expr(expr)?;
1076 let idx = self.eval_expr(index)?;
1077 self.index_access(&val, &idx, span)
1078 }
1079
1080 ExprKind::Slice {
1081 expr,
1082 start,
1083 end,
1084 inclusive,
1085 } => {
1086 let val = self.eval_expr(expr)?;
1087 let s = match start {
1088 Some(e) => Some(self.eval_expr(e)?),
1089 None => None,
1090 };
1091 let e = match end {
1092 Some(e) => Some(self.eval_expr(e)?),
1093 None => None,
1094 };
1095 self.slice_access(&val, s.as_ref(), e.as_ref(), *inclusive, span)
1096 }
1097
1098 ExprKind::MethodCall { expr, method, args } => {
1099 let receiver = self.eval_expr(expr)?;
1100 let mut arg_vals = Vec::new();
1101 for arg in args {
1102 arg_vals.push(self.eval_expr(&arg.value)?);
1103 }
1104 self.method_call(&receiver, method, &arg_vals, span)
1105 }
1106
1107 ExprKind::Call { func, args } => {
1108 let func_val = self.eval_expr(func)?;
1109 let has_named = args.iter().any(|a| a.name.is_some());
1110 if has_named {
1111 let mut evaluated: Vec<(Option<String>, Value)> = Vec::new();
1112 for arg in args {
1113 evaluated.push((arg.name.clone(), self.eval_expr(&arg.value)?));
1114 }
1115 self.call_with_named(&func_val, evaluated, span)
1116 } else {
1117 let mut arg_vals = Vec::new();
1118 for arg in args {
1119 arg_vals.push(self.eval_expr(&arg.value)?);
1120 }
1121 self.call_value(&func_val, &arg_vals, span)
1122 }
1123 }
1124
1125 ExprKind::Lambda { params, body } => {
1126 let captures = self.env.capture();
1127 let fn_params: Vec<Param> = params
1128 .iter()
1129 .map(|p| Param {
1130 name: p.clone(),
1131 default: None,
1132 })
1133 .collect();
1134 let body_stmts = vec![Stmt {
1136 kind: StmtKind::ExprStmt {
1137 expr: (**body).clone(),
1138 has_semi: false,
1139 },
1140 span,
1141 }];
1142 Ok(Value::Fn(IonFn::new(
1143 ion_str!("<lambda>").to_string(),
1144 fn_params,
1145 body_stmts,
1146 captures,
1147 )))
1148 }
1149
1150 ExprKind::If {
1151 cond,
1152 then_body,
1153 else_body,
1154 } => {
1155 let c = self.eval_expr(cond)?;
1156 self.env.push_scope();
1157 let result = if c.is_truthy() {
1158 self.eval_stmts(then_body)
1159 } else if let Some(else_stmts) = else_body {
1160 self.eval_stmts(else_stmts)
1161 } else {
1162 Ok(Value::Unit)
1163 };
1164 self.env.pop_scope();
1165 result
1166 }
1167
1168 ExprKind::IfLet {
1169 pattern,
1170 expr,
1171 then_body,
1172 else_body,
1173 } => {
1174 let val = self.eval_expr(expr)?;
1175 if self.pattern_matches(pattern, &val) {
1176 self.env.push_scope();
1177 self.bind_pattern(pattern, &val, false, span)?;
1178 let result = self.eval_stmts(then_body);
1179 self.env.pop_scope();
1180 result
1181 } else if let Some(else_stmts) = else_body {
1182 self.env.push_scope();
1183 let result = self.eval_stmts(else_stmts);
1184 self.env.pop_scope();
1185 result
1186 } else {
1187 Ok(Value::Unit)
1188 }
1189 }
1190
1191 ExprKind::Match { expr, arms } => {
1192 let val = self.eval_expr(expr)?;
1193 for arm in arms {
1194 if self.pattern_matches(&arm.pattern, &val) {
1195 self.env.push_scope();
1196 self.bind_pattern(&arm.pattern, &val, false, span)?;
1197 if let Some(guard) = &arm.guard {
1198 let guard_val = self.eval_expr(guard)?;
1199 if !guard_val.is_truthy() {
1200 self.env.pop_scope();
1201 continue;
1202 }
1203 }
1204 let result = self.eval_expr(&arm.body);
1205 self.env.pop_scope();
1206 return result;
1207 }
1208 }
1209 Err(IonError::runtime(
1210 ion_str!("non-exhaustive match").to_string(),
1211 span.line,
1212 span.col,
1213 )
1214 .into())
1215 }
1216
1217 ExprKind::Block(stmts) => {
1218 self.env.push_scope();
1219 let result = self.eval_stmts(stmts);
1220 self.env.pop_scope();
1221 result
1222 }
1223
1224 ExprKind::LoopExpr(body) => {
1225 let no_label: Option<String> = None;
1226 let result = loop {
1227 self.env.push_scope();
1228 match self.eval_stmts(body) {
1229 Ok(_) => {}
1230 Err(SignalOrError::Signal(Signal::Break {
1231 label: sig_label,
1232 value,
1233 })) => {
1234 self.env.pop_scope();
1235 if signal_targets_loop(&sig_label, &no_label) {
1236 break value;
1237 }
1238 return Err(Signal::Break {
1239 label: sig_label,
1240 value,
1241 }
1242 .into());
1243 }
1244 Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
1245 self.env.pop_scope();
1246 if signal_targets_loop(&sig_label, &no_label) {
1247 continue;
1248 }
1249 return Err(Signal::Continue { label: sig_label }.into());
1250 }
1251 Err(e) => {
1252 self.env.pop_scope();
1253 return Err(e);
1254 }
1255 }
1256 self.env.pop_scope();
1257 };
1258 Ok(result)
1259 }
1260
1261 ExprKind::TryCatch { body, var, handler } => {
1262 self.env.push_scope();
1263 let result = self.eval_stmts(body);
1264 self.env.pop_scope();
1265 match result {
1266 Ok(v) => Ok(v),
1267 Err(SignalOrError::Signal(s)) => {
1268 Err(SignalOrError::Signal(s))
1270 }
1271 Err(SignalOrError::Error(e)) => {
1272 self.env.push_scope();
1274 self.env
1275 .define(var.clone(), Value::Str(e.message.clone()), false);
1276 let handler_result = self.eval_stmts(handler);
1277 self.env.pop_scope();
1278 handler_result
1279 }
1280 }
1281 }
1282
1283 ExprKind::Range {
1284 start,
1285 end,
1286 inclusive,
1287 } => {
1288 let s = self.eval_expr(start)?;
1289 let e = self.eval_expr(end)?;
1290 match (&s, &e) {
1291 (Value::Int(a), Value::Int(b)) => Ok(Value::Range {
1292 start: *a,
1293 end: *b,
1294 inclusive: *inclusive,
1295 }),
1296 _ => Err(IonError::type_err(
1297 ion_str!("range requires integer bounds").to_string(),
1298 span.line,
1299 span.col,
1300 )
1301 .into()),
1302 }
1303 }
1304
1305 ExprKind::StructConstruct {
1306 name,
1307 fields,
1308 spread,
1309 } => {
1310 let mut field_map = IndexMap::new();
1311 if let Some(spread_expr) = spread {
1312 let spread_val = self.eval_expr(spread_expr)?;
1313 match spread_val {
1314 Value::HostStruct { fields: sf, .. } => {
1315 for (k, v) in sf {
1316 field_map.insert(k, v);
1317 }
1318 }
1319 _ => {
1320 return Err(IonError::type_err(
1321 ion_str!("spread in struct constructor requires a struct")
1322 .to_string(),
1323 span.line,
1324 span.col,
1325 )
1326 .into())
1327 }
1328 }
1329 }
1330 for (fname, fexpr) in fields {
1331 let val = self.eval_expr(fexpr)?;
1332 field_map.insert(fname.clone(), val);
1333 }
1334 self.types
1335 .construct_struct(name, field_map)
1336 .map_err(|msg| IonError::runtime(msg, span.line, span.col).into())
1337 }
1338 ExprKind::EnumVariant { enum_name, variant } => self
1339 .types
1340 .construct_enum(enum_name, variant, vec![])
1341 .map_err(|msg| IonError::runtime(msg, span.line, span.col).into()),
1342 ExprKind::EnumVariantCall {
1343 enum_name,
1344 variant,
1345 args,
1346 } => {
1347 let mut vals = Vec::new();
1348 for arg in args {
1349 vals.push(self.eval_expr(arg)?);
1350 }
1351 self.types
1352 .construct_enum(enum_name, variant, vals)
1353 .map_err(|msg| IonError::runtime(msg, span.line, span.col).into())
1354 }
1355
1356 #[cfg(feature = "concurrency")]
1358 ExprKind::AsyncBlock(body) => self.eval_async_block(body, span),
1359 #[cfg(feature = "concurrency")]
1360 ExprKind::SpawnExpr(expr) => self.eval_spawn(expr, span),
1361 #[cfg(feature = "concurrency")]
1362 ExprKind::AwaitExpr(expr) => self.eval_await(expr, span),
1363 #[cfg(feature = "concurrency")]
1364 ExprKind::SelectExpr(branches) => self.eval_select(branches, span),
1365
1366 #[cfg(not(feature = "concurrency"))]
1367 ExprKind::AsyncBlock(_)
1368 | ExprKind::SpawnExpr(_)
1369 | ExprKind::AwaitExpr(_)
1370 | ExprKind::SelectExpr(_) => Err(IonError::runtime(
1371 ion_str!("concurrency features require the 'concurrency' cargo feature")
1372 .to_string(),
1373 span.line,
1374 span.col,
1375 )
1376 .into()),
1377 }
1378 }
1379
1380 fn eval_binop(&self, op: BinOp, l: &Value, r: &Value, span: Span) -> SignalResult {
1383 match op {
1384 BinOp::Add => match (l, r) {
1385 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a + b)),
1386 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
1387 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 + b)),
1388 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a + *b as f64)),
1389 (Value::Str(a), Value::Str(b)) => Ok(Value::Str(format!("{}{}", a, b))),
1390 (Value::Bytes(a), Value::Bytes(b)) => {
1391 let mut result = a.clone();
1392 result.extend(b);
1393 Ok(Value::Bytes(result))
1394 }
1395 _ => Err(self.type_mismatch_err(ion_str!("+"), l, r, span)),
1396 },
1397 BinOp::Sub => match (l, r) {
1398 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a - b)),
1399 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
1400 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 - b)),
1401 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a - *b as f64)),
1402 _ => Err(self.type_mismatch_err(ion_str!("-"), l, r, span)),
1403 },
1404 BinOp::Mul => match (l, r) {
1405 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a * b)),
1406 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
1407 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 * b)),
1408 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a * *b as f64)),
1409 (Value::Str(s), Value::Int(n)) | (Value::Int(n), Value::Str(s)) => {
1410 Ok(Value::Str(s.repeat(*n as usize)))
1411 }
1412 _ => Err(self.type_mismatch_err(ion_str!("*"), l, r, span)),
1413 },
1414 BinOp::Div => match (l, r) {
1415 (Value::Int(a), Value::Int(b)) => {
1416 if *b == 0 {
1417 Err(IonError::runtime(
1418 ion_str!("division by zero").to_string(),
1419 span.line,
1420 span.col,
1421 )
1422 .into())
1423 } else {
1424 Ok(Value::Int(a / b))
1425 }
1426 }
1427 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a / b)),
1428 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 / b)),
1429 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a / *b as f64)),
1430 _ => Err(self.type_mismatch_err(ion_str!("/"), l, r, span)),
1431 },
1432 BinOp::Mod => match (l, r) {
1433 (Value::Int(a), Value::Int(b)) => {
1434 if *b == 0 {
1435 Err(IonError::runtime(
1436 ion_str!("modulo by zero").to_string(),
1437 span.line,
1438 span.col,
1439 )
1440 .into())
1441 } else {
1442 Ok(Value::Int(a % b))
1443 }
1444 }
1445 _ => Err(self.type_mismatch_err(ion_str!("%"), l, r, span)),
1446 },
1447 BinOp::Eq => Ok(Value::Bool(l == r)),
1448 BinOp::Ne => Ok(Value::Bool(l != r)),
1449 BinOp::Lt => self.compare_values(l, r, span, |o| o == std::cmp::Ordering::Less),
1450 BinOp::Gt => self.compare_values(l, r, span, |o| o == std::cmp::Ordering::Greater),
1451 BinOp::Le => self.compare_values(l, r, span, |o| o != std::cmp::Ordering::Greater),
1452 BinOp::Ge => self.compare_values(l, r, span, |o| o != std::cmp::Ordering::Less),
1453 BinOp::And | BinOp::Or => unreachable!(), BinOp::BitAnd => match (l, r) {
1455 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a & b)),
1456 _ => Err(self.type_mismatch_err(ion_str!("&"), l, r, span)),
1457 },
1458 BinOp::BitOr => match (l, r) {
1459 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a | b)),
1460 _ => Err(self.type_mismatch_err(ion_str!("|"), l, r, span)),
1461 },
1462 BinOp::BitXor => match (l, r) {
1463 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a ^ b)),
1464 _ => Err(self.type_mismatch_err(ion_str!("^"), l, r, span)),
1465 },
1466 BinOp::Shl => match (l, r) {
1467 (Value::Int(a), Value::Int(b)) if (0..64).contains(b) => Ok(Value::Int(a << b)),
1468 (Value::Int(_), Value::Int(b)) => Err(IonError::runtime(
1469 format!("shift count {} is out of range 0..64", b),
1470 span.line,
1471 span.col,
1472 )
1473 .into()),
1474 _ => Err(self.type_mismatch_err(ion_str!("<<"), l, r, span)),
1475 },
1476 BinOp::Shr => match (l, r) {
1477 (Value::Int(a), Value::Int(b)) if (0..64).contains(b) => Ok(Value::Int(a >> b)),
1478 (Value::Int(_), Value::Int(b)) => Err(IonError::runtime(
1479 format!("shift count {} is out of range 0..64", b),
1480 span.line,
1481 span.col,
1482 )
1483 .into()),
1484 _ => Err(self.type_mismatch_err(ion_str!(">>"), l, r, span)),
1485 },
1486 }
1487 }
1488
1489 fn compare_values(
1490 &self,
1491 l: &Value,
1492 r: &Value,
1493 span: Span,
1494 f: impl Fn(std::cmp::Ordering) -> bool,
1495 ) -> SignalResult {
1496 let ord = match (l, r) {
1497 (Value::Int(a), Value::Int(b)) => a.cmp(b),
1498 (Value::Float(a), Value::Float(b)) => {
1499 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
1500 }
1501 (Value::Int(a), Value::Float(b)) => (*a as f64)
1502 .partial_cmp(b)
1503 .unwrap_or(std::cmp::Ordering::Equal),
1504 (Value::Float(a), Value::Int(b)) => a
1505 .partial_cmp(&(*b as f64))
1506 .unwrap_or(std::cmp::Ordering::Equal),
1507 (Value::Str(a), Value::Str(b)) => a.cmp(b),
1508 _ => return Err(self.type_mismatch_err(ion_str!("compare"), l, r, span)),
1509 };
1510 Ok(Value::Bool(f(ord)))
1511 }
1512
1513 fn type_mismatch_err(
1514 &self,
1515 op: impl std::fmt::Display,
1516 l: &Value,
1517 r: &Value,
1518 span: Span,
1519 ) -> SignalOrError {
1520 IonError::type_err(
1521 format!(
1522 "{}{}{}{}{}{}",
1523 ion_str!("cannot apply '"),
1524 op,
1525 ion_str!("' to "),
1526 l.type_name(),
1527 ion_str!(" and "),
1528 r.type_name(),
1529 ),
1530 span.line,
1531 span.col,
1532 )
1533 .into()
1534 }
1535
1536 fn apply_compound_op(
1537 &self,
1538 op: AssignOp,
1539 lhs: &Value,
1540 rhs: &Value,
1541 span: Span,
1542 ) -> SignalResult {
1543 match op {
1544 AssignOp::PlusEq => self.eval_binop(BinOp::Add, lhs, rhs, span),
1545 AssignOp::MinusEq => self.eval_binop(BinOp::Sub, lhs, rhs, span),
1546 AssignOp::StarEq => self.eval_binop(BinOp::Mul, lhs, rhs, span),
1547 AssignOp::SlashEq => self.eval_binop(BinOp::Div, lhs, rhs, span),
1548 AssignOp::Eq => unreachable!(),
1549 }
1550 }
1551
1552 fn field_access(&self, val: &Value, field: &str, span: Span) -> SignalResult {
1553 match val {
1554 Value::Dict(map) => Ok(match map.get(field) {
1555 Some(v) => v.clone(),
1556 None => Value::Option(None),
1557 }),
1558 Value::HostStruct { fields, .. } => Ok(match fields.get(field) {
1559 Some(v) => v.clone(),
1560 None => {
1561 return Err(IonError::type_err(
1562 format!(
1563 "{}{}{}",
1564 ion_str!("no field '"),
1565 field,
1566 ion_str!("' on struct")
1567 ),
1568 span.line,
1569 span.col,
1570 )
1571 .into())
1572 }
1573 }),
1574 _ => Err(IonError::type_err(
1575 format!("{}{}", ion_str!("cannot access field on "), val.type_name()),
1576 span.line,
1577 span.col,
1578 )
1579 .into()),
1580 }
1581 }
1582
1583 fn index_access(&self, val: &Value, idx: &Value, span: Span) -> SignalResult {
1584 match (val, idx) {
1585 (Value::List(items), Value::Int(i)) => {
1586 let index = if *i < 0 { items.len() as i64 + i } else { *i } as usize;
1587 items.get(index).cloned().ok_or_else(|| {
1588 IonError::runtime(
1589 ion_str!("list index out of bounds").to_string(),
1590 span.line,
1591 span.col,
1592 )
1593 .into()
1594 })
1595 }
1596 (Value::Dict(map), Value::Str(key)) => Ok(match map.get(key.as_str()) {
1597 Some(v) => v.clone(),
1598 None => Value::Option(None),
1599 }),
1600 (Value::Bytes(bytes), Value::Int(i)) => {
1601 let index = if *i < 0 { bytes.len() as i64 + i } else { *i } as usize;
1602 bytes
1603 .get(index)
1604 .map(|&b| Value::Int(b as i64))
1605 .ok_or_else(|| {
1606 IonError::runtime(
1607 ion_str!("bytes index out of bounds").to_string(),
1608 span.line,
1609 span.col,
1610 )
1611 .into()
1612 })
1613 }
1614 (Value::Str(s), Value::Int(i)) => {
1615 let index = if *i < 0 {
1616 s.chars().count() as i64 + i
1617 } else {
1618 *i
1619 } as usize;
1620 s.chars()
1621 .nth(index)
1622 .map(|c| Value::Str(c.to_string()))
1623 .ok_or_else(|| {
1624 IonError::runtime(
1625 ion_str!("string index out of bounds").to_string(),
1626 span.line,
1627 span.col,
1628 )
1629 .into()
1630 })
1631 }
1632 (Value::Tuple(items), Value::Int(i)) => {
1633 let index = *i as usize;
1634 items.get(index).cloned().ok_or_else(|| {
1635 IonError::runtime(
1636 ion_str!("tuple index out of bounds").to_string(),
1637 span.line,
1638 span.col,
1639 )
1640 .into()
1641 })
1642 }
1643 _ => Err(IonError::type_err(
1644 format!(
1645 "{}{}{}{}",
1646 ion_str!("cannot index "),
1647 val.type_name(),
1648 ion_str!(" with "),
1649 idx.type_name(),
1650 ),
1651 span.line,
1652 span.col,
1653 )
1654 .into()),
1655 }
1656 }
1657
1658 fn slice_access(
1659 &self,
1660 val: &Value,
1661 start: Option<&Value>,
1662 end: Option<&Value>,
1663 inclusive: bool,
1664 span: Span,
1665 ) -> SignalResult {
1666 let get_idx = |v: Option<&Value>, default: i64| -> Result<i64, SignalOrError> {
1667 match v {
1668 Some(Value::Int(n)) => Ok(*n),
1669 None => Ok(default),
1670 Some(other) => Err(IonError::type_err(
1671 format!(
1672 "{}{}",
1673 ion_str!("slice index must be int, got "),
1674 other.type_name()
1675 ),
1676 span.line,
1677 span.col,
1678 )
1679 .into()),
1680 }
1681 };
1682
1683 match val {
1684 Value::List(items) => {
1685 let len = items.len() as i64;
1686 let s = get_idx(start, 0)?;
1687 let e = get_idx(end, len)?;
1688 let s = s.max(0).min(len) as usize;
1689 let e = if inclusive {
1690 (e + 1).max(0).min(len) as usize
1691 } else {
1692 e.max(0).min(len) as usize
1693 };
1694 Ok(Value::List(items[s..e].to_vec()))
1695 }
1696 Value::Str(string) => {
1697 let chars: Vec<char> = string.chars().collect();
1698 let len = chars.len() as i64;
1699 let s = get_idx(start, 0)?;
1700 let e = get_idx(end, len)?;
1701 let s = s.max(0).min(len) as usize;
1702 let e = if inclusive {
1703 (e + 1).max(0).min(len) as usize
1704 } else {
1705 e.max(0).min(len) as usize
1706 };
1707 Ok(Value::Str(chars[s..e].iter().collect()))
1708 }
1709 Value::Bytes(bytes) => {
1710 let len = bytes.len() as i64;
1711 let s = get_idx(start, 0)?;
1712 let e = get_idx(end, len)?;
1713 let s = s.max(0).min(len) as usize;
1714 let e = if inclusive {
1715 (e + 1).max(0).min(len) as usize
1716 } else {
1717 e.max(0).min(len) as usize
1718 };
1719 Ok(Value::Bytes(bytes[s..e].to_vec()))
1720 }
1721 _ => Err(IonError::type_err(
1722 format!("{}{}", ion_str!("cannot slice "), val.type_name()),
1723 span.line,
1724 span.col,
1725 )
1726 .into()),
1727 }
1728 }
1729
1730 fn method_call(
1731 &mut self,
1732 receiver: &Value,
1733 method: &str,
1734 args: &[Value],
1735 span: Span,
1736 ) -> SignalResult {
1737 if method == "to_string" {
1739 return Ok(Value::Str(format!("{}", receiver)));
1740 }
1741 match receiver {
1742 Value::List(items) => self.list_method(items, method, args, span),
1743 Value::Tuple(items) => self.tuple_method(items, method, args, span),
1744 Value::Str(s) => self.string_method(s, method, args, span),
1745 Value::Bytes(b) => self.bytes_method(b, method, args, span),
1746 Value::Dict(map) => match method {
1747 "map" => {
1748 let func = &args[0];
1749 let mut result = indexmap::IndexMap::new();
1750 for (k, v) in map {
1751 let mapped =
1752 self.call_value(func, &[Value::Str(k.clone()), v.clone()], span)?;
1753 result.insert(k.clone(), mapped);
1754 }
1755 Ok(Value::Dict(result))
1756 }
1757 "filter" => {
1758 let func = &args[0];
1759 let mut result = indexmap::IndexMap::new();
1760 for (k, v) in map {
1761 let keep =
1762 self.call_value(func, &[Value::Str(k.clone()), v.clone()], span)?;
1763 if keep.is_truthy() {
1764 result.insert(k.clone(), v.clone());
1765 }
1766 }
1767 Ok(Value::Dict(result))
1768 }
1769 _ => self.dict_method(map, method, args, span),
1770 },
1771 Value::Set(items) => self.set_method(items, method, args, span),
1772 Value::Option(opt) => self.option_method(opt.clone(), method, args, span),
1773 Value::Result(res) => self.result_method(res.clone(), method, args, span),
1774 Value::Range {
1775 start,
1776 end,
1777 inclusive,
1778 } => match method {
1779 "len" => Ok(Value::Int(Value::range_len(*start, *end, *inclusive))),
1780 "contains" => {
1781 let val = args[0]
1782 .as_int()
1783 .ok_or_else(|| {
1784 IonError::type_err(
1785 ion_str!("range.contains requires int"),
1786 span.line,
1787 span.col,
1788 )
1789 })
1790 .map_err(SignalOrError::from)?;
1791 let in_range = if *inclusive {
1792 val >= *start && val <= *end
1793 } else {
1794 val >= *start && val < *end
1795 };
1796 Ok(Value::Bool(in_range))
1797 }
1798 "to_list" => Ok(Value::List(Value::range_to_list(*start, *end, *inclusive))),
1799 _ => {
1801 let items = Value::range_to_list(*start, *end, *inclusive);
1802 self.list_method(&items, method, args, span)
1803 }
1804 },
1805 Value::Cell(cell) => self.cell_method(cell, method, args, span),
1806 #[cfg(feature = "concurrency")]
1807 Value::Task(handle) => self.task_method(handle, method, args, span),
1808 #[cfg(feature = "concurrency")]
1809 Value::Channel(ch) => self.channel_method(ch, method, args, span),
1810 _ => Err(IonError::type_err(
1811 format!(
1812 "{}{}{}{}",
1813 ion_str!("no method '"),
1814 method,
1815 ion_str!("' on "),
1816 receiver.type_name(),
1817 ),
1818 span.line,
1819 span.col,
1820 )
1821 .into()),
1822 }
1823 }
1824
1825 fn list_method(
1826 &mut self,
1827 items: &[Value],
1828 method: &str,
1829 args: &[Value],
1830 span: Span,
1831 ) -> SignalResult {
1832 match method {
1833 "len" => Ok(Value::Int(items.len() as i64)),
1834 "push" => {
1835 let mut new_list = items.to_vec();
1836 new_list.push(args[0].clone());
1837 Ok(Value::List(new_list))
1838 }
1839 "pop" => {
1840 if items.is_empty() {
1841 Ok(Value::Tuple(vec![Value::List(vec![]), Value::Option(None)]))
1842 } else {
1843 let mut new_list = items.to_vec();
1844 let popped = new_list.pop().unwrap();
1845 Ok(Value::Tuple(vec![
1846 Value::List(new_list),
1847 Value::Option(Some(Box::new(popped))),
1848 ]))
1849 }
1850 }
1851 "map" => {
1852 let func = &args[0];
1853 let mut result = Vec::new();
1854 for item in items {
1855 result.push(self.call_value(func, std::slice::from_ref(item), span)?);
1856 }
1857 Ok(Value::List(result))
1858 }
1859 "filter" => {
1860 let func = &args[0];
1861 let mut result = Vec::new();
1862 for item in items {
1863 let keep = self.call_value(func, std::slice::from_ref(item), span)?;
1864 if keep.is_truthy() {
1865 result.push(item.clone());
1866 }
1867 }
1868 Ok(Value::List(result))
1869 }
1870 "fold" => {
1871 let mut acc = args[0].clone();
1872 let func = &args[1];
1873 for item in items {
1874 acc = self.call_value(func, &[acc, item.clone()], span)?;
1875 }
1876 Ok(acc)
1877 }
1878 "flat_map" => {
1879 let func = &args[0];
1880 let mut result = Vec::new();
1881 for item in items {
1882 let mapped = self.call_value(func, std::slice::from_ref(item), span)?;
1883 match mapped {
1884 Value::List(sub) => result.extend(sub),
1885 other => result.push(other),
1886 }
1887 }
1888 Ok(Value::List(result))
1889 }
1890 "any" => {
1891 let func = &args[0];
1892 for item in items {
1893 let v = self.call_value(func, std::slice::from_ref(item), span)?;
1894 if v.is_truthy() {
1895 return Ok(Value::Bool(true));
1896 }
1897 }
1898 Ok(Value::Bool(false))
1899 }
1900 "all" => {
1901 let func = &args[0];
1902 for item in items {
1903 let v = self.call_value(func, std::slice::from_ref(item), span)?;
1904 if !v.is_truthy() {
1905 return Ok(Value::Bool(false));
1906 }
1907 }
1908 Ok(Value::Bool(true))
1909 }
1910 "first" => Ok(match items.first() {
1911 Some(v) => Value::Option(Some(Box::new(v.clone()))),
1912 None => Value::Option(None),
1913 }),
1914 "last" => Ok(match items.last() {
1915 Some(v) => Value::Option(Some(Box::new(v.clone()))),
1916 None => Value::Option(None),
1917 }),
1918 "reverse" => {
1919 let mut rev = items.to_vec();
1920 rev.reverse();
1921 Ok(Value::List(rev))
1922 }
1923 "sort" => {
1924 if !items.is_empty() {
1925 let first_type = std::mem::discriminant(&items[0]);
1926 for item in items.iter().skip(1) {
1927 if std::mem::discriminant(item) != first_type {
1928 return Err(IonError::type_err(
1929 ion_str!("sort() requires all elements to be the same type")
1930 .to_string(),
1931 span.line,
1932 span.col,
1933 )
1934 .into());
1935 }
1936 }
1937 }
1938 let mut sorted = items.to_vec();
1939 sorted.sort_by(|a, b| match (a, b) {
1940 (Value::Int(x), Value::Int(y)) => x.cmp(y),
1941 (Value::Float(x), Value::Float(y)) => {
1942 x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal)
1943 }
1944 (Value::Str(x), Value::Str(y)) => x.cmp(y),
1945 _ => std::cmp::Ordering::Equal,
1946 });
1947 Ok(Value::List(sorted))
1948 }
1949 "sort_by" => {
1950 let func = &args[0];
1951 let mut sorted = items.to_vec();
1952 let mut err: Option<SignalOrError> = None;
1953 let func_clone = func.clone();
1954 sorted.sort_by(|a, b| {
1955 if err.is_some() {
1956 return std::cmp::Ordering::Equal;
1957 }
1958 match self.call_value(&func_clone, &[a.clone(), b.clone()], span) {
1959 Ok(Value::Int(n)) => {
1960 if n < 0 {
1961 std::cmp::Ordering::Less
1962 } else if n > 0 {
1963 std::cmp::Ordering::Greater
1964 } else {
1965 std::cmp::Ordering::Equal
1966 }
1967 }
1968 Ok(_) => {
1969 err = Some(
1970 IonError::type_err(
1971 ion_str!("sort_by comparator must return an int").to_string(),
1972 span.line,
1973 span.col,
1974 )
1975 .into(),
1976 );
1977 std::cmp::Ordering::Equal
1978 }
1979 Err(e) => {
1980 err = Some(e);
1981 std::cmp::Ordering::Equal
1982 }
1983 }
1984 });
1985 if let Some(e) = err {
1986 return Err(e);
1987 }
1988 Ok(Value::List(sorted))
1989 }
1990 "flatten" => {
1991 let mut result = Vec::new();
1992 for item in items {
1993 if let Value::List(inner) = item {
1994 result.extend(inner.iter().cloned());
1995 } else {
1996 result.push(item.clone());
1997 }
1998 }
1999 Ok(Value::List(result))
2000 }
2001 "zip" => {
2002 if let Value::List(other) = &args[0] {
2003 let result: Vec<Value> = items
2004 .iter()
2005 .zip(other.iter())
2006 .map(|(a, b)| Value::Tuple(vec![a.clone(), b.clone()]))
2007 .collect();
2008 Ok(Value::List(result))
2009 } else {
2010 Err(IonError::type_err(
2011 ion_str!("zip requires a list argument").to_string(),
2012 span.line,
2013 span.col,
2014 )
2015 .into())
2016 }
2017 }
2018 "contains" => {
2019 let target = &args[0];
2020 Ok(Value::Bool(items.iter().any(|v| v == target)))
2021 }
2022 "join" => {
2023 let sep = if args.is_empty() {
2024 String::new()
2025 } else {
2026 args[0]
2027 .as_str()
2028 .ok_or_else(|| {
2029 IonError::type_err(
2030 ion_str!("join separator must be a string").to_string(),
2031 span.line,
2032 span.col,
2033 )
2034 })?
2035 .to_string()
2036 };
2037 let parts: Vec<String> = items.iter().map(|v| v.to_string()).collect();
2038 Ok(Value::Str(parts.join(&sep)))
2039 }
2040 "enumerate" => Ok(Value::List(
2041 items
2042 .iter()
2043 .enumerate()
2044 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v.clone()]))
2045 .collect(),
2046 )),
2047 "is_empty" => Ok(Value::Bool(items.is_empty())),
2048 "index" => {
2049 let target = &args[0];
2050 Ok(match items.iter().position(|v| v == target) {
2051 Some(i) => Value::Option(Some(Box::new(Value::Int(i as i64)))),
2052 None => Value::Option(None),
2053 })
2054 }
2055 "count" => {
2056 let target = &args[0];
2057 Ok(Value::Int(
2058 items.iter().filter(|v| *v == target).count() as i64
2059 ))
2060 }
2061 "slice" => {
2062 let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2063 let end = args
2064 .get(1)
2065 .and_then(|a| a.as_int())
2066 .map(|n| n as usize)
2067 .unwrap_or(items.len());
2068 let start = start.min(items.len());
2069 let end = end.min(items.len());
2070 Ok(Value::List(items[start..end].to_vec()))
2071 }
2072 "dedup" => {
2073 let mut result: Vec<Value> = Vec::new();
2074 for item in items {
2075 if result.last() != Some(item) {
2076 result.push(item.clone());
2077 }
2078 }
2079 Ok(Value::List(result))
2080 }
2081 "unique" => {
2082 let mut seen = Vec::new();
2083 let mut result = Vec::new();
2084 for item in items {
2085 if !seen.contains(item) {
2086 seen.push(item.clone());
2087 result.push(item.clone());
2088 }
2089 }
2090 Ok(Value::List(result))
2091 }
2092 "min" => {
2093 if items.is_empty() {
2094 return Ok(Value::Option(None));
2095 }
2096 let mut min = &items[0];
2097 for item in items.iter().skip(1) {
2098 match (min, item) {
2099 (Value::Int(a), Value::Int(b)) => {
2100 if b < a {
2101 min = item;
2102 }
2103 }
2104 (Value::Float(a), Value::Float(b)) => {
2105 if b < a {
2106 min = item;
2107 }
2108 }
2109 (Value::Str(a), Value::Str(b)) => {
2110 if b < a {
2111 min = item;
2112 }
2113 }
2114 _ => {
2115 return Err(IonError::type_err(
2116 ion_str!("min() requires homogeneous comparable elements")
2117 .to_string(),
2118 span.line,
2119 span.col,
2120 )
2121 .into())
2122 }
2123 }
2124 }
2125 Ok(Value::Option(Some(Box::new(min.clone()))))
2126 }
2127 "max" => {
2128 if items.is_empty() {
2129 return Ok(Value::Option(None));
2130 }
2131 let mut max = &items[0];
2132 for item in items.iter().skip(1) {
2133 match (max, item) {
2134 (Value::Int(a), Value::Int(b)) => {
2135 if b > a {
2136 max = item;
2137 }
2138 }
2139 (Value::Float(a), Value::Float(b)) => {
2140 if b > a {
2141 max = item;
2142 }
2143 }
2144 (Value::Str(a), Value::Str(b)) => {
2145 if b > a {
2146 max = item;
2147 }
2148 }
2149 _ => {
2150 return Err(IonError::type_err(
2151 ion_str!("max() requires homogeneous comparable elements")
2152 .to_string(),
2153 span.line,
2154 span.col,
2155 )
2156 .into())
2157 }
2158 }
2159 }
2160 Ok(Value::Option(Some(Box::new(max.clone()))))
2161 }
2162 "sum" => {
2163 let mut int_sum: i64 = 0;
2164 let mut float_sum: f64 = 0.0;
2165 let mut has_float = false;
2166 for item in items {
2167 match item {
2168 Value::Int(n) => int_sum += n,
2169 Value::Float(f) => {
2170 has_float = true;
2171 float_sum += f;
2172 }
2173 _ => {
2174 return Err(IonError::type_err(
2175 ion_str!("sum() requires numeric elements").to_string(),
2176 span.line,
2177 span.col,
2178 )
2179 .into())
2180 }
2181 }
2182 }
2183 if has_float {
2184 Ok(Value::Float(float_sum + int_sum as f64))
2185 } else {
2186 Ok(Value::Int(int_sum))
2187 }
2188 }
2189 "window" => {
2190 let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2191 IonError::type_err(
2192 ion_str!("window requires int argument").to_string(),
2193 span.line,
2194 span.col,
2195 )
2196 })? as usize;
2197 if n == 0 {
2198 return Err(IonError::runtime(
2199 ion_str!("window size must be > 0").to_string(),
2200 span.line,
2201 span.col,
2202 )
2203 .into());
2204 }
2205 let result: Vec<Value> =
2206 items.windows(n).map(|w| Value::List(w.to_vec())).collect();
2207 Ok(Value::List(result))
2208 }
2209 "chunk" => {
2210 let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2211 IonError::type_err(
2212 ion_str!("chunk requires int argument").to_string(),
2213 span.line,
2214 span.col,
2215 )
2216 })? as usize;
2217 if n == 0 {
2218 return Err(IonError::type_err(
2219 ion_str!("chunk size must be > 0").to_string(),
2220 span.line,
2221 span.col,
2222 )
2223 .into());
2224 }
2225 let result: Vec<Value> = items.chunks(n).map(|c| Value::List(c.to_vec())).collect();
2226 Ok(Value::List(result))
2227 }
2228 "reduce" => {
2229 if items.is_empty() {
2230 return Err(IonError::type_err(
2231 ion_str!("reduce on empty list").to_string(),
2232 span.line,
2233 span.col,
2234 )
2235 .into());
2236 }
2237 let func = &args[0];
2238 let mut acc = items[0].clone();
2239 for item in items.iter().skip(1) {
2240 acc = self.call_value(func, &[acc, item.clone()], span)?;
2241 }
2242 Ok(acc)
2243 }
2244 _ => Err(IonError::type_err(
2245 format!(
2246 "{}{}{}",
2247 ion_str!("no method '"),
2248 method,
2249 ion_str!("' on list")
2250 ),
2251 span.line,
2252 span.col,
2253 )
2254 .into()),
2255 }
2256 }
2257
2258 fn set_method(
2259 &self,
2260 items: &[Value],
2261 method: &str,
2262 args: &[Value],
2263 span: Span,
2264 ) -> SignalResult {
2265 match method {
2266 "len" => Ok(Value::Int(items.len() as i64)),
2267 "contains" => {
2268 let target = &args[0];
2269 Ok(Value::Bool(items.iter().any(|v| v == target)))
2270 }
2271 "is_empty" => Ok(Value::Bool(items.is_empty())),
2272 "add" => {
2273 let val = &args[0];
2274 let mut new = items.to_vec();
2275 if !new.iter().any(|v| v == val) {
2276 new.push(val.clone());
2277 }
2278 Ok(Value::Set(new))
2279 }
2280 "remove" => {
2281 let val = &args[0];
2282 let new: Vec<Value> = items.iter().filter(|v| *v != val).cloned().collect();
2283 Ok(Value::Set(new))
2284 }
2285 "union" => {
2286 if let Value::Set(other) = &args[0] {
2287 let mut new = items.to_vec();
2288 for v in other {
2289 if !new.iter().any(|x| x == v) {
2290 new.push(v.clone());
2291 }
2292 }
2293 Ok(Value::Set(new))
2294 } else {
2295 Err(IonError::type_err(
2296 ion_str!("union requires a set argument").to_string(),
2297 span.line,
2298 span.col,
2299 )
2300 .into())
2301 }
2302 }
2303 "intersection" => {
2304 if let Value::Set(other) = &args[0] {
2305 let new: Vec<Value> = items
2306 .iter()
2307 .filter(|v| other.iter().any(|x| x == *v))
2308 .cloned()
2309 .collect();
2310 Ok(Value::Set(new))
2311 } else {
2312 Err(IonError::type_err(
2313 ion_str!("intersection requires a set argument").to_string(),
2314 span.line,
2315 span.col,
2316 )
2317 .into())
2318 }
2319 }
2320 "difference" => {
2321 if let Value::Set(other) = &args[0] {
2322 let new: Vec<Value> = items
2323 .iter()
2324 .filter(|v| !other.iter().any(|x| x == *v))
2325 .cloned()
2326 .collect();
2327 Ok(Value::Set(new))
2328 } else {
2329 Err(IonError::type_err(
2330 ion_str!("difference requires a set argument").to_string(),
2331 span.line,
2332 span.col,
2333 )
2334 .into())
2335 }
2336 }
2337 "to_list" => Ok(Value::List(items.to_vec())),
2338 _ => Err(IonError::type_err(
2339 format!(
2340 "{}{}{}",
2341 ion_str!("no method '"),
2342 method,
2343 ion_str!("' on set")
2344 ),
2345 span.line,
2346 span.col,
2347 )
2348 .into()),
2349 }
2350 }
2351
2352 fn cell_method(
2353 &mut self,
2354 cell: &std::sync::Arc<std::sync::Mutex<Value>>,
2355 method: &str,
2356 args: &[Value],
2357 span: Span,
2358 ) -> SignalResult {
2359 match method {
2360 "get" => {
2361 let inner = cell.lock().unwrap();
2362 Ok(inner.clone())
2363 }
2364 "set" => {
2365 if args.is_empty() {
2366 return Err(IonError::runtime(
2367 ion_str!("cell.set() requires 1 argument").to_string(),
2368 span.line,
2369 span.col,
2370 )
2371 .into());
2372 }
2373 let mut inner = cell.lock().unwrap();
2374 *inner = args[0].clone();
2375 Ok(Value::Unit)
2376 }
2377 "update" => {
2378 if args.is_empty() {
2379 return Err(IonError::runtime(
2380 ion_str!("cell.update() requires a function argument").to_string(),
2381 span.line,
2382 span.col,
2383 )
2384 .into());
2385 }
2386 let current = { cell.lock().unwrap().clone() };
2387 let new_val = self.call_value(&args[0], &[current], span)?;
2388 let mut inner = cell.lock().unwrap();
2389 *inner = new_val.clone();
2390 Ok(new_val)
2391 }
2392 _ => Err(IonError::type_err(
2393 format!(
2394 "{}{}{}",
2395 ion_str!("no method '"),
2396 method,
2397 ion_str!("' on cell"),
2398 ),
2399 span.line,
2400 span.col,
2401 )
2402 .into()),
2403 }
2404 }
2405
2406 fn tuple_method(
2407 &self,
2408 items: &[Value],
2409 method: &str,
2410 args: &[Value],
2411 span: Span,
2412 ) -> SignalResult {
2413 match method {
2414 "len" => Ok(Value::Int(items.len() as i64)),
2415 "contains" => {
2416 let target = &args[0];
2417 Ok(Value::Bool(items.iter().any(|v| v == target)))
2418 }
2419 "to_list" => Ok(Value::List(items.to_vec())),
2420 _ => Err(IonError::type_err(
2421 format!(
2422 "{}{}{}",
2423 ion_str!("no method '"),
2424 method,
2425 ion_str!("' on tuple")
2426 ),
2427 span.line,
2428 span.col,
2429 )
2430 .into()),
2431 }
2432 }
2433
2434 fn string_method(&self, s: &str, method: &str, args: &[Value], span: Span) -> SignalResult {
2435 match method {
2436 "len" => Ok(Value::Int(s.len() as i64)),
2437 "contains" => match &args[0] {
2438 Value::Str(sub) => Ok(Value::Bool(s.contains(sub.as_str()))),
2439 Value::Int(code) => {
2440 let ch = char::from_u32(*code as u32).ok_or_else(|| {
2441 IonError::type_err(
2442 ion_str!("invalid char code").to_string(),
2443 span.line,
2444 span.col,
2445 )
2446 })?;
2447 Ok(Value::Bool(s.contains(ch)))
2448 }
2449 _ => Err(IonError::type_err(
2450 ion_str!("contains requires string or int argument").to_string(),
2451 span.line,
2452 span.col,
2453 )
2454 .into()),
2455 },
2456 "starts_with" => {
2457 let sub = args[0].as_str().ok_or_else(|| {
2458 IonError::type_err(
2459 ion_str!("starts_with requires string argument").to_string(),
2460 span.line,
2461 span.col,
2462 )
2463 })?;
2464 Ok(Value::Bool(s.starts_with(sub)))
2465 }
2466 "ends_with" => {
2467 let sub = args[0].as_str().ok_or_else(|| {
2468 IonError::type_err(
2469 ion_str!("ends_with requires string argument").to_string(),
2470 span.line,
2471 span.col,
2472 )
2473 })?;
2474 Ok(Value::Bool(s.ends_with(sub)))
2475 }
2476 "trim" => Ok(Value::Str(s.trim().to_string())),
2477 "to_upper" => Ok(Value::Str(s.to_uppercase())),
2478 "to_lower" => Ok(Value::Str(s.to_lowercase())),
2479 "split" => {
2480 let delim = args[0].as_str().ok_or_else(|| {
2481 IonError::type_err(
2482 ion_str!("split requires string argument").to_string(),
2483 span.line,
2484 span.col,
2485 )
2486 })?;
2487 let parts: Vec<Value> = s.split(delim).map(|p| Value::Str(p.to_string())).collect();
2488 Ok(Value::List(parts))
2489 }
2490 "replace" => {
2491 let from = args[0].as_str().ok_or_else(|| {
2492 IonError::type_err(
2493 ion_str!("replace requires string arguments").to_string(),
2494 span.line,
2495 span.col,
2496 )
2497 })?;
2498 let to = args[1].as_str().ok_or_else(|| {
2499 IonError::type_err(
2500 ion_str!("replace requires string arguments").to_string(),
2501 span.line,
2502 span.col,
2503 )
2504 })?;
2505 Ok(Value::Str(s.replace(from, to)))
2506 }
2507 "chars" => {
2508 let chars: Vec<Value> = s.chars().map(|c| Value::Str(c.to_string())).collect();
2509 Ok(Value::List(chars))
2510 }
2511 "char_len" => Ok(Value::Int(s.chars().count() as i64)),
2512 "is_empty" => Ok(Value::Bool(s.is_empty())),
2513 "trim_start" => Ok(Value::Str(s.trim_start().to_string())),
2514 "trim_end" => Ok(Value::Str(s.trim_end().to_string())),
2515 "repeat" => {
2516 let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2517 IonError::type_err(
2518 ion_str!("repeat requires int argument").to_string(),
2519 span.line,
2520 span.col,
2521 )
2522 })?;
2523 Ok(Value::Str(s.repeat(n as usize)))
2524 }
2525 "find" => {
2526 let sub = args[0].as_str().ok_or_else(|| {
2527 IonError::type_err(
2528 ion_str!("find requires string argument").to_string(),
2529 span.line,
2530 span.col,
2531 )
2532 })?;
2533 Ok(match s.find(sub) {
2534 Some(byte_idx) => {
2535 let char_idx = s[..byte_idx].chars().count();
2536 Value::Option(Some(Box::new(Value::Int(char_idx as i64))))
2537 }
2538 None => Value::Option(None),
2539 })
2540 }
2541 "to_int" => Ok(match s.trim().parse::<i64>() {
2542 std::result::Result::Ok(n) => Value::Result(Ok(Box::new(Value::Int(n)))),
2543 std::result::Result::Err(e) => {
2544 Value::Result(Err(Box::new(Value::Str(e.to_string()))))
2545 }
2546 }),
2547 "to_float" => Ok(match s.trim().parse::<f64>() {
2548 std::result::Result::Ok(f) => Value::Result(Ok(Box::new(Value::Float(f)))),
2549 std::result::Result::Err(e) => {
2550 Value::Result(Err(Box::new(Value::Str(e.to_string()))))
2551 }
2552 }),
2553 "bytes" => {
2554 let bytes: Vec<Value> = s.bytes().map(|b| Value::Int(b as i64)).collect();
2555 Ok(Value::List(bytes))
2556 }
2557 "strip_prefix" => {
2558 let pre = args[0].as_str().ok_or_else(|| {
2559 IonError::type_err(
2560 ion_str!("strip_prefix requires string argument").to_string(),
2561 span.line,
2562 span.col,
2563 )
2564 })?;
2565 Ok(Value::Str(s.strip_prefix(pre).unwrap_or(s).to_string()))
2566 }
2567 "strip_suffix" => {
2568 let suf = args[0].as_str().ok_or_else(|| {
2569 IonError::type_err(
2570 ion_str!("strip_suffix requires string argument").to_string(),
2571 span.line,
2572 span.col,
2573 )
2574 })?;
2575 Ok(Value::Str(s.strip_suffix(suf).unwrap_or(s).to_string()))
2576 }
2577 "pad_start" => {
2578 let width = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2579 IonError::type_err(
2580 ion_str!("pad_start requires int argument").to_string(),
2581 span.line,
2582 span.col,
2583 )
2584 })? as usize;
2585 let ch = args
2586 .get(1)
2587 .and_then(|a| a.as_str())
2588 .and_then(|s| s.chars().next())
2589 .unwrap_or(' ');
2590 let char_len = s.chars().count();
2591 if char_len >= width {
2592 Ok(Value::Str(s.to_string()))
2593 } else {
2594 let pad: String = std::iter::repeat_n(ch, width - char_len).collect();
2595 Ok(Value::Str(format!("{}{}", pad, s)))
2596 }
2597 }
2598 "pad_end" => {
2599 let width = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2600 IonError::type_err(
2601 ion_str!("pad_end requires int argument").to_string(),
2602 span.line,
2603 span.col,
2604 )
2605 })? as usize;
2606 let ch = args
2607 .get(1)
2608 .and_then(|a| a.as_str())
2609 .and_then(|s| s.chars().next())
2610 .unwrap_or(' ');
2611 let char_len = s.chars().count();
2612 if char_len >= width {
2613 Ok(Value::Str(s.to_string()))
2614 } else {
2615 let pad: String = std::iter::repeat_n(ch, width - char_len).collect();
2616 Ok(Value::Str(format!("{}{}", s, pad)))
2617 }
2618 }
2619 "reverse" => Ok(Value::Str(s.chars().rev().collect())),
2620 "slice" => {
2621 let chars: Vec<char> = s.chars().collect();
2622 let char_count = chars.len();
2623 let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2624 let end = args
2625 .get(1)
2626 .and_then(|a| a.as_int())
2627 .map(|n| n as usize)
2628 .unwrap_or(char_count);
2629 let start = start.min(char_count);
2630 let end = end.min(char_count);
2631 Ok(Value::Str(chars[start..end].iter().collect()))
2632 }
2633 _ => Err(IonError::type_err(
2634 format!(
2635 "{}{}{}",
2636 ion_str!("no method '"),
2637 method,
2638 ion_str!("' on string")
2639 ),
2640 span.line,
2641 span.col,
2642 )
2643 .into()),
2644 }
2645 }
2646
2647 fn bytes_method(&self, bytes: &[u8], method: &str, args: &[Value], span: Span) -> SignalResult {
2648 match method {
2649 "len" => Ok(Value::Int(bytes.len() as i64)),
2650 "is_empty" => Ok(Value::Bool(bytes.is_empty())),
2651 "contains" => {
2652 let byte = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2653 IonError::type_err(
2654 ion_str!("bytes.contains() requires an int argument").to_string(),
2655 span.line,
2656 span.col,
2657 )
2658 })?;
2659 Ok(Value::Bool(bytes.contains(&(byte as u8))))
2660 }
2661 "slice" => {
2662 let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2663 let end = args
2664 .get(1)
2665 .and_then(|a| a.as_int())
2666 .map(|n| n as usize)
2667 .unwrap_or(bytes.len());
2668 let start = start.min(bytes.len());
2669 let end = end.min(bytes.len());
2670 Ok(Value::Bytes(bytes[start..end].to_vec()))
2671 }
2672 "to_list" => Ok(Value::List(
2673 bytes.iter().map(|&b| Value::Int(b as i64)).collect(),
2674 )),
2675 "to_str" => match std::str::from_utf8(bytes) {
2676 std::result::Result::Ok(s) => {
2677 Ok(Value::Result(Ok(Box::new(Value::Str(s.to_string())))))
2678 }
2679 std::result::Result::Err(e) => {
2680 Ok(Value::Result(Err(Box::new(Value::Str(format!("{}", e))))))
2681 }
2682 },
2683 "to_hex" => {
2684 let hex: String = bytes.iter().map(|b| format!("{:02x}", b)).collect();
2685 Ok(Value::Str(hex))
2686 }
2687 "find" => {
2688 let needle = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2689 IonError::type_err(
2690 ion_str!("bytes.find() requires an int argument").to_string(),
2691 span.line,
2692 span.col,
2693 )
2694 })?;
2695 let pos = bytes.iter().position(|&b| b == needle as u8);
2696 Ok(match pos {
2697 Some(i) => Value::Option(Some(Box::new(Value::Int(i as i64)))),
2698 None => Value::Option(None),
2699 })
2700 }
2701 "reverse" => {
2702 let mut rev = bytes.to_vec();
2703 rev.reverse();
2704 Ok(Value::Bytes(rev))
2705 }
2706 "push" => {
2707 let byte = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2708 IonError::type_err(
2709 ion_str!("bytes.push() requires an int argument").to_string(),
2710 span.line,
2711 span.col,
2712 )
2713 })?;
2714 let mut new = bytes.to_vec();
2715 new.push(byte as u8);
2716 Ok(Value::Bytes(new))
2717 }
2718 _ => Err(IonError::type_err(
2719 format!(
2720 "{}{}{}{}",
2721 ion_str!("no method '"),
2722 method,
2723 ion_str!("' on "),
2724 ion_str!("bytes"),
2725 ),
2726 span.line,
2727 span.col,
2728 )
2729 .into()),
2730 }
2731 }
2732
2733 fn dict_method(
2734 &self,
2735 map: &IndexMap<String, Value>,
2736 method: &str,
2737 args: &[Value],
2738 span: Span,
2739 ) -> SignalResult {
2740 match method {
2741 "len" => Ok(Value::Int(map.len() as i64)),
2742 "keys" => Ok(Value::List(
2743 map.keys().map(|k| Value::Str(k.clone())).collect(),
2744 )),
2745 "values" => Ok(Value::List(map.values().cloned().collect())),
2746 "entries" => Ok(Value::List(
2747 map.iter()
2748 .map(|(k, v)| Value::Tuple(vec![Value::Str(k.clone()), v.clone()]))
2749 .collect(),
2750 )),
2751 "contains_key" => {
2752 let key = args[0].as_str().ok_or_else(|| {
2753 IonError::type_err(
2754 ion_str!("contains_key requires string argument").to_string(),
2755 span.line,
2756 span.col,
2757 )
2758 })?;
2759 Ok(Value::Bool(map.contains_key(key)))
2760 }
2761 "get" => {
2762 let key = args[0].as_str().ok_or_else(|| {
2763 IonError::type_err(
2764 ion_str!("get requires string argument").to_string(),
2765 span.line,
2766 span.col,
2767 )
2768 })?;
2769 Ok(match map.get(key) {
2770 Some(v) => Value::Option(Some(Box::new(v.clone()))),
2771 None => Value::Option(None),
2772 })
2773 }
2774 "insert" => {
2775 if args.len() < 2 {
2776 return Err(IonError::runtime(
2777 ion_str!("insert requires 2 arguments: key, value").to_string(),
2778 span.line,
2779 span.col,
2780 )
2781 .into());
2782 }
2783 let key = args[0].as_str().ok_or_else(|| {
2784 IonError::type_err(
2785 ion_str!("insert requires string key").to_string(),
2786 span.line,
2787 span.col,
2788 )
2789 })?;
2790 let mut new_map = map.clone();
2791 new_map.insert(key.to_string(), args[1].clone());
2792 Ok(Value::Dict(new_map))
2793 }
2794 "remove" => {
2795 let key = args[0].as_str().ok_or_else(|| {
2796 IonError::type_err(
2797 ion_str!("remove requires string key").to_string(),
2798 span.line,
2799 span.col,
2800 )
2801 })?;
2802 let mut new_map = map.clone();
2803 new_map.shift_remove(key);
2804 Ok(Value::Dict(new_map))
2805 }
2806 "merge" => {
2807 if let Value::Dict(other) = &args[0] {
2808 let mut new_map = map.clone();
2809 for (k, v) in other {
2810 new_map.insert(k.clone(), v.clone());
2811 }
2812 Ok(Value::Dict(new_map))
2813 } else {
2814 Err(IonError::type_err(
2815 ion_str!("merge requires a dict argument").to_string(),
2816 span.line,
2817 span.col,
2818 )
2819 .into())
2820 }
2821 }
2822 "is_empty" => Ok(Value::Bool(map.is_empty())),
2823 "update" => {
2824 if let Value::Dict(other) = &args[0] {
2825 let mut new_map = map.clone();
2826 for (k, v) in other {
2827 new_map.insert(k.clone(), v.clone());
2828 }
2829 Ok(Value::Dict(new_map))
2830 } else {
2831 Err(IonError::type_err(
2832 ion_str!("update requires a dict argument").to_string(),
2833 span.line,
2834 span.col,
2835 )
2836 .into())
2837 }
2838 }
2839 "keys_of" => {
2840 let target = &args[0];
2841 let keys: Vec<Value> = map
2842 .iter()
2843 .filter(|(_, v)| *v == target)
2844 .map(|(k, _)| Value::Str(k.clone()))
2845 .collect();
2846 Ok(Value::List(keys))
2847 }
2848 "zip" => {
2849 if let Value::Dict(other) = &args[0] {
2850 let mut result = indexmap::IndexMap::new();
2851 for (k, v) in map {
2852 if let Some(ov) = other.get(k) {
2853 result.insert(k.clone(), Value::Tuple(vec![v.clone(), ov.clone()]));
2854 }
2855 }
2856 Ok(Value::Dict(result))
2857 } else {
2858 Err(IonError::type_err(
2859 ion_str!("zip requires a dict argument").to_string(),
2860 span.line,
2861 span.col,
2862 )
2863 .into())
2864 }
2865 }
2866 _ => Err(IonError::type_err(
2867 format!(
2868 "{}{}{}",
2869 ion_str!("no method '"),
2870 method,
2871 ion_str!("' on dict")
2872 ),
2873 span.line,
2874 span.col,
2875 )
2876 .into()),
2877 }
2878 }
2879
2880 fn option_method(
2881 &mut self,
2882 opt: Option<Box<Value>>,
2883 method: &str,
2884 args: &[Value],
2885 span: Span,
2886 ) -> SignalResult {
2887 match method {
2888 "is_some" => Ok(Value::Bool(opt.is_some())),
2889 "is_none" => Ok(Value::Bool(opt.is_none())),
2890 "unwrap" => match opt {
2891 Some(v) => Ok(*v),
2892 None => {
2893 Err(
2894 IonError::runtime(ion_str!("called unwrap on None"), span.line, span.col)
2895 .into(),
2896 )
2897 }
2898 },
2899 "unwrap_or" => match opt {
2900 Some(v) => Ok(*v),
2901 None => Ok(args[0].clone()),
2902 },
2903 "expect" => match opt {
2904 Some(v) => Ok(*v),
2905 None => {
2906 let default_msg = ion_str!("expect failed");
2907 let msg = args[0].as_str().unwrap_or(&default_msg);
2908 Err(IonError::runtime(msg.to_string(), span.line, span.col).into())
2909 }
2910 },
2911 "map" => {
2912 let func = args[0].clone();
2913 match opt {
2914 Some(v) => {
2915 let result = self.call_value(&func, &[*v], span)?;
2916 Ok(Value::Option(Some(Box::new(result))))
2917 }
2918 None => Ok(Value::Option(None)),
2919 }
2920 }
2921 "and_then" => {
2922 let func = args[0].clone();
2923 match opt {
2924 Some(v) => self.call_value(&func, &[*v], span),
2925 None => Ok(Value::Option(None)),
2926 }
2927 }
2928 "or_else" => {
2929 let func = args[0].clone();
2930 match opt {
2931 Some(v) => Ok(Value::Option(Some(v))),
2932 None => self.call_value(&func, &[], span),
2933 }
2934 }
2935 "unwrap_or_else" => {
2936 let func = args[0].clone();
2937 match opt {
2938 Some(v) => Ok(*v),
2939 None => self.call_value(&func, &[], span),
2940 }
2941 }
2942 _ => Err(IonError::type_err(
2943 format!(
2944 "{}{}{}",
2945 ion_str!("no method '"),
2946 method,
2947 ion_str!("' on Option")
2948 ),
2949 span.line,
2950 span.col,
2951 )
2952 .into()),
2953 }
2954 }
2955
2956 fn result_method(
2957 &mut self,
2958 res: Result<Box<Value>, Box<Value>>,
2959 method: &str,
2960 args: &[Value],
2961 span: Span,
2962 ) -> SignalResult {
2963 match method {
2964 "is_ok" => Ok(Value::Bool(res.is_ok())),
2965 "is_err" => Ok(Value::Bool(res.is_err())),
2966 "unwrap" => match res {
2967 Ok(v) => Ok(*v),
2968 Err(e) => Err(IonError::runtime(
2969 format!("{}{}", ion_str!("called unwrap on Err: "), e),
2970 span.line,
2971 span.col,
2972 )
2973 .into()),
2974 },
2975 "unwrap_or" => match res {
2976 Ok(v) => Ok(*v),
2977 Err(_) => Ok(args[0].clone()),
2978 },
2979 "expect" => match res {
2980 Ok(v) => Ok(*v),
2981 Err(e) => {
2982 let default_msg = ion_str!("expect failed");
2983 let msg = args[0].as_str().unwrap_or(&default_msg);
2984 Err(IonError::runtime(format!("{}: {}", msg, e), span.line, span.col).into())
2985 }
2986 },
2987 "map" => {
2988 let func = args[0].clone();
2989 match res {
2990 Ok(v) => {
2991 let result = self.call_value(&func, &[*v], span)?;
2992 Ok(Value::Result(Ok(Box::new(result))))
2993 }
2994 Err(e) => Ok(Value::Result(Err(e))),
2995 }
2996 }
2997 "map_err" => {
2998 let func = args[0].clone();
2999 match res {
3000 Ok(v) => Ok(Value::Result(Ok(v))),
3001 Err(e) => {
3002 let result = self.call_value(&func, &[*e], span)?;
3003 Ok(Value::Result(Err(Box::new(result))))
3004 }
3005 }
3006 }
3007 "and_then" => {
3008 let func = args[0].clone();
3009 match res {
3010 Ok(v) => self.call_value(&func, &[*v], span),
3011 Err(e) => Ok(Value::Result(Err(e))),
3012 }
3013 }
3014 "or_else" => {
3015 let func = args[0].clone();
3016 match res {
3017 Ok(v) => Ok(Value::Result(Ok(v))),
3018 Err(e) => self.call_value(&func, &[*e], span),
3019 }
3020 }
3021 "unwrap_or_else" => {
3022 let func = args[0].clone();
3023 match res {
3024 Ok(v) => Ok(*v),
3025 Err(e) => self.call_value(&func, &[*e], span),
3026 }
3027 }
3028 _ => Err(IonError::type_err(
3029 format!(
3030 "{}{}{}",
3031 ion_str!("no method '"),
3032 method,
3033 ion_str!("' on Result")
3034 ),
3035 span.line,
3036 span.col,
3037 )
3038 .into()),
3039 }
3040 }
3041
3042 fn call_value(&mut self, func: &Value, args: &[Value], span: Span) -> SignalResult {
3043 match func {
3044 Value::Fn(ion_fn) => {
3045 if self.call_depth >= self.limits.max_call_depth {
3046 return Err(IonError::runtime(
3047 ion_str!("maximum call depth exceeded").to_string(),
3048 span.line,
3049 span.col,
3050 )
3051 .into());
3052 }
3053 self.call_depth += 1;
3054 self.env.push_scope();
3055 let result = (|| {
3056 for (name, val) in &ion_fn.captures {
3058 self.env.define(name.clone(), val.clone(), false);
3059 }
3060 for (i, param) in ion_fn.params.iter().enumerate() {
3062 let val = if i < args.len() {
3063 args[i].clone()
3064 } else if let Some(default) = ¶m.default {
3065 self.eval_expr(default)?
3066 } else {
3067 return Err(IonError::runtime(
3068 format!(
3069 "{}{}{}{}{}{}",
3070 ion_str!("function '"),
3071 ion_fn.name,
3072 ion_str!("' expected "),
3073 ion_fn.params.len(),
3074 ion_str!(" arguments, got "),
3075 args.len(),
3076 ),
3077 span.line,
3078 span.col,
3079 )
3080 .into());
3081 };
3082 self.env.define(param.name.clone(), val, false);
3083 }
3084 self.eval_stmts(&ion_fn.body)
3085 })();
3086 self.env.pop_scope();
3087 self.call_depth -= 1;
3088 match result {
3089 Ok(v) => Ok(v),
3090 Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
3091 Err(SignalOrError::Signal(Signal::Break { label, .. })) => Err(
3092 IonError::runtime(unmatched_label_msg("break", label), span.line, span.col)
3093 .into(),
3094 ),
3095 Err(SignalOrError::Signal(Signal::Continue { label })) => {
3096 Err(IonError::runtime(
3097 unmatched_label_msg("continue", label),
3098 span.line,
3099 span.col,
3100 )
3101 .into())
3102 }
3103 Err(SignalOrError::Error(e)) => {
3104 if e.kind == ErrorKind::PropagatedErr {
3106 Ok(Value::Result(Err(Box::new(Value::Str(e.message.clone())))))
3107 } else if e.kind == ErrorKind::PropagatedNone {
3108 Ok(Value::Option(None))
3109 } else {
3110 Err(e.into())
3111 }
3112 }
3113 }
3114 }
3115 #[cfg(feature = "concurrency")]
3116 Value::BuiltinFn(ref name, _) if name == "timeout" => self.builtin_timeout(args, span),
3117 Value::BuiltinFn(_, func) => {
3118 func(args).map_err(|msg| IonError::runtime(msg, span.line, span.col).into())
3119 }
3120 Value::BuiltinClosure(_, func) => func
3121 .call(args)
3122 .map_err(|msg| IonError::runtime(msg, span.line, span.col).into()),
3123 _ => Err(IonError::type_err(
3124 format!("{}{}", ion_str!("not callable: "), func.type_name()),
3125 span.line,
3126 span.col,
3127 )
3128 .into()),
3129 }
3130 }
3131
3132 #[cfg(feature = "concurrency")]
3133 fn builtin_timeout(&mut self, args: &[Value], span: Span) -> SignalResult {
3134 if args.len() < 2 {
3135 return Err(IonError::runtime(
3136 ion_str!("timeout(ms, fn) requires 2 arguments").to_string(),
3137 span.line,
3138 span.col,
3139 )
3140 .into());
3141 }
3142 let ms = args[0].as_int().ok_or_else(|| {
3143 SignalOrError::Error(IonError::runtime(
3144 ion_str!("timeout: first argument must be int (ms)").to_string(),
3145 span.line,
3146 span.col,
3147 ))
3148 })?;
3149 let func = args[1].clone();
3150 let captured_env = self.env.capture();
3152 let limits = self.limits.clone();
3153 let types = self.types.clone();
3154 let cancel_flag = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
3155 let task = crate::async_rt::spawn_task_with_cancel(cancel_flag, move |flag| {
3156 let mut child = Interpreter::new();
3157 child.limits = limits;
3158 child.types = types;
3159 child.cancel_flag = Some(flag);
3160 for (name, val) in captured_env {
3161 child.env.define(name, val, false);
3162 }
3163 child
3164 .call_value(&func, &[], crate::ast::Span { line: 0, col: 0 })
3165 .map_err(|e| match e {
3166 SignalOrError::Error(err) => err,
3167 SignalOrError::Signal(_) => {
3168 IonError::runtime(ion_str!("unexpected signal in timeout"), 0, 0)
3169 }
3170 })
3171 });
3172 match task.join_timeout(std::time::Duration::from_millis(ms as u64)) {
3173 Some(Ok(val)) => Ok(Value::Option(Some(Box::new(val)))),
3174 Some(Err(e)) => Err(e.into()),
3175 None => {
3176 task.cancel();
3179 Ok(Value::Option(None))
3180 }
3181 }
3182 }
3183
3184 fn call_with_named(
3185 &mut self,
3186 func: &Value,
3187 named_args: Vec<(Option<String>, Value)>,
3188 span: Span,
3189 ) -> SignalResult {
3190 match func {
3191 Value::Fn(ion_fn) => {
3192 let mut ordered = vec![None; ion_fn.params.len()];
3194 let mut pos_idx = 0;
3195 for (name, val) in named_args {
3196 if let Some(name) = name {
3197 let param_idx = ion_fn
3199 .params
3200 .iter()
3201 .position(|p| p.name == name)
3202 .ok_or_else(|| {
3203 IonError::runtime(
3204 format!(
3205 "{}{}{}{}",
3206 ion_str!("unknown parameter '"),
3207 name,
3208 ion_str!("' for function '"),
3209 ion_fn.name,
3210 ),
3211 span.line,
3212 span.col,
3213 )
3214 })?;
3215 ordered[param_idx] = Some(val);
3216 } else {
3217 while pos_idx < ordered.len() && ordered[pos_idx].is_some() {
3219 pos_idx += 1;
3220 }
3221 if pos_idx < ordered.len() {
3222 ordered[pos_idx] = Some(val);
3223 pos_idx += 1;
3224 }
3225 }
3226 }
3227 let opt_args: Vec<Option<Value>> = ordered;
3229 if self.call_depth >= self.limits.max_call_depth {
3231 return Err(IonError::runtime(
3232 ion_str!("maximum call depth exceeded").to_string(),
3233 span.line,
3234 span.col,
3235 )
3236 .into());
3237 }
3238 self.call_depth += 1;
3239 self.env.push_scope();
3240 let result = (|| {
3241 for (name, val) in &ion_fn.captures {
3242 self.env.define(name.clone(), val.clone(), false);
3243 }
3244 for (i, param) in ion_fn.params.iter().enumerate() {
3245 let val = if i < opt_args.len() && opt_args[i].is_some() {
3246 opt_args[i].clone().unwrap()
3247 } else if let Some(default) = ¶m.default {
3248 self.eval_expr(default)?
3249 } else {
3250 return Err(IonError::runtime(
3251 format!(
3252 "{}{}{}",
3253 ion_str!("missing argument '"),
3254 param.name,
3255 ion_str!("'"),
3256 ),
3257 span.line,
3258 span.col,
3259 )
3260 .into());
3261 };
3262 self.env.define(param.name.clone(), val, false);
3263 }
3264 self.eval_stmts(&ion_fn.body)
3265 })();
3266 self.env.pop_scope();
3267 self.call_depth -= 1;
3268 match result {
3269 Ok(v) => Ok(v),
3270 Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
3271 Err(SignalOrError::Error(e)) if e.kind == ErrorKind::PropagatedErr => {
3272 Ok(Value::Result(Err(Box::new(Value::Str(e.message.clone())))))
3273 }
3274 Err(SignalOrError::Error(e)) if e.kind == ErrorKind::PropagatedNone => {
3275 Ok(Value::Option(None))
3276 }
3277 Err(e) => Err(e),
3278 }
3279 }
3280 _ => {
3281 let args: Vec<Value> = named_args.into_iter().map(|(_, v)| v).collect();
3283 self.call_value(func, &args, span)
3284 }
3285 }
3286 }
3287
3288 fn check_type_ann(val: &Value, ann: &TypeAnn, span: Span) -> Result<(), SignalOrError> {
3289 let matches = match ann {
3290 TypeAnn::Simple(name) => match name.as_str() {
3291 "int" => matches!(val, Value::Int(_)),
3292 "float" => matches!(val, Value::Float(_)),
3293 "bool" => matches!(val, Value::Bool(_)),
3294 "string" => matches!(val, Value::Str(_)),
3295 "bytes" => matches!(val, Value::Bytes(_)),
3296 "list" => matches!(val, Value::List(_)),
3297 "dict" => matches!(val, Value::Dict(_)),
3298 "tuple" => matches!(val, Value::Tuple(_)),
3299 "set" => matches!(val, Value::Set(_)),
3300 "fn" => matches!(
3301 val,
3302 Value::Fn(_) | Value::BuiltinFn(_, _) | Value::BuiltinClosure(_, _)
3303 ),
3304 "cell" => matches!(val, Value::Cell(_)),
3305 "any" => true,
3306 _ => true, },
3308 TypeAnn::Option(_) => matches!(val, Value::Option(_)),
3309 TypeAnn::Result(_, _) => matches!(val, Value::Result(_)),
3310 TypeAnn::List(_) => matches!(val, Value::List(_)),
3311 TypeAnn::Dict(_, _) => matches!(val, Value::Dict(_)),
3312 };
3313 if !matches {
3314 return Err(IonError::type_err(
3315 format!(
3316 "{}{}{}{}",
3317 ion_str!("type mismatch: expected "),
3318 Self::type_ann_name(ann),
3319 ion_str!(", got "),
3320 val.type_name()
3321 ),
3322 span.line,
3323 span.col,
3324 )
3325 .into());
3326 }
3327 Ok(())
3328 }
3329
3330 fn type_ann_name(ann: &TypeAnn) -> String {
3331 match ann {
3332 TypeAnn::Simple(name) => name.clone(),
3333 TypeAnn::Option(inner) => format!("Option<{}>", Self::type_ann_name(inner)),
3334 TypeAnn::Result(ok, err) => {
3335 format!(
3336 "Result<{}, {}>",
3337 Self::type_ann_name(ok),
3338 Self::type_ann_name(err)
3339 )
3340 }
3341 TypeAnn::List(inner) => format!("list<{}>", Self::type_ann_name(inner)),
3342 TypeAnn::Dict(k, v) => {
3343 format!(
3344 "dict<{}, {}>",
3345 Self::type_ann_name(k),
3346 Self::type_ann_name(v)
3347 )
3348 }
3349 }
3350 }
3351
3352 fn value_to_iter(&self, val: &Value, span: Span) -> Result<Vec<Value>, SignalOrError> {
3353 match val {
3354 Value::List(items) => Ok(items.clone()),
3355 Value::Set(items) => Ok(items.clone()),
3356 Value::Tuple(items) => Ok(items.clone()),
3357 Value::Dict(map) => Ok(map
3358 .iter()
3359 .map(|(k, v)| Value::Tuple(vec![Value::Str(k.clone()), v.clone()]))
3360 .collect()),
3361 Value::Str(s) => Ok(s.chars().map(|c| Value::Str(c.to_string())).collect()),
3362 Value::Bytes(bytes) => Ok(bytes.iter().map(|&b| Value::Int(b as i64)).collect()),
3363 Value::Range {
3364 start,
3365 end,
3366 inclusive,
3367 } => Ok(Value::range_to_list(*start, *end, *inclusive)),
3368 _ => Err(IonError::type_err(
3369 format!("{}{}", ion_str!("cannot iterate over "), val.type_name()),
3370 span.line,
3371 span.col,
3372 )
3373 .into()),
3374 }
3375 }
3376
3377 fn pattern_matches(&self, pattern: &Pattern, val: &Value) -> bool {
3380 match (pattern, val) {
3381 (Pattern::Wildcard, _) => true,
3382 (Pattern::Ident(_), _) => true,
3383 (Pattern::Int(a), Value::Int(b)) => a == b,
3384 (Pattern::Float(a), Value::Float(b)) => a == b,
3385 (Pattern::Bool(a), Value::Bool(b)) => a == b,
3386 (Pattern::Str(a), Value::Str(b)) => a == b,
3387 (Pattern::Bytes(a), Value::Bytes(b)) => a == b,
3388 (Pattern::None, Value::Option(None)) => true,
3389 (Pattern::Some(p), Value::Option(Some(v))) => self.pattern_matches(p, v),
3390 (Pattern::Ok(p), Value::Result(Ok(v))) => self.pattern_matches(p, v),
3391 (Pattern::Err(p), Value::Result(Err(v))) => self.pattern_matches(p, v),
3392 (Pattern::Tuple(pats), Value::Tuple(vals)) => {
3393 pats.len() == vals.len()
3394 && pats
3395 .iter()
3396 .zip(vals)
3397 .all(|(p, v)| self.pattern_matches(p, v))
3398 }
3399 (Pattern::List(pats, rest), Value::List(vals)) => {
3400 if rest.is_some() {
3401 vals.len() >= pats.len()
3402 && pats
3403 .iter()
3404 .zip(vals)
3405 .all(|(p, v)| self.pattern_matches(p, v))
3406 } else {
3407 pats.len() == vals.len()
3408 && pats
3409 .iter()
3410 .zip(vals)
3411 .all(|(p, v)| self.pattern_matches(p, v))
3412 }
3413 }
3414 (
3415 Pattern::EnumVariant {
3416 enum_name,
3417 variant,
3418 fields,
3419 },
3420 Value::HostEnum {
3421 enum_name: en,
3422 variant: v,
3423 data,
3424 },
3425 ) => {
3426 if enum_name != en || variant != v {
3427 return false;
3428 }
3429 match fields {
3430 EnumPatternFields::None => data.is_empty(),
3431 EnumPatternFields::Positional(pats) => {
3432 pats.len() == data.len()
3433 && pats
3434 .iter()
3435 .zip(data)
3436 .all(|(p, v)| self.pattern_matches(p, v))
3437 }
3438 EnumPatternFields::Named(_) => false, }
3440 }
3441 (
3442 Pattern::Struct { name, fields },
3443 Value::HostStruct {
3444 type_name,
3445 fields: val_fields,
3446 },
3447 ) => {
3448 if name != type_name {
3449 return false;
3450 }
3451 fields.iter().all(|(fname, fpat)| {
3452 match val_fields.get(fname) {
3453 Some(v) => match fpat {
3454 Some(p) => self.pattern_matches(p, v),
3455 None => true, },
3457 None => false,
3458 }
3459 })
3460 }
3461 _ => false,
3462 }
3463 }
3464
3465 fn bind_pattern(
3466 &mut self,
3467 pattern: &Pattern,
3468 val: &Value,
3469 mutable: bool,
3470 span: Span,
3471 ) -> Result<(), SignalOrError> {
3472 match (pattern, val) {
3473 (Pattern::Wildcard, _) => Ok(()),
3474 (Pattern::Ident(name), _) => {
3475 self.env.define(name.clone(), val.clone(), mutable);
3476 Ok(())
3477 }
3478 (
3479 Pattern::Int(_)
3480 | Pattern::Float(_)
3481 | Pattern::Bool(_)
3482 | Pattern::Str(_)
3483 | Pattern::Bytes(_)
3484 | Pattern::None,
3485 _,
3486 ) => Ok(()),
3487 (Pattern::Some(p), Value::Option(Some(v))) => self.bind_pattern(p, v, mutable, span),
3488 (Pattern::Ok(p), Value::Result(Ok(v))) => self.bind_pattern(p, v, mutable, span),
3489 (Pattern::Err(p), Value::Result(Err(v))) => self.bind_pattern(p, v, mutable, span),
3490 (Pattern::Tuple(pats), Value::Tuple(vals)) => {
3491 for (p, v) in pats.iter().zip(vals) {
3492 self.bind_pattern(p, v, mutable, span)?;
3493 }
3494 Ok(())
3495 }
3496 (Pattern::List(pats, rest), Value::List(vals)) => {
3497 for (p, v) in pats.iter().zip(vals) {
3498 self.bind_pattern(p, v, mutable, span)?;
3499 }
3500 if let Some(rest_pat) = rest {
3501 let rest_vals = vals[pats.len()..].to_vec();
3502 self.bind_pattern(rest_pat, &Value::List(rest_vals), mutable, span)?;
3503 }
3504 Ok(())
3505 }
3506 (Pattern::EnumVariant { fields, .. }, Value::HostEnum { data, .. }) => match fields {
3507 EnumPatternFields::None => Ok(()),
3508 EnumPatternFields::Positional(pats) => {
3509 for (p, v) in pats.iter().zip(data) {
3510 self.bind_pattern(p, v, mutable, span)?;
3511 }
3512 Ok(())
3513 }
3514 EnumPatternFields::Named(_) => Ok(()),
3515 },
3516 (
3517 Pattern::Struct { fields, .. },
3518 Value::HostStruct {
3519 fields: val_fields, ..
3520 },
3521 ) => {
3522 for (fname, fpat) in fields {
3523 if let Some(v) = val_fields.get(fname) {
3524 match fpat {
3525 Some(p) => self.bind_pattern(p, v, mutable, span)?,
3526 None => self.env.define(fname.clone(), v.clone(), mutable),
3527 }
3528 }
3529 }
3530 Ok(())
3531 }
3532 _ => Err(IonError::runtime(
3533 ion_str!("pattern match failed in binding").to_string(),
3534 span.line,
3535 span.col,
3536 )
3537 .into()),
3538 }
3539 }
3540}
3541
3542#[cfg(feature = "concurrency")]
3543impl Interpreter {
3544 fn eval_async_block(&mut self, body: &[Stmt], _span: Span) -> SignalResult {
3545 use crate::async_rt::Nursery;
3546
3547 let prev_nursery = self.nursery.take();
3549 self.nursery = Some(Nursery::new());
3550
3551 self.env.push_scope();
3552 let result = self.eval_stmts(body);
3553 self.env.pop_scope();
3554
3555 let nursery = self.nursery.take().unwrap();
3557 self.nursery = prev_nursery;
3558
3559 if let Err(e) = nursery.join_all() {
3560 return Err(e.into());
3561 }
3562
3563 result
3564 }
3565
3566 fn eval_spawn(&mut self, expr: &Expr, span: Span) -> SignalResult {
3567 use std::sync::Arc;
3568
3569 if self.nursery.is_none() {
3571 return Err(IonError::runtime(
3572 ion_str!("spawn is only allowed inside async {}").to_string(),
3573 span.line,
3574 span.col,
3575 )
3576 .into());
3577 }
3578
3579 let captured_env = self.env.capture();
3581 let expr_clone = expr.clone();
3582 let limits = self.limits.clone();
3583 let types = self.types.clone();
3584
3585 let cancel_flag = Arc::new(std::sync::atomic::AtomicBool::new(false));
3586 let task_handle: Arc<dyn crate::async_rt::TaskHandle> =
3587 crate::async_rt::spawn_task_with_cancel(cancel_flag, move |flag| {
3588 let mut child = Interpreter::new();
3589 child.limits = limits;
3590 child.types = types;
3591 child.cancel_flag = Some(flag);
3592 for (name, val) in captured_env {
3594 child.env.define(name, val, false);
3595 }
3596 let program = crate::ast::Program {
3598 stmts: vec![crate::ast::Stmt {
3599 kind: crate::ast::StmtKind::ExprStmt {
3600 expr: expr_clone,
3601 has_semi: false,
3602 },
3603 span: crate::ast::Span { line: 0, col: 0 },
3604 }],
3605 };
3606 child.eval_program(&program)
3607 });
3608
3609 if let Some(nursery) = &mut self.nursery {
3611 nursery.spawn(task_handle.clone());
3612 }
3613
3614 Ok(Value::Task(task_handle))
3615 }
3616
3617 fn eval_await(&mut self, expr: &Expr, span: Span) -> SignalResult {
3618 let val = self.eval_expr(expr)?;
3619 match val {
3620 Value::Task(handle) => handle.join().map_err(SignalOrError::Error),
3621 _ => Err(IonError::type_err(
3622 format!("{}{}", ion_str!("cannot await "), val.type_name()),
3623 span.line,
3624 span.col,
3625 )
3626 .into()),
3627 }
3628 }
3629
3630 fn eval_select(&mut self, branches: &[crate::ast::SelectBranch], span: Span) -> SignalResult {
3631 use std::sync::Arc;
3632
3633 let mut tasks: Vec<(usize, Arc<dyn crate::async_rt::TaskHandle>)> = Vec::new();
3635 for (i, branch) in branches.iter().enumerate() {
3636 let captured_env = self.env.capture();
3637 let expr_clone = branch.future_expr.clone();
3638 let limits = self.limits.clone();
3639 let types = self.types.clone();
3640
3641 let cancel_flag = Arc::new(std::sync::atomic::AtomicBool::new(false));
3642 let handle = crate::async_rt::spawn_task_with_cancel(cancel_flag, move |flag| {
3643 let mut child = Interpreter::new();
3644 child.limits = limits;
3645 child.types = types;
3646 child.cancel_flag = Some(flag);
3647 for (name, val) in captured_env {
3648 child.env.define(name, val, false);
3649 }
3650 let program = crate::ast::Program {
3651 stmts: vec![crate::ast::Stmt {
3652 kind: crate::ast::StmtKind::ExprStmt {
3653 expr: expr_clone,
3654 has_semi: false,
3655 },
3656 span: crate::ast::Span { line: 0, col: 0 },
3657 }],
3658 };
3659 child.eval_program(&program)
3660 });
3661 tasks.push((i, handle));
3662 }
3663
3664 let handles: Vec<_> = tasks.iter().map(|(_, h)| h.clone()).collect();
3666 let (winner_idx, result) = crate::async_rt::wait_any(&handles);
3667
3668 for (i, h) in handles.iter().enumerate() {
3671 if i != winner_idx {
3672 h.cancel();
3673 }
3674 }
3675
3676 let result = result?;
3677 let branch = &branches[tasks[winner_idx].0];
3678 self.env.push_scope();
3679 self.bind_pattern(&branch.pattern, &result, false, span)?;
3680 let body_result = self.eval_expr(&branch.body);
3681 self.env.pop_scope();
3682 body_result
3683 }
3684
3685 fn task_method(
3686 &self,
3687 handle: &std::sync::Arc<dyn crate::async_rt::TaskHandle>,
3688 method: &str,
3689 args: &[Value],
3690 span: Span,
3691 ) -> SignalResult {
3692 match method {
3693 "is_finished" => Ok(Value::Bool(handle.is_finished())),
3694 "cancel" => {
3695 handle.cancel();
3696 Ok(Value::Unit)
3697 }
3698 "is_cancelled" => Ok(Value::Bool(handle.is_cancelled())),
3699 "await_timeout" => {
3700 let ms = args.first().and_then(|v| v.as_int()).ok_or_else(|| {
3701 IonError::runtime(
3702 ion_str!("await_timeout requires int (ms)").to_string(),
3703 span.line,
3704 span.col,
3705 )
3706 })?;
3707 match handle.join_timeout(std::time::Duration::from_millis(ms as u64)) {
3708 Some(result) => {
3709 let val = result.map_err(SignalOrError::Error)?;
3710 Ok(Value::Option(Some(Box::new(val))))
3711 }
3712 None => Ok(Value::Option(None)),
3713 }
3714 }
3715 _ => Err(IonError::type_err(
3716 format!(
3717 "{}{}{}",
3718 ion_str!("no method '"),
3719 method,
3720 ion_str!("' on Task")
3721 ),
3722 span.line,
3723 span.col,
3724 )
3725 .into()),
3726 }
3727 }
3728
3729 fn channel_method(
3730 &self,
3731 ch: &crate::async_rt::ChannelEnd,
3732 method: &str,
3733 args: &[Value],
3734 span: Span,
3735 ) -> SignalResult {
3736 use crate::async_rt::ChannelEnd;
3737 match (ch, method) {
3738 (ChannelEnd::Sender(tx), "send") => {
3739 if args.is_empty() {
3740 return Err(IonError::runtime(
3741 ion_str!("send requires a value").to_string(),
3742 span.line,
3743 span.col,
3744 )
3745 .into());
3746 }
3747 tx.send(args[0].clone()).map_err(|e| {
3748 IonError::runtime(
3749 format!("{}{}", ion_str!("channel send failed: "), e.message),
3750 span.line,
3751 span.col,
3752 )
3753 })?;
3754 Ok(Value::Unit)
3755 }
3756 (ChannelEnd::Sender(tx), "close") => {
3757 tx.close();
3758 Ok(Value::Unit)
3759 }
3760 (ChannelEnd::Receiver(rx), "recv") => match rx.recv() {
3761 Some(v) => Ok(Value::Option(Some(Box::new(v)))),
3762 None => Ok(Value::Option(None)),
3763 },
3764 (ChannelEnd::Receiver(rx), "try_recv") => match rx.try_recv() {
3765 Some(v) => Ok(Value::Option(Some(Box::new(v)))),
3766 None => Ok(Value::Option(None)),
3767 },
3768 (ChannelEnd::Receiver(rx), "recv_timeout") => {
3769 if args.is_empty() {
3770 return Err(IonError::runtime(
3771 ion_str!("recv_timeout requires a timeout in ms").to_string(),
3772 span.line,
3773 span.col,
3774 )
3775 .into());
3776 }
3777 let ms = args[0].as_int().ok_or_else(|| {
3778 IonError::runtime(
3779 ion_str!("recv_timeout requires int (ms)").to_string(),
3780 span.line,
3781 span.col,
3782 )
3783 })?;
3784 match rx.recv_timeout(std::time::Duration::from_millis(ms as u64)) {
3785 Some(v) => Ok(Value::Option(Some(Box::new(v)))),
3786 None => Ok(Value::Option(None)),
3787 }
3788 }
3789 _ => Err(IonError::type_err(
3790 format!(
3791 "{}{}{}",
3792 ion_str!("no method '"),
3793 method,
3794 ion_str!("' on Channel")
3795 ),
3796 span.line,
3797 span.col,
3798 )
3799 .into()),
3800 }
3801 }
3802}
3803
3804pub fn register_builtins(env: &mut Env, output: Arc<dyn OutputHandler>) {
3805 env.define(
3810 ion_str!("len").to_string(),
3811 Value::BuiltinFn(ion_str!("len").to_string(), |args| {
3812 if args.is_empty() {
3813 return Err(ion_str!("len() requires 1 argument"));
3814 }
3815 match &args[0] {
3816 Value::List(items) => Ok(Value::Int(items.len() as i64)),
3817 Value::Str(s) => Ok(Value::Int(s.len() as i64)),
3818 Value::Dict(map) => Ok(Value::Int(map.len() as i64)),
3819 Value::Bytes(b) => Ok(Value::Int(b.len() as i64)),
3820 _ => Err(format!(
3821 "{}{}",
3822 ion_str!("len() not supported for "),
3823 args[0].type_name()
3824 )),
3825 }
3826 }),
3827 false,
3828 );
3829 env.define(
3830 ion_str!("range").to_string(),
3831 Value::BuiltinFn(ion_str!("range").to_string(), |args| match args.len() {
3832 1 => {
3833 let n = args[0].as_int().ok_or(ion_str!("range requires int"))?;
3834 Ok(Value::Range {
3835 start: 0,
3836 end: n,
3837 inclusive: false,
3838 })
3839 }
3840 2 => {
3841 let start = args[0].as_int().ok_or(ion_str!("range requires int"))?;
3842 let end = args[1].as_int().ok_or(ion_str!("range requires int"))?;
3843 Ok(Value::Range {
3844 start,
3845 end,
3846 inclusive: false,
3847 })
3848 }
3849 _ => Err(ion_str!("range takes 1 or 2 arguments").to_string()),
3850 }),
3851 false,
3852 );
3853 env.define(
3854 ion_str!("set"),
3855 Value::BuiltinFn(ion_str!("set"), |args| {
3856 if args.is_empty() {
3857 return Ok(Value::Set(vec![]));
3858 }
3859 match &args[0] {
3860 Value::List(items) => {
3861 let mut unique = Vec::new();
3862 for v in items {
3863 if !unique.iter().any(|x| x == v) {
3864 unique.push(v.clone());
3865 }
3866 }
3867 Ok(Value::Set(unique))
3868 }
3869 _ => Err(ion_str!("set() requires a list argument")),
3870 }
3871 }),
3872 false,
3873 );
3874 env.define(
3875 ion_str!("cell"),
3876 Value::BuiltinFn(ion_str!("cell"), |args| {
3877 if args.len() != 1 {
3878 return Err(ion_str!("cell() takes 1 argument"));
3879 }
3880 Ok(Value::Cell(std::sync::Arc::new(std::sync::Mutex::new(
3881 args[0].clone(),
3882 ))))
3883 }),
3884 false,
3885 );
3886 env.define(
3887 ion_str!("type_of").to_string(),
3888 Value::BuiltinFn(ion_str!("type_of").to_string(), |args| {
3889 if args.is_empty() {
3890 return Err(ion_str!("type_of() requires 1 argument"));
3891 }
3892 Ok(Value::Str(args[0].type_name().to_string()))
3893 }),
3894 false,
3895 );
3896 env.define(
3898 ion_str!("str").to_string(),
3899 Value::BuiltinFn(ion_str!("str").to_string(), |args| {
3900 if args.len() != 1 {
3901 return Err(ion_str!("str takes 1 argument"));
3902 }
3903 Ok(Value::Str(args[0].to_string()))
3904 }),
3905 false,
3906 );
3907 env.define(
3908 ion_str!("int").to_string(),
3909 Value::BuiltinFn(ion_str!("int").to_string(), |args| {
3910 if args.len() != 1 {
3911 return Err(ion_str!("int takes 1 argument"));
3912 }
3913 match &args[0] {
3914 Value::Int(n) => Ok(Value::Int(*n)),
3915 Value::Float(n) => Ok(Value::Int(*n as i64)),
3916 Value::Str(s) => s.parse::<i64>().map(Value::Int).map_err(|_| {
3917 format!(
3918 "{}{}{}",
3919 ion_str!("cannot convert '"),
3920 s,
3921 ion_str!("' to int")
3922 )
3923 }),
3924 Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
3925 _ => Err(format!(
3926 "{}{}",
3927 ion_str!("cannot convert "),
3928 args[0].type_name()
3929 )),
3930 }
3931 }),
3932 false,
3933 );
3934 env.define(
3935 ion_str!("float").to_string(),
3936 Value::BuiltinFn(ion_str!("float").to_string(), |args| {
3937 if args.len() != 1 {
3938 return Err(ion_str!("float takes 1 argument"));
3939 }
3940 match &args[0] {
3941 Value::Float(n) => Ok(Value::Float(*n)),
3942 Value::Int(n) => Ok(Value::Float(*n as f64)),
3943 Value::Str(s) => s.parse::<f64>().map(Value::Float).map_err(|_| {
3944 format!(
3945 "{}{}{}",
3946 ion_str!("cannot convert '"),
3947 s,
3948 ion_str!("' to float")
3949 )
3950 }),
3951 _ => Err(format!(
3952 "{}{}",
3953 ion_str!("cannot convert "),
3954 args[0].type_name()
3955 )),
3956 }
3957 }),
3958 false,
3959 );
3960 env.define(
3964 ion_str!("enumerate").to_string(),
3965 Value::BuiltinFn(ion_str!("enumerate").to_string(), |args| {
3966 if args.len() != 1 {
3967 return Err(ion_str!("enumerate takes 1 argument"));
3968 }
3969 match &args[0] {
3970 Value::List(items) => Ok(Value::List(
3971 items
3972 .iter()
3973 .enumerate()
3974 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v.clone()]))
3975 .collect(),
3976 )),
3977 Value::Str(s) => Ok(Value::List(
3978 s.chars()
3979 .enumerate()
3980 .map(|(i, c)| {
3981 Value::Tuple(vec![Value::Int(i as i64), Value::Str(c.to_string())])
3982 })
3983 .collect(),
3984 )),
3985 Value::Dict(map) => Ok(Value::List(
3986 map.iter()
3987 .enumerate()
3988 .map(|(i, (k, v))| {
3989 Value::Tuple(vec![
3990 Value::Int(i as i64),
3991 Value::Tuple(vec![Value::Str(k.clone()), v.clone()]),
3992 ])
3993 })
3994 .collect(),
3995 )),
3996 _ => Err(format!(
3997 "{}{}",
3998 ion_str!("enumerate() not supported for "),
3999 args[0].type_name()
4000 )),
4001 }
4002 }),
4003 false,
4004 );
4005
4006 env.define(
4007 ion_str!("bytes").to_string(),
4008 Value::BuiltinFn(ion_str!("bytes").to_string(), |args| match args.first() {
4009 Some(Value::List(items)) => {
4010 let mut bytes = Vec::with_capacity(items.len());
4011 for item in items {
4012 let n = item
4013 .as_int()
4014 .ok_or_else(|| ion_str!("bytes() list items must be ints"))?;
4015 if !(0..=255).contains(&n) {
4016 return Err(format!("{}{}", ion_str!("byte value out of range: "), n));
4017 }
4018 bytes.push(n as u8);
4019 }
4020 Ok(Value::Bytes(bytes))
4021 }
4022 Some(Value::Str(s)) => Ok(Value::Bytes(s.as_bytes().to_vec())),
4023 Some(Value::Int(n)) if *n >= 0 && *n <= 10_000_000 => {
4024 Ok(Value::Bytes(vec![0u8; *n as usize]))
4025 }
4026 Some(Value::Int(n)) => Err(format!(
4027 "bytes(n): size {} is out of range (0..10_000_000)",
4028 n
4029 )),
4030 None => Ok(Value::Bytes(Vec::new())),
4031 _ => Err(format!(
4032 "{}{}",
4033 ion_str!("bytes() not supported for "),
4034 args[0].type_name()
4035 )),
4036 }),
4037 false,
4038 );
4039 env.define(
4040 ion_str!("bytes_from_hex").to_string(),
4041 Value::BuiltinFn(ion_str!("bytes_from_hex").to_string(), |args| {
4042 if args.len() != 1 {
4043 return Err(ion_str!("bytes_from_hex takes 1 argument"));
4044 }
4045 let s = args[0]
4046 .as_str()
4047 .ok_or_else(|| ion_str!("bytes_from_hex requires a string"))?;
4048 if !s.is_ascii() {
4049 return Err(ion_str!("bytes_from_hex requires an ASCII hex string"));
4050 }
4051 if s.len() % 2 != 0 {
4052 return Err(ion_str!("hex string must have even length").to_string());
4053 }
4054 let mut bytes = Vec::with_capacity(s.len() / 2);
4055 for i in (0..s.len()).step_by(2) {
4056 let byte = u8::from_str_radix(&s[i..i + 2], 16)
4057 .map_err(|_| format!("{}{}", ion_str!("invalid hex: "), &s[i..i + 2]))?;
4058 bytes.push(byte);
4059 }
4060 Ok(Value::Bytes(bytes))
4061 }),
4062 false,
4063 );
4064
4065 env.define(
4066 ion_str!("assert").to_string(),
4067 Value::BuiltinFn(ion_str!("assert").to_string(), |args| {
4068 if args.is_empty() {
4069 return Err(ion_str!("assert requires at least 1 argument").to_string());
4070 }
4071 let condition = match &args[0] {
4072 Value::Bool(b) => *b,
4073 _ => {
4074 return Err(format!(
4075 "{}{}",
4076 ion_str!("assert condition must be bool, got "),
4077 args[0].type_name()
4078 ))
4079 }
4080 };
4081 if !condition {
4082 let msg = if args.len() > 1 {
4083 args[1].to_string()
4084 } else {
4085 ion_str!("assertion failed").to_string()
4086 };
4087 return Err(msg);
4088 }
4089 Ok(Value::Unit)
4090 }),
4091 false,
4092 );
4093
4094 env.define(
4095 ion_str!("assert_eq").to_string(),
4096 Value::BuiltinFn(ion_str!("assert_eq").to_string(), |args| {
4097 if args.len() < 2 {
4098 return Err(ion_str!("assert_eq requires at least 2 arguments").to_string());
4099 }
4100 if args[0] != args[1] {
4101 let msg = if args.len() > 2 {
4102 format!(
4103 "{}{}{}{}{}",
4104 args[2],
4105 ion_str!(": expected "),
4106 args[0],
4107 ion_str!(", got "),
4108 args[1]
4109 )
4110 } else {
4111 format!(
4112 "{}{}{}{}",
4113 ion_str!("assertion failed: expected "),
4114 args[0],
4115 ion_str!(", got "),
4116 args[1]
4117 )
4118 };
4119 return Err(msg);
4120 }
4121 Ok(Value::Unit)
4122 }),
4123 false,
4124 );
4125
4126 #[cfg(feature = "concurrency")]
4127 {
4128 env.define(
4129 ion_str!("sleep").to_string(),
4130 Value::BuiltinFn(ion_str!("sleep").to_string(), |args| {
4131 let ms = args
4132 .first()
4133 .and_then(|v| v.as_int())
4134 .ok_or(ion_str!("sleep requires int (ms)"))?;
4135 crate::async_rt::sleep(std::time::Duration::from_millis(ms as u64));
4136 Ok(Value::Unit)
4137 }),
4138 false,
4139 );
4140 env.define(
4141 ion_str!("timeout").to_string(),
4142 Value::BuiltinFn(ion_str!("timeout").to_string(), |_args| {
4143 Err(ion_str!("timeout: internal error (should not reach here)"))
4145 }),
4146 false,
4147 );
4148 env.define(
4149 ion_str!("channel").to_string(),
4150 Value::BuiltinFn(ion_str!("channel").to_string(), |args| {
4151 let buffer = if args.is_empty() {
4152 16
4153 } else {
4154 args[0]
4155 .as_int()
4156 .ok_or(ion_str!("channel buffer size must be int"))?
4157 as usize
4158 };
4159 let (tx, rx) = crate::async_rt::create_channel(buffer);
4160 Ok(Value::Tuple(vec![tx, rx]))
4161 }),
4162 false,
4163 );
4164 }
4165
4166 crate::stdlib::register_stdlib_with_output(env, output);
4168}