1use crate::ast::*;
4use crate::bytecode::{Chunk, Op};
5use crate::error::IonError;
6use crate::value::{FnChunkCache, Value};
7
8#[derive(Debug, Clone)]
10struct Local {
11 name: String,
12 depth: usize,
13}
14
15fn unmatched_label_msg(keyword: &str, label: Option<&str>) -> String {
16 match label {
17 Some(name) => format!("{keyword} with unknown label '{name}"),
18 None => format!("{keyword} outside of loop"),
19 }
20}
21
22#[derive(Debug)]
25struct LoopFrame {
26 label: Option<String>,
27 break_jumps: Vec<usize>,
29 continue_target: usize,
31 is_for_loop: bool,
34 scope_depth: usize,
36}
37
38pub struct Compiler {
39 chunk: Chunk,
40 pub fn_chunks: FnChunkCache,
42 in_tail_position: bool,
44 locals: Vec<Local>,
46 scope_depth: usize,
48 needs_env_locals: bool,
50 loop_stack: Vec<LoopFrame>,
52 #[cfg(feature = "async-runtime")]
54 async_scope_depth: usize,
55}
56
57impl Default for Compiler {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl Compiler {
64 pub fn new() -> Self {
65 Self {
66 chunk: Chunk::new(),
67 fn_chunks: FnChunkCache::new(),
68 in_tail_position: false,
69 locals: Vec::new(),
70 scope_depth: 0,
71 needs_env_locals: true, loop_stack: Vec::new(),
73 #[cfg(feature = "async-runtime")]
74 async_scope_depth: 0,
75 }
76 }
77
78 fn resolve_loop_target(&self, label: Option<&str>) -> Option<usize> {
82 match label {
83 None => self.loop_stack.len().checked_sub(1),
84 Some(want) => self
85 .loop_stack
86 .iter()
87 .rposition(|f| f.label.as_deref() == Some(want)),
88 }
89 }
90
91 fn stmts_have_closures(stmts: &[Stmt]) -> bool {
93 for stmt in stmts {
94 match &stmt.kind {
95 StmtKind::FnDecl { body: _, .. } => {
96 return true;
98 }
101 StmtKind::ExprStmt { expr, .. } if Self::expr_has_closures(expr) => {
102 return true;
103 }
104 StmtKind::Let { value, .. } if Self::expr_has_closures(value) => {
105 return true;
106 }
107 StmtKind::For { body, iter, .. } => {
108 if Self::expr_has_closures(iter) {
109 return true;
110 }
111 if Self::stmts_have_closures(body) {
112 return true;
113 }
114 }
115 StmtKind::While { cond, body, .. } => {
116 if Self::expr_has_closures(cond) {
117 return true;
118 }
119 if Self::stmts_have_closures(body) {
120 return true;
121 }
122 }
123 StmtKind::Loop { body, .. } if Self::stmts_have_closures(body) => {
124 return true;
125 }
126 StmtKind::Return { value: Some(e) } if Self::expr_has_closures(e) => {
127 return true;
128 }
129 StmtKind::Assign { value, .. } if Self::expr_has_closures(value) => {
130 return true;
131 }
132 StmtKind::WhileLet { expr, body, .. } => {
133 if Self::expr_has_closures(expr) {
134 return true;
135 }
136 if Self::stmts_have_closures(body) {
137 return true;
138 }
139 }
140 _ => {}
141 }
142 }
143 false
144 }
145
146 fn expr_has_closures(expr: &Expr) -> bool {
147 match &expr.kind {
148 ExprKind::Lambda { .. } => true,
149 ExprKind::If {
150 cond,
151 then_body,
152 else_body,
153 } => {
154 Self::expr_has_closures(cond)
155 || Self::stmts_have_closures(then_body)
156 || else_body
157 .as_ref()
158 .is_some_and(|b| Self::stmts_have_closures(b))
159 }
160 ExprKind::Block(stmts) => Self::stmts_have_closures(stmts),
161 ExprKind::Call { func, args } => {
162 Self::expr_has_closures(func)
163 || args.iter().any(|a| Self::expr_has_closures(&a.value))
164 }
165 ExprKind::MethodCall { expr, args, .. } => {
166 Self::expr_has_closures(expr)
167 || args.iter().any(|a| Self::expr_has_closures(&a.value))
168 }
169 ExprKind::BinOp { left, right, .. } => {
170 Self::expr_has_closures(left) || Self::expr_has_closures(right)
171 }
172 ExprKind::UnaryOp { expr, .. } => Self::expr_has_closures(expr),
173 ExprKind::PipeOp { left, right } => {
174 Self::expr_has_closures(left) || Self::expr_has_closures(right)
175 }
176 ExprKind::Match { expr, arms } => {
177 Self::expr_has_closures(expr)
178 || arms.iter().any(|a| Self::expr_has_closures(&a.body))
179 }
180 ExprKind::List(items) => items.iter().any(|e| match e {
181 ListEntry::Elem(expr) | ListEntry::Spread(expr) => Self::expr_has_closures(expr),
182 }),
183 ExprKind::Tuple(items) => items.iter().any(Self::expr_has_closures),
184 ExprKind::ListComp {
185 expr, iter, cond, ..
186 } => {
187 Self::expr_has_closures(expr)
188 || Self::expr_has_closures(iter)
189 || cond.as_ref().is_some_and(|c| Self::expr_has_closures(c))
190 }
191 ExprKind::IfLet {
192 expr,
193 then_body,
194 else_body,
195 ..
196 } => {
197 Self::expr_has_closures(expr)
198 || Self::stmts_have_closures(then_body)
199 || else_body
200 .as_ref()
201 .is_some_and(|b| Self::stmts_have_closures(b))
202 }
203 ExprKind::TryCatch { body, handler, .. } => {
204 Self::stmts_have_closures(body) || Self::stmts_have_closures(handler)
205 }
206 ExprKind::LoopExpr(stmts) => Self::stmts_have_closures(stmts),
207 ExprKind::Range { start, end, .. } => {
208 Self::expr_has_closures(start) || Self::expr_has_closures(end)
209 }
210 ExprKind::Dict(entries) => entries.iter().any(|e| match e {
211 DictEntry::KeyValue(k, v) => {
212 Self::expr_has_closures(k) || Self::expr_has_closures(v)
213 }
214 DictEntry::Spread(expr) => Self::expr_has_closures(expr),
215 }),
216 ExprKind::DictComp {
217 key,
218 value,
219 iter,
220 cond,
221 ..
222 } => {
223 Self::expr_has_closures(key)
224 || Self::expr_has_closures(value)
225 || Self::expr_has_closures(iter)
226 || cond.as_ref().is_some_and(|c| Self::expr_has_closures(c))
227 }
228 ExprKind::FieldAccess { expr, .. }
229 | ExprKind::Try(expr)
230 | ExprKind::SomeExpr(expr)
231 | ExprKind::OkExpr(expr)
232 | ExprKind::ErrExpr(expr) => Self::expr_has_closures(expr),
233 ExprKind::Index { expr, index } => {
234 Self::expr_has_closures(expr) || Self::expr_has_closures(index)
235 }
236 ExprKind::Slice {
237 expr, start, end, ..
238 } => {
239 Self::expr_has_closures(expr)
240 || start.as_ref().is_some_and(|s| Self::expr_has_closures(s))
241 || end.as_ref().is_some_and(|e| Self::expr_has_closures(e))
242 }
243 ExprKind::FStr(parts) => parts.iter().any(|p| match p {
244 FStrPart::Expr(e) => Self::expr_has_closures(e),
245 _ => false,
246 }),
247 ExprKind::StructConstruct { fields, spread, .. } => {
248 fields.iter().any(|(_, e)| Self::expr_has_closures(e))
249 || spread.as_ref().is_some_and(|s| Self::expr_has_closures(s))
250 }
251 _ => false,
252 }
253 }
254
255 fn try_fold_binop(left: &Expr, op: &BinOp, right: &Expr) -> Option<Value> {
257 match (&left.kind, op, &right.kind) {
258 (ExprKind::Int(a), BinOp::Add, ExprKind::Int(b)) => {
260 Some(Value::Int(a.wrapping_add(*b)))
261 }
262 (ExprKind::Int(a), BinOp::Sub, ExprKind::Int(b)) => {
263 Some(Value::Int(a.wrapping_sub(*b)))
264 }
265 (ExprKind::Int(a), BinOp::Mul, ExprKind::Int(b)) => {
266 Some(Value::Int(a.wrapping_mul(*b)))
267 }
268 (ExprKind::Int(a), BinOp::Div, ExprKind::Int(b)) if *b != 0 => Some(Value::Int(a / b)),
269 (ExprKind::Int(a), BinOp::Mod, ExprKind::Int(b)) if *b != 0 => Some(Value::Int(a % b)),
270 (ExprKind::Int(a), BinOp::Eq, ExprKind::Int(b)) => Some(Value::Bool(a == b)),
271 (ExprKind::Int(a), BinOp::Ne, ExprKind::Int(b)) => Some(Value::Bool(a != b)),
272 (ExprKind::Int(a), BinOp::Lt, ExprKind::Int(b)) => Some(Value::Bool(a < b)),
273 (ExprKind::Int(a), BinOp::Gt, ExprKind::Int(b)) => Some(Value::Bool(a > b)),
274 (ExprKind::Int(a), BinOp::Le, ExprKind::Int(b)) => Some(Value::Bool(a <= b)),
275 (ExprKind::Int(a), BinOp::Ge, ExprKind::Int(b)) => Some(Value::Bool(a >= b)),
276 (ExprKind::Int(a), BinOp::BitAnd, ExprKind::Int(b)) => Some(Value::Int(a & b)),
277 (ExprKind::Int(a), BinOp::BitOr, ExprKind::Int(b)) => Some(Value::Int(a | b)),
278 (ExprKind::Int(a), BinOp::BitXor, ExprKind::Int(b)) => Some(Value::Int(a ^ b)),
279 (ExprKind::Int(a), BinOp::Shl, ExprKind::Int(b)) if (0..64).contains(b) => {
280 Some(Value::Int(a << (*b as u32)))
281 }
282 (ExprKind::Int(a), BinOp::Shr, ExprKind::Int(b)) if (0..64).contains(b) => {
283 Some(Value::Int(a >> (*b as u32)))
284 }
285 (ExprKind::Float(a), BinOp::Add, ExprKind::Float(b)) => Some(Value::Float(a + b)),
287 (ExprKind::Float(a), BinOp::Sub, ExprKind::Float(b)) => Some(Value::Float(a - b)),
288 (ExprKind::Float(a), BinOp::Mul, ExprKind::Float(b)) => Some(Value::Float(a * b)),
289 (ExprKind::Float(a), BinOp::Div, ExprKind::Float(b)) => Some(Value::Float(a / b)),
290 (ExprKind::Float(a), BinOp::Mod, ExprKind::Float(b)) => Some(Value::Float(a % b)),
291 (ExprKind::Int(a), BinOp::Add, ExprKind::Float(b)) => Some(Value::Float(*a as f64 + b)),
293 (ExprKind::Float(a), BinOp::Add, ExprKind::Int(b)) => Some(Value::Float(a + *b as f64)),
294 (ExprKind::Int(a), BinOp::Sub, ExprKind::Float(b)) => Some(Value::Float(*a as f64 - b)),
295 (ExprKind::Float(a), BinOp::Sub, ExprKind::Int(b)) => Some(Value::Float(a - *b as f64)),
296 (ExprKind::Int(a), BinOp::Mul, ExprKind::Float(b)) => Some(Value::Float(*a as f64 * b)),
297 (ExprKind::Float(a), BinOp::Mul, ExprKind::Int(b)) => Some(Value::Float(a * *b as f64)),
298 (ExprKind::Int(a), BinOp::Div, ExprKind::Float(b)) => Some(Value::Float(*a as f64 / b)),
299 (ExprKind::Float(a), BinOp::Div, ExprKind::Int(b)) => Some(Value::Float(a / *b as f64)),
300 (ExprKind::Str(a), BinOp::Add, ExprKind::Str(b)) => {
302 let mut s = a.clone();
303 s.push_str(b);
304 Some(Value::Str(s))
305 }
306 (ExprKind::Bool(a), BinOp::And, ExprKind::Bool(b)) => Some(Value::Bool(*a && *b)),
308 (ExprKind::Bool(a), BinOp::Or, ExprKind::Bool(b)) => Some(Value::Bool(*a || *b)),
309 (ExprKind::Bool(a), BinOp::Eq, ExprKind::Bool(b)) => Some(Value::Bool(a == b)),
310 (ExprKind::Bool(a), BinOp::Ne, ExprKind::Bool(b)) => Some(Value::Bool(a != b)),
311 _ => None,
312 }
313 }
314
315 fn try_fold_unary(op: &UnaryOp, inner: &Expr) -> Option<Value> {
317 match (op, &inner.kind) {
318 (UnaryOp::Neg, ExprKind::Int(v)) => Some(Value::Int(-v)),
319 (UnaryOp::Neg, ExprKind::Float(v)) => Some(Value::Float(-v)),
320 (UnaryOp::Not, ExprKind::Bool(v)) => Some(Value::Bool(!v)),
321 _ => None,
322 }
323 }
324
325 fn stmt_is_terminal(stmt: &Stmt) -> bool {
327 matches!(
328 &stmt.kind,
329 StmtKind::Return { .. } | StmtKind::Break { .. } | StmtKind::Continue { .. }
330 )
331 }
332
333 fn resolve_local(&self, name: &str) -> Option<usize> {
335 for (i, local) in self.locals.iter().enumerate().rev() {
336 if local.name == name {
337 return Some(i);
338 }
339 }
340 None
341 }
342
343 fn add_local(&mut self, name: String, _mutable: bool) {
345 self.locals.push(Local {
346 name,
347 depth: self.scope_depth,
348 });
349 }
350
351 fn begin_scope(&mut self, line: usize) {
353 self.scope_depth += 1;
354 self.chunk.emit_op(Op::PushScope, line);
355 }
356
357 fn emit_get_var(&mut self, name: &str, line: usize) {
359 if let Some(slot) = self.resolve_local(name) {
360 self.chunk.emit_op_u16(Op::GetLocalSlot, slot as u16, line);
361 } else {
362 let idx = self.chunk.add_constant(Value::Str(name.to_string()));
363 self.chunk.emit_op_u16(Op::GetGlobal, idx, line);
364 }
365 }
366
367 fn emit_define_local(&mut self, name: &str, mutable: bool, line: usize) {
370 self.add_local(name.to_string(), mutable);
371 if self.needs_env_locals {
372 self.chunk.emit_op(Op::Dup, line);
374 let idx = self.chunk.add_constant(Value::Str(name.to_string()));
375 self.chunk.emit_op_u16(Op::DefineLocal, idx, line);
376 self.chunk.emit(if mutable { 1 } else { 0 }, line);
377 }
378 self.chunk
379 .emit_op_u8(Op::DefineLocalSlot, if mutable { 1 } else { 0 }, line);
380 }
381
382 fn emit_set_var(&mut self, name: &str, line: usize) {
384 if let Some(slot) = self.resolve_local(name) {
385 self.chunk.emit_op_u16(Op::SetLocalSlot, slot as u16, line);
386 } else {
387 let idx = self.chunk.add_constant(Value::Str(name.to_string()));
388 self.chunk.emit_op_u16(Op::SetGlobal, idx, line);
389 }
390 }
391
392 fn end_scope(&mut self, line: usize) {
394 while let Some(local) = self.locals.last() {
395 if local.depth < self.scope_depth {
396 break;
397 }
398 self.locals.pop();
399 }
400 self.scope_depth -= 1;
401 self.chunk.emit_op(Op::PopScope, line);
402 }
403
404 pub fn compile_program(mut self, program: &Program) -> Result<(Chunk, FnChunkCache), IonError> {
405 let len = program.stmts.len();
406 for (i, stmt) in program.stmts.iter().enumerate() {
407 let is_last = i == len - 1;
408 match &stmt.kind {
409 StmtKind::ExprStmt { expr, has_semi } => {
410 self.compile_expr(expr)?;
411 if is_last && !has_semi {
412 } else {
414 self.chunk.emit_op(Op::Pop, stmt.span.line);
415 }
416 }
417 _ => {
418 self.compile_stmt(stmt)?;
419 if is_last {
420 self.chunk.emit_op(Op::Unit, stmt.span.line);
422 }
423 }
424 }
425 if !is_last && Self::stmt_is_terminal(stmt) {
426 break;
427 }
428 }
429 if program.stmts.is_empty() {
430 self.chunk.emit_op(Op::Unit, 0);
431 }
432 self.chunk.emit_op(Op::Return, 0);
433 self.chunk.peephole_optimize();
434 Ok((self.chunk, self.fn_chunks))
435 }
436
437 fn compile_stmt(&mut self, stmt: &Stmt) -> Result<(), IonError> {
438 let line = stmt.span.line;
439 match &stmt.kind {
440 StmtKind::Let {
441 mutable,
442 pattern,
443 type_ann,
444 value,
445 } => {
446 self.compile_expr(value)?;
447 if let Some(ann) = type_ann {
448 let type_name = Self::type_ann_to_string(ann);
449 let idx = self.chunk.add_constant(Value::Str(type_name));
450 self.chunk.emit_op_u16(Op::CheckType, idx, line);
451 }
452 self.compile_checked_let_pattern(pattern, *mutable, line)?;
453 }
454 StmtKind::ExprStmt { expr, .. } => {
455 self.compile_expr(expr)?;
456 self.chunk.emit_op(Op::Pop, line);
457 }
458 StmtKind::FnDecl { name, params, body } => {
459 self.compile_fn_decl(name, params, body, line)?;
460 }
461 StmtKind::For {
462 label,
463 pattern,
464 iter,
465 body,
466 } => {
467 self.compile_for(label.clone(), pattern, iter, body, line)?;
468 }
469 StmtKind::While { label, cond, body } => {
470 self.compile_while(label.clone(), cond, body, line)?;
471 }
472 StmtKind::Loop { label, body } => {
473 self.compile_loop(label.clone(), body, line)?;
474 }
475 StmtKind::Break { label, value } => {
476 let target_idx = match self.resolve_loop_target(label.as_deref()) {
477 Some(idx) => idx,
478 None => {
479 return Err(IonError::runtime(
480 unmatched_label_msg("break", label.as_deref()),
481 line,
482 0,
483 ));
484 }
485 };
486 if let Some(expr) = value {
487 self.compile_expr(expr)?;
488 } else {
489 self.chunk.emit_op(Op::Unit, line);
490 }
491 let target_scope = self.loop_stack[target_idx].scope_depth;
492 for _ in target_scope..self.scope_depth {
493 self.chunk.emit_op(Op::PopScope, line);
494 }
495 for frame in self.loop_stack[target_idx..].iter().rev() {
498 if frame.is_for_loop {
499 self.chunk.emit_op(Op::IterDrop, line);
500 }
501 }
502 let jump = self.chunk.emit_jump(Op::Jump, line);
503 self.loop_stack[target_idx].break_jumps.push(jump);
504 }
505 StmtKind::Continue { label } => {
506 let target_idx = match self.resolve_loop_target(label.as_deref()) {
507 Some(idx) => idx,
508 None => {
509 return Err(IonError::runtime(
510 unmatched_label_msg("continue", label.as_deref()),
511 line,
512 0,
513 ));
514 }
515 };
516 let target_scope = self.loop_stack[target_idx].scope_depth;
517 for _ in target_scope..self.scope_depth {
518 self.chunk.emit_op(Op::PopScope, line);
519 }
520 for frame in self.loop_stack[target_idx + 1..].iter().rev() {
523 if frame.is_for_loop {
524 self.chunk.emit_op(Op::IterDrop, line);
525 }
526 }
527 let target_frame = &self.loop_stack[target_idx];
528 if target_frame.is_for_loop {
529 self.chunk.emit_op(Op::Unit, line);
531 }
532 let offset = self.chunk.len() - target_frame.continue_target + 3;
533 self.chunk.emit_op_u16(Op::Loop, offset as u16, line);
534 }
535 StmtKind::Return { value } => {
536 if let Some(expr) = value {
537 let saved = self.in_tail_position;
538 self.in_tail_position = true;
539 self.compile_expr(expr)?;
540 self.in_tail_position = saved;
541 } else {
542 self.chunk.emit_op(Op::Unit, line);
543 }
544 self.chunk.emit_op(Op::Return, line);
545 }
546 StmtKind::Assign { target, op, value } => {
547 self.compile_assign(target, op, value, line)?;
548 self.chunk.emit_op(Op::Pop, line); }
550 StmtKind::Use { path, imports } => {
551 self.compile_use(path, imports, line)?;
552 }
553 StmtKind::WhileLet {
554 label,
555 pattern,
556 expr,
557 body,
558 } => {
559 let loop_start = self.chunk.len();
560 self.loop_stack.push(LoopFrame {
561 label: label.clone(),
562 break_jumps: Vec::new(),
563 continue_target: loop_start,
564 is_for_loop: false,
565 scope_depth: self.scope_depth,
566 });
567
568 self.compile_expr(expr)?;
570
571 self.chunk.emit_op(Op::Dup, line); self.compile_pattern_test(pattern, line)?;
574
575 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
576 self.chunk.emit_op(Op::Pop, line); self.begin_scope(line);
580 self.compile_pattern_bind(pattern, line)?;
581 for stmt in body {
582 self.compile_stmt(stmt)?;
583 if Self::stmt_is_terminal(stmt) {
584 break;
585 }
586 }
587 self.end_scope(line);
588
589 let offset = self.chunk.len() - loop_start + 3;
590 self.chunk.emit_op_u16(Op::Loop, offset as u16, line);
591
592 self.chunk.patch_jump(exit_jump);
593 self.chunk.emit_op(Op::Pop, line); self.chunk.emit_op(Op::Pop, line); let frame = self.loop_stack.pop().expect("loop frame");
597 for jump in &frame.break_jumps {
598 self.chunk.patch_jump(*jump);
599 }
600 }
601 }
602 Ok(())
603 }
604
605 fn compile_expr(&mut self, expr: &Expr) -> Result<(), IonError> {
606 let line = expr.span.line;
607 let col = expr.span.col;
608 let was_tail = self.in_tail_position;
610 self.in_tail_position = false;
611 match &expr.kind {
612 ExprKind::Int(n) => {
613 self.chunk.emit_constant(Value::Int(*n), line);
614 }
615 ExprKind::Float(n) => {
616 self.chunk.emit_constant(Value::Float(*n), line);
617 }
618 ExprKind::Bool(b) => {
619 self.chunk
620 .emit_op(if *b { Op::True } else { Op::False }, line);
621 }
622 ExprKind::Str(s) => {
623 self.chunk.emit_constant(Value::Str(s.clone()), line);
624 }
625 ExprKind::Bytes(b) => {
626 self.chunk.emit_constant(Value::Bytes(b.clone()), line);
627 }
628 ExprKind::Unit => {
629 self.chunk.emit_op(Op::Unit, line);
630 }
631 ExprKind::None => {
632 self.chunk.emit_op(Op::None, line);
633 }
634 ExprKind::SomeExpr(inner) => {
635 self.compile_expr(inner)?;
636 self.chunk.emit_op(Op::WrapSome, line);
637 }
638 ExprKind::OkExpr(inner) => {
639 self.compile_expr(inner)?;
640 self.chunk.emit_op(Op::WrapOk, line);
641 }
642 ExprKind::ErrExpr(inner) => {
643 self.compile_expr(inner)?;
644 self.chunk.emit_op(Op::WrapErr, line);
645 }
646
647 ExprKind::Ident(name) => {
648 if let Some(slot) = self.resolve_local(name) {
649 self.chunk.emit_op_u16(Op::GetLocalSlot, slot as u16, line);
650 } else {
651 let idx = self.chunk.add_constant(Value::Str(name.clone()));
652 self.chunk.emit_op_u16(Op::GetGlobal, idx, line);
653 }
654 }
655
656 ExprKind::ModulePath(segments) => {
657 let root_idx = self.chunk.add_constant(Value::Str(segments[0].clone()));
659 self.chunk.emit_op_u16(Op::GetGlobal, root_idx, line);
660 for seg in &segments[1..] {
661 let idx = self.chunk.add_constant(Value::Str(seg.clone()));
662 self.chunk.emit_op_u16_span(Op::GetField, idx, line, col);
663 }
664 }
665
666 ExprKind::BinOp { left, op, right } => {
667 let folded = Self::try_fold_binop(left, op, right);
669 if let Some(val) = folded {
670 self.chunk.emit_constant(val, line);
671 } else {
672 match op {
673 BinOp::And => {
674 self.compile_expr(left)?;
675 let jump = self.chunk.emit_jump(Op::And, line);
676 self.chunk.emit_op(Op::Pop, line);
677 self.compile_expr(right)?;
678 self.chunk.patch_jump(jump);
679 }
680 BinOp::Or => {
681 self.compile_expr(left)?;
682 let jump = self.chunk.emit_jump(Op::Or, line);
683 self.chunk.emit_op(Op::Pop, line);
684 self.compile_expr(right)?;
685 self.chunk.patch_jump(jump);
686 }
687 _ => {
688 self.compile_expr(left)?;
689 self.compile_expr(right)?;
690 match op {
691 BinOp::Add => self.chunk.emit_op_span(Op::Add, line, col),
692 BinOp::Sub => self.chunk.emit_op_span(Op::Sub, line, col),
693 BinOp::Mul => self.chunk.emit_op_span(Op::Mul, line, col),
694 BinOp::Div => self.chunk.emit_op_span(Op::Div, line, col),
695 BinOp::Mod => self.chunk.emit_op_span(Op::Mod, line, col),
696 BinOp::Eq => self.chunk.emit_op(Op::Eq, line),
697 BinOp::Ne => self.chunk.emit_op(Op::NotEq, line),
698 BinOp::Lt => self.chunk.emit_op(Op::Lt, line),
699 BinOp::Gt => self.chunk.emit_op(Op::Gt, line),
700 BinOp::Le => self.chunk.emit_op(Op::LtEq, line),
701 BinOp::Ge => self.chunk.emit_op(Op::GtEq, line),
702 BinOp::BitAnd => self.chunk.emit_op(Op::BitAnd, line),
703 BinOp::BitOr => self.chunk.emit_op(Op::BitOr, line),
704 BinOp::BitXor => self.chunk.emit_op(Op::BitXor, line),
705 BinOp::Shl => self.chunk.emit_op(Op::Shl, line),
706 BinOp::Shr => self.chunk.emit_op(Op::Shr, line),
707 _ => unreachable!(),
708 }
709 }
710 }
711 }
712 }
713
714 ExprKind::UnaryOp { op, expr: inner } => {
715 let folded = Self::try_fold_unary(op, inner);
716 if let Some(val) = folded {
717 self.chunk.emit_constant(val, line);
718 } else {
719 self.compile_expr(inner)?;
720 match op {
721 UnaryOp::Neg => self.chunk.emit_op_span(Op::Neg, line, col),
722 UnaryOp::Not => self.chunk.emit_op_span(Op::Not, line, col),
723 }
724 }
725 }
726
727 ExprKind::If {
728 cond,
729 then_body,
730 else_body,
731 } => {
732 self.compile_expr(cond)?;
734 let then_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
735 self.chunk.emit_op(Op::Pop, line); self.begin_scope(line);
737 self.in_tail_position = was_tail;
739 self.compile_block_expr(then_body, line)?;
740 self.end_scope(line);
741 let else_jump = self.chunk.emit_jump(Op::Jump, line);
742 self.chunk.patch_jump(then_jump);
743 self.chunk.emit_op(Op::Pop, line); if let Some(else_stmts) = else_body {
745 self.begin_scope(line);
746 self.in_tail_position = was_tail;
747 self.compile_block_expr(else_stmts, line)?;
748 self.end_scope(line);
749 } else {
750 self.chunk.emit_op(Op::Unit, line);
751 }
752 self.chunk.patch_jump(else_jump);
753 }
754
755 ExprKind::Block(stmts) => {
756 self.begin_scope(line);
757 self.in_tail_position = was_tail;
758 self.compile_block_expr(stmts, line)?;
759 self.end_scope(line);
760 }
761
762 ExprKind::Call { func, args } => {
763 let has_named = args.iter().any(|a| a.name.is_some());
764 self.compile_expr(func)?;
766 for arg in args {
767 self.compile_expr(&arg.value)?;
768 }
769 if has_named {
770 let named: Vec<(u8, u16)> = args
772 .iter()
773 .enumerate()
774 .filter_map(|(i, a)| {
775 a.name
776 .as_ref()
777 .map(|n| (i as u8, self.chunk.add_constant(Value::Str(n.clone()))))
778 })
779 .collect();
780 self.chunk.emit_op(Op::CallNamed, line);
781 self.chunk.emit(args.len() as u8, line);
782 self.chunk.emit(named.len() as u8, line);
783 for (pos, name_idx) in named {
784 self.chunk.emit(pos, line);
785 self.chunk.emit((name_idx >> 8) as u8, line);
786 self.chunk.emit(name_idx as u8, line);
787 }
788 } else {
789 let op = if was_tail { Op::TailCall } else { Op::Call };
790 self.chunk.emit_op_u8_span(op, args.len() as u8, line, col);
791 }
792 }
793
794 ExprKind::List(items) => {
795 let has_spread = items.iter().any(|e| matches!(e, ListEntry::Spread(_)));
796 if has_spread {
797 self.chunk.emit_op_u16(Op::BuildList, 0, line);
799 for entry in items {
800 match entry {
801 ListEntry::Elem(expr) => {
802 self.compile_expr(expr)?;
803 self.chunk.emit_op(Op::ListAppend, line);
804 }
805 ListEntry::Spread(expr) => {
806 self.compile_expr(expr)?;
807 self.chunk.emit_op(Op::ListExtend, line);
808 }
809 }
810 }
811 } else {
812 for entry in items {
814 if let ListEntry::Elem(expr) = entry {
815 self.compile_expr(expr)?;
816 }
817 }
818 self.chunk
819 .emit_op_u16(Op::BuildList, items.len() as u16, line);
820 }
821 }
822
823 ExprKind::Tuple(items) => {
824 for item in items {
825 self.compile_expr(item)?;
826 }
827 self.chunk
828 .emit_op_u16(Op::BuildTuple, items.len() as u16, line);
829 }
830
831 ExprKind::Dict(entries) => {
832 let has_spread = entries.iter().any(|e| matches!(e, DictEntry::Spread(_)));
833 if has_spread {
834 self.chunk.emit_op_u16(Op::BuildDict, 0, line);
836 for entry in entries {
837 match entry {
838 DictEntry::KeyValue(k, v) => {
839 self.compile_expr(k)?;
840 self.compile_expr(v)?;
841 self.chunk.emit_op(Op::DictInsert, line);
842 }
843 DictEntry::Spread(expr) => {
844 self.compile_expr(expr)?;
845 self.chunk.emit_op(Op::DictMerge, line);
846 }
847 }
848 }
849 } else {
850 let count = entries.len() as u16;
852 for entry in entries {
853 if let DictEntry::KeyValue(k, v) = entry {
854 self.compile_expr(k)?;
855 self.compile_expr(v)?;
856 }
857 }
858 self.chunk.emit_op_u16(Op::BuildDict, count, line);
859 }
860 }
861
862 ExprKind::FieldAccess { expr: inner, field } => {
863 self.compile_expr(inner)?;
864 let idx = self.chunk.add_constant(Value::Str(field.clone()));
865 self.chunk.emit_op_u16_span(Op::GetField, idx, line, col);
866 }
867
868 ExprKind::Index { expr: inner, index } => {
869 self.compile_expr(inner)?;
870 self.compile_expr(index)?;
871 self.chunk.emit_op_span(Op::GetIndex, line, col);
872 }
873
874 ExprKind::Slice {
875 expr: inner,
876 start,
877 end,
878 inclusive,
879 } => {
880 self.compile_expr(inner)?;
881 let mut flags: u8 = 0;
882 if let Some(s) = start {
883 self.compile_expr(s)?;
884 flags |= 1; }
886 if let Some(e) = end {
887 self.compile_expr(e)?;
888 flags |= 2; }
890 if *inclusive {
891 flags |= 4; }
893 self.chunk.emit_op_u8(Op::Slice, flags, line);
894 }
895
896 ExprKind::MethodCall {
897 expr: inner,
898 method,
899 args,
900 } => {
901 self.compile_expr(inner)?;
902 for arg in args {
903 self.compile_expr(&arg.value)?;
904 }
905 let idx = self.chunk.add_constant(Value::Str(method.clone()));
906 self.chunk.emit_op_u16_span(Op::MethodCall, idx, line, col);
907 self.chunk.emit_span(args.len() as u8, line, col);
908 }
909
910 ExprKind::Lambda { params, body } => {
911 let body_stmt = Stmt {
913 kind: StmtKind::ExprStmt {
914 expr: *body.clone(),
915 has_semi: false,
916 },
917 span: expr.span,
918 };
919 let mut fn_compiler = Compiler::new();
921 fn_compiler.in_tail_position = true;
922 fn_compiler.needs_env_locals = Self::expr_has_closures(body);
923 for p in params {
925 fn_compiler.add_local(p.clone(), false);
926 }
927 if fn_compiler.needs_env_locals {
929 for (i, p) in params.iter().enumerate() {
930 fn_compiler
931 .chunk
932 .emit_op_u16(Op::GetLocalSlot, i as u16, line);
933 let idx = fn_compiler.chunk.add_constant(Value::Str(p.clone()));
934 fn_compiler.chunk.emit_op_u16(Op::DefineLocal, idx, line);
935 fn_compiler.chunk.emit(0, line);
936 }
937 }
938 fn_compiler.compile_expr(body)?;
939 fn_compiler.chunk.emit_op(Op::Return, line);
940 fn_compiler.chunk.peephole_optimize();
941 let compiled_chunk = fn_compiler.chunk;
942 self.fn_chunks.extend(fn_compiler.fn_chunks);
943
944 let fn_value = Value::Fn(crate::value::IonFn::new(
945 "<lambda>".to_string(),
946 params
947 .iter()
948 .map(|n| crate::ast::Param {
949 name: n.clone(),
950 default: None,
951 })
952 .collect(),
953 vec![body_stmt],
954 std::collections::HashMap::new(),
955 ));
956 if let Value::Fn(ref ion_fn) = fn_value {
958 self.fn_chunks.insert(ion_fn.fn_id, compiled_chunk);
959 }
960 let fn_idx = self.chunk.add_constant(fn_value);
961 self.chunk.emit_op_u16(Op::Closure, fn_idx, line);
962 }
963
964 ExprKind::FStr(parts) => {
965 for part in parts {
966 match part {
967 FStrPart::Literal(s) => {
968 self.chunk.emit_constant(Value::Str(s.clone()), line);
969 }
970 FStrPart::Expr(expr) => {
971 self.compile_expr(expr)?;
972 }
973 }
974 }
975 self.chunk
976 .emit_op_u16(Op::BuildFString, parts.len() as u16, line);
977 }
978
979 ExprKind::PipeOp { left, right } => {
980 match &right.kind {
983 ExprKind::Call { func, args } => {
984 self.compile_expr(func)?;
985 self.compile_expr(left)?; for arg in args {
987 self.compile_expr(&arg.value)?;
988 }
989 self.chunk
990 .emit_op_u8(Op::Call, (args.len() + 1) as u8, line);
991 }
992 _ => {
993 self.compile_expr(right)?;
995 self.compile_expr(left)?;
996 self.chunk.emit_op_u8(Op::Call, 1, line);
997 }
998 }
999 }
1000
1001 ExprKind::Try(inner) => {
1002 self.compile_expr(inner)?;
1003 self.chunk.emit_op(Op::Try, line);
1004 }
1005
1006 ExprKind::Range {
1007 start,
1008 end,
1009 inclusive,
1010 } => {
1011 self.compile_expr(start)?;
1012 self.compile_expr(end)?;
1013 self.chunk
1014 .emit_op_u8(Op::BuildRange, if *inclusive { 1 } else { 0 }, line);
1015 }
1016
1017 ExprKind::LoopExpr(body) => {
1018 self.compile_loop(None, body, line)?;
1019 }
1020
1021 ExprKind::Match {
1022 expr: subject,
1023 arms,
1024 } => {
1025 self.compile_match(subject, arms, line)?;
1026 }
1027
1028 ExprKind::ListComp {
1029 expr: item_expr,
1030 pattern,
1031 iter,
1032 cond,
1033 } => {
1034 self.compile_list_comp(item_expr, pattern, iter, cond.as_deref(), line)?;
1035 }
1036
1037 ExprKind::DictComp {
1038 key,
1039 value,
1040 pattern,
1041 iter,
1042 cond,
1043 } => {
1044 self.compile_dict_comp(key, value, pattern, iter, cond.as_deref(), line)?;
1045 }
1046
1047 ExprKind::IfLet {
1048 pattern,
1049 expr: inner,
1050 then_body,
1051 else_body,
1052 } => {
1053 self.compile_expr(inner)?;
1055
1056 self.chunk.emit_op(Op::Dup, line); self.compile_pattern_test(pattern, line)?;
1059
1060 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
1061 self.chunk.emit_op(Op::Pop, line); self.begin_scope(line);
1065 self.compile_pattern_bind(pattern, line)?;
1066 self.in_tail_position = was_tail;
1067 self.compile_block_expr(then_body, line)?;
1068 self.end_scope(line);
1069
1070 let end_jump = self.chunk.emit_jump(Op::Jump, line);
1071
1072 self.chunk.patch_jump(else_jump);
1073 self.chunk.emit_op(Op::Pop, line); self.chunk.emit_op(Op::Pop, line); if let Some(else_stmts) = else_body {
1077 self.begin_scope(line);
1078 self.in_tail_position = was_tail;
1079 self.compile_block_expr(else_stmts, line)?;
1080 self.end_scope(line);
1081 } else {
1082 self.chunk.emit_op(Op::Unit, line);
1083 }
1084
1085 self.chunk.patch_jump(end_jump);
1086 }
1087
1088 ExprKind::StructConstruct {
1089 name,
1090 fields,
1091 spread,
1092 } => {
1093 if let Some(spread_expr) = spread {
1094 self.compile_expr(spread_expr)?;
1095 for (fname, fexpr) in fields {
1096 self.chunk.emit_constant(Value::Str(fname.clone()), line);
1097 self.compile_expr(fexpr)?;
1098 }
1099 let type_idx = self.chunk.add_constant(Value::Str(name.clone()));
1100 let field_count = (0x8000 | fields.len()) as u16;
1101 self.chunk.emit_op(Op::ConstructStruct, line);
1102 self.chunk.emit((type_idx >> 8) as u8, line);
1103 self.chunk.emit((type_idx & 0xff) as u8, line);
1104 self.chunk.emit((field_count >> 8) as u8, line);
1105 self.chunk.emit((field_count & 0xff) as u8, line);
1106 } else {
1107 for (fname, fexpr) in fields {
1108 self.chunk.emit_constant(Value::Str(fname.clone()), line);
1109 self.compile_expr(fexpr)?;
1110 }
1111 let type_idx = self.chunk.add_constant(Value::Str(name.clone()));
1112 let count = fields.len() as u16;
1113 self.chunk.emit_op(Op::ConstructStruct, line);
1114 self.chunk.emit((type_idx >> 8) as u8, line);
1115 self.chunk.emit((type_idx & 0xff) as u8, line);
1116 self.chunk.emit((count >> 8) as u8, line);
1117 self.chunk.emit((count & 0xff) as u8, line);
1118 }
1119 }
1120 ExprKind::EnumVariant { enum_name, variant } => {
1121 let enum_idx = self.chunk.add_constant(Value::Str(enum_name.clone()));
1122 let variant_idx = self.chunk.add_constant(Value::Str(variant.clone()));
1123 self.chunk.emit_op(Op::ConstructEnum, line);
1124 self.chunk.emit((enum_idx >> 8) as u8, line);
1125 self.chunk.emit((enum_idx & 0xff) as u8, line);
1126 self.chunk.emit((variant_idx >> 8) as u8, line);
1127 self.chunk.emit((variant_idx & 0xff) as u8, line);
1128 self.chunk.emit(0u8, line);
1129 }
1130 ExprKind::EnumVariantCall {
1131 enum_name,
1132 variant,
1133 args,
1134 } => {
1135 for arg in args {
1136 self.compile_expr(arg)?;
1137 }
1138 let enum_idx = self.chunk.add_constant(Value::Str(enum_name.clone()));
1139 let variant_idx = self.chunk.add_constant(Value::Str(variant.clone()));
1140 self.chunk.emit_op(Op::ConstructEnum, line);
1141 self.chunk.emit((enum_idx >> 8) as u8, line);
1142 self.chunk.emit((enum_idx & 0xff) as u8, line);
1143 self.chunk.emit((variant_idx >> 8) as u8, line);
1144 self.chunk.emit((variant_idx & 0xff) as u8, line);
1145 self.chunk.emit(args.len() as u8, line);
1146 }
1147
1148 #[cfg(feature = "async-runtime")]
1149 ExprKind::AsyncBlock(body) => {
1150 self.begin_scope(line);
1151 let saved_async_depth = self.async_scope_depth;
1152 self.async_scope_depth += 1;
1153 self.in_tail_position = was_tail;
1154 let result = self.compile_block_expr(body, line);
1155 self.async_scope_depth = saved_async_depth;
1156 result?;
1157 self.end_scope(line);
1158 }
1159 #[cfg(feature = "async-runtime")]
1160 ExprKind::SpawnExpr(inner) => {
1161 self.compile_spawn_expr(inner, line, col)?;
1162 }
1163 #[cfg(feature = "async-runtime")]
1164 ExprKind::AwaitExpr(inner) => {
1165 self.compile_expr(inner)?;
1166 self.chunk.emit_op_span(Op::AwaitTask, line, col);
1167 }
1168 #[cfg(feature = "async-runtime")]
1169 ExprKind::SelectExpr(branches) => {
1170 self.compile_select_expr(branches, line, col)?;
1171 }
1172 #[cfg(all(not(feature = "async-runtime"), feature = "legacy-threaded-concurrency"))]
1173 ExprKind::AsyncBlock(_)
1174 | ExprKind::SpawnExpr(_)
1175 | ExprKind::AwaitExpr(_)
1176 | ExprKind::SelectExpr(_) => {
1177 return Err(IonError::runtime(
1178 ion_str!(
1179 "legacy-threaded-concurrency is not supported in bytecode VM"
1180 )
1181 .to_string(),
1182 line,
1183 col,
1184 ));
1185 }
1186 #[cfg(all(not(feature = "async-runtime"), not(feature = "legacy-threaded-concurrency")))]
1187 ExprKind::AsyncBlock(_)
1188 | ExprKind::SpawnExpr(_)
1189 | ExprKind::AwaitExpr(_)
1190 | ExprKind::SelectExpr(_) => {
1191 return Err(IonError::runtime(
1192 ion_str!("concurrency syntax requires 'async-runtime'").to_string(),
1193 line,
1194 col,
1195 ));
1196 }
1197
1198 ExprKind::TryCatch { body, var, handler } => {
1199 let try_begin_patch = self.chunk.emit_jump(Op::TryBegin, line);
1201
1202 self.begin_scope(line);
1204 let old_tail = self.in_tail_position;
1205 self.in_tail_position = false;
1206 for (i, stmt) in body.iter().enumerate() {
1207 if i == body.len() - 1 {
1208 if let crate::ast::StmtKind::ExprStmt { expr, .. } = &stmt.kind {
1209 self.compile_expr(expr)?;
1210 } else {
1211 self.compile_stmt(stmt)?;
1212 self.chunk.emit_op(Op::Unit, line);
1213 }
1214 } else {
1215 self.compile_stmt(stmt)?;
1216 }
1217 }
1218 if body.is_empty() {
1219 self.chunk.emit_op(Op::Unit, line);
1220 }
1221 self.in_tail_position = old_tail;
1222 self.end_scope(line);
1223
1224 let try_end_patch = self.chunk.emit_jump(Op::TryEnd, line);
1226
1227 self.chunk.patch_jump(try_begin_patch);
1229
1230 self.begin_scope(line);
1232 self.emit_define_local(var, false, line);
1233 for (i, stmt) in handler.iter().enumerate() {
1234 if i == handler.len() - 1 {
1235 if let crate::ast::StmtKind::ExprStmt { expr, .. } = &stmt.kind {
1236 self.compile_expr(expr)?;
1237 } else {
1238 self.compile_stmt(stmt)?;
1239 self.chunk.emit_op(Op::Unit, line);
1240 }
1241 } else {
1242 self.compile_stmt(stmt)?;
1243 }
1244 }
1245 if handler.is_empty() {
1246 self.chunk.emit_op(Op::Unit, line);
1247 }
1248 self.end_scope(line);
1249
1250 self.chunk.patch_jump(try_end_patch);
1252 }
1253 }
1254 self.in_tail_position = was_tail;
1255 Ok(())
1256 }
1257
1258 fn compile_block_expr(&mut self, stmts: &[Stmt], line: usize) -> Result<(), IonError> {
1259 if stmts.is_empty() {
1260 self.chunk.emit_op(Op::Unit, line);
1261 return Ok(());
1262 }
1263 let len = stmts.len();
1264 let saved_tail = self.in_tail_position;
1265 for (i, stmt) in stmts.iter().enumerate() {
1266 let is_last = i == len - 1;
1267 if !is_last {
1269 self.in_tail_position = false;
1270 } else {
1271 self.in_tail_position = saved_tail;
1272 }
1273 match &stmt.kind {
1274 StmtKind::ExprStmt { expr, has_semi } => {
1275 if is_last && *has_semi {
1276 self.in_tail_position = false;
1277 }
1278 self.compile_expr(expr)?;
1279 if is_last && !has_semi {
1280 } else {
1282 self.chunk.emit_op(Op::Pop, stmt.span.line);
1283 }
1284 }
1285 _ => {
1286 self.in_tail_position = false;
1287 self.compile_stmt(stmt)?;
1288 if is_last {
1289 self.chunk.emit_op(Op::Unit, stmt.span.line);
1290 }
1291 }
1292 }
1293 if !is_last && Self::stmt_is_terminal(stmt) {
1295 break;
1296 }
1297 }
1298 self.in_tail_position = saved_tail;
1299 Ok(())
1300 }
1301
1302 #[cfg(feature = "async-runtime")]
1303 fn compile_spawn_expr(&mut self, expr: &Expr, line: usize, col: usize) -> Result<(), IonError> {
1304 if self.async_scope_depth == 0 {
1305 return Err(IonError::runtime(
1306 ion_str!("spawn is only allowed inside async {}").to_string(),
1307 line,
1308 col,
1309 ));
1310 }
1311 let ExprKind::Call { func, args } = &expr.kind else {
1312 return Err(IonError::runtime(
1313 ion_str!("spawn currently requires a function call in the bytecode VM").to_string(),
1314 line,
1315 col,
1316 ));
1317 };
1318 let has_named = args.iter().any(|arg| arg.name.is_some());
1319 self.compile_expr(func)?;
1320 for arg in args {
1321 self.compile_expr(&arg.value)?;
1322 }
1323 if has_named {
1324 let named: Vec<(u8, u16)> = args
1325 .iter()
1326 .enumerate()
1327 .filter_map(|(i, arg)| {
1328 arg.name
1329 .as_ref()
1330 .map(|name| (i as u8, self.chunk.add_constant(Value::Str(name.clone()))))
1331 })
1332 .collect();
1333 self.chunk.emit_op_span(Op::SpawnCallNamed, line, col);
1334 self.chunk.emit(args.len() as u8, line);
1335 self.chunk.emit(named.len() as u8, line);
1336 for (position, name_idx) in named {
1337 self.chunk.emit(position, line);
1338 self.chunk.emit((name_idx >> 8) as u8, line);
1339 self.chunk.emit(name_idx as u8, line);
1340 }
1341 } else {
1342 self.chunk
1343 .emit_op_u8_span(Op::SpawnCall, args.len() as u8, line, col);
1344 }
1345 Ok(())
1346 }
1347
1348 fn compile_use(
1349 &mut self,
1350 path: &[String],
1351 imports: &UseImports,
1352 line: usize,
1353 ) -> Result<(), IonError> {
1354 match imports {
1355 UseImports::Glob => {
1356 self.compile_module_path(path, line, 0)?;
1357 self.chunk.emit_op(Op::ImportGlob, line);
1358 }
1359 UseImports::Names(names) => {
1360 for name in names {
1361 self.compile_module_path_member(path, name, line, 0)?;
1362 self.emit_define_local(name, false, line);
1363 }
1364 }
1365 UseImports::Single(name) => {
1366 self.compile_module_path_member(path, name, line, 0)?;
1367 self.emit_define_local(name, false, line);
1368 }
1369 }
1370 Ok(())
1371 }
1372
1373 fn compile_module_path_member(
1374 &mut self,
1375 path: &[String],
1376 name: &str,
1377 line: usize,
1378 col: usize,
1379 ) -> Result<(), IonError> {
1380 let mut segments = path.to_vec();
1381 segments.push(name.to_string());
1382 self.compile_module_path(&segments, line, col)
1383 }
1384
1385 fn compile_module_path(
1386 &mut self,
1387 segments: &[String],
1388 line: usize,
1389 col: usize,
1390 ) -> Result<(), IonError> {
1391 let Some(root) = segments.first() else {
1392 return Err(IonError::runtime("empty module path", line, col));
1393 };
1394 let root_idx = self.chunk.add_constant(Value::Str(root.clone()));
1395 self.chunk.emit_op_u16(Op::GetGlobal, root_idx, line);
1396 for segment in &segments[1..] {
1397 let idx = self.chunk.add_constant(Value::Str(segment.clone()));
1398 self.chunk.emit_op_u16_span(Op::GetField, idx, line, col);
1399 }
1400 Ok(())
1401 }
1402
1403 #[cfg(feature = "async-runtime")]
1404 fn compile_select_expr(
1405 &mut self,
1406 branches: &[SelectBranch],
1407 line: usize,
1408 col: usize,
1409 ) -> Result<(), IonError> {
1410 if branches.is_empty() {
1411 return Err(IonError::runtime(
1412 ion_str!("select requires at least one branch").to_string(),
1413 line,
1414 col,
1415 ));
1416 }
1417 if branches.len() > u8::MAX as usize {
1418 return Err(IonError::runtime(
1419 ion_str!("select has too many branches").to_string(),
1420 line,
1421 col,
1422 ));
1423 }
1424
1425 for branch in branches {
1426 self.compile_spawn_expr(&branch.future_expr, line, col)?;
1427 }
1428 self.chunk
1429 .emit_op_u8_span(Op::SelectTasks, branches.len() as u8, line, col);
1430
1431 let mut end_jumps = Vec::with_capacity(branches.len());
1432 for (idx, branch) in branches.iter().enumerate() {
1433 self.chunk.emit_op(Op::Dup, line);
1434 self.chunk.emit_constant(Value::Int(0), line);
1435 self.chunk.emit_op(Op::GetIndex, line);
1436 self.chunk.emit_constant(Value::Int(idx as i64), line);
1437 self.chunk.emit_op(Op::Eq, line);
1438 let next_branch = self.chunk.emit_jump(Op::JumpIfFalse, line);
1439 self.chunk.emit_op(Op::Pop, line);
1440
1441 self.begin_scope(line);
1442 self.chunk.emit_op(Op::Dup, line);
1443 self.chunk.emit_constant(Value::Int(1), line);
1444 self.chunk.emit_op(Op::GetIndex, line);
1445 self.compile_checked_let_pattern(&branch.pattern, false, line)?;
1446 self.chunk.emit_op(Op::Pop, line);
1447 self.compile_expr(&branch.body)?;
1448 self.end_scope(line);
1449 end_jumps.push(self.chunk.emit_jump(Op::Jump, line));
1450
1451 self.chunk.patch_jump(next_branch);
1452 self.chunk.emit_op(Op::Pop, line);
1453 }
1454
1455 self.chunk.emit_op(Op::Pop, line);
1456 self.chunk.emit_op(Op::Unit, line);
1457 for jump in end_jumps {
1458 self.chunk.patch_jump(jump);
1459 }
1460 Ok(())
1461 }
1462
1463 fn type_ann_to_string(ann: &TypeAnn) -> String {
1464 match ann {
1465 TypeAnn::Simple(name) => name.clone(),
1466 TypeAnn::Option(inner) => format!("Option<{}>", Self::type_ann_to_string(inner)),
1467 TypeAnn::Result(ok, err) => format!(
1468 "Result<{}, {}>",
1469 Self::type_ann_to_string(ok),
1470 Self::type_ann_to_string(err)
1471 ),
1472 TypeAnn::List(inner) => format!("list<{}>", Self::type_ann_to_string(inner)),
1473 TypeAnn::Dict(k, v) => format!(
1474 "dict<{}, {}>",
1475 Self::type_ann_to_string(k),
1476 Self::type_ann_to_string(v)
1477 ),
1478 }
1479 }
1480
1481 fn compile_let_pattern(
1482 &mut self,
1483 pattern: &Pattern,
1484 mutable: bool,
1485 line: usize,
1486 ) -> Result<(), IonError> {
1487 match pattern {
1488 Pattern::Ident(name) => {
1489 self.emit_define_local(name, mutable, line);
1490 }
1491 Pattern::Int(_)
1492 | Pattern::Float(_)
1493 | Pattern::Bool(_)
1494 | Pattern::Str(_)
1495 | Pattern::Bytes(_)
1496 | Pattern::None => {
1497 self.chunk.emit_op(Op::Pop, line);
1498 }
1499 Pattern::Some(inner) => {
1500 self.chunk.emit_op_u8(Op::MatchArm, 1, line);
1501 self.compile_let_pattern(inner, mutable, line)?;
1502 }
1503 Pattern::Ok(inner) => {
1504 self.chunk.emit_op_u8(Op::MatchArm, 2, line);
1505 self.compile_let_pattern(inner, mutable, line)?;
1506 }
1507 Pattern::Err(inner) => {
1508 self.chunk.emit_op_u8(Op::MatchArm, 3, line);
1509 self.compile_let_pattern(inner, mutable, line)?;
1510 }
1511 Pattern::Tuple(pats) => {
1512 for (i, pat) in pats.iter().enumerate() {
1514 self.chunk.emit_op(Op::Dup, line);
1515 self.chunk.emit_constant(Value::Int(i as i64), line);
1516 self.chunk.emit_op(Op::GetIndex, line);
1517 self.compile_let_pattern(pat, mutable, line)?;
1518 }
1519 self.chunk.emit_op(Op::Pop, line); }
1521 Pattern::List(pats, rest) => {
1522 for (i, pat) in pats.iter().enumerate() {
1523 self.chunk.emit_op(Op::Dup, line);
1524 self.chunk.emit_constant(Value::Int(i as i64), line);
1525 self.chunk.emit_op(Op::GetIndex, line);
1526 self.compile_let_pattern(pat, mutable, line)?;
1527 }
1528 if let Some(rest_pat) = rest {
1529 self.chunk.emit_op(Op::Dup, line);
1530 self.chunk
1531 .emit_constant(Value::Int(pats.len() as i64), line);
1532 self.chunk.emit_op_u8(Op::Slice, 1, line); self.compile_let_pattern(rest_pat, mutable, line)?;
1534 }
1535 self.chunk.emit_op(Op::Pop, line);
1536 }
1537 Pattern::Struct { fields, .. } => {
1538 for (field, pattern) in fields {
1539 self.emit_match_string_operand(Op::MatchArm, 6, field, line);
1540 self.chunk.emit_op_u8(Op::MatchArm, 1, line);
1541 if let Some(pattern) = pattern {
1542 self.compile_let_pattern(pattern, mutable, line)?;
1543 } else {
1544 self.emit_define_local(field, mutable, line);
1545 }
1546 }
1547 self.chunk.emit_op(Op::Pop, line);
1548 }
1549 Pattern::EnumVariant { fields, .. } => match fields {
1550 EnumPatternFields::None => {
1551 self.chunk.emit_op(Op::Pop, line);
1552 }
1553 EnumPatternFields::Positional(pats) => {
1554 for (i, pat) in pats.iter().enumerate() {
1555 self.chunk.emit_op_u8(Op::MatchArm, 7, line);
1556 self.chunk.emit(i as u8, line);
1557 self.compile_let_pattern(pat, mutable, line)?;
1558 }
1559 self.chunk.emit_op(Op::Pop, line);
1560 }
1561 EnumPatternFields::Named(_) => {
1562 return Err(IonError::runtime(
1563 ion_str!("named enum pattern binding not supported in bytecode VM")
1564 .to_string(),
1565 line,
1566 0,
1567 ));
1568 }
1569 },
1570 Pattern::Wildcard => {
1571 self.chunk.emit_op(Op::Pop, line);
1572 }
1573 }
1574 Ok(())
1575 }
1576
1577 fn compile_checked_let_pattern(
1578 &mut self,
1579 pattern: &Pattern,
1580 mutable: bool,
1581 line: usize,
1582 ) -> Result<(), IonError> {
1583 match pattern {
1584 Pattern::Ident(_) | Pattern::Wildcard => {
1585 self.compile_let_pattern(pattern, mutable, line)
1586 }
1587 _ => {
1588 let test_keeps_matched_value = matches!(
1589 pattern,
1590 Pattern::Some(_)
1591 | Pattern::Ok(_)
1592 | Pattern::Err(_)
1593 | Pattern::Tuple(_)
1594 | Pattern::List(_, _)
1595 | Pattern::Struct { .. }
1596 | Pattern::EnumVariant { .. }
1597 );
1598 self.chunk.emit_op(Op::Dup, line);
1599 self.compile_pattern_test(pattern, line)?;
1600 let fail_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
1601 self.chunk.emit_op(Op::Pop, line); self.compile_let_pattern(pattern, mutable, line)?;
1603 if test_keeps_matched_value {
1604 self.chunk.emit_op(Op::Pop, line); }
1606 let end_jump = self.chunk.emit_jump(Op::Jump, line);
1607 self.chunk.patch_jump(fail_jump);
1608 self.chunk.emit_op(Op::Pop, line); self.chunk.emit_op(Op::Pop, line); self.chunk.emit_op(Op::MatchEnd, line);
1611 self.chunk.patch_jump(end_jump);
1612 Ok(())
1613 }
1614 }
1615 }
1616
1617 fn compile_fn_decl(
1618 &mut self,
1619 name: &str,
1620 params: &[Param],
1621 body: &[Stmt],
1622 line: usize,
1623 ) -> Result<(), IonError> {
1624 let mut fn_compiler = Compiler::new();
1626 fn_compiler.in_tail_position = true;
1627 fn_compiler.needs_env_locals = Self::stmts_have_closures(body);
1629 for param in params {
1631 fn_compiler.add_local(param.name.clone(), false);
1632 }
1633 if fn_compiler.needs_env_locals {
1635 for (i, param) in params.iter().enumerate() {
1636 fn_compiler
1637 .chunk
1638 .emit_op_u16(Op::GetLocalSlot, i as u16, line);
1639 let idx = fn_compiler
1640 .chunk
1641 .add_constant(Value::Str(param.name.clone()));
1642 fn_compiler.chunk.emit_op_u16(Op::DefineLocal, idx, line);
1643 fn_compiler.chunk.emit(0, line); }
1645 }
1646 fn_compiler.compile_block_expr(body, line)?;
1647 fn_compiler.chunk.emit_op(Op::Return, line);
1648 fn_compiler.chunk.peephole_optimize();
1649 let compiled_chunk = fn_compiler.chunk;
1650 self.fn_chunks.extend(fn_compiler.fn_chunks);
1652
1653 let fn_value = Value::Fn(crate::value::IonFn::new(
1654 name.to_string(),
1655 params.to_vec(),
1656 body.to_vec(), std::collections::HashMap::new(),
1658 ));
1659 if let Value::Fn(ref ion_fn) = fn_value {
1661 self.fn_chunks.insert(ion_fn.fn_id, compiled_chunk);
1662 }
1663
1664 self.chunk.emit_constant(fn_value, line);
1666 self.emit_define_local(name, false, line);
1667 Ok(())
1668 }
1669
1670 fn compile_for(
1671 &mut self,
1672 label: Option<String>,
1673 pattern: &Pattern,
1674 iter: &Expr,
1675 body: &[Stmt],
1676 line: usize,
1677 ) -> Result<(), IonError> {
1678 self.compile_expr(iter)?;
1680
1681 self.chunk.emit_op(Op::IterInit, line);
1683
1684 let loop_start = self.chunk.len();
1685 self.loop_stack.push(LoopFrame {
1686 label,
1687 break_jumps: Vec::new(),
1688 continue_target: loop_start,
1689 is_for_loop: true,
1690 scope_depth: self.scope_depth,
1691 });
1692
1693 let exit_jump = self.chunk.emit_jump(Op::IterNext, line);
1695
1696 self.begin_scope(line);
1698 self.compile_checked_let_pattern(pattern, false, line)?;
1699
1700 for stmt in body {
1702 self.compile_stmt(stmt)?;
1703 if Self::stmt_is_terminal(stmt) {
1704 break;
1705 }
1706 }
1707 self.end_scope(line);
1708
1709 self.chunk.emit_op(Op::Unit, line);
1711
1712 let offset = self.chunk.len() - loop_start + 3;
1714 self.chunk.emit_op_u16(Op::Loop, offset as u16, line);
1715
1716 self.chunk.patch_jump(exit_jump);
1717 self.chunk.emit_op(Op::Pop, line);
1719
1720 let frame = self.loop_stack.pop().expect("loop frame");
1721 for jump in &frame.break_jumps {
1722 self.chunk.patch_jump(*jump);
1723 }
1724 Ok(())
1725 }
1726
1727 fn compile_while(
1728 &mut self,
1729 label: Option<String>,
1730 cond: &Expr,
1731 body: &[Stmt],
1732 line: usize,
1733 ) -> Result<(), IonError> {
1734 let loop_start = self.chunk.len();
1735 self.loop_stack.push(LoopFrame {
1736 label,
1737 break_jumps: Vec::new(),
1738 continue_target: loop_start,
1739 is_for_loop: false,
1740 scope_depth: self.scope_depth,
1741 });
1742
1743 self.compile_expr(cond)?;
1744 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
1745 self.chunk.emit_op(Op::Pop, line); self.begin_scope(line);
1748 for stmt in body {
1749 self.compile_stmt(stmt)?;
1750 if Self::stmt_is_terminal(stmt) {
1751 break;
1752 }
1753 }
1754 self.end_scope(line);
1755
1756 let offset = self.chunk.len() - loop_start + 3;
1757 self.chunk.emit_op_u16(Op::Loop, offset as u16, line);
1758
1759 self.chunk.patch_jump(exit_jump);
1760 self.chunk.emit_op(Op::Pop, line); let frame = self.loop_stack.pop().expect("loop frame");
1763 for jump in &frame.break_jumps {
1764 self.chunk.patch_jump(*jump);
1765 }
1766 Ok(())
1767 }
1768
1769 fn compile_loop(
1770 &mut self,
1771 label: Option<String>,
1772 body: &[Stmt],
1773 line: usize,
1774 ) -> Result<(), IonError> {
1775 let loop_start = self.chunk.len();
1776 self.loop_stack.push(LoopFrame {
1777 label,
1778 break_jumps: Vec::new(),
1779 continue_target: loop_start,
1780 is_for_loop: false,
1781 scope_depth: self.scope_depth,
1782 });
1783
1784 self.begin_scope(line);
1785 for stmt in body {
1786 self.compile_stmt(stmt)?;
1787 if Self::stmt_is_terminal(stmt) {
1788 break;
1789 }
1790 }
1791 self.end_scope(line);
1792
1793 let offset = self.chunk.len() - loop_start + 3;
1794 self.chunk.emit_op_u16(Op::Loop, offset as u16, line);
1795
1796 let frame = self.loop_stack.pop().expect("loop frame");
1797 for jump in &frame.break_jumps {
1798 self.chunk.patch_jump(*jump);
1799 }
1800 Ok(())
1801 }
1802
1803 fn compile_assign(
1804 &mut self,
1805 target: &AssignTarget,
1806 op: &AssignOp,
1807 value: &Expr,
1808 line: usize,
1809 ) -> Result<(), IonError> {
1810 match target {
1811 AssignTarget::Ident(name) => {
1812 match op {
1813 AssignOp::Eq => {
1814 self.compile_expr(value)?;
1815 }
1816 AssignOp::PlusEq | AssignOp::MinusEq | AssignOp::StarEq | AssignOp::SlashEq => {
1817 self.emit_get_var(name, line);
1818 self.compile_expr(value)?;
1819 match op {
1820 AssignOp::PlusEq => self.chunk.emit_op(Op::Add, line),
1821 AssignOp::MinusEq => self.chunk.emit_op(Op::Sub, line),
1822 AssignOp::StarEq => self.chunk.emit_op(Op::Mul, line),
1823 AssignOp::SlashEq => self.chunk.emit_op(Op::Div, line),
1824 _ => unreachable!(),
1825 }
1826 }
1827 }
1828 self.emit_set_var(name, line);
1829 }
1830 AssignTarget::Index(obj_expr, index_expr) => {
1831 let var_name = match &obj_expr.kind {
1835 ExprKind::Ident(name) => name.clone(),
1836 _ => {
1837 return Err(IonError::runtime(
1838 ion_str!("index assignment only supported on variables").to_string(),
1839 line,
1840 0,
1841 ))
1842 }
1843 };
1844
1845 self.compile_expr(obj_expr)?;
1847 self.compile_expr(index_expr)?;
1848
1849 match op {
1851 AssignOp::Eq => {
1852 self.compile_expr(value)?;
1853 }
1854 _ => {
1855 self.compile_expr(obj_expr)?;
1857 self.compile_expr(index_expr)?;
1858 self.chunk.emit_op(Op::GetIndex, line);
1859 self.compile_expr(value)?;
1860 match op {
1861 AssignOp::PlusEq => self.chunk.emit_op(Op::Add, line),
1862 AssignOp::MinusEq => self.chunk.emit_op(Op::Sub, line),
1863 AssignOp::StarEq => self.chunk.emit_op(Op::Mul, line),
1864 AssignOp::SlashEq => self.chunk.emit_op(Op::Div, line),
1865 _ => unreachable!(),
1866 }
1867 }
1868 }
1869
1870 self.chunk.emit_op(Op::SetIndex, line);
1872 self.emit_set_var(&var_name, line);
1874 }
1875 AssignTarget::Field(obj_expr, field) => {
1876 let var_name = match &obj_expr.kind {
1877 ExprKind::Ident(name) => name.clone(),
1878 _ => {
1879 return Err(IonError::runtime(
1880 ion_str!("field assignment only supported on variables").to_string(),
1881 line,
1882 0,
1883 ))
1884 }
1885 };
1886
1887 self.compile_expr(obj_expr)?;
1888
1889 match op {
1890 AssignOp::Eq => {
1891 self.compile_expr(value)?;
1892 }
1893 _ => {
1894 self.chunk.emit_op(Op::Dup, line);
1895 let get_idx = self.chunk.add_constant(Value::Str(field.clone()));
1896 self.chunk.emit_op_u16(Op::GetField, get_idx, line);
1897 self.compile_expr(value)?;
1898 match op {
1899 AssignOp::PlusEq => self.chunk.emit_op(Op::Add, line),
1900 AssignOp::MinusEq => self.chunk.emit_op(Op::Sub, line),
1901 AssignOp::StarEq => self.chunk.emit_op(Op::Mul, line),
1902 AssignOp::SlashEq => self.chunk.emit_op(Op::Div, line),
1903 _ => unreachable!(),
1904 }
1905 }
1906 }
1907
1908 let field_idx = self.chunk.add_constant(Value::Str(field.clone()));
1910 self.chunk.emit_op_u16(Op::SetField, field_idx, line);
1911 self.emit_set_var(&var_name, line);
1913 }
1914 }
1915 Ok(())
1916 }
1917
1918 pub fn compile_fn_body(
1920 mut self,
1921 params: &[Param],
1922 body: &[Stmt],
1923 line: usize,
1924 ) -> Result<Chunk, IonError> {
1925 self.in_tail_position = true;
1926 self.needs_env_locals = Self::stmts_have_closures(body);
1927 for param in params {
1929 self.add_local(param.name.clone(), false);
1930 }
1931 if self.needs_env_locals {
1933 for (i, param) in params.iter().enumerate() {
1934 self.chunk.emit_op_u16(Op::GetLocalSlot, i as u16, line);
1935 let idx = self.chunk.add_constant(Value::Str(param.name.clone()));
1936 self.chunk.emit_op_u16(Op::DefineLocal, idx, line);
1937 self.chunk.emit(0, line); }
1939 }
1940 self.compile_block_expr(body, line)?;
1941 self.chunk.emit_op(Op::Return, line);
1942 Ok(self.chunk)
1943 }
1944
1945 fn compile_match(
1946 &mut self,
1947 subject: &Expr,
1948 arms: &[MatchArm],
1949 line: usize,
1950 ) -> Result<(), IonError> {
1951 let was_tail = self.in_tail_position;
1952 self.begin_scope(line);
1954 self.in_tail_position = false;
1955 self.compile_expr(subject)?;
1956 let tmp_name = "__match_subject__";
1957 self.emit_define_local(tmp_name, false, line);
1958 let subject_slot = self.locals.len() - 1;
1959
1960 let mut end_jumps = Vec::new();
1961
1962 for arm in arms {
1963 self.chunk
1965 .emit_op_u16(Op::GetLocalSlot, subject_slot as u16, line);
1966
1967 self.compile_pattern_test(&arm.pattern, line)?;
1969
1970 if let Some(guard) = &arm.guard {
1972 let skip_guard = self.chunk.emit_jump(Op::JumpIfFalse, line);
1973 self.chunk.emit_op(Op::Pop, line); self.compile_expr(guard)?;
1975 let after_guard = self.chunk.emit_jump(Op::Jump, line);
1976 self.chunk.patch_jump(skip_guard);
1977 self.chunk.patch_jump(after_guard);
1979 }
1980
1981 let next_arm = self.chunk.emit_jump(Op::JumpIfFalse, line);
1982 self.chunk.emit_op(Op::Pop, line); self.begin_scope(line);
1986 self.chunk
1987 .emit_op_u16(Op::GetLocalSlot, subject_slot as u16, line);
1988 self.compile_pattern_bind(&arm.pattern, line)?;
1989
1990 self.in_tail_position = was_tail;
1992 self.compile_expr(&arm.body)?;
1993 self.end_scope(line);
1994
1995 end_jumps.push(self.chunk.emit_jump(Op::Jump, line));
1996
1997 self.chunk.patch_jump(next_arm);
1998 self.chunk.emit_op(Op::Pop, line); }
2000
2001 self.chunk.emit_op(Op::MatchEnd, line);
2003
2004 for j in end_jumps {
2005 self.chunk.patch_jump(j);
2006 }
2007
2008 self.end_scope(line); Ok(())
2010 }
2011
2012 fn emit_match_string_operand(&mut self, op: Op, kind: u8, value: &str, line: usize) {
2013 let idx = self.chunk.add_constant(Value::Str(value.to_string()));
2014 self.chunk.emit_op_u8(op, kind, line);
2015 self.chunk.emit((idx >> 8) as u8, line);
2016 self.chunk.emit((idx & 0xff) as u8, line);
2017 }
2018
2019 fn emit_match_enum_operand(
2020 &mut self,
2021 enum_name: &str,
2022 variant: &str,
2023 arity: usize,
2024 line: usize,
2025 ) -> Result<(), IonError> {
2026 if arity > u8::MAX as usize {
2027 return Err(IonError::runtime(
2028 ion_str!("enum pattern has too many fields").to_string(),
2029 line,
2030 0,
2031 ));
2032 }
2033 let enum_idx = self.chunk.add_constant(Value::Str(enum_name.to_string()));
2034 let variant_idx = self.chunk.add_constant(Value::Str(variant.to_string()));
2035 self.chunk.emit_op_u8(Op::MatchBegin, 7, line);
2036 self.chunk.emit((enum_idx >> 8) as u8, line);
2037 self.chunk.emit((enum_idx & 0xff) as u8, line);
2038 self.chunk.emit((variant_idx >> 8) as u8, line);
2039 self.chunk.emit((variant_idx & 0xff) as u8, line);
2040 self.chunk.emit(arity as u8, line);
2041 Ok(())
2042 }
2043
2044 fn compile_struct_field_test(
2045 &mut self,
2046 field: &str,
2047 pattern: Option<&Pattern>,
2048 line: usize,
2049 ) -> Result<(), IonError> {
2050 self.emit_match_string_operand(Op::MatchArm, 6, field, line);
2051 self.chunk.emit_op_u8(Op::MatchBegin, 1, line); let missing_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2053 self.chunk.emit_op(Op::Pop, line); self.chunk.emit_op_u8(Op::MatchArm, 1, line); if let Some(pattern) = pattern {
2056 self.compile_pattern_test(pattern, line)?;
2057 } else {
2058 self.chunk.emit_op(Op::Pop, line); self.chunk.emit_op(Op::True, line);
2060 }
2061 let end = self.chunk.emit_jump(Op::Jump, line);
2062 self.chunk.patch_jump(missing_jump);
2063 self.chunk.patch_jump(end);
2064 Ok(())
2065 }
2066
2067 fn compile_pattern_test(&mut self, pattern: &Pattern, line: usize) -> Result<(), IonError> {
2069 match pattern {
2070 Pattern::Wildcard | Pattern::Ident(_) => {
2071 self.chunk.emit_op(Op::Pop, line); self.chunk.emit_op(Op::True, line); }
2074 Pattern::Int(n) => {
2075 self.chunk.emit_constant(Value::Int(*n), line);
2076 self.chunk.emit_op(Op::Eq, line);
2077 }
2078 Pattern::Float(n) => {
2079 self.chunk.emit_constant(Value::Float(*n), line);
2080 self.chunk.emit_op(Op::Eq, line);
2081 }
2082 Pattern::Bool(b) => {
2083 self.chunk
2084 .emit_op(if *b { Op::True } else { Op::False }, line);
2085 self.chunk.emit_op(Op::Eq, line);
2086 }
2087 Pattern::Str(s) => {
2088 self.chunk.emit_constant(Value::Str(s.clone()), line);
2089 self.chunk.emit_op(Op::Eq, line);
2090 }
2091 Pattern::Bytes(b) => {
2092 self.chunk.emit_constant(Value::Bytes(b.clone()), line);
2093 self.chunk.emit_op(Op::Eq, line);
2094 }
2095 Pattern::None => {
2096 self.chunk.emit_op(Op::None, line);
2098 self.chunk.emit_op(Op::Eq, line);
2099 }
2100 Pattern::Some(inner) => {
2101 self.chunk.emit_op_u8(Op::MatchBegin, 1, line); let fail_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2108 self.chunk.emit_op(Op::Pop, line); self.chunk.emit_op_u8(Op::MatchArm, 1, line); self.compile_pattern_test(inner, line)?;
2112 let end = self.chunk.emit_jump(Op::Jump, line);
2113 self.chunk.patch_jump(fail_jump);
2114 self.chunk.patch_jump(end);
2116 }
2117 Pattern::Ok(inner) => {
2118 self.chunk.emit_op_u8(Op::MatchBegin, 2, line); let fail_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2120 self.chunk.emit_op(Op::Pop, line);
2121 self.chunk.emit_op_u8(Op::MatchArm, 2, line); self.compile_pattern_test(inner, line)?;
2123 let end = self.chunk.emit_jump(Op::Jump, line);
2124 self.chunk.patch_jump(fail_jump);
2125 self.chunk.patch_jump(end);
2126 }
2127 Pattern::Err(inner) => {
2128 self.chunk.emit_op_u8(Op::MatchBegin, 3, line); let fail_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2130 self.chunk.emit_op(Op::Pop, line);
2131 self.chunk.emit_op_u8(Op::MatchArm, 3, line); self.compile_pattern_test(inner, line)?;
2133 let end = self.chunk.emit_jump(Op::Jump, line);
2134 self.chunk.patch_jump(fail_jump);
2135 self.chunk.patch_jump(end);
2136 }
2137 Pattern::Tuple(pats) => {
2138 self.chunk.emit_op_u8(Op::MatchBegin, 4, line); self.chunk.emit(pats.len() as u8, line); let fail_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2142 self.chunk.emit_op(Op::Pop, line); for (i, pat) in pats.iter().enumerate() {
2145 self.chunk.emit_op_u8(Op::MatchArm, 4, line); self.chunk.emit(i as u8, line);
2148 self.compile_pattern_test(pat, line)?;
2149 let sub_fail = self.chunk.emit_jump(Op::JumpIfFalse, line);
2150 self.chunk.emit_op(Op::Pop, line); if i == pats.len() - 1 {
2152 self.chunk.emit_op(Op::True, line);
2154 }
2155 let sub_end = self.chunk.emit_jump(Op::Jump, line);
2157 self.chunk.patch_jump(sub_fail);
2158 self.chunk.patch_jump(sub_end);
2160 }
2161 if pats.is_empty() {
2162 self.chunk.emit_op(Op::True, line);
2163 }
2164 let end = self.chunk.emit_jump(Op::Jump, line);
2165 self.chunk.patch_jump(fail_jump);
2166 self.chunk.patch_jump(end);
2168 }
2169 Pattern::List(pats, rest) => {
2170 let has_rest = rest.is_some();
2172 self.chunk.emit_op_u8(Op::MatchBegin, 5, line); self.chunk.emit(pats.len() as u8, line); self.chunk.emit(if has_rest { 1 } else { 0 }, line); let fail_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2176 self.chunk.emit_op(Op::Pop, line); for (i, pat) in pats.iter().enumerate() {
2179 self.chunk.emit_op_u8(Op::MatchArm, 5, line); self.chunk.emit(i as u8, line);
2181 self.compile_pattern_test(pat, line)?;
2182 let sub_fail = self.chunk.emit_jump(Op::JumpIfFalse, line);
2183 self.chunk.emit_op(Op::Pop, line); if i == pats.len() - 1 {
2185 self.chunk.emit_op(Op::True, line);
2186 }
2187 let sub_end = self.chunk.emit_jump(Op::Jump, line);
2188 self.chunk.patch_jump(sub_fail);
2189 self.chunk.patch_jump(sub_end);
2190 }
2191 if pats.is_empty() {
2192 self.chunk.emit_op(Op::True, line);
2193 }
2194 let end = self.chunk.emit_jump(Op::Jump, line);
2195 self.chunk.patch_jump(fail_jump);
2196 self.chunk.patch_jump(end);
2197 }
2198 Pattern::Struct { name, fields } => {
2199 self.emit_match_string_operand(Op::MatchBegin, 6, name, line);
2200 let type_fail_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2201 self.chunk.emit_op(Op::Pop, line); let mut field_fail_jumps = Vec::new();
2204 for (field, pattern) in fields {
2205 self.compile_struct_field_test(field, pattern.as_ref(), line)?;
2206 field_fail_jumps.push(self.chunk.emit_jump(Op::JumpIfFalse, line));
2207 self.chunk.emit_op(Op::Pop, line); }
2209
2210 self.chunk.emit_op(Op::True, line);
2211 let end = self.chunk.emit_jump(Op::Jump, line);
2212 self.chunk.patch_jump(type_fail_jump);
2213 for jump in field_fail_jumps {
2214 self.chunk.patch_jump(jump);
2215 }
2216 self.chunk.patch_jump(end);
2217 }
2218 Pattern::EnumVariant {
2219 enum_name,
2220 variant,
2221 fields,
2222 } => match fields {
2223 EnumPatternFields::None => {
2224 self.emit_match_enum_operand(enum_name, variant, 0, line)?;
2225 }
2226 EnumPatternFields::Positional(pats) => {
2227 self.emit_match_enum_operand(enum_name, variant, pats.len(), line)?;
2228 let variant_fail_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2229 self.chunk.emit_op(Op::Pop, line); let mut field_fail_jumps = Vec::new();
2232 for (i, pat) in pats.iter().enumerate() {
2233 self.chunk.emit_op_u8(Op::MatchArm, 7, line);
2234 self.chunk.emit(i as u8, line);
2235 self.compile_pattern_test(pat, line)?;
2236 field_fail_jumps.push(self.chunk.emit_jump(Op::JumpIfFalse, line));
2237 self.chunk.emit_op(Op::Pop, line); }
2239
2240 self.chunk.emit_op(Op::True, line);
2241 let end = self.chunk.emit_jump(Op::Jump, line);
2242 self.chunk.patch_jump(variant_fail_jump);
2243 for jump in field_fail_jumps {
2244 self.chunk.patch_jump(jump);
2245 }
2246 self.chunk.patch_jump(end);
2247 }
2248 EnumPatternFields::Named(_) => {
2249 return Err(IonError::runtime(
2250 ion_str!("named enum patterns not supported in bytecode VM match")
2251 .to_string(),
2252 line,
2253 0,
2254 ));
2255 }
2256 },
2257 }
2258 Ok(())
2259 }
2260
2261 fn compile_pattern_bind(&mut self, pattern: &Pattern, line: usize) -> Result<(), IonError> {
2263 match pattern {
2264 Pattern::Wildcard => {
2265 self.chunk.emit_op(Op::Pop, line);
2266 }
2267 Pattern::Ident(name) => {
2268 self.emit_define_local(name, false, line);
2269 }
2270 Pattern::Int(_)
2271 | Pattern::Float(_)
2272 | Pattern::Bool(_)
2273 | Pattern::Str(_)
2274 | Pattern::Bytes(_)
2275 | Pattern::None => {
2276 self.chunk.emit_op(Op::Pop, line); }
2278 Pattern::Some(inner) => {
2279 self.chunk.emit_op_u8(Op::MatchArm, 1, line); self.compile_pattern_bind(inner, line)?;
2282 }
2283 Pattern::Ok(inner) => {
2284 self.chunk.emit_op_u8(Op::MatchArm, 2, line); self.compile_pattern_bind(inner, line)?;
2286 }
2287 Pattern::Err(inner) => {
2288 self.chunk.emit_op_u8(Op::MatchArm, 3, line); self.compile_pattern_bind(inner, line)?;
2290 }
2291 Pattern::Tuple(pats) => {
2292 for (i, pat) in pats.iter().enumerate() {
2293 self.chunk.emit_op(Op::Dup, line); self.chunk.emit_constant(Value::Int(i as i64), line);
2295 self.chunk.emit_op(Op::GetIndex, line);
2296 self.compile_pattern_bind(pat, line)?;
2297 }
2298 self.chunk.emit_op(Op::Pop, line); }
2300 Pattern::List(pats, rest) => {
2301 for (i, pat) in pats.iter().enumerate() {
2303 self.chunk.emit_op(Op::Dup, line); self.chunk.emit_constant(Value::Int(i as i64), line);
2305 self.chunk.emit_op(Op::GetIndex, line);
2306 self.compile_pattern_bind(pat, line)?;
2307 }
2308 if let Some(rest_pat) = rest {
2310 self.chunk.emit_op(Op::Dup, line); self.chunk
2313 .emit_constant(Value::Int(pats.len() as i64), line);
2314 self.chunk.emit_op_u8(Op::Slice, 1, line); self.compile_pattern_bind(rest_pat, line)?;
2317 }
2318 self.chunk.emit_op(Op::Pop, line); }
2320 Pattern::Struct { fields, .. } => {
2321 for (field, pattern) in fields {
2322 self.emit_match_string_operand(Op::MatchArm, 6, field, line);
2323 self.chunk.emit_op_u8(Op::MatchArm, 1, line); if let Some(pattern) = pattern {
2325 self.compile_pattern_bind(pattern, line)?;
2326 } else {
2327 self.emit_define_local(field, false, line);
2328 }
2329 }
2330 self.chunk.emit_op(Op::Pop, line);
2331 }
2332 Pattern::EnumVariant { fields, .. } => match fields {
2333 EnumPatternFields::None => {
2334 self.chunk.emit_op(Op::Pop, line);
2335 }
2336 EnumPatternFields::Positional(pats) => {
2337 for (i, pat) in pats.iter().enumerate() {
2338 self.chunk.emit_op_u8(Op::MatchArm, 7, line);
2339 self.chunk.emit(i as u8, line);
2340 self.compile_pattern_bind(pat, line)?;
2341 }
2342 self.chunk.emit_op(Op::Pop, line);
2343 }
2344 EnumPatternFields::Named(_) => {
2345 return Err(IonError::runtime(
2346 ion_str!("named enum pattern binding not supported in bytecode VM")
2347 .to_string(),
2348 line,
2349 0,
2350 ));
2351 }
2352 },
2353 }
2354 Ok(())
2355 }
2356
2357 fn compile_list_comp(
2358 &mut self,
2359 item_expr: &Expr,
2360 pattern: &Pattern,
2361 iter: &Expr,
2362 cond: Option<&Expr>,
2363 line: usize,
2364 ) -> Result<(), IonError> {
2365 self.chunk.emit_op_u16(Op::BuildList, 0, line); self.compile_expr(iter)?;
2370 self.chunk.emit_op(Op::IterInit, line);
2371
2372 let loop_start = self.chunk.len();
2373 let exit_jump = self.chunk.emit_jump(Op::IterNext, line);
2374
2375 self.begin_scope(line);
2377 self.compile_checked_let_pattern(pattern, false, line)?;
2378
2379 if let Some(cond_expr) = cond {
2381 self.compile_expr(cond_expr)?;
2382 let skip_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2383 self.chunk.emit_op(Op::Pop, line); self.compile_expr(item_expr)?;
2387 self.chunk.emit_op(Op::ListAppend, line);
2388
2389 let after = self.chunk.emit_jump(Op::Jump, line);
2390 self.chunk.patch_jump(skip_jump);
2391 self.chunk.emit_op(Op::Pop, line); self.chunk.patch_jump(after);
2393 } else {
2394 self.compile_expr(item_expr)?;
2396 self.chunk.emit_op(Op::ListAppend, line);
2397 }
2398
2399 self.end_scope(line);
2400
2401 self.chunk.emit_op(Op::Unit, line);
2403
2404 let offset = self.chunk.len() - loop_start + 3;
2406 self.chunk.emit_op_u16(Op::Loop, offset as u16, line);
2407
2408 self.chunk.patch_jump(exit_jump);
2409 self.chunk.emit_op(Op::Pop, line); Ok(())
2412 }
2413
2414 fn compile_dict_comp(
2415 &mut self,
2416 key_expr: &Expr,
2417 value_expr: &Expr,
2418 pattern: &Pattern,
2419 iter: &Expr,
2420 cond: Option<&Expr>,
2421 line: usize,
2422 ) -> Result<(), IonError> {
2423 self.chunk.emit_op_u16(Op::BuildDict, 0, line);
2425
2426 self.compile_expr(iter)?;
2427 self.chunk.emit_op(Op::IterInit, line);
2428
2429 let loop_start = self.chunk.len();
2430 let exit_jump = self.chunk.emit_jump(Op::IterNext, line);
2431
2432 self.begin_scope(line);
2433 self.compile_checked_let_pattern(pattern, false, line)?;
2434
2435 if let Some(cond_expr) = cond {
2436 self.compile_expr(cond_expr)?;
2437 let skip_jump = self.chunk.emit_jump(Op::JumpIfFalse, line);
2438 self.chunk.emit_op(Op::Pop, line);
2439
2440 self.compile_expr(key_expr)?;
2441 self.compile_expr(value_expr)?;
2442 self.chunk.emit_op(Op::DictInsert, line);
2443
2444 let after = self.chunk.emit_jump(Op::Jump, line);
2445 self.chunk.patch_jump(skip_jump);
2446 self.chunk.emit_op(Op::Pop, line);
2447 self.chunk.patch_jump(after);
2448 } else {
2449 self.compile_expr(key_expr)?;
2450 self.compile_expr(value_expr)?;
2451 self.chunk.emit_op(Op::DictInsert, line);
2452 }
2453
2454 self.end_scope(line);
2455
2456 self.chunk.emit_op(Op::Unit, line);
2458
2459 let offset = self.chunk.len() - loop_start + 3;
2460 self.chunk.emit_op_u16(Op::Loop, offset as u16, line);
2461
2462 self.chunk.patch_jump(exit_jump);
2463 self.chunk.emit_op(Op::Pop, line);
2464 Ok(())
2465 }
2466}