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]) {
207 for param in params {
208 if let Some(type_expr) = ¶m.type_expr {
209 if let harn_parser::TypeExpr::Shape(fields) = type_expr {
211 let spec = Self::shape_to_spec_string(fields);
212 let fn_idx = self
214 .chunk
215 .add_constant(Constant::String("__assert_shape".into()));
216 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
217 let var_idx = self
218 .chunk
219 .add_constant(Constant::String(param.name.clone()));
220 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
221 let name_idx = self
222 .chunk
223 .add_constant(Constant::String(param.name.clone()));
224 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
225 let spec_idx = self.chunk.add_constant(Constant::String(spec));
226 self.chunk.emit_u16(Op::Constant, spec_idx, self.line);
227 self.chunk.emit_u8(Op::Call, 3, self.line);
228 self.chunk.emit(Op::Pop, self.line);
229 continue;
230 }
231
232 let type_name = Self::type_expr_to_runtime_name(type_expr);
233 if let Some(type_name) = type_name {
234 let var_idx = self
235 .chunk
236 .add_constant(Constant::String(param.name.clone()));
237 let type_idx = self.chunk.add_constant(Constant::String(type_name));
238 self.chunk.emit_u16(Op::CheckType, var_idx, self.line);
239 let hi = (type_idx >> 8) as u8;
241 let lo = type_idx as u8;
242 self.chunk.code.push(hi);
243 self.chunk.code.push(lo);
244 }
245 }
246 }
247 }
248
249 fn shape_to_spec_string(fields: &[harn_parser::ShapeField]) -> String {
252 fields
253 .iter()
254 .map(|f| {
255 let opt = if f.optional { "?" } else { "" };
256 let type_str = Self::type_expr_to_spec(&f.type_expr);
257 format!("{}:{}{}", f.name, opt, type_str)
258 })
259 .collect::<Vec<_>>()
260 .join(",")
261 }
262
263 fn type_expr_to_spec(type_expr: &harn_parser::TypeExpr) -> String {
265 match type_expr {
266 harn_parser::TypeExpr::Named(name) => name.clone(),
267 harn_parser::TypeExpr::Shape(fields) => {
268 let inner = Self::shape_to_spec_string(fields);
269 format!("{{{}}}", inner)
270 }
271 harn_parser::TypeExpr::List(_) => "list".to_string(),
272 harn_parser::TypeExpr::DictType(_, _) => "dict".to_string(),
273 harn_parser::TypeExpr::Union(_) => {
274 "any".to_string()
276 }
277 harn_parser::TypeExpr::FnType { .. } => "closure".to_string(),
278 }
279 }
280
281 fn type_expr_to_runtime_name(type_expr: &harn_parser::TypeExpr) -> Option<String> {
283 match type_expr {
284 harn_parser::TypeExpr::Named(name) => match name.as_str() {
285 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
286 | "closure" => Some(name.clone()),
287 _ => None, },
289 _ => None, }
291 }
292
293 fn emit_type_name_extra(&mut self, type_name_idx: u16) {
295 let hi = (type_name_idx >> 8) as u8;
296 let lo = type_name_idx as u8;
297 self.chunk.code.push(hi);
298 self.chunk.code.push(lo);
299 self.chunk.lines.push(self.line);
300 self.chunk.columns.push(self.column);
301 self.chunk.lines.push(self.line);
302 self.chunk.columns.push(self.column);
303 }
304
305 fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
307 if body.is_empty() {
308 self.chunk.emit(Op::Nil, self.line);
309 } else {
310 self.compile_block(body)?;
311 if !Self::produces_value(&body.last().unwrap().node) {
312 self.chunk.emit(Op::Nil, self.line);
313 }
314 }
315 Ok(())
316 }
317
318 fn compile_catch_binding(&mut self, error_var: &Option<String>) -> Result<(), CompileError> {
320 if let Some(var_name) = error_var {
321 let idx = self.chunk.add_constant(Constant::String(var_name.clone()));
322 self.chunk.emit_u16(Op::DefLet, idx, self.line);
323 } else {
324 self.chunk.emit(Op::Pop, self.line);
325 }
326 Ok(())
327 }
328
329 fn compile_finally_inline(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
331 if !finally_body.is_empty() {
332 self.compile_block(finally_body)?;
333 if Self::produces_value(&finally_body.last().unwrap().node) {
335 self.chunk.emit(Op::Pop, self.line);
336 }
337 }
338 Ok(())
339 }
340
341 fn compile_rethrow_with_finally(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
343 self.temp_counter += 1;
345 let temp_name = format!("__finally_err_{}__", self.temp_counter);
346 let err_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
347 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
348 self.compile_finally_inline(finally_body)?;
349 let get_idx = self.chunk.add_constant(Constant::String(temp_name));
350 self.chunk.emit_u16(Op::GetVar, get_idx, self.line);
351 self.chunk.emit(Op::Throw, self.line);
352 Ok(())
353 }
354
355 fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
356 for (i, snode) in stmts.iter().enumerate() {
357 self.compile_node(snode)?;
358 let is_last = i == stmts.len() - 1;
359 if is_last {
360 if !Self::produces_value(&snode.node) {
363 self.chunk.emit(Op::Nil, self.line);
364 }
365 } else {
366 if Self::produces_value(&snode.node) {
368 self.chunk.emit(Op::Pop, self.line);
369 }
370 }
371 }
372 Ok(())
373 }
374
375 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
376 self.line = snode.span.line as u32;
377 self.column = snode.span.column as u32;
378 self.chunk.set_column(self.column);
379 match &snode.node {
380 Node::IntLiteral(n) => {
381 let idx = self.chunk.add_constant(Constant::Int(*n));
382 self.chunk.emit_u16(Op::Constant, idx, self.line);
383 }
384 Node::FloatLiteral(n) => {
385 let idx = self.chunk.add_constant(Constant::Float(*n));
386 self.chunk.emit_u16(Op::Constant, idx, self.line);
387 }
388 Node::StringLiteral(s) => {
389 let idx = self.chunk.add_constant(Constant::String(s.clone()));
390 self.chunk.emit_u16(Op::Constant, idx, self.line);
391 }
392 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
393 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
394 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
395 Node::DurationLiteral(ms) => {
396 let idx = self.chunk.add_constant(Constant::Duration(*ms));
397 self.chunk.emit_u16(Op::Constant, idx, self.line);
398 }
399
400 Node::Identifier(name) => {
401 let idx = self.chunk.add_constant(Constant::String(name.clone()));
402 self.chunk.emit_u16(Op::GetVar, idx, self.line);
403 }
404
405 Node::LetBinding { pattern, value, .. } => {
406 self.compile_node(value)?;
407 self.compile_destructuring(pattern, false)?;
408 }
409
410 Node::VarBinding { pattern, value, .. } => {
411 self.compile_node(value)?;
412 self.compile_destructuring(pattern, true)?;
413 }
414
415 Node::Assignment {
416 target, value, op, ..
417 } => {
418 if let Node::Identifier(name) = &target.node {
419 let idx = self.chunk.add_constant(Constant::String(name.clone()));
420 if let Some(op) = op {
421 self.chunk.emit_u16(Op::GetVar, idx, self.line);
422 self.compile_node(value)?;
423 self.emit_compound_op(op)?;
424 self.chunk.emit_u16(Op::SetVar, idx, self.line);
425 } else {
426 self.compile_node(value)?;
427 self.chunk.emit_u16(Op::SetVar, idx, self.line);
428 }
429 } else if let Node::PropertyAccess { object, property } = &target.node {
430 if let Some(var_name) = self.root_var_name(object) {
432 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
433 let prop_idx = self.chunk.add_constant(Constant::String(property.clone()));
434 if let Some(op) = op {
435 self.compile_node(target)?; self.compile_node(value)?;
438 self.emit_compound_op(op)?;
439 } else {
440 self.compile_node(value)?;
441 }
442 self.chunk.emit_u16(Op::SetProperty, prop_idx, self.line);
445 let hi = (var_idx >> 8) as u8;
447 let lo = var_idx as u8;
448 self.chunk.code.push(hi);
449 self.chunk.code.push(lo);
450 self.chunk.lines.push(self.line);
451 self.chunk.columns.push(self.column);
452 self.chunk.lines.push(self.line);
453 self.chunk.columns.push(self.column);
454 }
455 } else if let Node::SubscriptAccess { object, index } = &target.node {
456 if let Some(var_name) = self.root_var_name(object) {
458 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
459 if let Some(op) = op {
460 self.compile_node(target)?;
461 self.compile_node(value)?;
462 self.emit_compound_op(op)?;
463 } else {
464 self.compile_node(value)?;
465 }
466 self.compile_node(index)?;
467 self.chunk.emit_u16(Op::SetSubscript, var_idx, self.line);
468 }
469 }
470 }
471
472 Node::BinaryOp { op, left, right } => {
473 match op.as_str() {
475 "&&" => {
476 self.compile_node(left)?;
477 let jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
478 self.chunk.emit(Op::Pop, self.line);
479 self.compile_node(right)?;
480 self.chunk.patch_jump(jump);
481 self.chunk.emit(Op::Not, self.line);
483 self.chunk.emit(Op::Not, self.line);
484 return Ok(());
485 }
486 "||" => {
487 self.compile_node(left)?;
488 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
489 self.chunk.emit(Op::Pop, self.line);
490 self.compile_node(right)?;
491 self.chunk.patch_jump(jump);
492 self.chunk.emit(Op::Not, self.line);
493 self.chunk.emit(Op::Not, self.line);
494 return Ok(());
495 }
496 "??" => {
497 self.compile_node(left)?;
498 self.chunk.emit(Op::Dup, self.line);
499 self.chunk.emit(Op::Nil, self.line);
501 self.chunk.emit(Op::NotEqual, self.line);
502 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
503 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(right)?;
506 let end = self.chunk.emit_jump(Op::Jump, self.line);
507 self.chunk.patch_jump(jump);
508 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
510 return Ok(());
511 }
512 "|>" => {
513 self.compile_node(left)?;
514 if contains_pipe_placeholder(right) {
517 let replaced = replace_pipe_placeholder(right);
518 let closure_node = SNode::dummy(Node::Closure {
519 params: vec![TypedParam {
520 name: "__pipe".into(),
521 type_expr: None,
522 default_value: None,
523 }],
524 body: vec![replaced],
525 });
526 self.compile_node(&closure_node)?;
527 } else {
528 self.compile_node(right)?;
529 }
530 self.chunk.emit(Op::Pipe, self.line);
531 return Ok(());
532 }
533 _ => {}
534 }
535
536 self.compile_node(left)?;
537 self.compile_node(right)?;
538 match op.as_str() {
539 "+" => self.chunk.emit(Op::Add, self.line),
540 "-" => self.chunk.emit(Op::Sub, self.line),
541 "*" => self.chunk.emit(Op::Mul, self.line),
542 "/" => self.chunk.emit(Op::Div, self.line),
543 "%" => self.chunk.emit(Op::Mod, self.line),
544 "==" => self.chunk.emit(Op::Equal, self.line),
545 "!=" => self.chunk.emit(Op::NotEqual, self.line),
546 "<" => self.chunk.emit(Op::Less, self.line),
547 ">" => self.chunk.emit(Op::Greater, self.line),
548 "<=" => self.chunk.emit(Op::LessEqual, self.line),
549 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
550 _ => {
551 return Err(CompileError {
552 message: format!("Unknown operator: {op}"),
553 line: self.line,
554 })
555 }
556 }
557 }
558
559 Node::UnaryOp { op, operand } => {
560 self.compile_node(operand)?;
561 match op.as_str() {
562 "-" => self.chunk.emit(Op::Negate, self.line),
563 "!" => self.chunk.emit(Op::Not, self.line),
564 _ => {}
565 }
566 }
567
568 Node::Ternary {
569 condition,
570 true_expr,
571 false_expr,
572 } => {
573 self.compile_node(condition)?;
574 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
575 self.chunk.emit(Op::Pop, self.line);
576 self.compile_node(true_expr)?;
577 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
578 self.chunk.patch_jump(else_jump);
579 self.chunk.emit(Op::Pop, self.line);
580 self.compile_node(false_expr)?;
581 self.chunk.patch_jump(end_jump);
582 }
583
584 Node::FunctionCall { name, args } => {
585 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
586 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
588 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
589
590 if has_spread {
591 self.chunk.emit_u16(Op::BuildList, 0, self.line);
594 let mut pending = 0u16;
595 for arg in args {
596 if let Node::Spread(inner) = &arg.node {
597 if pending > 0 {
598 self.chunk.emit_u16(Op::BuildList, pending, self.line);
599 self.chunk.emit(Op::Add, self.line);
600 pending = 0;
601 }
602 self.compile_node(inner)?;
603 self.chunk.emit(Op::Dup, self.line);
604 let assert_idx = self
605 .chunk
606 .add_constant(Constant::String("__assert_list".into()));
607 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
608 self.chunk.emit(Op::Swap, self.line);
609 self.chunk.emit_u8(Op::Call, 1, self.line);
610 self.chunk.emit(Op::Pop, self.line);
611 self.chunk.emit(Op::Add, self.line);
612 } else {
613 self.compile_node(arg)?;
614 pending += 1;
615 }
616 }
617 if pending > 0 {
618 self.chunk.emit_u16(Op::BuildList, pending, self.line);
619 self.chunk.emit(Op::Add, self.line);
620 }
621 self.chunk.emit(Op::CallSpread, self.line);
622 } else {
623 for arg in args {
625 self.compile_node(arg)?;
626 }
627 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
628 }
629 }
630
631 Node::MethodCall {
632 object,
633 method,
634 args,
635 } => {
636 if let Node::Identifier(name) = &object.node {
638 if self.enum_names.contains(name) {
639 for arg in args {
641 self.compile_node(arg)?;
642 }
643 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
644 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
645 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
646 let hi = (var_idx >> 8) as u8;
647 let lo = var_idx as u8;
648 self.chunk.code.push(hi);
649 self.chunk.code.push(lo);
650 self.chunk.lines.push(self.line);
651 self.chunk.columns.push(self.column);
652 self.chunk.lines.push(self.line);
653 self.chunk.columns.push(self.column);
654 let fc = args.len() as u16;
655 let fhi = (fc >> 8) as u8;
656 let flo = fc as u8;
657 self.chunk.code.push(fhi);
658 self.chunk.code.push(flo);
659 self.chunk.lines.push(self.line);
660 self.chunk.columns.push(self.column);
661 self.chunk.lines.push(self.line);
662 self.chunk.columns.push(self.column);
663 return Ok(());
664 }
665 }
666 self.compile_node(object)?;
667 for arg in args {
668 self.compile_node(arg)?;
669 }
670 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
671 self.chunk
672 .emit_method_call(name_idx, args.len() as u8, self.line);
673 }
674
675 Node::OptionalMethodCall {
676 object,
677 method,
678 args,
679 } => {
680 self.compile_node(object)?;
681 for arg in args {
682 self.compile_node(arg)?;
683 }
684 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
685 self.chunk
686 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
687 }
688
689 Node::PropertyAccess { object, property } => {
690 if let Node::Identifier(name) = &object.node {
692 if self.enum_names.contains(name) {
693 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
695 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
696 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
697 let hi = (var_idx >> 8) as u8;
698 let lo = var_idx as u8;
699 self.chunk.code.push(hi);
700 self.chunk.code.push(lo);
701 self.chunk.lines.push(self.line);
702 self.chunk.columns.push(self.column);
703 self.chunk.lines.push(self.line);
704 self.chunk.columns.push(self.column);
705 self.chunk.code.push(0);
707 self.chunk.code.push(0);
708 self.chunk.lines.push(self.line);
709 self.chunk.columns.push(self.column);
710 self.chunk.lines.push(self.line);
711 self.chunk.columns.push(self.column);
712 return Ok(());
713 }
714 }
715 self.compile_node(object)?;
716 let idx = self.chunk.add_constant(Constant::String(property.clone()));
717 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
718 }
719
720 Node::OptionalPropertyAccess { object, property } => {
721 self.compile_node(object)?;
722 let idx = self.chunk.add_constant(Constant::String(property.clone()));
723 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
724 }
725
726 Node::SubscriptAccess { object, index } => {
727 self.compile_node(object)?;
728 self.compile_node(index)?;
729 self.chunk.emit(Op::Subscript, self.line);
730 }
731
732 Node::SliceAccess { object, start, end } => {
733 self.compile_node(object)?;
734 if let Some(s) = start {
735 self.compile_node(s)?;
736 } else {
737 self.chunk.emit(Op::Nil, self.line);
738 }
739 if let Some(e) = end {
740 self.compile_node(e)?;
741 } else {
742 self.chunk.emit(Op::Nil, self.line);
743 }
744 self.chunk.emit(Op::Slice, self.line);
745 }
746
747 Node::IfElse {
748 condition,
749 then_body,
750 else_body,
751 } => {
752 self.compile_node(condition)?;
753 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
754 self.chunk.emit(Op::Pop, self.line);
755 self.compile_block(then_body)?;
756 if let Some(else_body) = else_body {
757 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
758 self.chunk.patch_jump(else_jump);
759 self.chunk.emit(Op::Pop, self.line);
760 self.compile_block(else_body)?;
761 self.chunk.patch_jump(end_jump);
762 } else {
763 self.chunk.patch_jump(else_jump);
764 self.chunk.emit(Op::Pop, self.line);
765 self.chunk.emit(Op::Nil, self.line);
766 }
767 }
768
769 Node::WhileLoop { condition, body } => {
770 let loop_start = self.chunk.current_offset();
771 self.loop_stack.push(LoopContext {
772 start_offset: loop_start,
773 break_patches: Vec::new(),
774 has_iterator: false,
775 handler_depth: self.handler_depth,
776 finally_depth: self.finally_bodies.len(),
777 });
778 self.compile_node(condition)?;
779 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
780 self.chunk.emit(Op::Pop, self.line); for sn in body {
783 self.compile_node(sn)?;
784 if Self::produces_value(&sn.node) {
785 self.chunk.emit(Op::Pop, self.line);
786 }
787 }
788 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
790 self.chunk.patch_jump(exit_jump);
791 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
794 for patch_pos in ctx.break_patches {
795 self.chunk.patch_jump(patch_pos);
796 }
797 self.chunk.emit(Op::Nil, self.line);
798 }
799
800 Node::ForIn {
801 pattern,
802 iterable,
803 body,
804 } => {
805 self.compile_node(iterable)?;
807 self.chunk.emit(Op::IterInit, self.line);
809 let loop_start = self.chunk.current_offset();
810 self.loop_stack.push(LoopContext {
811 start_offset: loop_start,
812 break_patches: Vec::new(),
813 has_iterator: true,
814 handler_depth: self.handler_depth,
815 finally_depth: self.finally_bodies.len(),
816 });
817 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
819 self.compile_destructuring(pattern, true)?;
821 for sn in body {
823 self.compile_node(sn)?;
824 if Self::produces_value(&sn.node) {
825 self.chunk.emit(Op::Pop, self.line);
826 }
827 }
828 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
830 self.chunk.patch_jump(exit_jump_pos);
831 let ctx = self.loop_stack.pop().unwrap();
833 for patch_pos in ctx.break_patches {
834 self.chunk.patch_jump(patch_pos);
835 }
836 self.chunk.emit(Op::Nil, self.line);
838 }
839
840 Node::ReturnStmt { value } => {
841 let has_pending_finally = !self.finally_bodies.is_empty();
842
843 if has_pending_finally {
844 if let Some(val) = value {
847 self.compile_node(val)?;
848 } else {
849 self.chunk.emit(Op::Nil, self.line);
850 }
851 self.temp_counter += 1;
852 let temp_name = format!("__return_val_{}__", self.temp_counter);
853 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
854 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
855 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
857 for fb in &finallys {
858 self.compile_finally_inline(fb)?;
859 }
860 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
861 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
862 self.chunk.emit(Op::Return, self.line);
863 } else {
864 if let Some(val) = value {
866 if let Node::FunctionCall { name, args } = &val.node {
867 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
868 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
869 for arg in args {
870 self.compile_node(arg)?;
871 }
872 self.chunk
873 .emit_u8(Op::TailCall, args.len() as u8, self.line);
874 } else if let Node::BinaryOp { op, left, right } = &val.node {
875 if op == "|>" {
876 self.compile_node(left)?;
877 self.compile_node(right)?;
878 self.chunk.emit(Op::Swap, self.line);
879 self.chunk.emit_u8(Op::TailCall, 1, self.line);
880 } else {
881 self.compile_node(val)?;
882 }
883 } else {
884 self.compile_node(val)?;
885 }
886 } else {
887 self.chunk.emit(Op::Nil, self.line);
888 }
889 self.chunk.emit(Op::Return, self.line);
890 }
891 }
892
893 Node::BreakStmt => {
894 if self.loop_stack.is_empty() {
895 return Err(CompileError {
896 message: "break outside of loop".to_string(),
897 line: self.line,
898 });
899 }
900 let ctx = self.loop_stack.last().unwrap();
902 let finally_depth = ctx.finally_depth;
903 let handler_depth = ctx.handler_depth;
904 let has_iterator = ctx.has_iterator;
905 for _ in handler_depth..self.handler_depth {
907 self.chunk.emit(Op::PopHandler, self.line);
908 }
909 if self.finally_bodies.len() > finally_depth {
911 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
912 .iter()
913 .rev()
914 .cloned()
915 .collect();
916 for fb in &finallys {
917 self.compile_finally_inline(fb)?;
918 }
919 }
920 if has_iterator {
921 self.chunk.emit(Op::PopIterator, self.line);
922 }
923 let patch = self.chunk.emit_jump(Op::Jump, self.line);
924 self.loop_stack
925 .last_mut()
926 .unwrap()
927 .break_patches
928 .push(patch);
929 }
930
931 Node::ContinueStmt => {
932 if self.loop_stack.is_empty() {
933 return Err(CompileError {
934 message: "continue outside of loop".to_string(),
935 line: self.line,
936 });
937 }
938 let ctx = self.loop_stack.last().unwrap();
939 let finally_depth = ctx.finally_depth;
940 let handler_depth = ctx.handler_depth;
941 let loop_start = ctx.start_offset;
942 for _ in handler_depth..self.handler_depth {
943 self.chunk.emit(Op::PopHandler, self.line);
944 }
945 if self.finally_bodies.len() > finally_depth {
946 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
947 .iter()
948 .rev()
949 .cloned()
950 .collect();
951 for fb in &finallys {
952 self.compile_finally_inline(fb)?;
953 }
954 }
955 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
956 }
957
958 Node::ListLiteral(elements) => {
959 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
960 if !has_spread {
961 for el in elements {
962 self.compile_node(el)?;
963 }
964 self.chunk
965 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
966 } else {
967 self.chunk.emit_u16(Op::BuildList, 0, self.line);
970 let mut pending = 0u16;
971 for el in elements {
972 if let Node::Spread(inner) = &el.node {
973 if pending > 0 {
975 self.chunk.emit_u16(Op::BuildList, pending, self.line);
976 self.chunk.emit(Op::Add, self.line);
978 pending = 0;
979 }
980 self.compile_node(inner)?;
982 self.chunk.emit(Op::Dup, self.line);
983 let assert_idx = self
984 .chunk
985 .add_constant(Constant::String("__assert_list".into()));
986 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
987 self.chunk.emit(Op::Swap, self.line);
988 self.chunk.emit_u8(Op::Call, 1, self.line);
989 self.chunk.emit(Op::Pop, self.line);
990 self.chunk.emit(Op::Add, self.line);
991 } else {
992 self.compile_node(el)?;
993 pending += 1;
994 }
995 }
996 if pending > 0 {
997 self.chunk.emit_u16(Op::BuildList, pending, self.line);
998 self.chunk.emit(Op::Add, self.line);
999 }
1000 }
1001 }
1002
1003 Node::DictLiteral(entries) => {
1004 let has_spread = entries
1005 .iter()
1006 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1007 if !has_spread {
1008 for entry in entries {
1009 self.compile_node(&entry.key)?;
1010 self.compile_node(&entry.value)?;
1011 }
1012 self.chunk
1013 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1014 } else {
1015 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1017 let mut pending = 0u16;
1018 for entry in entries {
1019 if let Node::Spread(inner) = &entry.value.node {
1020 if pending > 0 {
1022 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1023 self.chunk.emit(Op::Add, self.line);
1024 pending = 0;
1025 }
1026 self.compile_node(inner)?;
1028 self.chunk.emit(Op::Dup, self.line);
1029 let assert_idx = self
1030 .chunk
1031 .add_constant(Constant::String("__assert_dict".into()));
1032 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1033 self.chunk.emit(Op::Swap, self.line);
1034 self.chunk.emit_u8(Op::Call, 1, self.line);
1035 self.chunk.emit(Op::Pop, self.line);
1036 self.chunk.emit(Op::Add, self.line);
1037 } else {
1038 self.compile_node(&entry.key)?;
1039 self.compile_node(&entry.value)?;
1040 pending += 1;
1041 }
1042 }
1043 if pending > 0 {
1044 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1045 self.chunk.emit(Op::Add, self.line);
1046 }
1047 }
1048 }
1049
1050 Node::InterpolatedString(segments) => {
1051 let mut part_count = 0u16;
1052 for seg in segments {
1053 match seg {
1054 StringSegment::Literal(s) => {
1055 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1056 self.chunk.emit_u16(Op::Constant, idx, self.line);
1057 part_count += 1;
1058 }
1059 StringSegment::Expression(expr_str) => {
1060 let mut lexer = harn_lexer::Lexer::new(expr_str);
1062 if let Ok(tokens) = lexer.tokenize() {
1063 let mut parser = harn_parser::Parser::new(tokens);
1064 if let Ok(snode) = parser.parse_single_expression() {
1065 self.compile_node(&snode)?;
1066 let to_str = self
1068 .chunk
1069 .add_constant(Constant::String("to_string".into()));
1070 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1071 self.chunk.emit(Op::Swap, self.line);
1072 self.chunk.emit_u8(Op::Call, 1, self.line);
1073 part_count += 1;
1074 } else {
1075 let idx =
1077 self.chunk.add_constant(Constant::String(expr_str.clone()));
1078 self.chunk.emit_u16(Op::Constant, idx, self.line);
1079 part_count += 1;
1080 }
1081 }
1082 }
1083 }
1084 }
1085 if part_count > 1 {
1086 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1087 }
1088 }
1089
1090 Node::FnDecl {
1091 name, params, body, ..
1092 } => {
1093 let mut fn_compiler = Compiler::new();
1095 fn_compiler.enum_names = self.enum_names.clone();
1096 fn_compiler.emit_default_preamble(params)?;
1097 fn_compiler.emit_type_checks(params);
1098 fn_compiler.compile_block(body)?;
1099 fn_compiler.chunk.emit(Op::Nil, self.line);
1100 fn_compiler.chunk.emit(Op::Return, self.line);
1101
1102 let func = CompiledFunction {
1103 name: name.clone(),
1104 params: TypedParam::names(params),
1105 default_start: TypedParam::default_start(params),
1106 chunk: fn_compiler.chunk,
1107 };
1108 let fn_idx = self.chunk.functions.len();
1109 self.chunk.functions.push(func);
1110
1111 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1112 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1113 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1114 }
1115
1116 Node::Closure { params, body } => {
1117 let mut fn_compiler = Compiler::new();
1118 fn_compiler.enum_names = self.enum_names.clone();
1119 fn_compiler.emit_default_preamble(params)?;
1120 fn_compiler.emit_type_checks(params);
1121 fn_compiler.compile_block(body)?;
1122 fn_compiler.chunk.emit(Op::Return, self.line);
1124
1125 let func = CompiledFunction {
1126 name: "<closure>".to_string(),
1127 params: TypedParam::names(params),
1128 default_start: TypedParam::default_start(params),
1129 chunk: fn_compiler.chunk,
1130 };
1131 let fn_idx = self.chunk.functions.len();
1132 self.chunk.functions.push(func);
1133
1134 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1135 }
1136
1137 Node::ThrowStmt { value } => {
1138 self.compile_node(value)?;
1139 self.chunk.emit(Op::Throw, self.line);
1140 }
1141
1142 Node::MatchExpr { value, arms } => {
1143 self.compile_node(value)?;
1144 let mut end_jumps = Vec::new();
1145 for arm in arms {
1146 match &arm.pattern.node {
1147 Node::Identifier(name) if name == "_" => {
1149 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1151 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1152 }
1153 Node::EnumConstruct {
1155 enum_name,
1156 variant,
1157 args: pat_args,
1158 } => {
1159 self.chunk.emit(Op::Dup, self.line);
1161 let en_idx =
1162 self.chunk.add_constant(Constant::String(enum_name.clone()));
1163 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1164 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1165 let hi = (vn_idx >> 8) as u8;
1166 let lo = vn_idx as u8;
1167 self.chunk.code.push(hi);
1168 self.chunk.code.push(lo);
1169 self.chunk.lines.push(self.line);
1170 self.chunk.columns.push(self.column);
1171 self.chunk.lines.push(self.line);
1172 self.chunk.columns.push(self.column);
1173 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1175 self.chunk.emit(Op::Pop, self.line); for (i, pat_arg) in pat_args.iter().enumerate() {
1180 if let Node::Identifier(binding_name) = &pat_arg.node {
1181 self.chunk.emit(Op::Dup, self.line);
1183 let fields_idx = self
1184 .chunk
1185 .add_constant(Constant::String("fields".to_string()));
1186 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1187 let idx_const =
1188 self.chunk.add_constant(Constant::Int(i as i64));
1189 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1190 self.chunk.emit(Op::Subscript, self.line);
1191 let name_idx = self
1192 .chunk
1193 .add_constant(Constant::String(binding_name.clone()));
1194 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1195 }
1196 }
1197
1198 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1200 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1201 self.chunk.patch_jump(skip);
1202 self.chunk.emit(Op::Pop, self.line); }
1204 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1206 {
1207 let enum_name = if let Node::Identifier(n) = &object.node {
1208 n.clone()
1209 } else {
1210 unreachable!()
1211 };
1212 self.chunk.emit(Op::Dup, self.line);
1213 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1214 let vn_idx =
1215 self.chunk.add_constant(Constant::String(property.clone()));
1216 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1217 let hi = (vn_idx >> 8) as u8;
1218 let lo = vn_idx as u8;
1219 self.chunk.code.push(hi);
1220 self.chunk.code.push(lo);
1221 self.chunk.lines.push(self.line);
1222 self.chunk.columns.push(self.column);
1223 self.chunk.lines.push(self.line);
1224 self.chunk.columns.push(self.column);
1225 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1226 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1229 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1230 self.chunk.patch_jump(skip);
1231 self.chunk.emit(Op::Pop, self.line); }
1233 Node::MethodCall {
1236 object,
1237 method,
1238 args: pat_args,
1239 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1240 {
1241 let enum_name = if let Node::Identifier(n) = &object.node {
1242 n.clone()
1243 } else {
1244 unreachable!()
1245 };
1246 self.chunk.emit(Op::Dup, self.line);
1248 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1249 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1250 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1251 let hi = (vn_idx >> 8) as u8;
1252 let lo = vn_idx as u8;
1253 self.chunk.code.push(hi);
1254 self.chunk.code.push(lo);
1255 self.chunk.lines.push(self.line);
1256 self.chunk.columns.push(self.column);
1257 self.chunk.lines.push(self.line);
1258 self.chunk.columns.push(self.column);
1259 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1260 self.chunk.emit(Op::Pop, self.line); for (i, pat_arg) in pat_args.iter().enumerate() {
1264 if let Node::Identifier(binding_name) = &pat_arg.node {
1265 self.chunk.emit(Op::Dup, self.line);
1266 let fields_idx = self
1267 .chunk
1268 .add_constant(Constant::String("fields".to_string()));
1269 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1270 let idx_const =
1271 self.chunk.add_constant(Constant::Int(i as i64));
1272 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1273 self.chunk.emit(Op::Subscript, self.line);
1274 let name_idx = self
1275 .chunk
1276 .add_constant(Constant::String(binding_name.clone()));
1277 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1278 }
1279 }
1280
1281 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1283 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1284 self.chunk.patch_jump(skip);
1285 self.chunk.emit(Op::Pop, self.line); }
1287 Node::Identifier(name) => {
1289 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1292 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1293 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1295 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1296 }
1297 Node::DictLiteral(entries)
1299 if entries
1300 .iter()
1301 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1302 {
1303 self.chunk.emit(Op::Dup, self.line);
1305 let typeof_idx =
1306 self.chunk.add_constant(Constant::String("type_of".into()));
1307 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1308 self.chunk.emit(Op::Swap, self.line);
1309 self.chunk.emit_u8(Op::Call, 1, self.line);
1310 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1311 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1312 self.chunk.emit(Op::Equal, self.line);
1313 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1314 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1318 let mut bindings = Vec::new();
1319 for entry in entries {
1320 if let Node::StringLiteral(key) = &entry.key.node {
1321 match &entry.value.node {
1322 Node::StringLiteral(_)
1324 | Node::IntLiteral(_)
1325 | Node::FloatLiteral(_)
1326 | Node::BoolLiteral(_)
1327 | Node::NilLiteral => {
1328 self.chunk.emit(Op::Dup, self.line);
1329 let key_idx = self
1330 .chunk
1331 .add_constant(Constant::String(key.clone()));
1332 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1333 self.chunk.emit(Op::Subscript, self.line);
1334 self.compile_node(&entry.value)?;
1335 self.chunk.emit(Op::Equal, self.line);
1336 let skip =
1337 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1338 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1340 }
1341 Node::Identifier(binding) => {
1343 bindings.push((key.clone(), binding.clone()));
1344 }
1345 _ => {
1346 self.chunk.emit(Op::Dup, self.line);
1348 let key_idx = self
1349 .chunk
1350 .add_constant(Constant::String(key.clone()));
1351 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1352 self.chunk.emit(Op::Subscript, self.line);
1353 self.compile_node(&entry.value)?;
1354 self.chunk.emit(Op::Equal, self.line);
1355 let skip =
1356 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1357 self.chunk.emit(Op::Pop, self.line);
1358 constraint_skips.push(skip);
1359 }
1360 }
1361 }
1362 }
1363
1364 for (key, binding) in &bindings {
1366 self.chunk.emit(Op::Dup, self.line);
1367 let key_idx =
1368 self.chunk.add_constant(Constant::String(key.clone()));
1369 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1370 self.chunk.emit(Op::Subscript, self.line);
1371 let name_idx =
1372 self.chunk.add_constant(Constant::String(binding.clone()));
1373 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1374 }
1375
1376 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1378 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1379
1380 let fail_target = self.chunk.code.len();
1382 self.chunk.emit(Op::Pop, self.line); for skip in constraint_skips {
1385 self.chunk.patch_jump_to(skip, fail_target);
1386 }
1387 self.chunk.patch_jump_to(skip_type, fail_target);
1388 }
1389 Node::ListLiteral(elements) => {
1391 self.chunk.emit(Op::Dup, self.line);
1393 let typeof_idx =
1394 self.chunk.add_constant(Constant::String("type_of".into()));
1395 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1396 self.chunk.emit(Op::Swap, self.line);
1397 self.chunk.emit_u8(Op::Call, 1, self.line);
1398 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1399 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1400 self.chunk.emit(Op::Equal, self.line);
1401 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1402 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1406 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1407 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1408 self.chunk.emit(Op::Swap, self.line);
1409 self.chunk.emit_u8(Op::Call, 1, self.line);
1410 let count = self
1411 .chunk
1412 .add_constant(Constant::Int(elements.len() as i64));
1413 self.chunk.emit_u16(Op::Constant, count, self.line);
1414 self.chunk.emit(Op::GreaterEqual, self.line);
1415 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1416 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1420 let mut bindings = Vec::new();
1421 for (i, elem) in elements.iter().enumerate() {
1422 match &elem.node {
1423 Node::Identifier(name) if name != "_" => {
1424 bindings.push((i, name.clone()));
1425 }
1426 Node::Identifier(_) => {} _ => {
1429 self.chunk.emit(Op::Dup, self.line);
1430 let idx_const =
1431 self.chunk.add_constant(Constant::Int(i as i64));
1432 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1433 self.chunk.emit(Op::Subscript, self.line);
1434 self.compile_node(elem)?;
1435 self.chunk.emit(Op::Equal, self.line);
1436 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1437 self.chunk.emit(Op::Pop, self.line);
1438 constraint_skips.push(skip);
1439 }
1440 }
1441 }
1442
1443 for (i, name) in &bindings {
1445 self.chunk.emit(Op::Dup, self.line);
1446 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1447 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1448 self.chunk.emit(Op::Subscript, self.line);
1449 let name_idx =
1450 self.chunk.add_constant(Constant::String(name.clone()));
1451 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1452 }
1453
1454 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1456 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1457
1458 let fail_target = self.chunk.code.len();
1460 self.chunk.emit(Op::Pop, self.line); for skip in constraint_skips {
1462 self.chunk.patch_jump_to(skip, fail_target);
1463 }
1464 self.chunk.patch_jump_to(skip_len, fail_target);
1465 self.chunk.patch_jump_to(skip_type, fail_target);
1466 }
1467 _ => {
1469 self.chunk.emit(Op::Dup, self.line);
1470 self.compile_node(&arm.pattern)?;
1471 self.chunk.emit(Op::Equal, self.line);
1472 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1473 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1476 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1477 self.chunk.patch_jump(skip);
1478 self.chunk.emit(Op::Pop, self.line); }
1480 }
1481 }
1482 self.chunk.emit(Op::Pop, self.line);
1484 self.chunk.emit(Op::Nil, self.line);
1485 for j in end_jumps {
1486 self.chunk.patch_jump(j);
1487 }
1488 }
1489
1490 Node::RangeExpr {
1491 start,
1492 end,
1493 inclusive,
1494 } => {
1495 let name_idx = self
1497 .chunk
1498 .add_constant(Constant::String("__range__".to_string()));
1499 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1500 self.compile_node(start)?;
1501 self.compile_node(end)?;
1502 if *inclusive {
1503 self.chunk.emit(Op::True, self.line);
1504 } else {
1505 self.chunk.emit(Op::False, self.line);
1506 }
1507 self.chunk.emit_u8(Op::Call, 3, self.line);
1508 }
1509
1510 Node::GuardStmt {
1511 condition,
1512 else_body,
1513 } => {
1514 self.compile_node(condition)?;
1517 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1518 self.chunk.emit(Op::Pop, self.line); self.compile_block(else_body)?;
1521 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
1523 self.chunk.emit(Op::Pop, self.line);
1524 }
1525 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1526 self.chunk.patch_jump(skip_jump);
1527 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
1529 self.chunk.emit(Op::Nil, self.line);
1530 }
1531
1532 Node::Block(stmts) => {
1533 if stmts.is_empty() {
1534 self.chunk.emit(Op::Nil, self.line);
1535 } else {
1536 self.compile_block(stmts)?;
1537 }
1538 }
1539
1540 Node::DeadlineBlock { duration, body } => {
1541 self.compile_node(duration)?;
1542 self.chunk.emit(Op::DeadlineSetup, self.line);
1543 if body.is_empty() {
1544 self.chunk.emit(Op::Nil, self.line);
1545 } else {
1546 self.compile_block(body)?;
1547 }
1548 self.chunk.emit(Op::DeadlineEnd, self.line);
1549 }
1550
1551 Node::MutexBlock { body } => {
1552 if body.is_empty() {
1554 self.chunk.emit(Op::Nil, self.line);
1555 } else {
1556 for sn in body {
1559 self.compile_node(sn)?;
1560 if Self::produces_value(&sn.node) {
1561 self.chunk.emit(Op::Pop, self.line);
1562 }
1563 }
1564 self.chunk.emit(Op::Nil, self.line);
1565 }
1566 }
1567
1568 Node::YieldExpr { .. } => {
1569 self.chunk.emit(Op::Nil, self.line);
1571 }
1572
1573 Node::AskExpr { fields } => {
1574 for entry in fields {
1577 self.compile_node(&entry.key)?;
1578 self.compile_node(&entry.value)?;
1579 }
1580 self.chunk
1581 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
1582 }
1583
1584 Node::EnumConstruct {
1585 enum_name,
1586 variant,
1587 args,
1588 } => {
1589 for arg in args {
1591 self.compile_node(arg)?;
1592 }
1593 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
1594 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1595 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
1597 let hi = (var_idx >> 8) as u8;
1598 let lo = var_idx as u8;
1599 self.chunk.code.push(hi);
1600 self.chunk.code.push(lo);
1601 self.chunk.lines.push(self.line);
1602 self.chunk.columns.push(self.column);
1603 self.chunk.lines.push(self.line);
1604 self.chunk.columns.push(self.column);
1605 let fc = args.len() as u16;
1606 let fhi = (fc >> 8) as u8;
1607 let flo = fc as u8;
1608 self.chunk.code.push(fhi);
1609 self.chunk.code.push(flo);
1610 self.chunk.lines.push(self.line);
1611 self.chunk.columns.push(self.column);
1612 self.chunk.lines.push(self.line);
1613 self.chunk.columns.push(self.column);
1614 }
1615
1616 Node::StructConstruct {
1617 struct_name,
1618 fields,
1619 } => {
1620 let struct_key = self
1622 .chunk
1623 .add_constant(Constant::String("__struct__".to_string()));
1624 let struct_val = self
1625 .chunk
1626 .add_constant(Constant::String(struct_name.clone()));
1627 self.chunk.emit_u16(Op::Constant, struct_key, self.line);
1628 self.chunk.emit_u16(Op::Constant, struct_val, self.line);
1629
1630 for entry in fields {
1631 self.compile_node(&entry.key)?;
1632 self.compile_node(&entry.value)?;
1633 }
1634 self.chunk
1635 .emit_u16(Op::BuildDict, (fields.len() + 1) as u16, self.line);
1636 }
1637
1638 Node::ImportDecl { path } => {
1639 let idx = self.chunk.add_constant(Constant::String(path.clone()));
1640 self.chunk.emit_u16(Op::Import, idx, self.line);
1641 }
1642
1643 Node::SelectiveImport { names, path } => {
1644 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
1645 let names_str = names.join(",");
1646 let names_idx = self.chunk.add_constant(Constant::String(names_str));
1647 self.chunk
1648 .emit_u16(Op::SelectiveImport, path_idx, self.line);
1649 let hi = (names_idx >> 8) as u8;
1650 let lo = names_idx as u8;
1651 self.chunk.code.push(hi);
1652 self.chunk.code.push(lo);
1653 self.chunk.lines.push(self.line);
1654 self.chunk.columns.push(self.column);
1655 self.chunk.lines.push(self.line);
1656 self.chunk.columns.push(self.column);
1657 }
1658
1659 Node::TryOperator { operand } => {
1660 self.compile_node(operand)?;
1661 self.chunk.emit(Op::TryUnwrap, self.line);
1662 }
1663
1664 Node::ImplBlock { type_name, methods } => {
1665 for method_sn in methods {
1668 if let Node::FnDecl {
1669 name, params, body, ..
1670 } = &method_sn.node
1671 {
1672 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
1674 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1675
1676 let mut fn_compiler = Compiler::new();
1678 fn_compiler.enum_names = self.enum_names.clone();
1679 fn_compiler.emit_default_preamble(params)?;
1680 fn_compiler.emit_type_checks(params);
1681 fn_compiler.compile_block(body)?;
1682 fn_compiler.chunk.emit(Op::Nil, self.line);
1683 fn_compiler.chunk.emit(Op::Return, self.line);
1684
1685 let func = CompiledFunction {
1686 name: format!("{}.{}", type_name, name),
1687 params: TypedParam::names(params),
1688 default_start: TypedParam::default_start(params),
1689 chunk: fn_compiler.chunk,
1690 };
1691 let fn_idx = self.chunk.functions.len();
1692 self.chunk.functions.push(func);
1693 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1694 }
1695 }
1696 let method_count = methods
1697 .iter()
1698 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
1699 .count();
1700 self.chunk
1701 .emit_u16(Op::BuildDict, method_count as u16, self.line);
1702 let impl_name = format!("__impl_{}", type_name);
1703 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
1704 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1705 }
1706
1707 Node::StructDecl { name, .. } => {
1708 let mut fn_compiler = Compiler::new();
1710 fn_compiler.enum_names = self.enum_names.clone();
1711 let params = vec![TypedParam::untyped("__fields")];
1712 fn_compiler.emit_default_preamble(¶ms)?;
1713
1714 let make_idx = fn_compiler
1716 .chunk
1717 .add_constant(Constant::String("__make_struct".into()));
1718 fn_compiler
1719 .chunk
1720 .emit_u16(Op::Constant, make_idx, self.line);
1721 let sname_idx = fn_compiler
1722 .chunk
1723 .add_constant(Constant::String(name.clone()));
1724 fn_compiler
1725 .chunk
1726 .emit_u16(Op::Constant, sname_idx, self.line);
1727 let fields_idx = fn_compiler
1728 .chunk
1729 .add_constant(Constant::String("__fields".into()));
1730 fn_compiler
1731 .chunk
1732 .emit_u16(Op::GetVar, fields_idx, self.line);
1733 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
1734 fn_compiler.chunk.emit(Op::Return, self.line);
1735
1736 let func = CompiledFunction {
1737 name: name.clone(),
1738 params: TypedParam::names(¶ms),
1739 default_start: None,
1740 chunk: fn_compiler.chunk,
1741 };
1742 let fn_idx = self.chunk.functions.len();
1743 self.chunk.functions.push(func);
1744 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1745 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1746 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1747 }
1748
1749 Node::Pipeline { .. }
1751 | Node::OverrideDecl { .. }
1752 | Node::TypeDecl { .. }
1753 | Node::EnumDecl { .. }
1754 | Node::InterfaceDecl { .. } => {
1755 self.chunk.emit(Op::Nil, self.line);
1756 }
1757
1758 Node::TryCatch {
1759 body,
1760 error_var,
1761 error_type,
1762 catch_body,
1763 finally_body,
1764 } => {
1765 let type_name = error_type.as_ref().and_then(|te| {
1767 if let harn_parser::TypeExpr::Named(name) = te {
1768 Some(name.clone())
1769 } else {
1770 None
1771 }
1772 });
1773
1774 let type_name_idx = if let Some(ref tn) = type_name {
1775 self.chunk.add_constant(Constant::String(tn.clone()))
1776 } else {
1777 self.chunk.add_constant(Constant::String(String::new()))
1778 };
1779
1780 let has_catch = !catch_body.is_empty() || error_var.is_some();
1781 let has_finally = finally_body.is_some();
1782
1783 if has_catch && has_finally {
1784 let finally_body = finally_body.as_ref().unwrap();
1786
1787 self.finally_bodies.push(finally_body.clone());
1789
1790 self.handler_depth += 1;
1792 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1793 self.emit_type_name_extra(type_name_idx);
1794
1795 self.compile_try_body(body)?;
1797
1798 self.handler_depth -= 1;
1800 self.chunk.emit(Op::PopHandler, self.line);
1801 self.compile_finally_inline(finally_body)?;
1802 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1803
1804 self.chunk.patch_jump(catch_jump);
1806 self.compile_catch_binding(error_var)?;
1807
1808 self.handler_depth += 1;
1810 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1811 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1812 self.emit_type_name_extra(empty_type);
1813
1814 self.compile_try_body(catch_body)?;
1816
1817 self.handler_depth -= 1;
1819 self.chunk.emit(Op::PopHandler, self.line);
1820 self.compile_finally_inline(finally_body)?;
1821 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
1822
1823 self.chunk.patch_jump(rethrow_jump);
1825 self.compile_rethrow_with_finally(finally_body)?;
1826
1827 self.chunk.patch_jump(end_jump);
1828 self.chunk.patch_jump(end_jump2);
1829
1830 self.finally_bodies.pop();
1831 } else if has_finally {
1832 let finally_body = finally_body.as_ref().unwrap();
1834
1835 self.finally_bodies.push(finally_body.clone());
1836
1837 self.handler_depth += 1;
1839 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1840 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1841 self.emit_type_name_extra(empty_type);
1842
1843 self.compile_try_body(body)?;
1845
1846 self.handler_depth -= 1;
1848 self.chunk.emit(Op::PopHandler, self.line);
1849 self.compile_finally_inline(finally_body)?;
1850 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1851
1852 self.chunk.patch_jump(error_jump);
1854 self.compile_rethrow_with_finally(finally_body)?;
1855
1856 self.chunk.patch_jump(end_jump);
1857
1858 self.finally_bodies.pop();
1859 } else {
1860 self.handler_depth += 1;
1864 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1865 self.emit_type_name_extra(type_name_idx);
1866
1867 self.compile_try_body(body)?;
1869
1870 self.handler_depth -= 1;
1872 self.chunk.emit(Op::PopHandler, self.line);
1873 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1874
1875 self.chunk.patch_jump(catch_jump);
1877 self.compile_catch_binding(error_var)?;
1878
1879 self.compile_try_body(catch_body)?;
1881
1882 self.chunk.patch_jump(end_jump);
1884 }
1885 }
1886
1887 Node::TryExpr { body } => {
1888 self.handler_depth += 1;
1892 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1893 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1894 self.emit_type_name_extra(empty_type);
1895
1896 self.compile_try_body(body)?;
1898
1899 self.handler_depth -= 1;
1901 self.chunk.emit(Op::PopHandler, self.line);
1902
1903 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
1905 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
1906 self.chunk.emit(Op::Swap, self.line);
1907 self.chunk.emit_u8(Op::Call, 1, self.line);
1908
1909 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1911
1912 self.chunk.patch_jump(catch_jump);
1914
1915 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
1917 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
1918 self.chunk.emit(Op::Swap, self.line);
1919 self.chunk.emit_u8(Op::Call, 1, self.line);
1920
1921 self.chunk.patch_jump(end_jump);
1923 }
1924
1925 Node::Retry { count, body } => {
1926 self.compile_node(count)?;
1928 let counter_name = "__retry_counter__";
1929 let counter_idx = self
1930 .chunk
1931 .add_constant(Constant::String(counter_name.to_string()));
1932 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
1933
1934 self.chunk.emit(Op::Nil, self.line);
1936 let err_name = "__retry_last_error__";
1937 let err_idx = self
1938 .chunk
1939 .add_constant(Constant::String(err_name.to_string()));
1940 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
1941
1942 let loop_start = self.chunk.current_offset();
1944
1945 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1947 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1949 let hi = (empty_type >> 8) as u8;
1950 let lo = empty_type as u8;
1951 self.chunk.code.push(hi);
1952 self.chunk.code.push(lo);
1953 self.chunk.lines.push(self.line);
1954 self.chunk.columns.push(self.column);
1955 self.chunk.lines.push(self.line);
1956 self.chunk.columns.push(self.column);
1957
1958 self.compile_block(body)?;
1960
1961 self.chunk.emit(Op::PopHandler, self.line);
1963 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1964
1965 self.chunk.patch_jump(catch_jump);
1967 self.chunk.emit(Op::Dup, self.line);
1969 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
1970 self.chunk.emit(Op::Pop, self.line);
1972
1973 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
1975 let one_idx = self.chunk.add_constant(Constant::Int(1));
1976 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
1977 self.chunk.emit(Op::Sub, self.line);
1978 self.chunk.emit(Op::Dup, self.line);
1979 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
1980
1981 let zero_idx = self.chunk.add_constant(Constant::Int(0));
1983 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
1984 self.chunk.emit(Op::Greater, self.line);
1985 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1986 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1988
1989 self.chunk.patch_jump(retry_jump);
1991 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
1993 self.chunk.emit(Op::Throw, self.line);
1994
1995 self.chunk.patch_jump(end_jump);
1996 self.chunk.emit(Op::Nil, self.line);
1998 }
1999
2000 Node::Parallel {
2001 count,
2002 variable,
2003 body,
2004 } => {
2005 self.compile_node(count)?;
2006 let mut fn_compiler = Compiler::new();
2007 fn_compiler.enum_names = self.enum_names.clone();
2008 fn_compiler.compile_block(body)?;
2009 fn_compiler.chunk.emit(Op::Return, self.line);
2010 let params = vec![variable.clone().unwrap_or_else(|| "__i__".to_string())];
2011 let func = CompiledFunction {
2012 name: "<parallel>".to_string(),
2013 params,
2014 default_start: None,
2015 chunk: fn_compiler.chunk,
2016 };
2017 let fn_idx = self.chunk.functions.len();
2018 self.chunk.functions.push(func);
2019 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2020 self.chunk.emit(Op::Parallel, self.line);
2021 }
2022
2023 Node::ParallelMap {
2024 list,
2025 variable,
2026 body,
2027 } => {
2028 self.compile_node(list)?;
2029 let mut fn_compiler = Compiler::new();
2030 fn_compiler.enum_names = self.enum_names.clone();
2031 fn_compiler.compile_block(body)?;
2032 fn_compiler.chunk.emit(Op::Return, self.line);
2033 let func = CompiledFunction {
2034 name: "<parallel_map>".to_string(),
2035 params: vec![variable.clone()],
2036 default_start: None,
2037 chunk: fn_compiler.chunk,
2038 };
2039 let fn_idx = self.chunk.functions.len();
2040 self.chunk.functions.push(func);
2041 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2042 self.chunk.emit(Op::ParallelMap, self.line);
2043 }
2044
2045 Node::SpawnExpr { body } => {
2046 let mut fn_compiler = Compiler::new();
2047 fn_compiler.enum_names = self.enum_names.clone();
2048 fn_compiler.compile_block(body)?;
2049 fn_compiler.chunk.emit(Op::Return, self.line);
2050 let func = CompiledFunction {
2051 name: "<spawn>".to_string(),
2052 params: vec![],
2053 default_start: None,
2054 chunk: fn_compiler.chunk,
2055 };
2056 let fn_idx = self.chunk.functions.len();
2057 self.chunk.functions.push(func);
2058 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2059 self.chunk.emit(Op::Spawn, self.line);
2060 }
2061 Node::SelectExpr {
2062 cases,
2063 timeout,
2064 default_body,
2065 } => {
2066 let builtin_name = if timeout.is_some() {
2073 "__select_timeout"
2074 } else if default_body.is_some() {
2075 "__select_try"
2076 } else {
2077 "__select_list"
2078 };
2079
2080 let name_idx = self
2082 .chunk
2083 .add_constant(Constant::String(builtin_name.into()));
2084 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2085
2086 for case in cases {
2088 self.compile_node(&case.channel)?;
2089 }
2090 self.chunk
2091 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2092
2093 if let Some((duration_expr, _)) = timeout {
2095 self.compile_node(duration_expr)?;
2096 self.chunk.emit_u8(Op::Call, 2, self.line);
2097 } else {
2098 self.chunk.emit_u8(Op::Call, 1, self.line);
2099 }
2100
2101 self.temp_counter += 1;
2103 let result_name = format!("__sel_result_{}__", self.temp_counter);
2104 let result_idx = self
2105 .chunk
2106 .add_constant(Constant::String(result_name.clone()));
2107 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2108
2109 let mut end_jumps = Vec::new();
2111
2112 for (i, case) in cases.iter().enumerate() {
2113 let get_r = self
2114 .chunk
2115 .add_constant(Constant::String(result_name.clone()));
2116 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2117 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2118 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2119 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2120 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2121 self.chunk.emit(Op::Equal, self.line);
2122 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2123 self.chunk.emit(Op::Pop, self.line);
2124
2125 let get_r2 = self
2127 .chunk
2128 .add_constant(Constant::String(result_name.clone()));
2129 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2130 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2131 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2132 let var_idx = self
2133 .chunk
2134 .add_constant(Constant::String(case.variable.clone()));
2135 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2136
2137 self.compile_try_body(&case.body)?;
2138 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2139 self.chunk.patch_jump(skip);
2140 self.chunk.emit(Op::Pop, self.line);
2141 }
2142
2143 if let Some((_, ref timeout_body)) = timeout {
2145 self.compile_try_body(timeout_body)?;
2146 } else if let Some(ref def_body) = default_body {
2147 self.compile_try_body(def_body)?;
2148 } else {
2149 self.chunk.emit(Op::Nil, self.line);
2150 }
2151
2152 for ej in end_jumps {
2153 self.chunk.patch_jump(ej);
2154 }
2155 }
2156 Node::Spread(_) => {
2157 return Err(CompileError {
2158 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2159 line: self.line,
2160 });
2161 }
2162 }
2163 Ok(())
2164 }
2165
2166 fn compile_destructuring(
2170 &mut self,
2171 pattern: &BindingPattern,
2172 is_mutable: bool,
2173 ) -> Result<(), CompileError> {
2174 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2175 match pattern {
2176 BindingPattern::Identifier(name) => {
2177 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2179 self.chunk.emit_u16(def_op, idx, self.line);
2180 }
2181 BindingPattern::Dict(fields) => {
2182 self.chunk.emit(Op::Dup, self.line);
2185 let assert_idx = self
2186 .chunk
2187 .add_constant(Constant::String("__assert_dict".into()));
2188 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2189 self.chunk.emit(Op::Swap, self.line);
2190 self.chunk.emit_u8(Op::Call, 1, self.line);
2191 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2196 let rest_field = fields.iter().find(|f| f.is_rest);
2197
2198 for field in &non_rest {
2199 self.chunk.emit(Op::Dup, self.line);
2200 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2201 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2202 self.chunk.emit(Op::Subscript, self.line);
2203 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2204 let name_idx = self
2205 .chunk
2206 .add_constant(Constant::String(binding_name.to_string()));
2207 self.chunk.emit_u16(def_op, name_idx, self.line);
2208 }
2209
2210 if let Some(rest) = rest_field {
2211 let fn_idx = self
2214 .chunk
2215 .add_constant(Constant::String("__dict_rest".into()));
2216 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2217 self.chunk.emit(Op::Swap, self.line);
2219 for field in &non_rest {
2221 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2222 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2223 }
2224 self.chunk
2225 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2226 self.chunk.emit_u8(Op::Call, 2, self.line);
2228 let rest_name = &rest.key;
2229 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2230 self.chunk.emit_u16(def_op, rest_idx, self.line);
2231 } else {
2232 self.chunk.emit(Op::Pop, self.line);
2234 }
2235 }
2236 BindingPattern::List(elements) => {
2237 self.chunk.emit(Op::Dup, self.line);
2240 let assert_idx = self
2241 .chunk
2242 .add_constant(Constant::String("__assert_list".into()));
2243 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2244 self.chunk.emit(Op::Swap, self.line);
2245 self.chunk.emit_u8(Op::Call, 1, self.line);
2246 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2249 let rest_elem = elements.iter().find(|e| e.is_rest);
2250
2251 for (i, elem) in non_rest.iter().enumerate() {
2252 self.chunk.emit(Op::Dup, self.line);
2253 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2254 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2255 self.chunk.emit(Op::Subscript, self.line);
2256 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2257 self.chunk.emit_u16(def_op, name_idx, self.line);
2258 }
2259
2260 if let Some(rest) = rest_elem {
2261 let start_idx = self
2265 .chunk
2266 .add_constant(Constant::Int(non_rest.len() as i64));
2267 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2268 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2270 let rest_name_idx =
2271 self.chunk.add_constant(Constant::String(rest.name.clone()));
2272 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2273 } else {
2274 self.chunk.emit(Op::Pop, self.line);
2276 }
2277 }
2278 }
2279 Ok(())
2280 }
2281
2282 fn produces_value(node: &Node) -> bool {
2284 match node {
2285 Node::LetBinding { .. }
2287 | Node::VarBinding { .. }
2288 | Node::Assignment { .. }
2289 | Node::ReturnStmt { .. }
2290 | Node::FnDecl { .. }
2291 | Node::ImplBlock { .. }
2292 | Node::StructDecl { .. }
2293 | Node::ThrowStmt { .. }
2294 | Node::BreakStmt
2295 | Node::ContinueStmt => false,
2296 Node::TryCatch { .. }
2298 | Node::TryExpr { .. }
2299 | Node::Retry { .. }
2300 | Node::GuardStmt { .. }
2301 | Node::DeadlineBlock { .. }
2302 | Node::MutexBlock { .. }
2303 | Node::Spread(_) => true,
2304 _ => true,
2306 }
2307 }
2308}
2309
2310impl Compiler {
2311 pub fn compile_fn_body(
2313 &mut self,
2314 params: &[TypedParam],
2315 body: &[SNode],
2316 ) -> Result<CompiledFunction, CompileError> {
2317 let mut fn_compiler = Compiler::new();
2318 fn_compiler.compile_block(body)?;
2319 fn_compiler.chunk.emit(Op::Nil, 0);
2320 fn_compiler.chunk.emit(Op::Return, 0);
2321 Ok(CompiledFunction {
2322 name: String::new(),
2323 params: TypedParam::names(params),
2324 default_start: TypedParam::default_start(params),
2325 chunk: fn_compiler.chunk,
2326 })
2327 }
2328
2329 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2331 if body.is_empty() {
2332 self.chunk.emit(Op::Nil, self.line);
2333 } else {
2334 self.compile_block(body)?;
2335 if !Self::produces_value(&body.last().unwrap().node) {
2337 self.chunk.emit(Op::Nil, self.line);
2338 }
2339 }
2340 Ok(())
2341 }
2342
2343 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2345 match op {
2346 "+" => self.chunk.emit(Op::Add, self.line),
2347 "-" => self.chunk.emit(Op::Sub, self.line),
2348 "*" => self.chunk.emit(Op::Mul, self.line),
2349 "/" => self.chunk.emit(Op::Div, self.line),
2350 "%" => self.chunk.emit(Op::Mod, self.line),
2351 _ => {
2352 return Err(CompileError {
2353 message: format!("Unknown compound operator: {op}"),
2354 line: self.line,
2355 })
2356 }
2357 }
2358 Ok(())
2359 }
2360
2361 fn root_var_name(&self, node: &SNode) -> Option<String> {
2363 match &node.node {
2364 Node::Identifier(name) => Some(name.clone()),
2365 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2366 self.root_var_name(object)
2367 }
2368 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2369 _ => None,
2370 }
2371 }
2372}
2373
2374impl Compiler {
2375 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
2377 for sn in nodes {
2378 match &sn.node {
2379 Node::EnumDecl { name, .. } => {
2380 names.insert(name.clone());
2381 }
2382 Node::Pipeline { body, .. } => {
2383 Self::collect_enum_names(body, names);
2384 }
2385 Node::FnDecl { body, .. } => {
2386 Self::collect_enum_names(body, names);
2387 }
2388 Node::Block(stmts) => {
2389 Self::collect_enum_names(stmts, names);
2390 }
2391 _ => {}
2392 }
2393 }
2394 }
2395}
2396
2397impl Default for Compiler {
2398 fn default() -> Self {
2399 Self::new()
2400 }
2401}
2402
2403fn contains_pipe_placeholder(node: &SNode) -> bool {
2405 match &node.node {
2406 Node::Identifier(name) if name == "_" => true,
2407 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
2408 Node::MethodCall { object, args, .. } => {
2409 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
2410 }
2411 Node::BinaryOp { left, right, .. } => {
2412 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
2413 }
2414 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
2415 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
2416 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
2417 Node::SubscriptAccess { object, index } => {
2418 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
2419 }
2420 _ => false,
2421 }
2422}
2423
2424fn replace_pipe_placeholder(node: &SNode) -> SNode {
2426 let new_node = match &node.node {
2427 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
2428 Node::FunctionCall { name, args } => Node::FunctionCall {
2429 name: name.clone(),
2430 args: args.iter().map(replace_pipe_placeholder).collect(),
2431 },
2432 Node::MethodCall {
2433 object,
2434 method,
2435 args,
2436 } => Node::MethodCall {
2437 object: Box::new(replace_pipe_placeholder(object)),
2438 method: method.clone(),
2439 args: args.iter().map(replace_pipe_placeholder).collect(),
2440 },
2441 Node::BinaryOp { op, left, right } => Node::BinaryOp {
2442 op: op.clone(),
2443 left: Box::new(replace_pipe_placeholder(left)),
2444 right: Box::new(replace_pipe_placeholder(right)),
2445 },
2446 Node::UnaryOp { op, operand } => Node::UnaryOp {
2447 op: op.clone(),
2448 operand: Box::new(replace_pipe_placeholder(operand)),
2449 },
2450 Node::ListLiteral(items) => {
2451 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
2452 }
2453 Node::PropertyAccess { object, property } => Node::PropertyAccess {
2454 object: Box::new(replace_pipe_placeholder(object)),
2455 property: property.clone(),
2456 },
2457 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
2458 object: Box::new(replace_pipe_placeholder(object)),
2459 index: Box::new(replace_pipe_placeholder(index)),
2460 },
2461 _ => return node.clone(),
2462 };
2463 SNode::new(new_node, node.span)
2464}
2465
2466#[cfg(test)]
2467mod tests {
2468 use super::*;
2469 use harn_lexer::Lexer;
2470 use harn_parser::Parser;
2471
2472 fn compile_source(source: &str) -> Chunk {
2473 let mut lexer = Lexer::new(source);
2474 let tokens = lexer.tokenize().unwrap();
2475 let mut parser = Parser::new(tokens);
2476 let program = parser.parse().unwrap();
2477 Compiler::new().compile(&program).unwrap()
2478 }
2479
2480 #[test]
2481 fn test_compile_arithmetic() {
2482 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
2483 assert!(!chunk.code.is_empty());
2484 assert!(chunk.constants.contains(&Constant::Int(2)));
2486 assert!(chunk.constants.contains(&Constant::Int(3)));
2487 }
2488
2489 #[test]
2490 fn test_compile_function_call() {
2491 let chunk = compile_source("pipeline test(task) { log(42) }");
2492 let disasm = chunk.disassemble("test");
2493 assert!(disasm.contains("CALL"));
2494 }
2495
2496 #[test]
2497 fn test_compile_if_else() {
2498 let chunk =
2499 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
2500 let disasm = chunk.disassemble("test");
2501 assert!(disasm.contains("JUMP_IF_FALSE"));
2502 assert!(disasm.contains("JUMP"));
2503 }
2504
2505 #[test]
2506 fn test_compile_while() {
2507 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
2508 let disasm = chunk.disassemble("test");
2509 assert!(disasm.contains("JUMP_IF_FALSE"));
2510 assert!(disasm.contains("JUMP"));
2512 }
2513
2514 #[test]
2515 fn test_compile_closure() {
2516 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
2517 assert!(!chunk.functions.is_empty());
2518 assert_eq!(chunk.functions[0].params, vec!["x"]);
2519 }
2520
2521 #[test]
2522 fn test_compile_list() {
2523 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
2524 let disasm = chunk.disassemble("test");
2525 assert!(disasm.contains("BUILD_LIST"));
2526 }
2527
2528 #[test]
2529 fn test_compile_dict() {
2530 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
2531 let disasm = chunk.disassemble("test");
2532 assert!(disasm.contains("BUILD_DICT"));
2533 }
2534
2535 #[test]
2536 fn test_disassemble() {
2537 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
2538 let disasm = chunk.disassemble("test");
2539 assert!(disasm.contains("CONSTANT"));
2541 assert!(disasm.contains("ADD"));
2542 assert!(disasm.contains("CALL"));
2543 }
2544}