1use indexmap::IndexMap;
4
5use crate::bytecode::{Chunk, Op};
6use crate::env::Env;
7use crate::error::IonError;
8use crate::host_types::TypeRegistry;
9use crate::value::Value;
10
11#[derive(Debug, Clone)]
13struct LocalSlot {
14 value: Value,
15 mutable: bool,
16}
17
18#[derive(Debug, Clone)]
20struct ExceptionHandler {
21 catch_ip: usize,
23 stack_depth: usize,
25 local_frames_depth: usize,
27 locals_depth: usize,
29}
30
31pub struct Vm {
33 stack: Vec<Value>,
35 env: Env,
37 ip: usize,
39 iterators: Vec<Box<dyn Iterator<Item = Value>>>,
41 fn_cache: std::collections::HashMap<u64, crate::bytecode::Chunk>,
43 pending_tail_call: Option<(Value, Vec<Value>)>,
45 locals: Vec<LocalSlot>,
47 local_frames: Vec<usize>,
49 locals_base: usize,
51 types: TypeRegistry,
53 exception_handlers: Vec<ExceptionHandler>,
55}
56
57impl Default for Vm {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl Vm {
64 pub fn new() -> Self {
65 Self {
66 stack: Vec::with_capacity(256),
67 env: Env::new(),
68 ip: 0,
69 iterators: Vec::new(),
70 fn_cache: std::collections::HashMap::new(),
71 pending_tail_call: None,
72 locals: Vec::with_capacity(64),
73 local_frames: Vec::with_capacity(16),
74 locals_base: 0,
75 types: TypeRegistry::default(),
76 exception_handlers: Vec::new(),
77 }
78 }
79
80 pub fn with_env(env: Env) -> Self {
82 Self {
83 stack: Vec::with_capacity(256),
84 env,
85 ip: 0,
86 iterators: Vec::new(),
87 fn_cache: std::collections::HashMap::new(),
88 pending_tail_call: None,
89 locals: Vec::with_capacity(64),
90 local_frames: Vec::with_capacity(16),
91 locals_base: 0,
92 types: TypeRegistry::default(),
93 exception_handlers: Vec::new(),
94 }
95 }
96
97 pub fn set_types(&mut self, types: TypeRegistry) {
99 self.types = types;
100 }
101
102 pub fn env(&self) -> &Env {
104 &self.env
105 }
106
107 pub fn env_mut(&mut self) -> &mut Env {
109 &mut self.env
110 }
111
112 pub fn preload_fn_chunks(&mut self, chunks: crate::value::FnChunkCache) {
114 self.fn_cache.extend(chunks);
115 }
116
117 pub fn execute(&mut self, chunk: &Chunk) -> Result<Value, IonError> {
119 self.ip = 0;
120 self.stack.clear();
121 match self.run_chunk(chunk) {
122 Ok(v) => Ok(v),
123 Err(e) if e.kind == crate::error::ErrorKind::PropagatedErr => {
124 Ok(Value::Result(Err(Box::new(Value::Str(e.message.clone())))))
125 }
126 Err(e) if e.kind == crate::error::ErrorKind::PropagatedNone => Ok(Value::Option(None)),
127 Err(e) => Err(e),
128 }
129 }
130
131 fn run_chunk(&mut self, chunk: &Chunk) -> Result<Value, IonError> {
133 while self.ip < chunk.code.len() {
134 let op_byte = chunk.code[self.ip];
135 let line = chunk.lines[self.ip];
136 let col = chunk.cols[self.ip];
137 self.ip += 1;
138
139 let op = match self.decode_op(op_byte, line, col) {
140 Ok(op) => op,
141 Err(e) => {
142 if let Some(handler) = self.exception_handlers.pop() {
143 self.stack.truncate(handler.stack_depth);
144 self.locals.truncate(handler.locals_depth);
145 self.local_frames.truncate(handler.local_frames_depth);
146 self.stack.push(Value::Str(e.message.clone()));
147 self.ip = handler.catch_ip;
148 continue;
149 }
150 return Err(e);
151 }
152 };
153
154 match op {
156 Op::TryBegin => {
157 let offset = chunk.read_u16(self.ip) as usize;
158 self.ip += 2;
159 let catch_ip = self.ip + offset;
160 self.exception_handlers.push(ExceptionHandler {
161 catch_ip,
162 stack_depth: self.stack.len(),
163 local_frames_depth: self.local_frames.len(),
164 locals_depth: self.locals.len(),
165 });
166 continue;
167 }
168 Op::TryEnd => {
169 let offset = chunk.read_u16(self.ip) as usize;
170 self.ip += 2;
171 self.exception_handlers.pop();
172 self.ip += offset; continue;
174 }
175 _ => {}
176 }
177
178 match self.dispatch_instruction(op, chunk, line, col) {
179 Ok(Some(val)) => return Ok(val),
180 Ok(None) => {}
181 Err(e) => {
182 if e.kind != crate::error::ErrorKind::PropagatedErr
184 && e.kind != crate::error::ErrorKind::PropagatedNone
185 {
186 if let Some(handler) = self.exception_handlers.pop() {
187 self.stack.truncate(handler.stack_depth);
188 self.locals.truncate(handler.locals_depth);
189 self.local_frames.truncate(handler.local_frames_depth);
190 self.stack.push(Value::Str(e.message.clone()));
191 self.ip = handler.catch_ip;
192 continue;
193 }
194 }
195 return Err(e);
196 }
197 }
198 }
199 Ok(self.stack.pop().unwrap_or(Value::Unit))
200 }
201
202 fn dispatch_instruction(
204 &mut self,
205 op: Op,
206 chunk: &Chunk,
207 line: usize,
208 col: usize,
209 ) -> Result<Option<Value>, IonError> {
210 match op {
211 Op::Constant => {
212 let idx = chunk.read_u16(self.ip) as usize;
213 self.ip += 2;
214 let val = chunk.constants[idx].clone();
215 self.stack.push(val);
216 }
217
218 Op::True => self.stack.push(Value::Bool(true)),
219 Op::False => self.stack.push(Value::Bool(false)),
220 Op::Unit => self.stack.push(Value::Unit),
221 Op::None => self.stack.push(Value::Option(None)),
222
223 Op::Add => {
225 let b = self.pop(line, col)?;
226 let a = self.pop(line, col)?;
227 self.stack.push(self.op_add(a, b, line, col)?);
228 }
229 Op::Sub => {
230 let b = self.pop(line, col)?;
231 let a = self.pop(line, col)?;
232 self.stack.push(self.op_sub(a, b, line, col)?);
233 }
234 Op::Mul => {
235 let b = self.pop(line, col)?;
236 let a = self.pop(line, col)?;
237 self.stack.push(self.op_mul(a, b, line, col)?);
238 }
239 Op::Div => {
240 let b = self.pop(line, col)?;
241 let a = self.pop(line, col)?;
242 self.stack.push(self.op_div(a, b, line, col)?);
243 }
244 Op::Mod => {
245 let b = self.pop(line, col)?;
246 let a = self.pop(line, col)?;
247 self.stack.push(self.op_mod(a, b, line, col)?);
248 }
249 Op::Neg => {
250 let val = self.pop(line, col)?;
251 self.stack.push(self.op_neg(val, line, col)?);
252 }
253
254 Op::BitAnd => {
256 let b = self.pop(line, col)?;
257 let a = self.pop(line, col)?;
258 match (a, b) {
259 (Value::Int(x), Value::Int(y)) => self.stack.push(Value::Int(x & y)),
260 (a, b) => {
261 return Err(IonError::type_err(
262 format!(
263 "{}{} and {}",
264 ion_str!("'&' expects int, got "),
265 a.type_name(),
266 b.type_name()
267 ),
268 line,
269 col,
270 ))
271 }
272 }
273 }
274 Op::BitOr => {
275 let b = self.pop(line, col)?;
276 let a = self.pop(line, col)?;
277 match (a, b) {
278 (Value::Int(x), Value::Int(y)) => self.stack.push(Value::Int(x | y)),
279 (a, b) => {
280 return Err(IonError::type_err(
281 format!(
282 "{}{} and {}",
283 ion_str!("'|' expects int, got "),
284 a.type_name(),
285 b.type_name()
286 ),
287 line,
288 col,
289 ))
290 }
291 }
292 }
293 Op::BitXor => {
294 let b = self.pop(line, col)?;
295 let a = self.pop(line, col)?;
296 match (a, b) {
297 (Value::Int(x), Value::Int(y)) => self.stack.push(Value::Int(x ^ y)),
298 (a, b) => {
299 return Err(IonError::type_err(
300 format!(
301 "{}{} and {}",
302 ion_str!("'^' expects int, got "),
303 a.type_name(),
304 b.type_name()
305 ),
306 line,
307 col,
308 ))
309 }
310 }
311 }
312 Op::Shl => {
313 let b = self.pop(line, col)?;
314 let a = self.pop(line, col)?;
315 match (a, b) {
316 (Value::Int(x), Value::Int(y)) if (0..64).contains(&y) => {
317 self.stack.push(Value::Int(x << y))
318 }
319 (Value::Int(_), Value::Int(y)) => {
320 return Err(IonError::runtime(
321 format!("shift count {} is out of range 0..64", y),
322 line,
323 col,
324 ))
325 }
326 (a, b) => {
327 return Err(IonError::type_err(
328 format!(
329 "{}{} and {}",
330 ion_str!("'<<' expects int, got "),
331 a.type_name(),
332 b.type_name()
333 ),
334 line,
335 col,
336 ))
337 }
338 }
339 }
340 Op::Shr => {
341 let b = self.pop(line, col)?;
342 let a = self.pop(line, col)?;
343 match (a, b) {
344 (Value::Int(x), Value::Int(y)) if (0..64).contains(&y) => {
345 self.stack.push(Value::Int(x >> y))
346 }
347 (Value::Int(_), Value::Int(y)) => {
348 return Err(IonError::runtime(
349 format!("shift count {} is out of range 0..64", y),
350 line,
351 col,
352 ))
353 }
354 (a, b) => {
355 return Err(IonError::type_err(
356 format!(
357 "{}{} and {}",
358 ion_str!("'>>' expects int, got "),
359 a.type_name(),
360 b.type_name()
361 ),
362 line,
363 col,
364 ))
365 }
366 }
367 }
368
369 Op::Eq => {
371 let b = self.pop(line, col)?;
372 let a = self.pop(line, col)?;
373 self.stack.push(Value::Bool(a == b));
374 }
375 Op::NotEq => {
376 let b = self.pop(line, col)?;
377 let a = self.pop(line, col)?;
378 self.stack.push(Value::Bool(a != b));
379 }
380 Op::Lt => {
381 let b = self.pop(line, col)?;
382 let a = self.pop(line, col)?;
383 self.stack
384 .push(Value::Bool(self.compare_lt(&a, &b, line, col)?));
385 }
386 Op::Gt => {
387 let b = self.pop(line, col)?;
388 let a = self.pop(line, col)?;
389 self.stack
390 .push(Value::Bool(self.compare_lt(&b, &a, line, col)?));
391 }
392 Op::LtEq => {
393 let b = self.pop(line, col)?;
394 let a = self.pop(line, col)?;
395 self.stack
396 .push(Value::Bool(!self.compare_lt(&b, &a, line, col)?));
397 }
398 Op::GtEq => {
399 let b = self.pop(line, col)?;
400 let a = self.pop(line, col)?;
401 self.stack
402 .push(Value::Bool(!self.compare_lt(&a, &b, line, col)?));
403 }
404
405 Op::Not => {
407 let val = self.pop(line, col)?;
408 self.stack.push(Value::Bool(!val.is_truthy()));
409 }
410 Op::And => {
411 let offset = chunk.read_u16(self.ip) as usize;
412 self.ip += 2;
413 if !self.is_top_truthy(line, col)? {
414 self.ip += offset; }
416 }
418 Op::Or => {
419 let offset = chunk.read_u16(self.ip) as usize;
420 self.ip += 2;
421 if self.is_top_truthy(line, col)? {
422 self.ip += offset; }
424 }
425
426 Op::DefineLocal => {
428 let name_idx = chunk.read_u16(self.ip) as usize;
429 self.ip += 2;
430 let mutable = chunk.read_u8(self.ip) != 0;
431 self.ip += 1;
432 let sym = self.const_to_sym(&chunk.constants[name_idx], line, col)?;
433 let val = self.pop(line, col)?;
434 self.env.define_sym(sym, val, mutable);
435 }
436 Op::GetLocal => {
437 let name_idx = chunk.read_u16(self.ip) as usize;
438 self.ip += 2;
439 let sym = self.const_to_sym(&chunk.constants[name_idx], line, col)?;
440 let val = self.env.get_sym(sym).cloned().ok_or_else(|| {
441 let name = self.env.resolve(sym);
442 IonError::name(
443 format!("{}{}", ion_str!("undefined variable: "), name),
444 line,
445 col,
446 )
447 })?;
448 self.stack.push(val);
449 }
450 Op::SetLocal => {
451 let name_idx = chunk.read_u16(self.ip) as usize;
452 self.ip += 2;
453 let sym = self.const_to_sym(&chunk.constants[name_idx], line, col)?;
454 let val = self.pop(line, col)?;
455 self.env
456 .set_sym(sym, val.clone())
457 .map_err(|e| IonError::runtime(e, line, col))?;
458 self.stack.push(val); }
460 Op::GetGlobal => {
461 let name_idx = chunk.read_u16(self.ip) as usize;
462 self.ip += 2;
463 let sym = self.const_to_sym(&chunk.constants[name_idx], line, col)?;
464 let val = self.env.get_sym(sym).cloned().ok_or_else(|| {
465 let name = self.env.resolve(sym);
466 IonError::name(
467 format!("{}{}", ion_str!("undefined variable: "), name),
468 line,
469 col,
470 )
471 })?;
472 self.stack.push(val);
473 }
474 Op::SetGlobal => {
475 let name_idx = chunk.read_u16(self.ip) as usize;
476 self.ip += 2;
477 let sym = self.const_to_sym(&chunk.constants[name_idx], line, col)?;
478 let val = self.pop(line, col)?;
479 self.env
480 .set_sym(sym, val.clone())
481 .map_err(|e| IonError::runtime(e, line, col))?;
482 self.stack.push(val);
483 }
484
485 Op::DefineLocalSlot => {
487 let mutable = chunk.read_u8(self.ip) != 0;
488 self.ip += 1;
489 let val = self.pop(line, col)?;
490 self.locals.push(LocalSlot {
491 value: val,
492 mutable,
493 });
494 }
495 Op::GetLocalSlot => {
496 let slot = self.locals_base + chunk.read_u16(self.ip) as usize;
497 self.ip += 2;
498 let val = self.locals[slot].value.clone();
499 self.stack.push(val);
500 }
501 Op::SetLocalSlot => {
502 let slot = self.locals_base + chunk.read_u16(self.ip) as usize;
503 self.ip += 2;
504 let val = self.pop(line, col)?;
505 if !self.locals[slot].mutable {
506 return Err(IonError::runtime(
507 ion_str!("cannot assign to immutable variable"),
508 line,
509 col,
510 ));
511 }
512 self.locals[slot].value = val.clone();
513 self.stack.push(val); }
515
516 Op::Jump => {
518 let offset = chunk.read_u16(self.ip) as usize;
519 self.ip += 2;
520 self.ip += offset;
521 }
522 Op::JumpIfFalse => {
523 let offset = chunk.read_u16(self.ip) as usize;
524 self.ip += 2;
525 if !self.is_top_truthy(line, col)? {
526 self.ip += offset;
527 }
528 }
529 Op::Loop => {
530 let offset = chunk.read_u16(self.ip) as usize;
531 self.ip += 2;
532 self.ip -= offset;
533 }
534
535 Op::Call => {
537 let arg_count = chunk.read_u8(self.ip) as usize;
538 self.ip += 1;
539 self.call_function(arg_count, line, col)?;
540 }
541 Op::CallNamed => {
542 let arg_count = chunk.read_u8(self.ip) as usize;
543 self.ip += 1;
544 let named_count = chunk.read_u8(self.ip) as usize;
545 self.ip += 1;
546 let mut named_map: Vec<(usize, String)> = Vec::with_capacity(named_count);
548 for _ in 0..named_count {
549 let pos = chunk.read_u8(self.ip) as usize;
550 self.ip += 1;
551 let name_idx = chunk.read_u16(self.ip) as usize;
552 self.ip += 2;
553 if let Value::Str(name) = &chunk.constants[name_idx] {
554 named_map.push((pos, name.clone()));
555 }
556 }
557 self.call_function_named(arg_count, &named_map, line, col)?;
558 }
559 Op::TailCall => {
560 let arg_count = chunk.read_u8(self.ip) as usize;
561 self.ip += 1;
562 let args_start = self.stack.len() - arg_count;
564 let func_idx = args_start - 1;
565 let func = self.stack[func_idx].clone();
566 let args: Vec<Value> = self.stack[args_start..].to_vec();
567 self.stack.truncate(func_idx);
568 self.pending_tail_call = Some((func, args));
569 return Ok(Some(Value::Unit)); }
571 Op::Return => {
572 let val = if self.stack.is_empty() {
574 Value::Unit
575 } else {
576 self.pop(line, col)?
577 };
578 return Ok(Some(val));
579 }
580
581 Op::Pop => {
583 self.pop(line, col)?;
584 }
585 Op::Dup => {
586 let val = self.peek(line, col)?;
587 self.stack.push(val);
588 }
589
590 Op::BuildList => {
592 let count = chunk.read_u16(self.ip) as usize;
593 self.ip += 2;
594 let start = self.stack.len() - count;
595 let items: Vec<Value> = self.stack.drain(start..).collect();
596 self.stack.push(Value::List(items));
597 }
598 Op::BuildTuple => {
599 let count = chunk.read_u16(self.ip) as usize;
600 self.ip += 2;
601 let start = self.stack.len() - count;
602 let items: Vec<Value> = self.stack.drain(start..).collect();
603 self.stack.push(Value::Tuple(items));
604 }
605 Op::BuildDict => {
606 let count = chunk.read_u16(self.ip) as usize;
607 self.ip += 2;
608 let mut map = IndexMap::new();
609 let start = self.stack.len() - count * 2;
611 let items: Vec<Value> = self.stack.drain(start..).collect();
612 for pair in items.chunks(2) {
613 let key = match &pair[0] {
614 Value::Str(s) => s.clone(),
615 other => other.to_string(),
616 };
617 map.insert(key, pair[1].clone());
618 }
619 self.stack.push(Value::Dict(map));
620 }
621
622 Op::GetField => {
624 let field_idx = chunk.read_u16(self.ip) as usize;
625 self.ip += 2;
626 let field = self.const_as_str(&chunk.constants[field_idx], line, col)?;
627 let obj = self.pop(line, col)?;
628 self.stack.push(self.get_field(obj, &field, line, col)?);
629 }
630 Op::GetIndex => {
631 let index = self.pop(line, col)?;
632 let obj = self.pop(line, col)?;
633 self.stack.push(self.get_index(obj, index, line, col)?);
634 }
635 Op::SetField => {
636 let field_idx = chunk.read_u16(self.ip) as usize;
637 self.ip += 2;
638 let field = self.const_as_str(&chunk.constants[field_idx], line, col)?;
639 let value = self.pop(line, col)?;
640 let obj = self.pop(line, col)?;
641 let result = self.set_field(obj, &field, value, line, col)?;
642 self.stack.push(result);
643 }
644 Op::SetIndex => {
645 let value = self.pop(line, col)?;
646 let index = self.pop(line, col)?;
647 let obj = self.pop(line, col)?;
648 let result = self.set_index(obj, index, value, line, col)?;
649 self.stack.push(result);
650 }
651 Op::MethodCall => {
652 let method_idx = chunk.read_u16(self.ip) as usize;
653 self.ip += 2;
654 let arg_count = chunk.read_u8(self.ip) as usize;
655 self.ip += 1;
656 let method = self.const_as_str(&chunk.constants[method_idx], line, col)?;
657 let start = self.stack.len() - arg_count;
659 let args: Vec<Value> = self.stack.drain(start..).collect();
660 let receiver = self.pop(line, col)?;
661 let result = self.call_method(receiver, &method, &args, line, col)?;
662 self.stack.push(result);
663 }
664
665 Op::Closure => {
667 let fn_idx = chunk.read_u16(self.ip) as usize;
668 self.ip += 2;
669 let val = chunk.constants[fn_idx].clone();
670 if let Value::Fn(mut f) = val {
672 f.captures = self.env.capture();
673 self.stack.push(Value::Fn(f));
674 } else {
675 self.stack.push(val);
676 }
677 }
678
679 Op::WrapSome => {
681 let val = self.pop(line, col)?;
682 self.stack.push(Value::Option(Some(Box::new(val))));
683 }
684 Op::WrapOk => {
685 let val = self.pop(line, col)?;
686 self.stack.push(Value::Result(Ok(Box::new(val))));
687 }
688 Op::WrapErr => {
689 let val = self.pop(line, col)?;
690 self.stack.push(Value::Result(Err(Box::new(val))));
691 }
692 Op::Try => {
693 let val = self.pop(line, col)?;
694 match val {
695 Value::Option(Some(v)) => self.stack.push(*v),
696 Value::Option(None) => {
697 return Err(IonError::propagated_none(line, 0));
698 }
699 Value::Result(Ok(v)) => self.stack.push(*v),
700 Value::Result(Err(e)) => {
701 return Err(IonError::propagated_err(e.to_string(), line, col));
702 }
703 other => {
704 return Err(IonError::type_err(
705 format!(
706 "{}{}",
707 ion_str!("? operator requires Option or Result, got "),
708 other.type_name()
709 ),
710 line,
711 col,
712 ));
713 }
714 }
715 }
716
717 Op::PushScope => {
719 self.env.push_scope();
720 self.local_frames.push(self.locals.len());
721 }
722 Op::PopScope => {
723 self.env.pop_scope();
724 if let Some(base) = self.local_frames.pop() {
725 self.locals.truncate(base);
726 }
727 }
728
729 Op::BuildFString => {
731 let count = chunk.read_u16(self.ip) as usize;
732 self.ip += 2;
733 let start = self.stack.len() - count;
734 let parts: Vec<Value> = self.stack.drain(start..).collect();
735 let mut s = String::with_capacity(count * 8);
736 for part in &parts {
737 use std::fmt::Write;
738 let _ = write!(s, "{}", part);
739 }
740 self.stack.push(Value::Str(s));
741 }
742
743 Op::Pipe => {
745 let _arg_count = chunk.read_u8(self.ip);
746 self.ip += 1;
747 return Err(IonError::runtime(
749 ion_str!("pipe opcode should not be executed directly"),
750 line,
751 col,
752 ));
753 }
754
755 Op::MatchBegin => {
757 let kind = chunk.read_u8(self.ip);
759 self.ip += 1;
760 let val = self.pop(line, col)?;
761 let result = match kind {
762 1 => matches!(val, Value::Option(Some(_))),
763 2 => matches!(val, Value::Result(Ok(_))),
764 3 => matches!(val, Value::Result(Err(_))),
765 4 => {
766 let expected_len = chunk.read_u8(self.ip) as usize;
767 self.ip += 1;
768 match &val {
769 Value::Tuple(items) => items.len() == expected_len,
770 _ => false,
771 }
772 }
773 5 => {
774 let min_len = chunk.read_u8(self.ip) as usize;
775 self.ip += 1;
776 let has_rest = chunk.read_u8(self.ip) != 0;
777 self.ip += 1;
778 match &val {
779 Value::List(items) => {
780 if has_rest {
781 items.len() >= min_len
782 } else {
783 items.len() == min_len
784 }
785 }
786 _ => false,
787 }
788 }
789 _ => false,
790 };
791 self.stack.push(val);
793 self.stack.push(Value::Bool(result));
794 }
795 Op::MatchArm => {
796 let kind = chunk.read_u8(self.ip);
798 self.ip += 1;
799 match kind {
800 1 => {
801 let val = self.pop(line, col)?;
803 match val {
804 Value::Option(Some(v)) => self.stack.push(*v),
805 other => self.stack.push(other),
806 }
807 }
808 2 => {
809 let val = self.pop(line, col)?;
810 match val {
811 Value::Result(Ok(v)) => self.stack.push(*v),
812 other => self.stack.push(other),
813 }
814 }
815 3 => {
816 let val = self.pop(line, col)?;
817 match val {
818 Value::Result(Err(v)) => self.stack.push(*v),
819 other => self.stack.push(other),
820 }
821 }
822 4 | 5 => {
823 let idx = chunk.read_u8(self.ip) as usize;
825 self.ip += 1;
826 let val = self.peek(line, col)?;
827 match val {
828 Value::Tuple(items) | Value::List(items) => {
829 self.stack
830 .push(items.get(idx).cloned().unwrap_or(Value::Unit));
831 }
832 _ => self.stack.push(Value::Unit),
833 }
834 }
835 _ => {}
836 }
837 }
838 Op::MatchEnd => {
839 return Err(IonError::runtime(
840 ion_str!("non-exhaustive match").to_string(),
841 line,
842 col,
843 ));
844 }
845
846 Op::BuildRange => {
848 let inclusive = chunk.read_u8(self.ip) != 0;
849 self.ip += 1;
850 let end = self.pop(line, col)?;
851 let start = self.pop(line, col)?;
852 let s = start.as_int().ok_or_else(|| {
853 IonError::type_err(ion_str!("range start must be int"), line, col)
854 })?;
855 let e = end.as_int().ok_or_else(|| {
856 IonError::type_err(ion_str!("range end must be int"), line, col)
857 })?;
858 self.stack.push(Value::Range {
859 start: s,
860 end: e,
861 inclusive,
862 });
863 }
864
865 Op::ConstructStruct => {
867 let type_name_idx = chunk.read_u16(self.ip) as usize;
868 self.ip += 2;
869 let raw_count = chunk.read_u16(self.ip) as usize;
870 self.ip += 2;
871 let type_name = match &chunk.constants[type_name_idx] {
872 Value::Str(s) => s.clone(),
873 _ => return Err(IonError::runtime(ion_str!("invalid type name"), line, col)),
874 };
875 let has_spread = raw_count & 0x8000 != 0;
876 let field_count = raw_count & 0x7FFF;
877 let mut fields = IndexMap::new();
878 if has_spread {
879 let override_start = self.stack.len() - field_count * 2;
882 let overrides: Vec<Value> = self.stack.drain(override_start..).collect();
883 let spread_val = self.pop(line, col)?;
885 match spread_val {
886 Value::HostStruct { fields: sf, .. } => {
887 for (k, v) in sf {
888 fields.insert(k, v);
889 }
890 }
891 _ => {
892 return Err(IonError::type_err(
893 ion_str!("spread in struct constructor requires a struct"),
894 line,
895 col,
896 ))
897 }
898 }
899 for pair in overrides.chunks(2) {
901 let fname = match &pair[0] {
902 Value::Str(s) => s.clone(),
903 _ => {
904 return Err(IonError::runtime(
905 ion_str!("invalid field name"),
906 line,
907 col,
908 ))
909 }
910 };
911 fields.insert(fname, pair[1].clone());
912 }
913 } else {
914 let start = self.stack.len() - field_count * 2;
916 let items: Vec<Value> = self.stack.drain(start..).collect();
917 for pair in items.chunks(2) {
918 let fname = match &pair[0] {
919 Value::Str(s) => s.clone(),
920 _ => {
921 return Err(IonError::runtime(
922 ion_str!("invalid field name"),
923 line,
924 col,
925 ))
926 }
927 };
928 fields.insert(fname, pair[1].clone());
929 }
930 }
931 match self.types.construct_struct(&type_name, fields) {
932 Ok(val) => self.stack.push(val),
933 Err(msg) => return Err(IonError::runtime(msg, line, col)),
934 }
935 }
936 Op::ConstructEnum => {
937 let enum_name_idx = chunk.read_u16(self.ip) as usize;
938 self.ip += 2;
939 let variant_name_idx = chunk.read_u16(self.ip) as usize;
940 self.ip += 2;
941 let arg_count = chunk.read_u8(self.ip) as usize;
942 self.ip += 1;
943 let enum_name = match &chunk.constants[enum_name_idx] {
944 Value::Str(s) => s.clone(),
945 _ => return Err(IonError::runtime(ion_str!("invalid enum name"), line, col)),
946 };
947 let variant_name = match &chunk.constants[variant_name_idx] {
948 Value::Str(s) => s.clone(),
949 _ => {
950 return Err(IonError::runtime(
951 ion_str!("invalid variant name"),
952 line,
953 col,
954 ))
955 }
956 };
957 let start = self.stack.len() - arg_count;
958 let args: Vec<Value> = self.stack.drain(start..).collect();
959 match self.types.construct_enum(&enum_name, &variant_name, args) {
960 Ok(val) => self.stack.push(val),
961 Err(msg) => return Err(IonError::runtime(msg, line, col)),
962 }
963 }
964
965 Op::IterInit => {
967 let val = self.pop(line, col)?;
968 let iter: Box<dyn Iterator<Item = Value>> = match val {
969 Value::List(items) => Box::new(items.into_iter()),
970 Value::Set(items) => Box::new(items.into_iter()),
971 Value::Tuple(items) => Box::new(items.into_iter()),
972 Value::Dict(map) => Box::new(
973 map.into_iter()
974 .map(|(k, v)| Value::Tuple(vec![Value::Str(k), v])),
975 ),
976 Value::Str(s) => {
977 let chars: Vec<Value> =
978 s.chars().map(|c| Value::Str(c.to_string())).collect();
979 Box::new(chars.into_iter())
980 }
981 Value::Bytes(bytes) => {
982 let vals: Vec<Value> =
983 bytes.into_iter().map(|b| Value::Int(b as i64)).collect();
984 Box::new(vals.into_iter())
985 }
986 Value::Range {
987 start,
988 end,
989 inclusive,
990 } => {
991 if inclusive {
992 Box::new((start..=end).map(Value::Int))
993 } else {
994 if end > start {
996 Box::new((start..=(end - 1)).map(Value::Int))
997 } else {
998 Box::new(std::iter::empty())
999 }
1000 }
1001 }
1002 other => {
1003 return Err(IonError::type_err(
1004 format!("{}{}", ion_str!("cannot iterate over "), other.type_name()),
1005 line,
1006 col,
1007 ));
1008 }
1009 };
1010 self.iterators.push(iter);
1011 self.stack.push(Value::Unit);
1013 }
1014 Op::IterNext => {
1015 let offset = chunk.read_u16(self.ip) as usize;
1016 self.ip += 2;
1017 self.pop(line, col)?;
1019 let iter = self
1020 .iterators
1021 .last_mut()
1022 .ok_or_else(|| IonError::runtime(ion_str!("no active iterator"), line, col))?;
1023 match iter.next() {
1024 Some(val) => {
1025 self.stack.push(val);
1026 }
1027 None => {
1028 self.iterators.pop();
1029 self.stack.push(Value::Unit); self.ip += offset;
1031 }
1032 }
1033 }
1034 Op::IterDrop => {
1035 self.iterators.pop();
1036 }
1037 Op::ListAppend => {
1038 let item = self.pop(line, col)?;
1041 let mut found = false;
1049 for i in (0..self.stack.len()).rev() {
1050 if let Value::List(_) = &self.stack[i] {
1051 if let Value::List(ref mut items) = self.stack[i] {
1052 items.push(item.clone());
1053 }
1054 found = true;
1055 break;
1056 }
1057 }
1058 if !found {
1059 return Err(IonError::runtime(
1060 ion_str!("ListAppend: no list on stack"),
1061 line,
1062 col,
1063 ));
1064 }
1065 }
1066 Op::ListExtend => {
1067 let source = self.pop(line, col)?;
1069 match source {
1070 Value::List(other) => {
1071 let mut found = false;
1072 for i in (0..self.stack.len()).rev() {
1073 if let Value::List(ref mut items) = self.stack[i] {
1074 items.extend(other);
1075 found = true;
1076 break;
1077 }
1078 }
1079 if !found {
1080 return Err(IonError::runtime(
1081 ion_str!("ListExtend: no list on stack"),
1082 line,
1083 col,
1084 ));
1085 }
1086 }
1087 other => {
1088 return Err(IonError::type_err(
1089 format!(
1090 "{}{}",
1091 ion_str!("spread requires a list, got "),
1092 other.type_name()
1093 ),
1094 line,
1095 col,
1096 ));
1097 }
1098 }
1099 }
1100 Op::DictInsert => {
1101 let value = self.pop(line, col)?;
1103 let key = self.pop(line, col)?;
1104 let key_str = match key {
1105 Value::Str(s) => s,
1106 other => other.to_string(),
1107 };
1108 let mut found = false;
1109 for i in (0..self.stack.len()).rev() {
1110 if let Value::Dict(_) = &self.stack[i] {
1111 if let Value::Dict(ref mut map) = self.stack[i] {
1112 map.insert(key_str.clone(), value.clone());
1113 }
1114 found = true;
1115 break;
1116 }
1117 }
1118 if !found {
1119 return Err(IonError::runtime(
1120 ion_str!("DictInsert: no dict on stack"),
1121 line,
1122 col,
1123 ));
1124 }
1125 }
1126 Op::DictMerge => {
1127 let source = self.pop(line, col)?;
1129 match source {
1130 Value::Dict(other) => {
1131 let mut found = false;
1133 for i in (0..self.stack.len()).rev() {
1134 if let Value::Dict(ref mut map) = self.stack[i] {
1135 for (k, v) in other {
1136 map.insert(k, v);
1137 }
1138 found = true;
1139 break;
1140 }
1141 }
1142 if !found {
1143 return Err(IonError::runtime(
1144 ion_str!("DictMerge: no dict on stack"),
1145 line,
1146 col,
1147 ));
1148 }
1149 }
1150 _ => {
1151 return Err(IonError::type_err(
1152 ion_str!("spread requires a dict").to_string(),
1153 line,
1154 col,
1155 ))
1156 }
1157 }
1158 }
1159
1160 Op::CheckType => {
1162 let idx = chunk.read_u16(self.ip) as usize;
1163 self.ip += 2;
1164 let type_name = match &chunk.constants[idx] {
1165 Value::Str(s) => s.clone(),
1166 _ => unreachable!(),
1167 };
1168 let val = self.stack.last().ok_or_else(|| {
1170 IonError::runtime(ion_str!("CheckType: empty stack"), line, col)
1171 })?;
1172 let ok = match type_name.as_str() {
1173 "int" => matches!(val, Value::Int(_)),
1174 "float" => matches!(val, Value::Float(_)),
1175 "bool" => matches!(val, Value::Bool(_)),
1176 "string" => matches!(val, Value::Str(_)),
1177 "bytes" => matches!(val, Value::Bytes(_)),
1178 "list" => matches!(val, Value::List(_)),
1179 "dict" => matches!(val, Value::Dict(_)),
1180 "tuple" => matches!(val, Value::Tuple(_)),
1181 "set" => matches!(val, Value::Set(_)),
1182 "fn" => matches!(
1183 val,
1184 Value::Fn(_) | Value::BuiltinFn(_, _) | Value::BuiltinClosure(_, _)
1185 ),
1186 "cell" => matches!(val, Value::Cell(_)),
1187 "any" => true,
1188 s if s.starts_with("Option") => matches!(val, Value::Option(_)),
1189 s if s.starts_with("Result") => matches!(val, Value::Result(_)),
1190 s if s.starts_with("list<") => matches!(val, Value::List(_)),
1191 s if s.starts_with("dict<") => matches!(val, Value::Dict(_)),
1192 _ => true,
1193 };
1194 if !ok {
1195 return Err(IonError::type_err(
1196 format!(
1197 "{}{}, {}{}",
1198 ion_str!("type mismatch: expected "),
1199 type_name,
1200 ion_str!("got "),
1201 val.type_name()
1202 ),
1203 line,
1204 col,
1205 ));
1206 }
1207 }
1208 Op::Slice => {
1209 let flags = chunk.read_u8(self.ip);
1210 self.ip += 1;
1211 let has_start = flags & 1 != 0;
1212 let has_end = flags & 2 != 0;
1213 let inclusive = flags & 4 != 0;
1214 let end_val = if has_end {
1215 Some(self.pop(line, col)?)
1216 } else {
1217 None
1218 };
1219 let start_val = if has_start {
1220 Some(self.pop(line, col)?)
1221 } else {
1222 None
1223 };
1224 let obj = self.pop(line, col)?;
1225 let result = self.slice_access(obj, start_val, end_val, inclusive, line, col)?;
1226 self.stack.push(result);
1227 }
1228
1229 Op::Print => {
1231 let newline = chunk.read_u8(self.ip) != 0;
1232 self.ip += 1;
1233 let val = self.pop(line, col)?;
1234 if newline {
1235 println!("{}", val);
1236 } else {
1237 print!("{}", val);
1238 }
1239 self.stack.push(Value::Unit);
1240 }
1241
1242 Op::TryBegin | Op::TryEnd => {
1243 unreachable!()
1245 }
1246 }
1247 Ok(None)
1248 }
1249
1250 fn decode_op(&self, byte: u8, line: usize, col: usize) -> Result<Op, IonError> {
1253 if byte > Op::Print as u8 {
1254 return Err(IonError::runtime(
1255 format!("{}{}", ion_str!("invalid opcode: "), byte),
1256 line,
1257 col,
1258 ));
1259 }
1260 Ok(unsafe { std::mem::transmute::<u8, crate::bytecode::Op>(byte) })
1262 }
1263
1264 fn slice_access(
1265 &self,
1266 obj: Value,
1267 start: Option<Value>,
1268 end: Option<Value>,
1269 inclusive: bool,
1270 line: usize,
1271 col: usize,
1272 ) -> Result<Value, IonError> {
1273 let get_idx = |v: Option<Value>, default: i64| -> Result<i64, IonError> {
1274 match v {
1275 Some(Value::Int(n)) => Ok(n),
1276 None => Ok(default),
1277 Some(other) => Err(IonError::type_err(
1278 format!(
1279 "{}{}",
1280 ion_str!("slice index must be int, got "),
1281 other.type_name()
1282 ),
1283 line,
1284 col,
1285 )),
1286 }
1287 };
1288 match &obj {
1289 Value::List(items) => {
1290 let len = items.len() as i64;
1291 let s = get_idx(start, 0)?.max(0).min(len) as usize;
1292 let e_raw = get_idx(end, len)?;
1293 let e = if inclusive {
1294 (e_raw + 1).max(0).min(len) as usize
1295 } else {
1296 e_raw.max(0).min(len) as usize
1297 };
1298 Ok(Value::List(items[s..e].to_vec()))
1299 }
1300 Value::Str(string) => {
1301 let chars: Vec<char> = string.chars().collect();
1302 let len = chars.len() as i64;
1303 let s = get_idx(start, 0)?.max(0).min(len) as usize;
1304 let e_raw = get_idx(end, len)?;
1305 let e = if inclusive {
1306 (e_raw + 1).max(0).min(len) as usize
1307 } else {
1308 e_raw.max(0).min(len) as usize
1309 };
1310 Ok(Value::Str(chars[s..e].iter().collect()))
1311 }
1312 Value::Bytes(bytes) => {
1313 let len = bytes.len() as i64;
1314 let s = get_idx(start, 0)?.max(0).min(len) as usize;
1315 let e_raw = get_idx(end, len)?;
1316 let e = if inclusive {
1317 (e_raw + 1).max(0).min(len) as usize
1318 } else {
1319 e_raw.max(0).min(len) as usize
1320 };
1321 Ok(Value::Bytes(bytes[s..e].to_vec()))
1322 }
1323 _ => Err(IonError::type_err(
1324 format!("{}{}", ion_str!("cannot slice "), obj.type_name()),
1325 line,
1326 col,
1327 )),
1328 }
1329 }
1330
1331 fn pop(&mut self, line: usize, col: usize) -> Result<Value, IonError> {
1332 self.stack
1333 .pop()
1334 .ok_or_else(|| IonError::runtime(ion_str!("stack underflow"), line, col))
1335 }
1336
1337 fn peek(&self, line: usize, col: usize) -> Result<Value, IonError> {
1338 self.stack
1339 .last()
1340 .cloned()
1341 .ok_or_else(|| IonError::runtime(ion_str!("stack underflow (peek)"), line, col))
1342 }
1343
1344 fn is_top_truthy(&self, line: usize, col: usize) -> Result<bool, IonError> {
1346 self.stack
1347 .last()
1348 .map(|v| v.is_truthy())
1349 .ok_or_else(|| IonError::runtime(ion_str!("stack underflow (peek)"), line, col))
1350 }
1351
1352 fn const_as_str(&self, val: &Value, line: usize, col: usize) -> Result<String, IonError> {
1353 match val {
1354 Value::Str(s) => Ok(s.clone()),
1355 _ => Err(IonError::runtime(
1356 ion_str!("expected string constant"),
1357 line,
1358 col,
1359 )),
1360 }
1361 }
1362
1363 fn const_to_sym(
1365 &mut self,
1366 val: &Value,
1367 line: usize,
1368 col: usize,
1369 ) -> Result<crate::intern::Symbol, IonError> {
1370 match val {
1371 Value::Str(s) => Ok(self.env.intern(s)),
1372 _ => Err(IonError::runtime(
1373 ion_str!("expected string constant"),
1374 line,
1375 col,
1376 )),
1377 }
1378 }
1379
1380 fn op_add(&self, a: Value, b: Value, line: usize, col: usize) -> Result<Value, IonError> {
1383 match (&a, &b) {
1384 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x + y)),
1385 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)),
1386 (Value::Int(x), Value::Float(y)) => Ok(Value::Float(*x as f64 + y)),
1387 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x + *y as f64)),
1388 (Value::Str(x), Value::Str(y)) => {
1389 let mut s = String::with_capacity(x.len() + y.len());
1390 s.push_str(x);
1391 s.push_str(y);
1392 Ok(Value::Str(s))
1393 }
1394 (Value::List(x), Value::List(y)) => {
1395 let mut r = x.clone();
1396 r.extend(y.clone());
1397 Ok(Value::List(r))
1398 }
1399 (Value::Bytes(x), Value::Bytes(y)) => {
1400 let mut r = x.clone();
1401 r.extend(y);
1402 Ok(Value::Bytes(r))
1403 }
1404 _ => Err(IonError::type_err(
1405 format!(
1406 "{}{} and {}",
1407 ion_str!("cannot add "),
1408 a.type_name(),
1409 b.type_name()
1410 ),
1411 line,
1412 col,
1413 )),
1414 }
1415 }
1416
1417 fn op_sub(&self, a: Value, b: Value, line: usize, col: usize) -> Result<Value, IonError> {
1418 match (&a, &b) {
1419 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x - y)),
1420 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)),
1421 (Value::Int(x), Value::Float(y)) => Ok(Value::Float(*x as f64 - y)),
1422 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x - *y as f64)),
1423 _ => Err(IonError::type_err(
1424 format!(
1425 "{}{} from {}",
1426 ion_str!("cannot subtract "),
1427 b.type_name(),
1428 a.type_name()
1429 ),
1430 line,
1431 col,
1432 )),
1433 }
1434 }
1435
1436 fn op_mul(&self, a: Value, b: Value, line: usize, col: usize) -> Result<Value, IonError> {
1437 match (&a, &b) {
1438 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
1439 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
1440 (Value::Int(x), Value::Float(y)) => Ok(Value::Float(*x as f64 * y)),
1441 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * *y as f64)),
1442 (Value::Str(s), Value::Int(n)) | (Value::Int(n), Value::Str(s)) => {
1443 Ok(Value::Str(s.repeat(*n as usize)))
1444 }
1445 _ => Err(IonError::type_err(
1446 format!(
1447 "{}{} and {}",
1448 ion_str!("cannot multiply "),
1449 a.type_name(),
1450 b.type_name()
1451 ),
1452 line,
1453 col,
1454 )),
1455 }
1456 }
1457
1458 fn op_div(&self, a: Value, b: Value, line: usize, col: usize) -> Result<Value, IonError> {
1459 match (&a, &b) {
1460 (Value::Int(_), Value::Int(0)) => {
1461 Err(IonError::runtime(ion_str!("division by zero"), line, col))
1462 }
1463 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x / y)),
1464 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x / y)),
1465 (Value::Int(x), Value::Float(y)) => Ok(Value::Float(*x as f64 / y)),
1466 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x / *y as f64)),
1467 _ => Err(IonError::type_err(
1468 format!(
1469 "{}{} by {}",
1470 ion_str!("cannot divide "),
1471 a.type_name(),
1472 b.type_name()
1473 ),
1474 line,
1475 col,
1476 )),
1477 }
1478 }
1479
1480 fn op_mod(&self, a: Value, b: Value, line: usize, col: usize) -> Result<Value, IonError> {
1481 match (&a, &b) {
1482 (Value::Int(_), Value::Int(0)) => {
1483 Err(IonError::runtime(ion_str!("modulo by zero"), line, col))
1484 }
1485 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x % y)),
1486 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x % y)),
1487 (Value::Int(x), Value::Float(y)) => Ok(Value::Float(*x as f64 % y)),
1488 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x % *y as f64)),
1489 _ => Err(IonError::type_err(
1490 format!(
1491 "{}{} by {}",
1492 ion_str!("cannot modulo "),
1493 a.type_name(),
1494 b.type_name()
1495 ),
1496 line,
1497 col,
1498 )),
1499 }
1500 }
1501
1502 fn op_neg(&self, val: Value, line: usize, col: usize) -> Result<Value, IonError> {
1503 match val {
1504 Value::Int(n) => Ok(Value::Int(-n)),
1505 Value::Float(n) => Ok(Value::Float(-n)),
1506 _ => Err(IonError::type_err(
1507 format!("{}{}", ion_str!("cannot negate "), val.type_name()),
1508 line,
1509 col,
1510 )),
1511 }
1512 }
1513
1514 fn compare_lt(&self, a: &Value, b: &Value, line: usize, col: usize) -> Result<bool, IonError> {
1515 match (a, b) {
1516 (Value::Int(x), Value::Int(y)) => Ok(x < y),
1517 (Value::Float(x), Value::Float(y)) => Ok(x < y),
1518 (Value::Int(x), Value::Float(y)) => Ok((*x as f64) < *y),
1519 (Value::Float(x), Value::Int(y)) => Ok(*x < (*y as f64)),
1520 (Value::Str(x), Value::Str(y)) => Ok(x < y),
1521 _ => Err(IonError::type_err(
1522 format!(
1523 "{}{} and {}",
1524 ion_str!("cannot compare "),
1525 a.type_name(),
1526 b.type_name()
1527 ),
1528 line,
1529 col,
1530 )),
1531 }
1532 }
1533
1534 fn get_field(
1537 &self,
1538 obj: Value,
1539 field: &str,
1540 line: usize,
1541 col: usize,
1542 ) -> Result<Value, IonError> {
1543 match &obj {
1544 Value::Dict(map) => Ok(match map.get(field) {
1545 Some(v) => v.clone(),
1546 None => Value::Option(None),
1547 }),
1548 Value::HostStruct { fields, .. } => fields.get(field).cloned().ok_or_else(|| {
1549 IonError::runtime(
1550 format!(
1551 "{}{}{}",
1552 ion_str!("field '"),
1553 field,
1554 ion_str!("' not found")
1555 ),
1556 line,
1557 col,
1558 )
1559 }),
1560 Value::List(items) => match field {
1561 "len" => Ok(Value::Int(items.len() as i64)),
1562 _ => Err(IonError::runtime(
1563 format!(
1564 "{}{}{}",
1565 ion_str!("list has no field '"),
1566 field,
1567 ion_str!("'")
1568 ),
1569 line,
1570 col,
1571 )),
1572 },
1573 Value::Str(s) => match field {
1574 "len" => Ok(Value::Int(s.len() as i64)),
1575 _ => Err(IonError::runtime(
1576 format!(
1577 "{}{}{}",
1578 ion_str!("string has no field '"),
1579 field,
1580 ion_str!("'")
1581 ),
1582 line,
1583 col,
1584 )),
1585 },
1586 Value::Tuple(items) => match field {
1587 "len" => Ok(Value::Int(items.len() as i64)),
1588 _ => Err(IonError::runtime(
1589 format!(
1590 "{}{}{}",
1591 ion_str!("tuple has no field '"),
1592 field,
1593 ion_str!("'")
1594 ),
1595 line,
1596 col,
1597 )),
1598 },
1599 _ => Err(IonError::type_err(
1600 format!(
1601 "{}{}{}{}",
1602 ion_str!("cannot access field '"),
1603 field,
1604 ion_str!("' on "),
1605 obj.type_name()
1606 ),
1607 line,
1608 col,
1609 )),
1610 }
1611 }
1612
1613 fn get_index(
1614 &self,
1615 obj: Value,
1616 index: Value,
1617 line: usize,
1618 col: usize,
1619 ) -> Result<Value, IonError> {
1620 match (&obj, &index) {
1621 (Value::List(items), Value::Int(i)) => {
1622 let idx = if *i < 0 { items.len() as i64 + i } else { *i } as usize;
1623 items.get(idx).cloned().ok_or_else(|| {
1624 IonError::runtime(
1625 format!("{}{}{}", ion_str!("index "), i, ion_str!(" out of range")),
1626 line,
1627 col,
1628 )
1629 })
1630 }
1631 (Value::Tuple(items), Value::Int(i)) => {
1632 let idx = if *i < 0 { items.len() as i64 + i } else { *i } as usize;
1633 items.get(idx).cloned().ok_or_else(|| {
1634 IonError::runtime(
1635 format!("{}{}{}", ion_str!("index "), i, ion_str!(" out of range")),
1636 line,
1637 col,
1638 )
1639 })
1640 }
1641 (Value::Dict(map), Value::Str(key)) => Ok(match map.get(key) {
1642 Some(v) => v.clone(),
1643 None => Value::Option(None),
1644 }),
1645 (Value::Str(s), Value::Int(i)) => {
1646 let char_count = s.chars().count() as i64;
1647 let idx = if *i < 0 { char_count + i } else { *i } as usize;
1648 s.chars()
1649 .nth(idx)
1650 .map(|c| Value::Str(c.to_string()))
1651 .ok_or_else(|| {
1652 IonError::runtime(
1653 format!("{}{}{}", ion_str!("index "), i, ion_str!(" out of range")),
1654 line,
1655 col,
1656 )
1657 })
1658 }
1659 (Value::Bytes(bytes), Value::Int(i)) => {
1660 let idx = if *i < 0 { bytes.len() as i64 + i } else { *i } as usize;
1661 bytes
1662 .get(idx)
1663 .map(|&b| Value::Int(b as i64))
1664 .ok_or_else(|| {
1665 IonError::runtime(
1666 format!("{}{}{}", ion_str!("index "), i, ion_str!(" out of range")),
1667 line,
1668 col,
1669 )
1670 })
1671 }
1672 _ => Err(IonError::type_err(
1673 format!(
1674 "{}{}{}{}",
1675 ion_str!("cannot index "),
1676 obj.type_name(),
1677 ion_str!(" with "),
1678 index.type_name()
1679 ),
1680 line,
1681 col,
1682 )),
1683 }
1684 }
1685
1686 fn set_index(
1688 &self,
1689 obj: Value,
1690 index: Value,
1691 value: Value,
1692 line: usize,
1693 col: usize,
1694 ) -> Result<Value, IonError> {
1695 match (obj, &index) {
1696 (Value::List(mut items), Value::Int(i)) => {
1697 let idx = if *i < 0 { items.len() as i64 + i } else { *i } as usize;
1698 if idx >= items.len() {
1699 return Err(IonError::runtime(
1700 format!("{}{}{}", ion_str!("index "), i, ion_str!(" out of range")),
1701 line,
1702 col,
1703 ));
1704 }
1705 items[idx] = value;
1706 Ok(Value::List(items))
1707 }
1708 (Value::Dict(mut map), Value::Str(key)) => {
1709 map.insert(key.clone(), value);
1710 Ok(Value::Dict(map))
1711 }
1712 (obj, _) => Err(IonError::type_err(
1713 format!("{}{}", ion_str!("cannot set index on "), obj.type_name()),
1714 line,
1715 col,
1716 )),
1717 }
1718 }
1719
1720 fn set_field(
1722 &self,
1723 obj: Value,
1724 field: &str,
1725 value: Value,
1726 line: usize,
1727 col: usize,
1728 ) -> Result<Value, IonError> {
1729 match obj {
1730 Value::Dict(mut map) => {
1731 map.insert(field.to_string(), value);
1732 Ok(Value::Dict(map))
1733 }
1734 Value::HostStruct {
1735 type_name,
1736 mut fields,
1737 } => {
1738 if fields.contains_key(field) {
1739 fields.insert(field.to_string(), value);
1740 Ok(Value::HostStruct { type_name, fields })
1741 } else {
1742 Err(IonError::runtime(
1743 format!(
1744 "{}{}{}{}",
1745 ion_str!("field '"),
1746 field,
1747 ion_str!("' not found on "),
1748 type_name
1749 ),
1750 line,
1751 col,
1752 ))
1753 }
1754 }
1755 _ => Err(IonError::type_err(
1756 format!("{}{}", ion_str!("cannot set field on "), obj.type_name()),
1757 line,
1758 col,
1759 )),
1760 }
1761 }
1762
1763 fn call_method(
1766 &mut self,
1767 receiver: Value,
1768 method: &str,
1769 args: &[Value],
1770 line: usize,
1771 col: usize,
1772 ) -> Result<Value, IonError> {
1773 if method == "to_string" {
1775 return Ok(Value::Str(format!("{}", receiver)));
1776 }
1777 match (&receiver, method) {
1779 (Value::List(items), "map") => {
1781 let func = args.first().ok_or_else(|| {
1782 IonError::runtime(ion_str!("map requires a function argument"), line, col)
1783 })?;
1784 let mut result = Vec::new();
1785 for item in items {
1786 result.push(self.invoke_value(func, std::slice::from_ref(item), line, col)?);
1787 }
1788 return Ok(Value::List(result));
1789 }
1790 (Value::List(items), "filter") => {
1791 let func = args.first().ok_or_else(|| {
1792 IonError::runtime(ion_str!("filter requires a function argument"), line, col)
1793 })?;
1794 let mut result = Vec::new();
1795 for item in items {
1796 let keep = self.invoke_value(func, std::slice::from_ref(item), line, col)?;
1797 if keep.is_truthy() {
1798 result.push(item.clone());
1799 }
1800 }
1801 return Ok(Value::List(result));
1802 }
1803 (Value::List(items), "fold") => {
1804 let init = args.first().cloned().unwrap_or(Value::Unit);
1805 let func = args.get(1).ok_or_else(|| {
1806 IonError::runtime(
1807 ion_str!("fold requires an initial value and a function"),
1808 line,
1809 col,
1810 )
1811 })?;
1812 let mut acc = init;
1813 for item in items {
1814 acc = self.invoke_value(func, &[acc, item.clone()], line, col)?;
1815 }
1816 return Ok(acc);
1817 }
1818 (Value::List(items), "reduce") => {
1819 if items.is_empty() {
1820 return Err(IonError::runtime(
1821 ion_str!("reduce on empty list"),
1822 line,
1823 col,
1824 ));
1825 }
1826 let func = args.first().ok_or_else(|| {
1827 IonError::runtime(ion_str!("reduce requires a function argument"), line, col)
1828 })?;
1829 let mut acc = items[0].clone();
1830 for item in items.iter().skip(1) {
1831 acc = self.invoke_value(func, &[acc, item.clone()], line, col)?;
1832 }
1833 return Ok(acc);
1834 }
1835 (Value::List(items), "flat_map") => {
1836 let func = args.first().ok_or_else(|| {
1837 IonError::runtime(ion_str!("flat_map requires a function argument"), line, col)
1838 })?;
1839 let mut result = Vec::new();
1840 for item in items {
1841 let mapped = self.invoke_value(func, std::slice::from_ref(item), line, col)?;
1842 match mapped {
1843 Value::List(sub) => result.extend(sub),
1844 other => result.push(other),
1845 }
1846 }
1847 return Ok(Value::List(result));
1848 }
1849 (Value::List(items), "any") => {
1850 let func = args.first().ok_or_else(|| {
1851 IonError::runtime(ion_str!("any requires a function argument"), line, col)
1852 })?;
1853 for item in items {
1854 if self
1855 .invoke_value(func, std::slice::from_ref(item), line, col)?
1856 .is_truthy()
1857 {
1858 return Ok(Value::Bool(true));
1859 }
1860 }
1861 return Ok(Value::Bool(false));
1862 }
1863 (Value::List(items), "all") => {
1864 let func = args.first().ok_or_else(|| {
1865 IonError::runtime(ion_str!("all requires a function argument"), line, col)
1866 })?;
1867 for item in items {
1868 if !self
1869 .invoke_value(func, std::slice::from_ref(item), line, col)?
1870 .is_truthy()
1871 {
1872 return Ok(Value::Bool(false));
1873 }
1874 }
1875 return Ok(Value::Bool(true));
1876 }
1877 (Value::List(items), "sort_by") => {
1878 let func = args.first().ok_or_else(|| {
1879 IonError::runtime(ion_str!("sort_by requires a function argument"), line, col)
1880 })?;
1881 let mut result = items.to_vec();
1882 let mut err: Option<IonError> = None;
1883 let func_clone = func.clone();
1884 result.sort_by(|a, b| {
1885 if err.is_some() {
1886 return std::cmp::Ordering::Equal;
1887 }
1888 match self.invoke_value(&func_clone, &[a.clone(), b.clone()], line, col) {
1889 Ok(Value::Int(n)) => {
1890 if n < 0 {
1891 std::cmp::Ordering::Less
1892 } else if n > 0 {
1893 std::cmp::Ordering::Greater
1894 } else {
1895 std::cmp::Ordering::Equal
1896 }
1897 }
1898 Ok(_) => {
1899 err = Some(IonError::type_err(
1900 ion_str!("sort_by function must return int"),
1901 line,
1902 col,
1903 ));
1904 std::cmp::Ordering::Equal
1905 }
1906 Err(e) => {
1907 err = Some(e);
1908 std::cmp::Ordering::Equal
1909 }
1910 }
1911 });
1912 if let Some(e) = err {
1913 return Err(e);
1914 }
1915 return Ok(Value::List(result));
1916 }
1917
1918 (
1920 Value::Range {
1921 start,
1922 end,
1923 inclusive,
1924 },
1925 "map",
1926 )
1927 | (
1928 Value::Range {
1929 start,
1930 end,
1931 inclusive,
1932 },
1933 "filter",
1934 )
1935 | (
1936 Value::Range {
1937 start,
1938 end,
1939 inclusive,
1940 },
1941 "fold",
1942 )
1943 | (
1944 Value::Range {
1945 start,
1946 end,
1947 inclusive,
1948 },
1949 "reduce",
1950 )
1951 | (
1952 Value::Range {
1953 start,
1954 end,
1955 inclusive,
1956 },
1957 "flat_map",
1958 )
1959 | (
1960 Value::Range {
1961 start,
1962 end,
1963 inclusive,
1964 },
1965 "any",
1966 )
1967 | (
1968 Value::Range {
1969 start,
1970 end,
1971 inclusive,
1972 },
1973 "all",
1974 )
1975 | (
1976 Value::Range {
1977 start,
1978 end,
1979 inclusive,
1980 },
1981 "sort_by",
1982 ) => {
1983 let items = Value::range_to_list(*start, *end, *inclusive);
1984 let list_receiver = Value::List(items);
1985 return self.call_method(list_receiver, method, args, line, col);
1986 }
1987
1988 (Value::Dict(map), "map") => {
1990 let func = args.first().ok_or_else(|| {
1991 IonError::runtime(ion_str!("map requires a function argument"), line, col)
1992 })?;
1993 let mut result = indexmap::IndexMap::new();
1994 for (k, v) in map {
1995 let mapped =
1996 self.invoke_value(func, &[Value::Str(k.clone()), v.clone()], line, col)?;
1997 result.insert(k.clone(), mapped);
1998 }
1999 return Ok(Value::Dict(result));
2000 }
2001 (Value::Dict(map), "filter") => {
2002 let func = args.first().ok_or_else(|| {
2003 IonError::runtime(ion_str!("filter requires a function argument"), line, col)
2004 })?;
2005 let mut result = indexmap::IndexMap::new();
2006 for (k, v) in map {
2007 let keep =
2008 self.invoke_value(func, &[Value::Str(k.clone()), v.clone()], line, col)?;
2009 if keep.is_truthy() {
2010 result.insert(k.clone(), v.clone());
2011 }
2012 }
2013 return Ok(Value::Dict(result));
2014 }
2015
2016 (Value::Option(opt), "map") => {
2018 let func = args.first().ok_or_else(|| {
2019 IonError::runtime(ion_str!("map requires a function argument"), line, col)
2020 })?;
2021 return match opt {
2022 Some(v) => {
2023 let result = self.invoke_value(func, &[*v.clone()], line, col)?;
2024 Ok(Value::Option(Some(Box::new(result))))
2025 }
2026 None => Ok(Value::Option(None)),
2027 };
2028 }
2029 (Value::Option(opt), "and_then") => {
2030 let func = args.first().ok_or_else(|| {
2031 IonError::runtime(ion_str!("and_then requires a function argument"), line, col)
2032 })?;
2033 return match opt {
2034 Some(v) => self.invoke_value(func, &[*v.clone()], line, col),
2035 None => Ok(Value::Option(None)),
2036 };
2037 }
2038 (Value::Option(opt), "or_else") => {
2039 let func = args.first().ok_or_else(|| {
2040 IonError::runtime(ion_str!("or_else requires a function argument"), line, col)
2041 })?;
2042 return match opt {
2043 Some(v) => Ok(Value::Option(Some(v.clone()))),
2044 None => self.invoke_value(func, &[], line, col),
2045 };
2046 }
2047 (Value::Option(opt), "unwrap_or_else") => {
2048 let func = args.first().ok_or_else(|| {
2049 IonError::runtime(
2050 ion_str!("unwrap_or_else requires a function argument"),
2051 line,
2052 col,
2053 )
2054 })?;
2055 return match opt {
2056 Some(v) => Ok(*v.clone()),
2057 None => self.invoke_value(func, &[], line, col),
2058 };
2059 }
2060
2061 (Value::Result(res), "map") => {
2063 let func = args.first().ok_or_else(|| {
2064 IonError::runtime(ion_str!("map requires a function argument"), line, col)
2065 })?;
2066 return match res {
2067 Ok(v) => {
2068 let result = self.invoke_value(func, &[*v.clone()], line, col)?;
2069 Ok(Value::Result(Ok(Box::new(result))))
2070 }
2071 Err(e) => Ok(Value::Result(Err(e.clone()))),
2072 };
2073 }
2074 (Value::Result(res), "map_err") => {
2075 let func = args.first().ok_or_else(|| {
2076 IonError::runtime(ion_str!("map_err requires a function argument"), line, col)
2077 })?;
2078 return match res {
2079 Ok(v) => Ok(Value::Result(Ok(v.clone()))),
2080 Err(e) => {
2081 let result = self.invoke_value(func, &[*e.clone()], line, col)?;
2082 Ok(Value::Result(Err(Box::new(result))))
2083 }
2084 };
2085 }
2086 (Value::Result(res), "and_then") => {
2087 let func = args.first().ok_or_else(|| {
2088 IonError::runtime(ion_str!("and_then requires a function argument"), line, col)
2089 })?;
2090 return match res {
2091 Ok(v) => self.invoke_value(func, &[*v.clone()], line, col),
2092 Err(e) => Ok(Value::Result(Err(e.clone()))),
2093 };
2094 }
2095 (Value::Result(res), "or_else") => {
2096 let func = args.first().ok_or_else(|| {
2097 IonError::runtime(ion_str!("or_else requires a function argument"), line, col)
2098 })?;
2099 return match res {
2100 Ok(v) => Ok(Value::Result(Ok(v.clone()))),
2101 Err(e) => self.invoke_value(func, &[*e.clone()], line, col),
2102 };
2103 }
2104 (Value::Result(res), "unwrap_or_else") => {
2105 let func = args.first().ok_or_else(|| {
2106 IonError::runtime(
2107 ion_str!("unwrap_or_else requires a function argument"),
2108 line,
2109 col,
2110 )
2111 })?;
2112 return match res {
2113 Ok(v) => Ok(*v.clone()),
2114 Err(e) => self.invoke_value(func, &[*e.clone()], line, col),
2115 };
2116 }
2117
2118 (Value::Cell(cell), "update") => {
2120 let func = args.first().ok_or_else(|| {
2121 IonError::runtime(
2122 ion_str!("cell.update() requires a function argument"),
2123 line,
2124 col,
2125 )
2126 })?;
2127 let current = { cell.lock().unwrap().clone() };
2128 let new_val = self.invoke_value(func, &[current], line, col)?;
2129 let mut inner = cell.lock().unwrap();
2130 *inner = new_val.clone();
2131 return Ok(new_val);
2132 }
2133
2134 _ => {}
2135 }
2136
2137 match &receiver {
2139 Value::List(items) => self.list_method(items, method, args, line, col),
2140 Value::Tuple(items) => self.tuple_method(items, method, args, line, col),
2141 Value::Str(s) => self.str_method(s, method, args, line, col),
2142 Value::Dict(map) => self.dict_method(map, method, args, line, col),
2143 Value::Bytes(b) => self.bytes_method(b, method, args, line, col),
2144 Value::Set(items) => self.set_method(items, method, args, line, col),
2145 Value::Option(_) => self.option_method(&receiver, method, args, line, col),
2146 Value::Result(_) => self.result_method(&receiver, method, args, line, col),
2147 Value::Range {
2148 start,
2149 end,
2150 inclusive,
2151 } => match method {
2152 "len" => Ok(Value::Int(Value::range_len(*start, *end, *inclusive))),
2153 "contains" => {
2154 let val = args[0].as_int().ok_or_else(|| {
2155 IonError::type_err(ion_str!("range.contains requires int"), line, col)
2156 })?;
2157 let in_range = if *inclusive {
2158 val >= *start && val <= *end
2159 } else {
2160 val >= *start && val < *end
2161 };
2162 Ok(Value::Bool(in_range))
2163 }
2164 "to_list" => Ok(Value::List(Value::range_to_list(*start, *end, *inclusive))),
2165 _ => {
2166 let items = Value::range_to_list(*start, *end, *inclusive);
2167 self.list_method(&items, method, args, line, col)
2168 }
2169 },
2170 Value::Cell(cell) => match method {
2171 "get" => Ok(cell.lock().unwrap().clone()),
2172 "set" => {
2173 if let Some(val) = args.first() {
2174 let mut inner = cell.lock().unwrap();
2175 *inner = val.clone();
2176 Ok(Value::Unit)
2177 } else {
2178 Err(IonError::runtime(
2179 ion_str!("cell.set() requires 1 argument"),
2180 line,
2181 col,
2182 ))
2183 }
2184 }
2185 _ => Err(IonError::type_err(
2186 format!(
2187 "{}{}{}",
2188 ion_str!("no method '"),
2189 method,
2190 ion_str!("' on cell")
2191 ),
2192 line,
2193 col,
2194 )),
2195 },
2196 _ => Err(IonError::type_err(
2197 format!(
2198 "{}{}{}{}",
2199 receiver.type_name(),
2200 ion_str!(" has no method '"),
2201 method,
2202 ion_str!("'")
2203 ),
2204 line,
2205 col,
2206 )),
2207 }
2208 }
2209
2210 fn list_method(
2211 &self,
2212 items: &[Value],
2213 method: &str,
2214 args: &[Value],
2215 line: usize,
2216 col: usize,
2217 ) -> Result<Value, IonError> {
2218 match method {
2219 "len" => Ok(Value::Int(items.len() as i64)),
2220 "push" => {
2221 let mut new = items.to_vec();
2222 for a in args {
2223 new.push(a.clone());
2224 }
2225 Ok(Value::List(new))
2226 }
2227 "pop" => {
2228 if items.is_empty() {
2229 Ok(Value::Tuple(vec![Value::List(vec![]), Value::Option(None)]))
2230 } else {
2231 let mut new = items.to_vec();
2232 let popped = new.pop().unwrap();
2233 Ok(Value::Tuple(vec![
2234 Value::List(new),
2235 Value::Option(Some(Box::new(popped))),
2236 ]))
2237 }
2238 }
2239 "contains" => Ok(Value::Bool(
2240 args.first().map(|a| items.contains(a)).unwrap_or(false),
2241 )),
2242 "is_empty" => Ok(Value::Bool(items.is_empty())),
2243 "reverse" => {
2244 let mut new = items.to_vec();
2245 new.reverse();
2246 Ok(Value::List(new))
2247 }
2248 "join" => {
2249 let sep = args.first().and_then(|a| a.as_str()).unwrap_or("");
2250 let s: String = items
2251 .iter()
2252 .map(|v| v.to_string())
2253 .collect::<Vec<_>>()
2254 .join(sep);
2255 Ok(Value::Str(s))
2256 }
2257 "enumerate" => {
2258 let pairs: Vec<Value> = items
2259 .iter()
2260 .enumerate()
2261 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v.clone()]))
2262 .collect();
2263 Ok(Value::List(pairs))
2264 }
2265 "first" => Ok(match items.first() {
2266 Some(v) => Value::Option(Some(Box::new(v.clone()))),
2267 None => Value::Option(None),
2268 }),
2269 "last" => Ok(match items.last() {
2270 Some(v) => Value::Option(Some(Box::new(v.clone()))),
2271 None => Value::Option(None),
2272 }),
2273 "sort" => {
2274 if !items.is_empty() {
2275 let first_type = std::mem::discriminant(&items[0]);
2276 for item in items.iter().skip(1) {
2277 if std::mem::discriminant(item) != first_type {
2278 return Err(IonError::type_err(
2279 ion_str!("sort() requires all elements to be the same type"),
2280 line,
2281 col,
2282 ));
2283 }
2284 }
2285 }
2286 let mut sorted = items.to_vec();
2287 sorted.sort_by(|a, b| match (a, b) {
2288 (Value::Int(x), Value::Int(y)) => x.cmp(y),
2289 (Value::Float(x), Value::Float(y)) => {
2290 x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal)
2291 }
2292 (Value::Str(x), Value::Str(y)) => x.cmp(y),
2293 _ => std::cmp::Ordering::Equal,
2294 });
2295 Ok(Value::List(sorted))
2296 }
2297 "flatten" => {
2298 let mut result = Vec::new();
2299 for item in items {
2300 if let Value::List(inner) = item {
2301 result.extend(inner.iter().cloned());
2302 } else {
2303 result.push(item.clone());
2304 }
2305 }
2306 Ok(Value::List(result))
2307 }
2308 "zip" => {
2309 if let Some(Value::List(other)) = args.first() {
2310 let result: Vec<Value> = items
2311 .iter()
2312 .zip(other.iter())
2313 .map(|(a, b)| Value::Tuple(vec![a.clone(), b.clone()]))
2314 .collect();
2315 Ok(Value::List(result))
2316 } else {
2317 Err(IonError::type_err(
2318 ion_str!("zip requires a list argument"),
2319 line,
2320 col,
2321 ))
2322 }
2323 }
2324 "index" => {
2325 let target = args.first().ok_or_else(|| {
2326 IonError::type_err(ion_str!("index requires an argument"), line, col)
2327 })?;
2328 Ok(match items.iter().position(|v| v == target) {
2329 Some(i) => Value::Option(Some(Box::new(Value::Int(i as i64)))),
2330 None => Value::Option(None),
2331 })
2332 }
2333 "count" => {
2334 let target = args.first().ok_or_else(|| {
2335 IonError::type_err(ion_str!("count requires an argument"), line, col)
2336 })?;
2337 Ok(Value::Int(
2338 items.iter().filter(|v| *v == target).count() as i64
2339 ))
2340 }
2341 "slice" => {
2342 let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2343 let end = args
2344 .get(1)
2345 .and_then(|a| a.as_int())
2346 .map(|n| n as usize)
2347 .unwrap_or(items.len());
2348 let start = start.min(items.len());
2349 let end = end.min(items.len());
2350 Ok(Value::List(items[start..end].to_vec()))
2351 }
2352 "dedup" => {
2353 let mut result: Vec<Value> = Vec::new();
2354 for item in items {
2355 if result.last() != Some(item) {
2356 result.push(item.clone());
2357 }
2358 }
2359 Ok(Value::List(result))
2360 }
2361 "unique" => {
2362 let mut seen = Vec::new();
2363 let mut result = Vec::new();
2364 for item in items {
2365 if !seen.contains(item) {
2366 seen.push(item.clone());
2367 result.push(item.clone());
2368 }
2369 }
2370 Ok(Value::List(result))
2371 }
2372 "min" => {
2373 if items.is_empty() {
2374 return Ok(Value::Option(None));
2375 }
2376 let mut min = &items[0];
2377 for item in items.iter().skip(1) {
2378 match (min, item) {
2379 (Value::Int(a), Value::Int(b)) if b < a => min = item,
2380 (Value::Float(a), Value::Float(b)) if b < a => min = item,
2381 (Value::Str(a), Value::Str(b)) if b < a => min = item,
2382 (Value::Int(_), Value::Int(_))
2383 | (Value::Float(_), Value::Float(_))
2384 | (Value::Str(_), Value::Str(_)) => {}
2385 _ => {
2386 return Err(IonError::type_err(
2387 ion_str!("min() requires homogeneous comparable elements"),
2388 line,
2389 col,
2390 ))
2391 }
2392 }
2393 }
2394 Ok(Value::Option(Some(Box::new(min.clone()))))
2395 }
2396 "max" => {
2397 if items.is_empty() {
2398 return Ok(Value::Option(None));
2399 }
2400 let mut max = &items[0];
2401 for item in items.iter().skip(1) {
2402 match (max, item) {
2403 (Value::Int(a), Value::Int(b)) if b > a => max = item,
2404 (Value::Float(a), Value::Float(b)) if b > a => max = item,
2405 (Value::Str(a), Value::Str(b)) if b > a => max = item,
2406 (Value::Int(_), Value::Int(_))
2407 | (Value::Float(_), Value::Float(_))
2408 | (Value::Str(_), Value::Str(_)) => {}
2409 _ => {
2410 return Err(IonError::type_err(
2411 ion_str!("max() requires homogeneous comparable elements"),
2412 line,
2413 col,
2414 ))
2415 }
2416 }
2417 }
2418 Ok(Value::Option(Some(Box::new(max.clone()))))
2419 }
2420 "sum" => {
2421 let mut int_sum: i64 = 0;
2422 let mut float_sum: f64 = 0.0;
2423 let mut has_float = false;
2424 for item in items {
2425 match item {
2426 Value::Int(n) => int_sum += n,
2427 Value::Float(f) => {
2428 has_float = true;
2429 float_sum += f;
2430 }
2431 _ => {
2432 return Err(IonError::type_err(
2433 ion_str!("sum() requires numeric elements"),
2434 line,
2435 col,
2436 ))
2437 }
2438 }
2439 }
2440 if has_float {
2441 Ok(Value::Float(float_sum + int_sum as f64))
2442 } else {
2443 Ok(Value::Int(int_sum))
2444 }
2445 }
2446 "window" => {
2447 let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2448 IonError::type_err(ion_str!("window requires int argument"), line, col)
2449 })? as usize;
2450 if n == 0 {
2451 return Err(IonError::runtime(
2452 ion_str!("window size must be > 0"),
2453 line,
2454 col,
2455 ));
2456 }
2457 let result: Vec<Value> =
2458 items.windows(n).map(|w| Value::List(w.to_vec())).collect();
2459 Ok(Value::List(result))
2460 }
2461 "chunk" => {
2462 let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2463 IonError::type_err(ion_str!("chunk requires int argument"), line, col)
2464 })? as usize;
2465 if n == 0 {
2466 return Err(IonError::type_err(
2467 ion_str!("chunk size must be > 0"),
2468 line,
2469 col,
2470 ));
2471 }
2472 let result: Vec<Value> = items.chunks(n).map(|c| Value::List(c.to_vec())).collect();
2473 Ok(Value::List(result))
2474 }
2475 _ => Err(IonError::type_err(
2476 format!(
2477 "{}{}{}",
2478 ion_str!("list has no method '"),
2479 method,
2480 ion_str!("'")
2481 ),
2482 line,
2483 col,
2484 )),
2485 }
2486 }
2487
2488 fn set_method(
2489 &self,
2490 items: &[Value],
2491 method: &str,
2492 args: &[Value],
2493 line: usize,
2494 col: usize,
2495 ) -> Result<Value, IonError> {
2496 match method {
2497 "len" => Ok(Value::Int(items.len() as i64)),
2498 "contains" => Ok(Value::Bool(
2499 args.first().map(|a| items.contains(a)).unwrap_or(false),
2500 )),
2501 "is_empty" => Ok(Value::Bool(items.is_empty())),
2502 "add" => {
2503 let val = &args[0];
2504 let mut new = items.to_vec();
2505 if !new.iter().any(|v| v == val) {
2506 new.push(val.clone());
2507 }
2508 Ok(Value::Set(new))
2509 }
2510 "remove" => {
2511 let val = &args[0];
2512 let new: Vec<Value> = items.iter().filter(|v| *v != val).cloned().collect();
2513 Ok(Value::Set(new))
2514 }
2515 "union" => {
2516 if let Some(Value::Set(other)) = args.first() {
2517 let mut new = items.to_vec();
2518 for v in other {
2519 if !new.iter().any(|x| x == v) {
2520 new.push(v.clone());
2521 }
2522 }
2523 Ok(Value::Set(new))
2524 } else {
2525 Err(IonError::type_err(
2526 ion_str!("union requires a set argument"),
2527 line,
2528 col,
2529 ))
2530 }
2531 }
2532 "intersection" => {
2533 if let Some(Value::Set(other)) = args.first() {
2534 let new: Vec<Value> = items
2535 .iter()
2536 .filter(|v| other.iter().any(|x| x == *v))
2537 .cloned()
2538 .collect();
2539 Ok(Value::Set(new))
2540 } else {
2541 Err(IonError::type_err(
2542 ion_str!("intersection requires a set argument"),
2543 line,
2544 col,
2545 ))
2546 }
2547 }
2548 "difference" => {
2549 if let Some(Value::Set(other)) = args.first() {
2550 let new: Vec<Value> = items
2551 .iter()
2552 .filter(|v| !other.iter().any(|x| x == *v))
2553 .cloned()
2554 .collect();
2555 Ok(Value::Set(new))
2556 } else {
2557 Err(IonError::type_err(
2558 ion_str!("difference requires a set argument"),
2559 line,
2560 col,
2561 ))
2562 }
2563 }
2564 "to_list" => Ok(Value::List(items.to_vec())),
2565 _ => Err(IonError::type_err(
2566 format!(
2567 "{}{}{}",
2568 ion_str!("set has no method '"),
2569 method,
2570 ion_str!("'")
2571 ),
2572 line,
2573 col,
2574 )),
2575 }
2576 }
2577
2578 fn tuple_method(
2579 &self,
2580 items: &[Value],
2581 method: &str,
2582 args: &[Value],
2583 line: usize,
2584 col: usize,
2585 ) -> Result<Value, IonError> {
2586 match method {
2587 "len" => Ok(Value::Int(items.len() as i64)),
2588 "contains" => Ok(Value::Bool(
2589 args.first().map(|a| items.contains(a)).unwrap_or(false),
2590 )),
2591 "to_list" => Ok(Value::List(items.to_vec())),
2592 _ => Err(IonError::type_err(
2593 format!(
2594 "{}{}{}",
2595 ion_str!("tuple has no method '"),
2596 method,
2597 ion_str!("'")
2598 ),
2599 line,
2600 col,
2601 )),
2602 }
2603 }
2604
2605 fn str_method(
2606 &self,
2607 s: &str,
2608 method: &str,
2609 args: &[Value],
2610 line: usize,
2611 col: usize,
2612 ) -> Result<Value, IonError> {
2613 match method {
2614 "len" => Ok(Value::Int(s.len() as i64)),
2615 "to_upper" => Ok(Value::Str(s.to_uppercase())),
2616 "to_lower" => Ok(Value::Str(s.to_lowercase())),
2617 "trim" => Ok(Value::Str(s.trim().to_string())),
2618 "contains" => match args.first() {
2619 Some(Value::Str(sub)) => Ok(Value::Bool(s.contains(sub.as_str()))),
2620 Some(Value::Int(code)) => {
2621 let ch = char::from_u32(*code as u32).ok_or_else(|| {
2622 IonError::type_err(ion_str!("invalid char code"), line, col)
2623 })?;
2624 Ok(Value::Bool(s.contains(ch)))
2625 }
2626 _ => Err(IonError::type_err(
2627 ion_str!("contains requires string or int argument"),
2628 line,
2629 col,
2630 )),
2631 },
2632 "starts_with" => {
2633 let prefix = args.first().and_then(|a| a.as_str()).unwrap_or("");
2634 Ok(Value::Bool(s.starts_with(prefix)))
2635 }
2636 "ends_with" => {
2637 let suffix = args.first().and_then(|a| a.as_str()).unwrap_or("");
2638 Ok(Value::Bool(s.ends_with(suffix)))
2639 }
2640 "split" => {
2641 let sep = args.first().and_then(|a| a.as_str()).unwrap_or(" ");
2642 let parts: Vec<Value> = s.split(sep).map(|p| Value::Str(p.to_string())).collect();
2643 Ok(Value::List(parts))
2644 }
2645 "replace" => {
2646 let from = args.first().and_then(|a| a.as_str()).unwrap_or("");
2647 let to = args.get(1).and_then(|a| a.as_str()).unwrap_or("");
2648 Ok(Value::Str(s.replace(from, to)))
2649 }
2650 "chars" => {
2651 let chars: Vec<Value> = s.chars().map(|c| Value::Str(c.to_string())).collect();
2652 Ok(Value::List(chars))
2653 }
2654 "char_len" => Ok(Value::Int(s.chars().count() as i64)),
2655 "is_empty" => Ok(Value::Bool(s.is_empty())),
2656 "trim_start" => Ok(Value::Str(s.trim_start().to_string())),
2657 "trim_end" => Ok(Value::Str(s.trim_end().to_string())),
2658 "repeat" => {
2659 let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2660 IonError::type_err(ion_str!("repeat requires int argument"), line, col)
2661 })?;
2662 Ok(Value::Str(s.repeat(n as usize)))
2663 }
2664 "find" => {
2665 let sub = args.first().and_then(|a| a.as_str()).unwrap_or("");
2666 Ok(match s.find(sub) {
2667 Some(byte_idx) => {
2668 let char_idx = s[..byte_idx].chars().count();
2669 Value::Option(Some(Box::new(Value::Int(char_idx as i64))))
2670 }
2671 None => Value::Option(None),
2672 })
2673 }
2674 "to_int" => Ok(match s.trim().parse::<i64>() {
2675 std::result::Result::Ok(n) => Value::Result(Ok(Box::new(Value::Int(n)))),
2676 std::result::Result::Err(e) => {
2677 Value::Result(Err(Box::new(Value::Str(e.to_string()))))
2678 }
2679 }),
2680 "to_float" => Ok(match s.trim().parse::<f64>() {
2681 std::result::Result::Ok(f) => Value::Result(Ok(Box::new(Value::Float(f)))),
2682 std::result::Result::Err(e) => {
2683 Value::Result(Err(Box::new(Value::Str(e.to_string()))))
2684 }
2685 }),
2686 "bytes" => {
2687 let bytes: Vec<Value> = s.bytes().map(|b| Value::Int(b as i64)).collect();
2688 Ok(Value::List(bytes))
2689 }
2690 "strip_prefix" => {
2691 let pre = args.first().and_then(|a| a.as_str()).unwrap_or("");
2692 Ok(Value::Str(s.strip_prefix(pre).unwrap_or(s).to_string()))
2693 }
2694 "strip_suffix" => {
2695 let suf = args.first().and_then(|a| a.as_str()).unwrap_or("");
2696 Ok(Value::Str(s.strip_suffix(suf).unwrap_or(s).to_string()))
2697 }
2698 "pad_start" => {
2699 let width = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2700 IonError::type_err(ion_str!("pad_start requires int argument"), line, col)
2701 })? as usize;
2702 let ch = args
2703 .get(1)
2704 .and_then(|a| a.as_str())
2705 .and_then(|s| s.chars().next())
2706 .unwrap_or(' ');
2707 let char_len = s.chars().count();
2708 if char_len >= width {
2709 Ok(Value::Str(s.to_string()))
2710 } else {
2711 let pad: String = std::iter::repeat_n(ch, width - char_len).collect();
2712 Ok(Value::Str(format!("{}{}", pad, s)))
2713 }
2714 }
2715 "pad_end" => {
2716 let width = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2717 IonError::type_err(ion_str!("pad_end requires int argument"), line, col)
2718 })? as usize;
2719 let ch = args
2720 .get(1)
2721 .and_then(|a| a.as_str())
2722 .and_then(|s| s.chars().next())
2723 .unwrap_or(' ');
2724 let char_len = s.chars().count();
2725 if char_len >= width {
2726 Ok(Value::Str(s.to_string()))
2727 } else {
2728 let pad: String = std::iter::repeat_n(ch, width - char_len).collect();
2729 Ok(Value::Str(format!("{}{}", s, pad)))
2730 }
2731 }
2732 "reverse" => Ok(Value::Str(s.chars().rev().collect())),
2733 "slice" => {
2734 let chars: Vec<char> = s.chars().collect();
2735 let char_count = chars.len();
2736 let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2737 let end = args
2738 .get(1)
2739 .and_then(|a| a.as_int())
2740 .map(|n| n as usize)
2741 .unwrap_or(char_count);
2742 let start = start.min(char_count);
2743 let end = end.min(char_count);
2744 Ok(Value::Str(chars[start..end].iter().collect()))
2745 }
2746 _ => Err(IonError::type_err(
2747 format!(
2748 "{}{}{}",
2749 ion_str!("string has no method '"),
2750 method,
2751 ion_str!("'")
2752 ),
2753 line,
2754 col,
2755 )),
2756 }
2757 }
2758
2759 fn bytes_method(
2760 &self,
2761 bytes: &[u8],
2762 method: &str,
2763 args: &[Value],
2764 line: usize,
2765 col: usize,
2766 ) -> Result<Value, IonError> {
2767 match method {
2768 "len" => Ok(Value::Int(bytes.len() as i64)),
2769 "is_empty" => Ok(Value::Bool(bytes.is_empty())),
2770 "contains" => {
2771 let byte = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2772 IonError::type_err(ion_str!("bytes.contains() requires an int"), line, col)
2773 })?;
2774 Ok(Value::Bool(bytes.contains(&(byte as u8))))
2775 }
2776 "slice" => {
2777 let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2778 let end = args
2779 .get(1)
2780 .and_then(|a| a.as_int())
2781 .map(|n| n as usize)
2782 .unwrap_or(bytes.len());
2783 let start = start.min(bytes.len());
2784 let end = end.min(bytes.len());
2785 Ok(Value::Bytes(bytes[start..end].to_vec()))
2786 }
2787 "to_list" => Ok(Value::List(
2788 bytes.iter().map(|&b| Value::Int(b as i64)).collect(),
2789 )),
2790 "to_str" => match std::str::from_utf8(bytes) {
2791 std::result::Result::Ok(s) => {
2792 Ok(Value::Result(Ok(Box::new(Value::Str(s.to_string())))))
2793 }
2794 std::result::Result::Err(e) => {
2795 Ok(Value::Result(Err(Box::new(Value::Str(format!("{}", e))))))
2796 }
2797 },
2798 "to_hex" => {
2799 let hex: String = bytes.iter().map(|b| format!("{:02x}", b)).collect();
2800 Ok(Value::Str(hex))
2801 }
2802 "find" => {
2803 let needle = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2804 IonError::type_err(ion_str!("bytes.find() requires an int"), line, col)
2805 })?;
2806 let pos = bytes.iter().position(|&b| b == needle as u8);
2807 Ok(match pos {
2808 Some(i) => Value::Option(Some(Box::new(Value::Int(i as i64)))),
2809 None => Value::Option(None),
2810 })
2811 }
2812 "reverse" => {
2813 let mut rev = bytes.to_vec();
2814 rev.reverse();
2815 Ok(Value::Bytes(rev))
2816 }
2817 "push" => {
2818 let byte = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2819 IonError::type_err(ion_str!("bytes.push() requires an int"), line, col)
2820 })?;
2821 let mut new = bytes.to_vec();
2822 new.push(byte as u8);
2823 Ok(Value::Bytes(new))
2824 }
2825 _ => Err(IonError::type_err(
2826 format!(
2827 "{}{}{}",
2828 ion_str!("bytes has no method '"),
2829 method,
2830 ion_str!("'")
2831 ),
2832 line,
2833 col,
2834 )),
2835 }
2836 }
2837
2838 fn dict_method(
2839 &self,
2840 map: &IndexMap<String, Value>,
2841 method: &str,
2842 args: &[Value],
2843 line: usize,
2844 col: usize,
2845 ) -> Result<Value, IonError> {
2846 match method {
2847 "len" => Ok(Value::Int(map.len() as i64)),
2848 "keys" => {
2849 let keys: Vec<Value> = map.keys().map(|k| Value::Str(k.clone())).collect();
2850 Ok(Value::List(keys))
2851 }
2852 "values" => {
2853 let vals: Vec<Value> = map.values().cloned().collect();
2854 Ok(Value::List(vals))
2855 }
2856 "contains_key" => {
2857 let key = args.first().and_then(|a| a.as_str()).unwrap_or("");
2858 Ok(Value::Bool(map.contains_key(key)))
2859 }
2860 "get" => {
2861 let key = args.first().and_then(|a| a.as_str()).unwrap_or("");
2862 Ok(match map.get(key) {
2863 Some(v) => Value::Option(Some(Box::new(v.clone()))),
2864 None => Value::Option(None),
2865 })
2866 }
2867 "is_empty" => Ok(Value::Bool(map.is_empty())),
2868 "entries" => Ok(Value::List(
2869 map.iter()
2870 .map(|(k, v)| Value::Tuple(vec![Value::Str(k.clone()), v.clone()]))
2871 .collect(),
2872 )),
2873 "insert" => {
2874 let key = args.first().and_then(|a| a.as_str()).unwrap_or("");
2875 let val = args.get(1).cloned().unwrap_or(Value::Unit);
2876 let mut new_map = map.clone();
2877 new_map.insert(key.to_string(), val);
2878 Ok(Value::Dict(new_map))
2879 }
2880 "remove" => {
2881 let key = args.first().and_then(|a| a.as_str()).unwrap_or("");
2882 let mut new_map = map.clone();
2883 new_map.shift_remove(key);
2884 Ok(Value::Dict(new_map))
2885 }
2886 "merge" => {
2887 if let Some(Value::Dict(other)) = args.first() {
2888 let mut new_map = map.clone();
2889 for (k, v) in other {
2890 new_map.insert(k.clone(), v.clone());
2891 }
2892 Ok(Value::Dict(new_map))
2893 } else {
2894 Err(IonError::type_err(
2895 ion_str!("merge requires a dict argument"),
2896 line,
2897 col,
2898 ))
2899 }
2900 }
2901 "update" => {
2902 if let Some(Value::Dict(other)) = args.first() {
2903 let mut new_map = map.clone();
2904 for (k, v) in other {
2905 new_map.insert(k.clone(), v.clone());
2906 }
2907 Ok(Value::Dict(new_map))
2908 } else {
2909 Err(IonError::type_err(
2910 ion_str!("update requires a dict argument"),
2911 line,
2912 col,
2913 ))
2914 }
2915 }
2916 "keys_of" => {
2917 let target = args.first().ok_or_else(|| {
2918 IonError::type_err(ion_str!("keys_of requires an argument"), line, col)
2919 })?;
2920 let keys: Vec<Value> = map
2921 .iter()
2922 .filter(|(_, v)| *v == target)
2923 .map(|(k, _)| Value::Str(k.clone()))
2924 .collect();
2925 Ok(Value::List(keys))
2926 }
2927 "zip" => {
2928 if let Some(Value::Dict(other)) = args.first() {
2929 let mut result = indexmap::IndexMap::new();
2930 for (k, v) in map {
2931 if let Some(ov) = other.get(k) {
2932 result.insert(k.clone(), Value::Tuple(vec![v.clone(), ov.clone()]));
2933 }
2934 }
2935 Ok(Value::Dict(result))
2936 } else {
2937 Err(IonError::type_err(
2938 ion_str!("zip requires a dict argument"),
2939 line,
2940 col,
2941 ))
2942 }
2943 }
2944 _ => Err(IonError::type_err(
2945 format!(
2946 "{}{}{}",
2947 ion_str!("dict has no method '"),
2948 method,
2949 ion_str!("'")
2950 ),
2951 line,
2952 col,
2953 )),
2954 }
2955 }
2956
2957 fn option_method(
2958 &self,
2959 val: &Value,
2960 method: &str,
2961 args: &[Value],
2962 line: usize,
2963 col: usize,
2964 ) -> Result<Value, IonError> {
2965 let opt = match val {
2966 Value::Option(o) => o,
2967 _ => return Err(IonError::type_err(ion_str!("expected Option"), line, col)),
2968 };
2969 match method {
2970 "is_some" => Ok(Value::Bool(opt.is_some())),
2971 "is_none" => Ok(Value::Bool(opt.is_none())),
2972 "unwrap" => match opt {
2973 Some(v) => Ok(*v.clone()),
2974 None => Err(IonError::runtime(
2975 ion_str!("called unwrap on None"),
2976 line,
2977 col,
2978 )),
2979 },
2980 "unwrap_or" => Ok(opt
2981 .as_ref()
2982 .map(|v| *v.clone())
2983 .unwrap_or_else(|| args.first().cloned().unwrap_or(Value::Unit))),
2984 "expect" => match opt {
2985 Some(v) => Ok(*v.clone()),
2986 None => {
2987 let msg = args
2988 .first()
2989 .and_then(|a| a.as_str())
2990 .unwrap_or("called expect on None");
2991 Err(IonError::runtime(msg.to_string(), line, col))
2992 }
2993 },
2994 _ => Err(IonError::type_err(
2995 format!(
2996 "{}{}{}",
2997 ion_str!("Option has no method '"),
2998 method,
2999 ion_str!("'")
3000 ),
3001 line,
3002 col,
3003 )),
3004 }
3005 }
3006
3007 fn result_method(
3008 &self,
3009 val: &Value,
3010 method: &str,
3011 args: &[Value],
3012 line: usize,
3013 col: usize,
3014 ) -> Result<Value, IonError> {
3015 let res = match val {
3016 Value::Result(r) => r,
3017 _ => return Err(IonError::type_err(ion_str!("expected Result"), line, col)),
3018 };
3019 match method {
3020 "is_ok" => Ok(Value::Bool(res.is_ok())),
3021 "is_err" => Ok(Value::Bool(res.is_err())),
3022 "unwrap" => match res {
3023 Ok(v) => Ok(*v.clone()),
3024 Err(e) => Err(IonError::runtime(
3025 format!("{}{}", ion_str!("called unwrap on Err: "), e),
3026 line,
3027 col,
3028 )),
3029 },
3030 "unwrap_or" => Ok(match res {
3031 Ok(v) => *v.clone(),
3032 Err(_) => args.first().cloned().unwrap_or(Value::Unit),
3033 }),
3034 "expect" => match res {
3035 Ok(v) => Ok(*v.clone()),
3036 Err(e) => {
3037 let msg = args
3038 .first()
3039 .and_then(|a| a.as_str())
3040 .unwrap_or("called expect on Err");
3041 Err(IonError::runtime(format!("{}: {}", msg, e), line, col))
3042 }
3043 },
3044 _ => Err(IonError::type_err(
3045 format!(
3046 "{}{}{}",
3047 ion_str!("Result has no method '"),
3048 method,
3049 ion_str!("'")
3050 ),
3051 line,
3052 col,
3053 )),
3054 }
3055 }
3056
3057 fn invoke_value(
3061 &mut self,
3062 func: &Value,
3063 args: &[Value],
3064 line: usize,
3065 col: usize,
3066 ) -> Result<Value, IonError> {
3067 self.stack.push(func.clone());
3069 for arg in args {
3070 self.stack.push(arg.clone());
3071 }
3072 self.call_function(args.len(), line, col)?;
3073 self.pop(line, col)
3074 }
3075
3076 fn eval_default_arg(
3077 &self,
3078 param_name: &str,
3079 default: &crate::ast::Expr,
3080 line: usize,
3081 col: usize,
3082 ) -> Result<Value, IonError> {
3083 let mut interp = crate::interpreter::Interpreter::with_env(self.env.clone());
3084 interp.types = self.types.clone();
3085 interp.eval_single_expr(default).map_err(|e| {
3086 IonError::runtime(
3087 format!(
3088 "{}'{}': {}",
3089 ion_str!("error evaluating default for "),
3090 param_name,
3091 e.message
3092 ),
3093 line,
3094 col,
3095 )
3096 })
3097 }
3098
3099 fn prepare_positional_function_args(
3100 &mut self,
3101 ion_fn: &crate::value::IonFn,
3102 args: &[Value],
3103 line: usize,
3104 col: usize,
3105 ) -> Result<Vec<Value>, IonError> {
3106 let mut prepared = Vec::with_capacity(ion_fn.params.len());
3107 for (i, param) in ion_fn.params.iter().enumerate() {
3108 let val = if i < args.len() {
3109 args[i].clone()
3110 } else if let Some(default) = ¶m.default {
3111 self.eval_default_arg(¶m.name, default, line, col)?
3112 } else {
3113 return Err(IonError::runtime(
3114 format!(
3115 "{}{}{}{}{}{}",
3116 ion_str!("function '"),
3117 ion_fn.name,
3118 ion_str!("' expected "),
3119 ion_fn.params.len(),
3120 ion_str!(" arguments, got "),
3121 args.len(),
3122 ),
3123 line,
3124 col,
3125 ));
3126 };
3127 self.env.define(param.name.clone(), val.clone(), false);
3128 prepared.push(val);
3129 }
3130 Ok(prepared)
3131 }
3132
3133 fn prepare_named_function_args(
3134 &mut self,
3135 ion_fn: &crate::value::IonFn,
3136 ordered: &[Option<Value>],
3137 line: usize,
3138 col: usize,
3139 ) -> Result<Vec<Value>, IonError> {
3140 let mut prepared = Vec::with_capacity(ion_fn.params.len());
3141 for (i, param) in ion_fn.params.iter().enumerate() {
3142 let val = if let Some(Some(val)) = ordered.get(i) {
3143 val.clone()
3144 } else if let Some(default) = ¶m.default {
3145 self.eval_default_arg(¶m.name, default, line, col)?
3146 } else {
3147 return Err(IonError::runtime(
3148 format!(
3149 "{}{}{}",
3150 ion_str!("missing argument '"),
3151 param.name,
3152 ion_str!("'"),
3153 ),
3154 line,
3155 col,
3156 ));
3157 };
3158 self.env.define(param.name.clone(), val.clone(), false);
3159 prepared.push(val);
3160 }
3161 Ok(prepared)
3162 }
3163
3164 fn call_function(&mut self, arg_count: usize, line: usize, col: usize) -> Result<(), IonError> {
3165 let args_start = self.stack.len() - arg_count;
3167 let func_idx = args_start - 1;
3168 let mut func = self.stack[func_idx].clone();
3169 let mut args: Vec<Value> = self.stack[args_start..].to_vec();
3170 self.stack.truncate(func_idx);
3171
3172 loop {
3174 match func {
3175 #[cfg(feature = "concurrency")]
3176 Value::BuiltinFn(ref name, _) if name == "timeout" => {
3177 let result = self.builtin_timeout(&args, line, col)?;
3178 self.stack.push(result);
3179 return Ok(());
3180 }
3181 Value::BuiltinFn(_name, f) => {
3182 let result = f(&args).map_err(|e| IonError::runtime(e, line, col))?;
3183 self.stack.push(result);
3184 return Ok(());
3185 }
3186 Value::BuiltinClosure(_name, f) => {
3187 let result = f.call(&args).map_err(|e| IonError::runtime(e, line, col))?;
3188 self.stack.push(result);
3189 return Ok(());
3190 }
3191 Value::Fn(ion_fn) => {
3192 self.env.push_scope();
3193
3194 for (name, val) in &ion_fn.captures {
3195 self.env.define(name.clone(), val.clone(), false);
3196 }
3197
3198 let saved_locals_base = self.locals_base;
3200 let saved_locals_len = self.locals.len();
3201 let saved_frames_len = self.local_frames.len();
3202 let prepared_args =
3203 match self.prepare_positional_function_args(&ion_fn, &args, line, col) {
3204 Ok(args) => args,
3205 Err(e) => {
3206 self.env.pop_scope();
3207 return Err(e);
3208 }
3209 };
3210
3211 self.locals_base = self.locals.len(); for val in prepared_args {
3215 self.locals.push(LocalSlot {
3216 value: val,
3217 mutable: false,
3218 });
3219 }
3220
3221 let fn_id = ion_fn.fn_id;
3222 let chunk_opt = if let Some(chunk) = self.fn_cache.get(&fn_id) {
3223 Some(chunk.clone())
3224 } else {
3225 let compiler = crate::compiler::Compiler::new();
3226 compiler
3227 .compile_fn_body(&ion_fn.params, &ion_fn.body, line)
3228 .ok()
3229 };
3230 if let Some(chunk) = chunk_opt {
3231 self.fn_cache.entry(fn_id).or_insert_with(|| chunk.clone());
3232 let saved_ip = self.ip;
3233 let saved_iters = std::mem::take(&mut self.iterators);
3234 self.ip = 0;
3235 let result = self.run_chunk(&chunk);
3236 self.ip = saved_ip;
3237 self.iterators = saved_iters;
3238 self.locals.truncate(saved_locals_len);
3240 self.local_frames.truncate(saved_frames_len);
3241 self.locals_base = saved_locals_base;
3242 self.env.pop_scope();
3243
3244 if let Some((tail_func, tail_args)) = self.pending_tail_call.take() {
3246 func = tail_func;
3247 args = tail_args;
3248 continue; }
3250
3251 match result {
3252 Ok(val) => self.stack.push(val),
3253 Err(e) if e.kind == crate::error::ErrorKind::PropagatedErr => {
3254 self.stack.push(Value::Result(Err(Box::new(Value::Str(
3255 e.message.clone(),
3256 )))));
3257 }
3258 Err(e) if e.kind == crate::error::ErrorKind::PropagatedNone => {
3259 self.stack.push(Value::Option(None));
3260 }
3261 Err(e) => return Err(e),
3262 }
3263 } else {
3264 self.locals.truncate(saved_locals_len);
3266 self.local_frames.truncate(saved_frames_len);
3267 self.locals_base = saved_locals_base;
3268 let mut interp =
3269 crate::interpreter::Interpreter::with_env(self.env.clone());
3270 let result = interp.eval_block(&ion_fn.body);
3271 self.env = interp.take_env();
3272 self.env.pop_scope();
3273 match result {
3274 Ok(val) => self.stack.push(val),
3275 Err(e) if e.kind == crate::error::ErrorKind::PropagatedErr => {
3276 self.stack.push(Value::Result(Err(Box::new(Value::Str(
3277 e.message.clone(),
3278 )))));
3279 }
3280 Err(e) if e.kind == crate::error::ErrorKind::PropagatedNone => {
3281 self.stack.push(Value::Option(None));
3282 }
3283 Err(e) => return Err(e),
3284 }
3285 }
3286 return Ok(());
3287 }
3288 _ => {
3289 return Err(IonError::type_err(
3290 format!("{}{}", ion_str!("cannot call "), func.type_name()),
3291 line,
3292 col,
3293 ));
3294 }
3295 }
3296 }
3297 }
3298
3299 fn call_function_named(
3300 &mut self,
3301 arg_count: usize,
3302 named_map: &[(usize, String)],
3303 line: usize,
3304 col: usize,
3305 ) -> Result<(), IonError> {
3306 let args_start = self.stack.len() - arg_count;
3308 let func_idx = args_start - 1;
3309 let func = self.stack[func_idx].clone();
3310 let raw_args: Vec<Value> = self.stack[args_start..].to_vec();
3311 self.stack.truncate(func_idx);
3312
3313 match &func {
3314 Value::Fn(ion_fn) => {
3315 let mut ordered = vec![None; ion_fn.params.len()];
3317 let mut pos_idx = 0;
3318 for (i, val) in raw_args.into_iter().enumerate() {
3319 if let Some((_, ref name)) = named_map.iter().find(|(pos, _)| *pos == i) {
3320 let param_idx = ion_fn
3322 .params
3323 .iter()
3324 .position(|p| &p.name == name)
3325 .ok_or_else(|| {
3326 IonError::runtime(
3327 format!(
3328 "{}'{}'{}'{}'",
3329 ion_str!("unknown parameter '"),
3330 name,
3331 ion_str!("' for function '"),
3332 ion_fn.name
3333 ),
3334 line,
3335 col,
3336 )
3337 })?;
3338 ordered[param_idx] = Some(val);
3339 } else {
3340 while pos_idx < ordered.len() && ordered[pos_idx].is_some() {
3342 pos_idx += 1;
3343 }
3344 if pos_idx < ordered.len() {
3345 ordered[pos_idx] = Some(val);
3346 pos_idx += 1;
3347 }
3348 }
3349 }
3350 self.env.push_scope();
3352 for (name, val) in &ion_fn.captures {
3353 self.env.define(name.clone(), val.clone(), false);
3354 }
3355 let reordered = match self.prepare_named_function_args(ion_fn, &ordered, line, col)
3356 {
3357 Ok(args) => args,
3358 Err(e) => {
3359 self.env.pop_scope();
3360 return Err(e);
3361 }
3362 };
3363 self.env.pop_scope();
3364 self.stack.push(func.clone());
3366 for arg in &reordered {
3367 self.stack.push(arg.clone());
3368 }
3369 self.call_function(reordered.len(), line, col)
3370 }
3371 Value::BuiltinFn(_, f) => {
3372 let result = f(&raw_args).map_err(|e| IonError::runtime(e, line, col))?;
3374 self.stack.push(result);
3375 Ok(())
3376 }
3377 Value::BuiltinClosure(_, f) => {
3378 let result = f
3379 .call(&raw_args)
3380 .map_err(|e| IonError::runtime(e, line, col))?;
3381 self.stack.push(result);
3382 Ok(())
3383 }
3384 _ => Err(IonError::type_err(
3385 format!("cannot call {}", func.type_name()),
3386 line,
3387 col,
3388 )),
3389 }
3390 }
3391
3392 #[cfg(feature = "concurrency")]
3393 fn builtin_timeout(&self, args: &[Value], line: usize, col: usize) -> Result<Value, IonError> {
3394 if args.len() < 2 {
3395 return Err(IonError::runtime(
3396 ion_str!("timeout(ms, fn) requires 2 arguments"),
3397 line,
3398 col,
3399 ));
3400 }
3401 let ms = args[0].as_int().ok_or_else(|| {
3402 IonError::runtime(
3403 ion_str!("timeout: first argument must be int (ms)"),
3404 line,
3405 col,
3406 )
3407 })?;
3408 let func = args[1].clone();
3409 let captured_env = self.env.capture();
3410 let cancel_flag = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
3411 let task = crate::async_rt::spawn_task_with_cancel(cancel_flag, move |flag| {
3412 let mut child = crate::interpreter::Interpreter::new();
3413 child.cancel_flag = Some(flag);
3414 for (name, val) in captured_env {
3415 child.env.define(name, val, false);
3416 }
3417 let program = crate::ast::Program {
3419 stmts: vec![crate::ast::Stmt {
3420 kind: crate::ast::StmtKind::ExprStmt {
3421 expr: crate::ast::Expr {
3422 kind: crate::ast::ExprKind::Call {
3423 func: Box::new(crate::ast::Expr {
3424 kind: crate::ast::ExprKind::Ident("__timeout_fn__".to_string()),
3425 span: crate::ast::Span { line: 0, col: 0 },
3426 }),
3427 args: vec![],
3428 },
3429 span: crate::ast::Span { line: 0, col: 0 },
3430 },
3431 has_semi: false,
3432 },
3433 span: crate::ast::Span { line: 0, col: 0 },
3434 }],
3435 };
3436 child.env.define("__timeout_fn__".to_string(), func, false);
3437 child.eval_program(&program)
3438 });
3439 match task.join_timeout(std::time::Duration::from_millis(ms as u64)) {
3440 Some(Ok(val)) => Ok(Value::Option(Some(Box::new(val)))),
3441 Some(Err(e)) => Err(e),
3442 None => {
3443 task.cancel();
3444 Ok(Value::Option(None))
3445 }
3446 }
3447 }
3448}