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) | Node::RawStringLiteral(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 rest: false,
621 }],
622 body: vec![replaced],
623 fn_syntax: false,
624 });
625 self.compile_node(&closure_node)?;
626 } else {
627 self.compile_node(right)?;
628 }
629 self.chunk.emit(Op::Pipe, self.line);
630 return Ok(());
631 }
632 _ => {}
633 }
634
635 self.compile_node(left)?;
636 self.compile_node(right)?;
637 match op.as_str() {
638 "+" => self.chunk.emit(Op::Add, self.line),
639 "-" => self.chunk.emit(Op::Sub, self.line),
640 "*" => self.chunk.emit(Op::Mul, self.line),
641 "/" => self.chunk.emit(Op::Div, self.line),
642 "%" => self.chunk.emit(Op::Mod, self.line),
643 "==" => self.chunk.emit(Op::Equal, self.line),
644 "!=" => self.chunk.emit(Op::NotEqual, self.line),
645 "<" => self.chunk.emit(Op::Less, self.line),
646 ">" => self.chunk.emit(Op::Greater, self.line),
647 "<=" => self.chunk.emit(Op::LessEqual, self.line),
648 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
649 "in" => self.chunk.emit(Op::Contains, self.line),
650 "not_in" => {
651 self.chunk.emit(Op::Contains, self.line);
652 self.chunk.emit(Op::Not, self.line);
653 }
654 _ => {
655 return Err(CompileError {
656 message: format!("Unknown operator: {op}"),
657 line: self.line,
658 })
659 }
660 }
661 }
662
663 Node::UnaryOp { op, operand } => {
664 self.compile_node(operand)?;
665 match op.as_str() {
666 "-" => self.chunk.emit(Op::Negate, self.line),
667 "!" => self.chunk.emit(Op::Not, self.line),
668 _ => {}
669 }
670 }
671
672 Node::Ternary {
673 condition,
674 true_expr,
675 false_expr,
676 } => {
677 self.compile_node(condition)?;
678 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
679 self.chunk.emit(Op::Pop, self.line);
680 self.compile_node(true_expr)?;
681 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
682 self.chunk.patch_jump(else_jump);
683 self.chunk.emit(Op::Pop, self.line);
684 self.compile_node(false_expr)?;
685 self.chunk.patch_jump(end_jump);
686 }
687
688 Node::FunctionCall { name, args } => {
689 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
690 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
692 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
693
694 if has_spread {
695 self.chunk.emit_u16(Op::BuildList, 0, self.line);
698 let mut pending = 0u16;
699 for arg in args {
700 if let Node::Spread(inner) = &arg.node {
701 if pending > 0 {
702 self.chunk.emit_u16(Op::BuildList, pending, self.line);
703 self.chunk.emit(Op::Add, self.line);
704 pending = 0;
705 }
706 self.compile_node(inner)?;
707 self.chunk.emit(Op::Dup, self.line);
708 let assert_idx = self
709 .chunk
710 .add_constant(Constant::String("__assert_list".into()));
711 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
712 self.chunk.emit(Op::Swap, self.line);
713 self.chunk.emit_u8(Op::Call, 1, self.line);
714 self.chunk.emit(Op::Pop, self.line);
715 self.chunk.emit(Op::Add, self.line);
716 } else {
717 self.compile_node(arg)?;
718 pending += 1;
719 }
720 }
721 if pending > 0 {
722 self.chunk.emit_u16(Op::BuildList, pending, self.line);
723 self.chunk.emit(Op::Add, self.line);
724 }
725 self.chunk.emit(Op::CallSpread, self.line);
726 } else {
727 for arg in args {
729 self.compile_node(arg)?;
730 }
731 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
732 }
733 }
734
735 Node::MethodCall {
736 object,
737 method,
738 args,
739 } => {
740 if let Node::Identifier(name) = &object.node {
742 if self.enum_names.contains(name) {
743 for arg in args {
745 self.compile_node(arg)?;
746 }
747 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
748 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
749 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
750 let hi = (var_idx >> 8) as u8;
751 let lo = var_idx as u8;
752 self.chunk.code.push(hi);
753 self.chunk.code.push(lo);
754 self.chunk.lines.push(self.line);
755 self.chunk.columns.push(self.column);
756 self.chunk.lines.push(self.line);
757 self.chunk.columns.push(self.column);
758 let fc = args.len() as u16;
759 let fhi = (fc >> 8) as u8;
760 let flo = fc as u8;
761 self.chunk.code.push(fhi);
762 self.chunk.code.push(flo);
763 self.chunk.lines.push(self.line);
764 self.chunk.columns.push(self.column);
765 self.chunk.lines.push(self.line);
766 self.chunk.columns.push(self.column);
767 return Ok(());
768 }
769 }
770 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
771 self.compile_node(object)?;
772 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
773 if has_spread {
774 self.chunk.emit_u16(Op::BuildList, 0, self.line);
776 let mut pending = 0u16;
777 for arg in args {
778 if let Node::Spread(inner) = &arg.node {
779 if pending > 0 {
780 self.chunk.emit_u16(Op::BuildList, pending, self.line);
781 self.chunk.emit(Op::Add, self.line);
782 pending = 0;
783 }
784 self.compile_node(inner)?;
785 self.chunk.emit(Op::Dup, self.line);
786 let assert_idx = self
787 .chunk
788 .add_constant(Constant::String("__assert_list".into()));
789 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
790 self.chunk.emit(Op::Swap, self.line);
791 self.chunk.emit_u8(Op::Call, 1, self.line);
792 self.chunk.emit(Op::Pop, self.line);
793 self.chunk.emit(Op::Add, self.line);
794 } else {
795 self.compile_node(arg)?;
796 pending += 1;
797 }
798 }
799 if pending > 0 {
800 self.chunk.emit_u16(Op::BuildList, pending, self.line);
801 self.chunk.emit(Op::Add, self.line);
802 }
803 self.chunk
804 .emit_u16(Op::MethodCallSpread, name_idx, self.line);
805 } else {
806 for arg in args {
807 self.compile_node(arg)?;
808 }
809 self.chunk
810 .emit_method_call(name_idx, args.len() as u8, self.line);
811 }
812 }
813
814 Node::OptionalMethodCall {
815 object,
816 method,
817 args,
818 } => {
819 self.compile_node(object)?;
820 for arg in args {
821 self.compile_node(arg)?;
822 }
823 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
824 self.chunk
825 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
826 }
827
828 Node::PropertyAccess { object, property } => {
829 if let Node::Identifier(name) = &object.node {
831 if self.enum_names.contains(name) {
832 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
834 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
835 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
836 let hi = (var_idx >> 8) as u8;
837 let lo = var_idx as u8;
838 self.chunk.code.push(hi);
839 self.chunk.code.push(lo);
840 self.chunk.lines.push(self.line);
841 self.chunk.columns.push(self.column);
842 self.chunk.lines.push(self.line);
843 self.chunk.columns.push(self.column);
844 self.chunk.code.push(0);
846 self.chunk.code.push(0);
847 self.chunk.lines.push(self.line);
848 self.chunk.columns.push(self.column);
849 self.chunk.lines.push(self.line);
850 self.chunk.columns.push(self.column);
851 return Ok(());
852 }
853 }
854 self.compile_node(object)?;
855 let idx = self.chunk.add_constant(Constant::String(property.clone()));
856 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
857 }
858
859 Node::OptionalPropertyAccess { object, property } => {
860 self.compile_node(object)?;
861 let idx = self.chunk.add_constant(Constant::String(property.clone()));
862 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
863 }
864
865 Node::SubscriptAccess { object, index } => {
866 self.compile_node(object)?;
867 self.compile_node(index)?;
868 self.chunk.emit(Op::Subscript, self.line);
869 }
870
871 Node::SliceAccess { object, start, end } => {
872 self.compile_node(object)?;
873 if let Some(s) = start {
874 self.compile_node(s)?;
875 } else {
876 self.chunk.emit(Op::Nil, self.line);
877 }
878 if let Some(e) = end {
879 self.compile_node(e)?;
880 } else {
881 self.chunk.emit(Op::Nil, self.line);
882 }
883 self.chunk.emit(Op::Slice, self.line);
884 }
885
886 Node::IfElse {
887 condition,
888 then_body,
889 else_body,
890 } => {
891 self.compile_node(condition)?;
892 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
893 self.chunk.emit(Op::Pop, self.line);
894 self.compile_scoped_block(then_body)?;
895 if let Some(else_body) = else_body {
896 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
897 self.chunk.patch_jump(else_jump);
898 self.chunk.emit(Op::Pop, self.line);
899 self.compile_scoped_block(else_body)?;
900 self.chunk.patch_jump(end_jump);
901 } else {
902 self.chunk.patch_jump(else_jump);
903 self.chunk.emit(Op::Pop, self.line);
904 self.chunk.emit(Op::Nil, self.line);
905 }
906 }
907
908 Node::WhileLoop { condition, body } => {
909 let loop_start = self.chunk.current_offset();
910 self.loop_stack.push(LoopContext {
911 start_offset: loop_start,
912 break_patches: Vec::new(),
913 has_iterator: false,
914 handler_depth: self.handler_depth,
915 finally_depth: self.finally_bodies.len(),
916 scope_depth: self.scope_depth,
917 });
918 self.compile_node(condition)?;
919 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
920 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_statements(body)?;
922 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
924 self.chunk.patch_jump(exit_jump);
925 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
928 for patch_pos in ctx.break_patches {
929 self.chunk.patch_jump(patch_pos);
930 }
931 self.chunk.emit(Op::Nil, self.line);
932 }
933
934 Node::ForIn {
935 pattern,
936 iterable,
937 body,
938 } => {
939 self.compile_node(iterable)?;
941 self.chunk.emit(Op::IterInit, self.line);
943 let loop_start = self.chunk.current_offset();
944 self.loop_stack.push(LoopContext {
945 start_offset: loop_start,
946 break_patches: Vec::new(),
947 has_iterator: true,
948 handler_depth: self.handler_depth,
949 finally_depth: self.finally_bodies.len(),
950 scope_depth: self.scope_depth,
951 });
952 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
954 self.begin_scope();
955 self.compile_destructuring(pattern, true)?;
957 for sn in body {
958 self.compile_node(sn)?;
959 if Self::produces_value(&sn.node) {
960 self.chunk.emit(Op::Pop, self.line);
961 }
962 }
963 self.end_scope();
964 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
966 self.chunk.patch_jump(exit_jump_pos);
967 let ctx = self.loop_stack.pop().unwrap();
969 for patch_pos in ctx.break_patches {
970 self.chunk.patch_jump(patch_pos);
971 }
972 self.chunk.emit(Op::Nil, self.line);
974 }
975
976 Node::ReturnStmt { value } => {
977 let has_pending_finally = !self.finally_bodies.is_empty();
978
979 if has_pending_finally {
980 if let Some(val) = value {
983 self.compile_node(val)?;
984 } else {
985 self.chunk.emit(Op::Nil, self.line);
986 }
987 self.temp_counter += 1;
988 let temp_name = format!("__return_val_{}__", self.temp_counter);
989 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
990 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
991 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
993 for fb in &finallys {
994 self.compile_finally_inline(fb)?;
995 }
996 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
997 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
998 self.chunk.emit(Op::Return, self.line);
999 } else {
1000 if let Some(val) = value {
1002 if let Node::FunctionCall { name, args } = &val.node {
1003 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1004 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1005 for arg in args {
1006 self.compile_node(arg)?;
1007 }
1008 self.chunk
1009 .emit_u8(Op::TailCall, args.len() as u8, self.line);
1010 } else if let Node::BinaryOp { op, left, right } = &val.node {
1011 if op == "|>" {
1012 self.compile_node(left)?;
1013 self.compile_node(right)?;
1014 self.chunk.emit(Op::Swap, self.line);
1015 self.chunk.emit_u8(Op::TailCall, 1, self.line);
1016 } else {
1017 self.compile_node(val)?;
1018 }
1019 } else {
1020 self.compile_node(val)?;
1021 }
1022 } else {
1023 self.chunk.emit(Op::Nil, self.line);
1024 }
1025 self.chunk.emit(Op::Return, self.line);
1026 }
1027 }
1028
1029 Node::BreakStmt => {
1030 if self.loop_stack.is_empty() {
1031 return Err(CompileError {
1032 message: "break outside of loop".to_string(),
1033 line: self.line,
1034 });
1035 }
1036 let ctx = self.loop_stack.last().unwrap();
1038 let finally_depth = ctx.finally_depth;
1039 let handler_depth = ctx.handler_depth;
1040 let has_iterator = ctx.has_iterator;
1041 let scope_depth = ctx.scope_depth;
1042 for _ in handler_depth..self.handler_depth {
1044 self.chunk.emit(Op::PopHandler, self.line);
1045 }
1046 if self.finally_bodies.len() > finally_depth {
1048 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1049 .iter()
1050 .rev()
1051 .cloned()
1052 .collect();
1053 for fb in &finallys {
1054 self.compile_finally_inline(fb)?;
1055 }
1056 }
1057 self.unwind_scopes_to(scope_depth);
1058 if has_iterator {
1059 self.chunk.emit(Op::PopIterator, self.line);
1060 }
1061 let patch = self.chunk.emit_jump(Op::Jump, self.line);
1062 self.loop_stack
1063 .last_mut()
1064 .unwrap()
1065 .break_patches
1066 .push(patch);
1067 }
1068
1069 Node::ContinueStmt => {
1070 if self.loop_stack.is_empty() {
1071 return Err(CompileError {
1072 message: "continue outside of loop".to_string(),
1073 line: self.line,
1074 });
1075 }
1076 let ctx = self.loop_stack.last().unwrap();
1077 let finally_depth = ctx.finally_depth;
1078 let handler_depth = ctx.handler_depth;
1079 let loop_start = ctx.start_offset;
1080 let scope_depth = ctx.scope_depth;
1081 for _ in handler_depth..self.handler_depth {
1082 self.chunk.emit(Op::PopHandler, self.line);
1083 }
1084 if self.finally_bodies.len() > finally_depth {
1085 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1086 .iter()
1087 .rev()
1088 .cloned()
1089 .collect();
1090 for fb in &finallys {
1091 self.compile_finally_inline(fb)?;
1092 }
1093 }
1094 self.unwind_scopes_to(scope_depth);
1095 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1096 }
1097
1098 Node::ListLiteral(elements) => {
1099 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
1100 if !has_spread {
1101 for el in elements {
1102 self.compile_node(el)?;
1103 }
1104 self.chunk
1105 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
1106 } else {
1107 self.chunk.emit_u16(Op::BuildList, 0, self.line);
1110 let mut pending = 0u16;
1111 for el in elements {
1112 if let Node::Spread(inner) = &el.node {
1113 if pending > 0 {
1115 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1116 self.chunk.emit(Op::Add, self.line);
1118 pending = 0;
1119 }
1120 self.compile_node(inner)?;
1122 self.chunk.emit(Op::Dup, self.line);
1123 let assert_idx = self
1124 .chunk
1125 .add_constant(Constant::String("__assert_list".into()));
1126 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1127 self.chunk.emit(Op::Swap, self.line);
1128 self.chunk.emit_u8(Op::Call, 1, self.line);
1129 self.chunk.emit(Op::Pop, self.line);
1130 self.chunk.emit(Op::Add, self.line);
1131 } else {
1132 self.compile_node(el)?;
1133 pending += 1;
1134 }
1135 }
1136 if pending > 0 {
1137 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1138 self.chunk.emit(Op::Add, self.line);
1139 }
1140 }
1141 }
1142
1143 Node::DictLiteral(entries) => {
1144 let has_spread = entries
1145 .iter()
1146 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1147 if !has_spread {
1148 for entry in entries {
1149 self.compile_node(&entry.key)?;
1150 self.compile_node(&entry.value)?;
1151 }
1152 self.chunk
1153 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1154 } else {
1155 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1157 let mut pending = 0u16;
1158 for entry in entries {
1159 if let Node::Spread(inner) = &entry.value.node {
1160 if pending > 0 {
1162 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1163 self.chunk.emit(Op::Add, self.line);
1164 pending = 0;
1165 }
1166 self.compile_node(inner)?;
1168 self.chunk.emit(Op::Dup, self.line);
1169 let assert_idx = self
1170 .chunk
1171 .add_constant(Constant::String("__assert_dict".into()));
1172 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1173 self.chunk.emit(Op::Swap, self.line);
1174 self.chunk.emit_u8(Op::Call, 1, self.line);
1175 self.chunk.emit(Op::Pop, self.line);
1176 self.chunk.emit(Op::Add, self.line);
1177 } else {
1178 self.compile_node(&entry.key)?;
1179 self.compile_node(&entry.value)?;
1180 pending += 1;
1181 }
1182 }
1183 if pending > 0 {
1184 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1185 self.chunk.emit(Op::Add, self.line);
1186 }
1187 }
1188 }
1189
1190 Node::InterpolatedString(segments) => {
1191 let mut part_count = 0u16;
1192 for seg in segments {
1193 match seg {
1194 StringSegment::Literal(s) => {
1195 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1196 self.chunk.emit_u16(Op::Constant, idx, self.line);
1197 part_count += 1;
1198 }
1199 StringSegment::Expression(expr_str, expr_line, expr_col) => {
1200 let mut lexer =
1202 harn_lexer::Lexer::with_position(expr_str, *expr_line, *expr_col);
1203 if let Ok(tokens) = lexer.tokenize() {
1204 let mut parser = harn_parser::Parser::new(tokens);
1205 if let Ok(snode) = parser.parse_single_expression() {
1206 self.compile_node(&snode)?;
1207 let to_str = self
1209 .chunk
1210 .add_constant(Constant::String("to_string".into()));
1211 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1212 self.chunk.emit(Op::Swap, self.line);
1213 self.chunk.emit_u8(Op::Call, 1, self.line);
1214 part_count += 1;
1215 } else {
1216 let idx =
1218 self.chunk.add_constant(Constant::String(expr_str.clone()));
1219 self.chunk.emit_u16(Op::Constant, idx, self.line);
1220 part_count += 1;
1221 }
1222 }
1223 }
1224 }
1225 }
1226 if part_count > 1 {
1227 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1228 }
1229 }
1230
1231 Node::FnDecl {
1232 name, params, body, ..
1233 } => {
1234 let mut fn_compiler = Compiler::new();
1236 fn_compiler.enum_names = self.enum_names.clone();
1237 fn_compiler.emit_default_preamble(params)?;
1238 fn_compiler.emit_type_checks(params);
1239 let is_gen = body_contains_yield(body);
1240 fn_compiler.compile_block(body)?;
1241 fn_compiler.chunk.emit(Op::Nil, self.line);
1242 fn_compiler.chunk.emit(Op::Return, self.line);
1243
1244 let func = CompiledFunction {
1245 name: name.clone(),
1246 params: TypedParam::names(params),
1247 default_start: TypedParam::default_start(params),
1248 chunk: fn_compiler.chunk,
1249 is_generator: is_gen,
1250 has_rest_param: params.last().is_some_and(|p| p.rest),
1251 };
1252 let fn_idx = self.chunk.functions.len();
1253 self.chunk.functions.push(func);
1254
1255 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1256 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1257 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1258 }
1259
1260 Node::ToolDecl {
1261 name,
1262 description,
1263 params,
1264 return_type: _,
1265 body,
1266 ..
1267 } => {
1268 let mut fn_compiler = Compiler::new();
1270 fn_compiler.enum_names = self.enum_names.clone();
1271 fn_compiler.emit_default_preamble(params)?;
1272 fn_compiler.emit_type_checks(params);
1273 fn_compiler.compile_block(body)?;
1274 fn_compiler.chunk.emit(Op::Return, self.line);
1276
1277 let func = CompiledFunction {
1278 name: name.clone(),
1279 params: TypedParam::names(params),
1280 default_start: TypedParam::default_start(params),
1281 chunk: fn_compiler.chunk,
1282 is_generator: false,
1283 has_rest_param: params.last().is_some_and(|p| p.rest),
1284 };
1285 let fn_idx = self.chunk.functions.len();
1286 self.chunk.functions.push(func);
1287
1288 let define_name = self
1290 .chunk
1291 .add_constant(Constant::String("tool_define".into()));
1292 self.chunk.emit_u16(Op::Constant, define_name, self.line);
1293
1294 let reg_name = self
1296 .chunk
1297 .add_constant(Constant::String("tool_registry".into()));
1298 self.chunk.emit_u16(Op::Constant, reg_name, self.line);
1299 self.chunk.emit_u8(Op::Call, 0, self.line);
1300
1301 let tool_name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1303 self.chunk.emit_u16(Op::Constant, tool_name_idx, self.line);
1304
1305 let desc = description.as_deref().unwrap_or("");
1307 let desc_idx = self.chunk.add_constant(Constant::String(desc.to_string()));
1308 self.chunk.emit_u16(Op::Constant, desc_idx, self.line);
1309
1310 let mut param_count: u16 = 0;
1313 for p in params {
1314 let pn_idx = self.chunk.add_constant(Constant::String(p.name.clone()));
1315 self.chunk.emit_u16(Op::Constant, pn_idx, self.line);
1316
1317 let type_str = match &p.type_expr {
1318 Some(harn_parser::TypeExpr::Named(n)) => match n.as_str() {
1319 "string" => "string",
1320 "int" => "integer",
1321 "float" => "number",
1322 "bool" => "boolean",
1323 _ => "string",
1324 },
1325 _ => "string",
1326 };
1327 let type_key = self.chunk.add_constant(Constant::String("type".into()));
1328 self.chunk.emit_u16(Op::Constant, type_key, self.line);
1329 let type_val = self.chunk.add_constant(Constant::String(type_str.into()));
1330 self.chunk.emit_u16(Op::Constant, type_val, self.line);
1331 let mut param_schema_entries: u16 = 1;
1332 if let Some(default_expr) = &p.default_value {
1333 let required_key =
1334 self.chunk.add_constant(Constant::String("required".into()));
1335 self.chunk.emit_u16(Op::Constant, required_key, self.line);
1336 let required_val = self.chunk.add_constant(Constant::Bool(false));
1337 self.chunk.emit_u16(Op::Constant, required_val, self.line);
1338 let default_key =
1339 self.chunk.add_constant(Constant::String("default".into()));
1340 self.chunk.emit_u16(Op::Constant, default_key, self.line);
1341 self.compile_node(default_expr)?;
1342 param_schema_entries += 2;
1343 }
1344 self.chunk
1345 .emit_u16(Op::BuildDict, param_schema_entries, self.line);
1346 param_count += 1;
1347 }
1348 self.chunk.emit_u16(Op::BuildDict, param_count, self.line);
1349
1350 let params_key = self
1352 .chunk
1353 .add_constant(Constant::String("parameters".into()));
1354 self.chunk.emit_u16(Op::Constant, params_key, self.line);
1355 self.chunk.emit(Op::Swap, self.line);
1356
1357 let handler_key = self.chunk.add_constant(Constant::String("handler".into()));
1358 self.chunk.emit_u16(Op::Constant, handler_key, self.line);
1359 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1360
1361 self.chunk.emit_u16(Op::BuildDict, 2, self.line);
1362
1363 self.chunk.emit_u8(Op::Call, 4, self.line);
1365
1366 let bind_idx = self.chunk.add_constant(Constant::String(name.clone()));
1368 self.chunk.emit_u16(Op::DefLet, bind_idx, self.line);
1369 }
1370
1371 Node::Closure { params, body, .. } => {
1372 let mut fn_compiler = Compiler::new();
1373 fn_compiler.enum_names = self.enum_names.clone();
1374 fn_compiler.emit_default_preamble(params)?;
1375 fn_compiler.emit_type_checks(params);
1376 let is_gen = body_contains_yield(body);
1377 fn_compiler.compile_block(body)?;
1378 fn_compiler.chunk.emit(Op::Return, self.line);
1380
1381 let func = CompiledFunction {
1382 name: "<closure>".to_string(),
1383 params: TypedParam::names(params),
1384 default_start: TypedParam::default_start(params),
1385 chunk: fn_compiler.chunk,
1386 is_generator: is_gen,
1387 has_rest_param: false,
1388 };
1389 let fn_idx = self.chunk.functions.len();
1390 self.chunk.functions.push(func);
1391
1392 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1393 }
1394
1395 Node::ThrowStmt { value } => {
1396 self.compile_node(value)?;
1397 self.chunk.emit(Op::Throw, self.line);
1398 }
1399
1400 Node::MatchExpr { value, arms } => {
1401 self.compile_node(value)?;
1402 let mut end_jumps = Vec::new();
1403 for arm in arms {
1404 match &arm.pattern.node {
1405 Node::Identifier(name) if name == "_" => {
1407 self.begin_scope();
1408 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1410 self.end_scope();
1411 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1412 }
1413 Node::EnumConstruct {
1415 enum_name,
1416 variant,
1417 args: pat_args,
1418 } => {
1419 self.chunk.emit(Op::Dup, self.line);
1421 let en_idx =
1422 self.chunk.add_constant(Constant::String(enum_name.clone()));
1423 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1424 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1425 let hi = (vn_idx >> 8) as u8;
1426 let lo = vn_idx as u8;
1427 self.chunk.code.push(hi);
1428 self.chunk.code.push(lo);
1429 self.chunk.lines.push(self.line);
1430 self.chunk.columns.push(self.column);
1431 self.chunk.lines.push(self.line);
1432 self.chunk.columns.push(self.column);
1433 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1435 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1437
1438 for (i, pat_arg) in pat_args.iter().enumerate() {
1441 if let Node::Identifier(binding_name) = &pat_arg.node {
1442 self.chunk.emit(Op::Dup, self.line);
1444 let fields_idx = self
1445 .chunk
1446 .add_constant(Constant::String("fields".to_string()));
1447 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1448 let idx_const =
1449 self.chunk.add_constant(Constant::Int(i as i64));
1450 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1451 self.chunk.emit(Op::Subscript, self.line);
1452 let name_idx = self
1453 .chunk
1454 .add_constant(Constant::String(binding_name.clone()));
1455 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1456 }
1457 }
1458
1459 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1461 self.end_scope();
1462 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1463 self.chunk.patch_jump(skip);
1464 self.chunk.emit(Op::Pop, self.line); }
1466 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1468 {
1469 let enum_name = if let Node::Identifier(n) = &object.node {
1470 n.clone()
1471 } else {
1472 unreachable!()
1473 };
1474 self.chunk.emit(Op::Dup, self.line);
1475 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1476 let vn_idx =
1477 self.chunk.add_constant(Constant::String(property.clone()));
1478 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1479 let hi = (vn_idx >> 8) as u8;
1480 let lo = vn_idx as u8;
1481 self.chunk.code.push(hi);
1482 self.chunk.code.push(lo);
1483 self.chunk.lines.push(self.line);
1484 self.chunk.columns.push(self.column);
1485 self.chunk.lines.push(self.line);
1486 self.chunk.columns.push(self.column);
1487 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1488 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1490 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1492 self.end_scope();
1493 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1494 self.chunk.patch_jump(skip);
1495 self.chunk.emit(Op::Pop, self.line); }
1497 Node::MethodCall {
1500 object,
1501 method,
1502 args: pat_args,
1503 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1504 {
1505 let enum_name = if let Node::Identifier(n) = &object.node {
1506 n.clone()
1507 } else {
1508 unreachable!()
1509 };
1510 self.chunk.emit(Op::Dup, self.line);
1512 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1513 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1514 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1515 let hi = (vn_idx >> 8) as u8;
1516 let lo = vn_idx as u8;
1517 self.chunk.code.push(hi);
1518 self.chunk.code.push(lo);
1519 self.chunk.lines.push(self.line);
1520 self.chunk.columns.push(self.column);
1521 self.chunk.lines.push(self.line);
1522 self.chunk.columns.push(self.column);
1523 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1524 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1526
1527 for (i, pat_arg) in pat_args.iter().enumerate() {
1529 if let Node::Identifier(binding_name) = &pat_arg.node {
1530 self.chunk.emit(Op::Dup, self.line);
1531 let fields_idx = self
1532 .chunk
1533 .add_constant(Constant::String("fields".to_string()));
1534 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1535 let idx_const =
1536 self.chunk.add_constant(Constant::Int(i as i64));
1537 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1538 self.chunk.emit(Op::Subscript, self.line);
1539 let name_idx = self
1540 .chunk
1541 .add_constant(Constant::String(binding_name.clone()));
1542 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1543 }
1544 }
1545
1546 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1548 self.end_scope();
1549 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1550 self.chunk.patch_jump(skip);
1551 self.chunk.emit(Op::Pop, self.line); }
1553 Node::Identifier(name) => {
1555 self.begin_scope();
1556 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1559 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1560 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1562 self.end_scope();
1563 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1564 }
1565 Node::DictLiteral(entries)
1567 if entries
1568 .iter()
1569 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1570 {
1571 self.chunk.emit(Op::Dup, self.line);
1573 let typeof_idx =
1574 self.chunk.add_constant(Constant::String("type_of".into()));
1575 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1576 self.chunk.emit(Op::Swap, self.line);
1577 self.chunk.emit_u8(Op::Call, 1, self.line);
1578 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1579 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1580 self.chunk.emit(Op::Equal, self.line);
1581 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1582 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1586 let mut bindings = Vec::new();
1587 self.begin_scope();
1588 for entry in entries {
1589 if let Node::StringLiteral(key) = &entry.key.node {
1590 match &entry.value.node {
1591 Node::StringLiteral(_)
1593 | Node::IntLiteral(_)
1594 | Node::FloatLiteral(_)
1595 | Node::BoolLiteral(_)
1596 | Node::NilLiteral => {
1597 self.chunk.emit(Op::Dup, self.line);
1598 let key_idx = self
1599 .chunk
1600 .add_constant(Constant::String(key.clone()));
1601 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1602 self.chunk.emit(Op::Subscript, self.line);
1603 self.compile_node(&entry.value)?;
1604 self.chunk.emit(Op::Equal, self.line);
1605 let skip =
1606 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1607 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1609 }
1610 Node::Identifier(binding) => {
1612 bindings.push((key.clone(), binding.clone()));
1613 }
1614 _ => {
1615 self.chunk.emit(Op::Dup, self.line);
1617 let key_idx = self
1618 .chunk
1619 .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 self.compile_node(&entry.value)?;
1623 self.chunk.emit(Op::Equal, self.line);
1624 let skip =
1625 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1626 self.chunk.emit(Op::Pop, self.line);
1627 constraint_skips.push(skip);
1628 }
1629 }
1630 }
1631 }
1632
1633 for (key, binding) in &bindings {
1635 self.chunk.emit(Op::Dup, self.line);
1636 let key_idx =
1637 self.chunk.add_constant(Constant::String(key.clone()));
1638 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1639 self.chunk.emit(Op::Subscript, self.line);
1640 let name_idx =
1641 self.chunk.add_constant(Constant::String(binding.clone()));
1642 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1643 }
1644
1645 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1647 self.end_scope();
1648 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1649
1650 let type_fail_target = self.chunk.code.len();
1651 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1653 let scoped_fail_target = self.chunk.code.len();
1654 self.chunk.emit(Op::PopScope, self.line);
1655 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1657
1658 for skip in constraint_skips {
1659 self.chunk.patch_jump_to(skip, scoped_fail_target);
1660 }
1661 self.chunk.patch_jump_to(skip_type, type_fail_target);
1662 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1663 }
1664 Node::ListLiteral(elements) => {
1666 self.chunk.emit(Op::Dup, self.line);
1668 let typeof_idx =
1669 self.chunk.add_constant(Constant::String("type_of".into()));
1670 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1671 self.chunk.emit(Op::Swap, self.line);
1672 self.chunk.emit_u8(Op::Call, 1, self.line);
1673 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1674 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1675 self.chunk.emit(Op::Equal, self.line);
1676 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1677 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1681 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1682 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1683 self.chunk.emit(Op::Swap, self.line);
1684 self.chunk.emit_u8(Op::Call, 1, self.line);
1685 let count = self
1686 .chunk
1687 .add_constant(Constant::Int(elements.len() as i64));
1688 self.chunk.emit_u16(Op::Constant, count, self.line);
1689 self.chunk.emit(Op::GreaterEqual, self.line);
1690 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1691 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1695 let mut bindings = Vec::new();
1696 self.begin_scope();
1697 for (i, elem) in elements.iter().enumerate() {
1698 match &elem.node {
1699 Node::Identifier(name) if name != "_" => {
1700 bindings.push((i, name.clone()));
1701 }
1702 Node::Identifier(_) => {} _ => {
1705 self.chunk.emit(Op::Dup, self.line);
1706 let idx_const =
1707 self.chunk.add_constant(Constant::Int(i as i64));
1708 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1709 self.chunk.emit(Op::Subscript, self.line);
1710 self.compile_node(elem)?;
1711 self.chunk.emit(Op::Equal, self.line);
1712 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1713 self.chunk.emit(Op::Pop, self.line);
1714 constraint_skips.push(skip);
1715 }
1716 }
1717 }
1718
1719 for (i, name) in &bindings {
1721 self.chunk.emit(Op::Dup, self.line);
1722 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1723 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1724 self.chunk.emit(Op::Subscript, self.line);
1725 let name_idx =
1726 self.chunk.add_constant(Constant::String(name.clone()));
1727 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1728 }
1729
1730 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1732 self.end_scope();
1733 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1734
1735 let pre_scope_fail_target = self.chunk.code.len();
1736 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1738 let scoped_fail_target = self.chunk.code.len();
1739 self.chunk.emit(Op::PopScope, self.line);
1740 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1742 for skip in constraint_skips {
1743 self.chunk.patch_jump_to(skip, scoped_fail_target);
1744 }
1745 self.chunk.patch_jump_to(skip_len, pre_scope_fail_target);
1746 self.chunk.patch_jump_to(skip_type, pre_scope_fail_target);
1747 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1748 }
1749 _ => {
1751 self.chunk.emit(Op::Dup, self.line);
1752 self.compile_node(&arm.pattern)?;
1753 self.chunk.emit(Op::Equal, self.line);
1754 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1755 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1757 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1759 self.end_scope();
1760 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1761 self.chunk.patch_jump(skip);
1762 self.chunk.emit(Op::Pop, self.line); }
1764 }
1765 }
1766 self.chunk.emit(Op::Pop, self.line);
1768 self.chunk.emit(Op::Nil, self.line);
1769 for j in end_jumps {
1770 self.chunk.patch_jump(j);
1771 }
1772 }
1773
1774 Node::RangeExpr {
1775 start,
1776 end,
1777 inclusive,
1778 } => {
1779 let name_idx = self
1781 .chunk
1782 .add_constant(Constant::String("__range__".to_string()));
1783 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1784 self.compile_node(start)?;
1785 self.compile_node(end)?;
1786 if *inclusive {
1787 self.chunk.emit(Op::True, self.line);
1788 } else {
1789 self.chunk.emit(Op::False, self.line);
1790 }
1791 self.chunk.emit_u8(Op::Call, 3, self.line);
1792 }
1793
1794 Node::GuardStmt {
1795 condition,
1796 else_body,
1797 } => {
1798 self.compile_node(condition)?;
1801 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1802 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_block(else_body)?;
1805 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
1807 self.chunk.emit(Op::Pop, self.line);
1808 }
1809 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1810 self.chunk.patch_jump(skip_jump);
1811 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
1813 self.chunk.emit(Op::Nil, self.line);
1814 }
1815
1816 Node::RequireStmt { condition, message } => {
1817 self.compile_node(condition)?;
1818 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1819 self.chunk.emit(Op::Pop, self.line);
1820 if let Some(message) = message {
1821 self.compile_node(message)?;
1822 } else {
1823 let idx = self
1824 .chunk
1825 .add_constant(Constant::String("require condition failed".to_string()));
1826 self.chunk.emit_u16(Op::Constant, idx, self.line);
1827 }
1828 self.chunk.emit(Op::Throw, self.line);
1829 self.chunk.patch_jump(ok_jump);
1830 self.chunk.emit(Op::Pop, self.line);
1831 }
1832
1833 Node::Block(stmts) => {
1834 self.compile_scoped_block(stmts)?;
1835 }
1836
1837 Node::DeadlineBlock { duration, body } => {
1838 self.compile_node(duration)?;
1839 self.chunk.emit(Op::DeadlineSetup, self.line);
1840 self.compile_scoped_block(body)?;
1841 self.chunk.emit(Op::DeadlineEnd, self.line);
1842 }
1843
1844 Node::MutexBlock { body } => {
1845 self.begin_scope();
1847 for sn in body {
1848 self.compile_node(sn)?;
1849 if Self::produces_value(&sn.node) {
1850 self.chunk.emit(Op::Pop, self.line);
1851 }
1852 }
1853 self.chunk.emit(Op::Nil, self.line);
1854 self.end_scope();
1855 }
1856
1857 Node::YieldExpr { value } => {
1858 if let Some(val) = value {
1859 self.compile_node(val)?;
1860 } else {
1861 self.chunk.emit(Op::Nil, self.line);
1862 }
1863 self.chunk.emit(Op::Yield, self.line);
1864 }
1865
1866 Node::AskExpr { fields } => {
1867 for entry in fields {
1870 self.compile_node(&entry.key)?;
1871 self.compile_node(&entry.value)?;
1872 }
1873 self.chunk
1874 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
1875 }
1876
1877 Node::EnumConstruct {
1878 enum_name,
1879 variant,
1880 args,
1881 } => {
1882 for arg in args {
1884 self.compile_node(arg)?;
1885 }
1886 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
1887 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1888 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
1890 let hi = (var_idx >> 8) as u8;
1891 let lo = var_idx as u8;
1892 self.chunk.code.push(hi);
1893 self.chunk.code.push(lo);
1894 self.chunk.lines.push(self.line);
1895 self.chunk.columns.push(self.column);
1896 self.chunk.lines.push(self.line);
1897 self.chunk.columns.push(self.column);
1898 let fc = args.len() as u16;
1899 let fhi = (fc >> 8) as u8;
1900 let flo = fc as u8;
1901 self.chunk.code.push(fhi);
1902 self.chunk.code.push(flo);
1903 self.chunk.lines.push(self.line);
1904 self.chunk.columns.push(self.column);
1905 self.chunk.lines.push(self.line);
1906 self.chunk.columns.push(self.column);
1907 }
1908
1909 Node::StructConstruct {
1910 struct_name,
1911 fields,
1912 } => {
1913 let struct_key = self
1915 .chunk
1916 .add_constant(Constant::String("__struct__".to_string()));
1917 let struct_val = self
1918 .chunk
1919 .add_constant(Constant::String(struct_name.clone()));
1920 self.chunk.emit_u16(Op::Constant, struct_key, self.line);
1921 self.chunk.emit_u16(Op::Constant, struct_val, self.line);
1922
1923 for entry in fields {
1924 self.compile_node(&entry.key)?;
1925 self.compile_node(&entry.value)?;
1926 }
1927 self.chunk
1928 .emit_u16(Op::BuildDict, (fields.len() + 1) as u16, self.line);
1929 }
1930
1931 Node::ImportDecl { path } => {
1932 let idx = self.chunk.add_constant(Constant::String(path.clone()));
1933 self.chunk.emit_u16(Op::Import, idx, self.line);
1934 }
1935
1936 Node::SelectiveImport { names, path } => {
1937 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
1938 let names_str = names.join(",");
1939 let names_idx = self.chunk.add_constant(Constant::String(names_str));
1940 self.chunk
1941 .emit_u16(Op::SelectiveImport, path_idx, self.line);
1942 let hi = (names_idx >> 8) as u8;
1943 let lo = names_idx as u8;
1944 self.chunk.code.push(hi);
1945 self.chunk.code.push(lo);
1946 self.chunk.lines.push(self.line);
1947 self.chunk.columns.push(self.column);
1948 self.chunk.lines.push(self.line);
1949 self.chunk.columns.push(self.column);
1950 }
1951
1952 Node::TryOperator { operand } => {
1953 self.compile_node(operand)?;
1954 self.chunk.emit(Op::TryUnwrap, self.line);
1955 }
1956
1957 Node::ImplBlock { type_name, methods } => {
1958 for method_sn in methods {
1961 if let Node::FnDecl {
1962 name, params, body, ..
1963 } = &method_sn.node
1964 {
1965 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
1967 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1968
1969 let mut fn_compiler = Compiler::new();
1971 fn_compiler.enum_names = self.enum_names.clone();
1972 fn_compiler.emit_default_preamble(params)?;
1973 fn_compiler.emit_type_checks(params);
1974 fn_compiler.compile_block(body)?;
1975 fn_compiler.chunk.emit(Op::Nil, self.line);
1976 fn_compiler.chunk.emit(Op::Return, self.line);
1977
1978 let func = CompiledFunction {
1979 name: format!("{}.{}", type_name, name),
1980 params: TypedParam::names(params),
1981 default_start: TypedParam::default_start(params),
1982 chunk: fn_compiler.chunk,
1983 is_generator: false,
1984 has_rest_param: false,
1985 };
1986 let fn_idx = self.chunk.functions.len();
1987 self.chunk.functions.push(func);
1988 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1989 }
1990 }
1991 let method_count = methods
1992 .iter()
1993 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
1994 .count();
1995 self.chunk
1996 .emit_u16(Op::BuildDict, method_count as u16, self.line);
1997 let impl_name = format!("__impl_{}", type_name);
1998 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
1999 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2000 }
2001
2002 Node::StructDecl { name, .. } => {
2003 let mut fn_compiler = Compiler::new();
2005 fn_compiler.enum_names = self.enum_names.clone();
2006 let params = vec![TypedParam::untyped("__fields")];
2007 fn_compiler.emit_default_preamble(¶ms)?;
2008
2009 let make_idx = fn_compiler
2011 .chunk
2012 .add_constant(Constant::String("__make_struct".into()));
2013 fn_compiler
2014 .chunk
2015 .emit_u16(Op::Constant, make_idx, self.line);
2016 let sname_idx = fn_compiler
2017 .chunk
2018 .add_constant(Constant::String(name.clone()));
2019 fn_compiler
2020 .chunk
2021 .emit_u16(Op::Constant, sname_idx, self.line);
2022 let fields_idx = fn_compiler
2023 .chunk
2024 .add_constant(Constant::String("__fields".into()));
2025 fn_compiler
2026 .chunk
2027 .emit_u16(Op::GetVar, fields_idx, self.line);
2028 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
2029 fn_compiler.chunk.emit(Op::Return, self.line);
2030
2031 let func = CompiledFunction {
2032 name: name.clone(),
2033 params: TypedParam::names(¶ms),
2034 default_start: None,
2035 chunk: fn_compiler.chunk,
2036 is_generator: false,
2037 has_rest_param: false,
2038 };
2039 let fn_idx = self.chunk.functions.len();
2040 self.chunk.functions.push(func);
2041 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2042 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
2043 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2044 }
2045
2046 Node::Pipeline { .. }
2048 | Node::OverrideDecl { .. }
2049 | Node::TypeDecl { .. }
2050 | Node::EnumDecl { .. }
2051 | Node::InterfaceDecl { .. } => {
2052 self.chunk.emit(Op::Nil, self.line);
2053 }
2054
2055 Node::TryCatch {
2056 body,
2057 error_var,
2058 error_type,
2059 catch_body,
2060 finally_body,
2061 } => {
2062 let type_name = error_type.as_ref().and_then(|te| {
2064 if let harn_parser::TypeExpr::Named(name) = te {
2065 Some(name.clone())
2066 } else {
2067 None
2068 }
2069 });
2070
2071 let type_name_idx = if let Some(ref tn) = type_name {
2072 self.chunk.add_constant(Constant::String(tn.clone()))
2073 } else {
2074 self.chunk.add_constant(Constant::String(String::new()))
2075 };
2076
2077 let has_catch = !catch_body.is_empty() || error_var.is_some();
2078 let has_finally = finally_body.is_some();
2079
2080 if has_catch && has_finally {
2081 let finally_body = finally_body.as_ref().unwrap();
2083
2084 self.finally_bodies.push(finally_body.clone());
2086
2087 self.handler_depth += 1;
2089 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2090 self.emit_type_name_extra(type_name_idx);
2091
2092 self.compile_try_body(body)?;
2094
2095 self.handler_depth -= 1;
2097 self.chunk.emit(Op::PopHandler, self.line);
2098 self.compile_finally_inline(finally_body)?;
2099 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2100
2101 self.chunk.patch_jump(catch_jump);
2103 self.begin_scope();
2104 self.compile_catch_binding(error_var)?;
2105
2106 self.handler_depth += 1;
2108 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2109 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2110 self.emit_type_name_extra(empty_type);
2111
2112 self.compile_try_body(catch_body)?;
2114
2115 self.handler_depth -= 1;
2117 self.chunk.emit(Op::PopHandler, self.line);
2118 self.compile_finally_inline(finally_body)?;
2119 self.end_scope();
2120 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
2121
2122 self.chunk.patch_jump(rethrow_jump);
2124 self.compile_rethrow_with_finally(finally_body)?;
2125 self.end_scope();
2126
2127 self.chunk.patch_jump(end_jump);
2128 self.chunk.patch_jump(end_jump2);
2129
2130 self.finally_bodies.pop();
2131 } else if has_finally {
2132 let finally_body = finally_body.as_ref().unwrap();
2134
2135 self.finally_bodies.push(finally_body.clone());
2136
2137 self.handler_depth += 1;
2139 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2140 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2141 self.emit_type_name_extra(empty_type);
2142
2143 self.compile_try_body(body)?;
2145
2146 self.handler_depth -= 1;
2148 self.chunk.emit(Op::PopHandler, self.line);
2149 self.compile_finally_inline(finally_body)?;
2150 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2151
2152 self.chunk.patch_jump(error_jump);
2154 self.compile_rethrow_with_finally(finally_body)?;
2155
2156 self.chunk.patch_jump(end_jump);
2157
2158 self.finally_bodies.pop();
2159 } else {
2160 self.handler_depth += 1;
2164 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2165 self.emit_type_name_extra(type_name_idx);
2166
2167 self.compile_try_body(body)?;
2169
2170 self.handler_depth -= 1;
2172 self.chunk.emit(Op::PopHandler, self.line);
2173 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2174
2175 self.chunk.patch_jump(catch_jump);
2177 self.begin_scope();
2178 self.compile_catch_binding(error_var)?;
2179
2180 self.compile_try_body(catch_body)?;
2182 self.end_scope();
2183
2184 self.chunk.patch_jump(end_jump);
2186 }
2187 }
2188
2189 Node::TryExpr { body } => {
2190 self.handler_depth += 1;
2194 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2195 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2196 self.emit_type_name_extra(empty_type);
2197
2198 self.compile_try_body(body)?;
2200
2201 self.handler_depth -= 1;
2203 self.chunk.emit(Op::PopHandler, self.line);
2204
2205 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
2207 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
2208 self.chunk.emit(Op::Swap, self.line);
2209 self.chunk.emit_u8(Op::Call, 1, self.line);
2210
2211 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2213
2214 self.chunk.patch_jump(catch_jump);
2216
2217 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
2219 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
2220 self.chunk.emit(Op::Swap, self.line);
2221 self.chunk.emit_u8(Op::Call, 1, self.line);
2222
2223 self.chunk.patch_jump(end_jump);
2225 }
2226
2227 Node::Retry { count, body } => {
2228 self.compile_node(count)?;
2230 let counter_name = "__retry_counter__";
2231 let counter_idx = self
2232 .chunk
2233 .add_constant(Constant::String(counter_name.to_string()));
2234 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
2235
2236 self.chunk.emit(Op::Nil, self.line);
2238 let err_name = "__retry_last_error__";
2239 let err_idx = self
2240 .chunk
2241 .add_constant(Constant::String(err_name.to_string()));
2242 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
2243
2244 let loop_start = self.chunk.current_offset();
2246
2247 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2249 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2251 let hi = (empty_type >> 8) as u8;
2252 let lo = empty_type as u8;
2253 self.chunk.code.push(hi);
2254 self.chunk.code.push(lo);
2255 self.chunk.lines.push(self.line);
2256 self.chunk.columns.push(self.column);
2257 self.chunk.lines.push(self.line);
2258 self.chunk.columns.push(self.column);
2259
2260 self.compile_block(body)?;
2262
2263 self.chunk.emit(Op::PopHandler, self.line);
2265 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2266
2267 self.chunk.patch_jump(catch_jump);
2269 self.chunk.emit(Op::Dup, self.line);
2271 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
2272 self.chunk.emit(Op::Pop, self.line);
2274
2275 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
2277 let one_idx = self.chunk.add_constant(Constant::Int(1));
2278 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
2279 self.chunk.emit(Op::Sub, self.line);
2280 self.chunk.emit(Op::Dup, self.line);
2281 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
2282
2283 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2285 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2286 self.chunk.emit(Op::Greater, self.line);
2287 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2288 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
2290
2291 self.chunk.patch_jump(retry_jump);
2293 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
2295 self.chunk.emit(Op::Throw, self.line);
2296
2297 self.chunk.patch_jump(end_jump);
2298 self.chunk.emit(Op::Nil, self.line);
2300 }
2301
2302 Node::Parallel {
2303 count,
2304 variable,
2305 body,
2306 } => {
2307 self.compile_node(count)?;
2308 let mut fn_compiler = Compiler::new();
2309 fn_compiler.enum_names = self.enum_names.clone();
2310 fn_compiler.compile_block(body)?;
2311 fn_compiler.chunk.emit(Op::Return, self.line);
2312 let params = vec![variable.clone().unwrap_or_else(|| "__i__".to_string())];
2313 let func = CompiledFunction {
2314 name: "<parallel>".to_string(),
2315 params,
2316 default_start: None,
2317 chunk: fn_compiler.chunk,
2318 is_generator: false,
2319 has_rest_param: false,
2320 };
2321 let fn_idx = self.chunk.functions.len();
2322 self.chunk.functions.push(func);
2323 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2324 self.chunk.emit(Op::Parallel, self.line);
2325 }
2326
2327 Node::ParallelMap {
2328 list,
2329 variable,
2330 body,
2331 } => {
2332 self.compile_node(list)?;
2333 let mut fn_compiler = Compiler::new();
2334 fn_compiler.enum_names = self.enum_names.clone();
2335 fn_compiler.compile_block(body)?;
2336 fn_compiler.chunk.emit(Op::Return, self.line);
2337 let func = CompiledFunction {
2338 name: "<parallel_map>".to_string(),
2339 params: vec![variable.clone()],
2340 default_start: None,
2341 chunk: fn_compiler.chunk,
2342 is_generator: false,
2343 has_rest_param: false,
2344 };
2345 let fn_idx = self.chunk.functions.len();
2346 self.chunk.functions.push(func);
2347 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2348 self.chunk.emit(Op::ParallelMap, self.line);
2349 }
2350
2351 Node::ParallelSettle {
2352 list,
2353 variable,
2354 body,
2355 } => {
2356 self.compile_node(list)?;
2357 let mut fn_compiler = Compiler::new();
2358 fn_compiler.enum_names = self.enum_names.clone();
2359 fn_compiler.compile_block(body)?;
2360 fn_compiler.chunk.emit(Op::Return, self.line);
2361 let func = CompiledFunction {
2362 name: "<parallel_settle>".to_string(),
2363 params: vec![variable.clone()],
2364 default_start: None,
2365 chunk: fn_compiler.chunk,
2366 is_generator: false,
2367 has_rest_param: false,
2368 };
2369 let fn_idx = self.chunk.functions.len();
2370 self.chunk.functions.push(func);
2371 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2372 self.chunk.emit(Op::ParallelSettle, self.line);
2373 }
2374
2375 Node::SpawnExpr { body } => {
2376 let mut fn_compiler = Compiler::new();
2377 fn_compiler.enum_names = self.enum_names.clone();
2378 fn_compiler.compile_block(body)?;
2379 fn_compiler.chunk.emit(Op::Return, self.line);
2380 let func = CompiledFunction {
2381 name: "<spawn>".to_string(),
2382 params: vec![],
2383 default_start: None,
2384 chunk: fn_compiler.chunk,
2385 is_generator: false,
2386 has_rest_param: false,
2387 };
2388 let fn_idx = self.chunk.functions.len();
2389 self.chunk.functions.push(func);
2390 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2391 self.chunk.emit(Op::Spawn, self.line);
2392 }
2393 Node::SelectExpr {
2394 cases,
2395 timeout,
2396 default_body,
2397 } => {
2398 let builtin_name = if timeout.is_some() {
2405 "__select_timeout"
2406 } else if default_body.is_some() {
2407 "__select_try"
2408 } else {
2409 "__select_list"
2410 };
2411
2412 let name_idx = self
2414 .chunk
2415 .add_constant(Constant::String(builtin_name.into()));
2416 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2417
2418 for case in cases {
2420 self.compile_node(&case.channel)?;
2421 }
2422 self.chunk
2423 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2424
2425 if let Some((duration_expr, _)) = timeout {
2427 self.compile_node(duration_expr)?;
2428 self.chunk.emit_u8(Op::Call, 2, self.line);
2429 } else {
2430 self.chunk.emit_u8(Op::Call, 1, self.line);
2431 }
2432
2433 self.temp_counter += 1;
2435 let result_name = format!("__sel_result_{}__", self.temp_counter);
2436 let result_idx = self
2437 .chunk
2438 .add_constant(Constant::String(result_name.clone()));
2439 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2440
2441 let mut end_jumps = Vec::new();
2443
2444 for (i, case) in cases.iter().enumerate() {
2445 let get_r = self
2446 .chunk
2447 .add_constant(Constant::String(result_name.clone()));
2448 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2449 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2450 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2451 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2452 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2453 self.chunk.emit(Op::Equal, self.line);
2454 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2455 self.chunk.emit(Op::Pop, self.line);
2456 self.begin_scope();
2457
2458 let get_r2 = self
2460 .chunk
2461 .add_constant(Constant::String(result_name.clone()));
2462 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2463 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2464 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2465 let var_idx = self
2466 .chunk
2467 .add_constant(Constant::String(case.variable.clone()));
2468 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2469
2470 self.compile_try_body(&case.body)?;
2471 self.end_scope();
2472 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2473 self.chunk.patch_jump(skip);
2474 self.chunk.emit(Op::Pop, self.line);
2475 }
2476
2477 if let Some((_, ref timeout_body)) = timeout {
2479 self.compile_try_body(timeout_body)?;
2480 } else if let Some(ref def_body) = default_body {
2481 self.compile_try_body(def_body)?;
2482 } else {
2483 self.chunk.emit(Op::Nil, self.line);
2484 }
2485
2486 for ej in end_jumps {
2487 self.chunk.patch_jump(ej);
2488 }
2489 }
2490 Node::Spread(_) => {
2491 return Err(CompileError {
2492 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2493 line: self.line,
2494 });
2495 }
2496 }
2497 Ok(())
2498 }
2499
2500 fn compile_destructuring(
2504 &mut self,
2505 pattern: &BindingPattern,
2506 is_mutable: bool,
2507 ) -> Result<(), CompileError> {
2508 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2509 match pattern {
2510 BindingPattern::Identifier(name) => {
2511 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2513 self.chunk.emit_u16(def_op, idx, self.line);
2514 }
2515 BindingPattern::Dict(fields) => {
2516 self.chunk.emit(Op::Dup, self.line);
2519 let assert_idx = self
2520 .chunk
2521 .add_constant(Constant::String("__assert_dict".into()));
2522 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2523 self.chunk.emit(Op::Swap, self.line);
2524 self.chunk.emit_u8(Op::Call, 1, self.line);
2525 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2530 let rest_field = fields.iter().find(|f| f.is_rest);
2531
2532 for field in &non_rest {
2533 self.chunk.emit(Op::Dup, self.line);
2534 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2535 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2536 self.chunk.emit(Op::Subscript, self.line);
2537 if let Some(default_expr) = &field.default_value {
2539 self.chunk.emit(Op::Dup, self.line);
2540 self.chunk.emit(Op::Nil, self.line);
2541 self.chunk.emit(Op::NotEqual, self.line);
2542 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2543 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2546 let end = self.chunk.emit_jump(Op::Jump, self.line);
2547 self.chunk.patch_jump(skip_default);
2548 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2550 }
2551 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2552 let name_idx = self
2553 .chunk
2554 .add_constant(Constant::String(binding_name.to_string()));
2555 self.chunk.emit_u16(def_op, name_idx, self.line);
2556 }
2557
2558 if let Some(rest) = rest_field {
2559 let fn_idx = self
2562 .chunk
2563 .add_constant(Constant::String("__dict_rest".into()));
2564 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2565 self.chunk.emit(Op::Swap, self.line);
2567 for field in &non_rest {
2569 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2570 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2571 }
2572 self.chunk
2573 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2574 self.chunk.emit_u8(Op::Call, 2, self.line);
2576 let rest_name = &rest.key;
2577 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2578 self.chunk.emit_u16(def_op, rest_idx, self.line);
2579 } else {
2580 self.chunk.emit(Op::Pop, self.line);
2582 }
2583 }
2584 BindingPattern::List(elements) => {
2585 self.chunk.emit(Op::Dup, self.line);
2588 let assert_idx = self
2589 .chunk
2590 .add_constant(Constant::String("__assert_list".into()));
2591 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2592 self.chunk.emit(Op::Swap, self.line);
2593 self.chunk.emit_u8(Op::Call, 1, self.line);
2594 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2597 let rest_elem = elements.iter().find(|e| e.is_rest);
2598
2599 for (i, elem) in non_rest.iter().enumerate() {
2600 self.chunk.emit(Op::Dup, self.line);
2601 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2602 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2603 self.chunk.emit(Op::Subscript, self.line);
2604 if let Some(default_expr) = &elem.default_value {
2606 self.chunk.emit(Op::Dup, self.line);
2607 self.chunk.emit(Op::Nil, self.line);
2608 self.chunk.emit(Op::NotEqual, self.line);
2609 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2610 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2613 let end = self.chunk.emit_jump(Op::Jump, self.line);
2614 self.chunk.patch_jump(skip_default);
2615 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2617 }
2618 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2619 self.chunk.emit_u16(def_op, name_idx, self.line);
2620 }
2621
2622 if let Some(rest) = rest_elem {
2623 let start_idx = self
2627 .chunk
2628 .add_constant(Constant::Int(non_rest.len() as i64));
2629 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2630 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2632 let rest_name_idx =
2633 self.chunk.add_constant(Constant::String(rest.name.clone()));
2634 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2635 } else {
2636 self.chunk.emit(Op::Pop, self.line);
2638 }
2639 }
2640 }
2641 Ok(())
2642 }
2643
2644 fn produces_value(node: &Node) -> bool {
2646 match node {
2647 Node::LetBinding { .. }
2649 | Node::VarBinding { .. }
2650 | Node::Assignment { .. }
2651 | Node::ReturnStmt { .. }
2652 | Node::FnDecl { .. }
2653 | Node::ToolDecl { .. }
2654 | Node::ImplBlock { .. }
2655 | Node::StructDecl { .. }
2656 | Node::EnumDecl { .. }
2657 | Node::InterfaceDecl { .. }
2658 | Node::TypeDecl { .. }
2659 | Node::ThrowStmt { .. }
2660 | Node::BreakStmt
2661 | Node::ContinueStmt
2662 | Node::RequireStmt { .. } => false,
2663 Node::TryCatch { .. }
2665 | Node::TryExpr { .. }
2666 | Node::Retry { .. }
2667 | Node::GuardStmt { .. }
2668 | Node::DeadlineBlock { .. }
2669 | Node::MutexBlock { .. }
2670 | Node::Spread(_) => true,
2671 _ => true,
2673 }
2674 }
2675}
2676
2677impl Compiler {
2678 pub fn compile_fn_body(
2691 &mut self,
2692 params: &[TypedParam],
2693 body: &[SNode],
2694 source_file: Option<String>,
2695 ) -> Result<CompiledFunction, CompileError> {
2696 let mut fn_compiler = Compiler::new();
2697 fn_compiler.enum_names = self.enum_names.clone();
2698 fn_compiler.emit_default_preamble(params)?;
2699 fn_compiler.emit_type_checks(params);
2700 let is_gen = body_contains_yield(body);
2701 fn_compiler.compile_block(body)?;
2702 fn_compiler.chunk.emit(Op::Nil, 0);
2703 fn_compiler.chunk.emit(Op::Return, 0);
2704 fn_compiler.chunk.source_file = source_file;
2705 Ok(CompiledFunction {
2706 name: String::new(),
2707 params: TypedParam::names(params),
2708 default_start: TypedParam::default_start(params),
2709 chunk: fn_compiler.chunk,
2710 is_generator: is_gen,
2711 has_rest_param: false,
2712 })
2713 }
2714
2715 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2717 self.begin_scope();
2718 if body.is_empty() {
2719 self.chunk.emit(Op::Nil, self.line);
2720 } else {
2721 self.compile_block(body)?;
2722 if !Self::produces_value(&body.last().unwrap().node) {
2723 self.chunk.emit(Op::Nil, self.line);
2724 }
2725 }
2726 self.end_scope();
2727 Ok(())
2728 }
2729
2730 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2732 match op {
2733 "+" => self.chunk.emit(Op::Add, self.line),
2734 "-" => self.chunk.emit(Op::Sub, self.line),
2735 "*" => self.chunk.emit(Op::Mul, self.line),
2736 "/" => self.chunk.emit(Op::Div, self.line),
2737 "%" => self.chunk.emit(Op::Mod, self.line),
2738 _ => {
2739 return Err(CompileError {
2740 message: format!("Unknown compound operator: {op}"),
2741 line: self.line,
2742 })
2743 }
2744 }
2745 Ok(())
2746 }
2747
2748 fn root_var_name(&self, node: &SNode) -> Option<String> {
2750 match &node.node {
2751 Node::Identifier(name) => Some(name.clone()),
2752 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2753 self.root_var_name(object)
2754 }
2755 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2756 _ => None,
2757 }
2758 }
2759
2760 fn compile_top_level_declarations(&mut self, program: &[SNode]) -> Result<(), CompileError> {
2761 for sn in program {
2762 if matches!(
2763 &sn.node,
2764 Node::FnDecl { .. }
2765 | Node::ToolDecl { .. }
2766 | Node::ImplBlock { .. }
2767 | Node::StructDecl { .. }
2768 | Node::EnumDecl { .. }
2769 | Node::InterfaceDecl { .. }
2770 | Node::TypeDecl { .. }
2771 ) {
2772 self.compile_node(sn)?;
2773 }
2774 }
2775 Ok(())
2776 }
2777}
2778
2779impl Compiler {
2780 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
2782 for sn in nodes {
2783 match &sn.node {
2784 Node::EnumDecl { name, .. } => {
2785 names.insert(name.clone());
2786 }
2787 Node::Pipeline { body, .. } => {
2788 Self::collect_enum_names(body, names);
2789 }
2790 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
2791 Self::collect_enum_names(body, names);
2792 }
2793 Node::Block(stmts) => {
2794 Self::collect_enum_names(stmts, names);
2795 }
2796 _ => {}
2797 }
2798 }
2799 }
2800
2801 fn collect_interface_methods(
2802 nodes: &[SNode],
2803 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
2804 ) {
2805 for sn in nodes {
2806 match &sn.node {
2807 Node::InterfaceDecl { name, methods, .. } => {
2808 let method_names: Vec<String> =
2809 methods.iter().map(|m| m.name.clone()).collect();
2810 interfaces.insert(name.clone(), method_names);
2811 }
2812 Node::Pipeline { body, .. }
2813 | Node::FnDecl { body, .. }
2814 | Node::ToolDecl { body, .. } => {
2815 Self::collect_interface_methods(body, interfaces);
2816 }
2817 Node::Block(stmts) => {
2818 Self::collect_interface_methods(stmts, interfaces);
2819 }
2820 _ => {}
2821 }
2822 }
2823 }
2824}
2825
2826impl Default for Compiler {
2827 fn default() -> Self {
2828 Self::new()
2829 }
2830}
2831
2832fn body_contains_yield(nodes: &[SNode]) -> bool {
2834 nodes.iter().any(|sn| node_contains_yield(&sn.node))
2835}
2836
2837fn node_contains_yield(node: &Node) -> bool {
2838 match node {
2839 Node::YieldExpr { .. } => true,
2840 Node::FnDecl { .. } | Node::Closure { .. } => false,
2843 Node::Block(stmts) => body_contains_yield(stmts),
2844 Node::IfElse {
2845 condition,
2846 then_body,
2847 else_body,
2848 } => {
2849 node_contains_yield(&condition.node)
2850 || body_contains_yield(then_body)
2851 || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
2852 }
2853 Node::WhileLoop { condition, body } => {
2854 node_contains_yield(&condition.node) || body_contains_yield(body)
2855 }
2856 Node::ForIn { iterable, body, .. } => {
2857 node_contains_yield(&iterable.node) || body_contains_yield(body)
2858 }
2859 Node::TryCatch {
2860 body, catch_body, ..
2861 } => body_contains_yield(body) || body_contains_yield(catch_body),
2862 Node::TryExpr { body } => body_contains_yield(body),
2863 _ => false,
2864 }
2865}
2866
2867fn contains_pipe_placeholder(node: &SNode) -> bool {
2869 match &node.node {
2870 Node::Identifier(name) if name == "_" => true,
2871 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
2872 Node::MethodCall { object, args, .. } => {
2873 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
2874 }
2875 Node::BinaryOp { left, right, .. } => {
2876 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
2877 }
2878 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
2879 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
2880 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
2881 Node::SubscriptAccess { object, index } => {
2882 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
2883 }
2884 _ => false,
2885 }
2886}
2887
2888fn replace_pipe_placeholder(node: &SNode) -> SNode {
2890 let new_node = match &node.node {
2891 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
2892 Node::FunctionCall { name, args } => Node::FunctionCall {
2893 name: name.clone(),
2894 args: args.iter().map(replace_pipe_placeholder).collect(),
2895 },
2896 Node::MethodCall {
2897 object,
2898 method,
2899 args,
2900 } => Node::MethodCall {
2901 object: Box::new(replace_pipe_placeholder(object)),
2902 method: method.clone(),
2903 args: args.iter().map(replace_pipe_placeholder).collect(),
2904 },
2905 Node::BinaryOp { op, left, right } => Node::BinaryOp {
2906 op: op.clone(),
2907 left: Box::new(replace_pipe_placeholder(left)),
2908 right: Box::new(replace_pipe_placeholder(right)),
2909 },
2910 Node::UnaryOp { op, operand } => Node::UnaryOp {
2911 op: op.clone(),
2912 operand: Box::new(replace_pipe_placeholder(operand)),
2913 },
2914 Node::ListLiteral(items) => {
2915 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
2916 }
2917 Node::PropertyAccess { object, property } => Node::PropertyAccess {
2918 object: Box::new(replace_pipe_placeholder(object)),
2919 property: property.clone(),
2920 },
2921 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
2922 object: Box::new(replace_pipe_placeholder(object)),
2923 index: Box::new(replace_pipe_placeholder(index)),
2924 },
2925 _ => return node.clone(),
2926 };
2927 SNode::new(new_node, node.span)
2928}
2929
2930#[cfg(test)]
2931mod tests {
2932 use super::*;
2933 use harn_lexer::Lexer;
2934 use harn_parser::Parser;
2935
2936 fn compile_source(source: &str) -> Chunk {
2937 let mut lexer = Lexer::new(source);
2938 let tokens = lexer.tokenize().unwrap();
2939 let mut parser = Parser::new(tokens);
2940 let program = parser.parse().unwrap();
2941 Compiler::new().compile(&program).unwrap()
2942 }
2943
2944 #[test]
2945 fn test_compile_arithmetic() {
2946 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
2947 assert!(!chunk.code.is_empty());
2948 assert!(chunk.constants.contains(&Constant::Int(2)));
2950 assert!(chunk.constants.contains(&Constant::Int(3)));
2951 }
2952
2953 #[test]
2954 fn test_compile_function_call() {
2955 let chunk = compile_source("pipeline test(task) { log(42) }");
2956 let disasm = chunk.disassemble("test");
2957 assert!(disasm.contains("CALL"));
2958 }
2959
2960 #[test]
2961 fn test_compile_if_else() {
2962 let chunk =
2963 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
2964 let disasm = chunk.disassemble("test");
2965 assert!(disasm.contains("JUMP_IF_FALSE"));
2966 assert!(disasm.contains("JUMP"));
2967 }
2968
2969 #[test]
2970 fn test_compile_while() {
2971 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
2972 let disasm = chunk.disassemble("test");
2973 assert!(disasm.contains("JUMP_IF_FALSE"));
2974 assert!(disasm.contains("JUMP"));
2976 }
2977
2978 #[test]
2979 fn test_compile_closure() {
2980 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
2981 assert!(!chunk.functions.is_empty());
2982 assert_eq!(chunk.functions[0].params, vec!["x"]);
2983 }
2984
2985 #[test]
2986 fn test_compile_list() {
2987 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
2988 let disasm = chunk.disassemble("test");
2989 assert!(disasm.contains("BUILD_LIST"));
2990 }
2991
2992 #[test]
2993 fn test_compile_dict() {
2994 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
2995 let disasm = chunk.disassemble("test");
2996 assert!(disasm.contains("BUILD_DICT"));
2997 }
2998
2999 #[test]
3000 fn test_disassemble() {
3001 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
3002 let disasm = chunk.disassemble("test");
3003 assert!(disasm.contains("CONSTANT"));
3005 assert!(disasm.contains("ADD"));
3006 assert!(disasm.contains("CALL"));
3007 }
3008}