1use std::collections::BTreeMap;
2use std::rc::Rc;
3
4use harn_lexer::StringSegment;
5use harn_parser::{BindingPattern, Node, SNode, TypedParam};
6
7use crate::chunk::{Chunk, CompiledFunction, Constant, Op};
8use crate::schema;
9use crate::value::VmValue;
10
11#[derive(Debug)]
13pub struct CompileError {
14 pub message: String,
15 pub line: u32,
16}
17
18impl std::fmt::Display for CompileError {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 write!(f, "Compile error at line {}: {}", self.line, self.message)
21 }
22}
23
24impl std::error::Error for CompileError {}
25
26struct LoopContext {
28 start_offset: usize,
30 break_patches: Vec<usize>,
32 has_iterator: bool,
34 handler_depth: usize,
36 finally_depth: usize,
38 scope_depth: usize,
40}
41
42pub struct Compiler {
44 chunk: Chunk,
45 line: u32,
46 column: u32,
47 enum_names: std::collections::HashSet<String>,
49 interface_methods: std::collections::HashMap<String, Vec<String>>,
51 loop_stack: Vec<LoopContext>,
53 handler_depth: usize,
55 finally_bodies: Vec<Vec<SNode>>,
57 temp_counter: usize,
59 scope_depth: usize,
61}
62
63impl Compiler {
64 pub fn new() -> Self {
65 Self {
66 chunk: Chunk::new(),
67 line: 1,
68 column: 1,
69 enum_names: std::collections::HashSet::new(),
70 interface_methods: std::collections::HashMap::new(),
71 loop_stack: Vec::new(),
72 handler_depth: 0,
73 finally_bodies: Vec::new(),
74 temp_counter: 0,
75 scope_depth: 0,
76 }
77 }
78
79 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
82 Self::collect_enum_names(program, &mut self.enum_names);
85 self.enum_names.insert("Result".to_string());
87 Self::collect_interface_methods(program, &mut self.interface_methods);
88
89 for sn in program {
91 match &sn.node {
92 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
93 self.compile_node(sn)?;
94 }
95 _ => {}
96 }
97 }
98 let main = program
100 .iter()
101 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == "default"))
102 .or_else(|| {
103 program
104 .iter()
105 .find(|sn| matches!(&sn.node, Node::Pipeline { .. }))
106 });
107
108 if let Some(sn) = main {
109 self.compile_top_level_declarations(program)?;
110 if let Node::Pipeline { body, extends, .. } = &sn.node {
111 if let Some(parent_name) = extends {
113 self.compile_parent_pipeline(program, parent_name)?;
114 }
115 self.compile_block(body)?;
116 }
117 } else {
118 let top_level: Vec<&SNode> = program
121 .iter()
122 .filter(|sn| {
123 !matches!(
124 &sn.node,
125 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
126 )
127 })
128 .collect();
129 for sn in &top_level {
130 self.compile_node(sn)?;
131 if Self::produces_value(&sn.node) {
132 self.chunk.emit(Op::Pop, self.line);
133 }
134 }
135 }
136
137 self.chunk.emit(Op::Nil, self.line);
138 self.chunk.emit(Op::Return, self.line);
139 Ok(self.chunk)
140 }
141
142 pub fn compile_named(
144 mut self,
145 program: &[SNode],
146 pipeline_name: &str,
147 ) -> Result<Chunk, CompileError> {
148 Self::collect_enum_names(program, &mut self.enum_names);
149 Self::collect_interface_methods(program, &mut self.interface_methods);
150
151 for sn in program {
152 if matches!(
153 &sn.node,
154 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
155 ) {
156 self.compile_node(sn)?;
157 }
158 }
159 let target = program
160 .iter()
161 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == pipeline_name));
162
163 if let Some(sn) = target {
164 self.compile_top_level_declarations(program)?;
165 if let Node::Pipeline { body, extends, .. } = &sn.node {
166 if let Some(parent_name) = extends {
167 self.compile_parent_pipeline(program, parent_name)?;
168 }
169 self.compile_block(body)?;
170 }
171 }
172
173 self.chunk.emit(Op::Nil, self.line);
174 self.chunk.emit(Op::Return, self.line);
175 Ok(self.chunk)
176 }
177
178 fn compile_parent_pipeline(
180 &mut self,
181 program: &[SNode],
182 parent_name: &str,
183 ) -> Result<(), CompileError> {
184 let parent = program
185 .iter()
186 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
187 if let Some(sn) = parent {
188 if let Node::Pipeline { body, extends, .. } = &sn.node {
189 if let Some(grandparent) = extends {
191 self.compile_parent_pipeline(program, grandparent)?;
192 }
193 for stmt in body {
195 self.compile_node(stmt)?;
196 if Self::produces_value(&stmt.node) {
197 self.chunk.emit(Op::Pop, self.line);
198 }
199 }
200 }
201 }
202 Ok(())
203 }
204
205 fn emit_default_preamble(&mut self, params: &[TypedParam]) -> Result<(), CompileError> {
210 for (i, param) in params.iter().enumerate() {
211 if let Some(default_expr) = ¶m.default_value {
212 self.chunk.emit(Op::GetArgc, self.line);
213 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
214 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
215 self.chunk.emit(Op::GreaterEqual, self.line);
217 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
218 self.chunk.emit(Op::Pop, self.line);
220 self.compile_node(default_expr)?;
222 let name_idx = self
223 .chunk
224 .add_constant(Constant::String(param.name.clone()));
225 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
226 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
227 self.chunk.patch_jump(skip_jump);
228 self.chunk.emit(Op::Pop, self.line);
230 self.chunk.patch_jump(end_jump);
231 }
232 }
233 Ok(())
234 }
235
236 fn emit_type_checks(&mut self, params: &[TypedParam]) {
241 for param in params {
242 if let Some(type_expr) = ¶m.type_expr {
243 if let harn_parser::TypeExpr::Named(name) = type_expr {
245 if let Some(methods) = self.interface_methods.get(name) {
246 let fn_idx = self
247 .chunk
248 .add_constant(Constant::String("__assert_interface".into()));
249 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
250 let var_idx = self
251 .chunk
252 .add_constant(Constant::String(param.name.clone()));
253 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
254 let name_idx = self
255 .chunk
256 .add_constant(Constant::String(param.name.clone()));
257 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
258 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
259 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
260 let methods_str = methods.join(",");
261 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
262 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
263 self.chunk.emit_u8(Op::Call, 4, self.line);
264 self.chunk.emit(Op::Pop, self.line);
265 continue;
266 }
267 }
268
269 if let Some(schema) = Self::type_expr_to_schema_value(type_expr) {
270 let fn_idx = self
271 .chunk
272 .add_constant(Constant::String("__assert_schema".into()));
273 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
274 let var_idx = self
275 .chunk
276 .add_constant(Constant::String(param.name.clone()));
277 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
278 let name_idx = self
279 .chunk
280 .add_constant(Constant::String(param.name.clone()));
281 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
282 self.emit_vm_value_literal(&schema);
283 self.chunk.emit_u8(Op::Call, 3, self.line);
284 self.chunk.emit(Op::Pop, self.line);
285 }
286 }
287 }
288 }
289
290 fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
291 match type_expr {
292 harn_parser::TypeExpr::Named(name) => match name.as_str() {
293 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
294 | "closure" => Some(VmValue::Dict(Rc::new(BTreeMap::from([(
295 "type".to_string(),
296 VmValue::String(Rc::from(name.as_str())),
297 )])))),
298 _ => None,
299 },
300 harn_parser::TypeExpr::Shape(fields) => {
301 let mut properties = BTreeMap::new();
302 let mut required = Vec::new();
303 for field in fields {
304 let field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
305 properties.insert(field.name.clone(), field_schema);
306 if !field.optional {
307 required.push(VmValue::String(Rc::from(field.name.as_str())));
308 }
309 }
310 let mut out = BTreeMap::new();
311 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
312 out.insert("properties".to_string(), VmValue::Dict(Rc::new(properties)));
313 if !required.is_empty() {
314 out.insert("required".to_string(), VmValue::List(Rc::new(required)));
315 }
316 Some(VmValue::Dict(Rc::new(out)))
317 }
318 harn_parser::TypeExpr::List(inner) => {
319 let mut out = BTreeMap::new();
320 out.insert("type".to_string(), VmValue::String(Rc::from("list")));
321 if let Some(item_schema) = Self::type_expr_to_schema_value(inner) {
322 out.insert("items".to_string(), item_schema);
323 }
324 Some(VmValue::Dict(Rc::new(out)))
325 }
326 harn_parser::TypeExpr::DictType(key, value) => {
327 let mut out = BTreeMap::new();
328 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
329 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
330 if let Some(value_schema) = Self::type_expr_to_schema_value(value) {
331 out.insert("additional_properties".to_string(), value_schema);
332 }
333 }
334 Some(VmValue::Dict(Rc::new(out)))
335 }
336 harn_parser::TypeExpr::Union(members) => {
337 let branches = members
338 .iter()
339 .filter_map(Self::type_expr_to_schema_value)
340 .collect::<Vec<_>>();
341 if branches.is_empty() {
342 None
343 } else {
344 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
345 "union".to_string(),
346 VmValue::List(Rc::new(branches)),
347 )]))))
348 }
349 }
350 harn_parser::TypeExpr::FnType { .. } => {
351 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
352 "type".to_string(),
353 VmValue::String(Rc::from("closure")),
354 )]))))
355 }
356 harn_parser::TypeExpr::Never => None,
357 }
358 }
359
360 fn emit_vm_value_literal(&mut self, value: &VmValue) {
361 match value {
362 VmValue::String(text) => {
363 let idx = self.chunk.add_constant(Constant::String(text.to_string()));
364 self.chunk.emit_u16(Op::Constant, idx, self.line);
365 }
366 VmValue::Int(number) => {
367 let idx = self.chunk.add_constant(Constant::Int(*number));
368 self.chunk.emit_u16(Op::Constant, idx, self.line);
369 }
370 VmValue::Float(number) => {
371 let idx = self.chunk.add_constant(Constant::Float(*number));
372 self.chunk.emit_u16(Op::Constant, idx, self.line);
373 }
374 VmValue::Bool(value) => {
375 let idx = self.chunk.add_constant(Constant::Bool(*value));
376 self.chunk.emit_u16(Op::Constant, idx, self.line);
377 }
378 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
379 VmValue::List(items) => {
380 for item in items.iter() {
381 self.emit_vm_value_literal(item);
382 }
383 self.chunk
384 .emit_u16(Op::BuildList, items.len() as u16, self.line);
385 }
386 VmValue::Dict(entries) => {
387 for (key, item) in entries.iter() {
388 let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
389 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
390 self.emit_vm_value_literal(item);
391 }
392 self.chunk
393 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
394 }
395 _ => {}
396 }
397 }
398
399 fn emit_type_name_extra(&mut self, type_name_idx: u16) {
401 let hi = (type_name_idx >> 8) as u8;
402 let lo = type_name_idx as u8;
403 self.chunk.code.push(hi);
404 self.chunk.code.push(lo);
405 self.chunk.lines.push(self.line);
406 self.chunk.columns.push(self.column);
407 self.chunk.lines.push(self.line);
408 self.chunk.columns.push(self.column);
409 }
410
411 fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
413 if body.is_empty() {
414 self.chunk.emit(Op::Nil, self.line);
415 } else {
416 self.compile_scoped_block(body)?;
417 }
418 Ok(())
419 }
420
421 fn compile_catch_binding(&mut self, error_var: &Option<String>) -> Result<(), CompileError> {
423 if let Some(var_name) = error_var {
424 let idx = self.chunk.add_constant(Constant::String(var_name.clone()));
425 self.chunk.emit_u16(Op::DefLet, idx, self.line);
426 } else {
427 self.chunk.emit(Op::Pop, self.line);
428 }
429 Ok(())
430 }
431
432 fn compile_finally_inline(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
434 if !finally_body.is_empty() {
435 self.compile_scoped_block(finally_body)?;
436 if Self::produces_value(&finally_body.last().unwrap().node) {
438 self.chunk.emit(Op::Pop, self.line);
439 }
440 }
441 Ok(())
442 }
443
444 fn compile_rethrow_with_finally(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
446 self.temp_counter += 1;
448 let temp_name = format!("__finally_err_{}__", self.temp_counter);
449 let err_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
450 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
451 self.compile_finally_inline(finally_body)?;
452 let get_idx = self.chunk.add_constant(Constant::String(temp_name));
453 self.chunk.emit_u16(Op::GetVar, get_idx, self.line);
454 self.chunk.emit(Op::Throw, self.line);
455 Ok(())
456 }
457
458 fn begin_scope(&mut self) {
459 self.chunk.emit(Op::PushScope, self.line);
460 self.scope_depth += 1;
461 }
462
463 fn end_scope(&mut self) {
464 if self.scope_depth > 0 {
465 self.chunk.emit(Op::PopScope, self.line);
466 self.scope_depth -= 1;
467 }
468 }
469
470 fn unwind_scopes_to(&mut self, target_depth: usize) {
471 while self.scope_depth > target_depth {
472 self.chunk.emit(Op::PopScope, self.line);
473 self.scope_depth -= 1;
474 }
475 }
476
477 fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
478 self.begin_scope();
479 if stmts.is_empty() {
480 self.chunk.emit(Op::Nil, self.line);
481 } else {
482 self.compile_block(stmts)?;
483 }
484 self.end_scope();
485 Ok(())
486 }
487
488 fn compile_scoped_statements(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
489 self.begin_scope();
490 for sn in stmts {
491 self.compile_node(sn)?;
492 if Self::produces_value(&sn.node) {
493 self.chunk.emit(Op::Pop, self.line);
494 }
495 }
496 self.end_scope();
497 Ok(())
498 }
499
500 fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
501 for (i, snode) in stmts.iter().enumerate() {
502 self.compile_node(snode)?;
503 let is_last = i == stmts.len() - 1;
504 if is_last {
505 if !Self::produces_value(&snode.node) {
508 self.chunk.emit(Op::Nil, self.line);
509 }
510 } else {
511 if Self::produces_value(&snode.node) {
513 self.chunk.emit(Op::Pop, self.line);
514 }
515 }
516 }
517 Ok(())
518 }
519
520 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
521 self.line = snode.span.line as u32;
522 self.column = snode.span.column as u32;
523 self.chunk.set_column(self.column);
524 match &snode.node {
525 Node::IntLiteral(n) => {
526 let idx = self.chunk.add_constant(Constant::Int(*n));
527 self.chunk.emit_u16(Op::Constant, idx, self.line);
528 }
529 Node::FloatLiteral(n) => {
530 let idx = self.chunk.add_constant(Constant::Float(*n));
531 self.chunk.emit_u16(Op::Constant, idx, self.line);
532 }
533 Node::StringLiteral(s) | Node::RawStringLiteral(s) => {
534 let idx = self.chunk.add_constant(Constant::String(s.clone()));
535 self.chunk.emit_u16(Op::Constant, idx, self.line);
536 }
537 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
538 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
539 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
540 Node::DurationLiteral(ms) => {
541 let idx = self.chunk.add_constant(Constant::Duration(*ms));
542 self.chunk.emit_u16(Op::Constant, idx, self.line);
543 }
544
545 Node::Identifier(name) => {
546 let idx = self.chunk.add_constant(Constant::String(name.clone()));
547 self.chunk.emit_u16(Op::GetVar, idx, self.line);
548 }
549
550 Node::LetBinding { pattern, value, .. } => {
551 self.compile_node(value)?;
552 self.compile_destructuring(pattern, false)?;
553 }
554
555 Node::VarBinding { pattern, value, .. } => {
556 self.compile_node(value)?;
557 self.compile_destructuring(pattern, true)?;
558 }
559
560 Node::Assignment {
561 target, value, op, ..
562 } => {
563 if let Node::Identifier(name) = &target.node {
564 let idx = self.chunk.add_constant(Constant::String(name.clone()));
565 if let Some(op) = op {
566 self.chunk.emit_u16(Op::GetVar, idx, self.line);
567 self.compile_node(value)?;
568 self.emit_compound_op(op)?;
569 self.chunk.emit_u16(Op::SetVar, idx, self.line);
570 } else {
571 self.compile_node(value)?;
572 self.chunk.emit_u16(Op::SetVar, idx, self.line);
573 }
574 } else if let Node::PropertyAccess { object, property } = &target.node {
575 if let Some(var_name) = self.root_var_name(object) {
577 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
578 let prop_idx = self.chunk.add_constant(Constant::String(property.clone()));
579 if let Some(op) = op {
580 self.compile_node(target)?; self.compile_node(value)?;
583 self.emit_compound_op(op)?;
584 } else {
585 self.compile_node(value)?;
586 }
587 self.chunk.emit_u16(Op::SetProperty, prop_idx, self.line);
590 let hi = (var_idx >> 8) as u8;
592 let lo = var_idx as u8;
593 self.chunk.code.push(hi);
594 self.chunk.code.push(lo);
595 self.chunk.lines.push(self.line);
596 self.chunk.columns.push(self.column);
597 self.chunk.lines.push(self.line);
598 self.chunk.columns.push(self.column);
599 }
600 } else if let Node::SubscriptAccess { object, index } = &target.node {
601 if let Some(var_name) = self.root_var_name(object) {
603 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
604 if let Some(op) = op {
605 self.compile_node(target)?;
606 self.compile_node(value)?;
607 self.emit_compound_op(op)?;
608 } else {
609 self.compile_node(value)?;
610 }
611 self.compile_node(index)?;
612 self.chunk.emit_u16(Op::SetSubscript, var_idx, self.line);
613 }
614 }
615 }
616
617 Node::BinaryOp { op, left, right } => {
618 match op.as_str() {
620 "&&" => {
621 self.compile_node(left)?;
622 let jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
623 self.chunk.emit(Op::Pop, self.line);
624 self.compile_node(right)?;
625 self.chunk.patch_jump(jump);
626 self.chunk.emit(Op::Not, self.line);
628 self.chunk.emit(Op::Not, self.line);
629 return Ok(());
630 }
631 "||" => {
632 self.compile_node(left)?;
633 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
634 self.chunk.emit(Op::Pop, self.line);
635 self.compile_node(right)?;
636 self.chunk.patch_jump(jump);
637 self.chunk.emit(Op::Not, self.line);
638 self.chunk.emit(Op::Not, self.line);
639 return Ok(());
640 }
641 "??" => {
642 self.compile_node(left)?;
643 self.chunk.emit(Op::Dup, self.line);
644 self.chunk.emit(Op::Nil, self.line);
646 self.chunk.emit(Op::NotEqual, self.line);
647 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
648 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(right)?;
651 let end = self.chunk.emit_jump(Op::Jump, self.line);
652 self.chunk.patch_jump(jump);
653 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
655 return Ok(());
656 }
657 "|>" => {
658 self.compile_node(left)?;
659 if contains_pipe_placeholder(right) {
662 let replaced = replace_pipe_placeholder(right);
663 let closure_node = SNode::dummy(Node::Closure {
664 params: vec![TypedParam {
665 name: "__pipe".into(),
666 type_expr: None,
667 default_value: None,
668 rest: false,
669 }],
670 body: vec![replaced],
671 fn_syntax: false,
672 });
673 self.compile_node(&closure_node)?;
674 } else {
675 self.compile_node(right)?;
676 }
677 self.chunk.emit(Op::Pipe, self.line);
678 return Ok(());
679 }
680 _ => {}
681 }
682
683 self.compile_node(left)?;
684 self.compile_node(right)?;
685 match op.as_str() {
686 "+" => self.chunk.emit(Op::Add, self.line),
687 "-" => self.chunk.emit(Op::Sub, self.line),
688 "*" => self.chunk.emit(Op::Mul, self.line),
689 "/" => self.chunk.emit(Op::Div, self.line),
690 "%" => self.chunk.emit(Op::Mod, self.line),
691 "==" => self.chunk.emit(Op::Equal, self.line),
692 "!=" => self.chunk.emit(Op::NotEqual, self.line),
693 "<" => self.chunk.emit(Op::Less, self.line),
694 ">" => self.chunk.emit(Op::Greater, self.line),
695 "<=" => self.chunk.emit(Op::LessEqual, self.line),
696 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
697 "in" => self.chunk.emit(Op::Contains, self.line),
698 "not_in" => {
699 self.chunk.emit(Op::Contains, self.line);
700 self.chunk.emit(Op::Not, self.line);
701 }
702 _ => {
703 return Err(CompileError {
704 message: format!("Unknown operator: {op}"),
705 line: self.line,
706 })
707 }
708 }
709 }
710
711 Node::UnaryOp { op, operand } => {
712 self.compile_node(operand)?;
713 match op.as_str() {
714 "-" => self.chunk.emit(Op::Negate, self.line),
715 "!" => self.chunk.emit(Op::Not, self.line),
716 _ => {}
717 }
718 }
719
720 Node::Ternary {
721 condition,
722 true_expr,
723 false_expr,
724 } => {
725 self.compile_node(condition)?;
726 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
727 self.chunk.emit(Op::Pop, self.line);
728 self.compile_node(true_expr)?;
729 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
730 self.chunk.patch_jump(else_jump);
731 self.chunk.emit(Op::Pop, self.line);
732 self.compile_node(false_expr)?;
733 self.chunk.patch_jump(end_jump);
734 }
735
736 Node::FunctionCall { name, args } => {
737 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
738 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
740 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
741
742 if has_spread {
743 self.chunk.emit_u16(Op::BuildList, 0, self.line);
746 let mut pending = 0u16;
747 for arg in args {
748 if let Node::Spread(inner) = &arg.node {
749 if pending > 0 {
750 self.chunk.emit_u16(Op::BuildList, pending, self.line);
751 self.chunk.emit(Op::Add, self.line);
752 pending = 0;
753 }
754 self.compile_node(inner)?;
755 self.chunk.emit(Op::Dup, self.line);
756 let assert_idx = self
757 .chunk
758 .add_constant(Constant::String("__assert_list".into()));
759 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
760 self.chunk.emit(Op::Swap, self.line);
761 self.chunk.emit_u8(Op::Call, 1, self.line);
762 self.chunk.emit(Op::Pop, self.line);
763 self.chunk.emit(Op::Add, self.line);
764 } else {
765 self.compile_node(arg)?;
766 pending += 1;
767 }
768 }
769 if pending > 0 {
770 self.chunk.emit_u16(Op::BuildList, pending, self.line);
771 self.chunk.emit(Op::Add, self.line);
772 }
773 self.chunk.emit(Op::CallSpread, self.line);
774 } else {
775 for arg in args {
777 self.compile_node(arg)?;
778 }
779 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
780 }
781 }
782
783 Node::MethodCall {
784 object,
785 method,
786 args,
787 } => {
788 if let Node::Identifier(name) = &object.node {
790 if self.enum_names.contains(name) {
791 for arg in args {
793 self.compile_node(arg)?;
794 }
795 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
796 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
797 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
798 let hi = (var_idx >> 8) as u8;
799 let lo = var_idx as u8;
800 self.chunk.code.push(hi);
801 self.chunk.code.push(lo);
802 self.chunk.lines.push(self.line);
803 self.chunk.columns.push(self.column);
804 self.chunk.lines.push(self.line);
805 self.chunk.columns.push(self.column);
806 let fc = args.len() as u16;
807 let fhi = (fc >> 8) as u8;
808 let flo = fc as u8;
809 self.chunk.code.push(fhi);
810 self.chunk.code.push(flo);
811 self.chunk.lines.push(self.line);
812 self.chunk.columns.push(self.column);
813 self.chunk.lines.push(self.line);
814 self.chunk.columns.push(self.column);
815 return Ok(());
816 }
817 }
818 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
819 self.compile_node(object)?;
820 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
821 if has_spread {
822 self.chunk.emit_u16(Op::BuildList, 0, self.line);
824 let mut pending = 0u16;
825 for arg in args {
826 if let Node::Spread(inner) = &arg.node {
827 if pending > 0 {
828 self.chunk.emit_u16(Op::BuildList, pending, self.line);
829 self.chunk.emit(Op::Add, self.line);
830 pending = 0;
831 }
832 self.compile_node(inner)?;
833 self.chunk.emit(Op::Dup, self.line);
834 let assert_idx = self
835 .chunk
836 .add_constant(Constant::String("__assert_list".into()));
837 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
838 self.chunk.emit(Op::Swap, self.line);
839 self.chunk.emit_u8(Op::Call, 1, self.line);
840 self.chunk.emit(Op::Pop, self.line);
841 self.chunk.emit(Op::Add, self.line);
842 } else {
843 self.compile_node(arg)?;
844 pending += 1;
845 }
846 }
847 if pending > 0 {
848 self.chunk.emit_u16(Op::BuildList, pending, self.line);
849 self.chunk.emit(Op::Add, self.line);
850 }
851 self.chunk
852 .emit_u16(Op::MethodCallSpread, name_idx, self.line);
853 } else {
854 for arg in args {
855 self.compile_node(arg)?;
856 }
857 self.chunk
858 .emit_method_call(name_idx, args.len() as u8, self.line);
859 }
860 }
861
862 Node::OptionalMethodCall {
863 object,
864 method,
865 args,
866 } => {
867 self.compile_node(object)?;
868 for arg in args {
869 self.compile_node(arg)?;
870 }
871 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
872 self.chunk
873 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
874 }
875
876 Node::PropertyAccess { object, property } => {
877 if let Node::Identifier(name) = &object.node {
879 if self.enum_names.contains(name) {
880 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
882 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
883 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
884 let hi = (var_idx >> 8) as u8;
885 let lo = var_idx as u8;
886 self.chunk.code.push(hi);
887 self.chunk.code.push(lo);
888 self.chunk.lines.push(self.line);
889 self.chunk.columns.push(self.column);
890 self.chunk.lines.push(self.line);
891 self.chunk.columns.push(self.column);
892 self.chunk.code.push(0);
894 self.chunk.code.push(0);
895 self.chunk.lines.push(self.line);
896 self.chunk.columns.push(self.column);
897 self.chunk.lines.push(self.line);
898 self.chunk.columns.push(self.column);
899 return Ok(());
900 }
901 }
902 self.compile_node(object)?;
903 let idx = self.chunk.add_constant(Constant::String(property.clone()));
904 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
905 }
906
907 Node::OptionalPropertyAccess { object, property } => {
908 self.compile_node(object)?;
909 let idx = self.chunk.add_constant(Constant::String(property.clone()));
910 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
911 }
912
913 Node::SubscriptAccess { object, index } => {
914 self.compile_node(object)?;
915 self.compile_node(index)?;
916 self.chunk.emit(Op::Subscript, self.line);
917 }
918
919 Node::SliceAccess { object, start, end } => {
920 self.compile_node(object)?;
921 if let Some(s) = start {
922 self.compile_node(s)?;
923 } else {
924 self.chunk.emit(Op::Nil, self.line);
925 }
926 if let Some(e) = end {
927 self.compile_node(e)?;
928 } else {
929 self.chunk.emit(Op::Nil, self.line);
930 }
931 self.chunk.emit(Op::Slice, self.line);
932 }
933
934 Node::IfElse {
935 condition,
936 then_body,
937 else_body,
938 } => {
939 self.compile_node(condition)?;
940 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
941 self.chunk.emit(Op::Pop, self.line);
942 self.compile_scoped_block(then_body)?;
943 if let Some(else_body) = else_body {
944 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
945 self.chunk.patch_jump(else_jump);
946 self.chunk.emit(Op::Pop, self.line);
947 self.compile_scoped_block(else_body)?;
948 self.chunk.patch_jump(end_jump);
949 } else {
950 self.chunk.patch_jump(else_jump);
951 self.chunk.emit(Op::Pop, self.line);
952 self.chunk.emit(Op::Nil, self.line);
953 }
954 }
955
956 Node::WhileLoop { condition, body } => {
957 let loop_start = self.chunk.current_offset();
958 self.loop_stack.push(LoopContext {
959 start_offset: loop_start,
960 break_patches: Vec::new(),
961 has_iterator: false,
962 handler_depth: self.handler_depth,
963 finally_depth: self.finally_bodies.len(),
964 scope_depth: self.scope_depth,
965 });
966 self.compile_node(condition)?;
967 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
968 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_statements(body)?;
970 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
972 self.chunk.patch_jump(exit_jump);
973 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
976 for patch_pos in ctx.break_patches {
977 self.chunk.patch_jump(patch_pos);
978 }
979 self.chunk.emit(Op::Nil, self.line);
980 }
981
982 Node::ForIn {
983 pattern,
984 iterable,
985 body,
986 } => {
987 self.compile_node(iterable)?;
989 self.chunk.emit(Op::IterInit, self.line);
991 let loop_start = self.chunk.current_offset();
992 self.loop_stack.push(LoopContext {
993 start_offset: loop_start,
994 break_patches: Vec::new(),
995 has_iterator: true,
996 handler_depth: self.handler_depth,
997 finally_depth: self.finally_bodies.len(),
998 scope_depth: self.scope_depth,
999 });
1000 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
1002 self.begin_scope();
1003 self.compile_destructuring(pattern, true)?;
1005 for sn in body {
1006 self.compile_node(sn)?;
1007 if Self::produces_value(&sn.node) {
1008 self.chunk.emit(Op::Pop, self.line);
1009 }
1010 }
1011 self.end_scope();
1012 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1014 self.chunk.patch_jump(exit_jump_pos);
1015 let ctx = self.loop_stack.pop().unwrap();
1017 for patch_pos in ctx.break_patches {
1018 self.chunk.patch_jump(patch_pos);
1019 }
1020 self.chunk.emit(Op::Nil, self.line);
1022 }
1023
1024 Node::ReturnStmt { value } => {
1025 let has_pending_finally = !self.finally_bodies.is_empty();
1026
1027 if has_pending_finally {
1028 if let Some(val) = value {
1031 self.compile_node(val)?;
1032 } else {
1033 self.chunk.emit(Op::Nil, self.line);
1034 }
1035 self.temp_counter += 1;
1036 let temp_name = format!("__return_val_{}__", self.temp_counter);
1037 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
1038 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
1039 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
1041 for fb in &finallys {
1042 self.compile_finally_inline(fb)?;
1043 }
1044 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
1045 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
1046 self.chunk.emit(Op::Return, self.line);
1047 } else {
1048 if let Some(val) = value {
1050 if let Node::FunctionCall { name, args } = &val.node {
1051 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1052 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1053 for arg in args {
1054 self.compile_node(arg)?;
1055 }
1056 self.chunk
1057 .emit_u8(Op::TailCall, args.len() as u8, self.line);
1058 } else if let Node::BinaryOp { op, left, right } = &val.node {
1059 if op == "|>" {
1060 self.compile_node(left)?;
1061 self.compile_node(right)?;
1062 self.chunk.emit(Op::Swap, self.line);
1063 self.chunk.emit_u8(Op::TailCall, 1, self.line);
1064 } else {
1065 self.compile_node(val)?;
1066 }
1067 } else {
1068 self.compile_node(val)?;
1069 }
1070 } else {
1071 self.chunk.emit(Op::Nil, self.line);
1072 }
1073 self.chunk.emit(Op::Return, self.line);
1074 }
1075 }
1076
1077 Node::BreakStmt => {
1078 if self.loop_stack.is_empty() {
1079 return Err(CompileError {
1080 message: "break outside of loop".to_string(),
1081 line: self.line,
1082 });
1083 }
1084 let ctx = self.loop_stack.last().unwrap();
1086 let finally_depth = ctx.finally_depth;
1087 let handler_depth = ctx.handler_depth;
1088 let has_iterator = ctx.has_iterator;
1089 let scope_depth = ctx.scope_depth;
1090 for _ in handler_depth..self.handler_depth {
1092 self.chunk.emit(Op::PopHandler, self.line);
1093 }
1094 if self.finally_bodies.len() > finally_depth {
1096 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1097 .iter()
1098 .rev()
1099 .cloned()
1100 .collect();
1101 for fb in &finallys {
1102 self.compile_finally_inline(fb)?;
1103 }
1104 }
1105 self.unwind_scopes_to(scope_depth);
1106 if has_iterator {
1107 self.chunk.emit(Op::PopIterator, self.line);
1108 }
1109 let patch = self.chunk.emit_jump(Op::Jump, self.line);
1110 self.loop_stack
1111 .last_mut()
1112 .unwrap()
1113 .break_patches
1114 .push(patch);
1115 }
1116
1117 Node::ContinueStmt => {
1118 if self.loop_stack.is_empty() {
1119 return Err(CompileError {
1120 message: "continue outside of loop".to_string(),
1121 line: self.line,
1122 });
1123 }
1124 let ctx = self.loop_stack.last().unwrap();
1125 let finally_depth = ctx.finally_depth;
1126 let handler_depth = ctx.handler_depth;
1127 let loop_start = ctx.start_offset;
1128 let scope_depth = ctx.scope_depth;
1129 for _ in handler_depth..self.handler_depth {
1130 self.chunk.emit(Op::PopHandler, self.line);
1131 }
1132 if self.finally_bodies.len() > finally_depth {
1133 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1134 .iter()
1135 .rev()
1136 .cloned()
1137 .collect();
1138 for fb in &finallys {
1139 self.compile_finally_inline(fb)?;
1140 }
1141 }
1142 self.unwind_scopes_to(scope_depth);
1143 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1144 }
1145
1146 Node::ListLiteral(elements) => {
1147 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
1148 if !has_spread {
1149 for el in elements {
1150 self.compile_node(el)?;
1151 }
1152 self.chunk
1153 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
1154 } else {
1155 self.chunk.emit_u16(Op::BuildList, 0, self.line);
1158 let mut pending = 0u16;
1159 for el in elements {
1160 if let Node::Spread(inner) = &el.node {
1161 if pending > 0 {
1163 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1164 self.chunk.emit(Op::Add, self.line);
1166 pending = 0;
1167 }
1168 self.compile_node(inner)?;
1170 self.chunk.emit(Op::Dup, self.line);
1171 let assert_idx = self
1172 .chunk
1173 .add_constant(Constant::String("__assert_list".into()));
1174 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1175 self.chunk.emit(Op::Swap, self.line);
1176 self.chunk.emit_u8(Op::Call, 1, self.line);
1177 self.chunk.emit(Op::Pop, self.line);
1178 self.chunk.emit(Op::Add, self.line);
1179 } else {
1180 self.compile_node(el)?;
1181 pending += 1;
1182 }
1183 }
1184 if pending > 0 {
1185 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1186 self.chunk.emit(Op::Add, self.line);
1187 }
1188 }
1189 }
1190
1191 Node::DictLiteral(entries) => {
1192 let has_spread = entries
1193 .iter()
1194 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1195 if !has_spread {
1196 for entry in entries {
1197 self.compile_node(&entry.key)?;
1198 self.compile_node(&entry.value)?;
1199 }
1200 self.chunk
1201 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1202 } else {
1203 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1205 let mut pending = 0u16;
1206 for entry in entries {
1207 if let Node::Spread(inner) = &entry.value.node {
1208 if pending > 0 {
1210 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1211 self.chunk.emit(Op::Add, self.line);
1212 pending = 0;
1213 }
1214 self.compile_node(inner)?;
1216 self.chunk.emit(Op::Dup, self.line);
1217 let assert_idx = self
1218 .chunk
1219 .add_constant(Constant::String("__assert_dict".into()));
1220 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1221 self.chunk.emit(Op::Swap, self.line);
1222 self.chunk.emit_u8(Op::Call, 1, self.line);
1223 self.chunk.emit(Op::Pop, self.line);
1224 self.chunk.emit(Op::Add, self.line);
1225 } else {
1226 self.compile_node(&entry.key)?;
1227 self.compile_node(&entry.value)?;
1228 pending += 1;
1229 }
1230 }
1231 if pending > 0 {
1232 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1233 self.chunk.emit(Op::Add, self.line);
1234 }
1235 }
1236 }
1237
1238 Node::InterpolatedString(segments) => {
1239 let mut part_count = 0u16;
1240 for seg in segments {
1241 match seg {
1242 StringSegment::Literal(s) => {
1243 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1244 self.chunk.emit_u16(Op::Constant, idx, self.line);
1245 part_count += 1;
1246 }
1247 StringSegment::Expression(expr_str, expr_line, expr_col) => {
1248 let mut lexer =
1250 harn_lexer::Lexer::with_position(expr_str, *expr_line, *expr_col);
1251 if let Ok(tokens) = lexer.tokenize() {
1252 let mut parser = harn_parser::Parser::new(tokens);
1253 if let Ok(snode) = parser.parse_single_expression() {
1254 self.compile_node(&snode)?;
1255 let to_str = self
1257 .chunk
1258 .add_constant(Constant::String("to_string".into()));
1259 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1260 self.chunk.emit(Op::Swap, self.line);
1261 self.chunk.emit_u8(Op::Call, 1, self.line);
1262 part_count += 1;
1263 } else {
1264 let idx =
1266 self.chunk.add_constant(Constant::String(expr_str.clone()));
1267 self.chunk.emit_u16(Op::Constant, idx, self.line);
1268 part_count += 1;
1269 }
1270 }
1271 }
1272 }
1273 }
1274 if part_count > 1 {
1275 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1276 }
1277 }
1278
1279 Node::FnDecl {
1280 name, params, body, ..
1281 } => {
1282 let mut fn_compiler = Compiler::new();
1284 fn_compiler.enum_names = self.enum_names.clone();
1285 fn_compiler.emit_default_preamble(params)?;
1286 fn_compiler.emit_type_checks(params);
1287 let is_gen = body_contains_yield(body);
1288 fn_compiler.compile_block(body)?;
1289 fn_compiler.chunk.emit(Op::Nil, self.line);
1290 fn_compiler.chunk.emit(Op::Return, self.line);
1291
1292 let func = CompiledFunction {
1293 name: name.clone(),
1294 params: TypedParam::names(params),
1295 default_start: TypedParam::default_start(params),
1296 chunk: fn_compiler.chunk,
1297 is_generator: is_gen,
1298 has_rest_param: params.last().is_some_and(|p| p.rest),
1299 };
1300 let fn_idx = self.chunk.functions.len();
1301 self.chunk.functions.push(func);
1302
1303 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1304 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1305 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1306 }
1307
1308 Node::ToolDecl {
1309 name,
1310 description,
1311 params,
1312 return_type,
1313 body,
1314 ..
1315 } => {
1316 let mut fn_compiler = Compiler::new();
1318 fn_compiler.enum_names = self.enum_names.clone();
1319 fn_compiler.emit_default_preamble(params)?;
1320 fn_compiler.emit_type_checks(params);
1321 fn_compiler.compile_block(body)?;
1322 fn_compiler.chunk.emit(Op::Return, self.line);
1324
1325 let func = CompiledFunction {
1326 name: name.clone(),
1327 params: TypedParam::names(params),
1328 default_start: TypedParam::default_start(params),
1329 chunk: fn_compiler.chunk,
1330 is_generator: false,
1331 has_rest_param: params.last().is_some_and(|p| p.rest),
1332 };
1333 let fn_idx = self.chunk.functions.len();
1334 self.chunk.functions.push(func);
1335
1336 let define_name = self
1338 .chunk
1339 .add_constant(Constant::String("tool_define".into()));
1340 self.chunk.emit_u16(Op::Constant, define_name, self.line);
1341
1342 let reg_name = self
1344 .chunk
1345 .add_constant(Constant::String("tool_registry".into()));
1346 self.chunk.emit_u16(Op::Constant, reg_name, self.line);
1347 self.chunk.emit_u8(Op::Call, 0, self.line);
1348
1349 let tool_name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1351 self.chunk.emit_u16(Op::Constant, tool_name_idx, self.line);
1352
1353 let desc = description.as_deref().unwrap_or("");
1355 let desc_idx = self.chunk.add_constant(Constant::String(desc.to_string()));
1356 self.chunk.emit_u16(Op::Constant, desc_idx, self.line);
1357
1358 let mut param_count: u16 = 0;
1363 for p in params {
1364 let pn_idx = self.chunk.add_constant(Constant::String(p.name.clone()));
1365 self.chunk.emit_u16(Op::Constant, pn_idx, self.line);
1366
1367 let base_schema = p
1368 .type_expr
1369 .as_ref()
1370 .and_then(Self::type_expr_to_schema_value)
1371 .unwrap_or_else(|| {
1372 VmValue::Dict(Rc::new(BTreeMap::from([(
1373 "type".to_string(),
1374 VmValue::String(Rc::from("any")),
1375 )])))
1376 });
1377 let public_schema =
1378 schema::schema_to_json_schema_value(&base_schema).map_err(|error| {
1379 CompileError {
1380 message: format!(
1381 "failed to lower tool parameter schema for '{}': {}",
1382 p.name, error
1383 ),
1384 line: self.line,
1385 }
1386 })?;
1387 let mut param_schema = match public_schema {
1388 VmValue::Dict(map) => (*map).clone(),
1389 _ => BTreeMap::new(),
1390 };
1391
1392 if p.default_value.is_some() {
1393 param_schema.insert("required".to_string(), VmValue::Bool(false));
1394 }
1395
1396 self.emit_vm_value_literal(&VmValue::Dict(Rc::new(param_schema)));
1397
1398 if let Some(default_value) = p.default_value.as_ref() {
1399 let default_key =
1400 self.chunk.add_constant(Constant::String("default".into()));
1401 self.chunk.emit_u16(Op::Constant, default_key, self.line);
1402 self.compile_node(default_value)?;
1403 self.chunk.emit_u16(Op::BuildDict, 1, self.line);
1404 self.chunk.emit(Op::Add, self.line);
1405 }
1406
1407 param_count += 1;
1408 }
1409 self.chunk.emit_u16(Op::BuildDict, param_count, self.line);
1410
1411 let params_key = self
1413 .chunk
1414 .add_constant(Constant::String("parameters".into()));
1415 self.chunk.emit_u16(Op::Constant, params_key, self.line);
1416 self.chunk.emit(Op::Swap, self.line);
1417
1418 let handler_key = self.chunk.add_constant(Constant::String("handler".into()));
1419 self.chunk.emit_u16(Op::Constant, handler_key, self.line);
1420 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1421
1422 let mut config_entries = 2u16;
1423 if let Some(return_type) = return_type
1424 .as_ref()
1425 .and_then(Self::type_expr_to_schema_value)
1426 {
1427 let return_type =
1428 schema::schema_to_json_schema_value(&return_type).map_err(|error| {
1429 CompileError {
1430 message: format!(
1431 "failed to lower tool return schema for '{}': {}",
1432 name, error
1433 ),
1434 line: self.line,
1435 }
1436 })?;
1437 let returns_key = self.chunk.add_constant(Constant::String("returns".into()));
1438 self.chunk.emit_u16(Op::Constant, returns_key, self.line);
1439 self.emit_vm_value_literal(&return_type);
1440 config_entries += 1;
1441 }
1442
1443 self.chunk
1444 .emit_u16(Op::BuildDict, config_entries, self.line);
1445
1446 self.chunk.emit_u8(Op::Call, 4, self.line);
1448
1449 let bind_idx = self.chunk.add_constant(Constant::String(name.clone()));
1451 self.chunk.emit_u16(Op::DefLet, bind_idx, self.line);
1452 }
1453
1454 Node::Closure { params, body, .. } => {
1455 let mut fn_compiler = Compiler::new();
1456 fn_compiler.enum_names = self.enum_names.clone();
1457 fn_compiler.emit_default_preamble(params)?;
1458 fn_compiler.emit_type_checks(params);
1459 let is_gen = body_contains_yield(body);
1460 fn_compiler.compile_block(body)?;
1461 fn_compiler.chunk.emit(Op::Return, self.line);
1463
1464 let func = CompiledFunction {
1465 name: "<closure>".to_string(),
1466 params: TypedParam::names(params),
1467 default_start: TypedParam::default_start(params),
1468 chunk: fn_compiler.chunk,
1469 is_generator: is_gen,
1470 has_rest_param: false,
1471 };
1472 let fn_idx = self.chunk.functions.len();
1473 self.chunk.functions.push(func);
1474
1475 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1476 }
1477
1478 Node::ThrowStmt { value } => {
1479 self.compile_node(value)?;
1480 self.chunk.emit(Op::Throw, self.line);
1481 }
1482
1483 Node::MatchExpr { value, arms } => {
1484 self.compile_node(value)?;
1485 let mut end_jumps = Vec::new();
1486 for arm in arms {
1487 match &arm.pattern.node {
1488 Node::Identifier(name) if name == "_" => {
1490 self.begin_scope();
1491 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1493 self.end_scope();
1494 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1495 }
1496 Node::EnumConstruct {
1498 enum_name,
1499 variant,
1500 args: pat_args,
1501 } => {
1502 self.chunk.emit(Op::Dup, self.line);
1504 let en_idx =
1505 self.chunk.add_constant(Constant::String(enum_name.clone()));
1506 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1507 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1508 let hi = (vn_idx >> 8) as u8;
1509 let lo = vn_idx as u8;
1510 self.chunk.code.push(hi);
1511 self.chunk.code.push(lo);
1512 self.chunk.lines.push(self.line);
1513 self.chunk.columns.push(self.column);
1514 self.chunk.lines.push(self.line);
1515 self.chunk.columns.push(self.column);
1516 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1518 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1520
1521 for (i, pat_arg) in pat_args.iter().enumerate() {
1524 if let Node::Identifier(binding_name) = &pat_arg.node {
1525 self.chunk.emit(Op::Dup, self.line);
1527 let fields_idx = self
1528 .chunk
1529 .add_constant(Constant::String("fields".to_string()));
1530 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1531 let idx_const =
1532 self.chunk.add_constant(Constant::Int(i as i64));
1533 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1534 self.chunk.emit(Op::Subscript, self.line);
1535 let name_idx = self
1536 .chunk
1537 .add_constant(Constant::String(binding_name.clone()));
1538 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1539 }
1540 }
1541
1542 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1544 self.end_scope();
1545 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1546 self.chunk.patch_jump(skip);
1547 self.chunk.emit(Op::Pop, self.line); }
1549 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1551 {
1552 let enum_name = if let Node::Identifier(n) = &object.node {
1553 n.clone()
1554 } else {
1555 unreachable!()
1556 };
1557 self.chunk.emit(Op::Dup, self.line);
1558 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1559 let vn_idx =
1560 self.chunk.add_constant(Constant::String(property.clone()));
1561 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1562 let hi = (vn_idx >> 8) as u8;
1563 let lo = vn_idx as u8;
1564 self.chunk.code.push(hi);
1565 self.chunk.code.push(lo);
1566 self.chunk.lines.push(self.line);
1567 self.chunk.columns.push(self.column);
1568 self.chunk.lines.push(self.line);
1569 self.chunk.columns.push(self.column);
1570 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1571 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1573 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1575 self.end_scope();
1576 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1577 self.chunk.patch_jump(skip);
1578 self.chunk.emit(Op::Pop, self.line); }
1580 Node::MethodCall {
1583 object,
1584 method,
1585 args: pat_args,
1586 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1587 {
1588 let enum_name = if let Node::Identifier(n) = &object.node {
1589 n.clone()
1590 } else {
1591 unreachable!()
1592 };
1593 self.chunk.emit(Op::Dup, self.line);
1595 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1596 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1597 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1598 let hi = (vn_idx >> 8) as u8;
1599 let lo = vn_idx as u8;
1600 self.chunk.code.push(hi);
1601 self.chunk.code.push(lo);
1602 self.chunk.lines.push(self.line);
1603 self.chunk.columns.push(self.column);
1604 self.chunk.lines.push(self.line);
1605 self.chunk.columns.push(self.column);
1606 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1607 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1609
1610 for (i, pat_arg) in pat_args.iter().enumerate() {
1612 if let Node::Identifier(binding_name) = &pat_arg.node {
1613 self.chunk.emit(Op::Dup, self.line);
1614 let fields_idx = self
1615 .chunk
1616 .add_constant(Constant::String("fields".to_string()));
1617 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1618 let idx_const =
1619 self.chunk.add_constant(Constant::Int(i as i64));
1620 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1621 self.chunk.emit(Op::Subscript, self.line);
1622 let name_idx = self
1623 .chunk
1624 .add_constant(Constant::String(binding_name.clone()));
1625 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1626 }
1627 }
1628
1629 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1631 self.end_scope();
1632 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1633 self.chunk.patch_jump(skip);
1634 self.chunk.emit(Op::Pop, self.line); }
1636 Node::Identifier(name) => {
1638 self.begin_scope();
1639 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1642 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1643 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1645 self.end_scope();
1646 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1647 }
1648 Node::DictLiteral(entries)
1650 if entries
1651 .iter()
1652 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1653 {
1654 self.chunk.emit(Op::Dup, self.line);
1656 let typeof_idx =
1657 self.chunk.add_constant(Constant::String("type_of".into()));
1658 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1659 self.chunk.emit(Op::Swap, self.line);
1660 self.chunk.emit_u8(Op::Call, 1, self.line);
1661 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1662 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1663 self.chunk.emit(Op::Equal, self.line);
1664 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1665 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1669 let mut bindings = Vec::new();
1670 self.begin_scope();
1671 for entry in entries {
1672 if let Node::StringLiteral(key) = &entry.key.node {
1673 match &entry.value.node {
1674 Node::StringLiteral(_)
1676 | Node::IntLiteral(_)
1677 | Node::FloatLiteral(_)
1678 | Node::BoolLiteral(_)
1679 | Node::NilLiteral => {
1680 self.chunk.emit(Op::Dup, self.line);
1681 let key_idx = self
1682 .chunk
1683 .add_constant(Constant::String(key.clone()));
1684 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1685 self.chunk.emit(Op::Subscript, self.line);
1686 self.compile_node(&entry.value)?;
1687 self.chunk.emit(Op::Equal, self.line);
1688 let skip =
1689 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1690 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1692 }
1693 Node::Identifier(binding) => {
1695 bindings.push((key.clone(), binding.clone()));
1696 }
1697 _ => {
1698 self.chunk.emit(Op::Dup, self.line);
1700 let key_idx = self
1701 .chunk
1702 .add_constant(Constant::String(key.clone()));
1703 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1704 self.chunk.emit(Op::Subscript, self.line);
1705 self.compile_node(&entry.value)?;
1706 self.chunk.emit(Op::Equal, self.line);
1707 let skip =
1708 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1709 self.chunk.emit(Op::Pop, self.line);
1710 constraint_skips.push(skip);
1711 }
1712 }
1713 }
1714 }
1715
1716 for (key, binding) in &bindings {
1718 self.chunk.emit(Op::Dup, self.line);
1719 let key_idx =
1720 self.chunk.add_constant(Constant::String(key.clone()));
1721 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1722 self.chunk.emit(Op::Subscript, self.line);
1723 let name_idx =
1724 self.chunk.add_constant(Constant::String(binding.clone()));
1725 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1726 }
1727
1728 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1730 self.end_scope();
1731 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1732
1733 let type_fail_target = self.chunk.code.len();
1734 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1736 let scoped_fail_target = self.chunk.code.len();
1737 self.chunk.emit(Op::PopScope, self.line);
1738 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1740
1741 for skip in constraint_skips {
1742 self.chunk.patch_jump_to(skip, scoped_fail_target);
1743 }
1744 self.chunk.patch_jump_to(skip_type, type_fail_target);
1745 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1746 }
1747 Node::ListLiteral(elements) => {
1749 self.chunk.emit(Op::Dup, self.line);
1751 let typeof_idx =
1752 self.chunk.add_constant(Constant::String("type_of".into()));
1753 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1754 self.chunk.emit(Op::Swap, self.line);
1755 self.chunk.emit_u8(Op::Call, 1, self.line);
1756 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1757 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1758 self.chunk.emit(Op::Equal, self.line);
1759 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1760 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1764 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1765 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1766 self.chunk.emit(Op::Swap, self.line);
1767 self.chunk.emit_u8(Op::Call, 1, self.line);
1768 let count = self
1769 .chunk
1770 .add_constant(Constant::Int(elements.len() as i64));
1771 self.chunk.emit_u16(Op::Constant, count, self.line);
1772 self.chunk.emit(Op::GreaterEqual, self.line);
1773 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1774 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1778 let mut bindings = Vec::new();
1779 self.begin_scope();
1780 for (i, elem) in elements.iter().enumerate() {
1781 match &elem.node {
1782 Node::Identifier(name) if name != "_" => {
1783 bindings.push((i, name.clone()));
1784 }
1785 Node::Identifier(_) => {} _ => {
1788 self.chunk.emit(Op::Dup, self.line);
1789 let idx_const =
1790 self.chunk.add_constant(Constant::Int(i as i64));
1791 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1792 self.chunk.emit(Op::Subscript, self.line);
1793 self.compile_node(elem)?;
1794 self.chunk.emit(Op::Equal, self.line);
1795 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1796 self.chunk.emit(Op::Pop, self.line);
1797 constraint_skips.push(skip);
1798 }
1799 }
1800 }
1801
1802 for (i, name) in &bindings {
1804 self.chunk.emit(Op::Dup, self.line);
1805 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1806 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1807 self.chunk.emit(Op::Subscript, self.line);
1808 let name_idx =
1809 self.chunk.add_constant(Constant::String(name.clone()));
1810 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1811 }
1812
1813 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1815 self.end_scope();
1816 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1817
1818 let pre_scope_fail_target = self.chunk.code.len();
1819 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1821 let scoped_fail_target = self.chunk.code.len();
1822 self.chunk.emit(Op::PopScope, self.line);
1823 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1825 for skip in constraint_skips {
1826 self.chunk.patch_jump_to(skip, scoped_fail_target);
1827 }
1828 self.chunk.patch_jump_to(skip_len, pre_scope_fail_target);
1829 self.chunk.patch_jump_to(skip_type, pre_scope_fail_target);
1830 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1831 }
1832 _ => {
1834 self.chunk.emit(Op::Dup, self.line);
1835 self.compile_node(&arm.pattern)?;
1836 self.chunk.emit(Op::Equal, self.line);
1837 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1838 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1840 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1842 self.end_scope();
1843 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1844 self.chunk.patch_jump(skip);
1845 self.chunk.emit(Op::Pop, self.line); }
1847 }
1848 }
1849 self.chunk.emit(Op::Pop, self.line);
1851 self.chunk.emit(Op::Nil, self.line);
1852 for j in end_jumps {
1853 self.chunk.patch_jump(j);
1854 }
1855 }
1856
1857 Node::RangeExpr {
1858 start,
1859 end,
1860 inclusive,
1861 } => {
1862 let name_idx = self
1864 .chunk
1865 .add_constant(Constant::String("__range__".to_string()));
1866 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1867 self.compile_node(start)?;
1868 self.compile_node(end)?;
1869 if *inclusive {
1870 self.chunk.emit(Op::True, self.line);
1871 } else {
1872 self.chunk.emit(Op::False, self.line);
1873 }
1874 self.chunk.emit_u8(Op::Call, 3, self.line);
1875 }
1876
1877 Node::GuardStmt {
1878 condition,
1879 else_body,
1880 } => {
1881 self.compile_node(condition)?;
1884 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1885 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_block(else_body)?;
1888 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
1890 self.chunk.emit(Op::Pop, self.line);
1891 }
1892 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
1893 self.chunk.patch_jump(skip_jump);
1894 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
1896 self.chunk.emit(Op::Nil, self.line);
1897 }
1898
1899 Node::RequireStmt { condition, message } => {
1900 self.compile_node(condition)?;
1901 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
1902 self.chunk.emit(Op::Pop, self.line);
1903 if let Some(message) = message {
1904 self.compile_node(message)?;
1905 } else {
1906 let idx = self
1907 .chunk
1908 .add_constant(Constant::String("require condition failed".to_string()));
1909 self.chunk.emit_u16(Op::Constant, idx, self.line);
1910 }
1911 self.chunk.emit(Op::Throw, self.line);
1912 self.chunk.patch_jump(ok_jump);
1913 self.chunk.emit(Op::Pop, self.line);
1914 }
1915
1916 Node::Block(stmts) => {
1917 self.compile_scoped_block(stmts)?;
1918 }
1919
1920 Node::DeadlineBlock { duration, body } => {
1921 self.compile_node(duration)?;
1922 self.chunk.emit(Op::DeadlineSetup, self.line);
1923 self.compile_scoped_block(body)?;
1924 self.chunk.emit(Op::DeadlineEnd, self.line);
1925 }
1926
1927 Node::MutexBlock { body } => {
1928 self.begin_scope();
1930 for sn in body {
1931 self.compile_node(sn)?;
1932 if Self::produces_value(&sn.node) {
1933 self.chunk.emit(Op::Pop, self.line);
1934 }
1935 }
1936 self.chunk.emit(Op::Nil, self.line);
1937 self.end_scope();
1938 }
1939
1940 Node::YieldExpr { value } => {
1941 if let Some(val) = value {
1942 self.compile_node(val)?;
1943 } else {
1944 self.chunk.emit(Op::Nil, self.line);
1945 }
1946 self.chunk.emit(Op::Yield, self.line);
1947 }
1948
1949 Node::AskExpr { fields } => {
1950 for entry in fields {
1953 self.compile_node(&entry.key)?;
1954 self.compile_node(&entry.value)?;
1955 }
1956 self.chunk
1957 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
1958 }
1959
1960 Node::EnumConstruct {
1961 enum_name,
1962 variant,
1963 args,
1964 } => {
1965 for arg in args {
1967 self.compile_node(arg)?;
1968 }
1969 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
1970 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1971 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
1973 let hi = (var_idx >> 8) as u8;
1974 let lo = var_idx as u8;
1975 self.chunk.code.push(hi);
1976 self.chunk.code.push(lo);
1977 self.chunk.lines.push(self.line);
1978 self.chunk.columns.push(self.column);
1979 self.chunk.lines.push(self.line);
1980 self.chunk.columns.push(self.column);
1981 let fc = args.len() as u16;
1982 let fhi = (fc >> 8) as u8;
1983 let flo = fc as u8;
1984 self.chunk.code.push(fhi);
1985 self.chunk.code.push(flo);
1986 self.chunk.lines.push(self.line);
1987 self.chunk.columns.push(self.column);
1988 self.chunk.lines.push(self.line);
1989 self.chunk.columns.push(self.column);
1990 }
1991
1992 Node::StructConstruct {
1993 struct_name,
1994 fields,
1995 } => {
1996 let struct_key = self
1998 .chunk
1999 .add_constant(Constant::String("__struct__".to_string()));
2000 let struct_val = self
2001 .chunk
2002 .add_constant(Constant::String(struct_name.clone()));
2003 self.chunk.emit_u16(Op::Constant, struct_key, self.line);
2004 self.chunk.emit_u16(Op::Constant, struct_val, self.line);
2005
2006 for entry in fields {
2007 self.compile_node(&entry.key)?;
2008 self.compile_node(&entry.value)?;
2009 }
2010 self.chunk
2011 .emit_u16(Op::BuildDict, (fields.len() + 1) as u16, self.line);
2012 }
2013
2014 Node::ImportDecl { path } => {
2015 let idx = self.chunk.add_constant(Constant::String(path.clone()));
2016 self.chunk.emit_u16(Op::Import, idx, self.line);
2017 }
2018
2019 Node::SelectiveImport { names, path } => {
2020 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
2021 let names_str = names.join(",");
2022 let names_idx = self.chunk.add_constant(Constant::String(names_str));
2023 self.chunk
2024 .emit_u16(Op::SelectiveImport, path_idx, self.line);
2025 let hi = (names_idx >> 8) as u8;
2026 let lo = names_idx as u8;
2027 self.chunk.code.push(hi);
2028 self.chunk.code.push(lo);
2029 self.chunk.lines.push(self.line);
2030 self.chunk.columns.push(self.column);
2031 self.chunk.lines.push(self.line);
2032 self.chunk.columns.push(self.column);
2033 }
2034
2035 Node::TryOperator { operand } => {
2036 self.compile_node(operand)?;
2037 self.chunk.emit(Op::TryUnwrap, self.line);
2038 }
2039
2040 Node::ImplBlock { type_name, methods } => {
2041 for method_sn in methods {
2044 if let Node::FnDecl {
2045 name, params, body, ..
2046 } = &method_sn.node
2047 {
2048 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
2050 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2051
2052 let mut fn_compiler = Compiler::new();
2054 fn_compiler.enum_names = self.enum_names.clone();
2055 fn_compiler.emit_default_preamble(params)?;
2056 fn_compiler.emit_type_checks(params);
2057 fn_compiler.compile_block(body)?;
2058 fn_compiler.chunk.emit(Op::Nil, self.line);
2059 fn_compiler.chunk.emit(Op::Return, self.line);
2060
2061 let func = CompiledFunction {
2062 name: format!("{}.{}", type_name, name),
2063 params: TypedParam::names(params),
2064 default_start: TypedParam::default_start(params),
2065 chunk: fn_compiler.chunk,
2066 is_generator: false,
2067 has_rest_param: false,
2068 };
2069 let fn_idx = self.chunk.functions.len();
2070 self.chunk.functions.push(func);
2071 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2072 }
2073 }
2074 let method_count = methods
2075 .iter()
2076 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
2077 .count();
2078 self.chunk
2079 .emit_u16(Op::BuildDict, method_count as u16, self.line);
2080 let impl_name = format!("__impl_{}", type_name);
2081 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
2082 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2083 }
2084
2085 Node::StructDecl { name, .. } => {
2086 let mut fn_compiler = Compiler::new();
2088 fn_compiler.enum_names = self.enum_names.clone();
2089 let params = vec![TypedParam::untyped("__fields")];
2090 fn_compiler.emit_default_preamble(¶ms)?;
2091
2092 let make_idx = fn_compiler
2094 .chunk
2095 .add_constant(Constant::String("__make_struct".into()));
2096 fn_compiler
2097 .chunk
2098 .emit_u16(Op::Constant, make_idx, self.line);
2099 let sname_idx = fn_compiler
2100 .chunk
2101 .add_constant(Constant::String(name.clone()));
2102 fn_compiler
2103 .chunk
2104 .emit_u16(Op::Constant, sname_idx, self.line);
2105 let fields_idx = fn_compiler
2106 .chunk
2107 .add_constant(Constant::String("__fields".into()));
2108 fn_compiler
2109 .chunk
2110 .emit_u16(Op::GetVar, fields_idx, self.line);
2111 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
2112 fn_compiler.chunk.emit(Op::Return, self.line);
2113
2114 let func = CompiledFunction {
2115 name: name.clone(),
2116 params: TypedParam::names(¶ms),
2117 default_start: None,
2118 chunk: fn_compiler.chunk,
2119 is_generator: false,
2120 has_rest_param: 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 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
2126 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2127 }
2128
2129 Node::Pipeline { .. }
2131 | Node::OverrideDecl { .. }
2132 | Node::TypeDecl { .. }
2133 | Node::EnumDecl { .. }
2134 | Node::InterfaceDecl { .. } => {
2135 self.chunk.emit(Op::Nil, self.line);
2136 }
2137
2138 Node::TryCatch {
2139 body,
2140 error_var,
2141 error_type,
2142 catch_body,
2143 finally_body,
2144 } => {
2145 let type_name = error_type.as_ref().and_then(|te| {
2147 if let harn_parser::TypeExpr::Named(name) = te {
2148 Some(name.clone())
2149 } else {
2150 None
2151 }
2152 });
2153
2154 let type_name_idx = if let Some(ref tn) = type_name {
2155 self.chunk.add_constant(Constant::String(tn.clone()))
2156 } else {
2157 self.chunk.add_constant(Constant::String(String::new()))
2158 };
2159
2160 let has_catch = !catch_body.is_empty() || error_var.is_some();
2161 let has_finally = finally_body.is_some();
2162
2163 if has_catch && has_finally {
2164 let finally_body = finally_body.as_ref().unwrap();
2166
2167 self.finally_bodies.push(finally_body.clone());
2169
2170 self.handler_depth += 1;
2172 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2173 self.emit_type_name_extra(type_name_idx);
2174
2175 self.compile_try_body(body)?;
2177
2178 self.handler_depth -= 1;
2180 self.chunk.emit(Op::PopHandler, self.line);
2181 self.compile_finally_inline(finally_body)?;
2182 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2183
2184 self.chunk.patch_jump(catch_jump);
2186 self.begin_scope();
2187 self.compile_catch_binding(error_var)?;
2188
2189 self.handler_depth += 1;
2191 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2192 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2193 self.emit_type_name_extra(empty_type);
2194
2195 self.compile_try_body(catch_body)?;
2197
2198 self.handler_depth -= 1;
2200 self.chunk.emit(Op::PopHandler, self.line);
2201 self.compile_finally_inline(finally_body)?;
2202 self.end_scope();
2203 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
2204
2205 self.chunk.patch_jump(rethrow_jump);
2207 self.compile_rethrow_with_finally(finally_body)?;
2208 self.end_scope();
2209
2210 self.chunk.patch_jump(end_jump);
2211 self.chunk.patch_jump(end_jump2);
2212
2213 self.finally_bodies.pop();
2214 } else if has_finally {
2215 let finally_body = finally_body.as_ref().unwrap();
2217
2218 self.finally_bodies.push(finally_body.clone());
2219
2220 self.handler_depth += 1;
2222 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2223 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2224 self.emit_type_name_extra(empty_type);
2225
2226 self.compile_try_body(body)?;
2228
2229 self.handler_depth -= 1;
2231 self.chunk.emit(Op::PopHandler, self.line);
2232 self.compile_finally_inline(finally_body)?;
2233 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2234
2235 self.chunk.patch_jump(error_jump);
2237 self.compile_rethrow_with_finally(finally_body)?;
2238
2239 self.chunk.patch_jump(end_jump);
2240
2241 self.finally_bodies.pop();
2242 } else {
2243 self.handler_depth += 1;
2247 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2248 self.emit_type_name_extra(type_name_idx);
2249
2250 self.compile_try_body(body)?;
2252
2253 self.handler_depth -= 1;
2255 self.chunk.emit(Op::PopHandler, self.line);
2256 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2257
2258 self.chunk.patch_jump(catch_jump);
2260 self.begin_scope();
2261 self.compile_catch_binding(error_var)?;
2262
2263 self.compile_try_body(catch_body)?;
2265 self.end_scope();
2266
2267 self.chunk.patch_jump(end_jump);
2269 }
2270 }
2271
2272 Node::TryExpr { body } => {
2273 self.handler_depth += 1;
2277 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2278 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2279 self.emit_type_name_extra(empty_type);
2280
2281 self.compile_try_body(body)?;
2283
2284 self.handler_depth -= 1;
2286 self.chunk.emit(Op::PopHandler, self.line);
2287
2288 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
2290 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
2291 self.chunk.emit(Op::Swap, self.line);
2292 self.chunk.emit_u8(Op::Call, 1, self.line);
2293
2294 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2296
2297 self.chunk.patch_jump(catch_jump);
2299
2300 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
2302 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
2303 self.chunk.emit(Op::Swap, self.line);
2304 self.chunk.emit_u8(Op::Call, 1, self.line);
2305
2306 self.chunk.patch_jump(end_jump);
2308 }
2309
2310 Node::Retry { count, body } => {
2311 self.compile_node(count)?;
2313 let counter_name = "__retry_counter__";
2314 let counter_idx = self
2315 .chunk
2316 .add_constant(Constant::String(counter_name.to_string()));
2317 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
2318
2319 self.chunk.emit(Op::Nil, self.line);
2321 let err_name = "__retry_last_error__";
2322 let err_idx = self
2323 .chunk
2324 .add_constant(Constant::String(err_name.to_string()));
2325 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
2326
2327 let loop_start = self.chunk.current_offset();
2329
2330 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2332 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2334 let hi = (empty_type >> 8) as u8;
2335 let lo = empty_type as u8;
2336 self.chunk.code.push(hi);
2337 self.chunk.code.push(lo);
2338 self.chunk.lines.push(self.line);
2339 self.chunk.columns.push(self.column);
2340 self.chunk.lines.push(self.line);
2341 self.chunk.columns.push(self.column);
2342
2343 self.compile_block(body)?;
2345
2346 self.chunk.emit(Op::PopHandler, self.line);
2348 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2349
2350 self.chunk.patch_jump(catch_jump);
2352 self.chunk.emit(Op::Dup, self.line);
2354 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
2355 self.chunk.emit(Op::Pop, self.line);
2357
2358 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
2360 let one_idx = self.chunk.add_constant(Constant::Int(1));
2361 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
2362 self.chunk.emit(Op::Sub, self.line);
2363 self.chunk.emit(Op::Dup, self.line);
2364 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
2365
2366 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2368 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2369 self.chunk.emit(Op::Greater, self.line);
2370 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2371 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
2373
2374 self.chunk.patch_jump(retry_jump);
2376 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
2378 self.chunk.emit(Op::Throw, self.line);
2379
2380 self.chunk.patch_jump(end_jump);
2381 self.chunk.emit(Op::Nil, self.line);
2383 }
2384
2385 Node::Parallel {
2386 count,
2387 variable,
2388 body,
2389 } => {
2390 self.compile_node(count)?;
2391 let mut fn_compiler = Compiler::new();
2392 fn_compiler.enum_names = self.enum_names.clone();
2393 fn_compiler.compile_block(body)?;
2394 fn_compiler.chunk.emit(Op::Return, self.line);
2395 let params = vec![variable.clone().unwrap_or_else(|| "__i__".to_string())];
2396 let func = CompiledFunction {
2397 name: "<parallel>".to_string(),
2398 params,
2399 default_start: None,
2400 chunk: fn_compiler.chunk,
2401 is_generator: false,
2402 has_rest_param: false,
2403 };
2404 let fn_idx = self.chunk.functions.len();
2405 self.chunk.functions.push(func);
2406 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2407 self.chunk.emit(Op::Parallel, self.line);
2408 }
2409
2410 Node::ParallelMap {
2411 list,
2412 variable,
2413 body,
2414 } => {
2415 self.compile_node(list)?;
2416 let mut fn_compiler = Compiler::new();
2417 fn_compiler.enum_names = self.enum_names.clone();
2418 fn_compiler.compile_block(body)?;
2419 fn_compiler.chunk.emit(Op::Return, self.line);
2420 let func = CompiledFunction {
2421 name: "<parallel_map>".to_string(),
2422 params: vec![variable.clone()],
2423 default_start: None,
2424 chunk: fn_compiler.chunk,
2425 is_generator: false,
2426 has_rest_param: false,
2427 };
2428 let fn_idx = self.chunk.functions.len();
2429 self.chunk.functions.push(func);
2430 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2431 self.chunk.emit(Op::ParallelMap, self.line);
2432 }
2433
2434 Node::ParallelSettle {
2435 list,
2436 variable,
2437 body,
2438 } => {
2439 self.compile_node(list)?;
2440 let mut fn_compiler = Compiler::new();
2441 fn_compiler.enum_names = self.enum_names.clone();
2442 fn_compiler.compile_block(body)?;
2443 fn_compiler.chunk.emit(Op::Return, self.line);
2444 let func = CompiledFunction {
2445 name: "<parallel_settle>".to_string(),
2446 params: vec![variable.clone()],
2447 default_start: None,
2448 chunk: fn_compiler.chunk,
2449 is_generator: false,
2450 has_rest_param: false,
2451 };
2452 let fn_idx = self.chunk.functions.len();
2453 self.chunk.functions.push(func);
2454 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2455 self.chunk.emit(Op::ParallelSettle, self.line);
2456 }
2457
2458 Node::SpawnExpr { body } => {
2459 let mut fn_compiler = Compiler::new();
2460 fn_compiler.enum_names = self.enum_names.clone();
2461 fn_compiler.compile_block(body)?;
2462 fn_compiler.chunk.emit(Op::Return, self.line);
2463 let func = CompiledFunction {
2464 name: "<spawn>".to_string(),
2465 params: vec![],
2466 default_start: None,
2467 chunk: fn_compiler.chunk,
2468 is_generator: false,
2469 has_rest_param: false,
2470 };
2471 let fn_idx = self.chunk.functions.len();
2472 self.chunk.functions.push(func);
2473 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2474 self.chunk.emit(Op::Spawn, self.line);
2475 }
2476 Node::SelectExpr {
2477 cases,
2478 timeout,
2479 default_body,
2480 } => {
2481 let builtin_name = if timeout.is_some() {
2488 "__select_timeout"
2489 } else if default_body.is_some() {
2490 "__select_try"
2491 } else {
2492 "__select_list"
2493 };
2494
2495 let name_idx = self
2497 .chunk
2498 .add_constant(Constant::String(builtin_name.into()));
2499 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2500
2501 for case in cases {
2503 self.compile_node(&case.channel)?;
2504 }
2505 self.chunk
2506 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2507
2508 if let Some((duration_expr, _)) = timeout {
2510 self.compile_node(duration_expr)?;
2511 self.chunk.emit_u8(Op::Call, 2, self.line);
2512 } else {
2513 self.chunk.emit_u8(Op::Call, 1, self.line);
2514 }
2515
2516 self.temp_counter += 1;
2518 let result_name = format!("__sel_result_{}__", self.temp_counter);
2519 let result_idx = self
2520 .chunk
2521 .add_constant(Constant::String(result_name.clone()));
2522 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2523
2524 let mut end_jumps = Vec::new();
2526
2527 for (i, case) in cases.iter().enumerate() {
2528 let get_r = self
2529 .chunk
2530 .add_constant(Constant::String(result_name.clone()));
2531 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2532 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2533 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2534 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2535 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2536 self.chunk.emit(Op::Equal, self.line);
2537 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2538 self.chunk.emit(Op::Pop, self.line);
2539 self.begin_scope();
2540
2541 let get_r2 = self
2543 .chunk
2544 .add_constant(Constant::String(result_name.clone()));
2545 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2546 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2547 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2548 let var_idx = self
2549 .chunk
2550 .add_constant(Constant::String(case.variable.clone()));
2551 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2552
2553 self.compile_try_body(&case.body)?;
2554 self.end_scope();
2555 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2556 self.chunk.patch_jump(skip);
2557 self.chunk.emit(Op::Pop, self.line);
2558 }
2559
2560 if let Some((_, ref timeout_body)) = timeout {
2562 self.compile_try_body(timeout_body)?;
2563 } else if let Some(ref def_body) = default_body {
2564 self.compile_try_body(def_body)?;
2565 } else {
2566 self.chunk.emit(Op::Nil, self.line);
2567 }
2568
2569 for ej in end_jumps {
2570 self.chunk.patch_jump(ej);
2571 }
2572 }
2573 Node::Spread(_) => {
2574 return Err(CompileError {
2575 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2576 line: self.line,
2577 });
2578 }
2579 }
2580 Ok(())
2581 }
2582
2583 fn compile_destructuring(
2587 &mut self,
2588 pattern: &BindingPattern,
2589 is_mutable: bool,
2590 ) -> Result<(), CompileError> {
2591 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2592 match pattern {
2593 BindingPattern::Identifier(name) => {
2594 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2596 self.chunk.emit_u16(def_op, idx, self.line);
2597 }
2598 BindingPattern::Dict(fields) => {
2599 self.chunk.emit(Op::Dup, self.line);
2602 let assert_idx = self
2603 .chunk
2604 .add_constant(Constant::String("__assert_dict".into()));
2605 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2606 self.chunk.emit(Op::Swap, self.line);
2607 self.chunk.emit_u8(Op::Call, 1, self.line);
2608 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2613 let rest_field = fields.iter().find(|f| f.is_rest);
2614
2615 for field in &non_rest {
2616 self.chunk.emit(Op::Dup, self.line);
2617 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2618 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2619 self.chunk.emit(Op::Subscript, self.line);
2620 if let Some(default_expr) = &field.default_value {
2622 self.chunk.emit(Op::Dup, self.line);
2623 self.chunk.emit(Op::Nil, self.line);
2624 self.chunk.emit(Op::NotEqual, self.line);
2625 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2626 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2629 let end = self.chunk.emit_jump(Op::Jump, self.line);
2630 self.chunk.patch_jump(skip_default);
2631 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2633 }
2634 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2635 let name_idx = self
2636 .chunk
2637 .add_constant(Constant::String(binding_name.to_string()));
2638 self.chunk.emit_u16(def_op, name_idx, self.line);
2639 }
2640
2641 if let Some(rest) = rest_field {
2642 let fn_idx = self
2645 .chunk
2646 .add_constant(Constant::String("__dict_rest".into()));
2647 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2648 self.chunk.emit(Op::Swap, self.line);
2650 for field in &non_rest {
2652 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2653 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2654 }
2655 self.chunk
2656 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2657 self.chunk.emit_u8(Op::Call, 2, self.line);
2659 let rest_name = &rest.key;
2660 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2661 self.chunk.emit_u16(def_op, rest_idx, self.line);
2662 } else {
2663 self.chunk.emit(Op::Pop, self.line);
2665 }
2666 }
2667 BindingPattern::List(elements) => {
2668 self.chunk.emit(Op::Dup, self.line);
2671 let assert_idx = self
2672 .chunk
2673 .add_constant(Constant::String("__assert_list".into()));
2674 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2675 self.chunk.emit(Op::Swap, self.line);
2676 self.chunk.emit_u8(Op::Call, 1, self.line);
2677 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2680 let rest_elem = elements.iter().find(|e| e.is_rest);
2681
2682 for (i, elem) in non_rest.iter().enumerate() {
2683 self.chunk.emit(Op::Dup, self.line);
2684 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2685 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2686 self.chunk.emit(Op::Subscript, self.line);
2687 if let Some(default_expr) = &elem.default_value {
2689 self.chunk.emit(Op::Dup, self.line);
2690 self.chunk.emit(Op::Nil, self.line);
2691 self.chunk.emit(Op::NotEqual, self.line);
2692 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2693 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2696 let end = self.chunk.emit_jump(Op::Jump, self.line);
2697 self.chunk.patch_jump(skip_default);
2698 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2700 }
2701 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2702 self.chunk.emit_u16(def_op, name_idx, self.line);
2703 }
2704
2705 if let Some(rest) = rest_elem {
2706 let start_idx = self
2710 .chunk
2711 .add_constant(Constant::Int(non_rest.len() as i64));
2712 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2713 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2715 let rest_name_idx =
2716 self.chunk.add_constant(Constant::String(rest.name.clone()));
2717 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2718 } else {
2719 self.chunk.emit(Op::Pop, self.line);
2721 }
2722 }
2723 }
2724 Ok(())
2725 }
2726
2727 fn produces_value(node: &Node) -> bool {
2729 match node {
2730 Node::LetBinding { .. }
2732 | Node::VarBinding { .. }
2733 | Node::Assignment { .. }
2734 | Node::ReturnStmt { .. }
2735 | Node::FnDecl { .. }
2736 | Node::ToolDecl { .. }
2737 | Node::ImplBlock { .. }
2738 | Node::StructDecl { .. }
2739 | Node::EnumDecl { .. }
2740 | Node::InterfaceDecl { .. }
2741 | Node::TypeDecl { .. }
2742 | Node::ThrowStmt { .. }
2743 | Node::BreakStmt
2744 | Node::ContinueStmt
2745 | Node::RequireStmt { .. } => false,
2746 Node::TryCatch { .. }
2748 | Node::TryExpr { .. }
2749 | Node::Retry { .. }
2750 | Node::GuardStmt { .. }
2751 | Node::DeadlineBlock { .. }
2752 | Node::MutexBlock { .. }
2753 | Node::Spread(_) => true,
2754 _ => true,
2756 }
2757 }
2758}
2759
2760impl Compiler {
2761 pub fn compile_fn_body(
2774 &mut self,
2775 params: &[TypedParam],
2776 body: &[SNode],
2777 source_file: Option<String>,
2778 ) -> Result<CompiledFunction, CompileError> {
2779 let mut fn_compiler = Compiler::new();
2780 fn_compiler.enum_names = self.enum_names.clone();
2781 fn_compiler.emit_default_preamble(params)?;
2782 fn_compiler.emit_type_checks(params);
2783 let is_gen = body_contains_yield(body);
2784 fn_compiler.compile_block(body)?;
2785 fn_compiler.chunk.emit(Op::Nil, 0);
2786 fn_compiler.chunk.emit(Op::Return, 0);
2787 fn_compiler.chunk.source_file = source_file;
2788 Ok(CompiledFunction {
2789 name: String::new(),
2790 params: TypedParam::names(params),
2791 default_start: TypedParam::default_start(params),
2792 chunk: fn_compiler.chunk,
2793 is_generator: is_gen,
2794 has_rest_param: false,
2795 })
2796 }
2797
2798 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2800 self.begin_scope();
2801 if body.is_empty() {
2802 self.chunk.emit(Op::Nil, self.line);
2803 } else {
2804 self.compile_block(body)?;
2805 if !Self::produces_value(&body.last().unwrap().node) {
2806 self.chunk.emit(Op::Nil, self.line);
2807 }
2808 }
2809 self.end_scope();
2810 Ok(())
2811 }
2812
2813 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2815 match op {
2816 "+" => self.chunk.emit(Op::Add, self.line),
2817 "-" => self.chunk.emit(Op::Sub, self.line),
2818 "*" => self.chunk.emit(Op::Mul, self.line),
2819 "/" => self.chunk.emit(Op::Div, self.line),
2820 "%" => self.chunk.emit(Op::Mod, self.line),
2821 _ => {
2822 return Err(CompileError {
2823 message: format!("Unknown compound operator: {op}"),
2824 line: self.line,
2825 })
2826 }
2827 }
2828 Ok(())
2829 }
2830
2831 fn root_var_name(&self, node: &SNode) -> Option<String> {
2833 match &node.node {
2834 Node::Identifier(name) => Some(name.clone()),
2835 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2836 self.root_var_name(object)
2837 }
2838 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2839 _ => None,
2840 }
2841 }
2842
2843 fn compile_top_level_declarations(&mut self, program: &[SNode]) -> Result<(), CompileError> {
2844 for sn in program {
2845 if matches!(
2846 &sn.node,
2847 Node::FnDecl { .. }
2848 | Node::ToolDecl { .. }
2849 | Node::ImplBlock { .. }
2850 | Node::StructDecl { .. }
2851 | Node::EnumDecl { .. }
2852 | Node::InterfaceDecl { .. }
2853 | Node::TypeDecl { .. }
2854 ) {
2855 self.compile_node(sn)?;
2856 }
2857 }
2858 Ok(())
2859 }
2860}
2861
2862impl Compiler {
2863 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
2865 for sn in nodes {
2866 match &sn.node {
2867 Node::EnumDecl { name, .. } => {
2868 names.insert(name.clone());
2869 }
2870 Node::Pipeline { body, .. } => {
2871 Self::collect_enum_names(body, names);
2872 }
2873 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
2874 Self::collect_enum_names(body, names);
2875 }
2876 Node::Block(stmts) => {
2877 Self::collect_enum_names(stmts, names);
2878 }
2879 _ => {}
2880 }
2881 }
2882 }
2883
2884 fn collect_interface_methods(
2885 nodes: &[SNode],
2886 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
2887 ) {
2888 for sn in nodes {
2889 match &sn.node {
2890 Node::InterfaceDecl { name, methods, .. } => {
2891 let method_names: Vec<String> =
2892 methods.iter().map(|m| m.name.clone()).collect();
2893 interfaces.insert(name.clone(), method_names);
2894 }
2895 Node::Pipeline { body, .. }
2896 | Node::FnDecl { body, .. }
2897 | Node::ToolDecl { body, .. } => {
2898 Self::collect_interface_methods(body, interfaces);
2899 }
2900 Node::Block(stmts) => {
2901 Self::collect_interface_methods(stmts, interfaces);
2902 }
2903 _ => {}
2904 }
2905 }
2906 }
2907}
2908
2909impl Default for Compiler {
2910 fn default() -> Self {
2911 Self::new()
2912 }
2913}
2914
2915fn body_contains_yield(nodes: &[SNode]) -> bool {
2917 nodes.iter().any(|sn| node_contains_yield(&sn.node))
2918}
2919
2920fn node_contains_yield(node: &Node) -> bool {
2921 match node {
2922 Node::YieldExpr { .. } => true,
2923 Node::FnDecl { .. } | Node::Closure { .. } => false,
2926 Node::Block(stmts) => body_contains_yield(stmts),
2927 Node::IfElse {
2928 condition,
2929 then_body,
2930 else_body,
2931 } => {
2932 node_contains_yield(&condition.node)
2933 || body_contains_yield(then_body)
2934 || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
2935 }
2936 Node::WhileLoop { condition, body } => {
2937 node_contains_yield(&condition.node) || body_contains_yield(body)
2938 }
2939 Node::ForIn { iterable, body, .. } => {
2940 node_contains_yield(&iterable.node) || body_contains_yield(body)
2941 }
2942 Node::TryCatch {
2943 body, catch_body, ..
2944 } => body_contains_yield(body) || body_contains_yield(catch_body),
2945 Node::TryExpr { body } => body_contains_yield(body),
2946 _ => false,
2947 }
2948}
2949
2950fn contains_pipe_placeholder(node: &SNode) -> bool {
2952 match &node.node {
2953 Node::Identifier(name) if name == "_" => true,
2954 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
2955 Node::MethodCall { object, args, .. } => {
2956 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
2957 }
2958 Node::BinaryOp { left, right, .. } => {
2959 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
2960 }
2961 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
2962 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
2963 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
2964 Node::SubscriptAccess { object, index } => {
2965 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
2966 }
2967 _ => false,
2968 }
2969}
2970
2971fn replace_pipe_placeholder(node: &SNode) -> SNode {
2973 let new_node = match &node.node {
2974 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
2975 Node::FunctionCall { name, args } => Node::FunctionCall {
2976 name: name.clone(),
2977 args: args.iter().map(replace_pipe_placeholder).collect(),
2978 },
2979 Node::MethodCall {
2980 object,
2981 method,
2982 args,
2983 } => Node::MethodCall {
2984 object: Box::new(replace_pipe_placeholder(object)),
2985 method: method.clone(),
2986 args: args.iter().map(replace_pipe_placeholder).collect(),
2987 },
2988 Node::BinaryOp { op, left, right } => Node::BinaryOp {
2989 op: op.clone(),
2990 left: Box::new(replace_pipe_placeholder(left)),
2991 right: Box::new(replace_pipe_placeholder(right)),
2992 },
2993 Node::UnaryOp { op, operand } => Node::UnaryOp {
2994 op: op.clone(),
2995 operand: Box::new(replace_pipe_placeholder(operand)),
2996 },
2997 Node::ListLiteral(items) => {
2998 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
2999 }
3000 Node::PropertyAccess { object, property } => Node::PropertyAccess {
3001 object: Box::new(replace_pipe_placeholder(object)),
3002 property: property.clone(),
3003 },
3004 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
3005 object: Box::new(replace_pipe_placeholder(object)),
3006 index: Box::new(replace_pipe_placeholder(index)),
3007 },
3008 _ => return node.clone(),
3009 };
3010 SNode::new(new_node, node.span)
3011}
3012
3013#[cfg(test)]
3014mod tests {
3015 use super::*;
3016 use harn_lexer::Lexer;
3017 use harn_parser::Parser;
3018
3019 fn compile_source(source: &str) -> Chunk {
3020 let mut lexer = Lexer::new(source);
3021 let tokens = lexer.tokenize().unwrap();
3022 let mut parser = Parser::new(tokens);
3023 let program = parser.parse().unwrap();
3024 Compiler::new().compile(&program).unwrap()
3025 }
3026
3027 #[test]
3028 fn test_compile_arithmetic() {
3029 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
3030 assert!(!chunk.code.is_empty());
3031 assert!(chunk.constants.contains(&Constant::Int(2)));
3033 assert!(chunk.constants.contains(&Constant::Int(3)));
3034 }
3035
3036 #[test]
3037 fn test_compile_function_call() {
3038 let chunk = compile_source("pipeline test(task) { log(42) }");
3039 let disasm = chunk.disassemble("test");
3040 assert!(disasm.contains("CALL"));
3041 }
3042
3043 #[test]
3044 fn test_compile_if_else() {
3045 let chunk =
3046 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
3047 let disasm = chunk.disassemble("test");
3048 assert!(disasm.contains("JUMP_IF_FALSE"));
3049 assert!(disasm.contains("JUMP"));
3050 }
3051
3052 #[test]
3053 fn test_compile_while() {
3054 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
3055 let disasm = chunk.disassemble("test");
3056 assert!(disasm.contains("JUMP_IF_FALSE"));
3057 assert!(disasm.contains("JUMP"));
3059 }
3060
3061 #[test]
3062 fn test_compile_closure() {
3063 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
3064 assert!(!chunk.functions.is_empty());
3065 assert_eq!(chunk.functions[0].params, vec!["x"]);
3066 }
3067
3068 #[test]
3069 fn test_compile_list() {
3070 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
3071 let disasm = chunk.disassemble("test");
3072 assert!(disasm.contains("BUILD_LIST"));
3073 }
3074
3075 #[test]
3076 fn test_compile_dict() {
3077 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
3078 let disasm = chunk.disassemble("test");
3079 assert!(disasm.contains("BUILD_DICT"));
3080 }
3081
3082 #[test]
3083 fn test_disassemble() {
3084 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
3085 let disasm = chunk.disassemble("test");
3086 assert!(disasm.contains("CONSTANT"));
3088 assert!(disasm.contains("ADD"));
3089 assert!(disasm.contains("CALL"));
3090 }
3091}