1use std::collections::BTreeMap;
2use std::rc::Rc;
3
4use harn_lexer::StringSegment;
5use harn_parser::{BindingPattern, Node, ParallelMode, 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 if !self.finally_bodies.is_empty() {
139 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
140 for fb in &finallys {
141 self.compile_finally_inline(fb)?;
142 }
143 }
144 self.chunk.emit(Op::Nil, self.line);
145 self.chunk.emit(Op::Return, self.line);
146 Ok(self.chunk)
147 }
148
149 pub fn compile_named(
151 mut self,
152 program: &[SNode],
153 pipeline_name: &str,
154 ) -> Result<Chunk, CompileError> {
155 Self::collect_enum_names(program, &mut self.enum_names);
156 Self::collect_interface_methods(program, &mut self.interface_methods);
157
158 for sn in program {
159 if matches!(
160 &sn.node,
161 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
162 ) {
163 self.compile_node(sn)?;
164 }
165 }
166 let target = program
167 .iter()
168 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == pipeline_name));
169
170 if let Some(sn) = target {
171 self.compile_top_level_declarations(program)?;
172 if let Node::Pipeline { body, extends, .. } = &sn.node {
173 if let Some(parent_name) = extends {
174 self.compile_parent_pipeline(program, parent_name)?;
175 }
176 self.compile_block(body)?;
177 }
178 }
179
180 if !self.finally_bodies.is_empty() {
182 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
183 for fb in &finallys {
184 self.compile_finally_inline(fb)?;
185 }
186 }
187 self.chunk.emit(Op::Nil, self.line);
188 self.chunk.emit(Op::Return, self.line);
189 Ok(self.chunk)
190 }
191
192 fn compile_parent_pipeline(
194 &mut self,
195 program: &[SNode],
196 parent_name: &str,
197 ) -> Result<(), CompileError> {
198 let parent = program
199 .iter()
200 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
201 if let Some(sn) = parent {
202 if let Node::Pipeline { body, extends, .. } = &sn.node {
203 if let Some(grandparent) = extends {
205 self.compile_parent_pipeline(program, grandparent)?;
206 }
207 for stmt in body {
209 self.compile_node(stmt)?;
210 if Self::produces_value(&stmt.node) {
211 self.chunk.emit(Op::Pop, self.line);
212 }
213 }
214 }
215 }
216 Ok(())
217 }
218
219 fn emit_default_preamble(&mut self, params: &[TypedParam]) -> Result<(), CompileError> {
224 for (i, param) in params.iter().enumerate() {
225 if let Some(default_expr) = ¶m.default_value {
226 self.chunk.emit(Op::GetArgc, self.line);
227 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
228 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
229 self.chunk.emit(Op::GreaterEqual, self.line);
231 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
232 self.chunk.emit(Op::Pop, self.line);
234 self.compile_node(default_expr)?;
236 let name_idx = self
237 .chunk
238 .add_constant(Constant::String(param.name.clone()));
239 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
240 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
241 self.chunk.patch_jump(skip_jump);
242 self.chunk.emit(Op::Pop, self.line);
244 self.chunk.patch_jump(end_jump);
245 }
246 }
247 Ok(())
248 }
249
250 fn emit_type_checks(&mut self, params: &[TypedParam]) {
255 for param in params {
256 if let Some(type_expr) = ¶m.type_expr {
257 if let harn_parser::TypeExpr::Named(name) = type_expr {
259 if let Some(methods) = self.interface_methods.get(name) {
260 let fn_idx = self
261 .chunk
262 .add_constant(Constant::String("__assert_interface".into()));
263 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
264 let var_idx = self
265 .chunk
266 .add_constant(Constant::String(param.name.clone()));
267 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
268 let name_idx = self
269 .chunk
270 .add_constant(Constant::String(param.name.clone()));
271 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
272 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
273 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
274 let methods_str = methods.join(",");
275 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
276 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
277 self.chunk.emit_u8(Op::Call, 4, self.line);
278 self.chunk.emit(Op::Pop, self.line);
279 continue;
280 }
281 }
282
283 if let Some(schema) = Self::type_expr_to_schema_value(type_expr) {
284 let fn_idx = self
285 .chunk
286 .add_constant(Constant::String("__assert_schema".into()));
287 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
288 let var_idx = self
289 .chunk
290 .add_constant(Constant::String(param.name.clone()));
291 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
292 let name_idx = self
293 .chunk
294 .add_constant(Constant::String(param.name.clone()));
295 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
296 self.emit_vm_value_literal(&schema);
297 self.chunk.emit_u8(Op::Call, 3, self.line);
298 self.chunk.emit(Op::Pop, self.line);
299 }
300 }
301 }
302 }
303
304 fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
305 match type_expr {
306 harn_parser::TypeExpr::Named(name) => match name.as_str() {
307 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
308 | "closure" => Some(VmValue::Dict(Rc::new(BTreeMap::from([(
309 "type".to_string(),
310 VmValue::String(Rc::from(name.as_str())),
311 )])))),
312 _ => None,
313 },
314 harn_parser::TypeExpr::Shape(fields) => {
315 let mut properties = BTreeMap::new();
316 let mut required = Vec::new();
317 for field in fields {
318 let field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
319 properties.insert(field.name.clone(), field_schema);
320 if !field.optional {
321 required.push(VmValue::String(Rc::from(field.name.as_str())));
322 }
323 }
324 let mut out = BTreeMap::new();
325 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
326 out.insert("properties".to_string(), VmValue::Dict(Rc::new(properties)));
327 if !required.is_empty() {
328 out.insert("required".to_string(), VmValue::List(Rc::new(required)));
329 }
330 Some(VmValue::Dict(Rc::new(out)))
331 }
332 harn_parser::TypeExpr::List(inner) => {
333 let mut out = BTreeMap::new();
334 out.insert("type".to_string(), VmValue::String(Rc::from("list")));
335 if let Some(item_schema) = Self::type_expr_to_schema_value(inner) {
336 out.insert("items".to_string(), item_schema);
337 }
338 Some(VmValue::Dict(Rc::new(out)))
339 }
340 harn_parser::TypeExpr::DictType(key, value) => {
341 let mut out = BTreeMap::new();
342 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
343 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
344 if let Some(value_schema) = Self::type_expr_to_schema_value(value) {
345 out.insert("additional_properties".to_string(), value_schema);
346 }
347 }
348 Some(VmValue::Dict(Rc::new(out)))
349 }
350 harn_parser::TypeExpr::Union(members) => {
351 let branches = members
352 .iter()
353 .filter_map(Self::type_expr_to_schema_value)
354 .collect::<Vec<_>>();
355 if branches.is_empty() {
356 None
357 } else {
358 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
359 "union".to_string(),
360 VmValue::List(Rc::new(branches)),
361 )]))))
362 }
363 }
364 harn_parser::TypeExpr::FnType { .. } => {
365 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
366 "type".to_string(),
367 VmValue::String(Rc::from("closure")),
368 )]))))
369 }
370 harn_parser::TypeExpr::Never => None,
371 }
372 }
373
374 fn emit_vm_value_literal(&mut self, value: &VmValue) {
375 match value {
376 VmValue::String(text) => {
377 let idx = self.chunk.add_constant(Constant::String(text.to_string()));
378 self.chunk.emit_u16(Op::Constant, idx, self.line);
379 }
380 VmValue::Int(number) => {
381 let idx = self.chunk.add_constant(Constant::Int(*number));
382 self.chunk.emit_u16(Op::Constant, idx, self.line);
383 }
384 VmValue::Float(number) => {
385 let idx = self.chunk.add_constant(Constant::Float(*number));
386 self.chunk.emit_u16(Op::Constant, idx, self.line);
387 }
388 VmValue::Bool(value) => {
389 let idx = self.chunk.add_constant(Constant::Bool(*value));
390 self.chunk.emit_u16(Op::Constant, idx, self.line);
391 }
392 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
393 VmValue::List(items) => {
394 for item in items.iter() {
395 self.emit_vm_value_literal(item);
396 }
397 self.chunk
398 .emit_u16(Op::BuildList, items.len() as u16, self.line);
399 }
400 VmValue::Dict(entries) => {
401 for (key, item) in entries.iter() {
402 let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
403 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
404 self.emit_vm_value_literal(item);
405 }
406 self.chunk
407 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
408 }
409 _ => {}
410 }
411 }
412
413 fn emit_type_name_extra(&mut self, type_name_idx: u16) {
415 let hi = (type_name_idx >> 8) as u8;
416 let lo = type_name_idx as u8;
417 self.chunk.code.push(hi);
418 self.chunk.code.push(lo);
419 self.chunk.lines.push(self.line);
420 self.chunk.columns.push(self.column);
421 self.chunk.lines.push(self.line);
422 self.chunk.columns.push(self.column);
423 }
424
425 fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
427 if body.is_empty() {
428 self.chunk.emit(Op::Nil, self.line);
429 } else {
430 self.compile_scoped_block(body)?;
431 }
432 Ok(())
433 }
434
435 fn compile_catch_binding(&mut self, error_var: &Option<String>) -> Result<(), CompileError> {
437 if let Some(var_name) = error_var {
438 let idx = self.chunk.add_constant(Constant::String(var_name.clone()));
439 self.chunk.emit_u16(Op::DefLet, idx, self.line);
440 } else {
441 self.chunk.emit(Op::Pop, self.line);
442 }
443 Ok(())
444 }
445
446 fn compile_finally_inline(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
448 if !finally_body.is_empty() {
449 self.compile_scoped_block(finally_body)?;
450 if Self::produces_value(&finally_body.last().unwrap().node) {
452 self.chunk.emit(Op::Pop, self.line);
453 }
454 }
455 Ok(())
456 }
457
458 fn compile_rethrow_with_finally(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
460 self.temp_counter += 1;
462 let temp_name = format!("__finally_err_{}__", self.temp_counter);
463 let err_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
464 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
465 self.compile_finally_inline(finally_body)?;
466 let get_idx = self.chunk.add_constant(Constant::String(temp_name));
467 self.chunk.emit_u16(Op::GetVar, get_idx, self.line);
468 self.chunk.emit(Op::Throw, self.line);
469 Ok(())
470 }
471
472 fn begin_scope(&mut self) {
473 self.chunk.emit(Op::PushScope, self.line);
474 self.scope_depth += 1;
475 }
476
477 fn end_scope(&mut self) {
478 if self.scope_depth > 0 {
479 self.chunk.emit(Op::PopScope, self.line);
480 self.scope_depth -= 1;
481 }
482 }
483
484 fn unwind_scopes_to(&mut self, target_depth: usize) {
485 while self.scope_depth > target_depth {
486 self.chunk.emit(Op::PopScope, self.line);
487 self.scope_depth -= 1;
488 }
489 }
490
491 fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
492 self.begin_scope();
493 if stmts.is_empty() {
494 self.chunk.emit(Op::Nil, self.line);
495 } else {
496 self.compile_block(stmts)?;
497 }
498 self.end_scope();
499 Ok(())
500 }
501
502 fn compile_scoped_statements(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
503 self.begin_scope();
504 for sn in stmts {
505 self.compile_node(sn)?;
506 if Self::produces_value(&sn.node) {
507 self.chunk.emit(Op::Pop, self.line);
508 }
509 }
510 self.end_scope();
511 Ok(())
512 }
513
514 fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
515 for (i, snode) in stmts.iter().enumerate() {
516 self.compile_node(snode)?;
517 let is_last = i == stmts.len() - 1;
518 if is_last {
519 if !Self::produces_value(&snode.node) {
522 self.chunk.emit(Op::Nil, self.line);
523 }
524 } else {
525 if Self::produces_value(&snode.node) {
527 self.chunk.emit(Op::Pop, self.line);
528 }
529 }
530 }
531 Ok(())
532 }
533
534 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
535 self.line = snode.span.line as u32;
536 self.column = snode.span.column as u32;
537 self.chunk.set_column(self.column);
538 match &snode.node {
539 Node::IntLiteral(n) => {
540 let idx = self.chunk.add_constant(Constant::Int(*n));
541 self.chunk.emit_u16(Op::Constant, idx, self.line);
542 }
543 Node::FloatLiteral(n) => {
544 let idx = self.chunk.add_constant(Constant::Float(*n));
545 self.chunk.emit_u16(Op::Constant, idx, self.line);
546 }
547 Node::StringLiteral(s) | Node::RawStringLiteral(s) => {
548 let idx = self.chunk.add_constant(Constant::String(s.clone()));
549 self.chunk.emit_u16(Op::Constant, idx, self.line);
550 }
551 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
552 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
553 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
554 Node::DurationLiteral(ms) => {
555 let idx = self.chunk.add_constant(Constant::Duration(*ms));
556 self.chunk.emit_u16(Op::Constant, idx, self.line);
557 }
558
559 Node::Identifier(name) => {
560 let idx = self.chunk.add_constant(Constant::String(name.clone()));
561 self.chunk.emit_u16(Op::GetVar, idx, self.line);
562 }
563
564 Node::LetBinding { pattern, value, .. } => {
565 self.compile_node(value)?;
566 self.compile_destructuring(pattern, false)?;
567 }
568
569 Node::VarBinding { pattern, value, .. } => {
570 self.compile_node(value)?;
571 self.compile_destructuring(pattern, true)?;
572 }
573
574 Node::Assignment {
575 target, value, op, ..
576 } => {
577 if let Node::Identifier(name) = &target.node {
578 let idx = self.chunk.add_constant(Constant::String(name.clone()));
579 if let Some(op) = op {
580 self.chunk.emit_u16(Op::GetVar, idx, self.line);
581 self.compile_node(value)?;
582 self.emit_compound_op(op)?;
583 self.chunk.emit_u16(Op::SetVar, idx, self.line);
584 } else {
585 self.compile_node(value)?;
586 self.chunk.emit_u16(Op::SetVar, idx, self.line);
587 }
588 } else if let Node::PropertyAccess { object, property } = &target.node {
589 if let Some(var_name) = self.root_var_name(object) {
591 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
592 let prop_idx = self.chunk.add_constant(Constant::String(property.clone()));
593 if let Some(op) = op {
594 self.compile_node(target)?; self.compile_node(value)?;
597 self.emit_compound_op(op)?;
598 } else {
599 self.compile_node(value)?;
600 }
601 self.chunk.emit_u16(Op::SetProperty, prop_idx, self.line);
604 let hi = (var_idx >> 8) as u8;
606 let lo = var_idx as u8;
607 self.chunk.code.push(hi);
608 self.chunk.code.push(lo);
609 self.chunk.lines.push(self.line);
610 self.chunk.columns.push(self.column);
611 self.chunk.lines.push(self.line);
612 self.chunk.columns.push(self.column);
613 }
614 } else if let Node::SubscriptAccess { object, index } = &target.node {
615 if let Some(var_name) = self.root_var_name(object) {
617 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
618 if let Some(op) = op {
619 self.compile_node(target)?;
620 self.compile_node(value)?;
621 self.emit_compound_op(op)?;
622 } else {
623 self.compile_node(value)?;
624 }
625 self.compile_node(index)?;
626 self.chunk.emit_u16(Op::SetSubscript, var_idx, self.line);
627 }
628 }
629 }
630
631 Node::BinaryOp { op, left, right } => {
632 match op.as_str() {
634 "&&" => {
635 self.compile_node(left)?;
636 let jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
637 self.chunk.emit(Op::Pop, self.line);
638 self.compile_node(right)?;
639 self.chunk.patch_jump(jump);
640 self.chunk.emit(Op::Not, self.line);
642 self.chunk.emit(Op::Not, self.line);
643 return Ok(());
644 }
645 "||" => {
646 self.compile_node(left)?;
647 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
648 self.chunk.emit(Op::Pop, self.line);
649 self.compile_node(right)?;
650 self.chunk.patch_jump(jump);
651 self.chunk.emit(Op::Not, self.line);
652 self.chunk.emit(Op::Not, self.line);
653 return Ok(());
654 }
655 "??" => {
656 self.compile_node(left)?;
657 self.chunk.emit(Op::Dup, self.line);
658 self.chunk.emit(Op::Nil, self.line);
660 self.chunk.emit(Op::NotEqual, self.line);
661 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
662 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(right)?;
665 let end = self.chunk.emit_jump(Op::Jump, self.line);
666 self.chunk.patch_jump(jump);
667 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
669 return Ok(());
670 }
671 "|>" => {
672 self.compile_node(left)?;
673 if contains_pipe_placeholder(right) {
676 let replaced = replace_pipe_placeholder(right);
677 let closure_node = SNode::dummy(Node::Closure {
678 params: vec![TypedParam {
679 name: "__pipe".into(),
680 type_expr: None,
681 default_value: None,
682 rest: false,
683 }],
684 body: vec![replaced],
685 fn_syntax: false,
686 });
687 self.compile_node(&closure_node)?;
688 } else {
689 self.compile_node(right)?;
690 }
691 self.chunk.emit(Op::Pipe, self.line);
692 return Ok(());
693 }
694 _ => {}
695 }
696
697 self.compile_node(left)?;
698 self.compile_node(right)?;
699 match op.as_str() {
700 "+" => self.chunk.emit(Op::Add, self.line),
701 "-" => self.chunk.emit(Op::Sub, self.line),
702 "*" => self.chunk.emit(Op::Mul, self.line),
703 "/" => self.chunk.emit(Op::Div, self.line),
704 "%" => self.chunk.emit(Op::Mod, self.line),
705 "==" => self.chunk.emit(Op::Equal, self.line),
706 "!=" => self.chunk.emit(Op::NotEqual, self.line),
707 "<" => self.chunk.emit(Op::Less, self.line),
708 ">" => self.chunk.emit(Op::Greater, self.line),
709 "<=" => self.chunk.emit(Op::LessEqual, self.line),
710 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
711 "in" => self.chunk.emit(Op::Contains, self.line),
712 "not_in" => {
713 self.chunk.emit(Op::Contains, self.line);
714 self.chunk.emit(Op::Not, self.line);
715 }
716 _ => {
717 return Err(CompileError {
718 message: format!("Unknown operator: {op}"),
719 line: self.line,
720 })
721 }
722 }
723 }
724
725 Node::UnaryOp { op, operand } => {
726 self.compile_node(operand)?;
727 match op.as_str() {
728 "-" => self.chunk.emit(Op::Negate, self.line),
729 "!" => self.chunk.emit(Op::Not, self.line),
730 _ => {}
731 }
732 }
733
734 Node::Ternary {
735 condition,
736 true_expr,
737 false_expr,
738 } => {
739 self.compile_node(condition)?;
740 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
741 self.chunk.emit(Op::Pop, self.line);
742 self.compile_node(true_expr)?;
743 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
744 self.chunk.patch_jump(else_jump);
745 self.chunk.emit(Op::Pop, self.line);
746 self.compile_node(false_expr)?;
747 self.chunk.patch_jump(end_jump);
748 }
749
750 Node::FunctionCall { name, args } => {
751 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
752 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
754 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
755
756 if has_spread {
757 self.chunk.emit_u16(Op::BuildList, 0, self.line);
760 let mut pending = 0u16;
761 for arg in args {
762 if let Node::Spread(inner) = &arg.node {
763 if pending > 0 {
764 self.chunk.emit_u16(Op::BuildList, pending, self.line);
765 self.chunk.emit(Op::Add, self.line);
766 pending = 0;
767 }
768 self.compile_node(inner)?;
769 self.chunk.emit(Op::Dup, self.line);
770 let assert_idx = self
771 .chunk
772 .add_constant(Constant::String("__assert_list".into()));
773 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
774 self.chunk.emit(Op::Swap, self.line);
775 self.chunk.emit_u8(Op::Call, 1, self.line);
776 self.chunk.emit(Op::Pop, self.line);
777 self.chunk.emit(Op::Add, self.line);
778 } else {
779 self.compile_node(arg)?;
780 pending += 1;
781 }
782 }
783 if pending > 0 {
784 self.chunk.emit_u16(Op::BuildList, pending, self.line);
785 self.chunk.emit(Op::Add, self.line);
786 }
787 self.chunk.emit(Op::CallSpread, self.line);
788 } else {
789 for arg in args {
791 self.compile_node(arg)?;
792 }
793 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
794 }
795 }
796
797 Node::MethodCall {
798 object,
799 method,
800 args,
801 } => {
802 if let Node::Identifier(name) = &object.node {
804 if self.enum_names.contains(name) {
805 for arg in args {
807 self.compile_node(arg)?;
808 }
809 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
810 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
811 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
812 let hi = (var_idx >> 8) as u8;
813 let lo = var_idx as u8;
814 self.chunk.code.push(hi);
815 self.chunk.code.push(lo);
816 self.chunk.lines.push(self.line);
817 self.chunk.columns.push(self.column);
818 self.chunk.lines.push(self.line);
819 self.chunk.columns.push(self.column);
820 let fc = args.len() as u16;
821 let fhi = (fc >> 8) as u8;
822 let flo = fc as u8;
823 self.chunk.code.push(fhi);
824 self.chunk.code.push(flo);
825 self.chunk.lines.push(self.line);
826 self.chunk.columns.push(self.column);
827 self.chunk.lines.push(self.line);
828 self.chunk.columns.push(self.column);
829 return Ok(());
830 }
831 }
832 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
833 self.compile_node(object)?;
834 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
835 if has_spread {
836 self.chunk.emit_u16(Op::BuildList, 0, self.line);
838 let mut pending = 0u16;
839 for arg in args {
840 if let Node::Spread(inner) = &arg.node {
841 if pending > 0 {
842 self.chunk.emit_u16(Op::BuildList, pending, self.line);
843 self.chunk.emit(Op::Add, self.line);
844 pending = 0;
845 }
846 self.compile_node(inner)?;
847 self.chunk.emit(Op::Dup, self.line);
848 let assert_idx = self
849 .chunk
850 .add_constant(Constant::String("__assert_list".into()));
851 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
852 self.chunk.emit(Op::Swap, self.line);
853 self.chunk.emit_u8(Op::Call, 1, self.line);
854 self.chunk.emit(Op::Pop, self.line);
855 self.chunk.emit(Op::Add, self.line);
856 } else {
857 self.compile_node(arg)?;
858 pending += 1;
859 }
860 }
861 if pending > 0 {
862 self.chunk.emit_u16(Op::BuildList, pending, self.line);
863 self.chunk.emit(Op::Add, self.line);
864 }
865 self.chunk
866 .emit_u16(Op::MethodCallSpread, name_idx, self.line);
867 } else {
868 for arg in args {
869 self.compile_node(arg)?;
870 }
871 self.chunk
872 .emit_method_call(name_idx, args.len() as u8, self.line);
873 }
874 }
875
876 Node::OptionalMethodCall {
877 object,
878 method,
879 args,
880 } => {
881 self.compile_node(object)?;
882 for arg in args {
883 self.compile_node(arg)?;
884 }
885 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
886 self.chunk
887 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
888 }
889
890 Node::PropertyAccess { object, property } => {
891 if let Node::Identifier(name) = &object.node {
893 if self.enum_names.contains(name) {
894 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
896 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
897 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
898 let hi = (var_idx >> 8) as u8;
899 let lo = var_idx as u8;
900 self.chunk.code.push(hi);
901 self.chunk.code.push(lo);
902 self.chunk.lines.push(self.line);
903 self.chunk.columns.push(self.column);
904 self.chunk.lines.push(self.line);
905 self.chunk.columns.push(self.column);
906 self.chunk.code.push(0);
908 self.chunk.code.push(0);
909 self.chunk.lines.push(self.line);
910 self.chunk.columns.push(self.column);
911 self.chunk.lines.push(self.line);
912 self.chunk.columns.push(self.column);
913 return Ok(());
914 }
915 }
916 self.compile_node(object)?;
917 let idx = self.chunk.add_constant(Constant::String(property.clone()));
918 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
919 }
920
921 Node::OptionalPropertyAccess { object, property } => {
922 self.compile_node(object)?;
923 let idx = self.chunk.add_constant(Constant::String(property.clone()));
924 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
925 }
926
927 Node::SubscriptAccess { object, index } => {
928 self.compile_node(object)?;
929 self.compile_node(index)?;
930 self.chunk.emit(Op::Subscript, self.line);
931 }
932
933 Node::SliceAccess { object, start, end } => {
934 self.compile_node(object)?;
935 if let Some(s) = start {
936 self.compile_node(s)?;
937 } else {
938 self.chunk.emit(Op::Nil, self.line);
939 }
940 if let Some(e) = end {
941 self.compile_node(e)?;
942 } else {
943 self.chunk.emit(Op::Nil, self.line);
944 }
945 self.chunk.emit(Op::Slice, self.line);
946 }
947
948 Node::IfElse {
949 condition,
950 then_body,
951 else_body,
952 } => {
953 self.compile_node(condition)?;
954 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
955 self.chunk.emit(Op::Pop, self.line);
956 self.compile_scoped_block(then_body)?;
957 if let Some(else_body) = else_body {
958 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
959 self.chunk.patch_jump(else_jump);
960 self.chunk.emit(Op::Pop, self.line);
961 self.compile_scoped_block(else_body)?;
962 self.chunk.patch_jump(end_jump);
963 } else {
964 self.chunk.patch_jump(else_jump);
965 self.chunk.emit(Op::Pop, self.line);
966 self.chunk.emit(Op::Nil, self.line);
967 }
968 }
969
970 Node::WhileLoop { condition, body } => {
971 let loop_start = self.chunk.current_offset();
972 self.loop_stack.push(LoopContext {
973 start_offset: loop_start,
974 break_patches: Vec::new(),
975 has_iterator: false,
976 handler_depth: self.handler_depth,
977 finally_depth: self.finally_bodies.len(),
978 scope_depth: self.scope_depth,
979 });
980 self.compile_node(condition)?;
981 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
982 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_statements(body)?;
984 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
986 self.chunk.patch_jump(exit_jump);
987 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
990 for patch_pos in ctx.break_patches {
991 self.chunk.patch_jump(patch_pos);
992 }
993 self.chunk.emit(Op::Nil, self.line);
994 }
995
996 Node::ForIn {
997 pattern,
998 iterable,
999 body,
1000 } => {
1001 self.compile_node(iterable)?;
1003 self.chunk.emit(Op::IterInit, self.line);
1005 let loop_start = self.chunk.current_offset();
1006 self.loop_stack.push(LoopContext {
1007 start_offset: loop_start,
1008 break_patches: Vec::new(),
1009 has_iterator: true,
1010 handler_depth: self.handler_depth,
1011 finally_depth: self.finally_bodies.len(),
1012 scope_depth: self.scope_depth,
1013 });
1014 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
1016 self.begin_scope();
1017 self.compile_destructuring(pattern, true)?;
1019 for sn in body {
1020 self.compile_node(sn)?;
1021 if Self::produces_value(&sn.node) {
1022 self.chunk.emit(Op::Pop, self.line);
1023 }
1024 }
1025 self.end_scope();
1026 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1028 self.chunk.patch_jump(exit_jump_pos);
1029 let ctx = self.loop_stack.pop().unwrap();
1031 for patch_pos in ctx.break_patches {
1032 self.chunk.patch_jump(patch_pos);
1033 }
1034 self.chunk.emit(Op::Nil, self.line);
1036 }
1037
1038 Node::ReturnStmt { value } => {
1039 let has_pending_finally = !self.finally_bodies.is_empty();
1040
1041 if has_pending_finally {
1042 if let Some(val) = value {
1045 self.compile_node(val)?;
1046 } else {
1047 self.chunk.emit(Op::Nil, self.line);
1048 }
1049 self.temp_counter += 1;
1050 let temp_name = format!("__return_val_{}__", self.temp_counter);
1051 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
1052 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
1053 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
1055 for fb in &finallys {
1056 self.compile_finally_inline(fb)?;
1057 }
1058 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
1059 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
1060 self.chunk.emit(Op::Return, self.line);
1061 } else {
1062 if let Some(val) = value {
1064 if let Node::FunctionCall { name, args } = &val.node {
1065 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1066 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1067 for arg in args {
1068 self.compile_node(arg)?;
1069 }
1070 self.chunk
1071 .emit_u8(Op::TailCall, args.len() as u8, self.line);
1072 } else if let Node::BinaryOp { op, left, right } = &val.node {
1073 if op == "|>" {
1074 self.compile_node(left)?;
1075 self.compile_node(right)?;
1076 self.chunk.emit(Op::Swap, self.line);
1077 self.chunk.emit_u8(Op::TailCall, 1, self.line);
1078 } else {
1079 self.compile_node(val)?;
1080 }
1081 } else {
1082 self.compile_node(val)?;
1083 }
1084 } else {
1085 self.chunk.emit(Op::Nil, self.line);
1086 }
1087 self.chunk.emit(Op::Return, self.line);
1088 }
1089 }
1090
1091 Node::BreakStmt => {
1092 if self.loop_stack.is_empty() {
1093 return Err(CompileError {
1094 message: "break outside of loop".to_string(),
1095 line: self.line,
1096 });
1097 }
1098 let ctx = self.loop_stack.last().unwrap();
1100 let finally_depth = ctx.finally_depth;
1101 let handler_depth = ctx.handler_depth;
1102 let has_iterator = ctx.has_iterator;
1103 let scope_depth = ctx.scope_depth;
1104 for _ in handler_depth..self.handler_depth {
1106 self.chunk.emit(Op::PopHandler, self.line);
1107 }
1108 if self.finally_bodies.len() > finally_depth {
1110 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1111 .iter()
1112 .rev()
1113 .cloned()
1114 .collect();
1115 for fb in &finallys {
1116 self.compile_finally_inline(fb)?;
1117 }
1118 }
1119 self.unwind_scopes_to(scope_depth);
1120 if has_iterator {
1121 self.chunk.emit(Op::PopIterator, self.line);
1122 }
1123 let patch = self.chunk.emit_jump(Op::Jump, self.line);
1124 self.loop_stack
1125 .last_mut()
1126 .unwrap()
1127 .break_patches
1128 .push(patch);
1129 }
1130
1131 Node::ContinueStmt => {
1132 if self.loop_stack.is_empty() {
1133 return Err(CompileError {
1134 message: "continue outside of loop".to_string(),
1135 line: self.line,
1136 });
1137 }
1138 let ctx = self.loop_stack.last().unwrap();
1139 let finally_depth = ctx.finally_depth;
1140 let handler_depth = ctx.handler_depth;
1141 let loop_start = ctx.start_offset;
1142 let scope_depth = ctx.scope_depth;
1143 for _ in handler_depth..self.handler_depth {
1144 self.chunk.emit(Op::PopHandler, self.line);
1145 }
1146 if self.finally_bodies.len() > finally_depth {
1147 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1148 .iter()
1149 .rev()
1150 .cloned()
1151 .collect();
1152 for fb in &finallys {
1153 self.compile_finally_inline(fb)?;
1154 }
1155 }
1156 self.unwind_scopes_to(scope_depth);
1157 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1158 }
1159
1160 Node::ListLiteral(elements) => {
1161 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
1162 if !has_spread {
1163 for el in elements {
1164 self.compile_node(el)?;
1165 }
1166 self.chunk
1167 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
1168 } else {
1169 self.chunk.emit_u16(Op::BuildList, 0, self.line);
1172 let mut pending = 0u16;
1173 for el in elements {
1174 if let Node::Spread(inner) = &el.node {
1175 if pending > 0 {
1177 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1178 self.chunk.emit(Op::Add, self.line);
1180 pending = 0;
1181 }
1182 self.compile_node(inner)?;
1184 self.chunk.emit(Op::Dup, self.line);
1185 let assert_idx = self
1186 .chunk
1187 .add_constant(Constant::String("__assert_list".into()));
1188 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1189 self.chunk.emit(Op::Swap, self.line);
1190 self.chunk.emit_u8(Op::Call, 1, self.line);
1191 self.chunk.emit(Op::Pop, self.line);
1192 self.chunk.emit(Op::Add, self.line);
1193 } else {
1194 self.compile_node(el)?;
1195 pending += 1;
1196 }
1197 }
1198 if pending > 0 {
1199 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1200 self.chunk.emit(Op::Add, self.line);
1201 }
1202 }
1203 }
1204
1205 Node::DictLiteral(entries) => {
1206 let has_spread = entries
1207 .iter()
1208 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1209 if !has_spread {
1210 for entry in entries {
1211 self.compile_node(&entry.key)?;
1212 self.compile_node(&entry.value)?;
1213 }
1214 self.chunk
1215 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1216 } else {
1217 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1219 let mut pending = 0u16;
1220 for entry in entries {
1221 if let Node::Spread(inner) = &entry.value.node {
1222 if pending > 0 {
1224 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1225 self.chunk.emit(Op::Add, self.line);
1226 pending = 0;
1227 }
1228 self.compile_node(inner)?;
1230 self.chunk.emit(Op::Dup, self.line);
1231 let assert_idx = self
1232 .chunk
1233 .add_constant(Constant::String("__assert_dict".into()));
1234 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1235 self.chunk.emit(Op::Swap, self.line);
1236 self.chunk.emit_u8(Op::Call, 1, self.line);
1237 self.chunk.emit(Op::Pop, self.line);
1238 self.chunk.emit(Op::Add, self.line);
1239 } else {
1240 self.compile_node(&entry.key)?;
1241 self.compile_node(&entry.value)?;
1242 pending += 1;
1243 }
1244 }
1245 if pending > 0 {
1246 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1247 self.chunk.emit(Op::Add, self.line);
1248 }
1249 }
1250 }
1251
1252 Node::InterpolatedString(segments) => {
1253 let mut part_count = 0u16;
1254 for seg in segments {
1255 match seg {
1256 StringSegment::Literal(s) => {
1257 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1258 self.chunk.emit_u16(Op::Constant, idx, self.line);
1259 part_count += 1;
1260 }
1261 StringSegment::Expression(expr_str, expr_line, expr_col) => {
1262 let mut lexer =
1264 harn_lexer::Lexer::with_position(expr_str, *expr_line, *expr_col);
1265 if let Ok(tokens) = lexer.tokenize() {
1266 let mut parser = harn_parser::Parser::new(tokens);
1267 if let Ok(snode) = parser.parse_single_expression() {
1268 self.compile_node(&snode)?;
1269 let to_str = self
1271 .chunk
1272 .add_constant(Constant::String("to_string".into()));
1273 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1274 self.chunk.emit(Op::Swap, self.line);
1275 self.chunk.emit_u8(Op::Call, 1, self.line);
1276 part_count += 1;
1277 } else {
1278 let idx =
1280 self.chunk.add_constant(Constant::String(expr_str.clone()));
1281 self.chunk.emit_u16(Op::Constant, idx, self.line);
1282 part_count += 1;
1283 }
1284 }
1285 }
1286 }
1287 }
1288 if part_count > 1 {
1289 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1290 }
1291 }
1292
1293 Node::FnDecl {
1294 name, params, body, ..
1295 } => {
1296 let mut fn_compiler = Compiler::new();
1298 fn_compiler.enum_names = self.enum_names.clone();
1299 fn_compiler.emit_default_preamble(params)?;
1300 fn_compiler.emit_type_checks(params);
1301 let is_gen = body_contains_yield(body);
1302 fn_compiler.compile_block(body)?;
1303 if !fn_compiler.finally_bodies.is_empty() {
1305 let finallys: Vec<_> =
1306 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1307 for fb in &finallys {
1308 fn_compiler.compile_finally_inline(fb)?;
1309 }
1310 }
1311 fn_compiler.chunk.emit(Op::Nil, self.line);
1312 fn_compiler.chunk.emit(Op::Return, self.line);
1313
1314 let func = CompiledFunction {
1315 name: name.clone(),
1316 params: TypedParam::names(params),
1317 default_start: TypedParam::default_start(params),
1318 chunk: fn_compiler.chunk,
1319 is_generator: is_gen,
1320 has_rest_param: params.last().is_some_and(|p| p.rest),
1321 };
1322 let fn_idx = self.chunk.functions.len();
1323 self.chunk.functions.push(func);
1324
1325 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1326 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1327 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1328 }
1329
1330 Node::ToolDecl {
1331 name,
1332 description,
1333 params,
1334 return_type,
1335 body,
1336 ..
1337 } => {
1338 let mut fn_compiler = Compiler::new();
1340 fn_compiler.enum_names = self.enum_names.clone();
1341 fn_compiler.emit_default_preamble(params)?;
1342 fn_compiler.emit_type_checks(params);
1343 fn_compiler.compile_block(body)?;
1344 if !fn_compiler.finally_bodies.is_empty() {
1346 let finallys: Vec<_> =
1347 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1348 for fb in &finallys {
1349 fn_compiler.compile_finally_inline(fb)?;
1350 }
1351 }
1352 fn_compiler.chunk.emit(Op::Return, self.line);
1354
1355 let func = CompiledFunction {
1356 name: name.clone(),
1357 params: TypedParam::names(params),
1358 default_start: TypedParam::default_start(params),
1359 chunk: fn_compiler.chunk,
1360 is_generator: false,
1361 has_rest_param: params.last().is_some_and(|p| p.rest),
1362 };
1363 let fn_idx = self.chunk.functions.len();
1364 self.chunk.functions.push(func);
1365
1366 let define_name = self
1368 .chunk
1369 .add_constant(Constant::String("tool_define".into()));
1370 self.chunk.emit_u16(Op::Constant, define_name, self.line);
1371
1372 let reg_name = self
1374 .chunk
1375 .add_constant(Constant::String("tool_registry".into()));
1376 self.chunk.emit_u16(Op::Constant, reg_name, self.line);
1377 self.chunk.emit_u8(Op::Call, 0, self.line);
1378
1379 let tool_name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1381 self.chunk.emit_u16(Op::Constant, tool_name_idx, self.line);
1382
1383 let desc = description.as_deref().unwrap_or("");
1385 let desc_idx = self.chunk.add_constant(Constant::String(desc.to_string()));
1386 self.chunk.emit_u16(Op::Constant, desc_idx, self.line);
1387
1388 let mut param_count: u16 = 0;
1393 for p in params {
1394 let pn_idx = self.chunk.add_constant(Constant::String(p.name.clone()));
1395 self.chunk.emit_u16(Op::Constant, pn_idx, self.line);
1396
1397 let base_schema = p
1398 .type_expr
1399 .as_ref()
1400 .and_then(Self::type_expr_to_schema_value)
1401 .unwrap_or_else(|| {
1402 VmValue::Dict(Rc::new(BTreeMap::from([(
1403 "type".to_string(),
1404 VmValue::String(Rc::from("any")),
1405 )])))
1406 });
1407 let public_schema =
1408 schema::schema_to_json_schema_value(&base_schema).map_err(|error| {
1409 CompileError {
1410 message: format!(
1411 "failed to lower tool parameter schema for '{}': {}",
1412 p.name, error
1413 ),
1414 line: self.line,
1415 }
1416 })?;
1417 let mut param_schema = match public_schema {
1418 VmValue::Dict(map) => (*map).clone(),
1419 _ => BTreeMap::new(),
1420 };
1421
1422 if p.default_value.is_some() {
1423 param_schema.insert("required".to_string(), VmValue::Bool(false));
1424 }
1425
1426 self.emit_vm_value_literal(&VmValue::Dict(Rc::new(param_schema)));
1427
1428 if let Some(default_value) = p.default_value.as_ref() {
1429 let default_key =
1430 self.chunk.add_constant(Constant::String("default".into()));
1431 self.chunk.emit_u16(Op::Constant, default_key, self.line);
1432 self.compile_node(default_value)?;
1433 self.chunk.emit_u16(Op::BuildDict, 1, self.line);
1434 self.chunk.emit(Op::Add, self.line);
1435 }
1436
1437 param_count += 1;
1438 }
1439 self.chunk.emit_u16(Op::BuildDict, param_count, self.line);
1440
1441 let params_key = self
1443 .chunk
1444 .add_constant(Constant::String("parameters".into()));
1445 self.chunk.emit_u16(Op::Constant, params_key, self.line);
1446 self.chunk.emit(Op::Swap, self.line);
1447
1448 let handler_key = self.chunk.add_constant(Constant::String("handler".into()));
1449 self.chunk.emit_u16(Op::Constant, handler_key, self.line);
1450 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1451
1452 let mut config_entries = 2u16;
1453 if let Some(return_type) = return_type
1454 .as_ref()
1455 .and_then(Self::type_expr_to_schema_value)
1456 {
1457 let return_type =
1458 schema::schema_to_json_schema_value(&return_type).map_err(|error| {
1459 CompileError {
1460 message: format!(
1461 "failed to lower tool return schema for '{}': {}",
1462 name, error
1463 ),
1464 line: self.line,
1465 }
1466 })?;
1467 let returns_key = self.chunk.add_constant(Constant::String("returns".into()));
1468 self.chunk.emit_u16(Op::Constant, returns_key, self.line);
1469 self.emit_vm_value_literal(&return_type);
1470 config_entries += 1;
1471 }
1472
1473 self.chunk
1474 .emit_u16(Op::BuildDict, config_entries, self.line);
1475
1476 self.chunk.emit_u8(Op::Call, 4, self.line);
1478
1479 let bind_idx = self.chunk.add_constant(Constant::String(name.clone()));
1481 self.chunk.emit_u16(Op::DefLet, bind_idx, self.line);
1482 }
1483
1484 Node::Closure { params, body, .. } => {
1485 let mut fn_compiler = Compiler::new();
1486 fn_compiler.enum_names = self.enum_names.clone();
1487 fn_compiler.emit_default_preamble(params)?;
1488 fn_compiler.emit_type_checks(params);
1489 let is_gen = body_contains_yield(body);
1490 fn_compiler.compile_block(body)?;
1491 if !fn_compiler.finally_bodies.is_empty() {
1493 let finallys: Vec<_> =
1494 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1495 for fb in &finallys {
1496 fn_compiler.compile_finally_inline(fb)?;
1497 }
1498 }
1499 fn_compiler.chunk.emit(Op::Return, self.line);
1501
1502 let func = CompiledFunction {
1503 name: "<closure>".to_string(),
1504 params: TypedParam::names(params),
1505 default_start: TypedParam::default_start(params),
1506 chunk: fn_compiler.chunk,
1507 is_generator: is_gen,
1508 has_rest_param: false,
1509 };
1510 let fn_idx = self.chunk.functions.len();
1511 self.chunk.functions.push(func);
1512
1513 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1514 }
1515
1516 Node::ThrowStmt { value } => {
1517 if !self.finally_bodies.is_empty() {
1518 self.compile_node(value)?;
1520 self.temp_counter += 1;
1521 let temp_name = format!("__throw_val_{}__", self.temp_counter);
1522 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
1523 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
1524 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
1525 for fb in &finallys {
1526 self.compile_finally_inline(fb)?;
1527 }
1528 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
1529 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
1530 self.chunk.emit(Op::Throw, self.line);
1531 } else {
1532 self.compile_node(value)?;
1533 self.chunk.emit(Op::Throw, self.line);
1534 }
1535 }
1536
1537 Node::MatchExpr { value, arms } => {
1538 self.compile_node(value)?;
1539 let mut end_jumps = Vec::new();
1540 for arm in arms {
1541 match &arm.pattern.node {
1542 Node::Identifier(name) if name == "_" => {
1544 if let Some(ref guard) = arm.guard {
1545 self.compile_node(guard)?;
1546 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1547 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1549 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1551 self.end_scope();
1552 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1553 self.chunk.patch_jump(guard_skip);
1554 self.chunk.emit(Op::Pop, self.line); } else {
1556 self.begin_scope();
1557 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1559 self.end_scope();
1560 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1561 }
1562 }
1563 Node::EnumConstruct {
1565 enum_name,
1566 variant,
1567 args: pat_args,
1568 } => {
1569 self.chunk.emit(Op::Dup, self.line);
1571 let en_idx =
1572 self.chunk.add_constant(Constant::String(enum_name.clone()));
1573 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1574 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1575 let hi = (vn_idx >> 8) as u8;
1576 let lo = vn_idx as u8;
1577 self.chunk.code.push(hi);
1578 self.chunk.code.push(lo);
1579 self.chunk.lines.push(self.line);
1580 self.chunk.columns.push(self.column);
1581 self.chunk.lines.push(self.line);
1582 self.chunk.columns.push(self.column);
1583 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1585 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1587
1588 for (i, pat_arg) in pat_args.iter().enumerate() {
1591 if let Node::Identifier(binding_name) = &pat_arg.node {
1592 self.chunk.emit(Op::Dup, self.line);
1594 let fields_idx = self
1595 .chunk
1596 .add_constant(Constant::String("fields".to_string()));
1597 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1598 let idx_const =
1599 self.chunk.add_constant(Constant::Int(i as i64));
1600 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1601 self.chunk.emit(Op::Subscript, self.line);
1602 let name_idx = self
1603 .chunk
1604 .add_constant(Constant::String(binding_name.clone()));
1605 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1606 }
1607 }
1608
1609 if let Some(ref guard) = arm.guard {
1611 self.compile_node(guard)?;
1612 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1613 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1616 self.end_scope();
1617 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1618 self.chunk.patch_jump(guard_skip);
1619 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1621 } else {
1622 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1624 self.end_scope();
1625 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1626 }
1627 self.chunk.patch_jump(skip);
1628 self.chunk.emit(Op::Pop, self.line); }
1630 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1632 {
1633 let enum_name = if let Node::Identifier(n) = &object.node {
1634 n.clone()
1635 } else {
1636 unreachable!()
1637 };
1638 self.chunk.emit(Op::Dup, self.line);
1639 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1640 let vn_idx =
1641 self.chunk.add_constant(Constant::String(property.clone()));
1642 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1643 let hi = (vn_idx >> 8) as u8;
1644 let lo = vn_idx as u8;
1645 self.chunk.code.push(hi);
1646 self.chunk.code.push(lo);
1647 self.chunk.lines.push(self.line);
1648 self.chunk.columns.push(self.column);
1649 self.chunk.lines.push(self.line);
1650 self.chunk.columns.push(self.column);
1651 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1652 self.chunk.emit(Op::Pop, self.line); if let Some(ref guard) = arm.guard {
1655 self.compile_node(guard)?;
1656 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1657 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1659 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1661 self.end_scope();
1662 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1663 self.chunk.patch_jump(guard_skip);
1664 self.chunk.emit(Op::Pop, self.line); } else {
1666 self.begin_scope();
1667 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1669 self.end_scope();
1670 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1671 }
1672 self.chunk.patch_jump(skip);
1673 self.chunk.emit(Op::Pop, self.line); }
1675 Node::MethodCall {
1678 object,
1679 method,
1680 args: pat_args,
1681 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1682 {
1683 let enum_name = if let Node::Identifier(n) = &object.node {
1684 n.clone()
1685 } else {
1686 unreachable!()
1687 };
1688 self.chunk.emit(Op::Dup, self.line);
1690 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1691 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1692 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1693 let hi = (vn_idx >> 8) as u8;
1694 let lo = vn_idx as u8;
1695 self.chunk.code.push(hi);
1696 self.chunk.code.push(lo);
1697 self.chunk.lines.push(self.line);
1698 self.chunk.columns.push(self.column);
1699 self.chunk.lines.push(self.line);
1700 self.chunk.columns.push(self.column);
1701 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1702 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1704
1705 for (i, pat_arg) in pat_args.iter().enumerate() {
1707 if let Node::Identifier(binding_name) = &pat_arg.node {
1708 self.chunk.emit(Op::Dup, self.line);
1709 let fields_idx = self
1710 .chunk
1711 .add_constant(Constant::String("fields".to_string()));
1712 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1713 let idx_const =
1714 self.chunk.add_constant(Constant::Int(i as i64));
1715 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1716 self.chunk.emit(Op::Subscript, self.line);
1717 let name_idx = self
1718 .chunk
1719 .add_constant(Constant::String(binding_name.clone()));
1720 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1721 }
1722 }
1723
1724 if let Some(ref guard) = arm.guard {
1726 self.compile_node(guard)?;
1727 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1728 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1731 self.end_scope();
1732 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1733 self.chunk.patch_jump(guard_skip);
1734 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1736 } else {
1737 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1739 self.end_scope();
1740 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1741 }
1742 self.chunk.patch_jump(skip);
1743 self.chunk.emit(Op::Pop, self.line); }
1745 Node::Identifier(name) => {
1747 self.begin_scope();
1748 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1751 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1752 if let Some(ref guard) = arm.guard {
1754 self.compile_node(guard)?;
1755 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1756 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1759 self.end_scope();
1760 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1761 self.chunk.patch_jump(guard_skip);
1762 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1764 } else {
1765 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1767 self.end_scope();
1768 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1769 }
1770 }
1771 Node::DictLiteral(entries)
1773 if entries
1774 .iter()
1775 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1776 {
1777 self.chunk.emit(Op::Dup, self.line);
1779 let typeof_idx =
1780 self.chunk.add_constant(Constant::String("type_of".into()));
1781 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1782 self.chunk.emit(Op::Swap, self.line);
1783 self.chunk.emit_u8(Op::Call, 1, self.line);
1784 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1785 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1786 self.chunk.emit(Op::Equal, self.line);
1787 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1788 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1792 let mut bindings = Vec::new();
1793 self.begin_scope();
1794 for entry in entries {
1795 if let Node::StringLiteral(key) = &entry.key.node {
1796 match &entry.value.node {
1797 Node::StringLiteral(_)
1799 | Node::IntLiteral(_)
1800 | Node::FloatLiteral(_)
1801 | Node::BoolLiteral(_)
1802 | Node::NilLiteral => {
1803 self.chunk.emit(Op::Dup, self.line);
1804 let key_idx = self
1805 .chunk
1806 .add_constant(Constant::String(key.clone()));
1807 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1808 self.chunk.emit(Op::Subscript, self.line);
1809 self.compile_node(&entry.value)?;
1810 self.chunk.emit(Op::Equal, self.line);
1811 let skip =
1812 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1813 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1815 }
1816 Node::Identifier(binding) => {
1818 bindings.push((key.clone(), binding.clone()));
1819 }
1820 _ => {
1821 self.chunk.emit(Op::Dup, self.line);
1823 let key_idx = self
1824 .chunk
1825 .add_constant(Constant::String(key.clone()));
1826 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1827 self.chunk.emit(Op::Subscript, self.line);
1828 self.compile_node(&entry.value)?;
1829 self.chunk.emit(Op::Equal, self.line);
1830 let skip =
1831 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1832 self.chunk.emit(Op::Pop, self.line);
1833 constraint_skips.push(skip);
1834 }
1835 }
1836 }
1837 }
1838
1839 for (key, binding) in &bindings {
1841 self.chunk.emit(Op::Dup, self.line);
1842 let key_idx =
1843 self.chunk.add_constant(Constant::String(key.clone()));
1844 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1845 self.chunk.emit(Op::Subscript, self.line);
1846 let name_idx =
1847 self.chunk.add_constant(Constant::String(binding.clone()));
1848 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1849 }
1850
1851 if let Some(ref guard) = arm.guard {
1853 self.compile_node(guard)?;
1854 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1855 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1858 self.end_scope();
1859 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1860 self.chunk.patch_jump(guard_skip);
1861 self.chunk.emit(Op::Pop, self.line); } else {
1865 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1867 self.end_scope();
1868 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1869 }
1870
1871 let type_fail_target = self.chunk.code.len();
1872 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1874 let scoped_fail_target = self.chunk.code.len();
1875 self.chunk.emit(Op::PopScope, self.line);
1876 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1878
1879 for skip in constraint_skips {
1880 self.chunk.patch_jump_to(skip, scoped_fail_target);
1881 }
1882 self.chunk.patch_jump_to(skip_type, type_fail_target);
1883 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1884 }
1885 Node::ListLiteral(elements) => {
1887 self.chunk.emit(Op::Dup, self.line);
1889 let typeof_idx =
1890 self.chunk.add_constant(Constant::String("type_of".into()));
1891 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1892 self.chunk.emit(Op::Swap, self.line);
1893 self.chunk.emit_u8(Op::Call, 1, self.line);
1894 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1895 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1896 self.chunk.emit(Op::Equal, self.line);
1897 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1898 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1902 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1903 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1904 self.chunk.emit(Op::Swap, self.line);
1905 self.chunk.emit_u8(Op::Call, 1, self.line);
1906 let count = self
1907 .chunk
1908 .add_constant(Constant::Int(elements.len() as i64));
1909 self.chunk.emit_u16(Op::Constant, count, self.line);
1910 self.chunk.emit(Op::GreaterEqual, self.line);
1911 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1912 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1916 let mut bindings = Vec::new();
1917 self.begin_scope();
1918 for (i, elem) in elements.iter().enumerate() {
1919 match &elem.node {
1920 Node::Identifier(name) if name != "_" => {
1921 bindings.push((i, name.clone()));
1922 }
1923 Node::Identifier(_) => {} _ => {
1926 self.chunk.emit(Op::Dup, self.line);
1927 let idx_const =
1928 self.chunk.add_constant(Constant::Int(i as i64));
1929 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1930 self.chunk.emit(Op::Subscript, self.line);
1931 self.compile_node(elem)?;
1932 self.chunk.emit(Op::Equal, self.line);
1933 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1934 self.chunk.emit(Op::Pop, self.line);
1935 constraint_skips.push(skip);
1936 }
1937 }
1938 }
1939
1940 for (i, name) in &bindings {
1942 self.chunk.emit(Op::Dup, self.line);
1943 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1944 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1945 self.chunk.emit(Op::Subscript, self.line);
1946 let name_idx =
1947 self.chunk.add_constant(Constant::String(name.clone()));
1948 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1949 }
1950
1951 if let Some(ref guard) = arm.guard {
1953 self.compile_node(guard)?;
1954 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1955 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1958 self.end_scope();
1959 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1960 self.chunk.patch_jump(guard_skip);
1961 self.chunk.emit(Op::Pop, self.line); } else {
1963 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1965 self.end_scope();
1966 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1967 }
1968
1969 let pre_scope_fail_target = self.chunk.code.len();
1970 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1972 let scoped_fail_target = self.chunk.code.len();
1973 self.chunk.emit(Op::PopScope, self.line);
1974 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1976 for skip in constraint_skips {
1977 self.chunk.patch_jump_to(skip, scoped_fail_target);
1978 }
1979 self.chunk.patch_jump_to(skip_len, pre_scope_fail_target);
1980 self.chunk.patch_jump_to(skip_type, pre_scope_fail_target);
1981 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1982 }
1983 _ => {
1985 self.chunk.emit(Op::Dup, self.line);
1986 self.compile_node(&arm.pattern)?;
1987 self.chunk.emit(Op::Equal, self.line);
1988 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1989 self.chunk.emit(Op::Pop, self.line); if let Some(ref guard) = arm.guard {
1992 self.compile_node(guard)?;
1993 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1994 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1996 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1998 self.end_scope();
1999 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2000 self.chunk.patch_jump(guard_skip);
2001 self.chunk.emit(Op::Pop, self.line); } else {
2003 self.begin_scope();
2004 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
2006 self.end_scope();
2007 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2008 }
2009 self.chunk.patch_jump(skip);
2010 self.chunk.emit(Op::Pop, self.line); }
2012 }
2013 }
2014 let msg_idx = self.chunk.add_constant(Constant::String(
2016 "No match arm matched the value".to_string(),
2017 ));
2018 self.chunk.emit(Op::Pop, self.line);
2019 self.chunk.emit_u16(Op::Constant, msg_idx, self.line);
2020 self.chunk.emit(Op::Throw, self.line);
2021 for j in end_jumps {
2022 self.chunk.patch_jump(j);
2023 }
2024 }
2025
2026 Node::RangeExpr {
2027 start,
2028 end,
2029 inclusive,
2030 } => {
2031 let name_idx = self
2033 .chunk
2034 .add_constant(Constant::String("__range__".to_string()));
2035 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2036 self.compile_node(start)?;
2037 self.compile_node(end)?;
2038 if *inclusive {
2039 self.chunk.emit(Op::True, self.line);
2040 } else {
2041 self.chunk.emit(Op::False, self.line);
2042 }
2043 self.chunk.emit_u8(Op::Call, 3, self.line);
2044 }
2045
2046 Node::GuardStmt {
2047 condition,
2048 else_body,
2049 } => {
2050 self.compile_node(condition)?;
2053 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2054 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_block(else_body)?;
2057 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
2059 self.chunk.emit(Op::Pop, self.line);
2060 }
2061 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2062 self.chunk.patch_jump(skip_jump);
2063 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
2065 self.chunk.emit(Op::Nil, self.line);
2066 }
2067
2068 Node::RequireStmt { condition, message } => {
2069 self.compile_node(condition)?;
2070 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2071 self.chunk.emit(Op::Pop, self.line);
2072 if let Some(message) = message {
2073 self.compile_node(message)?;
2074 } else {
2075 let idx = self
2076 .chunk
2077 .add_constant(Constant::String("require condition failed".to_string()));
2078 self.chunk.emit_u16(Op::Constant, idx, self.line);
2079 }
2080 self.chunk.emit(Op::Throw, self.line);
2081 self.chunk.patch_jump(ok_jump);
2082 self.chunk.emit(Op::Pop, self.line);
2083 }
2084
2085 Node::Block(stmts) => {
2086 self.compile_scoped_block(stmts)?;
2087 }
2088
2089 Node::DeadlineBlock { duration, body } => {
2090 self.compile_node(duration)?;
2091 self.chunk.emit(Op::DeadlineSetup, self.line);
2092 self.compile_scoped_block(body)?;
2093 self.chunk.emit(Op::DeadlineEnd, self.line);
2094 }
2095
2096 Node::MutexBlock { body } => {
2097 self.begin_scope();
2099 for sn in body {
2100 self.compile_node(sn)?;
2101 if Self::produces_value(&sn.node) {
2102 self.chunk.emit(Op::Pop, self.line);
2103 }
2104 }
2105 self.chunk.emit(Op::Nil, self.line);
2106 self.end_scope();
2107 }
2108
2109 Node::DeferStmt { body } => {
2110 self.finally_bodies.push(body.clone());
2112 self.chunk.emit(Op::Nil, self.line);
2113 }
2114
2115 Node::YieldExpr { value } => {
2116 if let Some(val) = value {
2117 self.compile_node(val)?;
2118 } else {
2119 self.chunk.emit(Op::Nil, self.line);
2120 }
2121 self.chunk.emit(Op::Yield, self.line);
2122 }
2123
2124 Node::EnumConstruct {
2125 enum_name,
2126 variant,
2127 args,
2128 } => {
2129 for arg in args {
2131 self.compile_node(arg)?;
2132 }
2133 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
2134 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
2135 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
2137 let hi = (var_idx >> 8) as u8;
2138 let lo = var_idx as u8;
2139 self.chunk.code.push(hi);
2140 self.chunk.code.push(lo);
2141 self.chunk.lines.push(self.line);
2142 self.chunk.columns.push(self.column);
2143 self.chunk.lines.push(self.line);
2144 self.chunk.columns.push(self.column);
2145 let fc = args.len() as u16;
2146 let fhi = (fc >> 8) as u8;
2147 let flo = fc as u8;
2148 self.chunk.code.push(fhi);
2149 self.chunk.code.push(flo);
2150 self.chunk.lines.push(self.line);
2151 self.chunk.columns.push(self.column);
2152 self.chunk.lines.push(self.line);
2153 self.chunk.columns.push(self.column);
2154 }
2155
2156 Node::StructConstruct {
2157 struct_name,
2158 fields,
2159 } => {
2160 let struct_key = self
2162 .chunk
2163 .add_constant(Constant::String("__struct__".to_string()));
2164 let struct_val = self
2165 .chunk
2166 .add_constant(Constant::String(struct_name.clone()));
2167 self.chunk.emit_u16(Op::Constant, struct_key, self.line);
2168 self.chunk.emit_u16(Op::Constant, struct_val, self.line);
2169
2170 for entry in fields {
2171 self.compile_node(&entry.key)?;
2172 self.compile_node(&entry.value)?;
2173 }
2174 self.chunk
2175 .emit_u16(Op::BuildDict, (fields.len() + 1) as u16, self.line);
2176 }
2177
2178 Node::ImportDecl { path } => {
2179 let idx = self.chunk.add_constant(Constant::String(path.clone()));
2180 self.chunk.emit_u16(Op::Import, idx, self.line);
2181 }
2182
2183 Node::SelectiveImport { names, path } => {
2184 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
2185 let names_str = names.join(",");
2186 let names_idx = self.chunk.add_constant(Constant::String(names_str));
2187 self.chunk
2188 .emit_u16(Op::SelectiveImport, path_idx, self.line);
2189 let hi = (names_idx >> 8) as u8;
2190 let lo = names_idx as u8;
2191 self.chunk.code.push(hi);
2192 self.chunk.code.push(lo);
2193 self.chunk.lines.push(self.line);
2194 self.chunk.columns.push(self.column);
2195 self.chunk.lines.push(self.line);
2196 self.chunk.columns.push(self.column);
2197 }
2198
2199 Node::TryOperator { operand } => {
2200 self.compile_node(operand)?;
2201 self.chunk.emit(Op::TryUnwrap, self.line);
2202 }
2203
2204 Node::ImplBlock { type_name, methods } => {
2205 for method_sn in methods {
2208 if let Node::FnDecl {
2209 name, params, body, ..
2210 } = &method_sn.node
2211 {
2212 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
2214 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2215
2216 let mut fn_compiler = Compiler::new();
2218 fn_compiler.enum_names = self.enum_names.clone();
2219 fn_compiler.emit_default_preamble(params)?;
2220 fn_compiler.emit_type_checks(params);
2221 fn_compiler.compile_block(body)?;
2222 fn_compiler.chunk.emit(Op::Nil, self.line);
2223 fn_compiler.chunk.emit(Op::Return, self.line);
2224
2225 let func = CompiledFunction {
2226 name: format!("{}.{}", type_name, name),
2227 params: TypedParam::names(params),
2228 default_start: TypedParam::default_start(params),
2229 chunk: fn_compiler.chunk,
2230 is_generator: false,
2231 has_rest_param: false,
2232 };
2233 let fn_idx = self.chunk.functions.len();
2234 self.chunk.functions.push(func);
2235 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2236 }
2237 }
2238 let method_count = methods
2239 .iter()
2240 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
2241 .count();
2242 self.chunk
2243 .emit_u16(Op::BuildDict, method_count as u16, self.line);
2244 let impl_name = format!("__impl_{}", type_name);
2245 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
2246 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2247 }
2248
2249 Node::StructDecl { name, .. } => {
2250 let mut fn_compiler = Compiler::new();
2252 fn_compiler.enum_names = self.enum_names.clone();
2253 let params = vec![TypedParam::untyped("__fields")];
2254 fn_compiler.emit_default_preamble(¶ms)?;
2255
2256 let make_idx = fn_compiler
2258 .chunk
2259 .add_constant(Constant::String("__make_struct".into()));
2260 fn_compiler
2261 .chunk
2262 .emit_u16(Op::Constant, make_idx, self.line);
2263 let sname_idx = fn_compiler
2264 .chunk
2265 .add_constant(Constant::String(name.clone()));
2266 fn_compiler
2267 .chunk
2268 .emit_u16(Op::Constant, sname_idx, self.line);
2269 let fields_idx = fn_compiler
2270 .chunk
2271 .add_constant(Constant::String("__fields".into()));
2272 fn_compiler
2273 .chunk
2274 .emit_u16(Op::GetVar, fields_idx, self.line);
2275 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
2276 fn_compiler.chunk.emit(Op::Return, self.line);
2277
2278 let func = CompiledFunction {
2279 name: name.clone(),
2280 params: TypedParam::names(¶ms),
2281 default_start: None,
2282 chunk: fn_compiler.chunk,
2283 is_generator: false,
2284 has_rest_param: false,
2285 };
2286 let fn_idx = self.chunk.functions.len();
2287 self.chunk.functions.push(func);
2288 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2289 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
2290 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2291 }
2292
2293 Node::Pipeline { .. }
2295 | Node::OverrideDecl { .. }
2296 | Node::TypeDecl { .. }
2297 | Node::EnumDecl { .. }
2298 | Node::InterfaceDecl { .. } => {
2299 self.chunk.emit(Op::Nil, self.line);
2300 }
2301
2302 Node::TryCatch {
2303 body,
2304 error_var,
2305 error_type,
2306 catch_body,
2307 finally_body,
2308 } => {
2309 let type_name = error_type.as_ref().and_then(|te| {
2311 if let harn_parser::TypeExpr::Named(name) = te {
2312 Some(name.clone())
2313 } else {
2314 None
2315 }
2316 });
2317
2318 let type_name_idx = if let Some(ref tn) = type_name {
2319 self.chunk.add_constant(Constant::String(tn.clone()))
2320 } else {
2321 self.chunk.add_constant(Constant::String(String::new()))
2322 };
2323
2324 let has_catch = !catch_body.is_empty() || error_var.is_some();
2325 let has_finally = finally_body.is_some();
2326
2327 if has_catch && has_finally {
2328 let finally_body = finally_body.as_ref().unwrap();
2330
2331 self.finally_bodies.push(finally_body.clone());
2333
2334 self.handler_depth += 1;
2336 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2337 self.emit_type_name_extra(type_name_idx);
2338
2339 self.compile_try_body(body)?;
2341
2342 self.handler_depth -= 1;
2344 self.chunk.emit(Op::PopHandler, self.line);
2345 self.compile_finally_inline(finally_body)?;
2346 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2347
2348 self.chunk.patch_jump(catch_jump);
2350 self.begin_scope();
2351 self.compile_catch_binding(error_var)?;
2352
2353 self.handler_depth += 1;
2355 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2356 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2357 self.emit_type_name_extra(empty_type);
2358
2359 self.compile_try_body(catch_body)?;
2361
2362 self.handler_depth -= 1;
2364 self.chunk.emit(Op::PopHandler, self.line);
2365 self.compile_finally_inline(finally_body)?;
2366 self.end_scope();
2367 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
2368
2369 self.chunk.patch_jump(rethrow_jump);
2371 self.compile_rethrow_with_finally(finally_body)?;
2372 self.end_scope();
2373
2374 self.chunk.patch_jump(end_jump);
2375 self.chunk.patch_jump(end_jump2);
2376
2377 self.finally_bodies.pop();
2378 } else if has_finally {
2379 let finally_body = finally_body.as_ref().unwrap();
2381
2382 self.finally_bodies.push(finally_body.clone());
2383
2384 self.handler_depth += 1;
2386 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2387 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2388 self.emit_type_name_extra(empty_type);
2389
2390 self.compile_try_body(body)?;
2392
2393 self.handler_depth -= 1;
2395 self.chunk.emit(Op::PopHandler, self.line);
2396 self.compile_finally_inline(finally_body)?;
2397 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2398
2399 self.chunk.patch_jump(error_jump);
2401 self.compile_rethrow_with_finally(finally_body)?;
2402
2403 self.chunk.patch_jump(end_jump);
2404
2405 self.finally_bodies.pop();
2406 } else {
2407 self.handler_depth += 1;
2411 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2412 self.emit_type_name_extra(type_name_idx);
2413
2414 self.compile_try_body(body)?;
2416
2417 self.handler_depth -= 1;
2419 self.chunk.emit(Op::PopHandler, self.line);
2420 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2421
2422 self.chunk.patch_jump(catch_jump);
2424 self.begin_scope();
2425 self.compile_catch_binding(error_var)?;
2426
2427 self.compile_try_body(catch_body)?;
2429 self.end_scope();
2430
2431 self.chunk.patch_jump(end_jump);
2433 }
2434 }
2435
2436 Node::TryExpr { body } => {
2437 self.handler_depth += 1;
2441 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2442 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2443 self.emit_type_name_extra(empty_type);
2444
2445 self.compile_try_body(body)?;
2447
2448 self.handler_depth -= 1;
2450 self.chunk.emit(Op::PopHandler, self.line);
2451
2452 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
2454 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
2455 self.chunk.emit(Op::Swap, self.line);
2456 self.chunk.emit_u8(Op::Call, 1, self.line);
2457
2458 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2460
2461 self.chunk.patch_jump(catch_jump);
2463
2464 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
2466 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
2467 self.chunk.emit(Op::Swap, self.line);
2468 self.chunk.emit_u8(Op::Call, 1, self.line);
2469
2470 self.chunk.patch_jump(end_jump);
2472 }
2473
2474 Node::Retry { count, body } => {
2475 self.compile_node(count)?;
2477 let counter_name = "__retry_counter__";
2478 let counter_idx = self
2479 .chunk
2480 .add_constant(Constant::String(counter_name.to_string()));
2481 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
2482
2483 self.chunk.emit(Op::Nil, self.line);
2485 let err_name = "__retry_last_error__";
2486 let err_idx = self
2487 .chunk
2488 .add_constant(Constant::String(err_name.to_string()));
2489 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
2490
2491 let loop_start = self.chunk.current_offset();
2493
2494 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2496 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2498 let hi = (empty_type >> 8) as u8;
2499 let lo = empty_type as u8;
2500 self.chunk.code.push(hi);
2501 self.chunk.code.push(lo);
2502 self.chunk.lines.push(self.line);
2503 self.chunk.columns.push(self.column);
2504 self.chunk.lines.push(self.line);
2505 self.chunk.columns.push(self.column);
2506
2507 self.compile_block(body)?;
2509
2510 self.chunk.emit(Op::PopHandler, self.line);
2512 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2513
2514 self.chunk.patch_jump(catch_jump);
2516 self.chunk.emit(Op::Dup, self.line);
2518 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
2519 self.chunk.emit(Op::Pop, self.line);
2521
2522 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
2524 let one_idx = self.chunk.add_constant(Constant::Int(1));
2525 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
2526 self.chunk.emit(Op::Sub, self.line);
2527 self.chunk.emit(Op::Dup, self.line);
2528 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
2529
2530 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2532 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2533 self.chunk.emit(Op::Greater, self.line);
2534 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2535 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
2537
2538 self.chunk.patch_jump(retry_jump);
2540 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
2542 self.chunk.emit(Op::Throw, self.line);
2543
2544 self.chunk.patch_jump(end_jump);
2545 self.chunk.emit(Op::Nil, self.line);
2547 }
2548
2549 Node::Parallel {
2550 mode,
2551 expr,
2552 variable,
2553 body,
2554 } => {
2555 self.compile_node(expr)?;
2556 let mut fn_compiler = Compiler::new();
2557 fn_compiler.enum_names = self.enum_names.clone();
2558 fn_compiler.compile_block(body)?;
2559 fn_compiler.chunk.emit(Op::Return, self.line);
2560 let (fn_name, params) = match mode {
2561 ParallelMode::Count => (
2562 "<parallel>",
2563 vec![variable.clone().unwrap_or_else(|| "__i__".to_string())],
2564 ),
2565 ParallelMode::Each => (
2566 "<parallel_each>",
2567 vec![variable.clone().unwrap_or_else(|| "__item__".to_string())],
2568 ),
2569 ParallelMode::Settle => (
2570 "<parallel_settle>",
2571 vec![variable.clone().unwrap_or_else(|| "__item__".to_string())],
2572 ),
2573 };
2574 let func = CompiledFunction {
2575 name: fn_name.to_string(),
2576 params,
2577 default_start: None,
2578 chunk: fn_compiler.chunk,
2579 is_generator: false,
2580 has_rest_param: false,
2581 };
2582 let fn_idx = self.chunk.functions.len();
2583 self.chunk.functions.push(func);
2584 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2585 let op = match mode {
2586 ParallelMode::Count => Op::Parallel,
2587 ParallelMode::Each => Op::ParallelMap,
2588 ParallelMode::Settle => Op::ParallelSettle,
2589 };
2590 self.chunk.emit(op, self.line);
2591 }
2592
2593 Node::SpawnExpr { body } => {
2594 let mut fn_compiler = Compiler::new();
2595 fn_compiler.enum_names = self.enum_names.clone();
2596 fn_compiler.compile_block(body)?;
2597 fn_compiler.chunk.emit(Op::Return, self.line);
2598 let func = CompiledFunction {
2599 name: "<spawn>".to_string(),
2600 params: vec![],
2601 default_start: None,
2602 chunk: fn_compiler.chunk,
2603 is_generator: false,
2604 has_rest_param: false,
2605 };
2606 let fn_idx = self.chunk.functions.len();
2607 self.chunk.functions.push(func);
2608 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2609 self.chunk.emit(Op::Spawn, self.line);
2610 }
2611 Node::SelectExpr {
2612 cases,
2613 timeout,
2614 default_body,
2615 } => {
2616 let builtin_name = if timeout.is_some() {
2623 "__select_timeout"
2624 } else if default_body.is_some() {
2625 "__select_try"
2626 } else {
2627 "__select_list"
2628 };
2629
2630 let name_idx = self
2632 .chunk
2633 .add_constant(Constant::String(builtin_name.into()));
2634 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2635
2636 for case in cases {
2638 self.compile_node(&case.channel)?;
2639 }
2640 self.chunk
2641 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2642
2643 if let Some((duration_expr, _)) = timeout {
2645 self.compile_node(duration_expr)?;
2646 self.chunk.emit_u8(Op::Call, 2, self.line);
2647 } else {
2648 self.chunk.emit_u8(Op::Call, 1, self.line);
2649 }
2650
2651 self.temp_counter += 1;
2653 let result_name = format!("__sel_result_{}__", self.temp_counter);
2654 let result_idx = self
2655 .chunk
2656 .add_constant(Constant::String(result_name.clone()));
2657 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2658
2659 let mut end_jumps = Vec::new();
2661
2662 for (i, case) in cases.iter().enumerate() {
2663 let get_r = self
2664 .chunk
2665 .add_constant(Constant::String(result_name.clone()));
2666 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2667 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2668 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2669 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2670 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2671 self.chunk.emit(Op::Equal, self.line);
2672 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2673 self.chunk.emit(Op::Pop, self.line);
2674 self.begin_scope();
2675
2676 let get_r2 = self
2678 .chunk
2679 .add_constant(Constant::String(result_name.clone()));
2680 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2681 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2682 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2683 let var_idx = self
2684 .chunk
2685 .add_constant(Constant::String(case.variable.clone()));
2686 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2687
2688 self.compile_try_body(&case.body)?;
2689 self.end_scope();
2690 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2691 self.chunk.patch_jump(skip);
2692 self.chunk.emit(Op::Pop, self.line);
2693 }
2694
2695 if let Some((_, ref timeout_body)) = timeout {
2697 self.compile_try_body(timeout_body)?;
2698 } else if let Some(ref def_body) = default_body {
2699 self.compile_try_body(def_body)?;
2700 } else {
2701 self.chunk.emit(Op::Nil, self.line);
2702 }
2703
2704 for ej in end_jumps {
2705 self.chunk.patch_jump(ej);
2706 }
2707 }
2708 Node::Spread(_) => {
2709 return Err(CompileError {
2710 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2711 line: self.line,
2712 });
2713 }
2714 }
2715 Ok(())
2716 }
2717
2718 fn compile_destructuring(
2722 &mut self,
2723 pattern: &BindingPattern,
2724 is_mutable: bool,
2725 ) -> Result<(), CompileError> {
2726 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2727 match pattern {
2728 BindingPattern::Identifier(name) => {
2729 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2731 self.chunk.emit_u16(def_op, idx, self.line);
2732 }
2733 BindingPattern::Dict(fields) => {
2734 self.chunk.emit(Op::Dup, self.line);
2737 let assert_idx = self
2738 .chunk
2739 .add_constant(Constant::String("__assert_dict".into()));
2740 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2741 self.chunk.emit(Op::Swap, self.line);
2742 self.chunk.emit_u8(Op::Call, 1, self.line);
2743 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2748 let rest_field = fields.iter().find(|f| f.is_rest);
2749
2750 for field in &non_rest {
2751 self.chunk.emit(Op::Dup, self.line);
2752 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2753 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2754 self.chunk.emit(Op::Subscript, self.line);
2755 if let Some(default_expr) = &field.default_value {
2757 self.chunk.emit(Op::Dup, self.line);
2758 self.chunk.emit(Op::Nil, self.line);
2759 self.chunk.emit(Op::NotEqual, self.line);
2760 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2761 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2764 let end = self.chunk.emit_jump(Op::Jump, self.line);
2765 self.chunk.patch_jump(skip_default);
2766 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2768 }
2769 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2770 let name_idx = self
2771 .chunk
2772 .add_constant(Constant::String(binding_name.to_string()));
2773 self.chunk.emit_u16(def_op, name_idx, self.line);
2774 }
2775
2776 if let Some(rest) = rest_field {
2777 let fn_idx = self
2780 .chunk
2781 .add_constant(Constant::String("__dict_rest".into()));
2782 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2783 self.chunk.emit(Op::Swap, self.line);
2785 for field in &non_rest {
2787 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2788 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2789 }
2790 self.chunk
2791 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2792 self.chunk.emit_u8(Op::Call, 2, self.line);
2794 let rest_name = &rest.key;
2795 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2796 self.chunk.emit_u16(def_op, rest_idx, self.line);
2797 } else {
2798 self.chunk.emit(Op::Pop, self.line);
2800 }
2801 }
2802 BindingPattern::List(elements) => {
2803 self.chunk.emit(Op::Dup, self.line);
2806 let assert_idx = self
2807 .chunk
2808 .add_constant(Constant::String("__assert_list".into()));
2809 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2810 self.chunk.emit(Op::Swap, self.line);
2811 self.chunk.emit_u8(Op::Call, 1, self.line);
2812 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2815 let rest_elem = elements.iter().find(|e| e.is_rest);
2816
2817 for (i, elem) in non_rest.iter().enumerate() {
2818 self.chunk.emit(Op::Dup, self.line);
2819 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2820 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2821 self.chunk.emit(Op::Subscript, self.line);
2822 if let Some(default_expr) = &elem.default_value {
2824 self.chunk.emit(Op::Dup, self.line);
2825 self.chunk.emit(Op::Nil, self.line);
2826 self.chunk.emit(Op::NotEqual, self.line);
2827 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2828 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2831 let end = self.chunk.emit_jump(Op::Jump, self.line);
2832 self.chunk.patch_jump(skip_default);
2833 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2835 }
2836 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2837 self.chunk.emit_u16(def_op, name_idx, self.line);
2838 }
2839
2840 if let Some(rest) = rest_elem {
2841 let start_idx = self
2845 .chunk
2846 .add_constant(Constant::Int(non_rest.len() as i64));
2847 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2848 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2850 let rest_name_idx =
2851 self.chunk.add_constant(Constant::String(rest.name.clone()));
2852 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2853 } else {
2854 self.chunk.emit(Op::Pop, self.line);
2856 }
2857 }
2858 }
2859 Ok(())
2860 }
2861
2862 fn produces_value(node: &Node) -> bool {
2864 match node {
2865 Node::LetBinding { .. }
2867 | Node::VarBinding { .. }
2868 | Node::Assignment { .. }
2869 | Node::ReturnStmt { .. }
2870 | Node::FnDecl { .. }
2871 | Node::ToolDecl { .. }
2872 | Node::ImplBlock { .. }
2873 | Node::StructDecl { .. }
2874 | Node::EnumDecl { .. }
2875 | Node::InterfaceDecl { .. }
2876 | Node::TypeDecl { .. }
2877 | Node::ThrowStmt { .. }
2878 | Node::BreakStmt
2879 | Node::ContinueStmt
2880 | Node::RequireStmt { .. }
2881 | Node::DeferStmt { .. } => false,
2882 Node::TryCatch { .. }
2884 | Node::TryExpr { .. }
2885 | Node::Retry { .. }
2886 | Node::GuardStmt { .. }
2887 | Node::DeadlineBlock { .. }
2888 | Node::MutexBlock { .. }
2889 | Node::Spread(_) => true,
2890 _ => true,
2892 }
2893 }
2894}
2895
2896impl Compiler {
2897 pub fn compile_fn_body(
2910 &mut self,
2911 params: &[TypedParam],
2912 body: &[SNode],
2913 source_file: Option<String>,
2914 ) -> Result<CompiledFunction, CompileError> {
2915 let mut fn_compiler = Compiler::new();
2916 fn_compiler.enum_names = self.enum_names.clone();
2917 fn_compiler.emit_default_preamble(params)?;
2918 fn_compiler.emit_type_checks(params);
2919 let is_gen = body_contains_yield(body);
2920 fn_compiler.compile_block(body)?;
2921 fn_compiler.chunk.emit(Op::Nil, 0);
2922 fn_compiler.chunk.emit(Op::Return, 0);
2923 fn_compiler.chunk.source_file = source_file;
2924 Ok(CompiledFunction {
2925 name: String::new(),
2926 params: TypedParam::names(params),
2927 default_start: TypedParam::default_start(params),
2928 chunk: fn_compiler.chunk,
2929 is_generator: is_gen,
2930 has_rest_param: false,
2931 })
2932 }
2933
2934 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2936 self.begin_scope();
2937 if body.is_empty() {
2938 self.chunk.emit(Op::Nil, self.line);
2939 } else {
2940 self.compile_block(body)?;
2941 if !Self::produces_value(&body.last().unwrap().node) {
2942 self.chunk.emit(Op::Nil, self.line);
2943 }
2944 }
2945 self.end_scope();
2946 Ok(())
2947 }
2948
2949 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2951 match op {
2952 "+" => self.chunk.emit(Op::Add, self.line),
2953 "-" => self.chunk.emit(Op::Sub, self.line),
2954 "*" => self.chunk.emit(Op::Mul, self.line),
2955 "/" => self.chunk.emit(Op::Div, self.line),
2956 "%" => self.chunk.emit(Op::Mod, self.line),
2957 _ => {
2958 return Err(CompileError {
2959 message: format!("Unknown compound operator: {op}"),
2960 line: self.line,
2961 })
2962 }
2963 }
2964 Ok(())
2965 }
2966
2967 fn root_var_name(&self, node: &SNode) -> Option<String> {
2969 match &node.node {
2970 Node::Identifier(name) => Some(name.clone()),
2971 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2972 self.root_var_name(object)
2973 }
2974 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2975 _ => None,
2976 }
2977 }
2978
2979 fn compile_top_level_declarations(&mut self, program: &[SNode]) -> Result<(), CompileError> {
2980 for sn in program {
2981 if matches!(
2982 &sn.node,
2983 Node::FnDecl { .. }
2984 | Node::ToolDecl { .. }
2985 | Node::ImplBlock { .. }
2986 | Node::StructDecl { .. }
2987 | Node::EnumDecl { .. }
2988 | Node::InterfaceDecl { .. }
2989 | Node::TypeDecl { .. }
2990 ) {
2991 self.compile_node(sn)?;
2992 }
2993 }
2994 Ok(())
2995 }
2996}
2997
2998impl Compiler {
2999 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
3001 for sn in nodes {
3002 match &sn.node {
3003 Node::EnumDecl { name, .. } => {
3004 names.insert(name.clone());
3005 }
3006 Node::Pipeline { body, .. } => {
3007 Self::collect_enum_names(body, names);
3008 }
3009 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
3010 Self::collect_enum_names(body, names);
3011 }
3012 Node::Block(stmts) => {
3013 Self::collect_enum_names(stmts, names);
3014 }
3015 _ => {}
3016 }
3017 }
3018 }
3019
3020 fn collect_interface_methods(
3021 nodes: &[SNode],
3022 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
3023 ) {
3024 for sn in nodes {
3025 match &sn.node {
3026 Node::InterfaceDecl { name, methods, .. } => {
3027 let method_names: Vec<String> =
3028 methods.iter().map(|m| m.name.clone()).collect();
3029 interfaces.insert(name.clone(), method_names);
3030 }
3031 Node::Pipeline { body, .. }
3032 | Node::FnDecl { body, .. }
3033 | Node::ToolDecl { body, .. } => {
3034 Self::collect_interface_methods(body, interfaces);
3035 }
3036 Node::Block(stmts) => {
3037 Self::collect_interface_methods(stmts, interfaces);
3038 }
3039 _ => {}
3040 }
3041 }
3042 }
3043}
3044
3045impl Default for Compiler {
3046 fn default() -> Self {
3047 Self::new()
3048 }
3049}
3050
3051fn body_contains_yield(nodes: &[SNode]) -> bool {
3053 nodes.iter().any(|sn| node_contains_yield(&sn.node))
3054}
3055
3056fn node_contains_yield(node: &Node) -> bool {
3057 match node {
3058 Node::YieldExpr { .. } => true,
3059 Node::FnDecl { .. } | Node::Closure { .. } => false,
3062 Node::Block(stmts) => body_contains_yield(stmts),
3063 Node::IfElse {
3064 condition,
3065 then_body,
3066 else_body,
3067 } => {
3068 node_contains_yield(&condition.node)
3069 || body_contains_yield(then_body)
3070 || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
3071 }
3072 Node::WhileLoop { condition, body } => {
3073 node_contains_yield(&condition.node) || body_contains_yield(body)
3074 }
3075 Node::ForIn { iterable, body, .. } => {
3076 node_contains_yield(&iterable.node) || body_contains_yield(body)
3077 }
3078 Node::TryCatch {
3079 body, catch_body, ..
3080 } => body_contains_yield(body) || body_contains_yield(catch_body),
3081 Node::TryExpr { body } => body_contains_yield(body),
3082 _ => false,
3083 }
3084}
3085
3086fn contains_pipe_placeholder(node: &SNode) -> bool {
3088 match &node.node {
3089 Node::Identifier(name) if name == "_" => true,
3090 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
3091 Node::MethodCall { object, args, .. } => {
3092 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
3093 }
3094 Node::BinaryOp { left, right, .. } => {
3095 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
3096 }
3097 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
3098 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
3099 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
3100 Node::SubscriptAccess { object, index } => {
3101 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
3102 }
3103 _ => false,
3104 }
3105}
3106
3107fn replace_pipe_placeholder(node: &SNode) -> SNode {
3109 let new_node = match &node.node {
3110 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
3111 Node::FunctionCall { name, args } => Node::FunctionCall {
3112 name: name.clone(),
3113 args: args.iter().map(replace_pipe_placeholder).collect(),
3114 },
3115 Node::MethodCall {
3116 object,
3117 method,
3118 args,
3119 } => Node::MethodCall {
3120 object: Box::new(replace_pipe_placeholder(object)),
3121 method: method.clone(),
3122 args: args.iter().map(replace_pipe_placeholder).collect(),
3123 },
3124 Node::BinaryOp { op, left, right } => Node::BinaryOp {
3125 op: op.clone(),
3126 left: Box::new(replace_pipe_placeholder(left)),
3127 right: Box::new(replace_pipe_placeholder(right)),
3128 },
3129 Node::UnaryOp { op, operand } => Node::UnaryOp {
3130 op: op.clone(),
3131 operand: Box::new(replace_pipe_placeholder(operand)),
3132 },
3133 Node::ListLiteral(items) => {
3134 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
3135 }
3136 Node::PropertyAccess { object, property } => Node::PropertyAccess {
3137 object: Box::new(replace_pipe_placeholder(object)),
3138 property: property.clone(),
3139 },
3140 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
3141 object: Box::new(replace_pipe_placeholder(object)),
3142 index: Box::new(replace_pipe_placeholder(index)),
3143 },
3144 _ => return node.clone(),
3145 };
3146 SNode::new(new_node, node.span)
3147}
3148
3149#[cfg(test)]
3150mod tests {
3151 use super::*;
3152 use harn_lexer::Lexer;
3153 use harn_parser::Parser;
3154
3155 fn compile_source(source: &str) -> Chunk {
3156 let mut lexer = Lexer::new(source);
3157 let tokens = lexer.tokenize().unwrap();
3158 let mut parser = Parser::new(tokens);
3159 let program = parser.parse().unwrap();
3160 Compiler::new().compile(&program).unwrap()
3161 }
3162
3163 #[test]
3164 fn test_compile_arithmetic() {
3165 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
3166 assert!(!chunk.code.is_empty());
3167 assert!(chunk.constants.contains(&Constant::Int(2)));
3169 assert!(chunk.constants.contains(&Constant::Int(3)));
3170 }
3171
3172 #[test]
3173 fn test_compile_function_call() {
3174 let chunk = compile_source("pipeline test(task) { log(42) }");
3175 let disasm = chunk.disassemble("test");
3176 assert!(disasm.contains("CALL"));
3177 }
3178
3179 #[test]
3180 fn test_compile_if_else() {
3181 let chunk =
3182 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
3183 let disasm = chunk.disassemble("test");
3184 assert!(disasm.contains("JUMP_IF_FALSE"));
3185 assert!(disasm.contains("JUMP"));
3186 }
3187
3188 #[test]
3189 fn test_compile_while() {
3190 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
3191 let disasm = chunk.disassemble("test");
3192 assert!(disasm.contains("JUMP_IF_FALSE"));
3193 assert!(disasm.contains("JUMP"));
3195 }
3196
3197 #[test]
3198 fn test_compile_closure() {
3199 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
3200 assert!(!chunk.functions.is_empty());
3201 assert_eq!(chunk.functions[0].params, vec!["x"]);
3202 }
3203
3204 #[test]
3205 fn test_compile_list() {
3206 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
3207 let disasm = chunk.disassemble("test");
3208 assert!(disasm.contains("BUILD_LIST"));
3209 }
3210
3211 #[test]
3212 fn test_compile_dict() {
3213 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
3214 let disasm = chunk.disassemble("test");
3215 assert!(disasm.contains("BUILD_DICT"));
3216 }
3217
3218 #[test]
3219 fn test_disassemble() {
3220 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
3221 let disasm = chunk.disassemble("test");
3222 assert!(disasm.contains("CONSTANT"));
3224 assert!(disasm.contains("ADD"));
3225 assert!(disasm.contains("CALL"));
3226 }
3227}