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