1use harn_lexer::StringSegment;
2use harn_parser::{BindingPattern, Node, SNode, TypedParam};
3
4use crate::chunk::{Chunk, CompiledFunction, Constant, Op};
5
6#[derive(Debug)]
8pub struct CompileError {
9 pub message: String,
10 pub line: u32,
11}
12
13impl std::fmt::Display for CompileError {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 write!(f, "Compile error at line {}: {}", self.line, self.message)
16 }
17}
18
19impl std::error::Error for CompileError {}
20
21struct LoopContext {
23 start_offset: usize,
25 break_patches: Vec<usize>,
27 has_iterator: bool,
29 handler_depth: usize,
31 finally_depth: usize,
33 scope_depth: usize,
35}
36
37pub struct Compiler {
39 chunk: Chunk,
40 line: u32,
41 column: u32,
42 enum_names: std::collections::HashSet<String>,
44 interface_methods: std::collections::HashMap<String, Vec<String>>,
46 loop_stack: Vec<LoopContext>,
48 handler_depth: usize,
50 finally_bodies: Vec<Vec<SNode>>,
52 temp_counter: usize,
54 scope_depth: usize,
56}
57
58impl Compiler {
59 pub fn new() -> Self {
60 Self {
61 chunk: Chunk::new(),
62 line: 1,
63 column: 1,
64 enum_names: std::collections::HashSet::new(),
65 interface_methods: std::collections::HashMap::new(),
66 loop_stack: Vec::new(),
67 handler_depth: 0,
68 finally_bodies: Vec::new(),
69 temp_counter: 0,
70 scope_depth: 0,
71 }
72 }
73
74 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
77 Self::collect_enum_names(program, &mut self.enum_names);
80 self.enum_names.insert("Result".to_string());
82 Self::collect_interface_methods(program, &mut self.interface_methods);
83
84 for sn in program {
86 match &sn.node {
87 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
88 self.compile_node(sn)?;
89 }
90 _ => {}
91 }
92 }
93 let main = program
95 .iter()
96 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == "default"))
97 .or_else(|| {
98 program
99 .iter()
100 .find(|sn| matches!(&sn.node, Node::Pipeline { .. }))
101 });
102
103 if let Some(sn) = main {
104 self.compile_top_level_declarations(program)?;
105 if let Node::Pipeline { body, extends, .. } = &sn.node {
106 if let Some(parent_name) = extends {
108 self.compile_parent_pipeline(program, parent_name)?;
109 }
110 self.compile_block(body)?;
111 }
112 } else {
113 let top_level: Vec<&SNode> = program
116 .iter()
117 .filter(|sn| {
118 !matches!(
119 &sn.node,
120 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
121 )
122 })
123 .collect();
124 for sn in &top_level {
125 self.compile_node(sn)?;
126 if Self::produces_value(&sn.node) {
127 self.chunk.emit(Op::Pop, self.line);
128 }
129 }
130 }
131
132 self.chunk.emit(Op::Nil, self.line);
133 self.chunk.emit(Op::Return, self.line);
134 Ok(self.chunk)
135 }
136
137 pub fn compile_named(
139 mut self,
140 program: &[SNode],
141 pipeline_name: &str,
142 ) -> Result<Chunk, CompileError> {
143 Self::collect_enum_names(program, &mut self.enum_names);
144 Self::collect_interface_methods(program, &mut self.interface_methods);
145
146 for sn in program {
147 if matches!(
148 &sn.node,
149 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
150 ) {
151 self.compile_node(sn)?;
152 }
153 }
154 let target = program
155 .iter()
156 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == pipeline_name));
157
158 if let Some(sn) = target {
159 self.compile_top_level_declarations(program)?;
160 if let Node::Pipeline { body, extends, .. } = &sn.node {
161 if let Some(parent_name) = extends {
162 self.compile_parent_pipeline(program, parent_name)?;
163 }
164 self.compile_block(body)?;
165 }
166 }
167
168 self.chunk.emit(Op::Nil, self.line);
169 self.chunk.emit(Op::Return, self.line);
170 Ok(self.chunk)
171 }
172
173 fn compile_parent_pipeline(
175 &mut self,
176 program: &[SNode],
177 parent_name: &str,
178 ) -> Result<(), CompileError> {
179 let parent = program
180 .iter()
181 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
182 if let Some(sn) = parent {
183 if let Node::Pipeline { body, extends, .. } = &sn.node {
184 if let Some(grandparent) = extends {
186 self.compile_parent_pipeline(program, grandparent)?;
187 }
188 for stmt in body {
190 self.compile_node(stmt)?;
191 if Self::produces_value(&stmt.node) {
192 self.chunk.emit(Op::Pop, self.line);
193 }
194 }
195 }
196 }
197 Ok(())
198 }
199
200 fn emit_default_preamble(&mut self, params: &[TypedParam]) -> Result<(), CompileError> {
205 for (i, param) in params.iter().enumerate() {
206 if let Some(default_expr) = ¶m.default_value {
207 self.chunk.emit(Op::GetArgc, self.line);
208 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
209 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
210 self.chunk.emit(Op::GreaterEqual, self.line);
212 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
213 self.chunk.emit(Op::Pop, self.line);
215 self.compile_node(default_expr)?;
217 let name_idx = self
218 .chunk
219 .add_constant(Constant::String(param.name.clone()));
220 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
221 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
222 self.chunk.patch_jump(skip_jump);
223 self.chunk.emit(Op::Pop, self.line);
225 self.chunk.patch_jump(end_jump);
226 }
227 }
228 Ok(())
229 }
230
231 fn emit_type_checks(&mut self, params: &[TypedParam]) {
235 for param in params {
236 if let Some(type_expr) = ¶m.type_expr {
237 if let harn_parser::TypeExpr::Shape(fields) = type_expr {
239 let spec = Self::shape_to_spec_string(fields);
240 let fn_idx = self
242 .chunk
243 .add_constant(Constant::String("__assert_shape".into()));
244 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
245 let var_idx = self
246 .chunk
247 .add_constant(Constant::String(param.name.clone()));
248 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
249 let name_idx = self
250 .chunk
251 .add_constant(Constant::String(param.name.clone()));
252 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
253 let spec_idx = self.chunk.add_constant(Constant::String(spec));
254 self.chunk.emit_u16(Op::Constant, spec_idx, self.line);
255 self.chunk.emit_u8(Op::Call, 3, self.line);
256 self.chunk.emit(Op::Pop, self.line);
257 continue;
258 }
259
260 if let harn_parser::TypeExpr::Named(name) = type_expr {
262 if let Some(methods) = self.interface_methods.get(name) {
263 let fn_idx = self
264 .chunk
265 .add_constant(Constant::String("__assert_interface".into()));
266 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
267 let var_idx = self
268 .chunk
269 .add_constant(Constant::String(param.name.clone()));
270 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
271 let name_idx = self
272 .chunk
273 .add_constant(Constant::String(param.name.clone()));
274 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
275 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
276 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
277 let methods_str = methods.join(",");
278 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
279 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
280 self.chunk.emit_u8(Op::Call, 4, self.line);
281 self.chunk.emit(Op::Pop, self.line);
282 continue;
283 }
284 }
285
286 let type_name = Self::type_expr_to_runtime_name(type_expr);
287 if let Some(type_name) = type_name {
288 let var_idx = self
289 .chunk
290 .add_constant(Constant::String(param.name.clone()));
291 let type_idx = self.chunk.add_constant(Constant::String(type_name));
292 self.chunk.emit_u16(Op::CheckType, var_idx, self.line);
293 let hi = (type_idx >> 8) as u8;
295 let lo = type_idx as u8;
296 self.chunk.code.push(hi);
297 self.chunk.code.push(lo);
298 }
299 }
300 }
301 }
302
303 fn shape_to_spec_string(fields: &[harn_parser::ShapeField]) -> String {
306 fields
307 .iter()
308 .map(|f| {
309 let opt = if f.optional { "?" } else { "" };
310 let type_str = Self::type_expr_to_spec(&f.type_expr);
311 format!("{}:{}{}", f.name, opt, type_str)
312 })
313 .collect::<Vec<_>>()
314 .join(",")
315 }
316
317 fn type_expr_to_spec(type_expr: &harn_parser::TypeExpr) -> String {
319 match type_expr {
320 harn_parser::TypeExpr::Named(name) => name.clone(),
321 harn_parser::TypeExpr::Shape(fields) => {
322 let inner = Self::shape_to_spec_string(fields);
323 format!("{{{}}}", inner)
324 }
325 harn_parser::TypeExpr::List(_) => "list".to_string(),
326 harn_parser::TypeExpr::DictType(_, _) => "dict".to_string(),
327 harn_parser::TypeExpr::Union(members) => {
328 members
330 .iter()
331 .map(Self::type_expr_to_spec)
332 .collect::<Vec<_>>()
333 .join("|")
334 }
335 harn_parser::TypeExpr::FnType { .. } => "closure".to_string(),
336 }
337 }
338
339 fn type_expr_to_runtime_name(type_expr: &harn_parser::TypeExpr) -> Option<String> {
341 match type_expr {
342 harn_parser::TypeExpr::Named(name) => match name.as_str() {
343 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
344 | "closure" => Some(name.clone()),
345 _ => None, },
347 _ => None, }
349 }
350
351 fn emit_type_name_extra(&mut self, type_name_idx: u16) {
353 let hi = (type_name_idx >> 8) as u8;
354 let lo = type_name_idx as u8;
355 self.chunk.code.push(hi);
356 self.chunk.code.push(lo);
357 self.chunk.lines.push(self.line);
358 self.chunk.columns.push(self.column);
359 self.chunk.lines.push(self.line);
360 self.chunk.columns.push(self.column);
361 }
362
363 fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
365 if body.is_empty() {
366 self.chunk.emit(Op::Nil, self.line);
367 } else {
368 self.compile_scoped_block(body)?;
369 }
370 Ok(())
371 }
372
373 fn compile_catch_binding(&mut self, error_var: &Option<String>) -> Result<(), CompileError> {
375 if let Some(var_name) = error_var {
376 let idx = self.chunk.add_constant(Constant::String(var_name.clone()));
377 self.chunk.emit_u16(Op::DefLet, idx, self.line);
378 } else {
379 self.chunk.emit(Op::Pop, self.line);
380 }
381 Ok(())
382 }
383
384 fn compile_finally_inline(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
386 if !finally_body.is_empty() {
387 self.compile_scoped_block(finally_body)?;
388 if Self::produces_value(&finally_body.last().unwrap().node) {
390 self.chunk.emit(Op::Pop, self.line);
391 }
392 }
393 Ok(())
394 }
395
396 fn compile_rethrow_with_finally(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
398 self.temp_counter += 1;
400 let temp_name = format!("__finally_err_{}__", self.temp_counter);
401 let err_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
402 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
403 self.compile_finally_inline(finally_body)?;
404 let get_idx = self.chunk.add_constant(Constant::String(temp_name));
405 self.chunk.emit_u16(Op::GetVar, get_idx, self.line);
406 self.chunk.emit(Op::Throw, self.line);
407 Ok(())
408 }
409
410 fn begin_scope(&mut self) {
411 self.chunk.emit(Op::PushScope, self.line);
412 self.scope_depth += 1;
413 }
414
415 fn end_scope(&mut self) {
416 if self.scope_depth > 0 {
417 self.chunk.emit(Op::PopScope, self.line);
418 self.scope_depth -= 1;
419 }
420 }
421
422 fn unwind_scopes_to(&mut self, target_depth: usize) {
423 while self.scope_depth > target_depth {
424 self.chunk.emit(Op::PopScope, self.line);
425 self.scope_depth -= 1;
426 }
427 }
428
429 fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
430 self.begin_scope();
431 if stmts.is_empty() {
432 self.chunk.emit(Op::Nil, self.line);
433 } else {
434 self.compile_block(stmts)?;
435 }
436 self.end_scope();
437 Ok(())
438 }
439
440 fn compile_scoped_statements(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
441 self.begin_scope();
442 for sn in stmts {
443 self.compile_node(sn)?;
444 if Self::produces_value(&sn.node) {
445 self.chunk.emit(Op::Pop, self.line);
446 }
447 }
448 self.end_scope();
449 Ok(())
450 }
451
452 fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
453 for (i, snode) in stmts.iter().enumerate() {
454 self.compile_node(snode)?;
455 let is_last = i == stmts.len() - 1;
456 if is_last {
457 if !Self::produces_value(&snode.node) {
460 self.chunk.emit(Op::Nil, self.line);
461 }
462 } else {
463 if Self::produces_value(&snode.node) {
465 self.chunk.emit(Op::Pop, self.line);
466 }
467 }
468 }
469 Ok(())
470 }
471
472 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
473 self.line = snode.span.line as u32;
474 self.column = snode.span.column as u32;
475 self.chunk.set_column(self.column);
476 match &snode.node {
477 Node::IntLiteral(n) => {
478 let idx = self.chunk.add_constant(Constant::Int(*n));
479 self.chunk.emit_u16(Op::Constant, idx, self.line);
480 }
481 Node::FloatLiteral(n) => {
482 let idx = self.chunk.add_constant(Constant::Float(*n));
483 self.chunk.emit_u16(Op::Constant, idx, self.line);
484 }
485 Node::StringLiteral(s) => {
486 let idx = self.chunk.add_constant(Constant::String(s.clone()));
487 self.chunk.emit_u16(Op::Constant, idx, self.line);
488 }
489 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
490 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
491 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
492 Node::DurationLiteral(ms) => {
493 let idx = self.chunk.add_constant(Constant::Duration(*ms));
494 self.chunk.emit_u16(Op::Constant, idx, self.line);
495 }
496
497 Node::Identifier(name) => {
498 let idx = self.chunk.add_constant(Constant::String(name.clone()));
499 self.chunk.emit_u16(Op::GetVar, idx, self.line);
500 }
501
502 Node::LetBinding { pattern, value, .. } => {
503 self.compile_node(value)?;
504 self.compile_destructuring(pattern, false)?;
505 }
506
507 Node::VarBinding { pattern, value, .. } => {
508 self.compile_node(value)?;
509 self.compile_destructuring(pattern, true)?;
510 }
511
512 Node::Assignment {
513 target, value, op, ..
514 } => {
515 if let Node::Identifier(name) = &target.node {
516 let idx = self.chunk.add_constant(Constant::String(name.clone()));
517 if let Some(op) = op {
518 self.chunk.emit_u16(Op::GetVar, idx, self.line);
519 self.compile_node(value)?;
520 self.emit_compound_op(op)?;
521 self.chunk.emit_u16(Op::SetVar, idx, self.line);
522 } else {
523 self.compile_node(value)?;
524 self.chunk.emit_u16(Op::SetVar, idx, self.line);
525 }
526 } else if let Node::PropertyAccess { object, property } = &target.node {
527 if let Some(var_name) = self.root_var_name(object) {
529 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
530 let prop_idx = self.chunk.add_constant(Constant::String(property.clone()));
531 if let Some(op) = op {
532 self.compile_node(target)?; self.compile_node(value)?;
535 self.emit_compound_op(op)?;
536 } else {
537 self.compile_node(value)?;
538 }
539 self.chunk.emit_u16(Op::SetProperty, prop_idx, self.line);
542 let hi = (var_idx >> 8) as u8;
544 let lo = var_idx as u8;
545 self.chunk.code.push(hi);
546 self.chunk.code.push(lo);
547 self.chunk.lines.push(self.line);
548 self.chunk.columns.push(self.column);
549 self.chunk.lines.push(self.line);
550 self.chunk.columns.push(self.column);
551 }
552 } else if let Node::SubscriptAccess { object, index } = &target.node {
553 if let Some(var_name) = self.root_var_name(object) {
555 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
556 if let Some(op) = op {
557 self.compile_node(target)?;
558 self.compile_node(value)?;
559 self.emit_compound_op(op)?;
560 } else {
561 self.compile_node(value)?;
562 }
563 self.compile_node(index)?;
564 self.chunk.emit_u16(Op::SetSubscript, var_idx, self.line);
565 }
566 }
567 }
568
569 Node::BinaryOp { op, left, right } => {
570 match op.as_str() {
572 "&&" => {
573 self.compile_node(left)?;
574 let jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
575 self.chunk.emit(Op::Pop, self.line);
576 self.compile_node(right)?;
577 self.chunk.patch_jump(jump);
578 self.chunk.emit(Op::Not, self.line);
580 self.chunk.emit(Op::Not, self.line);
581 return Ok(());
582 }
583 "||" => {
584 self.compile_node(left)?;
585 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
586 self.chunk.emit(Op::Pop, self.line);
587 self.compile_node(right)?;
588 self.chunk.patch_jump(jump);
589 self.chunk.emit(Op::Not, self.line);
590 self.chunk.emit(Op::Not, self.line);
591 return Ok(());
592 }
593 "??" => {
594 self.compile_node(left)?;
595 self.chunk.emit(Op::Dup, self.line);
596 self.chunk.emit(Op::Nil, self.line);
598 self.chunk.emit(Op::NotEqual, self.line);
599 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
600 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(right)?;
603 let end = self.chunk.emit_jump(Op::Jump, self.line);
604 self.chunk.patch_jump(jump);
605 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
607 return Ok(());
608 }
609 "|>" => {
610 self.compile_node(left)?;
611 if contains_pipe_placeholder(right) {
614 let replaced = replace_pipe_placeholder(right);
615 let closure_node = SNode::dummy(Node::Closure {
616 params: vec![TypedParam {
617 name: "__pipe".into(),
618 type_expr: None,
619 default_value: None,
620 }],
621 body: vec![replaced],
622 fn_syntax: false,
623 });
624 self.compile_node(&closure_node)?;
625 } else {
626 self.compile_node(right)?;
627 }
628 self.chunk.emit(Op::Pipe, self.line);
629 return Ok(());
630 }
631 _ => {}
632 }
633
634 self.compile_node(left)?;
635 self.compile_node(right)?;
636 match op.as_str() {
637 "+" => self.chunk.emit(Op::Add, self.line),
638 "-" => self.chunk.emit(Op::Sub, self.line),
639 "*" => self.chunk.emit(Op::Mul, self.line),
640 "/" => self.chunk.emit(Op::Div, self.line),
641 "%" => self.chunk.emit(Op::Mod, self.line),
642 "==" => self.chunk.emit(Op::Equal, self.line),
643 "!=" => self.chunk.emit(Op::NotEqual, self.line),
644 "<" => self.chunk.emit(Op::Less, self.line),
645 ">" => self.chunk.emit(Op::Greater, self.line),
646 "<=" => self.chunk.emit(Op::LessEqual, self.line),
647 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
648 "in" => self.chunk.emit(Op::Contains, self.line),
649 "not_in" => {
650 self.chunk.emit(Op::Contains, self.line);
651 self.chunk.emit(Op::Not, self.line);
652 }
653 _ => {
654 return Err(CompileError {
655 message: format!("Unknown operator: {op}"),
656 line: self.line,
657 })
658 }
659 }
660 }
661
662 Node::UnaryOp { op, operand } => {
663 self.compile_node(operand)?;
664 match op.as_str() {
665 "-" => self.chunk.emit(Op::Negate, self.line),
666 "!" => self.chunk.emit(Op::Not, self.line),
667 _ => {}
668 }
669 }
670
671 Node::Ternary {
672 condition,
673 true_expr,
674 false_expr,
675 } => {
676 self.compile_node(condition)?;
677 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
678 self.chunk.emit(Op::Pop, self.line);
679 self.compile_node(true_expr)?;
680 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
681 self.chunk.patch_jump(else_jump);
682 self.chunk.emit(Op::Pop, self.line);
683 self.compile_node(false_expr)?;
684 self.chunk.patch_jump(end_jump);
685 }
686
687 Node::FunctionCall { name, args } => {
688 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
689 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
691 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
692
693 if has_spread {
694 self.chunk.emit_u16(Op::BuildList, 0, self.line);
697 let mut pending = 0u16;
698 for arg in args {
699 if let Node::Spread(inner) = &arg.node {
700 if pending > 0 {
701 self.chunk.emit_u16(Op::BuildList, pending, self.line);
702 self.chunk.emit(Op::Add, self.line);
703 pending = 0;
704 }
705 self.compile_node(inner)?;
706 self.chunk.emit(Op::Dup, self.line);
707 let assert_idx = self
708 .chunk
709 .add_constant(Constant::String("__assert_list".into()));
710 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
711 self.chunk.emit(Op::Swap, self.line);
712 self.chunk.emit_u8(Op::Call, 1, self.line);
713 self.chunk.emit(Op::Pop, self.line);
714 self.chunk.emit(Op::Add, self.line);
715 } else {
716 self.compile_node(arg)?;
717 pending += 1;
718 }
719 }
720 if pending > 0 {
721 self.chunk.emit_u16(Op::BuildList, pending, self.line);
722 self.chunk.emit(Op::Add, self.line);
723 }
724 self.chunk.emit(Op::CallSpread, self.line);
725 } else {
726 for arg in args {
728 self.compile_node(arg)?;
729 }
730 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
731 }
732 }
733
734 Node::MethodCall {
735 object,
736 method,
737 args,
738 } => {
739 if let Node::Identifier(name) = &object.node {
741 if self.enum_names.contains(name) {
742 for arg in args {
744 self.compile_node(arg)?;
745 }
746 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
747 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
748 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
749 let hi = (var_idx >> 8) as u8;
750 let lo = var_idx as u8;
751 self.chunk.code.push(hi);
752 self.chunk.code.push(lo);
753 self.chunk.lines.push(self.line);
754 self.chunk.columns.push(self.column);
755 self.chunk.lines.push(self.line);
756 self.chunk.columns.push(self.column);
757 let fc = args.len() as u16;
758 let fhi = (fc >> 8) as u8;
759 let flo = fc as u8;
760 self.chunk.code.push(fhi);
761 self.chunk.code.push(flo);
762 self.chunk.lines.push(self.line);
763 self.chunk.columns.push(self.column);
764 self.chunk.lines.push(self.line);
765 self.chunk.columns.push(self.column);
766 return Ok(());
767 }
768 }
769 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
770 self.compile_node(object)?;
771 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
772 if has_spread {
773 self.chunk.emit_u16(Op::BuildList, 0, self.line);
775 let mut pending = 0u16;
776 for arg in args {
777 if let Node::Spread(inner) = &arg.node {
778 if pending > 0 {
779 self.chunk.emit_u16(Op::BuildList, pending, self.line);
780 self.chunk.emit(Op::Add, self.line);
781 pending = 0;
782 }
783 self.compile_node(inner)?;
784 self.chunk.emit(Op::Dup, self.line);
785 let assert_idx = self
786 .chunk
787 .add_constant(Constant::String("__assert_list".into()));
788 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
789 self.chunk.emit(Op::Swap, self.line);
790 self.chunk.emit_u8(Op::Call, 1, self.line);
791 self.chunk.emit(Op::Pop, self.line);
792 self.chunk.emit(Op::Add, self.line);
793 } else {
794 self.compile_node(arg)?;
795 pending += 1;
796 }
797 }
798 if pending > 0 {
799 self.chunk.emit_u16(Op::BuildList, pending, self.line);
800 self.chunk.emit(Op::Add, self.line);
801 }
802 self.chunk
803 .emit_u16(Op::MethodCallSpread, name_idx, self.line);
804 } else {
805 for arg in args {
806 self.compile_node(arg)?;
807 }
808 self.chunk
809 .emit_method_call(name_idx, args.len() as u8, self.line);
810 }
811 }
812
813 Node::OptionalMethodCall {
814 object,
815 method,
816 args,
817 } => {
818 self.compile_node(object)?;
819 for arg in args {
820 self.compile_node(arg)?;
821 }
822 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
823 self.chunk
824 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
825 }
826
827 Node::PropertyAccess { object, property } => {
828 if let Node::Identifier(name) = &object.node {
830 if self.enum_names.contains(name) {
831 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
833 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
834 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
835 let hi = (var_idx >> 8) as u8;
836 let lo = var_idx as u8;
837 self.chunk.code.push(hi);
838 self.chunk.code.push(lo);
839 self.chunk.lines.push(self.line);
840 self.chunk.columns.push(self.column);
841 self.chunk.lines.push(self.line);
842 self.chunk.columns.push(self.column);
843 self.chunk.code.push(0);
845 self.chunk.code.push(0);
846 self.chunk.lines.push(self.line);
847 self.chunk.columns.push(self.column);
848 self.chunk.lines.push(self.line);
849 self.chunk.columns.push(self.column);
850 return Ok(());
851 }
852 }
853 self.compile_node(object)?;
854 let idx = self.chunk.add_constant(Constant::String(property.clone()));
855 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
856 }
857
858 Node::OptionalPropertyAccess { object, property } => {
859 self.compile_node(object)?;
860 let idx = self.chunk.add_constant(Constant::String(property.clone()));
861 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
862 }
863
864 Node::SubscriptAccess { object, index } => {
865 self.compile_node(object)?;
866 self.compile_node(index)?;
867 self.chunk.emit(Op::Subscript, self.line);
868 }
869
870 Node::SliceAccess { object, start, end } => {
871 self.compile_node(object)?;
872 if let Some(s) = start {
873 self.compile_node(s)?;
874 } else {
875 self.chunk.emit(Op::Nil, self.line);
876 }
877 if let Some(e) = end {
878 self.compile_node(e)?;
879 } else {
880 self.chunk.emit(Op::Nil, self.line);
881 }
882 self.chunk.emit(Op::Slice, self.line);
883 }
884
885 Node::IfElse {
886 condition,
887 then_body,
888 else_body,
889 } => {
890 self.compile_node(condition)?;
891 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
892 self.chunk.emit(Op::Pop, self.line);
893 self.compile_scoped_block(then_body)?;
894 if let Some(else_body) = else_body {
895 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
896 self.chunk.patch_jump(else_jump);
897 self.chunk.emit(Op::Pop, self.line);
898 self.compile_scoped_block(else_body)?;
899 self.chunk.patch_jump(end_jump);
900 } else {
901 self.chunk.patch_jump(else_jump);
902 self.chunk.emit(Op::Pop, self.line);
903 self.chunk.emit(Op::Nil, self.line);
904 }
905 }
906
907 Node::WhileLoop { condition, body } => {
908 let loop_start = self.chunk.current_offset();
909 self.loop_stack.push(LoopContext {
910 start_offset: loop_start,
911 break_patches: Vec::new(),
912 has_iterator: false,
913 handler_depth: self.handler_depth,
914 finally_depth: self.finally_bodies.len(),
915 scope_depth: self.scope_depth,
916 });
917 self.compile_node(condition)?;
918 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
919 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_statements(body)?;
921 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
923 self.chunk.patch_jump(exit_jump);
924 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
927 for patch_pos in ctx.break_patches {
928 self.chunk.patch_jump(patch_pos);
929 }
930 self.chunk.emit(Op::Nil, self.line);
931 }
932
933 Node::ForIn {
934 pattern,
935 iterable,
936 body,
937 } => {
938 self.compile_node(iterable)?;
940 self.chunk.emit(Op::IterInit, self.line);
942 let loop_start = self.chunk.current_offset();
943 self.loop_stack.push(LoopContext {
944 start_offset: loop_start,
945 break_patches: Vec::new(),
946 has_iterator: true,
947 handler_depth: self.handler_depth,
948 finally_depth: self.finally_bodies.len(),
949 scope_depth: self.scope_depth,
950 });
951 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
953 self.begin_scope();
954 self.compile_destructuring(pattern, true)?;
956 for sn in body {
957 self.compile_node(sn)?;
958 if Self::produces_value(&sn.node) {
959 self.chunk.emit(Op::Pop, self.line);
960 }
961 }
962 self.end_scope();
963 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
965 self.chunk.patch_jump(exit_jump_pos);
966 let ctx = self.loop_stack.pop().unwrap();
968 for patch_pos in ctx.break_patches {
969 self.chunk.patch_jump(patch_pos);
970 }
971 self.chunk.emit(Op::Nil, self.line);
973 }
974
975 Node::ReturnStmt { value } => {
976 let has_pending_finally = !self.finally_bodies.is_empty();
977
978 if has_pending_finally {
979 if let Some(val) = value {
982 self.compile_node(val)?;
983 } else {
984 self.chunk.emit(Op::Nil, self.line);
985 }
986 self.temp_counter += 1;
987 let temp_name = format!("__return_val_{}__", self.temp_counter);
988 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
989 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
990 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
992 for fb in &finallys {
993 self.compile_finally_inline(fb)?;
994 }
995 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
996 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
997 self.chunk.emit(Op::Return, self.line);
998 } else {
999 if let Some(val) = value {
1001 if let Node::FunctionCall { name, args } = &val.node {
1002 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1003 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1004 for arg in args {
1005 self.compile_node(arg)?;
1006 }
1007 self.chunk
1008 .emit_u8(Op::TailCall, args.len() as u8, self.line);
1009 } else if let Node::BinaryOp { op, left, right } = &val.node {
1010 if op == "|>" {
1011 self.compile_node(left)?;
1012 self.compile_node(right)?;
1013 self.chunk.emit(Op::Swap, self.line);
1014 self.chunk.emit_u8(Op::TailCall, 1, self.line);
1015 } else {
1016 self.compile_node(val)?;
1017 }
1018 } else {
1019 self.compile_node(val)?;
1020 }
1021 } else {
1022 self.chunk.emit(Op::Nil, self.line);
1023 }
1024 self.chunk.emit(Op::Return, self.line);
1025 }
1026 }
1027
1028 Node::BreakStmt => {
1029 if self.loop_stack.is_empty() {
1030 return Err(CompileError {
1031 message: "break outside of loop".to_string(),
1032 line: self.line,
1033 });
1034 }
1035 let ctx = self.loop_stack.last().unwrap();
1037 let finally_depth = ctx.finally_depth;
1038 let handler_depth = ctx.handler_depth;
1039 let has_iterator = ctx.has_iterator;
1040 let scope_depth = ctx.scope_depth;
1041 for _ in handler_depth..self.handler_depth {
1043 self.chunk.emit(Op::PopHandler, self.line);
1044 }
1045 if self.finally_bodies.len() > finally_depth {
1047 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1048 .iter()
1049 .rev()
1050 .cloned()
1051 .collect();
1052 for fb in &finallys {
1053 self.compile_finally_inline(fb)?;
1054 }
1055 }
1056 self.unwind_scopes_to(scope_depth);
1057 if has_iterator {
1058 self.chunk.emit(Op::PopIterator, self.line);
1059 }
1060 let patch = self.chunk.emit_jump(Op::Jump, self.line);
1061 self.loop_stack
1062 .last_mut()
1063 .unwrap()
1064 .break_patches
1065 .push(patch);
1066 }
1067
1068 Node::ContinueStmt => {
1069 if self.loop_stack.is_empty() {
1070 return Err(CompileError {
1071 message: "continue outside of loop".to_string(),
1072 line: self.line,
1073 });
1074 }
1075 let ctx = self.loop_stack.last().unwrap();
1076 let finally_depth = ctx.finally_depth;
1077 let handler_depth = ctx.handler_depth;
1078 let loop_start = ctx.start_offset;
1079 let scope_depth = ctx.scope_depth;
1080 for _ in handler_depth..self.handler_depth {
1081 self.chunk.emit(Op::PopHandler, self.line);
1082 }
1083 if self.finally_bodies.len() > finally_depth {
1084 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1085 .iter()
1086 .rev()
1087 .cloned()
1088 .collect();
1089 for fb in &finallys {
1090 self.compile_finally_inline(fb)?;
1091 }
1092 }
1093 self.unwind_scopes_to(scope_depth);
1094 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1095 }
1096
1097 Node::ListLiteral(elements) => {
1098 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
1099 if !has_spread {
1100 for el in elements {
1101 self.compile_node(el)?;
1102 }
1103 self.chunk
1104 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
1105 } else {
1106 self.chunk.emit_u16(Op::BuildList, 0, self.line);
1109 let mut pending = 0u16;
1110 for el in elements {
1111 if let Node::Spread(inner) = &el.node {
1112 if pending > 0 {
1114 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1115 self.chunk.emit(Op::Add, self.line);
1117 pending = 0;
1118 }
1119 self.compile_node(inner)?;
1121 self.chunk.emit(Op::Dup, self.line);
1122 let assert_idx = self
1123 .chunk
1124 .add_constant(Constant::String("__assert_list".into()));
1125 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1126 self.chunk.emit(Op::Swap, self.line);
1127 self.chunk.emit_u8(Op::Call, 1, self.line);
1128 self.chunk.emit(Op::Pop, self.line);
1129 self.chunk.emit(Op::Add, self.line);
1130 } else {
1131 self.compile_node(el)?;
1132 pending += 1;
1133 }
1134 }
1135 if pending > 0 {
1136 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1137 self.chunk.emit(Op::Add, self.line);
1138 }
1139 }
1140 }
1141
1142 Node::DictLiteral(entries) => {
1143 let has_spread = entries
1144 .iter()
1145 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1146 if !has_spread {
1147 for entry in entries {
1148 self.compile_node(&entry.key)?;
1149 self.compile_node(&entry.value)?;
1150 }
1151 self.chunk
1152 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1153 } else {
1154 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1156 let mut pending = 0u16;
1157 for entry in entries {
1158 if let Node::Spread(inner) = &entry.value.node {
1159 if pending > 0 {
1161 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1162 self.chunk.emit(Op::Add, self.line);
1163 pending = 0;
1164 }
1165 self.compile_node(inner)?;
1167 self.chunk.emit(Op::Dup, self.line);
1168 let assert_idx = self
1169 .chunk
1170 .add_constant(Constant::String("__assert_dict".into()));
1171 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1172 self.chunk.emit(Op::Swap, self.line);
1173 self.chunk.emit_u8(Op::Call, 1, self.line);
1174 self.chunk.emit(Op::Pop, self.line);
1175 self.chunk.emit(Op::Add, self.line);
1176 } else {
1177 self.compile_node(&entry.key)?;
1178 self.compile_node(&entry.value)?;
1179 pending += 1;
1180 }
1181 }
1182 if pending > 0 {
1183 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1184 self.chunk.emit(Op::Add, self.line);
1185 }
1186 }
1187 }
1188
1189 Node::InterpolatedString(segments) => {
1190 let mut part_count = 0u16;
1191 for seg in segments {
1192 match seg {
1193 StringSegment::Literal(s) => {
1194 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1195 self.chunk.emit_u16(Op::Constant, idx, self.line);
1196 part_count += 1;
1197 }
1198 StringSegment::Expression(expr_str, expr_line, expr_col) => {
1199 let mut lexer =
1201 harn_lexer::Lexer::with_position(expr_str, *expr_line, *expr_col);
1202 if let Ok(tokens) = lexer.tokenize() {
1203 let mut parser = harn_parser::Parser::new(tokens);
1204 if let Ok(snode) = parser.parse_single_expression() {
1205 self.compile_node(&snode)?;
1206 let to_str = self
1208 .chunk
1209 .add_constant(Constant::String("to_string".into()));
1210 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1211 self.chunk.emit(Op::Swap, self.line);
1212 self.chunk.emit_u8(Op::Call, 1, self.line);
1213 part_count += 1;
1214 } else {
1215 let idx =
1217 self.chunk.add_constant(Constant::String(expr_str.clone()));
1218 self.chunk.emit_u16(Op::Constant, idx, self.line);
1219 part_count += 1;
1220 }
1221 }
1222 }
1223 }
1224 }
1225 if part_count > 1 {
1226 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1227 }
1228 }
1229
1230 Node::FnDecl {
1231 name, params, body, ..
1232 } => {
1233 let mut fn_compiler = Compiler::new();
1235 fn_compiler.enum_names = self.enum_names.clone();
1236 fn_compiler.emit_default_preamble(params)?;
1237 fn_compiler.emit_type_checks(params);
1238 let is_gen = body_contains_yield(body);
1239 fn_compiler.compile_block(body)?;
1240 fn_compiler.chunk.emit(Op::Nil, self.line);
1241 fn_compiler.chunk.emit(Op::Return, self.line);
1242
1243 let func = CompiledFunction {
1244 name: name.clone(),
1245 params: TypedParam::names(params),
1246 default_start: TypedParam::default_start(params),
1247 chunk: fn_compiler.chunk,
1248 is_generator: is_gen,
1249 };
1250 let fn_idx = self.chunk.functions.len();
1251 self.chunk.functions.push(func);
1252
1253 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1254 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1255 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1256 }
1257
1258 Node::ToolDecl {
1259 name,
1260 description,
1261 params,
1262 return_type: _,
1263 body,
1264 ..
1265 } => {
1266 let mut fn_compiler = Compiler::new();
1268 fn_compiler.enum_names = self.enum_names.clone();
1269 fn_compiler.emit_default_preamble(params)?;
1270 fn_compiler.emit_type_checks(params);
1271 fn_compiler.compile_block(body)?;
1272 fn_compiler.chunk.emit(Op::Return, self.line);
1274
1275 let func = CompiledFunction {
1276 name: name.clone(),
1277 params: TypedParam::names(params),
1278 default_start: TypedParam::default_start(params),
1279 chunk: fn_compiler.chunk,
1280 is_generator: false,
1281 };
1282 let fn_idx = self.chunk.functions.len();
1283 self.chunk.functions.push(func);
1284
1285 let define_name = self
1287 .chunk
1288 .add_constant(Constant::String("tool_define".into()));
1289 self.chunk.emit_u16(Op::Constant, define_name, self.line);
1290
1291 let reg_name = self
1293 .chunk
1294 .add_constant(Constant::String("tool_registry".into()));
1295 self.chunk.emit_u16(Op::Constant, reg_name, self.line);
1296 self.chunk.emit_u8(Op::Call, 0, self.line);
1297
1298 let tool_name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1300 self.chunk.emit_u16(Op::Constant, tool_name_idx, self.line);
1301
1302 let desc = description.as_deref().unwrap_or("");
1304 let desc_idx = self.chunk.add_constant(Constant::String(desc.to_string()));
1305 self.chunk.emit_u16(Op::Constant, desc_idx, self.line);
1306
1307 let mut param_count: u16 = 0;
1310 for p in params {
1311 let pn_idx = self.chunk.add_constant(Constant::String(p.name.clone()));
1312 self.chunk.emit_u16(Op::Constant, pn_idx, self.line);
1313
1314 let type_str = match &p.type_expr {
1315 Some(harn_parser::TypeExpr::Named(n)) => match n.as_str() {
1316 "string" => "string",
1317 "int" => "integer",
1318 "float" => "number",
1319 "bool" => "boolean",
1320 _ => "string",
1321 },
1322 _ => "string",
1323 };
1324 let type_key = self.chunk.add_constant(Constant::String("type".into()));
1325 self.chunk.emit_u16(Op::Constant, type_key, self.line);
1326 let type_val = self.chunk.add_constant(Constant::String(type_str.into()));
1327 self.chunk.emit_u16(Op::Constant, type_val, self.line);
1328 self.chunk.emit_u16(Op::BuildDict, 1, self.line);
1329 param_count += 1;
1330 }
1331 self.chunk.emit_u16(Op::BuildDict, param_count, self.line);
1332
1333 let params_key = self
1335 .chunk
1336 .add_constant(Constant::String("parameters".into()));
1337 self.chunk.emit_u16(Op::Constant, params_key, self.line);
1338 self.chunk.emit(Op::Swap, self.line);
1339
1340 let handler_key = self.chunk.add_constant(Constant::String("handler".into()));
1341 self.chunk.emit_u16(Op::Constant, handler_key, self.line);
1342 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1343
1344 self.chunk.emit_u16(Op::BuildDict, 2, self.line);
1345
1346 self.chunk.emit_u8(Op::Call, 4, self.line);
1348
1349 let bind_idx = self.chunk.add_constant(Constant::String(name.clone()));
1351 self.chunk.emit_u16(Op::DefLet, bind_idx, self.line);
1352 }
1353
1354 Node::Closure { params, body, .. } => {
1355 let mut fn_compiler = Compiler::new();
1356 fn_compiler.enum_names = self.enum_names.clone();
1357 fn_compiler.emit_default_preamble(params)?;
1358 fn_compiler.emit_type_checks(params);
1359 let is_gen = body_contains_yield(body);
1360 fn_compiler.compile_block(body)?;
1361 fn_compiler.chunk.emit(Op::Return, self.line);
1363
1364 let func = CompiledFunction {
1365 name: "<closure>".to_string(),
1366 params: TypedParam::names(params),
1367 default_start: TypedParam::default_start(params),
1368 chunk: fn_compiler.chunk,
1369 is_generator: is_gen,
1370 };
1371 let fn_idx = self.chunk.functions.len();
1372 self.chunk.functions.push(func);
1373
1374 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1375 }
1376
1377 Node::ThrowStmt { value } => {
1378 self.compile_node(value)?;
1379 self.chunk.emit(Op::Throw, self.line);
1380 }
1381
1382 Node::MatchExpr { value, arms } => {
1383 self.compile_node(value)?;
1384 let mut end_jumps = Vec::new();
1385 for arm in arms {
1386 match &arm.pattern.node {
1387 Node::Identifier(name) if name == "_" => {
1389 self.begin_scope();
1390 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1392 self.end_scope();
1393 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1394 }
1395 Node::EnumConstruct {
1397 enum_name,
1398 variant,
1399 args: pat_args,
1400 } => {
1401 self.chunk.emit(Op::Dup, self.line);
1403 let en_idx =
1404 self.chunk.add_constant(Constant::String(enum_name.clone()));
1405 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1406 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1407 let hi = (vn_idx >> 8) as u8;
1408 let lo = vn_idx as u8;
1409 self.chunk.code.push(hi);
1410 self.chunk.code.push(lo);
1411 self.chunk.lines.push(self.line);
1412 self.chunk.columns.push(self.column);
1413 self.chunk.lines.push(self.line);
1414 self.chunk.columns.push(self.column);
1415 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1417 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1419
1420 for (i, pat_arg) in pat_args.iter().enumerate() {
1423 if let Node::Identifier(binding_name) = &pat_arg.node {
1424 self.chunk.emit(Op::Dup, self.line);
1426 let fields_idx = self
1427 .chunk
1428 .add_constant(Constant::String("fields".to_string()));
1429 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1430 let idx_const =
1431 self.chunk.add_constant(Constant::Int(i as i64));
1432 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1433 self.chunk.emit(Op::Subscript, self.line);
1434 let name_idx = self
1435 .chunk
1436 .add_constant(Constant::String(binding_name.clone()));
1437 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1438 }
1439 }
1440
1441 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1443 self.end_scope();
1444 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1445 self.chunk.patch_jump(skip);
1446 self.chunk.emit(Op::Pop, self.line); }
1448 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1450 {
1451 let enum_name = if let Node::Identifier(n) = &object.node {
1452 n.clone()
1453 } else {
1454 unreachable!()
1455 };
1456 self.chunk.emit(Op::Dup, self.line);
1457 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1458 let vn_idx =
1459 self.chunk.add_constant(Constant::String(property.clone()));
1460 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1461 let hi = (vn_idx >> 8) as u8;
1462 let lo = vn_idx as u8;
1463 self.chunk.code.push(hi);
1464 self.chunk.code.push(lo);
1465 self.chunk.lines.push(self.line);
1466 self.chunk.columns.push(self.column);
1467 self.chunk.lines.push(self.line);
1468 self.chunk.columns.push(self.column);
1469 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1470 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1472 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1474 self.end_scope();
1475 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1476 self.chunk.patch_jump(skip);
1477 self.chunk.emit(Op::Pop, self.line); }
1479 Node::MethodCall {
1482 object,
1483 method,
1484 args: pat_args,
1485 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1486 {
1487 let enum_name = if let Node::Identifier(n) = &object.node {
1488 n.clone()
1489 } else {
1490 unreachable!()
1491 };
1492 self.chunk.emit(Op::Dup, self.line);
1494 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1495 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1496 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1497 let hi = (vn_idx >> 8) as u8;
1498 let lo = vn_idx as u8;
1499 self.chunk.code.push(hi);
1500 self.chunk.code.push(lo);
1501 self.chunk.lines.push(self.line);
1502 self.chunk.columns.push(self.column);
1503 self.chunk.lines.push(self.line);
1504 self.chunk.columns.push(self.column);
1505 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1506 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1508
1509 for (i, pat_arg) in pat_args.iter().enumerate() {
1511 if let Node::Identifier(binding_name) = &pat_arg.node {
1512 self.chunk.emit(Op::Dup, self.line);
1513 let fields_idx = self
1514 .chunk
1515 .add_constant(Constant::String("fields".to_string()));
1516 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1517 let idx_const =
1518 self.chunk.add_constant(Constant::Int(i as i64));
1519 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1520 self.chunk.emit(Op::Subscript, self.line);
1521 let name_idx = self
1522 .chunk
1523 .add_constant(Constant::String(binding_name.clone()));
1524 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1525 }
1526 }
1527
1528 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1530 self.end_scope();
1531 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1532 self.chunk.patch_jump(skip);
1533 self.chunk.emit(Op::Pop, self.line); }
1535 Node::Identifier(name) => {
1537 self.begin_scope();
1538 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1541 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1542 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1544 self.end_scope();
1545 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1546 }
1547 Node::DictLiteral(entries)
1549 if entries
1550 .iter()
1551 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1552 {
1553 self.chunk.emit(Op::Dup, self.line);
1555 let typeof_idx =
1556 self.chunk.add_constant(Constant::String("type_of".into()));
1557 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1558 self.chunk.emit(Op::Swap, self.line);
1559 self.chunk.emit_u8(Op::Call, 1, self.line);
1560 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1561 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1562 self.chunk.emit(Op::Equal, self.line);
1563 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1564 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1568 let mut bindings = Vec::new();
1569 self.begin_scope();
1570 for entry in entries {
1571 if let Node::StringLiteral(key) = &entry.key.node {
1572 match &entry.value.node {
1573 Node::StringLiteral(_)
1575 | Node::IntLiteral(_)
1576 | Node::FloatLiteral(_)
1577 | Node::BoolLiteral(_)
1578 | Node::NilLiteral => {
1579 self.chunk.emit(Op::Dup, self.line);
1580 let key_idx = self
1581 .chunk
1582 .add_constant(Constant::String(key.clone()));
1583 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1584 self.chunk.emit(Op::Subscript, self.line);
1585 self.compile_node(&entry.value)?;
1586 self.chunk.emit(Op::Equal, self.line);
1587 let skip =
1588 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1589 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1591 }
1592 Node::Identifier(binding) => {
1594 bindings.push((key.clone(), binding.clone()));
1595 }
1596 _ => {
1597 self.chunk.emit(Op::Dup, self.line);
1599 let key_idx = self
1600 .chunk
1601 .add_constant(Constant::String(key.clone()));
1602 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1603 self.chunk.emit(Op::Subscript, self.line);
1604 self.compile_node(&entry.value)?;
1605 self.chunk.emit(Op::Equal, self.line);
1606 let skip =
1607 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1608 self.chunk.emit(Op::Pop, self.line);
1609 constraint_skips.push(skip);
1610 }
1611 }
1612 }
1613 }
1614
1615 for (key, binding) in &bindings {
1617 self.chunk.emit(Op::Dup, self.line);
1618 let key_idx =
1619 self.chunk.add_constant(Constant::String(key.clone()));
1620 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1621 self.chunk.emit(Op::Subscript, self.line);
1622 let name_idx =
1623 self.chunk.add_constant(Constant::String(binding.clone()));
1624 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1625 }
1626
1627 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1629 self.end_scope();
1630 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1631
1632 let type_fail_target = self.chunk.code.len();
1633 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1635 let scoped_fail_target = self.chunk.code.len();
1636 self.chunk.emit(Op::PopScope, self.line);
1637 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1639
1640 for skip in constraint_skips {
1641 self.chunk.patch_jump_to(skip, scoped_fail_target);
1642 }
1643 self.chunk.patch_jump_to(skip_type, type_fail_target);
1644 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1645 }
1646 Node::ListLiteral(elements) => {
1648 self.chunk.emit(Op::Dup, self.line);
1650 let typeof_idx =
1651 self.chunk.add_constant(Constant::String("type_of".into()));
1652 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1653 self.chunk.emit(Op::Swap, self.line);
1654 self.chunk.emit_u8(Op::Call, 1, self.line);
1655 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1656 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1657 self.chunk.emit(Op::Equal, self.line);
1658 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1659 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1663 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1664 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1665 self.chunk.emit(Op::Swap, self.line);
1666 self.chunk.emit_u8(Op::Call, 1, self.line);
1667 let count = self
1668 .chunk
1669 .add_constant(Constant::Int(elements.len() as i64));
1670 self.chunk.emit_u16(Op::Constant, count, self.line);
1671 self.chunk.emit(Op::GreaterEqual, self.line);
1672 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1673 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1677 let mut bindings = Vec::new();
1678 self.begin_scope();
1679 for (i, elem) in elements.iter().enumerate() {
1680 match &elem.node {
1681 Node::Identifier(name) if name != "_" => {
1682 bindings.push((i, name.clone()));
1683 }
1684 Node::Identifier(_) => {} _ => {
1687 self.chunk.emit(Op::Dup, self.line);
1688 let idx_const =
1689 self.chunk.add_constant(Constant::Int(i as i64));
1690 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1691 self.chunk.emit(Op::Subscript, self.line);
1692 self.compile_node(elem)?;
1693 self.chunk.emit(Op::Equal, self.line);
1694 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1695 self.chunk.emit(Op::Pop, self.line);
1696 constraint_skips.push(skip);
1697 }
1698 }
1699 }
1700
1701 for (i, name) in &bindings {
1703 self.chunk.emit(Op::Dup, self.line);
1704 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1705 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1706 self.chunk.emit(Op::Subscript, self.line);
1707 let name_idx =
1708 self.chunk.add_constant(Constant::String(name.clone()));
1709 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1710 }
1711
1712 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1714 self.end_scope();
1715 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1716
1717 let pre_scope_fail_target = self.chunk.code.len();
1718 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1720 let scoped_fail_target = self.chunk.code.len();
1721 self.chunk.emit(Op::PopScope, self.line);
1722 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1724 for skip in constraint_skips {
1725 self.chunk.patch_jump_to(skip, scoped_fail_target);
1726 }
1727 self.chunk.patch_jump_to(skip_len, pre_scope_fail_target);
1728 self.chunk.patch_jump_to(skip_type, pre_scope_fail_target);
1729 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1730 }
1731 _ => {
1733 self.chunk.emit(Op::Dup, self.line);
1734 self.compile_node(&arm.pattern)?;
1735 self.chunk.emit(Op::Equal, self.line);
1736 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1737 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1739 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1741 self.end_scope();
1742 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1743 self.chunk.patch_jump(skip);
1744 self.chunk.emit(Op::Pop, self.line); }
1746 }
1747 }
1748 self.chunk.emit(Op::Pop, self.line);
1750 self.chunk.emit(Op::Nil, self.line);
1751 for j in end_jumps {
1752 self.chunk.patch_jump(j);
1753 }
1754 }
1755
1756 Node::RangeExpr {
1757 start,
1758 end,
1759 inclusive,
1760 } => {
1761 let name_idx = self
1763 .chunk
1764 .add_constant(Constant::String("__range__".to_string()));
1765 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1766 self.compile_node(start)?;
1767 self.compile_node(end)?;
1768 if *inclusive {
1769 self.chunk.emit(Op::True, self.line);
1770 } else {
1771 self.chunk.emit(Op::False, self.line);
1772 }
1773 self.chunk.emit_u8(Op::Call, 3, self.line);
1774 }
1775
1776 Node::GuardStmt {
1777 condition,
1778 else_body,
1779 } => {
1780 self.compile_node(condition)?;
1783 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1784 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_block(else_body)?;
1787 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
1789 self.chunk.emit(Op::Pop, self.line);
1790 }
1791 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1792 self.chunk.patch_jump(skip_jump);
1793 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
1795 self.chunk.emit(Op::Nil, self.line);
1796 }
1797
1798 Node::RequireStmt { condition, message } => {
1799 self.compile_node(condition)?;
1800 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1801 self.chunk.emit(Op::Pop, self.line);
1802 if let Some(message) = message {
1803 self.compile_node(message)?;
1804 } else {
1805 let idx = self
1806 .chunk
1807 .add_constant(Constant::String("require condition failed".to_string()));
1808 self.chunk.emit_u16(Op::Constant, idx, self.line);
1809 }
1810 self.chunk.emit(Op::Throw, self.line);
1811 self.chunk.patch_jump(ok_jump);
1812 self.chunk.emit(Op::Pop, self.line);
1813 }
1814
1815 Node::Block(stmts) => {
1816 self.compile_scoped_block(stmts)?;
1817 }
1818
1819 Node::DeadlineBlock { duration, body } => {
1820 self.compile_node(duration)?;
1821 self.chunk.emit(Op::DeadlineSetup, self.line);
1822 self.compile_scoped_block(body)?;
1823 self.chunk.emit(Op::DeadlineEnd, self.line);
1824 }
1825
1826 Node::MutexBlock { body } => {
1827 self.begin_scope();
1829 for sn in body {
1830 self.compile_node(sn)?;
1831 if Self::produces_value(&sn.node) {
1832 self.chunk.emit(Op::Pop, self.line);
1833 }
1834 }
1835 self.chunk.emit(Op::Nil, self.line);
1836 self.end_scope();
1837 }
1838
1839 Node::YieldExpr { value } => {
1840 if let Some(val) = value {
1841 self.compile_node(val)?;
1842 } else {
1843 self.chunk.emit(Op::Nil, self.line);
1844 }
1845 self.chunk.emit(Op::Yield, self.line);
1846 }
1847
1848 Node::AskExpr { fields } => {
1849 for entry in fields {
1852 self.compile_node(&entry.key)?;
1853 self.compile_node(&entry.value)?;
1854 }
1855 self.chunk
1856 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
1857 }
1858
1859 Node::EnumConstruct {
1860 enum_name,
1861 variant,
1862 args,
1863 } => {
1864 for arg in args {
1866 self.compile_node(arg)?;
1867 }
1868 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
1869 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1870 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
1872 let hi = (var_idx >> 8) as u8;
1873 let lo = var_idx as u8;
1874 self.chunk.code.push(hi);
1875 self.chunk.code.push(lo);
1876 self.chunk.lines.push(self.line);
1877 self.chunk.columns.push(self.column);
1878 self.chunk.lines.push(self.line);
1879 self.chunk.columns.push(self.column);
1880 let fc = args.len() as u16;
1881 let fhi = (fc >> 8) as u8;
1882 let flo = fc as u8;
1883 self.chunk.code.push(fhi);
1884 self.chunk.code.push(flo);
1885 self.chunk.lines.push(self.line);
1886 self.chunk.columns.push(self.column);
1887 self.chunk.lines.push(self.line);
1888 self.chunk.columns.push(self.column);
1889 }
1890
1891 Node::StructConstruct {
1892 struct_name,
1893 fields,
1894 } => {
1895 let struct_key = self
1897 .chunk
1898 .add_constant(Constant::String("__struct__".to_string()));
1899 let struct_val = self
1900 .chunk
1901 .add_constant(Constant::String(struct_name.clone()));
1902 self.chunk.emit_u16(Op::Constant, struct_key, self.line);
1903 self.chunk.emit_u16(Op::Constant, struct_val, self.line);
1904
1905 for entry in fields {
1906 self.compile_node(&entry.key)?;
1907 self.compile_node(&entry.value)?;
1908 }
1909 self.chunk
1910 .emit_u16(Op::BuildDict, (fields.len() + 1) as u16, self.line);
1911 }
1912
1913 Node::ImportDecl { path } => {
1914 let idx = self.chunk.add_constant(Constant::String(path.clone()));
1915 self.chunk.emit_u16(Op::Import, idx, self.line);
1916 }
1917
1918 Node::SelectiveImport { names, path } => {
1919 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
1920 let names_str = names.join(",");
1921 let names_idx = self.chunk.add_constant(Constant::String(names_str));
1922 self.chunk
1923 .emit_u16(Op::SelectiveImport, path_idx, self.line);
1924 let hi = (names_idx >> 8) as u8;
1925 let lo = names_idx as u8;
1926 self.chunk.code.push(hi);
1927 self.chunk.code.push(lo);
1928 self.chunk.lines.push(self.line);
1929 self.chunk.columns.push(self.column);
1930 self.chunk.lines.push(self.line);
1931 self.chunk.columns.push(self.column);
1932 }
1933
1934 Node::TryOperator { operand } => {
1935 self.compile_node(operand)?;
1936 self.chunk.emit(Op::TryUnwrap, self.line);
1937 }
1938
1939 Node::ImplBlock { type_name, methods } => {
1940 for method_sn in methods {
1943 if let Node::FnDecl {
1944 name, params, body, ..
1945 } = &method_sn.node
1946 {
1947 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
1949 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1950
1951 let mut fn_compiler = Compiler::new();
1953 fn_compiler.enum_names = self.enum_names.clone();
1954 fn_compiler.emit_default_preamble(params)?;
1955 fn_compiler.emit_type_checks(params);
1956 fn_compiler.compile_block(body)?;
1957 fn_compiler.chunk.emit(Op::Nil, self.line);
1958 fn_compiler.chunk.emit(Op::Return, self.line);
1959
1960 let func = CompiledFunction {
1961 name: format!("{}.{}", type_name, name),
1962 params: TypedParam::names(params),
1963 default_start: TypedParam::default_start(params),
1964 chunk: fn_compiler.chunk,
1965 is_generator: false,
1966 };
1967 let fn_idx = self.chunk.functions.len();
1968 self.chunk.functions.push(func);
1969 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1970 }
1971 }
1972 let method_count = methods
1973 .iter()
1974 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
1975 .count();
1976 self.chunk
1977 .emit_u16(Op::BuildDict, method_count as u16, self.line);
1978 let impl_name = format!("__impl_{}", type_name);
1979 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
1980 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1981 }
1982
1983 Node::StructDecl { name, .. } => {
1984 let mut fn_compiler = Compiler::new();
1986 fn_compiler.enum_names = self.enum_names.clone();
1987 let params = vec![TypedParam::untyped("__fields")];
1988 fn_compiler.emit_default_preamble(¶ms)?;
1989
1990 let make_idx = fn_compiler
1992 .chunk
1993 .add_constant(Constant::String("__make_struct".into()));
1994 fn_compiler
1995 .chunk
1996 .emit_u16(Op::Constant, make_idx, self.line);
1997 let sname_idx = fn_compiler
1998 .chunk
1999 .add_constant(Constant::String(name.clone()));
2000 fn_compiler
2001 .chunk
2002 .emit_u16(Op::Constant, sname_idx, self.line);
2003 let fields_idx = fn_compiler
2004 .chunk
2005 .add_constant(Constant::String("__fields".into()));
2006 fn_compiler
2007 .chunk
2008 .emit_u16(Op::GetVar, fields_idx, self.line);
2009 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
2010 fn_compiler.chunk.emit(Op::Return, self.line);
2011
2012 let func = CompiledFunction {
2013 name: name.clone(),
2014 params: TypedParam::names(¶ms),
2015 default_start: None,
2016 chunk: fn_compiler.chunk,
2017 is_generator: false,
2018 };
2019 let fn_idx = self.chunk.functions.len();
2020 self.chunk.functions.push(func);
2021 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2022 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
2023 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2024 }
2025
2026 Node::Pipeline { .. }
2028 | Node::OverrideDecl { .. }
2029 | Node::TypeDecl { .. }
2030 | Node::EnumDecl { .. }
2031 | Node::InterfaceDecl { .. } => {
2032 self.chunk.emit(Op::Nil, self.line);
2033 }
2034
2035 Node::TryCatch {
2036 body,
2037 error_var,
2038 error_type,
2039 catch_body,
2040 finally_body,
2041 } => {
2042 let type_name = error_type.as_ref().and_then(|te| {
2044 if let harn_parser::TypeExpr::Named(name) = te {
2045 Some(name.clone())
2046 } else {
2047 None
2048 }
2049 });
2050
2051 let type_name_idx = if let Some(ref tn) = type_name {
2052 self.chunk.add_constant(Constant::String(tn.clone()))
2053 } else {
2054 self.chunk.add_constant(Constant::String(String::new()))
2055 };
2056
2057 let has_catch = !catch_body.is_empty() || error_var.is_some();
2058 let has_finally = finally_body.is_some();
2059
2060 if has_catch && has_finally {
2061 let finally_body = finally_body.as_ref().unwrap();
2063
2064 self.finally_bodies.push(finally_body.clone());
2066
2067 self.handler_depth += 1;
2069 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2070 self.emit_type_name_extra(type_name_idx);
2071
2072 self.compile_try_body(body)?;
2074
2075 self.handler_depth -= 1;
2077 self.chunk.emit(Op::PopHandler, self.line);
2078 self.compile_finally_inline(finally_body)?;
2079 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2080
2081 self.chunk.patch_jump(catch_jump);
2083 self.begin_scope();
2084 self.compile_catch_binding(error_var)?;
2085
2086 self.handler_depth += 1;
2088 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2089 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2090 self.emit_type_name_extra(empty_type);
2091
2092 self.compile_try_body(catch_body)?;
2094
2095 self.handler_depth -= 1;
2097 self.chunk.emit(Op::PopHandler, self.line);
2098 self.compile_finally_inline(finally_body)?;
2099 self.end_scope();
2100 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
2101
2102 self.chunk.patch_jump(rethrow_jump);
2104 self.compile_rethrow_with_finally(finally_body)?;
2105 self.end_scope();
2106
2107 self.chunk.patch_jump(end_jump);
2108 self.chunk.patch_jump(end_jump2);
2109
2110 self.finally_bodies.pop();
2111 } else if has_finally {
2112 let finally_body = finally_body.as_ref().unwrap();
2114
2115 self.finally_bodies.push(finally_body.clone());
2116
2117 self.handler_depth += 1;
2119 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2120 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2121 self.emit_type_name_extra(empty_type);
2122
2123 self.compile_try_body(body)?;
2125
2126 self.handler_depth -= 1;
2128 self.chunk.emit(Op::PopHandler, self.line);
2129 self.compile_finally_inline(finally_body)?;
2130 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2131
2132 self.chunk.patch_jump(error_jump);
2134 self.compile_rethrow_with_finally(finally_body)?;
2135
2136 self.chunk.patch_jump(end_jump);
2137
2138 self.finally_bodies.pop();
2139 } else {
2140 self.handler_depth += 1;
2144 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2145 self.emit_type_name_extra(type_name_idx);
2146
2147 self.compile_try_body(body)?;
2149
2150 self.handler_depth -= 1;
2152 self.chunk.emit(Op::PopHandler, self.line);
2153 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2154
2155 self.chunk.patch_jump(catch_jump);
2157 self.begin_scope();
2158 self.compile_catch_binding(error_var)?;
2159
2160 self.compile_try_body(catch_body)?;
2162 self.end_scope();
2163
2164 self.chunk.patch_jump(end_jump);
2166 }
2167 }
2168
2169 Node::TryExpr { body } => {
2170 self.handler_depth += 1;
2174 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2175 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2176 self.emit_type_name_extra(empty_type);
2177
2178 self.compile_try_body(body)?;
2180
2181 self.handler_depth -= 1;
2183 self.chunk.emit(Op::PopHandler, self.line);
2184
2185 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
2187 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
2188 self.chunk.emit(Op::Swap, self.line);
2189 self.chunk.emit_u8(Op::Call, 1, self.line);
2190
2191 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2193
2194 self.chunk.patch_jump(catch_jump);
2196
2197 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
2199 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
2200 self.chunk.emit(Op::Swap, self.line);
2201 self.chunk.emit_u8(Op::Call, 1, self.line);
2202
2203 self.chunk.patch_jump(end_jump);
2205 }
2206
2207 Node::Retry { count, body } => {
2208 self.compile_node(count)?;
2210 let counter_name = "__retry_counter__";
2211 let counter_idx = self
2212 .chunk
2213 .add_constant(Constant::String(counter_name.to_string()));
2214 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
2215
2216 self.chunk.emit(Op::Nil, self.line);
2218 let err_name = "__retry_last_error__";
2219 let err_idx = self
2220 .chunk
2221 .add_constant(Constant::String(err_name.to_string()));
2222 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
2223
2224 let loop_start = self.chunk.current_offset();
2226
2227 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2229 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2231 let hi = (empty_type >> 8) as u8;
2232 let lo = empty_type as u8;
2233 self.chunk.code.push(hi);
2234 self.chunk.code.push(lo);
2235 self.chunk.lines.push(self.line);
2236 self.chunk.columns.push(self.column);
2237 self.chunk.lines.push(self.line);
2238 self.chunk.columns.push(self.column);
2239
2240 self.compile_block(body)?;
2242
2243 self.chunk.emit(Op::PopHandler, self.line);
2245 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2246
2247 self.chunk.patch_jump(catch_jump);
2249 self.chunk.emit(Op::Dup, self.line);
2251 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
2252 self.chunk.emit(Op::Pop, self.line);
2254
2255 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
2257 let one_idx = self.chunk.add_constant(Constant::Int(1));
2258 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
2259 self.chunk.emit(Op::Sub, self.line);
2260 self.chunk.emit(Op::Dup, self.line);
2261 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
2262
2263 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2265 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2266 self.chunk.emit(Op::Greater, self.line);
2267 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2268 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
2270
2271 self.chunk.patch_jump(retry_jump);
2273 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
2275 self.chunk.emit(Op::Throw, self.line);
2276
2277 self.chunk.patch_jump(end_jump);
2278 self.chunk.emit(Op::Nil, self.line);
2280 }
2281
2282 Node::Parallel {
2283 count,
2284 variable,
2285 body,
2286 } => {
2287 self.compile_node(count)?;
2288 let mut fn_compiler = Compiler::new();
2289 fn_compiler.enum_names = self.enum_names.clone();
2290 fn_compiler.compile_block(body)?;
2291 fn_compiler.chunk.emit(Op::Return, self.line);
2292 let params = vec![variable.clone().unwrap_or_else(|| "__i__".to_string())];
2293 let func = CompiledFunction {
2294 name: "<parallel>".to_string(),
2295 params,
2296 default_start: None,
2297 chunk: fn_compiler.chunk,
2298 is_generator: false,
2299 };
2300 let fn_idx = self.chunk.functions.len();
2301 self.chunk.functions.push(func);
2302 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2303 self.chunk.emit(Op::Parallel, self.line);
2304 }
2305
2306 Node::ParallelMap {
2307 list,
2308 variable,
2309 body,
2310 } => {
2311 self.compile_node(list)?;
2312 let mut fn_compiler = Compiler::new();
2313 fn_compiler.enum_names = self.enum_names.clone();
2314 fn_compiler.compile_block(body)?;
2315 fn_compiler.chunk.emit(Op::Return, self.line);
2316 let func = CompiledFunction {
2317 name: "<parallel_map>".to_string(),
2318 params: vec![variable.clone()],
2319 default_start: None,
2320 chunk: fn_compiler.chunk,
2321 is_generator: false,
2322 };
2323 let fn_idx = self.chunk.functions.len();
2324 self.chunk.functions.push(func);
2325 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2326 self.chunk.emit(Op::ParallelMap, self.line);
2327 }
2328
2329 Node::ParallelSettle {
2330 list,
2331 variable,
2332 body,
2333 } => {
2334 self.compile_node(list)?;
2335 let mut fn_compiler = Compiler::new();
2336 fn_compiler.enum_names = self.enum_names.clone();
2337 fn_compiler.compile_block(body)?;
2338 fn_compiler.chunk.emit(Op::Return, self.line);
2339 let func = CompiledFunction {
2340 name: "<parallel_settle>".to_string(),
2341 params: vec![variable.clone()],
2342 default_start: None,
2343 chunk: fn_compiler.chunk,
2344 is_generator: false,
2345 };
2346 let fn_idx = self.chunk.functions.len();
2347 self.chunk.functions.push(func);
2348 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2349 self.chunk.emit(Op::ParallelSettle, self.line);
2350 }
2351
2352 Node::SpawnExpr { body } => {
2353 let mut fn_compiler = Compiler::new();
2354 fn_compiler.enum_names = self.enum_names.clone();
2355 fn_compiler.compile_block(body)?;
2356 fn_compiler.chunk.emit(Op::Return, self.line);
2357 let func = CompiledFunction {
2358 name: "<spawn>".to_string(),
2359 params: vec![],
2360 default_start: None,
2361 chunk: fn_compiler.chunk,
2362 is_generator: false,
2363 };
2364 let fn_idx = self.chunk.functions.len();
2365 self.chunk.functions.push(func);
2366 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2367 self.chunk.emit(Op::Spawn, self.line);
2368 }
2369 Node::SelectExpr {
2370 cases,
2371 timeout,
2372 default_body,
2373 } => {
2374 let builtin_name = if timeout.is_some() {
2381 "__select_timeout"
2382 } else if default_body.is_some() {
2383 "__select_try"
2384 } else {
2385 "__select_list"
2386 };
2387
2388 let name_idx = self
2390 .chunk
2391 .add_constant(Constant::String(builtin_name.into()));
2392 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2393
2394 for case in cases {
2396 self.compile_node(&case.channel)?;
2397 }
2398 self.chunk
2399 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2400
2401 if let Some((duration_expr, _)) = timeout {
2403 self.compile_node(duration_expr)?;
2404 self.chunk.emit_u8(Op::Call, 2, self.line);
2405 } else {
2406 self.chunk.emit_u8(Op::Call, 1, self.line);
2407 }
2408
2409 self.temp_counter += 1;
2411 let result_name = format!("__sel_result_{}__", self.temp_counter);
2412 let result_idx = self
2413 .chunk
2414 .add_constant(Constant::String(result_name.clone()));
2415 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2416
2417 let mut end_jumps = Vec::new();
2419
2420 for (i, case) in cases.iter().enumerate() {
2421 let get_r = self
2422 .chunk
2423 .add_constant(Constant::String(result_name.clone()));
2424 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2425 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2426 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2427 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2428 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2429 self.chunk.emit(Op::Equal, self.line);
2430 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2431 self.chunk.emit(Op::Pop, self.line);
2432 self.begin_scope();
2433
2434 let get_r2 = self
2436 .chunk
2437 .add_constant(Constant::String(result_name.clone()));
2438 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2439 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2440 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2441 let var_idx = self
2442 .chunk
2443 .add_constant(Constant::String(case.variable.clone()));
2444 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2445
2446 self.compile_try_body(&case.body)?;
2447 self.end_scope();
2448 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2449 self.chunk.patch_jump(skip);
2450 self.chunk.emit(Op::Pop, self.line);
2451 }
2452
2453 if let Some((_, ref timeout_body)) = timeout {
2455 self.compile_try_body(timeout_body)?;
2456 } else if let Some(ref def_body) = default_body {
2457 self.compile_try_body(def_body)?;
2458 } else {
2459 self.chunk.emit(Op::Nil, self.line);
2460 }
2461
2462 for ej in end_jumps {
2463 self.chunk.patch_jump(ej);
2464 }
2465 }
2466 Node::Spread(_) => {
2467 return Err(CompileError {
2468 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2469 line: self.line,
2470 });
2471 }
2472 }
2473 Ok(())
2474 }
2475
2476 fn compile_destructuring(
2480 &mut self,
2481 pattern: &BindingPattern,
2482 is_mutable: bool,
2483 ) -> Result<(), CompileError> {
2484 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2485 match pattern {
2486 BindingPattern::Identifier(name) => {
2487 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2489 self.chunk.emit_u16(def_op, idx, self.line);
2490 }
2491 BindingPattern::Dict(fields) => {
2492 self.chunk.emit(Op::Dup, self.line);
2495 let assert_idx = self
2496 .chunk
2497 .add_constant(Constant::String("__assert_dict".into()));
2498 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2499 self.chunk.emit(Op::Swap, self.line);
2500 self.chunk.emit_u8(Op::Call, 1, self.line);
2501 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2506 let rest_field = fields.iter().find(|f| f.is_rest);
2507
2508 for field in &non_rest {
2509 self.chunk.emit(Op::Dup, self.line);
2510 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2511 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2512 self.chunk.emit(Op::Subscript, self.line);
2513 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2514 let name_idx = self
2515 .chunk
2516 .add_constant(Constant::String(binding_name.to_string()));
2517 self.chunk.emit_u16(def_op, name_idx, self.line);
2518 }
2519
2520 if let Some(rest) = rest_field {
2521 let fn_idx = self
2524 .chunk
2525 .add_constant(Constant::String("__dict_rest".into()));
2526 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2527 self.chunk.emit(Op::Swap, self.line);
2529 for field in &non_rest {
2531 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2532 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2533 }
2534 self.chunk
2535 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2536 self.chunk.emit_u8(Op::Call, 2, self.line);
2538 let rest_name = &rest.key;
2539 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2540 self.chunk.emit_u16(def_op, rest_idx, self.line);
2541 } else {
2542 self.chunk.emit(Op::Pop, self.line);
2544 }
2545 }
2546 BindingPattern::List(elements) => {
2547 self.chunk.emit(Op::Dup, self.line);
2550 let assert_idx = self
2551 .chunk
2552 .add_constant(Constant::String("__assert_list".into()));
2553 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2554 self.chunk.emit(Op::Swap, self.line);
2555 self.chunk.emit_u8(Op::Call, 1, self.line);
2556 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2559 let rest_elem = elements.iter().find(|e| e.is_rest);
2560
2561 for (i, elem) in non_rest.iter().enumerate() {
2562 self.chunk.emit(Op::Dup, self.line);
2563 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2564 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2565 self.chunk.emit(Op::Subscript, self.line);
2566 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2567 self.chunk.emit_u16(def_op, name_idx, self.line);
2568 }
2569
2570 if let Some(rest) = rest_elem {
2571 let start_idx = self
2575 .chunk
2576 .add_constant(Constant::Int(non_rest.len() as i64));
2577 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2578 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2580 let rest_name_idx =
2581 self.chunk.add_constant(Constant::String(rest.name.clone()));
2582 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2583 } else {
2584 self.chunk.emit(Op::Pop, self.line);
2586 }
2587 }
2588 }
2589 Ok(())
2590 }
2591
2592 fn produces_value(node: &Node) -> bool {
2594 match node {
2595 Node::LetBinding { .. }
2597 | Node::VarBinding { .. }
2598 | Node::Assignment { .. }
2599 | Node::ReturnStmt { .. }
2600 | Node::FnDecl { .. }
2601 | Node::ToolDecl { .. }
2602 | Node::ImplBlock { .. }
2603 | Node::StructDecl { .. }
2604 | Node::EnumDecl { .. }
2605 | Node::InterfaceDecl { .. }
2606 | Node::TypeDecl { .. }
2607 | Node::ThrowStmt { .. }
2608 | Node::BreakStmt
2609 | Node::ContinueStmt
2610 | Node::RequireStmt { .. } => false,
2611 Node::TryCatch { .. }
2613 | Node::TryExpr { .. }
2614 | Node::Retry { .. }
2615 | Node::GuardStmt { .. }
2616 | Node::DeadlineBlock { .. }
2617 | Node::MutexBlock { .. }
2618 | Node::Spread(_) => true,
2619 _ => true,
2621 }
2622 }
2623}
2624
2625impl Compiler {
2626 pub fn compile_fn_body(
2639 &mut self,
2640 params: &[TypedParam],
2641 body: &[SNode],
2642 source_file: Option<String>,
2643 ) -> Result<CompiledFunction, CompileError> {
2644 let mut fn_compiler = Compiler::new();
2645 fn_compiler.enum_names = self.enum_names.clone();
2646 fn_compiler.emit_default_preamble(params)?;
2647 fn_compiler.emit_type_checks(params);
2648 let is_gen = body_contains_yield(body);
2649 fn_compiler.compile_block(body)?;
2650 fn_compiler.chunk.emit(Op::Nil, 0);
2651 fn_compiler.chunk.emit(Op::Return, 0);
2652 fn_compiler.chunk.source_file = source_file;
2653 Ok(CompiledFunction {
2654 name: String::new(),
2655 params: TypedParam::names(params),
2656 default_start: TypedParam::default_start(params),
2657 chunk: fn_compiler.chunk,
2658 is_generator: is_gen,
2659 })
2660 }
2661
2662 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2664 self.begin_scope();
2665 if body.is_empty() {
2666 self.chunk.emit(Op::Nil, self.line);
2667 } else {
2668 self.compile_block(body)?;
2669 if !Self::produces_value(&body.last().unwrap().node) {
2670 self.chunk.emit(Op::Nil, self.line);
2671 }
2672 }
2673 self.end_scope();
2674 Ok(())
2675 }
2676
2677 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2679 match op {
2680 "+" => self.chunk.emit(Op::Add, self.line),
2681 "-" => self.chunk.emit(Op::Sub, self.line),
2682 "*" => self.chunk.emit(Op::Mul, self.line),
2683 "/" => self.chunk.emit(Op::Div, self.line),
2684 "%" => self.chunk.emit(Op::Mod, self.line),
2685 _ => {
2686 return Err(CompileError {
2687 message: format!("Unknown compound operator: {op}"),
2688 line: self.line,
2689 })
2690 }
2691 }
2692 Ok(())
2693 }
2694
2695 fn root_var_name(&self, node: &SNode) -> Option<String> {
2697 match &node.node {
2698 Node::Identifier(name) => Some(name.clone()),
2699 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2700 self.root_var_name(object)
2701 }
2702 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2703 _ => None,
2704 }
2705 }
2706
2707 fn compile_top_level_declarations(&mut self, program: &[SNode]) -> Result<(), CompileError> {
2708 for sn in program {
2709 if matches!(
2710 &sn.node,
2711 Node::FnDecl { .. }
2712 | Node::ToolDecl { .. }
2713 | Node::ImplBlock { .. }
2714 | Node::StructDecl { .. }
2715 | Node::EnumDecl { .. }
2716 | Node::InterfaceDecl { .. }
2717 | Node::TypeDecl { .. }
2718 ) {
2719 self.compile_node(sn)?;
2720 }
2721 }
2722 Ok(())
2723 }
2724}
2725
2726impl Compiler {
2727 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
2729 for sn in nodes {
2730 match &sn.node {
2731 Node::EnumDecl { name, .. } => {
2732 names.insert(name.clone());
2733 }
2734 Node::Pipeline { body, .. } => {
2735 Self::collect_enum_names(body, names);
2736 }
2737 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
2738 Self::collect_enum_names(body, names);
2739 }
2740 Node::Block(stmts) => {
2741 Self::collect_enum_names(stmts, names);
2742 }
2743 _ => {}
2744 }
2745 }
2746 }
2747
2748 fn collect_interface_methods(
2749 nodes: &[SNode],
2750 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
2751 ) {
2752 for sn in nodes {
2753 match &sn.node {
2754 Node::InterfaceDecl { name, methods, .. } => {
2755 let method_names: Vec<String> =
2756 methods.iter().map(|m| m.name.clone()).collect();
2757 interfaces.insert(name.clone(), method_names);
2758 }
2759 Node::Pipeline { body, .. }
2760 | Node::FnDecl { body, .. }
2761 | Node::ToolDecl { body, .. } => {
2762 Self::collect_interface_methods(body, interfaces);
2763 }
2764 Node::Block(stmts) => {
2765 Self::collect_interface_methods(stmts, interfaces);
2766 }
2767 _ => {}
2768 }
2769 }
2770 }
2771}
2772
2773impl Default for Compiler {
2774 fn default() -> Self {
2775 Self::new()
2776 }
2777}
2778
2779fn body_contains_yield(nodes: &[SNode]) -> bool {
2781 nodes.iter().any(|sn| node_contains_yield(&sn.node))
2782}
2783
2784fn node_contains_yield(node: &Node) -> bool {
2785 match node {
2786 Node::YieldExpr { .. } => true,
2787 Node::FnDecl { .. } | Node::Closure { .. } => false,
2790 Node::Block(stmts) => body_contains_yield(stmts),
2791 Node::IfElse {
2792 condition,
2793 then_body,
2794 else_body,
2795 } => {
2796 node_contains_yield(&condition.node)
2797 || body_contains_yield(then_body)
2798 || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
2799 }
2800 Node::WhileLoop { condition, body } => {
2801 node_contains_yield(&condition.node) || body_contains_yield(body)
2802 }
2803 Node::ForIn { iterable, body, .. } => {
2804 node_contains_yield(&iterable.node) || body_contains_yield(body)
2805 }
2806 Node::TryCatch {
2807 body, catch_body, ..
2808 } => body_contains_yield(body) || body_contains_yield(catch_body),
2809 Node::TryExpr { body } => body_contains_yield(body),
2810 _ => false,
2811 }
2812}
2813
2814fn contains_pipe_placeholder(node: &SNode) -> bool {
2816 match &node.node {
2817 Node::Identifier(name) if name == "_" => true,
2818 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
2819 Node::MethodCall { object, args, .. } => {
2820 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
2821 }
2822 Node::BinaryOp { left, right, .. } => {
2823 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
2824 }
2825 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
2826 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
2827 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
2828 Node::SubscriptAccess { object, index } => {
2829 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
2830 }
2831 _ => false,
2832 }
2833}
2834
2835fn replace_pipe_placeholder(node: &SNode) -> SNode {
2837 let new_node = match &node.node {
2838 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
2839 Node::FunctionCall { name, args } => Node::FunctionCall {
2840 name: name.clone(),
2841 args: args.iter().map(replace_pipe_placeholder).collect(),
2842 },
2843 Node::MethodCall {
2844 object,
2845 method,
2846 args,
2847 } => Node::MethodCall {
2848 object: Box::new(replace_pipe_placeholder(object)),
2849 method: method.clone(),
2850 args: args.iter().map(replace_pipe_placeholder).collect(),
2851 },
2852 Node::BinaryOp { op, left, right } => Node::BinaryOp {
2853 op: op.clone(),
2854 left: Box::new(replace_pipe_placeholder(left)),
2855 right: Box::new(replace_pipe_placeholder(right)),
2856 },
2857 Node::UnaryOp { op, operand } => Node::UnaryOp {
2858 op: op.clone(),
2859 operand: Box::new(replace_pipe_placeholder(operand)),
2860 },
2861 Node::ListLiteral(items) => {
2862 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
2863 }
2864 Node::PropertyAccess { object, property } => Node::PropertyAccess {
2865 object: Box::new(replace_pipe_placeholder(object)),
2866 property: property.clone(),
2867 },
2868 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
2869 object: Box::new(replace_pipe_placeholder(object)),
2870 index: Box::new(replace_pipe_placeholder(index)),
2871 },
2872 _ => return node.clone(),
2873 };
2874 SNode::new(new_node, node.span)
2875}
2876
2877#[cfg(test)]
2878mod tests {
2879 use super::*;
2880 use harn_lexer::Lexer;
2881 use harn_parser::Parser;
2882
2883 fn compile_source(source: &str) -> Chunk {
2884 let mut lexer = Lexer::new(source);
2885 let tokens = lexer.tokenize().unwrap();
2886 let mut parser = Parser::new(tokens);
2887 let program = parser.parse().unwrap();
2888 Compiler::new().compile(&program).unwrap()
2889 }
2890
2891 #[test]
2892 fn test_compile_arithmetic() {
2893 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
2894 assert!(!chunk.code.is_empty());
2895 assert!(chunk.constants.contains(&Constant::Int(2)));
2897 assert!(chunk.constants.contains(&Constant::Int(3)));
2898 }
2899
2900 #[test]
2901 fn test_compile_function_call() {
2902 let chunk = compile_source("pipeline test(task) { log(42) }");
2903 let disasm = chunk.disassemble("test");
2904 assert!(disasm.contains("CALL"));
2905 }
2906
2907 #[test]
2908 fn test_compile_if_else() {
2909 let chunk =
2910 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
2911 let disasm = chunk.disassemble("test");
2912 assert!(disasm.contains("JUMP_IF_FALSE"));
2913 assert!(disasm.contains("JUMP"));
2914 }
2915
2916 #[test]
2917 fn test_compile_while() {
2918 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
2919 let disasm = chunk.disassemble("test");
2920 assert!(disasm.contains("JUMP_IF_FALSE"));
2921 assert!(disasm.contains("JUMP"));
2923 }
2924
2925 #[test]
2926 fn test_compile_closure() {
2927 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
2928 assert!(!chunk.functions.is_empty());
2929 assert_eq!(chunk.functions[0].params, vec!["x"]);
2930 }
2931
2932 #[test]
2933 fn test_compile_list() {
2934 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
2935 let disasm = chunk.disassemble("test");
2936 assert!(disasm.contains("BUILD_LIST"));
2937 }
2938
2939 #[test]
2940 fn test_compile_dict() {
2941 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
2942 let disasm = chunk.disassemble("test");
2943 assert!(disasm.contains("BUILD_DICT"));
2944 }
2945
2946 #[test]
2947 fn test_disassemble() {
2948 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
2949 let disasm = chunk.disassemble("test");
2950 assert!(disasm.contains("CONSTANT"));
2952 assert!(disasm.contains("ADD"));
2953 assert!(disasm.contains("CALL"));
2954 }
2955}