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 fn_syntax: false,
561 });
562 self.compile_node(&closure_node)?;
563 } else {
564 self.compile_node(right)?;
565 }
566 self.chunk.emit(Op::Pipe, self.line);
567 return Ok(());
568 }
569 _ => {}
570 }
571
572 self.compile_node(left)?;
573 self.compile_node(right)?;
574 match op.as_str() {
575 "+" => self.chunk.emit(Op::Add, self.line),
576 "-" => self.chunk.emit(Op::Sub, self.line),
577 "*" => self.chunk.emit(Op::Mul, self.line),
578 "/" => self.chunk.emit(Op::Div, self.line),
579 "%" => self.chunk.emit(Op::Mod, self.line),
580 "==" => self.chunk.emit(Op::Equal, self.line),
581 "!=" => self.chunk.emit(Op::NotEqual, self.line),
582 "<" => self.chunk.emit(Op::Less, self.line),
583 ">" => self.chunk.emit(Op::Greater, self.line),
584 "<=" => self.chunk.emit(Op::LessEqual, self.line),
585 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
586 _ => {
587 return Err(CompileError {
588 message: format!("Unknown operator: {op}"),
589 line: self.line,
590 })
591 }
592 }
593 }
594
595 Node::UnaryOp { op, operand } => {
596 self.compile_node(operand)?;
597 match op.as_str() {
598 "-" => self.chunk.emit(Op::Negate, self.line),
599 "!" => self.chunk.emit(Op::Not, self.line),
600 _ => {}
601 }
602 }
603
604 Node::Ternary {
605 condition,
606 true_expr,
607 false_expr,
608 } => {
609 self.compile_node(condition)?;
610 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
611 self.chunk.emit(Op::Pop, self.line);
612 self.compile_node(true_expr)?;
613 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
614 self.chunk.patch_jump(else_jump);
615 self.chunk.emit(Op::Pop, self.line);
616 self.compile_node(false_expr)?;
617 self.chunk.patch_jump(end_jump);
618 }
619
620 Node::FunctionCall { name, args } => {
621 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
622 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
624 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
625
626 if has_spread {
627 self.chunk.emit_u16(Op::BuildList, 0, self.line);
630 let mut pending = 0u16;
631 for arg in args {
632 if let Node::Spread(inner) = &arg.node {
633 if pending > 0 {
634 self.chunk.emit_u16(Op::BuildList, pending, self.line);
635 self.chunk.emit(Op::Add, self.line);
636 pending = 0;
637 }
638 self.compile_node(inner)?;
639 self.chunk.emit(Op::Dup, self.line);
640 let assert_idx = self
641 .chunk
642 .add_constant(Constant::String("__assert_list".into()));
643 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
644 self.chunk.emit(Op::Swap, self.line);
645 self.chunk.emit_u8(Op::Call, 1, self.line);
646 self.chunk.emit(Op::Pop, self.line);
647 self.chunk.emit(Op::Add, self.line);
648 } else {
649 self.compile_node(arg)?;
650 pending += 1;
651 }
652 }
653 if pending > 0 {
654 self.chunk.emit_u16(Op::BuildList, pending, self.line);
655 self.chunk.emit(Op::Add, self.line);
656 }
657 self.chunk.emit(Op::CallSpread, self.line);
658 } else {
659 for arg in args {
661 self.compile_node(arg)?;
662 }
663 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
664 }
665 }
666
667 Node::MethodCall {
668 object,
669 method,
670 args,
671 } => {
672 if let Node::Identifier(name) = &object.node {
674 if self.enum_names.contains(name) {
675 for arg in args {
677 self.compile_node(arg)?;
678 }
679 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
680 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
681 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
682 let hi = (var_idx >> 8) as u8;
683 let lo = var_idx as u8;
684 self.chunk.code.push(hi);
685 self.chunk.code.push(lo);
686 self.chunk.lines.push(self.line);
687 self.chunk.columns.push(self.column);
688 self.chunk.lines.push(self.line);
689 self.chunk.columns.push(self.column);
690 let fc = args.len() as u16;
691 let fhi = (fc >> 8) as u8;
692 let flo = fc as u8;
693 self.chunk.code.push(fhi);
694 self.chunk.code.push(flo);
695 self.chunk.lines.push(self.line);
696 self.chunk.columns.push(self.column);
697 self.chunk.lines.push(self.line);
698 self.chunk.columns.push(self.column);
699 return Ok(());
700 }
701 }
702 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
703 self.compile_node(object)?;
704 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
705 if has_spread {
706 self.chunk.emit_u16(Op::BuildList, 0, self.line);
708 let mut pending = 0u16;
709 for arg in args {
710 if let Node::Spread(inner) = &arg.node {
711 if pending > 0 {
712 self.chunk.emit_u16(Op::BuildList, pending, self.line);
713 self.chunk.emit(Op::Add, self.line);
714 pending = 0;
715 }
716 self.compile_node(inner)?;
717 self.chunk.emit(Op::Dup, self.line);
718 let assert_idx = self
719 .chunk
720 .add_constant(Constant::String("__assert_list".into()));
721 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
722 self.chunk.emit(Op::Swap, self.line);
723 self.chunk.emit_u8(Op::Call, 1, self.line);
724 self.chunk.emit(Op::Pop, self.line);
725 self.chunk.emit(Op::Add, self.line);
726 } else {
727 self.compile_node(arg)?;
728 pending += 1;
729 }
730 }
731 if pending > 0 {
732 self.chunk.emit_u16(Op::BuildList, pending, self.line);
733 self.chunk.emit(Op::Add, self.line);
734 }
735 self.chunk
736 .emit_u16(Op::MethodCallSpread, name_idx, self.line);
737 } else {
738 for arg in args {
739 self.compile_node(arg)?;
740 }
741 self.chunk
742 .emit_method_call(name_idx, args.len() as u8, self.line);
743 }
744 }
745
746 Node::OptionalMethodCall {
747 object,
748 method,
749 args,
750 } => {
751 self.compile_node(object)?;
752 for arg in args {
753 self.compile_node(arg)?;
754 }
755 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
756 self.chunk
757 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
758 }
759
760 Node::PropertyAccess { object, property } => {
761 if let Node::Identifier(name) = &object.node {
763 if self.enum_names.contains(name) {
764 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
766 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
767 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
768 let hi = (var_idx >> 8) as u8;
769 let lo = var_idx as u8;
770 self.chunk.code.push(hi);
771 self.chunk.code.push(lo);
772 self.chunk.lines.push(self.line);
773 self.chunk.columns.push(self.column);
774 self.chunk.lines.push(self.line);
775 self.chunk.columns.push(self.column);
776 self.chunk.code.push(0);
778 self.chunk.code.push(0);
779 self.chunk.lines.push(self.line);
780 self.chunk.columns.push(self.column);
781 self.chunk.lines.push(self.line);
782 self.chunk.columns.push(self.column);
783 return Ok(());
784 }
785 }
786 self.compile_node(object)?;
787 let idx = self.chunk.add_constant(Constant::String(property.clone()));
788 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
789 }
790
791 Node::OptionalPropertyAccess { object, property } => {
792 self.compile_node(object)?;
793 let idx = self.chunk.add_constant(Constant::String(property.clone()));
794 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
795 }
796
797 Node::SubscriptAccess { object, index } => {
798 self.compile_node(object)?;
799 self.compile_node(index)?;
800 self.chunk.emit(Op::Subscript, self.line);
801 }
802
803 Node::SliceAccess { object, start, end } => {
804 self.compile_node(object)?;
805 if let Some(s) = start {
806 self.compile_node(s)?;
807 } else {
808 self.chunk.emit(Op::Nil, self.line);
809 }
810 if let Some(e) = end {
811 self.compile_node(e)?;
812 } else {
813 self.chunk.emit(Op::Nil, self.line);
814 }
815 self.chunk.emit(Op::Slice, self.line);
816 }
817
818 Node::IfElse {
819 condition,
820 then_body,
821 else_body,
822 } => {
823 self.compile_node(condition)?;
824 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
825 self.chunk.emit(Op::Pop, self.line);
826 self.compile_block(then_body)?;
827 if let Some(else_body) = else_body {
828 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
829 self.chunk.patch_jump(else_jump);
830 self.chunk.emit(Op::Pop, self.line);
831 self.compile_block(else_body)?;
832 self.chunk.patch_jump(end_jump);
833 } else {
834 self.chunk.patch_jump(else_jump);
835 self.chunk.emit(Op::Pop, self.line);
836 self.chunk.emit(Op::Nil, self.line);
837 }
838 }
839
840 Node::WhileLoop { condition, body } => {
841 let loop_start = self.chunk.current_offset();
842 self.loop_stack.push(LoopContext {
843 start_offset: loop_start,
844 break_patches: Vec::new(),
845 has_iterator: false,
846 handler_depth: self.handler_depth,
847 finally_depth: self.finally_bodies.len(),
848 });
849 self.compile_node(condition)?;
850 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
851 self.chunk.emit(Op::Pop, self.line); for sn in body {
854 self.compile_node(sn)?;
855 if Self::produces_value(&sn.node) {
856 self.chunk.emit(Op::Pop, self.line);
857 }
858 }
859 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
861 self.chunk.patch_jump(exit_jump);
862 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
865 for patch_pos in ctx.break_patches {
866 self.chunk.patch_jump(patch_pos);
867 }
868 self.chunk.emit(Op::Nil, self.line);
869 }
870
871 Node::ForIn {
872 pattern,
873 iterable,
874 body,
875 } => {
876 self.compile_node(iterable)?;
878 self.chunk.emit(Op::IterInit, self.line);
880 let loop_start = self.chunk.current_offset();
881 self.loop_stack.push(LoopContext {
882 start_offset: loop_start,
883 break_patches: Vec::new(),
884 has_iterator: true,
885 handler_depth: self.handler_depth,
886 finally_depth: self.finally_bodies.len(),
887 });
888 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
890 self.compile_destructuring(pattern, true)?;
892 for sn in body {
894 self.compile_node(sn)?;
895 if Self::produces_value(&sn.node) {
896 self.chunk.emit(Op::Pop, self.line);
897 }
898 }
899 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
901 self.chunk.patch_jump(exit_jump_pos);
902 let ctx = self.loop_stack.pop().unwrap();
904 for patch_pos in ctx.break_patches {
905 self.chunk.patch_jump(patch_pos);
906 }
907 self.chunk.emit(Op::Nil, self.line);
909 }
910
911 Node::ReturnStmt { value } => {
912 let has_pending_finally = !self.finally_bodies.is_empty();
913
914 if has_pending_finally {
915 if let Some(val) = value {
918 self.compile_node(val)?;
919 } else {
920 self.chunk.emit(Op::Nil, self.line);
921 }
922 self.temp_counter += 1;
923 let temp_name = format!("__return_val_{}__", self.temp_counter);
924 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
925 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
926 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
928 for fb in &finallys {
929 self.compile_finally_inline(fb)?;
930 }
931 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
932 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
933 self.chunk.emit(Op::Return, self.line);
934 } else {
935 if let Some(val) = value {
937 if let Node::FunctionCall { name, args } = &val.node {
938 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
939 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
940 for arg in args {
941 self.compile_node(arg)?;
942 }
943 self.chunk
944 .emit_u8(Op::TailCall, args.len() as u8, self.line);
945 } else if let Node::BinaryOp { op, left, right } = &val.node {
946 if op == "|>" {
947 self.compile_node(left)?;
948 self.compile_node(right)?;
949 self.chunk.emit(Op::Swap, self.line);
950 self.chunk.emit_u8(Op::TailCall, 1, self.line);
951 } else {
952 self.compile_node(val)?;
953 }
954 } else {
955 self.compile_node(val)?;
956 }
957 } else {
958 self.chunk.emit(Op::Nil, self.line);
959 }
960 self.chunk.emit(Op::Return, self.line);
961 }
962 }
963
964 Node::BreakStmt => {
965 if self.loop_stack.is_empty() {
966 return Err(CompileError {
967 message: "break outside of loop".to_string(),
968 line: self.line,
969 });
970 }
971 let ctx = self.loop_stack.last().unwrap();
973 let finally_depth = ctx.finally_depth;
974 let handler_depth = ctx.handler_depth;
975 let has_iterator = ctx.has_iterator;
976 for _ in handler_depth..self.handler_depth {
978 self.chunk.emit(Op::PopHandler, self.line);
979 }
980 if self.finally_bodies.len() > finally_depth {
982 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
983 .iter()
984 .rev()
985 .cloned()
986 .collect();
987 for fb in &finallys {
988 self.compile_finally_inline(fb)?;
989 }
990 }
991 if has_iterator {
992 self.chunk.emit(Op::PopIterator, self.line);
993 }
994 let patch = self.chunk.emit_jump(Op::Jump, self.line);
995 self.loop_stack
996 .last_mut()
997 .unwrap()
998 .break_patches
999 .push(patch);
1000 }
1001
1002 Node::ContinueStmt => {
1003 if self.loop_stack.is_empty() {
1004 return Err(CompileError {
1005 message: "continue outside of loop".to_string(),
1006 line: self.line,
1007 });
1008 }
1009 let ctx = self.loop_stack.last().unwrap();
1010 let finally_depth = ctx.finally_depth;
1011 let handler_depth = ctx.handler_depth;
1012 let loop_start = ctx.start_offset;
1013 for _ in handler_depth..self.handler_depth {
1014 self.chunk.emit(Op::PopHandler, self.line);
1015 }
1016 if self.finally_bodies.len() > finally_depth {
1017 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1018 .iter()
1019 .rev()
1020 .cloned()
1021 .collect();
1022 for fb in &finallys {
1023 self.compile_finally_inline(fb)?;
1024 }
1025 }
1026 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1027 }
1028
1029 Node::ListLiteral(elements) => {
1030 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
1031 if !has_spread {
1032 for el in elements {
1033 self.compile_node(el)?;
1034 }
1035 self.chunk
1036 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
1037 } else {
1038 self.chunk.emit_u16(Op::BuildList, 0, self.line);
1041 let mut pending = 0u16;
1042 for el in elements {
1043 if let Node::Spread(inner) = &el.node {
1044 if pending > 0 {
1046 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1047 self.chunk.emit(Op::Add, self.line);
1049 pending = 0;
1050 }
1051 self.compile_node(inner)?;
1053 self.chunk.emit(Op::Dup, self.line);
1054 let assert_idx = self
1055 .chunk
1056 .add_constant(Constant::String("__assert_list".into()));
1057 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1058 self.chunk.emit(Op::Swap, self.line);
1059 self.chunk.emit_u8(Op::Call, 1, self.line);
1060 self.chunk.emit(Op::Pop, self.line);
1061 self.chunk.emit(Op::Add, self.line);
1062 } else {
1063 self.compile_node(el)?;
1064 pending += 1;
1065 }
1066 }
1067 if pending > 0 {
1068 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1069 self.chunk.emit(Op::Add, self.line);
1070 }
1071 }
1072 }
1073
1074 Node::DictLiteral(entries) => {
1075 let has_spread = entries
1076 .iter()
1077 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1078 if !has_spread {
1079 for entry in entries {
1080 self.compile_node(&entry.key)?;
1081 self.compile_node(&entry.value)?;
1082 }
1083 self.chunk
1084 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1085 } else {
1086 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1088 let mut pending = 0u16;
1089 for entry in entries {
1090 if let Node::Spread(inner) = &entry.value.node {
1091 if pending > 0 {
1093 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1094 self.chunk.emit(Op::Add, self.line);
1095 pending = 0;
1096 }
1097 self.compile_node(inner)?;
1099 self.chunk.emit(Op::Dup, self.line);
1100 let assert_idx = self
1101 .chunk
1102 .add_constant(Constant::String("__assert_dict".into()));
1103 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1104 self.chunk.emit(Op::Swap, self.line);
1105 self.chunk.emit_u8(Op::Call, 1, self.line);
1106 self.chunk.emit(Op::Pop, self.line);
1107 self.chunk.emit(Op::Add, self.line);
1108 } else {
1109 self.compile_node(&entry.key)?;
1110 self.compile_node(&entry.value)?;
1111 pending += 1;
1112 }
1113 }
1114 if pending > 0 {
1115 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1116 self.chunk.emit(Op::Add, self.line);
1117 }
1118 }
1119 }
1120
1121 Node::InterpolatedString(segments) => {
1122 let mut part_count = 0u16;
1123 for seg in segments {
1124 match seg {
1125 StringSegment::Literal(s) => {
1126 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1127 self.chunk.emit_u16(Op::Constant, idx, self.line);
1128 part_count += 1;
1129 }
1130 StringSegment::Expression(expr_str) => {
1131 let mut lexer = harn_lexer::Lexer::new(expr_str);
1133 if let Ok(tokens) = lexer.tokenize() {
1134 let mut parser = harn_parser::Parser::new(tokens);
1135 if let Ok(snode) = parser.parse_single_expression() {
1136 self.compile_node(&snode)?;
1137 let to_str = self
1139 .chunk
1140 .add_constant(Constant::String("to_string".into()));
1141 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1142 self.chunk.emit(Op::Swap, self.line);
1143 self.chunk.emit_u8(Op::Call, 1, self.line);
1144 part_count += 1;
1145 } else {
1146 let idx =
1148 self.chunk.add_constant(Constant::String(expr_str.clone()));
1149 self.chunk.emit_u16(Op::Constant, idx, self.line);
1150 part_count += 1;
1151 }
1152 }
1153 }
1154 }
1155 }
1156 if part_count > 1 {
1157 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1158 }
1159 }
1160
1161 Node::FnDecl {
1162 name, params, body, ..
1163 } => {
1164 let mut fn_compiler = Compiler::new();
1166 fn_compiler.enum_names = self.enum_names.clone();
1167 fn_compiler.emit_default_preamble(params)?;
1168 fn_compiler.emit_type_checks(params);
1169 let is_gen = body_contains_yield(body);
1170 fn_compiler.compile_block(body)?;
1171 fn_compiler.chunk.emit(Op::Nil, self.line);
1172 fn_compiler.chunk.emit(Op::Return, self.line);
1173
1174 let func = CompiledFunction {
1175 name: name.clone(),
1176 params: TypedParam::names(params),
1177 default_start: TypedParam::default_start(params),
1178 chunk: fn_compiler.chunk,
1179 is_generator: is_gen,
1180 };
1181 let fn_idx = self.chunk.functions.len();
1182 self.chunk.functions.push(func);
1183
1184 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1185 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1186 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1187 }
1188
1189 Node::Closure { params, body, .. } => {
1190 let mut fn_compiler = Compiler::new();
1191 fn_compiler.enum_names = self.enum_names.clone();
1192 fn_compiler.emit_default_preamble(params)?;
1193 fn_compiler.emit_type_checks(params);
1194 let is_gen = body_contains_yield(body);
1195 fn_compiler.compile_block(body)?;
1196 fn_compiler.chunk.emit(Op::Return, self.line);
1198
1199 let func = CompiledFunction {
1200 name: "<closure>".to_string(),
1201 params: TypedParam::names(params),
1202 default_start: TypedParam::default_start(params),
1203 chunk: fn_compiler.chunk,
1204 is_generator: is_gen,
1205 };
1206 let fn_idx = self.chunk.functions.len();
1207 self.chunk.functions.push(func);
1208
1209 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1210 }
1211
1212 Node::ThrowStmt { value } => {
1213 self.compile_node(value)?;
1214 self.chunk.emit(Op::Throw, self.line);
1215 }
1216
1217 Node::MatchExpr { value, arms } => {
1218 self.compile_node(value)?;
1219 let mut end_jumps = Vec::new();
1220 for arm in arms {
1221 match &arm.pattern.node {
1222 Node::Identifier(name) if name == "_" => {
1224 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1226 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1227 }
1228 Node::EnumConstruct {
1230 enum_name,
1231 variant,
1232 args: pat_args,
1233 } => {
1234 self.chunk.emit(Op::Dup, self.line);
1236 let en_idx =
1237 self.chunk.add_constant(Constant::String(enum_name.clone()));
1238 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1239 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1240 let hi = (vn_idx >> 8) as u8;
1241 let lo = vn_idx as u8;
1242 self.chunk.code.push(hi);
1243 self.chunk.code.push(lo);
1244 self.chunk.lines.push(self.line);
1245 self.chunk.columns.push(self.column);
1246 self.chunk.lines.push(self.line);
1247 self.chunk.columns.push(self.column);
1248 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1250 self.chunk.emit(Op::Pop, self.line); for (i, pat_arg) in pat_args.iter().enumerate() {
1255 if let Node::Identifier(binding_name) = &pat_arg.node {
1256 self.chunk.emit(Op::Dup, self.line);
1258 let fields_idx = self
1259 .chunk
1260 .add_constant(Constant::String("fields".to_string()));
1261 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1262 let idx_const =
1263 self.chunk.add_constant(Constant::Int(i as i64));
1264 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1265 self.chunk.emit(Op::Subscript, self.line);
1266 let name_idx = self
1267 .chunk
1268 .add_constant(Constant::String(binding_name.clone()));
1269 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1270 }
1271 }
1272
1273 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1275 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1276 self.chunk.patch_jump(skip);
1277 self.chunk.emit(Op::Pop, self.line); }
1279 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1281 {
1282 let enum_name = if let Node::Identifier(n) = &object.node {
1283 n.clone()
1284 } else {
1285 unreachable!()
1286 };
1287 self.chunk.emit(Op::Dup, self.line);
1288 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1289 let vn_idx =
1290 self.chunk.add_constant(Constant::String(property.clone()));
1291 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1292 let hi = (vn_idx >> 8) as u8;
1293 let lo = vn_idx as u8;
1294 self.chunk.code.push(hi);
1295 self.chunk.code.push(lo);
1296 self.chunk.lines.push(self.line);
1297 self.chunk.columns.push(self.column);
1298 self.chunk.lines.push(self.line);
1299 self.chunk.columns.push(self.column);
1300 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1301 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1304 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1305 self.chunk.patch_jump(skip);
1306 self.chunk.emit(Op::Pop, self.line); }
1308 Node::MethodCall {
1311 object,
1312 method,
1313 args: pat_args,
1314 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1315 {
1316 let enum_name = if let Node::Identifier(n) = &object.node {
1317 n.clone()
1318 } else {
1319 unreachable!()
1320 };
1321 self.chunk.emit(Op::Dup, self.line);
1323 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1324 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1325 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1326 let hi = (vn_idx >> 8) as u8;
1327 let lo = vn_idx as u8;
1328 self.chunk.code.push(hi);
1329 self.chunk.code.push(lo);
1330 self.chunk.lines.push(self.line);
1331 self.chunk.columns.push(self.column);
1332 self.chunk.lines.push(self.line);
1333 self.chunk.columns.push(self.column);
1334 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1335 self.chunk.emit(Op::Pop, self.line); for (i, pat_arg) in pat_args.iter().enumerate() {
1339 if let Node::Identifier(binding_name) = &pat_arg.node {
1340 self.chunk.emit(Op::Dup, self.line);
1341 let fields_idx = self
1342 .chunk
1343 .add_constant(Constant::String("fields".to_string()));
1344 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1345 let idx_const =
1346 self.chunk.add_constant(Constant::Int(i as i64));
1347 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1348 self.chunk.emit(Op::Subscript, self.line);
1349 let name_idx = self
1350 .chunk
1351 .add_constant(Constant::String(binding_name.clone()));
1352 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1353 }
1354 }
1355
1356 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1358 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1359 self.chunk.patch_jump(skip);
1360 self.chunk.emit(Op::Pop, self.line); }
1362 Node::Identifier(name) => {
1364 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1367 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1368 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1370 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1371 }
1372 Node::DictLiteral(entries)
1374 if entries
1375 .iter()
1376 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1377 {
1378 self.chunk.emit(Op::Dup, self.line);
1380 let typeof_idx =
1381 self.chunk.add_constant(Constant::String("type_of".into()));
1382 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1383 self.chunk.emit(Op::Swap, self.line);
1384 self.chunk.emit_u8(Op::Call, 1, self.line);
1385 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1386 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1387 self.chunk.emit(Op::Equal, self.line);
1388 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1389 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1393 let mut bindings = Vec::new();
1394 for entry in entries {
1395 if let Node::StringLiteral(key) = &entry.key.node {
1396 match &entry.value.node {
1397 Node::StringLiteral(_)
1399 | Node::IntLiteral(_)
1400 | Node::FloatLiteral(_)
1401 | Node::BoolLiteral(_)
1402 | Node::NilLiteral => {
1403 self.chunk.emit(Op::Dup, self.line);
1404 let key_idx = self
1405 .chunk
1406 .add_constant(Constant::String(key.clone()));
1407 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1408 self.chunk.emit(Op::Subscript, self.line);
1409 self.compile_node(&entry.value)?;
1410 self.chunk.emit(Op::Equal, self.line);
1411 let skip =
1412 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1413 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1415 }
1416 Node::Identifier(binding) => {
1418 bindings.push((key.clone(), binding.clone()));
1419 }
1420 _ => {
1421 self.chunk.emit(Op::Dup, self.line);
1423 let key_idx = self
1424 .chunk
1425 .add_constant(Constant::String(key.clone()));
1426 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1427 self.chunk.emit(Op::Subscript, self.line);
1428 self.compile_node(&entry.value)?;
1429 self.chunk.emit(Op::Equal, self.line);
1430 let skip =
1431 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1432 self.chunk.emit(Op::Pop, self.line);
1433 constraint_skips.push(skip);
1434 }
1435 }
1436 }
1437 }
1438
1439 for (key, binding) in &bindings {
1441 self.chunk.emit(Op::Dup, self.line);
1442 let key_idx =
1443 self.chunk.add_constant(Constant::String(key.clone()));
1444 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1445 self.chunk.emit(Op::Subscript, self.line);
1446 let name_idx =
1447 self.chunk.add_constant(Constant::String(binding.clone()));
1448 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1449 }
1450
1451 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1453 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1454
1455 let fail_target = self.chunk.code.len();
1457 self.chunk.emit(Op::Pop, self.line); for skip in constraint_skips {
1460 self.chunk.patch_jump_to(skip, fail_target);
1461 }
1462 self.chunk.patch_jump_to(skip_type, fail_target);
1463 }
1464 Node::ListLiteral(elements) => {
1466 self.chunk.emit(Op::Dup, self.line);
1468 let typeof_idx =
1469 self.chunk.add_constant(Constant::String("type_of".into()));
1470 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1471 self.chunk.emit(Op::Swap, self.line);
1472 self.chunk.emit_u8(Op::Call, 1, self.line);
1473 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1474 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1475 self.chunk.emit(Op::Equal, self.line);
1476 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1477 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1481 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1482 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1483 self.chunk.emit(Op::Swap, self.line);
1484 self.chunk.emit_u8(Op::Call, 1, self.line);
1485 let count = self
1486 .chunk
1487 .add_constant(Constant::Int(elements.len() as i64));
1488 self.chunk.emit_u16(Op::Constant, count, self.line);
1489 self.chunk.emit(Op::GreaterEqual, self.line);
1490 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1491 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1495 let mut bindings = Vec::new();
1496 for (i, elem) in elements.iter().enumerate() {
1497 match &elem.node {
1498 Node::Identifier(name) if name != "_" => {
1499 bindings.push((i, name.clone()));
1500 }
1501 Node::Identifier(_) => {} _ => {
1504 self.chunk.emit(Op::Dup, self.line);
1505 let idx_const =
1506 self.chunk.add_constant(Constant::Int(i as i64));
1507 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1508 self.chunk.emit(Op::Subscript, self.line);
1509 self.compile_node(elem)?;
1510 self.chunk.emit(Op::Equal, self.line);
1511 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1512 self.chunk.emit(Op::Pop, self.line);
1513 constraint_skips.push(skip);
1514 }
1515 }
1516 }
1517
1518 for (i, name) in &bindings {
1520 self.chunk.emit(Op::Dup, self.line);
1521 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1522 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1523 self.chunk.emit(Op::Subscript, self.line);
1524 let name_idx =
1525 self.chunk.add_constant(Constant::String(name.clone()));
1526 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1527 }
1528
1529 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1531 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1532
1533 let fail_target = self.chunk.code.len();
1535 self.chunk.emit(Op::Pop, self.line); for skip in constraint_skips {
1537 self.chunk.patch_jump_to(skip, fail_target);
1538 }
1539 self.chunk.patch_jump_to(skip_len, fail_target);
1540 self.chunk.patch_jump_to(skip_type, fail_target);
1541 }
1542 _ => {
1544 self.chunk.emit(Op::Dup, self.line);
1545 self.compile_node(&arm.pattern)?;
1546 self.chunk.emit(Op::Equal, self.line);
1547 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1548 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1551 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1552 self.chunk.patch_jump(skip);
1553 self.chunk.emit(Op::Pop, self.line); }
1555 }
1556 }
1557 self.chunk.emit(Op::Pop, self.line);
1559 self.chunk.emit(Op::Nil, self.line);
1560 for j in end_jumps {
1561 self.chunk.patch_jump(j);
1562 }
1563 }
1564
1565 Node::RangeExpr {
1566 start,
1567 end,
1568 inclusive,
1569 } => {
1570 let name_idx = self
1572 .chunk
1573 .add_constant(Constant::String("__range__".to_string()));
1574 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1575 self.compile_node(start)?;
1576 self.compile_node(end)?;
1577 if *inclusive {
1578 self.chunk.emit(Op::True, self.line);
1579 } else {
1580 self.chunk.emit(Op::False, self.line);
1581 }
1582 self.chunk.emit_u8(Op::Call, 3, self.line);
1583 }
1584
1585 Node::GuardStmt {
1586 condition,
1587 else_body,
1588 } => {
1589 self.compile_node(condition)?;
1592 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1593 self.chunk.emit(Op::Pop, self.line); self.compile_block(else_body)?;
1596 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
1598 self.chunk.emit(Op::Pop, self.line);
1599 }
1600 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1601 self.chunk.patch_jump(skip_jump);
1602 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
1604 self.chunk.emit(Op::Nil, self.line);
1605 }
1606
1607 Node::Block(stmts) => {
1608 if stmts.is_empty() {
1609 self.chunk.emit(Op::Nil, self.line);
1610 } else {
1611 self.compile_block(stmts)?;
1612 }
1613 }
1614
1615 Node::DeadlineBlock { duration, body } => {
1616 self.compile_node(duration)?;
1617 self.chunk.emit(Op::DeadlineSetup, self.line);
1618 if body.is_empty() {
1619 self.chunk.emit(Op::Nil, self.line);
1620 } else {
1621 self.compile_block(body)?;
1622 }
1623 self.chunk.emit(Op::DeadlineEnd, self.line);
1624 }
1625
1626 Node::MutexBlock { body } => {
1627 if body.is_empty() {
1629 self.chunk.emit(Op::Nil, self.line);
1630 } else {
1631 for sn in body {
1634 self.compile_node(sn)?;
1635 if Self::produces_value(&sn.node) {
1636 self.chunk.emit(Op::Pop, self.line);
1637 }
1638 }
1639 self.chunk.emit(Op::Nil, self.line);
1640 }
1641 }
1642
1643 Node::YieldExpr { value } => {
1644 if let Some(val) = value {
1645 self.compile_node(val)?;
1646 } else {
1647 self.chunk.emit(Op::Nil, self.line);
1648 }
1649 self.chunk.emit(Op::Yield, self.line);
1650 }
1651
1652 Node::AskExpr { fields } => {
1653 for entry in fields {
1656 self.compile_node(&entry.key)?;
1657 self.compile_node(&entry.value)?;
1658 }
1659 self.chunk
1660 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
1661 }
1662
1663 Node::EnumConstruct {
1664 enum_name,
1665 variant,
1666 args,
1667 } => {
1668 for arg in args {
1670 self.compile_node(arg)?;
1671 }
1672 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
1673 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1674 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
1676 let hi = (var_idx >> 8) as u8;
1677 let lo = var_idx as u8;
1678 self.chunk.code.push(hi);
1679 self.chunk.code.push(lo);
1680 self.chunk.lines.push(self.line);
1681 self.chunk.columns.push(self.column);
1682 self.chunk.lines.push(self.line);
1683 self.chunk.columns.push(self.column);
1684 let fc = args.len() as u16;
1685 let fhi = (fc >> 8) as u8;
1686 let flo = fc as u8;
1687 self.chunk.code.push(fhi);
1688 self.chunk.code.push(flo);
1689 self.chunk.lines.push(self.line);
1690 self.chunk.columns.push(self.column);
1691 self.chunk.lines.push(self.line);
1692 self.chunk.columns.push(self.column);
1693 }
1694
1695 Node::StructConstruct {
1696 struct_name,
1697 fields,
1698 } => {
1699 let struct_key = self
1701 .chunk
1702 .add_constant(Constant::String("__struct__".to_string()));
1703 let struct_val = self
1704 .chunk
1705 .add_constant(Constant::String(struct_name.clone()));
1706 self.chunk.emit_u16(Op::Constant, struct_key, self.line);
1707 self.chunk.emit_u16(Op::Constant, struct_val, self.line);
1708
1709 for entry in fields {
1710 self.compile_node(&entry.key)?;
1711 self.compile_node(&entry.value)?;
1712 }
1713 self.chunk
1714 .emit_u16(Op::BuildDict, (fields.len() + 1) as u16, self.line);
1715 }
1716
1717 Node::ImportDecl { path } => {
1718 let idx = self.chunk.add_constant(Constant::String(path.clone()));
1719 self.chunk.emit_u16(Op::Import, idx, self.line);
1720 }
1721
1722 Node::SelectiveImport { names, path } => {
1723 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
1724 let names_str = names.join(",");
1725 let names_idx = self.chunk.add_constant(Constant::String(names_str));
1726 self.chunk
1727 .emit_u16(Op::SelectiveImport, path_idx, self.line);
1728 let hi = (names_idx >> 8) as u8;
1729 let lo = names_idx as u8;
1730 self.chunk.code.push(hi);
1731 self.chunk.code.push(lo);
1732 self.chunk.lines.push(self.line);
1733 self.chunk.columns.push(self.column);
1734 self.chunk.lines.push(self.line);
1735 self.chunk.columns.push(self.column);
1736 }
1737
1738 Node::TryOperator { operand } => {
1739 self.compile_node(operand)?;
1740 self.chunk.emit(Op::TryUnwrap, self.line);
1741 }
1742
1743 Node::ImplBlock { type_name, methods } => {
1744 for method_sn in methods {
1747 if let Node::FnDecl {
1748 name, params, body, ..
1749 } = &method_sn.node
1750 {
1751 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
1753 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1754
1755 let mut fn_compiler = Compiler::new();
1757 fn_compiler.enum_names = self.enum_names.clone();
1758 fn_compiler.emit_default_preamble(params)?;
1759 fn_compiler.emit_type_checks(params);
1760 fn_compiler.compile_block(body)?;
1761 fn_compiler.chunk.emit(Op::Nil, self.line);
1762 fn_compiler.chunk.emit(Op::Return, self.line);
1763
1764 let func = CompiledFunction {
1765 name: format!("{}.{}", type_name, name),
1766 params: TypedParam::names(params),
1767 default_start: TypedParam::default_start(params),
1768 chunk: fn_compiler.chunk,
1769 is_generator: false,
1770 };
1771 let fn_idx = self.chunk.functions.len();
1772 self.chunk.functions.push(func);
1773 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1774 }
1775 }
1776 let method_count = methods
1777 .iter()
1778 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
1779 .count();
1780 self.chunk
1781 .emit_u16(Op::BuildDict, method_count as u16, self.line);
1782 let impl_name = format!("__impl_{}", type_name);
1783 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
1784 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1785 }
1786
1787 Node::StructDecl { name, .. } => {
1788 let mut fn_compiler = Compiler::new();
1790 fn_compiler.enum_names = self.enum_names.clone();
1791 let params = vec![TypedParam::untyped("__fields")];
1792 fn_compiler.emit_default_preamble(¶ms)?;
1793
1794 let make_idx = fn_compiler
1796 .chunk
1797 .add_constant(Constant::String("__make_struct".into()));
1798 fn_compiler
1799 .chunk
1800 .emit_u16(Op::Constant, make_idx, self.line);
1801 let sname_idx = fn_compiler
1802 .chunk
1803 .add_constant(Constant::String(name.clone()));
1804 fn_compiler
1805 .chunk
1806 .emit_u16(Op::Constant, sname_idx, self.line);
1807 let fields_idx = fn_compiler
1808 .chunk
1809 .add_constant(Constant::String("__fields".into()));
1810 fn_compiler
1811 .chunk
1812 .emit_u16(Op::GetVar, fields_idx, self.line);
1813 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
1814 fn_compiler.chunk.emit(Op::Return, self.line);
1815
1816 let func = CompiledFunction {
1817 name: name.clone(),
1818 params: TypedParam::names(¶ms),
1819 default_start: None,
1820 chunk: fn_compiler.chunk,
1821 is_generator: false,
1822 };
1823 let fn_idx = self.chunk.functions.len();
1824 self.chunk.functions.push(func);
1825 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1826 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1827 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1828 }
1829
1830 Node::Pipeline { .. }
1832 | Node::OverrideDecl { .. }
1833 | Node::TypeDecl { .. }
1834 | Node::EnumDecl { .. }
1835 | Node::InterfaceDecl { .. } => {
1836 self.chunk.emit(Op::Nil, self.line);
1837 }
1838
1839 Node::TryCatch {
1840 body,
1841 error_var,
1842 error_type,
1843 catch_body,
1844 finally_body,
1845 } => {
1846 let type_name = error_type.as_ref().and_then(|te| {
1848 if let harn_parser::TypeExpr::Named(name) = te {
1849 Some(name.clone())
1850 } else {
1851 None
1852 }
1853 });
1854
1855 let type_name_idx = if let Some(ref tn) = type_name {
1856 self.chunk.add_constant(Constant::String(tn.clone()))
1857 } else {
1858 self.chunk.add_constant(Constant::String(String::new()))
1859 };
1860
1861 let has_catch = !catch_body.is_empty() || error_var.is_some();
1862 let has_finally = finally_body.is_some();
1863
1864 if has_catch && has_finally {
1865 let finally_body = finally_body.as_ref().unwrap();
1867
1868 self.finally_bodies.push(finally_body.clone());
1870
1871 self.handler_depth += 1;
1873 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1874 self.emit_type_name_extra(type_name_idx);
1875
1876 self.compile_try_body(body)?;
1878
1879 self.handler_depth -= 1;
1881 self.chunk.emit(Op::PopHandler, self.line);
1882 self.compile_finally_inline(finally_body)?;
1883 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1884
1885 self.chunk.patch_jump(catch_jump);
1887 self.compile_catch_binding(error_var)?;
1888
1889 self.handler_depth += 1;
1891 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1892 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1893 self.emit_type_name_extra(empty_type);
1894
1895 self.compile_try_body(catch_body)?;
1897
1898 self.handler_depth -= 1;
1900 self.chunk.emit(Op::PopHandler, self.line);
1901 self.compile_finally_inline(finally_body)?;
1902 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
1903
1904 self.chunk.patch_jump(rethrow_jump);
1906 self.compile_rethrow_with_finally(finally_body)?;
1907
1908 self.chunk.patch_jump(end_jump);
1909 self.chunk.patch_jump(end_jump2);
1910
1911 self.finally_bodies.pop();
1912 } else if has_finally {
1913 let finally_body = finally_body.as_ref().unwrap();
1915
1916 self.finally_bodies.push(finally_body.clone());
1917
1918 self.handler_depth += 1;
1920 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1921 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1922 self.emit_type_name_extra(empty_type);
1923
1924 self.compile_try_body(body)?;
1926
1927 self.handler_depth -= 1;
1929 self.chunk.emit(Op::PopHandler, self.line);
1930 self.compile_finally_inline(finally_body)?;
1931 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1932
1933 self.chunk.patch_jump(error_jump);
1935 self.compile_rethrow_with_finally(finally_body)?;
1936
1937 self.chunk.patch_jump(end_jump);
1938
1939 self.finally_bodies.pop();
1940 } else {
1941 self.handler_depth += 1;
1945 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1946 self.emit_type_name_extra(type_name_idx);
1947
1948 self.compile_try_body(body)?;
1950
1951 self.handler_depth -= 1;
1953 self.chunk.emit(Op::PopHandler, self.line);
1954 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1955
1956 self.chunk.patch_jump(catch_jump);
1958 self.compile_catch_binding(error_var)?;
1959
1960 self.compile_try_body(catch_body)?;
1962
1963 self.chunk.patch_jump(end_jump);
1965 }
1966 }
1967
1968 Node::TryExpr { body } => {
1969 self.handler_depth += 1;
1973 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
1974 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
1975 self.emit_type_name_extra(empty_type);
1976
1977 self.compile_try_body(body)?;
1979
1980 self.handler_depth -= 1;
1982 self.chunk.emit(Op::PopHandler, self.line);
1983
1984 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
1986 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
1987 self.chunk.emit(Op::Swap, self.line);
1988 self.chunk.emit_u8(Op::Call, 1, self.line);
1989
1990 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1992
1993 self.chunk.patch_jump(catch_jump);
1995
1996 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
1998 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
1999 self.chunk.emit(Op::Swap, self.line);
2000 self.chunk.emit_u8(Op::Call, 1, self.line);
2001
2002 self.chunk.patch_jump(end_jump);
2004 }
2005
2006 Node::Retry { count, body } => {
2007 self.compile_node(count)?;
2009 let counter_name = "__retry_counter__";
2010 let counter_idx = self
2011 .chunk
2012 .add_constant(Constant::String(counter_name.to_string()));
2013 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
2014
2015 self.chunk.emit(Op::Nil, self.line);
2017 let err_name = "__retry_last_error__";
2018 let err_idx = self
2019 .chunk
2020 .add_constant(Constant::String(err_name.to_string()));
2021 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
2022
2023 let loop_start = self.chunk.current_offset();
2025
2026 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2028 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2030 let hi = (empty_type >> 8) as u8;
2031 let lo = empty_type as u8;
2032 self.chunk.code.push(hi);
2033 self.chunk.code.push(lo);
2034 self.chunk.lines.push(self.line);
2035 self.chunk.columns.push(self.column);
2036 self.chunk.lines.push(self.line);
2037 self.chunk.columns.push(self.column);
2038
2039 self.compile_block(body)?;
2041
2042 self.chunk.emit(Op::PopHandler, self.line);
2044 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2045
2046 self.chunk.patch_jump(catch_jump);
2048 self.chunk.emit(Op::Dup, self.line);
2050 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
2051 self.chunk.emit(Op::Pop, self.line);
2053
2054 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
2056 let one_idx = self.chunk.add_constant(Constant::Int(1));
2057 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
2058 self.chunk.emit(Op::Sub, self.line);
2059 self.chunk.emit(Op::Dup, self.line);
2060 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
2061
2062 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2064 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2065 self.chunk.emit(Op::Greater, self.line);
2066 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2067 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
2069
2070 self.chunk.patch_jump(retry_jump);
2072 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
2074 self.chunk.emit(Op::Throw, self.line);
2075
2076 self.chunk.patch_jump(end_jump);
2077 self.chunk.emit(Op::Nil, self.line);
2079 }
2080
2081 Node::Parallel {
2082 count,
2083 variable,
2084 body,
2085 } => {
2086 self.compile_node(count)?;
2087 let mut fn_compiler = Compiler::new();
2088 fn_compiler.enum_names = self.enum_names.clone();
2089 fn_compiler.compile_block(body)?;
2090 fn_compiler.chunk.emit(Op::Return, self.line);
2091 let params = vec![variable.clone().unwrap_or_else(|| "__i__".to_string())];
2092 let func = CompiledFunction {
2093 name: "<parallel>".to_string(),
2094 params,
2095 default_start: None,
2096 chunk: fn_compiler.chunk,
2097 is_generator: false,
2098 };
2099 let fn_idx = self.chunk.functions.len();
2100 self.chunk.functions.push(func);
2101 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2102 self.chunk.emit(Op::Parallel, self.line);
2103 }
2104
2105 Node::ParallelMap {
2106 list,
2107 variable,
2108 body,
2109 } => {
2110 self.compile_node(list)?;
2111 let mut fn_compiler = Compiler::new();
2112 fn_compiler.enum_names = self.enum_names.clone();
2113 fn_compiler.compile_block(body)?;
2114 fn_compiler.chunk.emit(Op::Return, self.line);
2115 let func = CompiledFunction {
2116 name: "<parallel_map>".to_string(),
2117 params: vec![variable.clone()],
2118 default_start: None,
2119 chunk: fn_compiler.chunk,
2120 is_generator: false,
2121 };
2122 let fn_idx = self.chunk.functions.len();
2123 self.chunk.functions.push(func);
2124 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2125 self.chunk.emit(Op::ParallelMap, self.line);
2126 }
2127
2128 Node::ParallelSettle {
2129 list,
2130 variable,
2131 body,
2132 } => {
2133 self.compile_node(list)?;
2134 let mut fn_compiler = Compiler::new();
2135 fn_compiler.enum_names = self.enum_names.clone();
2136 fn_compiler.compile_block(body)?;
2137 fn_compiler.chunk.emit(Op::Return, self.line);
2138 let func = CompiledFunction {
2139 name: "<parallel_settle>".to_string(),
2140 params: vec![variable.clone()],
2141 default_start: None,
2142 chunk: fn_compiler.chunk,
2143 is_generator: false,
2144 };
2145 let fn_idx = self.chunk.functions.len();
2146 self.chunk.functions.push(func);
2147 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2148 self.chunk.emit(Op::ParallelSettle, self.line);
2149 }
2150
2151 Node::SpawnExpr { body } => {
2152 let mut fn_compiler = Compiler::new();
2153 fn_compiler.enum_names = self.enum_names.clone();
2154 fn_compiler.compile_block(body)?;
2155 fn_compiler.chunk.emit(Op::Return, self.line);
2156 let func = CompiledFunction {
2157 name: "<spawn>".to_string(),
2158 params: vec![],
2159 default_start: None,
2160 chunk: fn_compiler.chunk,
2161 is_generator: false,
2162 };
2163 let fn_idx = self.chunk.functions.len();
2164 self.chunk.functions.push(func);
2165 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2166 self.chunk.emit(Op::Spawn, self.line);
2167 }
2168 Node::SelectExpr {
2169 cases,
2170 timeout,
2171 default_body,
2172 } => {
2173 let builtin_name = if timeout.is_some() {
2180 "__select_timeout"
2181 } else if default_body.is_some() {
2182 "__select_try"
2183 } else {
2184 "__select_list"
2185 };
2186
2187 let name_idx = self
2189 .chunk
2190 .add_constant(Constant::String(builtin_name.into()));
2191 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2192
2193 for case in cases {
2195 self.compile_node(&case.channel)?;
2196 }
2197 self.chunk
2198 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2199
2200 if let Some((duration_expr, _)) = timeout {
2202 self.compile_node(duration_expr)?;
2203 self.chunk.emit_u8(Op::Call, 2, self.line);
2204 } else {
2205 self.chunk.emit_u8(Op::Call, 1, self.line);
2206 }
2207
2208 self.temp_counter += 1;
2210 let result_name = format!("__sel_result_{}__", self.temp_counter);
2211 let result_idx = self
2212 .chunk
2213 .add_constant(Constant::String(result_name.clone()));
2214 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2215
2216 let mut end_jumps = Vec::new();
2218
2219 for (i, case) in cases.iter().enumerate() {
2220 let get_r = self
2221 .chunk
2222 .add_constant(Constant::String(result_name.clone()));
2223 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2224 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2225 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2226 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2227 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2228 self.chunk.emit(Op::Equal, self.line);
2229 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2230 self.chunk.emit(Op::Pop, self.line);
2231
2232 let get_r2 = self
2234 .chunk
2235 .add_constant(Constant::String(result_name.clone()));
2236 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2237 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2238 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2239 let var_idx = self
2240 .chunk
2241 .add_constant(Constant::String(case.variable.clone()));
2242 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2243
2244 self.compile_try_body(&case.body)?;
2245 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2246 self.chunk.patch_jump(skip);
2247 self.chunk.emit(Op::Pop, self.line);
2248 }
2249
2250 if let Some((_, ref timeout_body)) = timeout {
2252 self.compile_try_body(timeout_body)?;
2253 } else if let Some(ref def_body) = default_body {
2254 self.compile_try_body(def_body)?;
2255 } else {
2256 self.chunk.emit(Op::Nil, self.line);
2257 }
2258
2259 for ej in end_jumps {
2260 self.chunk.patch_jump(ej);
2261 }
2262 }
2263 Node::Spread(_) => {
2264 return Err(CompileError {
2265 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2266 line: self.line,
2267 });
2268 }
2269 }
2270 Ok(())
2271 }
2272
2273 fn compile_destructuring(
2277 &mut self,
2278 pattern: &BindingPattern,
2279 is_mutable: bool,
2280 ) -> Result<(), CompileError> {
2281 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2282 match pattern {
2283 BindingPattern::Identifier(name) => {
2284 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2286 self.chunk.emit_u16(def_op, idx, self.line);
2287 }
2288 BindingPattern::Dict(fields) => {
2289 self.chunk.emit(Op::Dup, self.line);
2292 let assert_idx = self
2293 .chunk
2294 .add_constant(Constant::String("__assert_dict".into()));
2295 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2296 self.chunk.emit(Op::Swap, self.line);
2297 self.chunk.emit_u8(Op::Call, 1, self.line);
2298 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2303 let rest_field = fields.iter().find(|f| f.is_rest);
2304
2305 for field in &non_rest {
2306 self.chunk.emit(Op::Dup, self.line);
2307 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2308 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2309 self.chunk.emit(Op::Subscript, self.line);
2310 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2311 let name_idx = self
2312 .chunk
2313 .add_constant(Constant::String(binding_name.to_string()));
2314 self.chunk.emit_u16(def_op, name_idx, self.line);
2315 }
2316
2317 if let Some(rest) = rest_field {
2318 let fn_idx = self
2321 .chunk
2322 .add_constant(Constant::String("__dict_rest".into()));
2323 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2324 self.chunk.emit(Op::Swap, self.line);
2326 for field in &non_rest {
2328 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2329 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2330 }
2331 self.chunk
2332 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2333 self.chunk.emit_u8(Op::Call, 2, self.line);
2335 let rest_name = &rest.key;
2336 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2337 self.chunk.emit_u16(def_op, rest_idx, self.line);
2338 } else {
2339 self.chunk.emit(Op::Pop, self.line);
2341 }
2342 }
2343 BindingPattern::List(elements) => {
2344 self.chunk.emit(Op::Dup, self.line);
2347 let assert_idx = self
2348 .chunk
2349 .add_constant(Constant::String("__assert_list".into()));
2350 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2351 self.chunk.emit(Op::Swap, self.line);
2352 self.chunk.emit_u8(Op::Call, 1, self.line);
2353 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2356 let rest_elem = elements.iter().find(|e| e.is_rest);
2357
2358 for (i, elem) in non_rest.iter().enumerate() {
2359 self.chunk.emit(Op::Dup, self.line);
2360 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2361 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2362 self.chunk.emit(Op::Subscript, self.line);
2363 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2364 self.chunk.emit_u16(def_op, name_idx, self.line);
2365 }
2366
2367 if let Some(rest) = rest_elem {
2368 let start_idx = self
2372 .chunk
2373 .add_constant(Constant::Int(non_rest.len() as i64));
2374 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2375 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2377 let rest_name_idx =
2378 self.chunk.add_constant(Constant::String(rest.name.clone()));
2379 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2380 } else {
2381 self.chunk.emit(Op::Pop, self.line);
2383 }
2384 }
2385 }
2386 Ok(())
2387 }
2388
2389 fn produces_value(node: &Node) -> bool {
2391 match node {
2392 Node::LetBinding { .. }
2394 | Node::VarBinding { .. }
2395 | Node::Assignment { .. }
2396 | Node::ReturnStmt { .. }
2397 | Node::FnDecl { .. }
2398 | Node::ImplBlock { .. }
2399 | Node::StructDecl { .. }
2400 | Node::ThrowStmt { .. }
2401 | Node::BreakStmt
2402 | Node::ContinueStmt => false,
2403 Node::TryCatch { .. }
2405 | Node::TryExpr { .. }
2406 | Node::Retry { .. }
2407 | Node::GuardStmt { .. }
2408 | Node::DeadlineBlock { .. }
2409 | Node::MutexBlock { .. }
2410 | Node::Spread(_) => true,
2411 _ => true,
2413 }
2414 }
2415}
2416
2417impl Compiler {
2418 pub fn compile_fn_body(
2420 &mut self,
2421 params: &[TypedParam],
2422 body: &[SNode],
2423 ) -> Result<CompiledFunction, CompileError> {
2424 let mut fn_compiler = Compiler::new();
2425 fn_compiler.compile_block(body)?;
2426 fn_compiler.chunk.emit(Op::Nil, 0);
2427 fn_compiler.chunk.emit(Op::Return, 0);
2428 Ok(CompiledFunction {
2429 name: String::new(),
2430 params: TypedParam::names(params),
2431 default_start: TypedParam::default_start(params),
2432 chunk: fn_compiler.chunk,
2433 is_generator: false,
2434 })
2435 }
2436
2437 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2439 if body.is_empty() {
2440 self.chunk.emit(Op::Nil, self.line);
2441 } else {
2442 self.compile_block(body)?;
2443 if !Self::produces_value(&body.last().unwrap().node) {
2445 self.chunk.emit(Op::Nil, self.line);
2446 }
2447 }
2448 Ok(())
2449 }
2450
2451 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2453 match op {
2454 "+" => self.chunk.emit(Op::Add, self.line),
2455 "-" => self.chunk.emit(Op::Sub, self.line),
2456 "*" => self.chunk.emit(Op::Mul, self.line),
2457 "/" => self.chunk.emit(Op::Div, self.line),
2458 "%" => self.chunk.emit(Op::Mod, self.line),
2459 _ => {
2460 return Err(CompileError {
2461 message: format!("Unknown compound operator: {op}"),
2462 line: self.line,
2463 })
2464 }
2465 }
2466 Ok(())
2467 }
2468
2469 fn root_var_name(&self, node: &SNode) -> Option<String> {
2471 match &node.node {
2472 Node::Identifier(name) => Some(name.clone()),
2473 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2474 self.root_var_name(object)
2475 }
2476 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2477 _ => None,
2478 }
2479 }
2480}
2481
2482impl Compiler {
2483 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
2485 for sn in nodes {
2486 match &sn.node {
2487 Node::EnumDecl { name, .. } => {
2488 names.insert(name.clone());
2489 }
2490 Node::Pipeline { body, .. } => {
2491 Self::collect_enum_names(body, names);
2492 }
2493 Node::FnDecl { body, .. } => {
2494 Self::collect_enum_names(body, names);
2495 }
2496 Node::Block(stmts) => {
2497 Self::collect_enum_names(stmts, names);
2498 }
2499 _ => {}
2500 }
2501 }
2502 }
2503
2504 fn collect_interface_methods(
2505 nodes: &[SNode],
2506 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
2507 ) {
2508 for sn in nodes {
2509 match &sn.node {
2510 Node::InterfaceDecl { name, methods } => {
2511 let method_names: Vec<String> =
2512 methods.iter().map(|m| m.name.clone()).collect();
2513 interfaces.insert(name.clone(), method_names);
2514 }
2515 Node::Pipeline { body, .. } | Node::FnDecl { body, .. } => {
2516 Self::collect_interface_methods(body, interfaces);
2517 }
2518 Node::Block(stmts) => {
2519 Self::collect_interface_methods(stmts, interfaces);
2520 }
2521 _ => {}
2522 }
2523 }
2524 }
2525}
2526
2527impl Default for Compiler {
2528 fn default() -> Self {
2529 Self::new()
2530 }
2531}
2532
2533fn body_contains_yield(nodes: &[SNode]) -> bool {
2535 nodes.iter().any(|sn| node_contains_yield(&sn.node))
2536}
2537
2538fn node_contains_yield(node: &Node) -> bool {
2539 match node {
2540 Node::YieldExpr { .. } => true,
2541 Node::FnDecl { .. } | Node::Closure { .. } => false,
2544 Node::Block(stmts) => body_contains_yield(stmts),
2545 Node::IfElse {
2546 condition,
2547 then_body,
2548 else_body,
2549 } => {
2550 node_contains_yield(&condition.node)
2551 || body_contains_yield(then_body)
2552 || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
2553 }
2554 Node::WhileLoop { condition, body } => {
2555 node_contains_yield(&condition.node) || body_contains_yield(body)
2556 }
2557 Node::ForIn { iterable, body, .. } => {
2558 node_contains_yield(&iterable.node) || body_contains_yield(body)
2559 }
2560 Node::TryCatch {
2561 body, catch_body, ..
2562 } => body_contains_yield(body) || body_contains_yield(catch_body),
2563 Node::TryExpr { body } => body_contains_yield(body),
2564 _ => false,
2565 }
2566}
2567
2568fn contains_pipe_placeholder(node: &SNode) -> bool {
2570 match &node.node {
2571 Node::Identifier(name) if name == "_" => true,
2572 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
2573 Node::MethodCall { object, args, .. } => {
2574 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
2575 }
2576 Node::BinaryOp { left, right, .. } => {
2577 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
2578 }
2579 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
2580 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
2581 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
2582 Node::SubscriptAccess { object, index } => {
2583 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
2584 }
2585 _ => false,
2586 }
2587}
2588
2589fn replace_pipe_placeholder(node: &SNode) -> SNode {
2591 let new_node = match &node.node {
2592 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
2593 Node::FunctionCall { name, args } => Node::FunctionCall {
2594 name: name.clone(),
2595 args: args.iter().map(replace_pipe_placeholder).collect(),
2596 },
2597 Node::MethodCall {
2598 object,
2599 method,
2600 args,
2601 } => Node::MethodCall {
2602 object: Box::new(replace_pipe_placeholder(object)),
2603 method: method.clone(),
2604 args: args.iter().map(replace_pipe_placeholder).collect(),
2605 },
2606 Node::BinaryOp { op, left, right } => Node::BinaryOp {
2607 op: op.clone(),
2608 left: Box::new(replace_pipe_placeholder(left)),
2609 right: Box::new(replace_pipe_placeholder(right)),
2610 },
2611 Node::UnaryOp { op, operand } => Node::UnaryOp {
2612 op: op.clone(),
2613 operand: Box::new(replace_pipe_placeholder(operand)),
2614 },
2615 Node::ListLiteral(items) => {
2616 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
2617 }
2618 Node::PropertyAccess { object, property } => Node::PropertyAccess {
2619 object: Box::new(replace_pipe_placeholder(object)),
2620 property: property.clone(),
2621 },
2622 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
2623 object: Box::new(replace_pipe_placeholder(object)),
2624 index: Box::new(replace_pipe_placeholder(index)),
2625 },
2626 _ => return node.clone(),
2627 };
2628 SNode::new(new_node, node.span)
2629}
2630
2631#[cfg(test)]
2632mod tests {
2633 use super::*;
2634 use harn_lexer::Lexer;
2635 use harn_parser::Parser;
2636
2637 fn compile_source(source: &str) -> Chunk {
2638 let mut lexer = Lexer::new(source);
2639 let tokens = lexer.tokenize().unwrap();
2640 let mut parser = Parser::new(tokens);
2641 let program = parser.parse().unwrap();
2642 Compiler::new().compile(&program).unwrap()
2643 }
2644
2645 #[test]
2646 fn test_compile_arithmetic() {
2647 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
2648 assert!(!chunk.code.is_empty());
2649 assert!(chunk.constants.contains(&Constant::Int(2)));
2651 assert!(chunk.constants.contains(&Constant::Int(3)));
2652 }
2653
2654 #[test]
2655 fn test_compile_function_call() {
2656 let chunk = compile_source("pipeline test(task) { log(42) }");
2657 let disasm = chunk.disassemble("test");
2658 assert!(disasm.contains("CALL"));
2659 }
2660
2661 #[test]
2662 fn test_compile_if_else() {
2663 let chunk =
2664 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
2665 let disasm = chunk.disassemble("test");
2666 assert!(disasm.contains("JUMP_IF_FALSE"));
2667 assert!(disasm.contains("JUMP"));
2668 }
2669
2670 #[test]
2671 fn test_compile_while() {
2672 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
2673 let disasm = chunk.disassemble("test");
2674 assert!(disasm.contains("JUMP_IF_FALSE"));
2675 assert!(disasm.contains("JUMP"));
2677 }
2678
2679 #[test]
2680 fn test_compile_closure() {
2681 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
2682 assert!(!chunk.functions.is_empty());
2683 assert_eq!(chunk.functions[0].params, vec!["x"]);
2684 }
2685
2686 #[test]
2687 fn test_compile_list() {
2688 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
2689 let disasm = chunk.disassemble("test");
2690 assert!(disasm.contains("BUILD_LIST"));
2691 }
2692
2693 #[test]
2694 fn test_compile_dict() {
2695 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
2696 let disasm = chunk.disassemble("test");
2697 assert!(disasm.contains("BUILD_DICT"));
2698 }
2699
2700 #[test]
2701 fn test_disassemble() {
2702 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
2703 let disasm = chunk.disassemble("test");
2704 assert!(disasm.contains("CONSTANT"));
2706 assert!(disasm.contains("ADD"));
2707 assert!(disasm.contains("CALL"));
2708 }
2709}