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