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