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