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 interface_methods: std::collections::HashMap<String, Vec<String>>,
44 loop_stack: Vec<LoopContext>,
46 handler_depth: usize,
48 finally_bodies: Vec<Vec<SNode>>,
50 temp_counter: usize,
52}
53
54impl Compiler {
55 pub fn new() -> Self {
56 Self {
57 chunk: Chunk::new(),
58 line: 1,
59 column: 1,
60 enum_names: std::collections::HashSet::new(),
61 interface_methods: std::collections::HashMap::new(),
62 loop_stack: Vec::new(),
63 handler_depth: 0,
64 finally_bodies: Vec::new(),
65 temp_counter: 0,
66 }
67 }
68
69 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
72 Self::collect_enum_names(program, &mut self.enum_names);
75 self.enum_names.insert("Result".to_string());
77 Self::collect_interface_methods(program, &mut self.interface_methods);
78
79 for sn in program {
81 match &sn.node {
82 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
83 self.compile_node(sn)?;
84 }
85 _ => {}
86 }
87 }
88
89 let main = program
91 .iter()
92 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == "default"))
93 .or_else(|| {
94 program
95 .iter()
96 .find(|sn| matches!(&sn.node, Node::Pipeline { .. }))
97 });
98
99 if let Some(sn) = main {
100 if let Node::Pipeline { body, extends, .. } = &sn.node {
101 if let Some(parent_name) = extends {
103 self.compile_parent_pipeline(program, parent_name)?;
104 }
105 self.compile_block(body)?;
106 }
107 }
108
109 self.chunk.emit(Op::Nil, self.line);
110 self.chunk.emit(Op::Return, self.line);
111 Ok(self.chunk)
112 }
113
114 pub fn compile_named(
116 mut self,
117 program: &[SNode],
118 pipeline_name: &str,
119 ) -> Result<Chunk, CompileError> {
120 Self::collect_enum_names(program, &mut self.enum_names);
121 Self::collect_interface_methods(program, &mut self.interface_methods);
122
123 for sn in program {
124 if matches!(
125 &sn.node,
126 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
127 ) {
128 self.compile_node(sn)?;
129 }
130 }
131
132 let target = program
133 .iter()
134 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == pipeline_name));
135
136 if let Some(sn) = target {
137 if let Node::Pipeline { body, extends, .. } = &sn.node {
138 if let Some(parent_name) = extends {
139 self.compile_parent_pipeline(program, parent_name)?;
140 }
141 self.compile_block(body)?;
142 }
143 }
144
145 self.chunk.emit(Op::Nil, self.line);
146 self.chunk.emit(Op::Return, self.line);
147 Ok(self.chunk)
148 }
149
150 fn compile_parent_pipeline(
152 &mut self,
153 program: &[SNode],
154 parent_name: &str,
155 ) -> Result<(), CompileError> {
156 let parent = program
157 .iter()
158 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
159 if let Some(sn) = parent {
160 if let Node::Pipeline { body, extends, .. } = &sn.node {
161 if let Some(grandparent) = extends {
163 self.compile_parent_pipeline(program, grandparent)?;
164 }
165 for stmt in body {
167 self.compile_node(stmt)?;
168 if Self::produces_value(&stmt.node) {
169 self.chunk.emit(Op::Pop, self.line);
170 }
171 }
172 }
173 }
174 Ok(())
175 }
176
177 fn emit_default_preamble(&mut self, params: &[TypedParam]) -> Result<(), CompileError> {
182 for (i, param) in params.iter().enumerate() {
183 if let Some(default_expr) = ¶m.default_value {
184 self.chunk.emit(Op::GetArgc, self.line);
185 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
186 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
187 self.chunk.emit(Op::GreaterEqual, self.line);
189 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
190 self.chunk.emit(Op::Pop, self.line);
192 self.compile_node(default_expr)?;
194 let name_idx = self
195 .chunk
196 .add_constant(Constant::String(param.name.clone()));
197 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
198 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
199 self.chunk.patch_jump(skip_jump);
200 self.chunk.emit(Op::Pop, self.line);
202 self.chunk.patch_jump(end_jump);
203 }
204 }
205 Ok(())
206 }
207
208 fn emit_type_checks(&mut self, params: &[TypedParam]) {
212 for param in params {
213 if let Some(type_expr) = ¶m.type_expr {
214 if let harn_parser::TypeExpr::Shape(fields) = type_expr {
216 let spec = Self::shape_to_spec_string(fields);
217 let fn_idx = self
219 .chunk
220 .add_constant(Constant::String("__assert_shape".into()));
221 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
222 let var_idx = self
223 .chunk
224 .add_constant(Constant::String(param.name.clone()));
225 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
226 let name_idx = self
227 .chunk
228 .add_constant(Constant::String(param.name.clone()));
229 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
230 let spec_idx = self.chunk.add_constant(Constant::String(spec));
231 self.chunk.emit_u16(Op::Constant, spec_idx, self.line);
232 self.chunk.emit_u8(Op::Call, 3, self.line);
233 self.chunk.emit(Op::Pop, self.line);
234 continue;
235 }
236
237 if let harn_parser::TypeExpr::Named(name) = type_expr {
239 if let Some(methods) = self.interface_methods.get(name) {
240 let fn_idx = self
241 .chunk
242 .add_constant(Constant::String("__assert_interface".into()));
243 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
244 let var_idx = self
245 .chunk
246 .add_constant(Constant::String(param.name.clone()));
247 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
248 let name_idx = self
249 .chunk
250 .add_constant(Constant::String(param.name.clone()));
251 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
252 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
253 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
254 let methods_str = methods.join(",");
255 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
256 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
257 self.chunk.emit_u8(Op::Call, 4, self.line);
258 self.chunk.emit(Op::Pop, self.line);
259 continue;
260 }
261 }
262
263 let type_name = Self::type_expr_to_runtime_name(type_expr);
264 if let Some(type_name) = type_name {
265 let var_idx = self
266 .chunk
267 .add_constant(Constant::String(param.name.clone()));
268 let type_idx = self.chunk.add_constant(Constant::String(type_name));
269 self.chunk.emit_u16(Op::CheckType, var_idx, self.line);
270 let hi = (type_idx >> 8) as u8;
272 let lo = type_idx as u8;
273 self.chunk.code.push(hi);
274 self.chunk.code.push(lo);
275 }
276 }
277 }
278 }
279
280 fn shape_to_spec_string(fields: &[harn_parser::ShapeField]) -> String {
283 fields
284 .iter()
285 .map(|f| {
286 let opt = if f.optional { "?" } else { "" };
287 let type_str = Self::type_expr_to_spec(&f.type_expr);
288 format!("{}:{}{}", f.name, opt, type_str)
289 })
290 .collect::<Vec<_>>()
291 .join(",")
292 }
293
294 fn type_expr_to_spec(type_expr: &harn_parser::TypeExpr) -> String {
296 match type_expr {
297 harn_parser::TypeExpr::Named(name) => name.clone(),
298 harn_parser::TypeExpr::Shape(fields) => {
299 let inner = Self::shape_to_spec_string(fields);
300 format!("{{{}}}", inner)
301 }
302 harn_parser::TypeExpr::List(_) => "list".to_string(),
303 harn_parser::TypeExpr::DictType(_, _) => "dict".to_string(),
304 harn_parser::TypeExpr::Union(members) => {
305 members
307 .iter()
308 .map(Self::type_expr_to_spec)
309 .collect::<Vec<_>>()
310 .join("|")
311 }
312 harn_parser::TypeExpr::FnType { .. } => "closure".to_string(),
313 }
314 }
315
316 fn type_expr_to_runtime_name(type_expr: &harn_parser::TypeExpr) -> Option<String> {
318 match type_expr {
319 harn_parser::TypeExpr::Named(name) => match name.as_str() {
320 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
321 | "closure" => Some(name.clone()),
322 _ => None, },
324 _ => None, }
326 }
327
328 fn emit_type_name_extra(&mut self, type_name_idx: u16) {
330 let hi = (type_name_idx >> 8) as u8;
331 let lo = type_name_idx as u8;
332 self.chunk.code.push(hi);
333 self.chunk.code.push(lo);
334 self.chunk.lines.push(self.line);
335 self.chunk.columns.push(self.column);
336 self.chunk.lines.push(self.line);
337 self.chunk.columns.push(self.column);
338 }
339
340 fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
342 if body.is_empty() {
343 self.chunk.emit(Op::Nil, self.line);
344 } else {
345 self.compile_block(body)?;
346 if !Self::produces_value(&body.last().unwrap().node) {
347 self.chunk.emit(Op::Nil, self.line);
348 }
349 }
350 Ok(())
351 }
352
353 fn compile_catch_binding(&mut self, error_var: &Option<String>) -> Result<(), CompileError> {
355 if let Some(var_name) = error_var {
356 let idx = self.chunk.add_constant(Constant::String(var_name.clone()));
357 self.chunk.emit_u16(Op::DefLet, idx, self.line);
358 } else {
359 self.chunk.emit(Op::Pop, self.line);
360 }
361 Ok(())
362 }
363
364 fn compile_finally_inline(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
366 if !finally_body.is_empty() {
367 self.compile_block(finally_body)?;
368 if Self::produces_value(&finally_body.last().unwrap().node) {
370 self.chunk.emit(Op::Pop, self.line);
371 }
372 }
373 Ok(())
374 }
375
376 fn compile_rethrow_with_finally(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
378 self.temp_counter += 1;
380 let temp_name = format!("__finally_err_{}__", self.temp_counter);
381 let err_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
382 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
383 self.compile_finally_inline(finally_body)?;
384 let get_idx = self.chunk.add_constant(Constant::String(temp_name));
385 self.chunk.emit_u16(Op::GetVar, get_idx, self.line);
386 self.chunk.emit(Op::Throw, self.line);
387 Ok(())
388 }
389
390 fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
391 for (i, snode) in stmts.iter().enumerate() {
392 self.compile_node(snode)?;
393 let is_last = i == stmts.len() - 1;
394 if is_last {
395 if !Self::produces_value(&snode.node) {
398 self.chunk.emit(Op::Nil, self.line);
399 }
400 } else {
401 if Self::produces_value(&snode.node) {
403 self.chunk.emit(Op::Pop, self.line);
404 }
405 }
406 }
407 Ok(())
408 }
409
410 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
411 self.line = snode.span.line as u32;
412 self.column = snode.span.column as u32;
413 self.chunk.set_column(self.column);
414 match &snode.node {
415 Node::IntLiteral(n) => {
416 let idx = self.chunk.add_constant(Constant::Int(*n));
417 self.chunk.emit_u16(Op::Constant, idx, self.line);
418 }
419 Node::FloatLiteral(n) => {
420 let idx = self.chunk.add_constant(Constant::Float(*n));
421 self.chunk.emit_u16(Op::Constant, idx, self.line);
422 }
423 Node::StringLiteral(s) => {
424 let idx = self.chunk.add_constant(Constant::String(s.clone()));
425 self.chunk.emit_u16(Op::Constant, idx, self.line);
426 }
427 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
428 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
429 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
430 Node::DurationLiteral(ms) => {
431 let idx = self.chunk.add_constant(Constant::Duration(*ms));
432 self.chunk.emit_u16(Op::Constant, idx, self.line);
433 }
434
435 Node::Identifier(name) => {
436 let idx = self.chunk.add_constant(Constant::String(name.clone()));
437 self.chunk.emit_u16(Op::GetVar, idx, self.line);
438 }
439
440 Node::LetBinding { pattern, value, .. } => {
441 self.compile_node(value)?;
442 self.compile_destructuring(pattern, false)?;
443 }
444
445 Node::VarBinding { pattern, value, .. } => {
446 self.compile_node(value)?;
447 self.compile_destructuring(pattern, true)?;
448 }
449
450 Node::Assignment {
451 target, value, op, ..
452 } => {
453 if let Node::Identifier(name) = &target.node {
454 let idx = self.chunk.add_constant(Constant::String(name.clone()));
455 if let Some(op) = op {
456 self.chunk.emit_u16(Op::GetVar, idx, self.line);
457 self.compile_node(value)?;
458 self.emit_compound_op(op)?;
459 self.chunk.emit_u16(Op::SetVar, idx, self.line);
460 } else {
461 self.compile_node(value)?;
462 self.chunk.emit_u16(Op::SetVar, idx, self.line);
463 }
464 } else if let Node::PropertyAccess { object, property } = &target.node {
465 if let Some(var_name) = self.root_var_name(object) {
467 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
468 let prop_idx = self.chunk.add_constant(Constant::String(property.clone()));
469 if let Some(op) = op {
470 self.compile_node(target)?; self.compile_node(value)?;
473 self.emit_compound_op(op)?;
474 } else {
475 self.compile_node(value)?;
476 }
477 self.chunk.emit_u16(Op::SetProperty, prop_idx, self.line);
480 let hi = (var_idx >> 8) as u8;
482 let lo = var_idx as u8;
483 self.chunk.code.push(hi);
484 self.chunk.code.push(lo);
485 self.chunk.lines.push(self.line);
486 self.chunk.columns.push(self.column);
487 self.chunk.lines.push(self.line);
488 self.chunk.columns.push(self.column);
489 }
490 } else if let Node::SubscriptAccess { object, index } = &target.node {
491 if let Some(var_name) = self.root_var_name(object) {
493 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
494 if let Some(op) = op {
495 self.compile_node(target)?;
496 self.compile_node(value)?;
497 self.emit_compound_op(op)?;
498 } else {
499 self.compile_node(value)?;
500 }
501 self.compile_node(index)?;
502 self.chunk.emit_u16(Op::SetSubscript, var_idx, self.line);
503 }
504 }
505 }
506
507 Node::BinaryOp { op, left, right } => {
508 match op.as_str() {
510 "&&" => {
511 self.compile_node(left)?;
512 let jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
513 self.chunk.emit(Op::Pop, self.line);
514 self.compile_node(right)?;
515 self.chunk.patch_jump(jump);
516 self.chunk.emit(Op::Not, self.line);
518 self.chunk.emit(Op::Not, self.line);
519 return Ok(());
520 }
521 "||" => {
522 self.compile_node(left)?;
523 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
524 self.chunk.emit(Op::Pop, self.line);
525 self.compile_node(right)?;
526 self.chunk.patch_jump(jump);
527 self.chunk.emit(Op::Not, self.line);
528 self.chunk.emit(Op::Not, self.line);
529 return Ok(());
530 }
531 "??" => {
532 self.compile_node(left)?;
533 self.chunk.emit(Op::Dup, self.line);
534 self.chunk.emit(Op::Nil, self.line);
536 self.chunk.emit(Op::NotEqual, self.line);
537 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
538 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(right)?;
541 let end = self.chunk.emit_jump(Op::Jump, self.line);
542 self.chunk.patch_jump(jump);
543 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
545 return Ok(());
546 }
547 "|>" => {
548 self.compile_node(left)?;
549 if contains_pipe_placeholder(right) {
552 let replaced = replace_pipe_placeholder(right);
553 let closure_node = SNode::dummy(Node::Closure {
554 params: vec![TypedParam {
555 name: "__pipe".into(),
556 type_expr: None,
557 default_value: None,
558 }],
559 body: vec![replaced],
560 });
561 self.compile_node(&closure_node)?;
562 } else {
563 self.compile_node(right)?;
564 }
565 self.chunk.emit(Op::Pipe, self.line);
566 return Ok(());
567 }
568 _ => {}
569 }
570
571 self.compile_node(left)?;
572 self.compile_node(right)?;
573 match op.as_str() {
574 "+" => self.chunk.emit(Op::Add, self.line),
575 "-" => self.chunk.emit(Op::Sub, self.line),
576 "*" => self.chunk.emit(Op::Mul, self.line),
577 "/" => self.chunk.emit(Op::Div, self.line),
578 "%" => self.chunk.emit(Op::Mod, self.line),
579 "==" => self.chunk.emit(Op::Equal, self.line),
580 "!=" => self.chunk.emit(Op::NotEqual, self.line),
581 "<" => self.chunk.emit(Op::Less, self.line),
582 ">" => self.chunk.emit(Op::Greater, self.line),
583 "<=" => self.chunk.emit(Op::LessEqual, self.line),
584 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
585 _ => {
586 return Err(CompileError {
587 message: format!("Unknown operator: {op}"),
588 line: self.line,
589 })
590 }
591 }
592 }
593
594 Node::UnaryOp { op, operand } => {
595 self.compile_node(operand)?;
596 match op.as_str() {
597 "-" => self.chunk.emit(Op::Negate, self.line),
598 "!" => self.chunk.emit(Op::Not, self.line),
599 _ => {}
600 }
601 }
602
603 Node::Ternary {
604 condition,
605 true_expr,
606 false_expr,
607 } => {
608 self.compile_node(condition)?;
609 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
610 self.chunk.emit(Op::Pop, self.line);
611 self.compile_node(true_expr)?;
612 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
613 self.chunk.patch_jump(else_jump);
614 self.chunk.emit(Op::Pop, self.line);
615 self.compile_node(false_expr)?;
616 self.chunk.patch_jump(end_jump);
617 }
618
619 Node::FunctionCall { name, args } => {
620 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
621 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
623 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
624
625 if has_spread {
626 self.chunk.emit_u16(Op::BuildList, 0, self.line);
629 let mut pending = 0u16;
630 for arg in args {
631 if let Node::Spread(inner) = &arg.node {
632 if pending > 0 {
633 self.chunk.emit_u16(Op::BuildList, pending, self.line);
634 self.chunk.emit(Op::Add, self.line);
635 pending = 0;
636 }
637 self.compile_node(inner)?;
638 self.chunk.emit(Op::Dup, self.line);
639 let assert_idx = self
640 .chunk
641 .add_constant(Constant::String("__assert_list".into()));
642 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
643 self.chunk.emit(Op::Swap, self.line);
644 self.chunk.emit_u8(Op::Call, 1, self.line);
645 self.chunk.emit(Op::Pop, self.line);
646 self.chunk.emit(Op::Add, self.line);
647 } else {
648 self.compile_node(arg)?;
649 pending += 1;
650 }
651 }
652 if pending > 0 {
653 self.chunk.emit_u16(Op::BuildList, pending, self.line);
654 self.chunk.emit(Op::Add, self.line);
655 }
656 self.chunk.emit(Op::CallSpread, self.line);
657 } else {
658 for arg in args {
660 self.compile_node(arg)?;
661 }
662 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
663 }
664 }
665
666 Node::MethodCall {
667 object,
668 method,
669 args,
670 } => {
671 if let Node::Identifier(name) = &object.node {
673 if self.enum_names.contains(name) {
674 for arg in args {
676 self.compile_node(arg)?;
677 }
678 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
679 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
680 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
681 let hi = (var_idx >> 8) as u8;
682 let lo = var_idx as u8;
683 self.chunk.code.push(hi);
684 self.chunk.code.push(lo);
685 self.chunk.lines.push(self.line);
686 self.chunk.columns.push(self.column);
687 self.chunk.lines.push(self.line);
688 self.chunk.columns.push(self.column);
689 let fc = args.len() as u16;
690 let fhi = (fc >> 8) as u8;
691 let flo = fc as u8;
692 self.chunk.code.push(fhi);
693 self.chunk.code.push(flo);
694 self.chunk.lines.push(self.line);
695 self.chunk.columns.push(self.column);
696 self.chunk.lines.push(self.line);
697 self.chunk.columns.push(self.column);
698 return Ok(());
699 }
700 }
701 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
702 self.compile_node(object)?;
703 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
704 if has_spread {
705 self.chunk.emit_u16(Op::BuildList, 0, self.line);
707 let mut pending = 0u16;
708 for arg in args {
709 if let Node::Spread(inner) = &arg.node {
710 if pending > 0 {
711 self.chunk.emit_u16(Op::BuildList, pending, self.line);
712 self.chunk.emit(Op::Add, self.line);
713 pending = 0;
714 }
715 self.compile_node(inner)?;
716 self.chunk.emit(Op::Dup, self.line);
717 let assert_idx = self
718 .chunk
719 .add_constant(Constant::String("__assert_list".into()));
720 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
721 self.chunk.emit(Op::Swap, self.line);
722 self.chunk.emit_u8(Op::Call, 1, self.line);
723 self.chunk.emit(Op::Pop, self.line);
724 self.chunk.emit(Op::Add, self.line);
725 } else {
726 self.compile_node(arg)?;
727 pending += 1;
728 }
729 }
730 if pending > 0 {
731 self.chunk.emit_u16(Op::BuildList, pending, self.line);
732 self.chunk.emit(Op::Add, self.line);
733 }
734 self.chunk
735 .emit_u16(Op::MethodCallSpread, name_idx, self.line);
736 } else {
737 for arg in args {
738 self.compile_node(arg)?;
739 }
740 self.chunk
741 .emit_method_call(name_idx, args.len() as u8, self.line);
742 }
743 }
744
745 Node::OptionalMethodCall {
746 object,
747 method,
748 args,
749 } => {
750 self.compile_node(object)?;
751 for arg in args {
752 self.compile_node(arg)?;
753 }
754 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
755 self.chunk
756 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
757 }
758
759 Node::PropertyAccess { object, property } => {
760 if let Node::Identifier(name) = &object.node {
762 if self.enum_names.contains(name) {
763 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
765 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
766 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
767 let hi = (var_idx >> 8) as u8;
768 let lo = var_idx as u8;
769 self.chunk.code.push(hi);
770 self.chunk.code.push(lo);
771 self.chunk.lines.push(self.line);
772 self.chunk.columns.push(self.column);
773 self.chunk.lines.push(self.line);
774 self.chunk.columns.push(self.column);
775 self.chunk.code.push(0);
777 self.chunk.code.push(0);
778 self.chunk.lines.push(self.line);
779 self.chunk.columns.push(self.column);
780 self.chunk.lines.push(self.line);
781 self.chunk.columns.push(self.column);
782 return Ok(());
783 }
784 }
785 self.compile_node(object)?;
786 let idx = self.chunk.add_constant(Constant::String(property.clone()));
787 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
788 }
789
790 Node::OptionalPropertyAccess { object, property } => {
791 self.compile_node(object)?;
792 let idx = self.chunk.add_constant(Constant::String(property.clone()));
793 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
794 }
795
796 Node::SubscriptAccess { object, index } => {
797 self.compile_node(object)?;
798 self.compile_node(index)?;
799 self.chunk.emit(Op::Subscript, self.line);
800 }
801
802 Node::SliceAccess { object, start, end } => {
803 self.compile_node(object)?;
804 if let Some(s) = start {
805 self.compile_node(s)?;
806 } else {
807 self.chunk.emit(Op::Nil, self.line);
808 }
809 if let Some(e) = end {
810 self.compile_node(e)?;
811 } else {
812 self.chunk.emit(Op::Nil, self.line);
813 }
814 self.chunk.emit(Op::Slice, self.line);
815 }
816
817 Node::IfElse {
818 condition,
819 then_body,
820 else_body,
821 } => {
822 self.compile_node(condition)?;
823 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
824 self.chunk.emit(Op::Pop, self.line);
825 self.compile_block(then_body)?;
826 if let Some(else_body) = else_body {
827 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
828 self.chunk.patch_jump(else_jump);
829 self.chunk.emit(Op::Pop, self.line);
830 self.compile_block(else_body)?;
831 self.chunk.patch_jump(end_jump);
832 } else {
833 self.chunk.patch_jump(else_jump);
834 self.chunk.emit(Op::Pop, self.line);
835 self.chunk.emit(Op::Nil, self.line);
836 }
837 }
838
839 Node::WhileLoop { condition, body } => {
840 let loop_start = self.chunk.current_offset();
841 self.loop_stack.push(LoopContext {
842 start_offset: loop_start,
843 break_patches: Vec::new(),
844 has_iterator: false,
845 handler_depth: self.handler_depth,
846 finally_depth: self.finally_bodies.len(),
847 });
848 self.compile_node(condition)?;
849 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
850 self.chunk.emit(Op::Pop, self.line); for sn in body {
853 self.compile_node(sn)?;
854 if Self::produces_value(&sn.node) {
855 self.chunk.emit(Op::Pop, self.line);
856 }
857 }
858 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
860 self.chunk.patch_jump(exit_jump);
861 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
864 for patch_pos in ctx.break_patches {
865 self.chunk.patch_jump(patch_pos);
866 }
867 self.chunk.emit(Op::Nil, self.line);
868 }
869
870 Node::ForIn {
871 pattern,
872 iterable,
873 body,
874 } => {
875 self.compile_node(iterable)?;
877 self.chunk.emit(Op::IterInit, self.line);
879 let loop_start = self.chunk.current_offset();
880 self.loop_stack.push(LoopContext {
881 start_offset: loop_start,
882 break_patches: Vec::new(),
883 has_iterator: true,
884 handler_depth: self.handler_depth,
885 finally_depth: self.finally_bodies.len(),
886 });
887 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
889 self.compile_destructuring(pattern, true)?;
891 for sn in body {
893 self.compile_node(sn)?;
894 if Self::produces_value(&sn.node) {
895 self.chunk.emit(Op::Pop, self.line);
896 }
897 }
898 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
900 self.chunk.patch_jump(exit_jump_pos);
901 let ctx = self.loop_stack.pop().unwrap();
903 for patch_pos in ctx.break_patches {
904 self.chunk.patch_jump(patch_pos);
905 }
906 self.chunk.emit(Op::Nil, self.line);
908 }
909
910 Node::ReturnStmt { value } => {
911 let has_pending_finally = !self.finally_bodies.is_empty();
912
913 if has_pending_finally {
914 if let Some(val) = value {
917 self.compile_node(val)?;
918 } else {
919 self.chunk.emit(Op::Nil, self.line);
920 }
921 self.temp_counter += 1;
922 let temp_name = format!("__return_val_{}__", self.temp_counter);
923 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
924 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
925 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
927 for fb in &finallys {
928 self.compile_finally_inline(fb)?;
929 }
930 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
931 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
932 self.chunk.emit(Op::Return, self.line);
933 } else {
934 if let Some(val) = value {
936 if let Node::FunctionCall { name, args } = &val.node {
937 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
938 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
939 for arg in args {
940 self.compile_node(arg)?;
941 }
942 self.chunk
943 .emit_u8(Op::TailCall, args.len() as u8, self.line);
944 } else if let Node::BinaryOp { op, left, right } = &val.node {
945 if op == "|>" {
946 self.compile_node(left)?;
947 self.compile_node(right)?;
948 self.chunk.emit(Op::Swap, self.line);
949 self.chunk.emit_u8(Op::TailCall, 1, self.line);
950 } else {
951 self.compile_node(val)?;
952 }
953 } else {
954 self.compile_node(val)?;
955 }
956 } else {
957 self.chunk.emit(Op::Nil, self.line);
958 }
959 self.chunk.emit(Op::Return, self.line);
960 }
961 }
962
963 Node::BreakStmt => {
964 if self.loop_stack.is_empty() {
965 return Err(CompileError {
966 message: "break outside of loop".to_string(),
967 line: self.line,
968 });
969 }
970 let ctx = self.loop_stack.last().unwrap();
972 let finally_depth = ctx.finally_depth;
973 let handler_depth = ctx.handler_depth;
974 let has_iterator = ctx.has_iterator;
975 for _ in handler_depth..self.handler_depth {
977 self.chunk.emit(Op::PopHandler, self.line);
978 }
979 if self.finally_bodies.len() > finally_depth {
981 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
982 .iter()
983 .rev()
984 .cloned()
985 .collect();
986 for fb in &finallys {
987 self.compile_finally_inline(fb)?;
988 }
989 }
990 if has_iterator {
991 self.chunk.emit(Op::PopIterator, self.line);
992 }
993 let patch = self.chunk.emit_jump(Op::Jump, self.line);
994 self.loop_stack
995 .last_mut()
996 .unwrap()
997 .break_patches
998 .push(patch);
999 }
1000
1001 Node::ContinueStmt => {
1002 if self.loop_stack.is_empty() {
1003 return Err(CompileError {
1004 message: "continue outside of loop".to_string(),
1005 line: self.line,
1006 });
1007 }
1008 let ctx = self.loop_stack.last().unwrap();
1009 let finally_depth = ctx.finally_depth;
1010 let handler_depth = ctx.handler_depth;
1011 let loop_start = ctx.start_offset;
1012 for _ in handler_depth..self.handler_depth {
1013 self.chunk.emit(Op::PopHandler, self.line);
1014 }
1015 if self.finally_bodies.len() > finally_depth {
1016 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1017 .iter()
1018 .rev()
1019 .cloned()
1020 .collect();
1021 for fb in &finallys {
1022 self.compile_finally_inline(fb)?;
1023 }
1024 }
1025 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1026 }
1027
1028 Node::ListLiteral(elements) => {
1029 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
1030 if !has_spread {
1031 for el in elements {
1032 self.compile_node(el)?;
1033 }
1034 self.chunk
1035 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
1036 } else {
1037 self.chunk.emit_u16(Op::BuildList, 0, self.line);
1040 let mut pending = 0u16;
1041 for el in elements {
1042 if let Node::Spread(inner) = &el.node {
1043 if pending > 0 {
1045 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1046 self.chunk.emit(Op::Add, self.line);
1048 pending = 0;
1049 }
1050 self.compile_node(inner)?;
1052 self.chunk.emit(Op::Dup, self.line);
1053 let assert_idx = self
1054 .chunk
1055 .add_constant(Constant::String("__assert_list".into()));
1056 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1057 self.chunk.emit(Op::Swap, self.line);
1058 self.chunk.emit_u8(Op::Call, 1, self.line);
1059 self.chunk.emit(Op::Pop, self.line);
1060 self.chunk.emit(Op::Add, self.line);
1061 } else {
1062 self.compile_node(el)?;
1063 pending += 1;
1064 }
1065 }
1066 if pending > 0 {
1067 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1068 self.chunk.emit(Op::Add, self.line);
1069 }
1070 }
1071 }
1072
1073 Node::DictLiteral(entries) => {
1074 let has_spread = entries
1075 .iter()
1076 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1077 if !has_spread {
1078 for entry in entries {
1079 self.compile_node(&entry.key)?;
1080 self.compile_node(&entry.value)?;
1081 }
1082 self.chunk
1083 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1084 } else {
1085 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1087 let mut pending = 0u16;
1088 for entry in entries {
1089 if let Node::Spread(inner) = &entry.value.node {
1090 if pending > 0 {
1092 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1093 self.chunk.emit(Op::Add, self.line);
1094 pending = 0;
1095 }
1096 self.compile_node(inner)?;
1098 self.chunk.emit(Op::Dup, self.line);
1099 let assert_idx = self
1100 .chunk
1101 .add_constant(Constant::String("__assert_dict".into()));
1102 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1103 self.chunk.emit(Op::Swap, self.line);
1104 self.chunk.emit_u8(Op::Call, 1, self.line);
1105 self.chunk.emit(Op::Pop, self.line);
1106 self.chunk.emit(Op::Add, self.line);
1107 } else {
1108 self.compile_node(&entry.key)?;
1109 self.compile_node(&entry.value)?;
1110 pending += 1;
1111 }
1112 }
1113 if pending > 0 {
1114 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1115 self.chunk.emit(Op::Add, self.line);
1116 }
1117 }
1118 }
1119
1120 Node::InterpolatedString(segments) => {
1121 let mut part_count = 0u16;
1122 for seg in segments {
1123 match seg {
1124 StringSegment::Literal(s) => {
1125 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1126 self.chunk.emit_u16(Op::Constant, idx, self.line);
1127 part_count += 1;
1128 }
1129 StringSegment::Expression(expr_str) => {
1130 let mut lexer = harn_lexer::Lexer::new(expr_str);
1132 if let Ok(tokens) = lexer.tokenize() {
1133 let mut parser = harn_parser::Parser::new(tokens);
1134 if let Ok(snode) = parser.parse_single_expression() {
1135 self.compile_node(&snode)?;
1136 let to_str = self
1138 .chunk
1139 .add_constant(Constant::String("to_string".into()));
1140 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1141 self.chunk.emit(Op::Swap, self.line);
1142 self.chunk.emit_u8(Op::Call, 1, self.line);
1143 part_count += 1;
1144 } else {
1145 let idx =
1147 self.chunk.add_constant(Constant::String(expr_str.clone()));
1148 self.chunk.emit_u16(Op::Constant, idx, self.line);
1149 part_count += 1;
1150 }
1151 }
1152 }
1153 }
1154 }
1155 if part_count > 1 {
1156 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1157 }
1158 }
1159
1160 Node::FnDecl {
1161 name, params, body, ..
1162 } => {
1163 let mut fn_compiler = Compiler::new();
1165 fn_compiler.enum_names = self.enum_names.clone();
1166 fn_compiler.emit_default_preamble(params)?;
1167 fn_compiler.emit_type_checks(params);
1168 let is_gen = body_contains_yield(body);
1169 fn_compiler.compile_block(body)?;
1170 fn_compiler.chunk.emit(Op::Nil, self.line);
1171 fn_compiler.chunk.emit(Op::Return, self.line);
1172
1173 let func = CompiledFunction {
1174 name: name.clone(),
1175 params: TypedParam::names(params),
1176 default_start: TypedParam::default_start(params),
1177 chunk: fn_compiler.chunk,
1178 is_generator: is_gen,
1179 };
1180 let fn_idx = self.chunk.functions.len();
1181 self.chunk.functions.push(func);
1182
1183 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1184 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1185 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1186 }
1187
1188 Node::Closure { params, body } => {
1189 let mut fn_compiler = Compiler::new();
1190 fn_compiler.enum_names = self.enum_names.clone();
1191 fn_compiler.emit_default_preamble(params)?;
1192 fn_compiler.emit_type_checks(params);
1193 let is_gen = body_contains_yield(body);
1194 fn_compiler.compile_block(body)?;
1195 fn_compiler.chunk.emit(Op::Return, self.line);
1197
1198 let func = CompiledFunction {
1199 name: "<closure>".to_string(),
1200 params: TypedParam::names(params),
1201 default_start: TypedParam::default_start(params),
1202 chunk: fn_compiler.chunk,
1203 is_generator: is_gen,
1204 };
1205 let fn_idx = self.chunk.functions.len();
1206 self.chunk.functions.push(func);
1207
1208 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1209 }
1210
1211 Node::ThrowStmt { value } => {
1212 self.compile_node(value)?;
1213 self.chunk.emit(Op::Throw, self.line);
1214 }
1215
1216 Node::MatchExpr { value, arms } => {
1217 self.compile_node(value)?;
1218 let mut end_jumps = Vec::new();
1219 for arm in arms {
1220 match &arm.pattern.node {
1221 Node::Identifier(name) if name == "_" => {
1223 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1225 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1226 }
1227 Node::EnumConstruct {
1229 enum_name,
1230 variant,
1231 args: pat_args,
1232 } => {
1233 self.chunk.emit(Op::Dup, self.line);
1235 let en_idx =
1236 self.chunk.add_constant(Constant::String(enum_name.clone()));
1237 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1238 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1239 let hi = (vn_idx >> 8) as u8;
1240 let lo = vn_idx as u8;
1241 self.chunk.code.push(hi);
1242 self.chunk.code.push(lo);
1243 self.chunk.lines.push(self.line);
1244 self.chunk.columns.push(self.column);
1245 self.chunk.lines.push(self.line);
1246 self.chunk.columns.push(self.column);
1247 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1249 self.chunk.emit(Op::Pop, self.line); for (i, pat_arg) in pat_args.iter().enumerate() {
1254 if let Node::Identifier(binding_name) = &pat_arg.node {
1255 self.chunk.emit(Op::Dup, self.line);
1257 let fields_idx = self
1258 .chunk
1259 .add_constant(Constant::String("fields".to_string()));
1260 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1261 let idx_const =
1262 self.chunk.add_constant(Constant::Int(i as i64));
1263 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1264 self.chunk.emit(Op::Subscript, self.line);
1265 let name_idx = self
1266 .chunk
1267 .add_constant(Constant::String(binding_name.clone()));
1268 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1269 }
1270 }
1271
1272 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1274 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1275 self.chunk.patch_jump(skip);
1276 self.chunk.emit(Op::Pop, self.line); }
1278 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1280 {
1281 let enum_name = if let Node::Identifier(n) = &object.node {
1282 n.clone()
1283 } else {
1284 unreachable!()
1285 };
1286 self.chunk.emit(Op::Dup, self.line);
1287 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1288 let vn_idx =
1289 self.chunk.add_constant(Constant::String(property.clone()));
1290 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1291 let hi = (vn_idx >> 8) as u8;
1292 let lo = vn_idx as u8;
1293 self.chunk.code.push(hi);
1294 self.chunk.code.push(lo);
1295 self.chunk.lines.push(self.line);
1296 self.chunk.columns.push(self.column);
1297 self.chunk.lines.push(self.line);
1298 self.chunk.columns.push(self.column);
1299 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1300 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1303 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1304 self.chunk.patch_jump(skip);
1305 self.chunk.emit(Op::Pop, self.line); }
1307 Node::MethodCall {
1310 object,
1311 method,
1312 args: pat_args,
1313 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1314 {
1315 let enum_name = if let Node::Identifier(n) = &object.node {
1316 n.clone()
1317 } else {
1318 unreachable!()
1319 };
1320 self.chunk.emit(Op::Dup, self.line);
1322 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1323 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1324 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1325 let hi = (vn_idx >> 8) as u8;
1326 let lo = vn_idx as u8;
1327 self.chunk.code.push(hi);
1328 self.chunk.code.push(lo);
1329 self.chunk.lines.push(self.line);
1330 self.chunk.columns.push(self.column);
1331 self.chunk.lines.push(self.line);
1332 self.chunk.columns.push(self.column);
1333 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1334 self.chunk.emit(Op::Pop, self.line); for (i, pat_arg) in pat_args.iter().enumerate() {
1338 if let Node::Identifier(binding_name) = &pat_arg.node {
1339 self.chunk.emit(Op::Dup, self.line);
1340 let fields_idx = self
1341 .chunk
1342 .add_constant(Constant::String("fields".to_string()));
1343 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1344 let idx_const =
1345 self.chunk.add_constant(Constant::Int(i as i64));
1346 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1347 self.chunk.emit(Op::Subscript, self.line);
1348 let name_idx = self
1349 .chunk
1350 .add_constant(Constant::String(binding_name.clone()));
1351 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1352 }
1353 }
1354
1355 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1357 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1358 self.chunk.patch_jump(skip);
1359 self.chunk.emit(Op::Pop, self.line); }
1361 Node::Identifier(name) => {
1363 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1366 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1367 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1369 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1370 }
1371 Node::DictLiteral(entries)
1373 if entries
1374 .iter()
1375 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1376 {
1377 self.chunk.emit(Op::Dup, self.line);
1379 let typeof_idx =
1380 self.chunk.add_constant(Constant::String("type_of".into()));
1381 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1382 self.chunk.emit(Op::Swap, self.line);
1383 self.chunk.emit_u8(Op::Call, 1, self.line);
1384 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1385 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1386 self.chunk.emit(Op::Equal, self.line);
1387 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1388 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1392 let mut bindings = Vec::new();
1393 for entry in entries {
1394 if let Node::StringLiteral(key) = &entry.key.node {
1395 match &entry.value.node {
1396 Node::StringLiteral(_)
1398 | Node::IntLiteral(_)
1399 | Node::FloatLiteral(_)
1400 | Node::BoolLiteral(_)
1401 | Node::NilLiteral => {
1402 self.chunk.emit(Op::Dup, self.line);
1403 let key_idx = self
1404 .chunk
1405 .add_constant(Constant::String(key.clone()));
1406 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1407 self.chunk.emit(Op::Subscript, self.line);
1408 self.compile_node(&entry.value)?;
1409 self.chunk.emit(Op::Equal, self.line);
1410 let skip =
1411 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1412 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1414 }
1415 Node::Identifier(binding) => {
1417 bindings.push((key.clone(), binding.clone()));
1418 }
1419 _ => {
1420 self.chunk.emit(Op::Dup, self.line);
1422 let key_idx = self
1423 .chunk
1424 .add_constant(Constant::String(key.clone()));
1425 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1426 self.chunk.emit(Op::Subscript, self.line);
1427 self.compile_node(&entry.value)?;
1428 self.chunk.emit(Op::Equal, self.line);
1429 let skip =
1430 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1431 self.chunk.emit(Op::Pop, self.line);
1432 constraint_skips.push(skip);
1433 }
1434 }
1435 }
1436 }
1437
1438 for (key, binding) in &bindings {
1440 self.chunk.emit(Op::Dup, self.line);
1441 let key_idx =
1442 self.chunk.add_constant(Constant::String(key.clone()));
1443 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1444 self.chunk.emit(Op::Subscript, self.line);
1445 let name_idx =
1446 self.chunk.add_constant(Constant::String(binding.clone()));
1447 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1448 }
1449
1450 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1452 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1453
1454 let fail_target = self.chunk.code.len();
1456 self.chunk.emit(Op::Pop, self.line); for skip in constraint_skips {
1459 self.chunk.patch_jump_to(skip, fail_target);
1460 }
1461 self.chunk.patch_jump_to(skip_type, fail_target);
1462 }
1463 Node::ListLiteral(elements) => {
1465 self.chunk.emit(Op::Dup, self.line);
1467 let typeof_idx =
1468 self.chunk.add_constant(Constant::String("type_of".into()));
1469 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1470 self.chunk.emit(Op::Swap, self.line);
1471 self.chunk.emit_u8(Op::Call, 1, self.line);
1472 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1473 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1474 self.chunk.emit(Op::Equal, self.line);
1475 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1476 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1480 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1481 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1482 self.chunk.emit(Op::Swap, self.line);
1483 self.chunk.emit_u8(Op::Call, 1, self.line);
1484 let count = self
1485 .chunk
1486 .add_constant(Constant::Int(elements.len() as i64));
1487 self.chunk.emit_u16(Op::Constant, count, self.line);
1488 self.chunk.emit(Op::GreaterEqual, self.line);
1489 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1490 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1494 let mut bindings = Vec::new();
1495 for (i, elem) in elements.iter().enumerate() {
1496 match &elem.node {
1497 Node::Identifier(name) if name != "_" => {
1498 bindings.push((i, name.clone()));
1499 }
1500 Node::Identifier(_) => {} _ => {
1503 self.chunk.emit(Op::Dup, self.line);
1504 let idx_const =
1505 self.chunk.add_constant(Constant::Int(i as i64));
1506 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1507 self.chunk.emit(Op::Subscript, self.line);
1508 self.compile_node(elem)?;
1509 self.chunk.emit(Op::Equal, self.line);
1510 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1511 self.chunk.emit(Op::Pop, self.line);
1512 constraint_skips.push(skip);
1513 }
1514 }
1515 }
1516
1517 for (i, name) in &bindings {
1519 self.chunk.emit(Op::Dup, self.line);
1520 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1521 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1522 self.chunk.emit(Op::Subscript, self.line);
1523 let name_idx =
1524 self.chunk.add_constant(Constant::String(name.clone()));
1525 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1526 }
1527
1528 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1530 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1531
1532 let fail_target = self.chunk.code.len();
1534 self.chunk.emit(Op::Pop, self.line); for skip in constraint_skips {
1536 self.chunk.patch_jump_to(skip, fail_target);
1537 }
1538 self.chunk.patch_jump_to(skip_len, fail_target);
1539 self.chunk.patch_jump_to(skip_type, fail_target);
1540 }
1541 _ => {
1543 self.chunk.emit(Op::Dup, self.line);
1544 self.compile_node(&arm.pattern)?;
1545 self.chunk.emit(Op::Equal, self.line);
1546 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1547 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1550 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1551 self.chunk.patch_jump(skip);
1552 self.chunk.emit(Op::Pop, self.line); }
1554 }
1555 }
1556 self.chunk.emit(Op::Pop, self.line);
1558 self.chunk.emit(Op::Nil, self.line);
1559 for j in end_jumps {
1560 self.chunk.patch_jump(j);
1561 }
1562 }
1563
1564 Node::RangeExpr {
1565 start,
1566 end,
1567 inclusive,
1568 } => {
1569 let name_idx = self
1571 .chunk
1572 .add_constant(Constant::String("__range__".to_string()));
1573 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1574 self.compile_node(start)?;
1575 self.compile_node(end)?;
1576 if *inclusive {
1577 self.chunk.emit(Op::True, self.line);
1578 } else {
1579 self.chunk.emit(Op::False, self.line);
1580 }
1581 self.chunk.emit_u8(Op::Call, 3, self.line);
1582 }
1583
1584 Node::GuardStmt {
1585 condition,
1586 else_body,
1587 } => {
1588 self.compile_node(condition)?;
1591 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1592 self.chunk.emit(Op::Pop, self.line); self.compile_block(else_body)?;
1595 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
1597 self.chunk.emit(Op::Pop, self.line);
1598 }
1599 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1600 self.chunk.patch_jump(skip_jump);
1601 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
1603 self.chunk.emit(Op::Nil, self.line);
1604 }
1605
1606 Node::Block(stmts) => {
1607 if stmts.is_empty() {
1608 self.chunk.emit(Op::Nil, self.line);
1609 } else {
1610 self.compile_block(stmts)?;
1611 }
1612 }
1613
1614 Node::DeadlineBlock { duration, body } => {
1615 self.compile_node(duration)?;
1616 self.chunk.emit(Op::DeadlineSetup, self.line);
1617 if body.is_empty() {
1618 self.chunk.emit(Op::Nil, self.line);
1619 } else {
1620 self.compile_block(body)?;
1621 }
1622 self.chunk.emit(Op::DeadlineEnd, self.line);
1623 }
1624
1625 Node::MutexBlock { body } => {
1626 if body.is_empty() {
1628 self.chunk.emit(Op::Nil, self.line);
1629 } else {
1630 for sn in body {
1633 self.compile_node(sn)?;
1634 if Self::produces_value(&sn.node) {
1635 self.chunk.emit(Op::Pop, self.line);
1636 }
1637 }
1638 self.chunk.emit(Op::Nil, self.line);
1639 }
1640 }
1641
1642 Node::YieldExpr { value } => {
1643 if let Some(val) = value {
1644 self.compile_node(val)?;
1645 } else {
1646 self.chunk.emit(Op::Nil, self.line);
1647 }
1648 self.chunk.emit(Op::Yield, self.line);
1649 }
1650
1651 Node::AskExpr { fields } => {
1652 for entry in fields {
1655 self.compile_node(&entry.key)?;
1656 self.compile_node(&entry.value)?;
1657 }
1658 self.chunk
1659 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
1660 }
1661
1662 Node::EnumConstruct {
1663 enum_name,
1664 variant,
1665 args,
1666 } => {
1667 for arg in args {
1669 self.compile_node(arg)?;
1670 }
1671 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
1672 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1673 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
1675 let hi = (var_idx >> 8) as u8;
1676 let lo = var_idx as u8;
1677 self.chunk.code.push(hi);
1678 self.chunk.code.push(lo);
1679 self.chunk.lines.push(self.line);
1680 self.chunk.columns.push(self.column);
1681 self.chunk.lines.push(self.line);
1682 self.chunk.columns.push(self.column);
1683 let fc = args.len() as u16;
1684 let fhi = (fc >> 8) as u8;
1685 let flo = fc as u8;
1686 self.chunk.code.push(fhi);
1687 self.chunk.code.push(flo);
1688 self.chunk.lines.push(self.line);
1689 self.chunk.columns.push(self.column);
1690 self.chunk.lines.push(self.line);
1691 self.chunk.columns.push(self.column);
1692 }
1693
1694 Node::StructConstruct {
1695 struct_name,
1696 fields,
1697 } => {
1698 let struct_key = self
1700 .chunk
1701 .add_constant(Constant::String("__struct__".to_string()));
1702 let struct_val = self
1703 .chunk
1704 .add_constant(Constant::String(struct_name.clone()));
1705 self.chunk.emit_u16(Op::Constant, struct_key, self.line);
1706 self.chunk.emit_u16(Op::Constant, struct_val, self.line);
1707
1708 for entry in fields {
1709 self.compile_node(&entry.key)?;
1710 self.compile_node(&entry.value)?;
1711 }
1712 self.chunk
1713 .emit_u16(Op::BuildDict, (fields.len() + 1) as u16, self.line);
1714 }
1715
1716 Node::ImportDecl { path } => {
1717 let idx = self.chunk.add_constant(Constant::String(path.clone()));
1718 self.chunk.emit_u16(Op::Import, idx, self.line);
1719 }
1720
1721 Node::SelectiveImport { names, path } => {
1722 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
1723 let names_str = names.join(",");
1724 let names_idx = self.chunk.add_constant(Constant::String(names_str));
1725 self.chunk
1726 .emit_u16(Op::SelectiveImport, path_idx, self.line);
1727 let hi = (names_idx >> 8) as u8;
1728 let lo = names_idx as u8;
1729 self.chunk.code.push(hi);
1730 self.chunk.code.push(lo);
1731 self.chunk.lines.push(self.line);
1732 self.chunk.columns.push(self.column);
1733 self.chunk.lines.push(self.line);
1734 self.chunk.columns.push(self.column);
1735 }
1736
1737 Node::TryOperator { operand } => {
1738 self.compile_node(operand)?;
1739 self.chunk.emit(Op::TryUnwrap, self.line);
1740 }
1741
1742 Node::ImplBlock { type_name, methods } => {
1743 for method_sn in methods {
1746 if let Node::FnDecl {
1747 name, params, body, ..
1748 } = &method_sn.node
1749 {
1750 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
1752 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1753
1754 let mut fn_compiler = Compiler::new();
1756 fn_compiler.enum_names = self.enum_names.clone();
1757 fn_compiler.emit_default_preamble(params)?;
1758 fn_compiler.emit_type_checks(params);
1759 fn_compiler.compile_block(body)?;
1760 fn_compiler.chunk.emit(Op::Nil, self.line);
1761 fn_compiler.chunk.emit(Op::Return, self.line);
1762
1763 let func = CompiledFunction {
1764 name: format!("{}.{}", type_name, name),
1765 params: TypedParam::names(params),
1766 default_start: TypedParam::default_start(params),
1767 chunk: fn_compiler.chunk,
1768 is_generator: false,
1769 };
1770 let fn_idx = self.chunk.functions.len();
1771 self.chunk.functions.push(func);
1772 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1773 }
1774 }
1775 let method_count = methods
1776 .iter()
1777 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
1778 .count();
1779 self.chunk
1780 .emit_u16(Op::BuildDict, method_count as u16, self.line);
1781 let impl_name = format!("__impl_{}", type_name);
1782 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
1783 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1784 }
1785
1786 Node::StructDecl { name, .. } => {
1787 let mut fn_compiler = Compiler::new();
1789 fn_compiler.enum_names = self.enum_names.clone();
1790 let params = vec![TypedParam::untyped("__fields")];
1791 fn_compiler.emit_default_preamble(¶ms)?;
1792
1793 let make_idx = fn_compiler
1795 .chunk
1796 .add_constant(Constant::String("__make_struct".into()));
1797 fn_compiler
1798 .chunk
1799 .emit_u16(Op::Constant, make_idx, self.line);
1800 let sname_idx = fn_compiler
1801 .chunk
1802 .add_constant(Constant::String(name.clone()));
1803 fn_compiler
1804 .chunk
1805 .emit_u16(Op::Constant, sname_idx, self.line);
1806 let fields_idx = fn_compiler
1807 .chunk
1808 .add_constant(Constant::String("__fields".into()));
1809 fn_compiler
1810 .chunk
1811 .emit_u16(Op::GetVar, fields_idx, self.line);
1812 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
1813 fn_compiler.chunk.emit(Op::Return, self.line);
1814
1815 let func = CompiledFunction {
1816 name: name.clone(),
1817 params: TypedParam::names(¶ms),
1818 default_start: None,
1819 chunk: fn_compiler.chunk,
1820 is_generator: false,
1821 };
1822 let fn_idx = self.chunk.functions.len();
1823 self.chunk.functions.push(func);
1824 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1825 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1826 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1827 }
1828
1829 Node::Pipeline { .. }
1831 | Node::OverrideDecl { .. }
1832 | Node::TypeDecl { .. }
1833 | Node::EnumDecl { .. }
1834 | Node::InterfaceDecl { .. } => {
1835 self.chunk.emit(Op::Nil, self.line);
1836 }
1837
1838 Node::TryCatch {
1839 body,
1840 error_var,
1841 error_type,
1842 catch_body,
1843 finally_body,
1844 } => {
1845 let type_name = error_type.as_ref().and_then(|te| {
1847 if let harn_parser::TypeExpr::Named(name) = te {
1848 Some(name.clone())
1849 } else {
1850 None
1851 }
1852 });
1853
1854 let type_name_idx = if let Some(ref tn) = type_name {
1855 self.chunk.add_constant(Constant::String(tn.clone()))
1856 } else {
1857 self.chunk.add_constant(Constant::String(String::new()))
1858 };
1859
1860 let has_catch = !catch_body.is_empty() || error_var.is_some();
1861 let has_finally = finally_body.is_some();
1862
1863 if has_catch && has_finally {
1864 let finally_body = finally_body.as_ref().unwrap();
1866
1867 self.finally_bodies.push(finally_body.clone());
1869
1870 self.handler_depth += 1;
1872 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1873 self.emit_type_name_extra(type_name_idx);
1874
1875 self.compile_try_body(body)?;
1877
1878 self.handler_depth -= 1;
1880 self.chunk.emit(Op::PopHandler, self.line);
1881 self.compile_finally_inline(finally_body)?;
1882 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1883
1884 self.chunk.patch_jump(catch_jump);
1886 self.compile_catch_binding(error_var)?;
1887
1888 self.handler_depth += 1;
1890 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1891 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1892 self.emit_type_name_extra(empty_type);
1893
1894 self.compile_try_body(catch_body)?;
1896
1897 self.handler_depth -= 1;
1899 self.chunk.emit(Op::PopHandler, self.line);
1900 self.compile_finally_inline(finally_body)?;
1901 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
1902
1903 self.chunk.patch_jump(rethrow_jump);
1905 self.compile_rethrow_with_finally(finally_body)?;
1906
1907 self.chunk.patch_jump(end_jump);
1908 self.chunk.patch_jump(end_jump2);
1909
1910 self.finally_bodies.pop();
1911 } else if has_finally {
1912 let finally_body = finally_body.as_ref().unwrap();
1914
1915 self.finally_bodies.push(finally_body.clone());
1916
1917 self.handler_depth += 1;
1919 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1920 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1921 self.emit_type_name_extra(empty_type);
1922
1923 self.compile_try_body(body)?;
1925
1926 self.handler_depth -= 1;
1928 self.chunk.emit(Op::PopHandler, self.line);
1929 self.compile_finally_inline(finally_body)?;
1930 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1931
1932 self.chunk.patch_jump(error_jump);
1934 self.compile_rethrow_with_finally(finally_body)?;
1935
1936 self.chunk.patch_jump(end_jump);
1937
1938 self.finally_bodies.pop();
1939 } else {
1940 self.handler_depth += 1;
1944 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1945 self.emit_type_name_extra(type_name_idx);
1946
1947 self.compile_try_body(body)?;
1949
1950 self.handler_depth -= 1;
1952 self.chunk.emit(Op::PopHandler, self.line);
1953 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1954
1955 self.chunk.patch_jump(catch_jump);
1957 self.compile_catch_binding(error_var)?;
1958
1959 self.compile_try_body(catch_body)?;
1961
1962 self.chunk.patch_jump(end_jump);
1964 }
1965 }
1966
1967 Node::TryExpr { body } => {
1968 self.handler_depth += 1;
1972 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1973 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1974 self.emit_type_name_extra(empty_type);
1975
1976 self.compile_try_body(body)?;
1978
1979 self.handler_depth -= 1;
1981 self.chunk.emit(Op::PopHandler, self.line);
1982
1983 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
1985 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
1986 self.chunk.emit(Op::Swap, self.line);
1987 self.chunk.emit_u8(Op::Call, 1, self.line);
1988
1989 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1991
1992 self.chunk.patch_jump(catch_jump);
1994
1995 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
1997 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
1998 self.chunk.emit(Op::Swap, self.line);
1999 self.chunk.emit_u8(Op::Call, 1, self.line);
2000
2001 self.chunk.patch_jump(end_jump);
2003 }
2004
2005 Node::Retry { count, body } => {
2006 self.compile_node(count)?;
2008 let counter_name = "__retry_counter__";
2009 let counter_idx = self
2010 .chunk
2011 .add_constant(Constant::String(counter_name.to_string()));
2012 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
2013
2014 self.chunk.emit(Op::Nil, self.line);
2016 let err_name = "__retry_last_error__";
2017 let err_idx = self
2018 .chunk
2019 .add_constant(Constant::String(err_name.to_string()));
2020 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
2021
2022 let loop_start = self.chunk.current_offset();
2024
2025 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2027 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2029 let hi = (empty_type >> 8) as u8;
2030 let lo = empty_type as u8;
2031 self.chunk.code.push(hi);
2032 self.chunk.code.push(lo);
2033 self.chunk.lines.push(self.line);
2034 self.chunk.columns.push(self.column);
2035 self.chunk.lines.push(self.line);
2036 self.chunk.columns.push(self.column);
2037
2038 self.compile_block(body)?;
2040
2041 self.chunk.emit(Op::PopHandler, self.line);
2043 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2044
2045 self.chunk.patch_jump(catch_jump);
2047 self.chunk.emit(Op::Dup, self.line);
2049 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
2050 self.chunk.emit(Op::Pop, self.line);
2052
2053 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
2055 let one_idx = self.chunk.add_constant(Constant::Int(1));
2056 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
2057 self.chunk.emit(Op::Sub, self.line);
2058 self.chunk.emit(Op::Dup, self.line);
2059 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
2060
2061 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2063 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2064 self.chunk.emit(Op::Greater, self.line);
2065 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2066 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
2068
2069 self.chunk.patch_jump(retry_jump);
2071 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
2073 self.chunk.emit(Op::Throw, self.line);
2074
2075 self.chunk.patch_jump(end_jump);
2076 self.chunk.emit(Op::Nil, self.line);
2078 }
2079
2080 Node::Parallel {
2081 count,
2082 variable,
2083 body,
2084 } => {
2085 self.compile_node(count)?;
2086 let mut fn_compiler = Compiler::new();
2087 fn_compiler.enum_names = self.enum_names.clone();
2088 fn_compiler.compile_block(body)?;
2089 fn_compiler.chunk.emit(Op::Return, self.line);
2090 let params = vec![variable.clone().unwrap_or_else(|| "__i__".to_string())];
2091 let func = CompiledFunction {
2092 name: "<parallel>".to_string(),
2093 params,
2094 default_start: None,
2095 chunk: fn_compiler.chunk,
2096 is_generator: false,
2097 };
2098 let fn_idx = self.chunk.functions.len();
2099 self.chunk.functions.push(func);
2100 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2101 self.chunk.emit(Op::Parallel, self.line);
2102 }
2103
2104 Node::ParallelMap {
2105 list,
2106 variable,
2107 body,
2108 } => {
2109 self.compile_node(list)?;
2110 let mut fn_compiler = Compiler::new();
2111 fn_compiler.enum_names = self.enum_names.clone();
2112 fn_compiler.compile_block(body)?;
2113 fn_compiler.chunk.emit(Op::Return, self.line);
2114 let func = CompiledFunction {
2115 name: "<parallel_map>".to_string(),
2116 params: vec![variable.clone()],
2117 default_start: None,
2118 chunk: fn_compiler.chunk,
2119 is_generator: false,
2120 };
2121 let fn_idx = self.chunk.functions.len();
2122 self.chunk.functions.push(func);
2123 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2124 self.chunk.emit(Op::ParallelMap, self.line);
2125 }
2126
2127 Node::ParallelSettle {
2128 list,
2129 variable,
2130 body,
2131 } => {
2132 self.compile_node(list)?;
2133 let mut fn_compiler = Compiler::new();
2134 fn_compiler.enum_names = self.enum_names.clone();
2135 fn_compiler.compile_block(body)?;
2136 fn_compiler.chunk.emit(Op::Return, self.line);
2137 let func = CompiledFunction {
2138 name: "<parallel_settle>".to_string(),
2139 params: vec![variable.clone()],
2140 default_start: None,
2141 chunk: fn_compiler.chunk,
2142 is_generator: false,
2143 };
2144 let fn_idx = self.chunk.functions.len();
2145 self.chunk.functions.push(func);
2146 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2147 self.chunk.emit(Op::ParallelSettle, self.line);
2148 }
2149
2150 Node::SpawnExpr { body } => {
2151 let mut fn_compiler = Compiler::new();
2152 fn_compiler.enum_names = self.enum_names.clone();
2153 fn_compiler.compile_block(body)?;
2154 fn_compiler.chunk.emit(Op::Return, self.line);
2155 let func = CompiledFunction {
2156 name: "<spawn>".to_string(),
2157 params: vec![],
2158 default_start: None,
2159 chunk: fn_compiler.chunk,
2160 is_generator: false,
2161 };
2162 let fn_idx = self.chunk.functions.len();
2163 self.chunk.functions.push(func);
2164 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2165 self.chunk.emit(Op::Spawn, self.line);
2166 }
2167 Node::SelectExpr {
2168 cases,
2169 timeout,
2170 default_body,
2171 } => {
2172 let builtin_name = if timeout.is_some() {
2179 "__select_timeout"
2180 } else if default_body.is_some() {
2181 "__select_try"
2182 } else {
2183 "__select_list"
2184 };
2185
2186 let name_idx = self
2188 .chunk
2189 .add_constant(Constant::String(builtin_name.into()));
2190 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2191
2192 for case in cases {
2194 self.compile_node(&case.channel)?;
2195 }
2196 self.chunk
2197 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2198
2199 if let Some((duration_expr, _)) = timeout {
2201 self.compile_node(duration_expr)?;
2202 self.chunk.emit_u8(Op::Call, 2, self.line);
2203 } else {
2204 self.chunk.emit_u8(Op::Call, 1, self.line);
2205 }
2206
2207 self.temp_counter += 1;
2209 let result_name = format!("__sel_result_{}__", self.temp_counter);
2210 let result_idx = self
2211 .chunk
2212 .add_constant(Constant::String(result_name.clone()));
2213 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2214
2215 let mut end_jumps = Vec::new();
2217
2218 for (i, case) in cases.iter().enumerate() {
2219 let get_r = self
2220 .chunk
2221 .add_constant(Constant::String(result_name.clone()));
2222 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2223 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2224 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2225 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2226 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2227 self.chunk.emit(Op::Equal, self.line);
2228 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2229 self.chunk.emit(Op::Pop, self.line);
2230
2231 let get_r2 = self
2233 .chunk
2234 .add_constant(Constant::String(result_name.clone()));
2235 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2236 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2237 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2238 let var_idx = self
2239 .chunk
2240 .add_constant(Constant::String(case.variable.clone()));
2241 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2242
2243 self.compile_try_body(&case.body)?;
2244 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2245 self.chunk.patch_jump(skip);
2246 self.chunk.emit(Op::Pop, self.line);
2247 }
2248
2249 if let Some((_, ref timeout_body)) = timeout {
2251 self.compile_try_body(timeout_body)?;
2252 } else if let Some(ref def_body) = default_body {
2253 self.compile_try_body(def_body)?;
2254 } else {
2255 self.chunk.emit(Op::Nil, self.line);
2256 }
2257
2258 for ej in end_jumps {
2259 self.chunk.patch_jump(ej);
2260 }
2261 }
2262 Node::Spread(_) => {
2263 return Err(CompileError {
2264 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2265 line: self.line,
2266 });
2267 }
2268 }
2269 Ok(())
2270 }
2271
2272 fn compile_destructuring(
2276 &mut self,
2277 pattern: &BindingPattern,
2278 is_mutable: bool,
2279 ) -> Result<(), CompileError> {
2280 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2281 match pattern {
2282 BindingPattern::Identifier(name) => {
2283 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2285 self.chunk.emit_u16(def_op, idx, self.line);
2286 }
2287 BindingPattern::Dict(fields) => {
2288 self.chunk.emit(Op::Dup, self.line);
2291 let assert_idx = self
2292 .chunk
2293 .add_constant(Constant::String("__assert_dict".into()));
2294 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2295 self.chunk.emit(Op::Swap, self.line);
2296 self.chunk.emit_u8(Op::Call, 1, self.line);
2297 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2302 let rest_field = fields.iter().find(|f| f.is_rest);
2303
2304 for field in &non_rest {
2305 self.chunk.emit(Op::Dup, self.line);
2306 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2307 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2308 self.chunk.emit(Op::Subscript, self.line);
2309 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2310 let name_idx = self
2311 .chunk
2312 .add_constant(Constant::String(binding_name.to_string()));
2313 self.chunk.emit_u16(def_op, name_idx, self.line);
2314 }
2315
2316 if let Some(rest) = rest_field {
2317 let fn_idx = self
2320 .chunk
2321 .add_constant(Constant::String("__dict_rest".into()));
2322 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2323 self.chunk.emit(Op::Swap, self.line);
2325 for field in &non_rest {
2327 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2328 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2329 }
2330 self.chunk
2331 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2332 self.chunk.emit_u8(Op::Call, 2, self.line);
2334 let rest_name = &rest.key;
2335 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2336 self.chunk.emit_u16(def_op, rest_idx, self.line);
2337 } else {
2338 self.chunk.emit(Op::Pop, self.line);
2340 }
2341 }
2342 BindingPattern::List(elements) => {
2343 self.chunk.emit(Op::Dup, self.line);
2346 let assert_idx = self
2347 .chunk
2348 .add_constant(Constant::String("__assert_list".into()));
2349 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2350 self.chunk.emit(Op::Swap, self.line);
2351 self.chunk.emit_u8(Op::Call, 1, self.line);
2352 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2355 let rest_elem = elements.iter().find(|e| e.is_rest);
2356
2357 for (i, elem) in non_rest.iter().enumerate() {
2358 self.chunk.emit(Op::Dup, self.line);
2359 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2360 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2361 self.chunk.emit(Op::Subscript, self.line);
2362 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2363 self.chunk.emit_u16(def_op, name_idx, self.line);
2364 }
2365
2366 if let Some(rest) = rest_elem {
2367 let start_idx = self
2371 .chunk
2372 .add_constant(Constant::Int(non_rest.len() as i64));
2373 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2374 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2376 let rest_name_idx =
2377 self.chunk.add_constant(Constant::String(rest.name.clone()));
2378 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2379 } else {
2380 self.chunk.emit(Op::Pop, self.line);
2382 }
2383 }
2384 }
2385 Ok(())
2386 }
2387
2388 fn produces_value(node: &Node) -> bool {
2390 match node {
2391 Node::LetBinding { .. }
2393 | Node::VarBinding { .. }
2394 | Node::Assignment { .. }
2395 | Node::ReturnStmt { .. }
2396 | Node::FnDecl { .. }
2397 | Node::ImplBlock { .. }
2398 | Node::StructDecl { .. }
2399 | Node::ThrowStmt { .. }
2400 | Node::BreakStmt
2401 | Node::ContinueStmt => false,
2402 Node::TryCatch { .. }
2404 | Node::TryExpr { .. }
2405 | Node::Retry { .. }
2406 | Node::GuardStmt { .. }
2407 | Node::DeadlineBlock { .. }
2408 | Node::MutexBlock { .. }
2409 | Node::Spread(_) => true,
2410 _ => true,
2412 }
2413 }
2414}
2415
2416impl Compiler {
2417 pub fn compile_fn_body(
2419 &mut self,
2420 params: &[TypedParam],
2421 body: &[SNode],
2422 ) -> Result<CompiledFunction, CompileError> {
2423 let mut fn_compiler = Compiler::new();
2424 fn_compiler.compile_block(body)?;
2425 fn_compiler.chunk.emit(Op::Nil, 0);
2426 fn_compiler.chunk.emit(Op::Return, 0);
2427 Ok(CompiledFunction {
2428 name: String::new(),
2429 params: TypedParam::names(params),
2430 default_start: TypedParam::default_start(params),
2431 chunk: fn_compiler.chunk,
2432 is_generator: false,
2433 })
2434 }
2435
2436 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2438 if body.is_empty() {
2439 self.chunk.emit(Op::Nil, self.line);
2440 } else {
2441 self.compile_block(body)?;
2442 if !Self::produces_value(&body.last().unwrap().node) {
2444 self.chunk.emit(Op::Nil, self.line);
2445 }
2446 }
2447 Ok(())
2448 }
2449
2450 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2452 match op {
2453 "+" => self.chunk.emit(Op::Add, self.line),
2454 "-" => self.chunk.emit(Op::Sub, self.line),
2455 "*" => self.chunk.emit(Op::Mul, self.line),
2456 "/" => self.chunk.emit(Op::Div, self.line),
2457 "%" => self.chunk.emit(Op::Mod, self.line),
2458 _ => {
2459 return Err(CompileError {
2460 message: format!("Unknown compound operator: {op}"),
2461 line: self.line,
2462 })
2463 }
2464 }
2465 Ok(())
2466 }
2467
2468 fn root_var_name(&self, node: &SNode) -> Option<String> {
2470 match &node.node {
2471 Node::Identifier(name) => Some(name.clone()),
2472 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2473 self.root_var_name(object)
2474 }
2475 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2476 _ => None,
2477 }
2478 }
2479}
2480
2481impl Compiler {
2482 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
2484 for sn in nodes {
2485 match &sn.node {
2486 Node::EnumDecl { name, .. } => {
2487 names.insert(name.clone());
2488 }
2489 Node::Pipeline { body, .. } => {
2490 Self::collect_enum_names(body, names);
2491 }
2492 Node::FnDecl { body, .. } => {
2493 Self::collect_enum_names(body, names);
2494 }
2495 Node::Block(stmts) => {
2496 Self::collect_enum_names(stmts, names);
2497 }
2498 _ => {}
2499 }
2500 }
2501 }
2502
2503 fn collect_interface_methods(
2504 nodes: &[SNode],
2505 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
2506 ) {
2507 for sn in nodes {
2508 match &sn.node {
2509 Node::InterfaceDecl { name, methods } => {
2510 let method_names: Vec<String> =
2511 methods.iter().map(|m| m.name.clone()).collect();
2512 interfaces.insert(name.clone(), method_names);
2513 }
2514 Node::Pipeline { body, .. } | Node::FnDecl { body, .. } => {
2515 Self::collect_interface_methods(body, interfaces);
2516 }
2517 Node::Block(stmts) => {
2518 Self::collect_interface_methods(stmts, interfaces);
2519 }
2520 _ => {}
2521 }
2522 }
2523 }
2524}
2525
2526impl Default for Compiler {
2527 fn default() -> Self {
2528 Self::new()
2529 }
2530}
2531
2532fn body_contains_yield(nodes: &[SNode]) -> bool {
2534 nodes.iter().any(|sn| node_contains_yield(&sn.node))
2535}
2536
2537fn node_contains_yield(node: &Node) -> bool {
2538 match node {
2539 Node::YieldExpr { .. } => true,
2540 Node::FnDecl { .. } | Node::Closure { .. } => false,
2543 Node::Block(stmts) => body_contains_yield(stmts),
2544 Node::IfElse {
2545 condition,
2546 then_body,
2547 else_body,
2548 } => {
2549 node_contains_yield(&condition.node)
2550 || body_contains_yield(then_body)
2551 || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
2552 }
2553 Node::WhileLoop { condition, body } => {
2554 node_contains_yield(&condition.node) || body_contains_yield(body)
2555 }
2556 Node::ForIn { iterable, body, .. } => {
2557 node_contains_yield(&iterable.node) || body_contains_yield(body)
2558 }
2559 Node::TryCatch {
2560 body, catch_body, ..
2561 } => body_contains_yield(body) || body_contains_yield(catch_body),
2562 Node::TryExpr { body } => body_contains_yield(body),
2563 _ => false,
2564 }
2565}
2566
2567fn contains_pipe_placeholder(node: &SNode) -> bool {
2569 match &node.node {
2570 Node::Identifier(name) if name == "_" => true,
2571 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
2572 Node::MethodCall { object, args, .. } => {
2573 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
2574 }
2575 Node::BinaryOp { left, right, .. } => {
2576 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
2577 }
2578 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
2579 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
2580 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
2581 Node::SubscriptAccess { object, index } => {
2582 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
2583 }
2584 _ => false,
2585 }
2586}
2587
2588fn replace_pipe_placeholder(node: &SNode) -> SNode {
2590 let new_node = match &node.node {
2591 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
2592 Node::FunctionCall { name, args } => Node::FunctionCall {
2593 name: name.clone(),
2594 args: args.iter().map(replace_pipe_placeholder).collect(),
2595 },
2596 Node::MethodCall {
2597 object,
2598 method,
2599 args,
2600 } => Node::MethodCall {
2601 object: Box::new(replace_pipe_placeholder(object)),
2602 method: method.clone(),
2603 args: args.iter().map(replace_pipe_placeholder).collect(),
2604 },
2605 Node::BinaryOp { op, left, right } => Node::BinaryOp {
2606 op: op.clone(),
2607 left: Box::new(replace_pipe_placeholder(left)),
2608 right: Box::new(replace_pipe_placeholder(right)),
2609 },
2610 Node::UnaryOp { op, operand } => Node::UnaryOp {
2611 op: op.clone(),
2612 operand: Box::new(replace_pipe_placeholder(operand)),
2613 },
2614 Node::ListLiteral(items) => {
2615 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
2616 }
2617 Node::PropertyAccess { object, property } => Node::PropertyAccess {
2618 object: Box::new(replace_pipe_placeholder(object)),
2619 property: property.clone(),
2620 },
2621 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
2622 object: Box::new(replace_pipe_placeholder(object)),
2623 index: Box::new(replace_pipe_placeholder(index)),
2624 },
2625 _ => return node.clone(),
2626 };
2627 SNode::new(new_node, node.span)
2628}
2629
2630#[cfg(test)]
2631mod tests {
2632 use super::*;
2633 use harn_lexer::Lexer;
2634 use harn_parser::Parser;
2635
2636 fn compile_source(source: &str) -> Chunk {
2637 let mut lexer = Lexer::new(source);
2638 let tokens = lexer.tokenize().unwrap();
2639 let mut parser = Parser::new(tokens);
2640 let program = parser.parse().unwrap();
2641 Compiler::new().compile(&program).unwrap()
2642 }
2643
2644 #[test]
2645 fn test_compile_arithmetic() {
2646 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
2647 assert!(!chunk.code.is_empty());
2648 assert!(chunk.constants.contains(&Constant::Int(2)));
2650 assert!(chunk.constants.contains(&Constant::Int(3)));
2651 }
2652
2653 #[test]
2654 fn test_compile_function_call() {
2655 let chunk = compile_source("pipeline test(task) { log(42) }");
2656 let disasm = chunk.disassemble("test");
2657 assert!(disasm.contains("CALL"));
2658 }
2659
2660 #[test]
2661 fn test_compile_if_else() {
2662 let chunk =
2663 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
2664 let disasm = chunk.disassemble("test");
2665 assert!(disasm.contains("JUMP_IF_FALSE"));
2666 assert!(disasm.contains("JUMP"));
2667 }
2668
2669 #[test]
2670 fn test_compile_while() {
2671 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
2672 let disasm = chunk.disassemble("test");
2673 assert!(disasm.contains("JUMP_IF_FALSE"));
2674 assert!(disasm.contains("JUMP"));
2676 }
2677
2678 #[test]
2679 fn test_compile_closure() {
2680 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
2681 assert!(!chunk.functions.is_empty());
2682 assert_eq!(chunk.functions[0].params, vec!["x"]);
2683 }
2684
2685 #[test]
2686 fn test_compile_list() {
2687 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
2688 let disasm = chunk.disassemble("test");
2689 assert!(disasm.contains("BUILD_LIST"));
2690 }
2691
2692 #[test]
2693 fn test_compile_dict() {
2694 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
2695 let disasm = chunk.disassemble("test");
2696 assert!(disasm.contains("BUILD_DICT"));
2697 }
2698
2699 #[test]
2700 fn test_disassemble() {
2701 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
2702 let disasm = chunk.disassemble("test");
2703 assert!(disasm.contains("CONSTANT"));
2705 assert!(disasm.contains("ADD"));
2706 assert!(disasm.contains("CALL"));
2707 }
2708}