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