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}
34
35pub struct Compiler {
37 chunk: Chunk,
38 line: u32,
39 column: u32,
40 enum_names: std::collections::HashSet<String>,
42 loop_stack: Vec<LoopContext>,
44 handler_depth: usize,
46 finally_bodies: Vec<Vec<SNode>>,
48 temp_counter: usize,
50}
51
52impl Compiler {
53 pub fn new() -> Self {
54 Self {
55 chunk: Chunk::new(),
56 line: 1,
57 column: 1,
58 enum_names: std::collections::HashSet::new(),
59 loop_stack: Vec::new(),
60 handler_depth: 0,
61 finally_bodies: Vec::new(),
62 temp_counter: 0,
63 }
64 }
65
66 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
69 Self::collect_enum_names(program, &mut self.enum_names);
72 self.enum_names.insert("Result".to_string());
74
75 for sn in program {
77 match &sn.node {
78 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
79 self.compile_node(sn)?;
80 }
81 _ => {}
82 }
83 }
84
85 let main = program
87 .iter()
88 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == "default"))
89 .or_else(|| {
90 program
91 .iter()
92 .find(|sn| matches!(&sn.node, Node::Pipeline { .. }))
93 });
94
95 if let Some(sn) = main {
96 if let Node::Pipeline { body, extends, .. } = &sn.node {
97 if let Some(parent_name) = extends {
99 self.compile_parent_pipeline(program, parent_name)?;
100 }
101 self.compile_block(body)?;
102 }
103 }
104
105 self.chunk.emit(Op::Nil, self.line);
106 self.chunk.emit(Op::Return, self.line);
107 Ok(self.chunk)
108 }
109
110 pub fn compile_named(
112 mut self,
113 program: &[SNode],
114 pipeline_name: &str,
115 ) -> Result<Chunk, CompileError> {
116 Self::collect_enum_names(program, &mut self.enum_names);
117
118 for sn in program {
119 if matches!(
120 &sn.node,
121 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
122 ) {
123 self.compile_node(sn)?;
124 }
125 }
126
127 let target = program
128 .iter()
129 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == pipeline_name));
130
131 if let Some(sn) = target {
132 if let Node::Pipeline { body, extends, .. } = &sn.node {
133 if let Some(parent_name) = extends {
134 self.compile_parent_pipeline(program, parent_name)?;
135 }
136 self.compile_block(body)?;
137 }
138 }
139
140 self.chunk.emit(Op::Nil, self.line);
141 self.chunk.emit(Op::Return, self.line);
142 Ok(self.chunk)
143 }
144
145 fn compile_parent_pipeline(
147 &mut self,
148 program: &[SNode],
149 parent_name: &str,
150 ) -> Result<(), CompileError> {
151 let parent = program
152 .iter()
153 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
154 if let Some(sn) = parent {
155 if let Node::Pipeline { body, extends, .. } = &sn.node {
156 if let Some(grandparent) = extends {
158 self.compile_parent_pipeline(program, grandparent)?;
159 }
160 for stmt in body {
162 self.compile_node(stmt)?;
163 if Self::produces_value(&stmt.node) {
164 self.chunk.emit(Op::Pop, self.line);
165 }
166 }
167 }
168 }
169 Ok(())
170 }
171
172 fn emit_default_preamble(&mut self, params: &[TypedParam]) -> Result<(), CompileError> {
177 for (i, param) in params.iter().enumerate() {
178 if let Some(default_expr) = ¶m.default_value {
179 self.chunk.emit(Op::GetArgc, self.line);
180 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
181 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
182 self.chunk.emit(Op::GreaterEqual, self.line);
184 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
185 self.chunk.emit(Op::Pop, self.line);
187 self.compile_node(default_expr)?;
189 let name_idx = self
190 .chunk
191 .add_constant(Constant::String(param.name.clone()));
192 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
193 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
194 self.chunk.patch_jump(skip_jump);
195 self.chunk.emit(Op::Pop, self.line);
197 self.chunk.patch_jump(end_jump);
198 }
199 }
200 Ok(())
201 }
202
203 fn emit_type_checks(&mut self, params: &[TypedParam]) {
206 for param in params {
207 if let Some(type_expr) = ¶m.type_expr {
208 let type_name = Self::type_expr_to_runtime_name(type_expr);
209 if let Some(type_name) = type_name {
210 let var_idx = self
211 .chunk
212 .add_constant(Constant::String(param.name.clone()));
213 let type_idx = self.chunk.add_constant(Constant::String(type_name));
214 self.chunk.emit_u16(Op::CheckType, var_idx, self.line);
215 let hi = (type_idx >> 8) as u8;
217 let lo = type_idx as u8;
218 self.chunk.code.push(hi);
219 self.chunk.code.push(lo);
220 }
221 }
222 }
223 }
224
225 fn type_expr_to_runtime_name(type_expr: &harn_parser::TypeExpr) -> Option<String> {
227 match type_expr {
228 harn_parser::TypeExpr::Named(name) => match name.as_str() {
229 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
230 | "closure" => Some(name.clone()),
231 _ => None, },
233 _ => None, }
235 }
236
237 fn emit_type_name_extra(&mut self, type_name_idx: u16) {
239 let hi = (type_name_idx >> 8) as u8;
240 let lo = type_name_idx as u8;
241 self.chunk.code.push(hi);
242 self.chunk.code.push(lo);
243 self.chunk.lines.push(self.line);
244 self.chunk.columns.push(self.column);
245 self.chunk.lines.push(self.line);
246 self.chunk.columns.push(self.column);
247 }
248
249 fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
251 if body.is_empty() {
252 self.chunk.emit(Op::Nil, self.line);
253 } else {
254 self.compile_block(body)?;
255 if !Self::produces_value(&body.last().unwrap().node) {
256 self.chunk.emit(Op::Nil, self.line);
257 }
258 }
259 Ok(())
260 }
261
262 fn compile_catch_binding(&mut self, error_var: &Option<String>) -> Result<(), CompileError> {
264 if let Some(var_name) = error_var {
265 let idx = self.chunk.add_constant(Constant::String(var_name.clone()));
266 self.chunk.emit_u16(Op::DefLet, idx, self.line);
267 } else {
268 self.chunk.emit(Op::Pop, self.line);
269 }
270 Ok(())
271 }
272
273 fn compile_finally_inline(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
275 if !finally_body.is_empty() {
276 self.compile_block(finally_body)?;
277 if Self::produces_value(&finally_body.last().unwrap().node) {
279 self.chunk.emit(Op::Pop, self.line);
280 }
281 }
282 Ok(())
283 }
284
285 fn compile_rethrow_with_finally(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
287 self.temp_counter += 1;
289 let temp_name = format!("__finally_err_{}__", self.temp_counter);
290 let err_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
291 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
292 self.compile_finally_inline(finally_body)?;
293 let get_idx = self.chunk.add_constant(Constant::String(temp_name));
294 self.chunk.emit_u16(Op::GetVar, get_idx, self.line);
295 self.chunk.emit(Op::Throw, self.line);
296 Ok(())
297 }
298
299 fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
300 for (i, snode) in stmts.iter().enumerate() {
301 self.compile_node(snode)?;
302 let is_last = i == stmts.len() - 1;
303 if is_last {
304 if !Self::produces_value(&snode.node) {
307 self.chunk.emit(Op::Nil, self.line);
308 }
309 } else {
310 if Self::produces_value(&snode.node) {
312 self.chunk.emit(Op::Pop, self.line);
313 }
314 }
315 }
316 Ok(())
317 }
318
319 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
320 self.line = snode.span.line as u32;
321 self.column = snode.span.column as u32;
322 self.chunk.set_column(self.column);
323 match &snode.node {
324 Node::IntLiteral(n) => {
325 let idx = self.chunk.add_constant(Constant::Int(*n));
326 self.chunk.emit_u16(Op::Constant, idx, self.line);
327 }
328 Node::FloatLiteral(n) => {
329 let idx = self.chunk.add_constant(Constant::Float(*n));
330 self.chunk.emit_u16(Op::Constant, idx, self.line);
331 }
332 Node::StringLiteral(s) => {
333 let idx = self.chunk.add_constant(Constant::String(s.clone()));
334 self.chunk.emit_u16(Op::Constant, idx, self.line);
335 }
336 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
337 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
338 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
339 Node::DurationLiteral(ms) => {
340 let idx = self.chunk.add_constant(Constant::Duration(*ms));
341 self.chunk.emit_u16(Op::Constant, idx, self.line);
342 }
343
344 Node::Identifier(name) => {
345 let idx = self.chunk.add_constant(Constant::String(name.clone()));
346 self.chunk.emit_u16(Op::GetVar, idx, self.line);
347 }
348
349 Node::LetBinding { pattern, value, .. } => {
350 self.compile_node(value)?;
351 self.compile_destructuring(pattern, false)?;
352 }
353
354 Node::VarBinding { pattern, value, .. } => {
355 self.compile_node(value)?;
356 self.compile_destructuring(pattern, true)?;
357 }
358
359 Node::Assignment {
360 target, value, op, ..
361 } => {
362 if let Node::Identifier(name) = &target.node {
363 let idx = self.chunk.add_constant(Constant::String(name.clone()));
364 if let Some(op) = op {
365 self.chunk.emit_u16(Op::GetVar, idx, self.line);
366 self.compile_node(value)?;
367 self.emit_compound_op(op)?;
368 self.chunk.emit_u16(Op::SetVar, idx, self.line);
369 } else {
370 self.compile_node(value)?;
371 self.chunk.emit_u16(Op::SetVar, idx, self.line);
372 }
373 } else if let Node::PropertyAccess { object, property } = &target.node {
374 if let Some(var_name) = self.root_var_name(object) {
376 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
377 let prop_idx = self.chunk.add_constant(Constant::String(property.clone()));
378 if let Some(op) = op {
379 self.compile_node(target)?; self.compile_node(value)?;
382 self.emit_compound_op(op)?;
383 } else {
384 self.compile_node(value)?;
385 }
386 self.chunk.emit_u16(Op::SetProperty, prop_idx, self.line);
389 let hi = (var_idx >> 8) as u8;
391 let lo = var_idx as u8;
392 self.chunk.code.push(hi);
393 self.chunk.code.push(lo);
394 self.chunk.lines.push(self.line);
395 self.chunk.columns.push(self.column);
396 self.chunk.lines.push(self.line);
397 self.chunk.columns.push(self.column);
398 }
399 } else if let Node::SubscriptAccess { object, index } = &target.node {
400 if let Some(var_name) = self.root_var_name(object) {
402 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
403 if let Some(op) = op {
404 self.compile_node(target)?;
405 self.compile_node(value)?;
406 self.emit_compound_op(op)?;
407 } else {
408 self.compile_node(value)?;
409 }
410 self.compile_node(index)?;
411 self.chunk.emit_u16(Op::SetSubscript, var_idx, self.line);
412 }
413 }
414 }
415
416 Node::BinaryOp { op, left, right } => {
417 match op.as_str() {
419 "&&" => {
420 self.compile_node(left)?;
421 let jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
422 self.chunk.emit(Op::Pop, self.line);
423 self.compile_node(right)?;
424 self.chunk.patch_jump(jump);
425 self.chunk.emit(Op::Not, self.line);
427 self.chunk.emit(Op::Not, self.line);
428 return Ok(());
429 }
430 "||" => {
431 self.compile_node(left)?;
432 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
433 self.chunk.emit(Op::Pop, self.line);
434 self.compile_node(right)?;
435 self.chunk.patch_jump(jump);
436 self.chunk.emit(Op::Not, self.line);
437 self.chunk.emit(Op::Not, self.line);
438 return Ok(());
439 }
440 "??" => {
441 self.compile_node(left)?;
442 self.chunk.emit(Op::Dup, self.line);
443 self.chunk.emit(Op::Nil, self.line);
445 self.chunk.emit(Op::NotEqual, self.line);
446 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
447 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(right)?;
450 let end = self.chunk.emit_jump(Op::Jump, self.line);
451 self.chunk.patch_jump(jump);
452 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
454 return Ok(());
455 }
456 "|>" => {
457 self.compile_node(left)?;
458 if contains_pipe_placeholder(right) {
461 let replaced = replace_pipe_placeholder(right);
462 let closure_node = SNode::dummy(Node::Closure {
463 params: vec![TypedParam {
464 name: "__pipe".into(),
465 type_expr: None,
466 default_value: None,
467 }],
468 body: vec![replaced],
469 });
470 self.compile_node(&closure_node)?;
471 } else {
472 self.compile_node(right)?;
473 }
474 self.chunk.emit(Op::Pipe, self.line);
475 return Ok(());
476 }
477 _ => {}
478 }
479
480 self.compile_node(left)?;
481 self.compile_node(right)?;
482 match op.as_str() {
483 "+" => self.chunk.emit(Op::Add, self.line),
484 "-" => self.chunk.emit(Op::Sub, self.line),
485 "*" => self.chunk.emit(Op::Mul, self.line),
486 "/" => self.chunk.emit(Op::Div, self.line),
487 "%" => self.chunk.emit(Op::Mod, self.line),
488 "==" => self.chunk.emit(Op::Equal, self.line),
489 "!=" => self.chunk.emit(Op::NotEqual, self.line),
490 "<" => self.chunk.emit(Op::Less, self.line),
491 ">" => self.chunk.emit(Op::Greater, self.line),
492 "<=" => self.chunk.emit(Op::LessEqual, self.line),
493 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
494 _ => {
495 return Err(CompileError {
496 message: format!("Unknown operator: {op}"),
497 line: self.line,
498 })
499 }
500 }
501 }
502
503 Node::UnaryOp { op, operand } => {
504 self.compile_node(operand)?;
505 match op.as_str() {
506 "-" => self.chunk.emit(Op::Negate, self.line),
507 "!" => self.chunk.emit(Op::Not, self.line),
508 _ => {}
509 }
510 }
511
512 Node::Ternary {
513 condition,
514 true_expr,
515 false_expr,
516 } => {
517 self.compile_node(condition)?;
518 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
519 self.chunk.emit(Op::Pop, self.line);
520 self.compile_node(true_expr)?;
521 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
522 self.chunk.patch_jump(else_jump);
523 self.chunk.emit(Op::Pop, self.line);
524 self.compile_node(false_expr)?;
525 self.chunk.patch_jump(end_jump);
526 }
527
528 Node::FunctionCall { name, args } => {
529 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
531 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
532 for arg in args {
534 self.compile_node(arg)?;
535 }
536 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
537 }
538
539 Node::MethodCall {
540 object,
541 method,
542 args,
543 } => {
544 if let Node::Identifier(name) = &object.node {
546 if self.enum_names.contains(name) {
547 for arg in args {
549 self.compile_node(arg)?;
550 }
551 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
552 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
553 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
554 let hi = (var_idx >> 8) as u8;
555 let lo = var_idx as u8;
556 self.chunk.code.push(hi);
557 self.chunk.code.push(lo);
558 self.chunk.lines.push(self.line);
559 self.chunk.columns.push(self.column);
560 self.chunk.lines.push(self.line);
561 self.chunk.columns.push(self.column);
562 let fc = args.len() as u16;
563 let fhi = (fc >> 8) as u8;
564 let flo = fc as u8;
565 self.chunk.code.push(fhi);
566 self.chunk.code.push(flo);
567 self.chunk.lines.push(self.line);
568 self.chunk.columns.push(self.column);
569 self.chunk.lines.push(self.line);
570 self.chunk.columns.push(self.column);
571 return Ok(());
572 }
573 }
574 self.compile_node(object)?;
575 for arg in args {
576 self.compile_node(arg)?;
577 }
578 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
579 self.chunk
580 .emit_method_call(name_idx, args.len() as u8, self.line);
581 }
582
583 Node::OptionalMethodCall {
584 object,
585 method,
586 args,
587 } => {
588 self.compile_node(object)?;
589 for arg in args {
590 self.compile_node(arg)?;
591 }
592 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
593 self.chunk
594 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
595 }
596
597 Node::PropertyAccess { object, property } => {
598 if let Node::Identifier(name) = &object.node {
600 if self.enum_names.contains(name) {
601 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
603 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
604 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
605 let hi = (var_idx >> 8) as u8;
606 let lo = var_idx as u8;
607 self.chunk.code.push(hi);
608 self.chunk.code.push(lo);
609 self.chunk.lines.push(self.line);
610 self.chunk.columns.push(self.column);
611 self.chunk.lines.push(self.line);
612 self.chunk.columns.push(self.column);
613 self.chunk.code.push(0);
615 self.chunk.code.push(0);
616 self.chunk.lines.push(self.line);
617 self.chunk.columns.push(self.column);
618 self.chunk.lines.push(self.line);
619 self.chunk.columns.push(self.column);
620 return Ok(());
621 }
622 }
623 self.compile_node(object)?;
624 let idx = self.chunk.add_constant(Constant::String(property.clone()));
625 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
626 }
627
628 Node::OptionalPropertyAccess { object, property } => {
629 self.compile_node(object)?;
630 let idx = self.chunk.add_constant(Constant::String(property.clone()));
631 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
632 }
633
634 Node::SubscriptAccess { object, index } => {
635 self.compile_node(object)?;
636 self.compile_node(index)?;
637 self.chunk.emit(Op::Subscript, self.line);
638 }
639
640 Node::SliceAccess { object, start, end } => {
641 self.compile_node(object)?;
642 if let Some(s) = start {
643 self.compile_node(s)?;
644 } else {
645 self.chunk.emit(Op::Nil, self.line);
646 }
647 if let Some(e) = end {
648 self.compile_node(e)?;
649 } else {
650 self.chunk.emit(Op::Nil, self.line);
651 }
652 self.chunk.emit(Op::Slice, self.line);
653 }
654
655 Node::IfElse {
656 condition,
657 then_body,
658 else_body,
659 } => {
660 self.compile_node(condition)?;
661 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
662 self.chunk.emit(Op::Pop, self.line);
663 self.compile_block(then_body)?;
664 if let Some(else_body) = else_body {
665 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
666 self.chunk.patch_jump(else_jump);
667 self.chunk.emit(Op::Pop, self.line);
668 self.compile_block(else_body)?;
669 self.chunk.patch_jump(end_jump);
670 } else {
671 self.chunk.patch_jump(else_jump);
672 self.chunk.emit(Op::Pop, self.line);
673 self.chunk.emit(Op::Nil, self.line);
674 }
675 }
676
677 Node::WhileLoop { condition, body } => {
678 let loop_start = self.chunk.current_offset();
679 self.loop_stack.push(LoopContext {
680 start_offset: loop_start,
681 break_patches: Vec::new(),
682 has_iterator: false,
683 handler_depth: self.handler_depth,
684 finally_depth: self.finally_bodies.len(),
685 });
686 self.compile_node(condition)?;
687 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
688 self.chunk.emit(Op::Pop, self.line); for sn in body {
691 self.compile_node(sn)?;
692 if Self::produces_value(&sn.node) {
693 self.chunk.emit(Op::Pop, self.line);
694 }
695 }
696 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
698 self.chunk.patch_jump(exit_jump);
699 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
702 for patch_pos in ctx.break_patches {
703 self.chunk.patch_jump(patch_pos);
704 }
705 self.chunk.emit(Op::Nil, self.line);
706 }
707
708 Node::ForIn {
709 pattern,
710 iterable,
711 body,
712 } => {
713 self.compile_node(iterable)?;
715 self.chunk.emit(Op::IterInit, self.line);
717 let loop_start = self.chunk.current_offset();
718 self.loop_stack.push(LoopContext {
719 start_offset: loop_start,
720 break_patches: Vec::new(),
721 has_iterator: true,
722 handler_depth: self.handler_depth,
723 finally_depth: self.finally_bodies.len(),
724 });
725 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
727 self.compile_destructuring(pattern, true)?;
729 for sn in body {
731 self.compile_node(sn)?;
732 if Self::produces_value(&sn.node) {
733 self.chunk.emit(Op::Pop, self.line);
734 }
735 }
736 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
738 self.chunk.patch_jump(exit_jump_pos);
739 let ctx = self.loop_stack.pop().unwrap();
741 for patch_pos in ctx.break_patches {
742 self.chunk.patch_jump(patch_pos);
743 }
744 self.chunk.emit(Op::Nil, self.line);
746 }
747
748 Node::ReturnStmt { value } => {
749 let has_pending_finally = !self.finally_bodies.is_empty();
750
751 if has_pending_finally {
752 if let Some(val) = value {
755 self.compile_node(val)?;
756 } else {
757 self.chunk.emit(Op::Nil, self.line);
758 }
759 self.temp_counter += 1;
760 let temp_name = format!("__return_val_{}__", self.temp_counter);
761 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
762 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
763 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
765 for fb in &finallys {
766 self.compile_finally_inline(fb)?;
767 }
768 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
769 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
770 self.chunk.emit(Op::Return, self.line);
771 } else {
772 if let Some(val) = value {
774 if let Node::FunctionCall { name, args } = &val.node {
775 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
776 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
777 for arg in args {
778 self.compile_node(arg)?;
779 }
780 self.chunk
781 .emit_u8(Op::TailCall, args.len() as u8, self.line);
782 } else if let Node::BinaryOp { op, left, right } = &val.node {
783 if op == "|>" {
784 self.compile_node(left)?;
785 self.compile_node(right)?;
786 self.chunk.emit(Op::Swap, self.line);
787 self.chunk.emit_u8(Op::TailCall, 1, self.line);
788 } else {
789 self.compile_node(val)?;
790 }
791 } else {
792 self.compile_node(val)?;
793 }
794 } else {
795 self.chunk.emit(Op::Nil, self.line);
796 }
797 self.chunk.emit(Op::Return, self.line);
798 }
799 }
800
801 Node::BreakStmt => {
802 if self.loop_stack.is_empty() {
803 return Err(CompileError {
804 message: "break outside of loop".to_string(),
805 line: self.line,
806 });
807 }
808 let ctx = self.loop_stack.last().unwrap();
810 let finally_depth = ctx.finally_depth;
811 let handler_depth = ctx.handler_depth;
812 let has_iterator = ctx.has_iterator;
813 for _ in handler_depth..self.handler_depth {
815 self.chunk.emit(Op::PopHandler, self.line);
816 }
817 if self.finally_bodies.len() > finally_depth {
819 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
820 .iter()
821 .rev()
822 .cloned()
823 .collect();
824 for fb in &finallys {
825 self.compile_finally_inline(fb)?;
826 }
827 }
828 if has_iterator {
829 self.chunk.emit(Op::PopIterator, self.line);
830 }
831 let patch = self.chunk.emit_jump(Op::Jump, self.line);
832 self.loop_stack
833 .last_mut()
834 .unwrap()
835 .break_patches
836 .push(patch);
837 }
838
839 Node::ContinueStmt => {
840 if self.loop_stack.is_empty() {
841 return Err(CompileError {
842 message: "continue outside of loop".to_string(),
843 line: self.line,
844 });
845 }
846 let ctx = self.loop_stack.last().unwrap();
847 let finally_depth = ctx.finally_depth;
848 let handler_depth = ctx.handler_depth;
849 let loop_start = ctx.start_offset;
850 for _ in handler_depth..self.handler_depth {
851 self.chunk.emit(Op::PopHandler, self.line);
852 }
853 if self.finally_bodies.len() > finally_depth {
854 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
855 .iter()
856 .rev()
857 .cloned()
858 .collect();
859 for fb in &finallys {
860 self.compile_finally_inline(fb)?;
861 }
862 }
863 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
864 }
865
866 Node::ListLiteral(elements) => {
867 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
868 if !has_spread {
869 for el in elements {
870 self.compile_node(el)?;
871 }
872 self.chunk
873 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
874 } else {
875 self.chunk.emit_u16(Op::BuildList, 0, self.line);
878 let mut pending = 0u16;
879 for el in elements {
880 if let Node::Spread(inner) = &el.node {
881 if pending > 0 {
883 self.chunk.emit_u16(Op::BuildList, pending, self.line);
884 self.chunk.emit(Op::Add, self.line);
886 pending = 0;
887 }
888 self.compile_node(inner)?;
890 self.chunk.emit(Op::Dup, self.line);
891 let assert_idx = self
892 .chunk
893 .add_constant(Constant::String("__assert_list".into()));
894 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
895 self.chunk.emit(Op::Swap, self.line);
896 self.chunk.emit_u8(Op::Call, 1, self.line);
897 self.chunk.emit(Op::Pop, self.line);
898 self.chunk.emit(Op::Add, self.line);
899 } else {
900 self.compile_node(el)?;
901 pending += 1;
902 }
903 }
904 if pending > 0 {
905 self.chunk.emit_u16(Op::BuildList, pending, self.line);
906 self.chunk.emit(Op::Add, self.line);
907 }
908 }
909 }
910
911 Node::DictLiteral(entries) => {
912 let has_spread = entries
913 .iter()
914 .any(|e| matches!(&e.value.node, Node::Spread(_)));
915 if !has_spread {
916 for entry in entries {
917 self.compile_node(&entry.key)?;
918 self.compile_node(&entry.value)?;
919 }
920 self.chunk
921 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
922 } else {
923 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
925 let mut pending = 0u16;
926 for entry in entries {
927 if let Node::Spread(inner) = &entry.value.node {
928 if pending > 0 {
930 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
931 self.chunk.emit(Op::Add, self.line);
932 pending = 0;
933 }
934 self.compile_node(inner)?;
936 self.chunk.emit(Op::Dup, self.line);
937 let assert_idx = self
938 .chunk
939 .add_constant(Constant::String("__assert_dict".into()));
940 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
941 self.chunk.emit(Op::Swap, self.line);
942 self.chunk.emit_u8(Op::Call, 1, self.line);
943 self.chunk.emit(Op::Pop, self.line);
944 self.chunk.emit(Op::Add, self.line);
945 } else {
946 self.compile_node(&entry.key)?;
947 self.compile_node(&entry.value)?;
948 pending += 1;
949 }
950 }
951 if pending > 0 {
952 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
953 self.chunk.emit(Op::Add, self.line);
954 }
955 }
956 }
957
958 Node::InterpolatedString(segments) => {
959 let mut part_count = 0u16;
960 for seg in segments {
961 match seg {
962 StringSegment::Literal(s) => {
963 let idx = self.chunk.add_constant(Constant::String(s.clone()));
964 self.chunk.emit_u16(Op::Constant, idx, self.line);
965 part_count += 1;
966 }
967 StringSegment::Expression(expr_str) => {
968 let mut lexer = harn_lexer::Lexer::new(expr_str);
970 if let Ok(tokens) = lexer.tokenize() {
971 let mut parser = harn_parser::Parser::new(tokens);
972 if let Ok(snode) = parser.parse_single_expression() {
973 self.compile_node(&snode)?;
974 let to_str = self
976 .chunk
977 .add_constant(Constant::String("to_string".into()));
978 self.chunk.emit_u16(Op::Constant, to_str, self.line);
979 self.chunk.emit(Op::Swap, self.line);
980 self.chunk.emit_u8(Op::Call, 1, self.line);
981 part_count += 1;
982 } else {
983 let idx =
985 self.chunk.add_constant(Constant::String(expr_str.clone()));
986 self.chunk.emit_u16(Op::Constant, idx, self.line);
987 part_count += 1;
988 }
989 }
990 }
991 }
992 }
993 if part_count > 1 {
994 self.chunk.emit_u16(Op::Concat, part_count, self.line);
995 }
996 }
997
998 Node::FnDecl {
999 name, params, body, ..
1000 } => {
1001 let mut fn_compiler = Compiler::new();
1003 fn_compiler.enum_names = self.enum_names.clone();
1004 fn_compiler.emit_default_preamble(params)?;
1005 fn_compiler.emit_type_checks(params);
1006 fn_compiler.compile_block(body)?;
1007 fn_compiler.chunk.emit(Op::Nil, self.line);
1008 fn_compiler.chunk.emit(Op::Return, self.line);
1009
1010 let func = CompiledFunction {
1011 name: name.clone(),
1012 params: TypedParam::names(params),
1013 default_start: TypedParam::default_start(params),
1014 chunk: fn_compiler.chunk,
1015 };
1016 let fn_idx = self.chunk.functions.len();
1017 self.chunk.functions.push(func);
1018
1019 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1020 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1021 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1022 }
1023
1024 Node::Closure { params, body } => {
1025 let mut fn_compiler = Compiler::new();
1026 fn_compiler.enum_names = self.enum_names.clone();
1027 fn_compiler.emit_default_preamble(params)?;
1028 fn_compiler.emit_type_checks(params);
1029 fn_compiler.compile_block(body)?;
1030 fn_compiler.chunk.emit(Op::Return, self.line);
1032
1033 let func = CompiledFunction {
1034 name: "<closure>".to_string(),
1035 params: TypedParam::names(params),
1036 default_start: TypedParam::default_start(params),
1037 chunk: fn_compiler.chunk,
1038 };
1039 let fn_idx = self.chunk.functions.len();
1040 self.chunk.functions.push(func);
1041
1042 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1043 }
1044
1045 Node::ThrowStmt { value } => {
1046 self.compile_node(value)?;
1047 self.chunk.emit(Op::Throw, self.line);
1048 }
1049
1050 Node::MatchExpr { value, arms } => {
1051 self.compile_node(value)?;
1052 let mut end_jumps = Vec::new();
1053 for arm in arms {
1054 match &arm.pattern.node {
1055 Node::Identifier(name) if name == "_" => {
1057 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1059 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1060 }
1061 Node::EnumConstruct {
1063 enum_name,
1064 variant,
1065 args: pat_args,
1066 } => {
1067 self.chunk.emit(Op::Dup, self.line);
1069 let en_idx =
1070 self.chunk.add_constant(Constant::String(enum_name.clone()));
1071 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1072 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1073 let hi = (vn_idx >> 8) as u8;
1074 let lo = vn_idx as u8;
1075 self.chunk.code.push(hi);
1076 self.chunk.code.push(lo);
1077 self.chunk.lines.push(self.line);
1078 self.chunk.columns.push(self.column);
1079 self.chunk.lines.push(self.line);
1080 self.chunk.columns.push(self.column);
1081 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1083 self.chunk.emit(Op::Pop, self.line); for (i, pat_arg) in pat_args.iter().enumerate() {
1088 if let Node::Identifier(binding_name) = &pat_arg.node {
1089 self.chunk.emit(Op::Dup, self.line);
1091 let fields_idx = self
1092 .chunk
1093 .add_constant(Constant::String("fields".to_string()));
1094 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1095 let idx_const =
1096 self.chunk.add_constant(Constant::Int(i as i64));
1097 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1098 self.chunk.emit(Op::Subscript, self.line);
1099 let name_idx = self
1100 .chunk
1101 .add_constant(Constant::String(binding_name.clone()));
1102 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1103 }
1104 }
1105
1106 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1108 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1109 self.chunk.patch_jump(skip);
1110 self.chunk.emit(Op::Pop, self.line); }
1112 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1114 {
1115 let enum_name = if let Node::Identifier(n) = &object.node {
1116 n.clone()
1117 } else {
1118 unreachable!()
1119 };
1120 self.chunk.emit(Op::Dup, self.line);
1121 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1122 let vn_idx =
1123 self.chunk.add_constant(Constant::String(property.clone()));
1124 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1125 let hi = (vn_idx >> 8) as u8;
1126 let lo = vn_idx as u8;
1127 self.chunk.code.push(hi);
1128 self.chunk.code.push(lo);
1129 self.chunk.lines.push(self.line);
1130 self.chunk.columns.push(self.column);
1131 self.chunk.lines.push(self.line);
1132 self.chunk.columns.push(self.column);
1133 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1134 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1137 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1138 self.chunk.patch_jump(skip);
1139 self.chunk.emit(Op::Pop, self.line); }
1141 Node::MethodCall {
1144 object,
1145 method,
1146 args: pat_args,
1147 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1148 {
1149 let enum_name = if let Node::Identifier(n) = &object.node {
1150 n.clone()
1151 } else {
1152 unreachable!()
1153 };
1154 self.chunk.emit(Op::Dup, self.line);
1156 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1157 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1158 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1159 let hi = (vn_idx >> 8) as u8;
1160 let lo = vn_idx as u8;
1161 self.chunk.code.push(hi);
1162 self.chunk.code.push(lo);
1163 self.chunk.lines.push(self.line);
1164 self.chunk.columns.push(self.column);
1165 self.chunk.lines.push(self.line);
1166 self.chunk.columns.push(self.column);
1167 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1168 self.chunk.emit(Op::Pop, self.line); for (i, pat_arg) in pat_args.iter().enumerate() {
1172 if let Node::Identifier(binding_name) = &pat_arg.node {
1173 self.chunk.emit(Op::Dup, self.line);
1174 let fields_idx = self
1175 .chunk
1176 .add_constant(Constant::String("fields".to_string()));
1177 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1178 let idx_const =
1179 self.chunk.add_constant(Constant::Int(i as i64));
1180 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1181 self.chunk.emit(Op::Subscript, self.line);
1182 let name_idx = self
1183 .chunk
1184 .add_constant(Constant::String(binding_name.clone()));
1185 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1186 }
1187 }
1188
1189 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1191 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1192 self.chunk.patch_jump(skip);
1193 self.chunk.emit(Op::Pop, self.line); }
1195 Node::Identifier(name) => {
1197 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1200 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1201 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1203 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1204 }
1205 Node::DictLiteral(entries)
1207 if entries
1208 .iter()
1209 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1210 {
1211 self.chunk.emit(Op::Dup, self.line);
1213 let typeof_idx =
1214 self.chunk.add_constant(Constant::String("type_of".into()));
1215 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1216 self.chunk.emit(Op::Swap, self.line);
1217 self.chunk.emit_u8(Op::Call, 1, self.line);
1218 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1219 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1220 self.chunk.emit(Op::Equal, self.line);
1221 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1222 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1226 let mut bindings = Vec::new();
1227 for entry in entries {
1228 if let Node::StringLiteral(key) = &entry.key.node {
1229 match &entry.value.node {
1230 Node::StringLiteral(_)
1232 | Node::IntLiteral(_)
1233 | Node::FloatLiteral(_)
1234 | Node::BoolLiteral(_)
1235 | Node::NilLiteral => {
1236 self.chunk.emit(Op::Dup, self.line);
1237 let key_idx = self
1238 .chunk
1239 .add_constant(Constant::String(key.clone()));
1240 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1241 self.chunk.emit(Op::Subscript, self.line);
1242 self.compile_node(&entry.value)?;
1243 self.chunk.emit(Op::Equal, self.line);
1244 let skip =
1245 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1246 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1248 }
1249 Node::Identifier(binding) => {
1251 bindings.push((key.clone(), binding.clone()));
1252 }
1253 _ => {
1254 self.chunk.emit(Op::Dup, self.line);
1256 let key_idx = self
1257 .chunk
1258 .add_constant(Constant::String(key.clone()));
1259 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1260 self.chunk.emit(Op::Subscript, self.line);
1261 self.compile_node(&entry.value)?;
1262 self.chunk.emit(Op::Equal, self.line);
1263 let skip =
1264 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1265 self.chunk.emit(Op::Pop, self.line);
1266 constraint_skips.push(skip);
1267 }
1268 }
1269 }
1270 }
1271
1272 for (key, binding) in &bindings {
1274 self.chunk.emit(Op::Dup, self.line);
1275 let key_idx =
1276 self.chunk.add_constant(Constant::String(key.clone()));
1277 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1278 self.chunk.emit(Op::Subscript, self.line);
1279 let name_idx =
1280 self.chunk.add_constant(Constant::String(binding.clone()));
1281 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1282 }
1283
1284 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1286 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1287
1288 let fail_target = self.chunk.code.len();
1290 self.chunk.emit(Op::Pop, self.line); for skip in constraint_skips {
1293 self.chunk.patch_jump_to(skip, fail_target);
1294 }
1295 self.chunk.patch_jump_to(skip_type, fail_target);
1296 }
1297 Node::ListLiteral(elements) => {
1299 self.chunk.emit(Op::Dup, self.line);
1301 let typeof_idx =
1302 self.chunk.add_constant(Constant::String("type_of".into()));
1303 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1304 self.chunk.emit(Op::Swap, self.line);
1305 self.chunk.emit_u8(Op::Call, 1, self.line);
1306 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1307 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1308 self.chunk.emit(Op::Equal, self.line);
1309 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1310 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1314 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1315 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1316 self.chunk.emit(Op::Swap, self.line);
1317 self.chunk.emit_u8(Op::Call, 1, self.line);
1318 let count = self
1319 .chunk
1320 .add_constant(Constant::Int(elements.len() as i64));
1321 self.chunk.emit_u16(Op::Constant, count, self.line);
1322 self.chunk.emit(Op::GreaterEqual, self.line);
1323 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1324 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1328 let mut bindings = Vec::new();
1329 for (i, elem) in elements.iter().enumerate() {
1330 match &elem.node {
1331 Node::Identifier(name) if name != "_" => {
1332 bindings.push((i, name.clone()));
1333 }
1334 Node::Identifier(_) => {} _ => {
1337 self.chunk.emit(Op::Dup, self.line);
1338 let idx_const =
1339 self.chunk.add_constant(Constant::Int(i as i64));
1340 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1341 self.chunk.emit(Op::Subscript, self.line);
1342 self.compile_node(elem)?;
1343 self.chunk.emit(Op::Equal, self.line);
1344 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1345 self.chunk.emit(Op::Pop, self.line);
1346 constraint_skips.push(skip);
1347 }
1348 }
1349 }
1350
1351 for (i, name) in &bindings {
1353 self.chunk.emit(Op::Dup, self.line);
1354 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1355 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1356 self.chunk.emit(Op::Subscript, self.line);
1357 let name_idx =
1358 self.chunk.add_constant(Constant::String(name.clone()));
1359 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1360 }
1361
1362 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1364 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1365
1366 let fail_target = self.chunk.code.len();
1368 self.chunk.emit(Op::Pop, self.line); for skip in constraint_skips {
1370 self.chunk.patch_jump_to(skip, fail_target);
1371 }
1372 self.chunk.patch_jump_to(skip_len, fail_target);
1373 self.chunk.patch_jump_to(skip_type, fail_target);
1374 }
1375 _ => {
1377 self.chunk.emit(Op::Dup, self.line);
1378 self.compile_node(&arm.pattern)?;
1379 self.chunk.emit(Op::Equal, self.line);
1380 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1381 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1384 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1385 self.chunk.patch_jump(skip);
1386 self.chunk.emit(Op::Pop, self.line); }
1388 }
1389 }
1390 self.chunk.emit(Op::Pop, self.line);
1392 self.chunk.emit(Op::Nil, self.line);
1393 for j in end_jumps {
1394 self.chunk.patch_jump(j);
1395 }
1396 }
1397
1398 Node::RangeExpr {
1399 start,
1400 end,
1401 inclusive,
1402 } => {
1403 let name_idx = self
1405 .chunk
1406 .add_constant(Constant::String("__range__".to_string()));
1407 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1408 self.compile_node(start)?;
1409 self.compile_node(end)?;
1410 if *inclusive {
1411 self.chunk.emit(Op::True, self.line);
1412 } else {
1413 self.chunk.emit(Op::False, self.line);
1414 }
1415 self.chunk.emit_u8(Op::Call, 3, self.line);
1416 }
1417
1418 Node::GuardStmt {
1419 condition,
1420 else_body,
1421 } => {
1422 self.compile_node(condition)?;
1425 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1426 self.chunk.emit(Op::Pop, self.line); self.compile_block(else_body)?;
1429 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
1431 self.chunk.emit(Op::Pop, self.line);
1432 }
1433 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1434 self.chunk.patch_jump(skip_jump);
1435 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
1437 self.chunk.emit(Op::Nil, self.line);
1438 }
1439
1440 Node::Block(stmts) => {
1441 if stmts.is_empty() {
1442 self.chunk.emit(Op::Nil, self.line);
1443 } else {
1444 self.compile_block(stmts)?;
1445 }
1446 }
1447
1448 Node::DeadlineBlock { duration, body } => {
1449 self.compile_node(duration)?;
1450 self.chunk.emit(Op::DeadlineSetup, self.line);
1451 if body.is_empty() {
1452 self.chunk.emit(Op::Nil, self.line);
1453 } else {
1454 self.compile_block(body)?;
1455 }
1456 self.chunk.emit(Op::DeadlineEnd, self.line);
1457 }
1458
1459 Node::MutexBlock { body } => {
1460 if body.is_empty() {
1462 self.chunk.emit(Op::Nil, self.line);
1463 } else {
1464 for sn in body {
1467 self.compile_node(sn)?;
1468 if Self::produces_value(&sn.node) {
1469 self.chunk.emit(Op::Pop, self.line);
1470 }
1471 }
1472 self.chunk.emit(Op::Nil, self.line);
1473 }
1474 }
1475
1476 Node::YieldExpr { .. } => {
1477 self.chunk.emit(Op::Nil, self.line);
1479 }
1480
1481 Node::AskExpr { fields } => {
1482 for entry in fields {
1485 self.compile_node(&entry.key)?;
1486 self.compile_node(&entry.value)?;
1487 }
1488 self.chunk
1489 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
1490 }
1491
1492 Node::EnumConstruct {
1493 enum_name,
1494 variant,
1495 args,
1496 } => {
1497 for arg in args {
1499 self.compile_node(arg)?;
1500 }
1501 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
1502 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1503 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
1505 let hi = (var_idx >> 8) as u8;
1506 let lo = var_idx as u8;
1507 self.chunk.code.push(hi);
1508 self.chunk.code.push(lo);
1509 self.chunk.lines.push(self.line);
1510 self.chunk.columns.push(self.column);
1511 self.chunk.lines.push(self.line);
1512 self.chunk.columns.push(self.column);
1513 let fc = args.len() as u16;
1514 let fhi = (fc >> 8) as u8;
1515 let flo = fc as u8;
1516 self.chunk.code.push(fhi);
1517 self.chunk.code.push(flo);
1518 self.chunk.lines.push(self.line);
1519 self.chunk.columns.push(self.column);
1520 self.chunk.lines.push(self.line);
1521 self.chunk.columns.push(self.column);
1522 }
1523
1524 Node::StructConstruct {
1525 struct_name,
1526 fields,
1527 } => {
1528 let struct_key = self
1530 .chunk
1531 .add_constant(Constant::String("__struct__".to_string()));
1532 let struct_val = self
1533 .chunk
1534 .add_constant(Constant::String(struct_name.clone()));
1535 self.chunk.emit_u16(Op::Constant, struct_key, self.line);
1536 self.chunk.emit_u16(Op::Constant, struct_val, self.line);
1537
1538 for entry in fields {
1539 self.compile_node(&entry.key)?;
1540 self.compile_node(&entry.value)?;
1541 }
1542 self.chunk
1543 .emit_u16(Op::BuildDict, (fields.len() + 1) as u16, self.line);
1544 }
1545
1546 Node::ImportDecl { path } => {
1547 let idx = self.chunk.add_constant(Constant::String(path.clone()));
1548 self.chunk.emit_u16(Op::Import, idx, self.line);
1549 }
1550
1551 Node::SelectiveImport { names, path } => {
1552 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
1553 let names_str = names.join(",");
1554 let names_idx = self.chunk.add_constant(Constant::String(names_str));
1555 self.chunk
1556 .emit_u16(Op::SelectiveImport, path_idx, self.line);
1557 let hi = (names_idx >> 8) as u8;
1558 let lo = names_idx as u8;
1559 self.chunk.code.push(hi);
1560 self.chunk.code.push(lo);
1561 self.chunk.lines.push(self.line);
1562 self.chunk.columns.push(self.column);
1563 self.chunk.lines.push(self.line);
1564 self.chunk.columns.push(self.column);
1565 }
1566
1567 Node::TryOperator { operand } => {
1568 self.compile_node(operand)?;
1569 self.chunk.emit(Op::TryUnwrap, self.line);
1570 }
1571
1572 Node::ImplBlock { type_name, methods } => {
1573 for method_sn in methods {
1576 if let Node::FnDecl {
1577 name, params, body, ..
1578 } = &method_sn.node
1579 {
1580 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
1582 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1583
1584 let mut fn_compiler = Compiler::new();
1586 fn_compiler.enum_names = self.enum_names.clone();
1587 fn_compiler.emit_default_preamble(params)?;
1588 fn_compiler.emit_type_checks(params);
1589 fn_compiler.compile_block(body)?;
1590 fn_compiler.chunk.emit(Op::Nil, self.line);
1591 fn_compiler.chunk.emit(Op::Return, self.line);
1592
1593 let func = CompiledFunction {
1594 name: format!("{}.{}", type_name, name),
1595 params: TypedParam::names(params),
1596 default_start: TypedParam::default_start(params),
1597 chunk: fn_compiler.chunk,
1598 };
1599 let fn_idx = self.chunk.functions.len();
1600 self.chunk.functions.push(func);
1601 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1602 }
1603 }
1604 let method_count = methods
1605 .iter()
1606 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
1607 .count();
1608 self.chunk
1609 .emit_u16(Op::BuildDict, method_count as u16, self.line);
1610 let impl_name = format!("__impl_{}", type_name);
1611 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
1612 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1613 }
1614
1615 Node::StructDecl { name, .. } => {
1616 let mut fn_compiler = Compiler::new();
1618 fn_compiler.enum_names = self.enum_names.clone();
1619 let params = vec![TypedParam::untyped("__fields")];
1620 fn_compiler.emit_default_preamble(¶ms)?;
1621
1622 let make_idx = fn_compiler
1624 .chunk
1625 .add_constant(Constant::String("__make_struct".into()));
1626 fn_compiler
1627 .chunk
1628 .emit_u16(Op::Constant, make_idx, self.line);
1629 let sname_idx = fn_compiler
1630 .chunk
1631 .add_constant(Constant::String(name.clone()));
1632 fn_compiler
1633 .chunk
1634 .emit_u16(Op::Constant, sname_idx, self.line);
1635 let fields_idx = fn_compiler
1636 .chunk
1637 .add_constant(Constant::String("__fields".into()));
1638 fn_compiler
1639 .chunk
1640 .emit_u16(Op::GetVar, fields_idx, self.line);
1641 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
1642 fn_compiler.chunk.emit(Op::Return, self.line);
1643
1644 let func = CompiledFunction {
1645 name: name.clone(),
1646 params: TypedParam::names(¶ms),
1647 default_start: None,
1648 chunk: fn_compiler.chunk,
1649 };
1650 let fn_idx = self.chunk.functions.len();
1651 self.chunk.functions.push(func);
1652 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1653 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1654 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1655 }
1656
1657 Node::Pipeline { .. }
1659 | Node::OverrideDecl { .. }
1660 | Node::TypeDecl { .. }
1661 | Node::EnumDecl { .. }
1662 | Node::InterfaceDecl { .. } => {
1663 self.chunk.emit(Op::Nil, self.line);
1664 }
1665
1666 Node::TryCatch {
1667 body,
1668 error_var,
1669 error_type,
1670 catch_body,
1671 finally_body,
1672 } => {
1673 let type_name = error_type.as_ref().and_then(|te| {
1675 if let harn_parser::TypeExpr::Named(name) = te {
1676 Some(name.clone())
1677 } else {
1678 None
1679 }
1680 });
1681
1682 let type_name_idx = if let Some(ref tn) = type_name {
1683 self.chunk.add_constant(Constant::String(tn.clone()))
1684 } else {
1685 self.chunk.add_constant(Constant::String(String::new()))
1686 };
1687
1688 let has_catch = !catch_body.is_empty() || error_var.is_some();
1689 let has_finally = finally_body.is_some();
1690
1691 if has_catch && has_finally {
1692 let finally_body = finally_body.as_ref().unwrap();
1694
1695 self.finally_bodies.push(finally_body.clone());
1697
1698 self.handler_depth += 1;
1700 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1701 self.emit_type_name_extra(type_name_idx);
1702
1703 self.compile_try_body(body)?;
1705
1706 self.handler_depth -= 1;
1708 self.chunk.emit(Op::PopHandler, self.line);
1709 self.compile_finally_inline(finally_body)?;
1710 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1711
1712 self.chunk.patch_jump(catch_jump);
1714 self.compile_catch_binding(error_var)?;
1715
1716 self.handler_depth += 1;
1718 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1719 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1720 self.emit_type_name_extra(empty_type);
1721
1722 self.compile_try_body(catch_body)?;
1724
1725 self.handler_depth -= 1;
1727 self.chunk.emit(Op::PopHandler, self.line);
1728 self.compile_finally_inline(finally_body)?;
1729 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
1730
1731 self.chunk.patch_jump(rethrow_jump);
1733 self.compile_rethrow_with_finally(finally_body)?;
1734
1735 self.chunk.patch_jump(end_jump);
1736 self.chunk.patch_jump(end_jump2);
1737
1738 self.finally_bodies.pop();
1739 } else if has_finally {
1740 let finally_body = finally_body.as_ref().unwrap();
1742
1743 self.finally_bodies.push(finally_body.clone());
1744
1745 self.handler_depth += 1;
1747 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1748 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1749 self.emit_type_name_extra(empty_type);
1750
1751 self.compile_try_body(body)?;
1753
1754 self.handler_depth -= 1;
1756 self.chunk.emit(Op::PopHandler, self.line);
1757 self.compile_finally_inline(finally_body)?;
1758 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1759
1760 self.chunk.patch_jump(error_jump);
1762 self.compile_rethrow_with_finally(finally_body)?;
1763
1764 self.chunk.patch_jump(end_jump);
1765
1766 self.finally_bodies.pop();
1767 } else {
1768 self.handler_depth += 1;
1772 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1773 self.emit_type_name_extra(type_name_idx);
1774
1775 self.compile_try_body(body)?;
1777
1778 self.handler_depth -= 1;
1780 self.chunk.emit(Op::PopHandler, self.line);
1781 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1782
1783 self.chunk.patch_jump(catch_jump);
1785 self.compile_catch_binding(error_var)?;
1786
1787 self.compile_try_body(catch_body)?;
1789
1790 self.chunk.patch_jump(end_jump);
1792 }
1793 }
1794
1795 Node::Retry { count, body } => {
1796 self.compile_node(count)?;
1798 let counter_name = "__retry_counter__";
1799 let counter_idx = self
1800 .chunk
1801 .add_constant(Constant::String(counter_name.to_string()));
1802 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
1803
1804 self.chunk.emit(Op::Nil, self.line);
1806 let err_name = "__retry_last_error__";
1807 let err_idx = self
1808 .chunk
1809 .add_constant(Constant::String(err_name.to_string()));
1810 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
1811
1812 let loop_start = self.chunk.current_offset();
1814
1815 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1817 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1819 let hi = (empty_type >> 8) as u8;
1820 let lo = empty_type as u8;
1821 self.chunk.code.push(hi);
1822 self.chunk.code.push(lo);
1823 self.chunk.lines.push(self.line);
1824 self.chunk.columns.push(self.column);
1825 self.chunk.lines.push(self.line);
1826 self.chunk.columns.push(self.column);
1827
1828 self.compile_block(body)?;
1830
1831 self.chunk.emit(Op::PopHandler, self.line);
1833 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1834
1835 self.chunk.patch_jump(catch_jump);
1837 self.chunk.emit(Op::Dup, self.line);
1839 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
1840 self.chunk.emit(Op::Pop, self.line);
1842
1843 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
1845 let one_idx = self.chunk.add_constant(Constant::Int(1));
1846 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
1847 self.chunk.emit(Op::Sub, self.line);
1848 self.chunk.emit(Op::Dup, self.line);
1849 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
1850
1851 let zero_idx = self.chunk.add_constant(Constant::Int(0));
1853 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
1854 self.chunk.emit(Op::Greater, self.line);
1855 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1856 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1858
1859 self.chunk.patch_jump(retry_jump);
1861 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
1863 self.chunk.emit(Op::Throw, self.line);
1864
1865 self.chunk.patch_jump(end_jump);
1866 self.chunk.emit(Op::Nil, self.line);
1868 }
1869
1870 Node::Parallel {
1871 count,
1872 variable,
1873 body,
1874 } => {
1875 self.compile_node(count)?;
1876 let mut fn_compiler = Compiler::new();
1877 fn_compiler.enum_names = self.enum_names.clone();
1878 fn_compiler.compile_block(body)?;
1879 fn_compiler.chunk.emit(Op::Return, self.line);
1880 let params = vec![variable.clone().unwrap_or_else(|| "__i__".to_string())];
1881 let func = CompiledFunction {
1882 name: "<parallel>".to_string(),
1883 params,
1884 default_start: None,
1885 chunk: fn_compiler.chunk,
1886 };
1887 let fn_idx = self.chunk.functions.len();
1888 self.chunk.functions.push(func);
1889 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1890 self.chunk.emit(Op::Parallel, self.line);
1891 }
1892
1893 Node::ParallelMap {
1894 list,
1895 variable,
1896 body,
1897 } => {
1898 self.compile_node(list)?;
1899 let mut fn_compiler = Compiler::new();
1900 fn_compiler.enum_names = self.enum_names.clone();
1901 fn_compiler.compile_block(body)?;
1902 fn_compiler.chunk.emit(Op::Return, self.line);
1903 let func = CompiledFunction {
1904 name: "<parallel_map>".to_string(),
1905 params: vec![variable.clone()],
1906 default_start: None,
1907 chunk: fn_compiler.chunk,
1908 };
1909 let fn_idx = self.chunk.functions.len();
1910 self.chunk.functions.push(func);
1911 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1912 self.chunk.emit(Op::ParallelMap, self.line);
1913 }
1914
1915 Node::SpawnExpr { body } => {
1916 let mut fn_compiler = Compiler::new();
1917 fn_compiler.enum_names = self.enum_names.clone();
1918 fn_compiler.compile_block(body)?;
1919 fn_compiler.chunk.emit(Op::Return, self.line);
1920 let func = CompiledFunction {
1921 name: "<spawn>".to_string(),
1922 params: vec![],
1923 default_start: None,
1924 chunk: fn_compiler.chunk,
1925 };
1926 let fn_idx = self.chunk.functions.len();
1927 self.chunk.functions.push(func);
1928 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1929 self.chunk.emit(Op::Spawn, self.line);
1930 }
1931 Node::SelectExpr {
1932 cases,
1933 timeout,
1934 default_body,
1935 } => {
1936 let builtin_name = if timeout.is_some() {
1943 "__select_timeout"
1944 } else if default_body.is_some() {
1945 "__select_try"
1946 } else {
1947 "__select_list"
1948 };
1949
1950 let name_idx = self
1952 .chunk
1953 .add_constant(Constant::String(builtin_name.into()));
1954 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1955
1956 for case in cases {
1958 self.compile_node(&case.channel)?;
1959 }
1960 self.chunk
1961 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
1962
1963 if let Some((duration_expr, _)) = timeout {
1965 self.compile_node(duration_expr)?;
1966 self.chunk.emit_u8(Op::Call, 2, self.line);
1967 } else {
1968 self.chunk.emit_u8(Op::Call, 1, self.line);
1969 }
1970
1971 self.temp_counter += 1;
1973 let result_name = format!("__sel_result_{}__", self.temp_counter);
1974 let result_idx = self
1975 .chunk
1976 .add_constant(Constant::String(result_name.clone()));
1977 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
1978
1979 let mut end_jumps = Vec::new();
1981
1982 for (i, case) in cases.iter().enumerate() {
1983 let get_r = self
1984 .chunk
1985 .add_constant(Constant::String(result_name.clone()));
1986 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
1987 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
1988 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
1989 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
1990 self.chunk.emit_u16(Op::Constant, case_i, self.line);
1991 self.chunk.emit(Op::Equal, self.line);
1992 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1993 self.chunk.emit(Op::Pop, self.line);
1994
1995 let get_r2 = self
1997 .chunk
1998 .add_constant(Constant::String(result_name.clone()));
1999 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2000 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2001 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2002 let var_idx = self
2003 .chunk
2004 .add_constant(Constant::String(case.variable.clone()));
2005 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2006
2007 self.compile_try_body(&case.body)?;
2008 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2009 self.chunk.patch_jump(skip);
2010 self.chunk.emit(Op::Pop, self.line);
2011 }
2012
2013 if let Some((_, ref timeout_body)) = timeout {
2015 self.compile_try_body(timeout_body)?;
2016 } else if let Some(ref def_body) = default_body {
2017 self.compile_try_body(def_body)?;
2018 } else {
2019 self.chunk.emit(Op::Nil, self.line);
2020 }
2021
2022 for ej in end_jumps {
2023 self.chunk.patch_jump(ej);
2024 }
2025 }
2026 Node::Spread(_) => {
2027 return Err(CompileError {
2028 message: "spread (...) can only be used inside list or dict literals".into(),
2029 line: self.line,
2030 });
2031 }
2032 }
2033 Ok(())
2034 }
2035
2036 fn compile_destructuring(
2040 &mut self,
2041 pattern: &BindingPattern,
2042 is_mutable: bool,
2043 ) -> Result<(), CompileError> {
2044 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2045 match pattern {
2046 BindingPattern::Identifier(name) => {
2047 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2049 self.chunk.emit_u16(def_op, idx, self.line);
2050 }
2051 BindingPattern::Dict(fields) => {
2052 self.chunk.emit(Op::Dup, self.line);
2055 let assert_idx = self
2056 .chunk
2057 .add_constant(Constant::String("__assert_dict".into()));
2058 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2059 self.chunk.emit(Op::Swap, self.line);
2060 self.chunk.emit_u8(Op::Call, 1, self.line);
2061 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2066 let rest_field = fields.iter().find(|f| f.is_rest);
2067
2068 for field in &non_rest {
2069 self.chunk.emit(Op::Dup, self.line);
2070 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2071 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2072 self.chunk.emit(Op::Subscript, self.line);
2073 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2074 let name_idx = self
2075 .chunk
2076 .add_constant(Constant::String(binding_name.to_string()));
2077 self.chunk.emit_u16(def_op, name_idx, self.line);
2078 }
2079
2080 if let Some(rest) = rest_field {
2081 let fn_idx = self
2084 .chunk
2085 .add_constant(Constant::String("__dict_rest".into()));
2086 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2087 self.chunk.emit(Op::Swap, self.line);
2089 for field in &non_rest {
2091 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2092 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2093 }
2094 self.chunk
2095 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2096 self.chunk.emit_u8(Op::Call, 2, self.line);
2098 let rest_name = &rest.key;
2099 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2100 self.chunk.emit_u16(def_op, rest_idx, self.line);
2101 } else {
2102 self.chunk.emit(Op::Pop, self.line);
2104 }
2105 }
2106 BindingPattern::List(elements) => {
2107 self.chunk.emit(Op::Dup, self.line);
2110 let assert_idx = self
2111 .chunk
2112 .add_constant(Constant::String("__assert_list".into()));
2113 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2114 self.chunk.emit(Op::Swap, self.line);
2115 self.chunk.emit_u8(Op::Call, 1, self.line);
2116 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2119 let rest_elem = elements.iter().find(|e| e.is_rest);
2120
2121 for (i, elem) in non_rest.iter().enumerate() {
2122 self.chunk.emit(Op::Dup, self.line);
2123 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2124 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2125 self.chunk.emit(Op::Subscript, self.line);
2126 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2127 self.chunk.emit_u16(def_op, name_idx, self.line);
2128 }
2129
2130 if let Some(rest) = rest_elem {
2131 let start_idx = self
2135 .chunk
2136 .add_constant(Constant::Int(non_rest.len() as i64));
2137 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2138 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2140 let rest_name_idx =
2141 self.chunk.add_constant(Constant::String(rest.name.clone()));
2142 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2143 } else {
2144 self.chunk.emit(Op::Pop, self.line);
2146 }
2147 }
2148 }
2149 Ok(())
2150 }
2151
2152 fn produces_value(node: &Node) -> bool {
2154 match node {
2155 Node::LetBinding { .. }
2157 | Node::VarBinding { .. }
2158 | Node::Assignment { .. }
2159 | Node::ReturnStmt { .. }
2160 | Node::FnDecl { .. }
2161 | Node::ImplBlock { .. }
2162 | Node::StructDecl { .. }
2163 | Node::ThrowStmt { .. }
2164 | Node::BreakStmt
2165 | Node::ContinueStmt => false,
2166 Node::TryCatch { .. }
2168 | Node::Retry { .. }
2169 | Node::GuardStmt { .. }
2170 | Node::DeadlineBlock { .. }
2171 | Node::MutexBlock { .. }
2172 | Node::Spread(_) => true,
2173 _ => true,
2175 }
2176 }
2177}
2178
2179impl Compiler {
2180 pub fn compile_fn_body(
2182 &mut self,
2183 params: &[TypedParam],
2184 body: &[SNode],
2185 ) -> Result<CompiledFunction, CompileError> {
2186 let mut fn_compiler = Compiler::new();
2187 fn_compiler.compile_block(body)?;
2188 fn_compiler.chunk.emit(Op::Nil, 0);
2189 fn_compiler.chunk.emit(Op::Return, 0);
2190 Ok(CompiledFunction {
2191 name: String::new(),
2192 params: TypedParam::names(params),
2193 default_start: TypedParam::default_start(params),
2194 chunk: fn_compiler.chunk,
2195 })
2196 }
2197
2198 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2200 if body.is_empty() {
2201 self.chunk.emit(Op::Nil, self.line);
2202 } else {
2203 self.compile_block(body)?;
2204 if !Self::produces_value(&body.last().unwrap().node) {
2206 self.chunk.emit(Op::Nil, self.line);
2207 }
2208 }
2209 Ok(())
2210 }
2211
2212 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2214 match op {
2215 "+" => self.chunk.emit(Op::Add, self.line),
2216 "-" => self.chunk.emit(Op::Sub, self.line),
2217 "*" => self.chunk.emit(Op::Mul, self.line),
2218 "/" => self.chunk.emit(Op::Div, self.line),
2219 "%" => self.chunk.emit(Op::Mod, self.line),
2220 _ => {
2221 return Err(CompileError {
2222 message: format!("Unknown compound operator: {op}"),
2223 line: self.line,
2224 })
2225 }
2226 }
2227 Ok(())
2228 }
2229
2230 fn root_var_name(&self, node: &SNode) -> Option<String> {
2232 match &node.node {
2233 Node::Identifier(name) => Some(name.clone()),
2234 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2235 self.root_var_name(object)
2236 }
2237 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2238 _ => None,
2239 }
2240 }
2241}
2242
2243impl Compiler {
2244 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
2246 for sn in nodes {
2247 match &sn.node {
2248 Node::EnumDecl { name, .. } => {
2249 names.insert(name.clone());
2250 }
2251 Node::Pipeline { body, .. } => {
2252 Self::collect_enum_names(body, names);
2253 }
2254 Node::FnDecl { body, .. } => {
2255 Self::collect_enum_names(body, names);
2256 }
2257 Node::Block(stmts) => {
2258 Self::collect_enum_names(stmts, names);
2259 }
2260 _ => {}
2261 }
2262 }
2263 }
2264}
2265
2266impl Default for Compiler {
2267 fn default() -> Self {
2268 Self::new()
2269 }
2270}
2271
2272fn contains_pipe_placeholder(node: &SNode) -> bool {
2274 match &node.node {
2275 Node::Identifier(name) if name == "_" => true,
2276 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
2277 Node::MethodCall { object, args, .. } => {
2278 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
2279 }
2280 Node::BinaryOp { left, right, .. } => {
2281 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
2282 }
2283 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
2284 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
2285 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
2286 Node::SubscriptAccess { object, index } => {
2287 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
2288 }
2289 _ => false,
2290 }
2291}
2292
2293fn replace_pipe_placeholder(node: &SNode) -> SNode {
2295 let new_node = match &node.node {
2296 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
2297 Node::FunctionCall { name, args } => Node::FunctionCall {
2298 name: name.clone(),
2299 args: args.iter().map(replace_pipe_placeholder).collect(),
2300 },
2301 Node::MethodCall {
2302 object,
2303 method,
2304 args,
2305 } => Node::MethodCall {
2306 object: Box::new(replace_pipe_placeholder(object)),
2307 method: method.clone(),
2308 args: args.iter().map(replace_pipe_placeholder).collect(),
2309 },
2310 Node::BinaryOp { op, left, right } => Node::BinaryOp {
2311 op: op.clone(),
2312 left: Box::new(replace_pipe_placeholder(left)),
2313 right: Box::new(replace_pipe_placeholder(right)),
2314 },
2315 Node::UnaryOp { op, operand } => Node::UnaryOp {
2316 op: op.clone(),
2317 operand: Box::new(replace_pipe_placeholder(operand)),
2318 },
2319 Node::ListLiteral(items) => {
2320 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
2321 }
2322 Node::PropertyAccess { object, property } => Node::PropertyAccess {
2323 object: Box::new(replace_pipe_placeholder(object)),
2324 property: property.clone(),
2325 },
2326 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
2327 object: Box::new(replace_pipe_placeholder(object)),
2328 index: Box::new(replace_pipe_placeholder(index)),
2329 },
2330 _ => return node.clone(),
2331 };
2332 SNode::new(new_node, node.span)
2333}
2334
2335#[cfg(test)]
2336mod tests {
2337 use super::*;
2338 use harn_lexer::Lexer;
2339 use harn_parser::Parser;
2340
2341 fn compile_source(source: &str) -> Chunk {
2342 let mut lexer = Lexer::new(source);
2343 let tokens = lexer.tokenize().unwrap();
2344 let mut parser = Parser::new(tokens);
2345 let program = parser.parse().unwrap();
2346 Compiler::new().compile(&program).unwrap()
2347 }
2348
2349 #[test]
2350 fn test_compile_arithmetic() {
2351 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
2352 assert!(!chunk.code.is_empty());
2353 assert!(chunk.constants.contains(&Constant::Int(2)));
2355 assert!(chunk.constants.contains(&Constant::Int(3)));
2356 }
2357
2358 #[test]
2359 fn test_compile_function_call() {
2360 let chunk = compile_source("pipeline test(task) { log(42) }");
2361 let disasm = chunk.disassemble("test");
2362 assert!(disasm.contains("CALL"));
2363 }
2364
2365 #[test]
2366 fn test_compile_if_else() {
2367 let chunk =
2368 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
2369 let disasm = chunk.disassemble("test");
2370 assert!(disasm.contains("JUMP_IF_FALSE"));
2371 assert!(disasm.contains("JUMP"));
2372 }
2373
2374 #[test]
2375 fn test_compile_while() {
2376 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
2377 let disasm = chunk.disassemble("test");
2378 assert!(disasm.contains("JUMP_IF_FALSE"));
2379 assert!(disasm.contains("JUMP"));
2381 }
2382
2383 #[test]
2384 fn test_compile_closure() {
2385 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
2386 assert!(!chunk.functions.is_empty());
2387 assert_eq!(chunk.functions[0].params, vec!["x"]);
2388 }
2389
2390 #[test]
2391 fn test_compile_list() {
2392 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
2393 let disasm = chunk.disassemble("test");
2394 assert!(disasm.contains("BUILD_LIST"));
2395 }
2396
2397 #[test]
2398 fn test_compile_dict() {
2399 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
2400 let disasm = chunk.disassemble("test");
2401 assert!(disasm.contains("BUILD_DICT"));
2402 }
2403
2404 #[test]
2405 fn test_disassemble() {
2406 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
2407 let disasm = chunk.disassemble("test");
2408 assert!(disasm.contains("CONSTANT"));
2410 assert!(disasm.contains("ADD"));
2411 assert!(disasm.contains("CALL"));
2412 }
2413}