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::Applied { .. } => None,
371 harn_parser::TypeExpr::Iter(_) => None,
372 harn_parser::TypeExpr::Never => None,
373 }
374 }
375
376 fn emit_vm_value_literal(&mut self, value: &VmValue) {
377 match value {
378 VmValue::String(text) => {
379 let idx = self.chunk.add_constant(Constant::String(text.to_string()));
380 self.chunk.emit_u16(Op::Constant, idx, self.line);
381 }
382 VmValue::Int(number) => {
383 let idx = self.chunk.add_constant(Constant::Int(*number));
384 self.chunk.emit_u16(Op::Constant, idx, self.line);
385 }
386 VmValue::Float(number) => {
387 let idx = self.chunk.add_constant(Constant::Float(*number));
388 self.chunk.emit_u16(Op::Constant, idx, self.line);
389 }
390 VmValue::Bool(value) => {
391 let idx = self.chunk.add_constant(Constant::Bool(*value));
392 self.chunk.emit_u16(Op::Constant, idx, self.line);
393 }
394 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
395 VmValue::List(items) => {
396 for item in items.iter() {
397 self.emit_vm_value_literal(item);
398 }
399 self.chunk
400 .emit_u16(Op::BuildList, items.len() as u16, self.line);
401 }
402 VmValue::Dict(entries) => {
403 for (key, item) in entries.iter() {
404 let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
405 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
406 self.emit_vm_value_literal(item);
407 }
408 self.chunk
409 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
410 }
411 _ => {}
412 }
413 }
414
415 fn emit_type_name_extra(&mut self, type_name_idx: u16) {
417 let hi = (type_name_idx >> 8) as u8;
418 let lo = type_name_idx as u8;
419 self.chunk.code.push(hi);
420 self.chunk.code.push(lo);
421 self.chunk.lines.push(self.line);
422 self.chunk.columns.push(self.column);
423 self.chunk.lines.push(self.line);
424 self.chunk.columns.push(self.column);
425 }
426
427 fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
429 if body.is_empty() {
430 self.chunk.emit(Op::Nil, self.line);
431 } else {
432 self.compile_scoped_block(body)?;
433 }
434 Ok(())
435 }
436
437 fn compile_catch_binding(&mut self, error_var: &Option<String>) -> Result<(), CompileError> {
439 if let Some(var_name) = error_var {
440 let idx = self.chunk.add_constant(Constant::String(var_name.clone()));
441 self.chunk.emit_u16(Op::DefLet, idx, self.line);
442 } else {
443 self.chunk.emit(Op::Pop, self.line);
444 }
445 Ok(())
446 }
447
448 fn compile_finally_inline(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
450 if !finally_body.is_empty() {
451 self.compile_scoped_block(finally_body)?;
452 if Self::produces_value(&finally_body.last().unwrap().node) {
454 self.chunk.emit(Op::Pop, self.line);
455 }
456 }
457 Ok(())
458 }
459
460 fn compile_rethrow_with_finally(&mut self, finally_body: &[SNode]) -> Result<(), CompileError> {
462 self.temp_counter += 1;
464 let temp_name = format!("__finally_err_{}__", self.temp_counter);
465 let err_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
466 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
467 self.compile_finally_inline(finally_body)?;
468 let get_idx = self.chunk.add_constant(Constant::String(temp_name));
469 self.chunk.emit_u16(Op::GetVar, get_idx, self.line);
470 self.chunk.emit(Op::Throw, self.line);
471 Ok(())
472 }
473
474 fn begin_scope(&mut self) {
475 self.chunk.emit(Op::PushScope, self.line);
476 self.scope_depth += 1;
477 }
478
479 fn end_scope(&mut self) {
480 if self.scope_depth > 0 {
481 self.chunk.emit(Op::PopScope, self.line);
482 self.scope_depth -= 1;
483 }
484 }
485
486 fn unwind_scopes_to(&mut self, target_depth: usize) {
487 while self.scope_depth > target_depth {
488 self.chunk.emit(Op::PopScope, self.line);
489 self.scope_depth -= 1;
490 }
491 }
492
493 fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
494 self.begin_scope();
495 if stmts.is_empty() {
496 self.chunk.emit(Op::Nil, self.line);
497 } else {
498 self.compile_block(stmts)?;
499 }
500 self.end_scope();
501 Ok(())
502 }
503
504 fn compile_scoped_statements(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
505 self.begin_scope();
506 for sn in stmts {
507 self.compile_node(sn)?;
508 if Self::produces_value(&sn.node) {
509 self.chunk.emit(Op::Pop, self.line);
510 }
511 }
512 self.end_scope();
513 Ok(())
514 }
515
516 fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
517 for (i, snode) in stmts.iter().enumerate() {
518 self.compile_node(snode)?;
519 let is_last = i == stmts.len() - 1;
520 if is_last {
521 if !Self::produces_value(&snode.node) {
524 self.chunk.emit(Op::Nil, self.line);
525 }
526 } else {
527 if Self::produces_value(&snode.node) {
529 self.chunk.emit(Op::Pop, self.line);
530 }
531 }
532 }
533 Ok(())
534 }
535
536 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
537 self.line = snode.span.line as u32;
538 self.column = snode.span.column as u32;
539 self.chunk.set_column(self.column);
540 match &snode.node {
541 Node::IntLiteral(n) => {
542 let idx = self.chunk.add_constant(Constant::Int(*n));
543 self.chunk.emit_u16(Op::Constant, idx, self.line);
544 }
545 Node::FloatLiteral(n) => {
546 let idx = self.chunk.add_constant(Constant::Float(*n));
547 self.chunk.emit_u16(Op::Constant, idx, self.line);
548 }
549 Node::StringLiteral(s) | Node::RawStringLiteral(s) => {
550 let idx = self.chunk.add_constant(Constant::String(s.clone()));
551 self.chunk.emit_u16(Op::Constant, idx, self.line);
552 }
553 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
554 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
555 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
556 Node::DurationLiteral(ms) => {
557 let idx = self.chunk.add_constant(Constant::Duration(*ms));
558 self.chunk.emit_u16(Op::Constant, idx, self.line);
559 }
560
561 Node::Identifier(name) => {
562 let idx = self.chunk.add_constant(Constant::String(name.clone()));
563 self.chunk.emit_u16(Op::GetVar, idx, self.line);
564 }
565
566 Node::LetBinding { pattern, value, .. } => {
567 self.compile_node(value)?;
568 self.compile_destructuring(pattern, false)?;
569 }
570
571 Node::VarBinding { pattern, value, .. } => {
572 self.compile_node(value)?;
573 self.compile_destructuring(pattern, true)?;
574 }
575
576 Node::Assignment {
577 target, value, op, ..
578 } => {
579 if let Node::Identifier(name) = &target.node {
580 let idx = self.chunk.add_constant(Constant::String(name.clone()));
581 if let Some(op) = op {
582 self.chunk.emit_u16(Op::GetVar, idx, self.line);
583 self.compile_node(value)?;
584 self.emit_compound_op(op)?;
585 self.chunk.emit_u16(Op::SetVar, idx, self.line);
586 } else {
587 self.compile_node(value)?;
588 self.chunk.emit_u16(Op::SetVar, idx, self.line);
589 }
590 } else if let Node::PropertyAccess { object, property } = &target.node {
591 if let Some(var_name) = self.root_var_name(object) {
593 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
594 let prop_idx = self.chunk.add_constant(Constant::String(property.clone()));
595 if let Some(op) = op {
596 self.compile_node(target)?; self.compile_node(value)?;
599 self.emit_compound_op(op)?;
600 } else {
601 self.compile_node(value)?;
602 }
603 self.chunk.emit_u16(Op::SetProperty, prop_idx, self.line);
606 let hi = (var_idx >> 8) as u8;
608 let lo = var_idx as u8;
609 self.chunk.code.push(hi);
610 self.chunk.code.push(lo);
611 self.chunk.lines.push(self.line);
612 self.chunk.columns.push(self.column);
613 self.chunk.lines.push(self.line);
614 self.chunk.columns.push(self.column);
615 }
616 } else if let Node::SubscriptAccess { object, index } = &target.node {
617 if let Some(var_name) = self.root_var_name(object) {
619 let var_idx = self.chunk.add_constant(Constant::String(var_name.clone()));
620 if let Some(op) = op {
621 self.compile_node(target)?;
622 self.compile_node(value)?;
623 self.emit_compound_op(op)?;
624 } else {
625 self.compile_node(value)?;
626 }
627 self.compile_node(index)?;
628 self.chunk.emit_u16(Op::SetSubscript, var_idx, self.line);
629 }
630 }
631 }
632
633 Node::BinaryOp { op, left, right } => {
634 match op.as_str() {
636 "&&" => {
637 self.compile_node(left)?;
638 let jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
639 self.chunk.emit(Op::Pop, self.line);
640 self.compile_node(right)?;
641 self.chunk.patch_jump(jump);
642 self.chunk.emit(Op::Not, self.line);
644 self.chunk.emit(Op::Not, self.line);
645 return Ok(());
646 }
647 "||" => {
648 self.compile_node(left)?;
649 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
650 self.chunk.emit(Op::Pop, self.line);
651 self.compile_node(right)?;
652 self.chunk.patch_jump(jump);
653 self.chunk.emit(Op::Not, self.line);
654 self.chunk.emit(Op::Not, self.line);
655 return Ok(());
656 }
657 "??" => {
658 self.compile_node(left)?;
659 self.chunk.emit(Op::Dup, self.line);
660 self.chunk.emit(Op::Nil, self.line);
662 self.chunk.emit(Op::NotEqual, self.line);
663 let jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
664 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(right)?;
667 let end = self.chunk.emit_jump(Op::Jump, self.line);
668 self.chunk.patch_jump(jump);
669 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
671 return Ok(());
672 }
673 "|>" => {
674 self.compile_node(left)?;
675 if contains_pipe_placeholder(right) {
678 let replaced = replace_pipe_placeholder(right);
679 let closure_node = SNode::dummy(Node::Closure {
680 params: vec![TypedParam {
681 name: "__pipe".into(),
682 type_expr: None,
683 default_value: None,
684 rest: false,
685 }],
686 body: vec![replaced],
687 fn_syntax: false,
688 });
689 self.compile_node(&closure_node)?;
690 } else {
691 self.compile_node(right)?;
692 }
693 self.chunk.emit(Op::Pipe, self.line);
694 return Ok(());
695 }
696 _ => {}
697 }
698
699 self.compile_node(left)?;
700 self.compile_node(right)?;
701 match op.as_str() {
702 "+" => self.chunk.emit(Op::Add, self.line),
703 "-" => self.chunk.emit(Op::Sub, self.line),
704 "*" => self.chunk.emit(Op::Mul, self.line),
705 "/" => self.chunk.emit(Op::Div, self.line),
706 "%" => self.chunk.emit(Op::Mod, self.line),
707 "**" => self.chunk.emit(Op::Pow, self.line),
708 "==" => self.chunk.emit(Op::Equal, self.line),
709 "!=" => self.chunk.emit(Op::NotEqual, self.line),
710 "<" => self.chunk.emit(Op::Less, self.line),
711 ">" => self.chunk.emit(Op::Greater, self.line),
712 "<=" => self.chunk.emit(Op::LessEqual, self.line),
713 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
714 "in" => self.chunk.emit(Op::Contains, self.line),
715 "not_in" => {
716 self.chunk.emit(Op::Contains, self.line);
717 self.chunk.emit(Op::Not, self.line);
718 }
719 _ => {
720 return Err(CompileError {
721 message: format!("Unknown operator: {op}"),
722 line: self.line,
723 })
724 }
725 }
726 }
727
728 Node::UnaryOp { op, operand } => {
729 self.compile_node(operand)?;
730 match op.as_str() {
731 "-" => self.chunk.emit(Op::Negate, self.line),
732 "!" => self.chunk.emit(Op::Not, self.line),
733 _ => {}
734 }
735 }
736
737 Node::Ternary {
738 condition,
739 true_expr,
740 false_expr,
741 } => {
742 self.compile_node(condition)?;
743 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
744 self.chunk.emit(Op::Pop, self.line);
745 self.compile_node(true_expr)?;
746 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
747 self.chunk.patch_jump(else_jump);
748 self.chunk.emit(Op::Pop, self.line);
749 self.compile_node(false_expr)?;
750 self.chunk.patch_jump(end_jump);
751 }
752
753 Node::FunctionCall { name, args } => {
754 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
755 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
757 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
758
759 if has_spread {
760 self.chunk.emit_u16(Op::BuildList, 0, self.line);
763 let mut pending = 0u16;
764 for arg in args {
765 if let Node::Spread(inner) = &arg.node {
766 if pending > 0 {
767 self.chunk.emit_u16(Op::BuildList, pending, self.line);
768 self.chunk.emit(Op::Add, self.line);
769 pending = 0;
770 }
771 self.compile_node(inner)?;
772 self.chunk.emit(Op::Dup, self.line);
773 let assert_idx = self
774 .chunk
775 .add_constant(Constant::String("__assert_list".into()));
776 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
777 self.chunk.emit(Op::Swap, self.line);
778 self.chunk.emit_u8(Op::Call, 1, self.line);
779 self.chunk.emit(Op::Pop, self.line);
780 self.chunk.emit(Op::Add, self.line);
781 } else {
782 self.compile_node(arg)?;
783 pending += 1;
784 }
785 }
786 if pending > 0 {
787 self.chunk.emit_u16(Op::BuildList, pending, self.line);
788 self.chunk.emit(Op::Add, self.line);
789 }
790 self.chunk.emit(Op::CallSpread, self.line);
791 } else {
792 for arg in args {
794 self.compile_node(arg)?;
795 }
796 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
797 }
798 }
799
800 Node::MethodCall {
801 object,
802 method,
803 args,
804 } => {
805 if let Node::Identifier(name) = &object.node {
807 if self.enum_names.contains(name) {
808 for arg in args {
810 self.compile_node(arg)?;
811 }
812 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
813 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
814 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
815 let hi = (var_idx >> 8) as u8;
816 let lo = var_idx as u8;
817 self.chunk.code.push(hi);
818 self.chunk.code.push(lo);
819 self.chunk.lines.push(self.line);
820 self.chunk.columns.push(self.column);
821 self.chunk.lines.push(self.line);
822 self.chunk.columns.push(self.column);
823 let fc = args.len() as u16;
824 let fhi = (fc >> 8) as u8;
825 let flo = fc as u8;
826 self.chunk.code.push(fhi);
827 self.chunk.code.push(flo);
828 self.chunk.lines.push(self.line);
829 self.chunk.columns.push(self.column);
830 self.chunk.lines.push(self.line);
831 self.chunk.columns.push(self.column);
832 return Ok(());
833 }
834 }
835 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
836 self.compile_node(object)?;
837 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
838 if has_spread {
839 self.chunk.emit_u16(Op::BuildList, 0, self.line);
841 let mut pending = 0u16;
842 for arg in args {
843 if let Node::Spread(inner) = &arg.node {
844 if pending > 0 {
845 self.chunk.emit_u16(Op::BuildList, pending, self.line);
846 self.chunk.emit(Op::Add, self.line);
847 pending = 0;
848 }
849 self.compile_node(inner)?;
850 self.chunk.emit(Op::Dup, self.line);
851 let assert_idx = self
852 .chunk
853 .add_constant(Constant::String("__assert_list".into()));
854 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
855 self.chunk.emit(Op::Swap, self.line);
856 self.chunk.emit_u8(Op::Call, 1, self.line);
857 self.chunk.emit(Op::Pop, self.line);
858 self.chunk.emit(Op::Add, self.line);
859 } else {
860 self.compile_node(arg)?;
861 pending += 1;
862 }
863 }
864 if pending > 0 {
865 self.chunk.emit_u16(Op::BuildList, pending, self.line);
866 self.chunk.emit(Op::Add, self.line);
867 }
868 self.chunk
869 .emit_u16(Op::MethodCallSpread, name_idx, self.line);
870 } else {
871 for arg in args {
872 self.compile_node(arg)?;
873 }
874 self.chunk
875 .emit_method_call(name_idx, args.len() as u8, self.line);
876 }
877 }
878
879 Node::OptionalMethodCall {
880 object,
881 method,
882 args,
883 } => {
884 self.compile_node(object)?;
885 for arg in args {
886 self.compile_node(arg)?;
887 }
888 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
889 self.chunk
890 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
891 }
892
893 Node::PropertyAccess { object, property } => {
894 if let Node::Identifier(name) = &object.node {
896 if self.enum_names.contains(name) {
897 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
899 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
900 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
901 let hi = (var_idx >> 8) as u8;
902 let lo = var_idx as u8;
903 self.chunk.code.push(hi);
904 self.chunk.code.push(lo);
905 self.chunk.lines.push(self.line);
906 self.chunk.columns.push(self.column);
907 self.chunk.lines.push(self.line);
908 self.chunk.columns.push(self.column);
909 self.chunk.code.push(0);
911 self.chunk.code.push(0);
912 self.chunk.lines.push(self.line);
913 self.chunk.columns.push(self.column);
914 self.chunk.lines.push(self.line);
915 self.chunk.columns.push(self.column);
916 return Ok(());
917 }
918 }
919 self.compile_node(object)?;
920 let idx = self.chunk.add_constant(Constant::String(property.clone()));
921 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
922 }
923
924 Node::OptionalPropertyAccess { object, property } => {
925 self.compile_node(object)?;
926 let idx = self.chunk.add_constant(Constant::String(property.clone()));
927 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
928 }
929
930 Node::SubscriptAccess { object, index } => {
931 self.compile_node(object)?;
932 self.compile_node(index)?;
933 self.chunk.emit(Op::Subscript, self.line);
934 }
935
936 Node::SliceAccess { object, start, end } => {
937 self.compile_node(object)?;
938 if let Some(s) = start {
939 self.compile_node(s)?;
940 } else {
941 self.chunk.emit(Op::Nil, self.line);
942 }
943 if let Some(e) = end {
944 self.compile_node(e)?;
945 } else {
946 self.chunk.emit(Op::Nil, self.line);
947 }
948 self.chunk.emit(Op::Slice, self.line);
949 }
950
951 Node::IfElse {
952 condition,
953 then_body,
954 else_body,
955 } => {
956 self.compile_node(condition)?;
957 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
958 self.chunk.emit(Op::Pop, self.line);
959 self.compile_scoped_block(then_body)?;
960 if let Some(else_body) = else_body {
961 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
962 self.chunk.patch_jump(else_jump);
963 self.chunk.emit(Op::Pop, self.line);
964 self.compile_scoped_block(else_body)?;
965 self.chunk.patch_jump(end_jump);
966 } else {
967 self.chunk.patch_jump(else_jump);
968 self.chunk.emit(Op::Pop, self.line);
969 self.chunk.emit(Op::Nil, self.line);
970 }
971 }
972
973 Node::WhileLoop { condition, body } => {
974 let loop_start = self.chunk.current_offset();
975 self.loop_stack.push(LoopContext {
976 start_offset: loop_start,
977 break_patches: Vec::new(),
978 has_iterator: false,
979 handler_depth: self.handler_depth,
980 finally_depth: self.finally_bodies.len(),
981 scope_depth: self.scope_depth,
982 });
983 self.compile_node(condition)?;
984 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
985 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_statements(body)?;
987 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
989 self.chunk.patch_jump(exit_jump);
990 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
993 for patch_pos in ctx.break_patches {
994 self.chunk.patch_jump(patch_pos);
995 }
996 self.chunk.emit(Op::Nil, self.line);
997 }
998
999 Node::ForIn {
1000 pattern,
1001 iterable,
1002 body,
1003 } => {
1004 self.compile_node(iterable)?;
1006 self.chunk.emit(Op::IterInit, self.line);
1008 let loop_start = self.chunk.current_offset();
1009 self.loop_stack.push(LoopContext {
1010 start_offset: loop_start,
1011 break_patches: Vec::new(),
1012 has_iterator: true,
1013 handler_depth: self.handler_depth,
1014 finally_depth: self.finally_bodies.len(),
1015 scope_depth: self.scope_depth,
1016 });
1017 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
1019 self.begin_scope();
1020 self.compile_destructuring(pattern, true)?;
1022 for sn in body {
1023 self.compile_node(sn)?;
1024 if Self::produces_value(&sn.node) {
1025 self.chunk.emit(Op::Pop, self.line);
1026 }
1027 }
1028 self.end_scope();
1029 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1031 self.chunk.patch_jump(exit_jump_pos);
1032 let ctx = self.loop_stack.pop().unwrap();
1034 for patch_pos in ctx.break_patches {
1035 self.chunk.patch_jump(patch_pos);
1036 }
1037 self.chunk.emit(Op::Nil, self.line);
1039 }
1040
1041 Node::ReturnStmt { value } => {
1042 let has_pending_finally = !self.finally_bodies.is_empty();
1043
1044 if has_pending_finally {
1045 if let Some(val) = value {
1048 self.compile_node(val)?;
1049 } else {
1050 self.chunk.emit(Op::Nil, self.line);
1051 }
1052 self.temp_counter += 1;
1053 let temp_name = format!("__return_val_{}__", self.temp_counter);
1054 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
1055 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
1056 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
1058 for fb in &finallys {
1059 self.compile_finally_inline(fb)?;
1060 }
1061 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
1062 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
1063 self.chunk.emit(Op::Return, self.line);
1064 } else {
1065 if let Some(val) = value {
1067 if let Node::FunctionCall { name, args } = &val.node {
1068 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1069 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1070 for arg in args {
1071 self.compile_node(arg)?;
1072 }
1073 self.chunk
1074 .emit_u8(Op::TailCall, args.len() as u8, self.line);
1075 } else if let Node::BinaryOp { op, left, right } = &val.node {
1076 if op == "|>" {
1077 self.compile_node(left)?;
1078 self.compile_node(right)?;
1079 self.chunk.emit(Op::Swap, self.line);
1080 self.chunk.emit_u8(Op::TailCall, 1, self.line);
1081 } else {
1082 self.compile_node(val)?;
1083 }
1084 } else {
1085 self.compile_node(val)?;
1086 }
1087 } else {
1088 self.chunk.emit(Op::Nil, self.line);
1089 }
1090 self.chunk.emit(Op::Return, self.line);
1091 }
1092 }
1093
1094 Node::BreakStmt => {
1095 if self.loop_stack.is_empty() {
1096 return Err(CompileError {
1097 message: "break outside of loop".to_string(),
1098 line: self.line,
1099 });
1100 }
1101 let ctx = self.loop_stack.last().unwrap();
1103 let finally_depth = ctx.finally_depth;
1104 let handler_depth = ctx.handler_depth;
1105 let has_iterator = ctx.has_iterator;
1106 let scope_depth = ctx.scope_depth;
1107 for _ in handler_depth..self.handler_depth {
1109 self.chunk.emit(Op::PopHandler, self.line);
1110 }
1111 if self.finally_bodies.len() > finally_depth {
1113 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1114 .iter()
1115 .rev()
1116 .cloned()
1117 .collect();
1118 for fb in &finallys {
1119 self.compile_finally_inline(fb)?;
1120 }
1121 }
1122 self.unwind_scopes_to(scope_depth);
1123 if has_iterator {
1124 self.chunk.emit(Op::PopIterator, self.line);
1125 }
1126 let patch = self.chunk.emit_jump(Op::Jump, self.line);
1127 self.loop_stack
1128 .last_mut()
1129 .unwrap()
1130 .break_patches
1131 .push(patch);
1132 }
1133
1134 Node::ContinueStmt => {
1135 if self.loop_stack.is_empty() {
1136 return Err(CompileError {
1137 message: "continue outside of loop".to_string(),
1138 line: self.line,
1139 });
1140 }
1141 let ctx = self.loop_stack.last().unwrap();
1142 let finally_depth = ctx.finally_depth;
1143 let handler_depth = ctx.handler_depth;
1144 let loop_start = ctx.start_offset;
1145 let scope_depth = ctx.scope_depth;
1146 for _ in handler_depth..self.handler_depth {
1147 self.chunk.emit(Op::PopHandler, self.line);
1148 }
1149 if self.finally_bodies.len() > finally_depth {
1150 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1151 .iter()
1152 .rev()
1153 .cloned()
1154 .collect();
1155 for fb in &finallys {
1156 self.compile_finally_inline(fb)?;
1157 }
1158 }
1159 self.unwind_scopes_to(scope_depth);
1160 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1161 }
1162
1163 Node::ListLiteral(elements) => {
1164 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
1165 if !has_spread {
1166 for el in elements {
1167 self.compile_node(el)?;
1168 }
1169 self.chunk
1170 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
1171 } else {
1172 self.chunk.emit_u16(Op::BuildList, 0, self.line);
1175 let mut pending = 0u16;
1176 for el in elements {
1177 if let Node::Spread(inner) = &el.node {
1178 if pending > 0 {
1180 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1181 self.chunk.emit(Op::Add, self.line);
1183 pending = 0;
1184 }
1185 self.compile_node(inner)?;
1187 self.chunk.emit(Op::Dup, self.line);
1188 let assert_idx = self
1189 .chunk
1190 .add_constant(Constant::String("__assert_list".into()));
1191 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1192 self.chunk.emit(Op::Swap, self.line);
1193 self.chunk.emit_u8(Op::Call, 1, self.line);
1194 self.chunk.emit(Op::Pop, self.line);
1195 self.chunk.emit(Op::Add, self.line);
1196 } else {
1197 self.compile_node(el)?;
1198 pending += 1;
1199 }
1200 }
1201 if pending > 0 {
1202 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1203 self.chunk.emit(Op::Add, self.line);
1204 }
1205 }
1206 }
1207
1208 Node::DictLiteral(entries) => {
1209 let has_spread = entries
1210 .iter()
1211 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1212 if !has_spread {
1213 for entry in entries {
1214 self.compile_node(&entry.key)?;
1215 self.compile_node(&entry.value)?;
1216 }
1217 self.chunk
1218 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1219 } else {
1220 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1222 let mut pending = 0u16;
1223 for entry in entries {
1224 if let Node::Spread(inner) = &entry.value.node {
1225 if pending > 0 {
1227 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1228 self.chunk.emit(Op::Add, self.line);
1229 pending = 0;
1230 }
1231 self.compile_node(inner)?;
1233 self.chunk.emit(Op::Dup, self.line);
1234 let assert_idx = self
1235 .chunk
1236 .add_constant(Constant::String("__assert_dict".into()));
1237 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1238 self.chunk.emit(Op::Swap, self.line);
1239 self.chunk.emit_u8(Op::Call, 1, self.line);
1240 self.chunk.emit(Op::Pop, self.line);
1241 self.chunk.emit(Op::Add, self.line);
1242 } else {
1243 self.compile_node(&entry.key)?;
1244 self.compile_node(&entry.value)?;
1245 pending += 1;
1246 }
1247 }
1248 if pending > 0 {
1249 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1250 self.chunk.emit(Op::Add, self.line);
1251 }
1252 }
1253 }
1254
1255 Node::InterpolatedString(segments) => {
1256 let mut part_count = 0u16;
1257 for seg in segments {
1258 match seg {
1259 StringSegment::Literal(s) => {
1260 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1261 self.chunk.emit_u16(Op::Constant, idx, self.line);
1262 part_count += 1;
1263 }
1264 StringSegment::Expression(expr_str, expr_line, expr_col) => {
1265 let mut lexer =
1267 harn_lexer::Lexer::with_position(expr_str, *expr_line, *expr_col);
1268 if let Ok(tokens) = lexer.tokenize() {
1269 let mut parser = harn_parser::Parser::new(tokens);
1270 if let Ok(snode) = parser.parse_single_expression() {
1271 self.compile_node(&snode)?;
1272 let to_str = self
1274 .chunk
1275 .add_constant(Constant::String("to_string".into()));
1276 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1277 self.chunk.emit(Op::Swap, self.line);
1278 self.chunk.emit_u8(Op::Call, 1, self.line);
1279 part_count += 1;
1280 } else {
1281 let idx =
1283 self.chunk.add_constant(Constant::String(expr_str.clone()));
1284 self.chunk.emit_u16(Op::Constant, idx, self.line);
1285 part_count += 1;
1286 }
1287 }
1288 }
1289 }
1290 }
1291 if part_count > 1 {
1292 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1293 }
1294 }
1295
1296 Node::FnDecl {
1297 name, params, body, ..
1298 } => {
1299 let mut fn_compiler = Compiler::new();
1301 fn_compiler.enum_names = self.enum_names.clone();
1302 fn_compiler.emit_default_preamble(params)?;
1303 fn_compiler.emit_type_checks(params);
1304 let is_gen = body_contains_yield(body);
1305 fn_compiler.compile_block(body)?;
1306 if !fn_compiler.finally_bodies.is_empty() {
1308 let finallys: Vec<_> =
1309 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1310 for fb in &finallys {
1311 fn_compiler.compile_finally_inline(fb)?;
1312 }
1313 }
1314 fn_compiler.chunk.emit(Op::Nil, self.line);
1315 fn_compiler.chunk.emit(Op::Return, self.line);
1316
1317 let func = CompiledFunction {
1318 name: name.clone(),
1319 params: TypedParam::names(params),
1320 default_start: TypedParam::default_start(params),
1321 chunk: fn_compiler.chunk,
1322 is_generator: is_gen,
1323 has_rest_param: params.last().is_some_and(|p| p.rest),
1324 };
1325 let fn_idx = self.chunk.functions.len();
1326 self.chunk.functions.push(func);
1327
1328 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1329 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1330 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1331 }
1332
1333 Node::ToolDecl {
1334 name,
1335 description,
1336 params,
1337 return_type,
1338 body,
1339 ..
1340 } => {
1341 let mut fn_compiler = Compiler::new();
1343 fn_compiler.enum_names = self.enum_names.clone();
1344 fn_compiler.emit_default_preamble(params)?;
1345 fn_compiler.emit_type_checks(params);
1346 fn_compiler.compile_block(body)?;
1347 if !fn_compiler.finally_bodies.is_empty() {
1349 let finallys: Vec<_> =
1350 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1351 for fb in &finallys {
1352 fn_compiler.compile_finally_inline(fb)?;
1353 }
1354 }
1355 fn_compiler.chunk.emit(Op::Return, self.line);
1357
1358 let func = CompiledFunction {
1359 name: name.clone(),
1360 params: TypedParam::names(params),
1361 default_start: TypedParam::default_start(params),
1362 chunk: fn_compiler.chunk,
1363 is_generator: false,
1364 has_rest_param: params.last().is_some_and(|p| p.rest),
1365 };
1366 let fn_idx = self.chunk.functions.len();
1367 self.chunk.functions.push(func);
1368
1369 let define_name = self
1371 .chunk
1372 .add_constant(Constant::String("tool_define".into()));
1373 self.chunk.emit_u16(Op::Constant, define_name, self.line);
1374
1375 let reg_name = self
1377 .chunk
1378 .add_constant(Constant::String("tool_registry".into()));
1379 self.chunk.emit_u16(Op::Constant, reg_name, self.line);
1380 self.chunk.emit_u8(Op::Call, 0, self.line);
1381
1382 let tool_name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1384 self.chunk.emit_u16(Op::Constant, tool_name_idx, self.line);
1385
1386 let desc = description.as_deref().unwrap_or("");
1388 let desc_idx = self.chunk.add_constant(Constant::String(desc.to_string()));
1389 self.chunk.emit_u16(Op::Constant, desc_idx, self.line);
1390
1391 let mut param_count: u16 = 0;
1396 for p in params {
1397 let pn_idx = self.chunk.add_constant(Constant::String(p.name.clone()));
1398 self.chunk.emit_u16(Op::Constant, pn_idx, self.line);
1399
1400 let base_schema = p
1401 .type_expr
1402 .as_ref()
1403 .and_then(Self::type_expr_to_schema_value)
1404 .unwrap_or_else(|| {
1405 VmValue::Dict(Rc::new(BTreeMap::from([(
1406 "type".to_string(),
1407 VmValue::String(Rc::from("any")),
1408 )])))
1409 });
1410 let public_schema =
1411 schema::schema_to_json_schema_value(&base_schema).map_err(|error| {
1412 CompileError {
1413 message: format!(
1414 "failed to lower tool parameter schema for '{}': {}",
1415 p.name, error
1416 ),
1417 line: self.line,
1418 }
1419 })?;
1420 let mut param_schema = match public_schema {
1421 VmValue::Dict(map) => (*map).clone(),
1422 _ => BTreeMap::new(),
1423 };
1424
1425 if p.default_value.is_some() {
1426 param_schema.insert("required".to_string(), VmValue::Bool(false));
1427 }
1428
1429 self.emit_vm_value_literal(&VmValue::Dict(Rc::new(param_schema)));
1430
1431 if let Some(default_value) = p.default_value.as_ref() {
1432 let default_key =
1433 self.chunk.add_constant(Constant::String("default".into()));
1434 self.chunk.emit_u16(Op::Constant, default_key, self.line);
1435 self.compile_node(default_value)?;
1436 self.chunk.emit_u16(Op::BuildDict, 1, self.line);
1437 self.chunk.emit(Op::Add, self.line);
1438 }
1439
1440 param_count += 1;
1441 }
1442 self.chunk.emit_u16(Op::BuildDict, param_count, self.line);
1443
1444 let params_key = self
1446 .chunk
1447 .add_constant(Constant::String("parameters".into()));
1448 self.chunk.emit_u16(Op::Constant, params_key, self.line);
1449 self.chunk.emit(Op::Swap, self.line);
1450
1451 let handler_key = self.chunk.add_constant(Constant::String("handler".into()));
1452 self.chunk.emit_u16(Op::Constant, handler_key, self.line);
1453 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1454
1455 let mut config_entries = 2u16;
1456 if let Some(return_type) = return_type
1457 .as_ref()
1458 .and_then(Self::type_expr_to_schema_value)
1459 {
1460 let return_type =
1461 schema::schema_to_json_schema_value(&return_type).map_err(|error| {
1462 CompileError {
1463 message: format!(
1464 "failed to lower tool return schema for '{}': {}",
1465 name, error
1466 ),
1467 line: self.line,
1468 }
1469 })?;
1470 let returns_key = self.chunk.add_constant(Constant::String("returns".into()));
1471 self.chunk.emit_u16(Op::Constant, returns_key, self.line);
1472 self.emit_vm_value_literal(&return_type);
1473 config_entries += 1;
1474 }
1475
1476 self.chunk
1477 .emit_u16(Op::BuildDict, config_entries, self.line);
1478
1479 self.chunk.emit_u8(Op::Call, 4, self.line);
1481
1482 let bind_idx = self.chunk.add_constant(Constant::String(name.clone()));
1484 self.chunk.emit_u16(Op::DefLet, bind_idx, self.line);
1485 }
1486
1487 Node::Closure { params, body, .. } => {
1488 let mut fn_compiler = Compiler::new();
1489 fn_compiler.enum_names = self.enum_names.clone();
1490 fn_compiler.emit_default_preamble(params)?;
1491 fn_compiler.emit_type_checks(params);
1492 let is_gen = body_contains_yield(body);
1493 fn_compiler.compile_block(body)?;
1494 if !fn_compiler.finally_bodies.is_empty() {
1496 let finallys: Vec<_> =
1497 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1498 for fb in &finallys {
1499 fn_compiler.compile_finally_inline(fb)?;
1500 }
1501 }
1502 fn_compiler.chunk.emit(Op::Return, self.line);
1504
1505 let func = CompiledFunction {
1506 name: "<closure>".to_string(),
1507 params: TypedParam::names(params),
1508 default_start: TypedParam::default_start(params),
1509 chunk: fn_compiler.chunk,
1510 is_generator: is_gen,
1511 has_rest_param: false,
1512 };
1513 let fn_idx = self.chunk.functions.len();
1514 self.chunk.functions.push(func);
1515
1516 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1517 }
1518
1519 Node::ThrowStmt { value } => {
1520 if !self.finally_bodies.is_empty() {
1521 self.compile_node(value)?;
1523 self.temp_counter += 1;
1524 let temp_name = format!("__throw_val_{}__", self.temp_counter);
1525 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
1526 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
1527 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
1528 for fb in &finallys {
1529 self.compile_finally_inline(fb)?;
1530 }
1531 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
1532 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
1533 self.chunk.emit(Op::Throw, self.line);
1534 } else {
1535 self.compile_node(value)?;
1536 self.chunk.emit(Op::Throw, self.line);
1537 }
1538 }
1539
1540 Node::MatchExpr { value, arms } => {
1541 self.compile_node(value)?;
1542 let mut end_jumps = Vec::new();
1543 for arm in arms {
1544 match &arm.pattern.node {
1545 Node::Identifier(name) if name == "_" => {
1547 if let Some(ref guard) = arm.guard {
1548 self.compile_node(guard)?;
1549 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1550 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1552 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1554 self.end_scope();
1555 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1556 self.chunk.patch_jump(guard_skip);
1557 self.chunk.emit(Op::Pop, self.line); } else {
1559 self.begin_scope();
1560 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1562 self.end_scope();
1563 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1564 }
1565 }
1566 Node::EnumConstruct {
1568 enum_name,
1569 variant,
1570 args: pat_args,
1571 } => {
1572 self.chunk.emit(Op::Dup, self.line);
1574 let en_idx =
1575 self.chunk.add_constant(Constant::String(enum_name.clone()));
1576 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1577 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1578 let hi = (vn_idx >> 8) as u8;
1579 let lo = vn_idx as u8;
1580 self.chunk.code.push(hi);
1581 self.chunk.code.push(lo);
1582 self.chunk.lines.push(self.line);
1583 self.chunk.columns.push(self.column);
1584 self.chunk.lines.push(self.line);
1585 self.chunk.columns.push(self.column);
1586 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1588 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1590
1591 for (i, pat_arg) in pat_args.iter().enumerate() {
1594 if let Node::Identifier(binding_name) = &pat_arg.node {
1595 self.chunk.emit(Op::Dup, self.line);
1597 let fields_idx = self
1598 .chunk
1599 .add_constant(Constant::String("fields".to_string()));
1600 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1601 let idx_const =
1602 self.chunk.add_constant(Constant::Int(i as i64));
1603 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1604 self.chunk.emit(Op::Subscript, self.line);
1605 let name_idx = self
1606 .chunk
1607 .add_constant(Constant::String(binding_name.clone()));
1608 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1609 }
1610 }
1611
1612 if let Some(ref guard) = arm.guard {
1614 self.compile_node(guard)?;
1615 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1616 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1619 self.end_scope();
1620 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1621 self.chunk.patch_jump(guard_skip);
1622 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1624 } else {
1625 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1627 self.end_scope();
1628 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1629 }
1630 self.chunk.patch_jump(skip);
1631 self.chunk.emit(Op::Pop, self.line); }
1633 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1635 {
1636 let enum_name = if let Node::Identifier(n) = &object.node {
1637 n.clone()
1638 } else {
1639 unreachable!()
1640 };
1641 self.chunk.emit(Op::Dup, self.line);
1642 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1643 let vn_idx =
1644 self.chunk.add_constant(Constant::String(property.clone()));
1645 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1646 let hi = (vn_idx >> 8) as u8;
1647 let lo = vn_idx as u8;
1648 self.chunk.code.push(hi);
1649 self.chunk.code.push(lo);
1650 self.chunk.lines.push(self.line);
1651 self.chunk.columns.push(self.column);
1652 self.chunk.lines.push(self.line);
1653 self.chunk.columns.push(self.column);
1654 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1655 self.chunk.emit(Op::Pop, self.line); if let Some(ref guard) = arm.guard {
1658 self.compile_node(guard)?;
1659 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1660 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1662 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1664 self.end_scope();
1665 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1666 self.chunk.patch_jump(guard_skip);
1667 self.chunk.emit(Op::Pop, self.line); } else {
1669 self.begin_scope();
1670 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1672 self.end_scope();
1673 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1674 }
1675 self.chunk.patch_jump(skip);
1676 self.chunk.emit(Op::Pop, self.line); }
1678 Node::MethodCall {
1681 object,
1682 method,
1683 args: pat_args,
1684 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1685 {
1686 let enum_name = if let Node::Identifier(n) = &object.node {
1687 n.clone()
1688 } else {
1689 unreachable!()
1690 };
1691 self.chunk.emit(Op::Dup, self.line);
1693 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1694 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1695 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1696 let hi = (vn_idx >> 8) as u8;
1697 let lo = vn_idx as u8;
1698 self.chunk.code.push(hi);
1699 self.chunk.code.push(lo);
1700 self.chunk.lines.push(self.line);
1701 self.chunk.columns.push(self.column);
1702 self.chunk.lines.push(self.line);
1703 self.chunk.columns.push(self.column);
1704 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1705 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1707
1708 for (i, pat_arg) in pat_args.iter().enumerate() {
1710 if let Node::Identifier(binding_name) = &pat_arg.node {
1711 self.chunk.emit(Op::Dup, self.line);
1712 let fields_idx = self
1713 .chunk
1714 .add_constant(Constant::String("fields".to_string()));
1715 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1716 let idx_const =
1717 self.chunk.add_constant(Constant::Int(i as i64));
1718 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1719 self.chunk.emit(Op::Subscript, self.line);
1720 let name_idx = self
1721 .chunk
1722 .add_constant(Constant::String(binding_name.clone()));
1723 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1724 }
1725 }
1726
1727 if let Some(ref guard) = arm.guard {
1729 self.compile_node(guard)?;
1730 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1731 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1734 self.end_scope();
1735 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1736 self.chunk.patch_jump(guard_skip);
1737 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1739 } else {
1740 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1742 self.end_scope();
1743 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1744 }
1745 self.chunk.patch_jump(skip);
1746 self.chunk.emit(Op::Pop, self.line); }
1748 Node::Identifier(name) => {
1750 self.begin_scope();
1751 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1754 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1755 if let Some(ref guard) = arm.guard {
1757 self.compile_node(guard)?;
1758 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1759 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1762 self.end_scope();
1763 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1764 self.chunk.patch_jump(guard_skip);
1765 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1767 } else {
1768 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1770 self.end_scope();
1771 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1772 }
1773 }
1774 Node::DictLiteral(entries)
1776 if entries
1777 .iter()
1778 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1779 {
1780 self.chunk.emit(Op::Dup, self.line);
1782 let typeof_idx =
1783 self.chunk.add_constant(Constant::String("type_of".into()));
1784 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1785 self.chunk.emit(Op::Swap, self.line);
1786 self.chunk.emit_u8(Op::Call, 1, self.line);
1787 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1788 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1789 self.chunk.emit(Op::Equal, self.line);
1790 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1791 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1795 let mut bindings = Vec::new();
1796 self.begin_scope();
1797 for entry in entries {
1798 if let Node::StringLiteral(key) = &entry.key.node {
1799 match &entry.value.node {
1800 Node::StringLiteral(_)
1802 | Node::IntLiteral(_)
1803 | Node::FloatLiteral(_)
1804 | Node::BoolLiteral(_)
1805 | Node::NilLiteral => {
1806 self.chunk.emit(Op::Dup, self.line);
1807 let key_idx = self
1808 .chunk
1809 .add_constant(Constant::String(key.clone()));
1810 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1811 self.chunk.emit(Op::Subscript, self.line);
1812 self.compile_node(&entry.value)?;
1813 self.chunk.emit(Op::Equal, self.line);
1814 let skip =
1815 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1816 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1818 }
1819 Node::Identifier(binding) => {
1821 bindings.push((key.clone(), binding.clone()));
1822 }
1823 _ => {
1824 self.chunk.emit(Op::Dup, self.line);
1826 let key_idx = self
1827 .chunk
1828 .add_constant(Constant::String(key.clone()));
1829 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1830 self.chunk.emit(Op::Subscript, self.line);
1831 self.compile_node(&entry.value)?;
1832 self.chunk.emit(Op::Equal, self.line);
1833 let skip =
1834 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1835 self.chunk.emit(Op::Pop, self.line);
1836 constraint_skips.push(skip);
1837 }
1838 }
1839 }
1840 }
1841
1842 for (key, binding) in &bindings {
1844 self.chunk.emit(Op::Dup, self.line);
1845 let key_idx =
1846 self.chunk.add_constant(Constant::String(key.clone()));
1847 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1848 self.chunk.emit(Op::Subscript, self.line);
1849 let name_idx =
1850 self.chunk.add_constant(Constant::String(binding.clone()));
1851 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1852 }
1853
1854 if let Some(ref guard) = arm.guard {
1856 self.compile_node(guard)?;
1857 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1858 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1861 self.end_scope();
1862 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1863 self.chunk.patch_jump(guard_skip);
1864 self.chunk.emit(Op::Pop, self.line); } else {
1868 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1870 self.end_scope();
1871 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1872 }
1873
1874 let type_fail_target = self.chunk.code.len();
1875 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1877 let scoped_fail_target = self.chunk.code.len();
1878 self.chunk.emit(Op::PopScope, self.line);
1879 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1881
1882 for skip in constraint_skips {
1883 self.chunk.patch_jump_to(skip, scoped_fail_target);
1884 }
1885 self.chunk.patch_jump_to(skip_type, type_fail_target);
1886 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1887 }
1888 Node::ListLiteral(elements) => {
1890 self.chunk.emit(Op::Dup, self.line);
1892 let typeof_idx =
1893 self.chunk.add_constant(Constant::String("type_of".into()));
1894 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1895 self.chunk.emit(Op::Swap, self.line);
1896 self.chunk.emit_u8(Op::Call, 1, self.line);
1897 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1898 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1899 self.chunk.emit(Op::Equal, self.line);
1900 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1901 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1905 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1906 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1907 self.chunk.emit(Op::Swap, self.line);
1908 self.chunk.emit_u8(Op::Call, 1, self.line);
1909 let count = self
1910 .chunk
1911 .add_constant(Constant::Int(elements.len() as i64));
1912 self.chunk.emit_u16(Op::Constant, count, self.line);
1913 self.chunk.emit(Op::GreaterEqual, self.line);
1914 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1915 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1919 let mut bindings = Vec::new();
1920 self.begin_scope();
1921 for (i, elem) in elements.iter().enumerate() {
1922 match &elem.node {
1923 Node::Identifier(name) if name != "_" => {
1924 bindings.push((i, name.clone()));
1925 }
1926 Node::Identifier(_) => {} _ => {
1929 self.chunk.emit(Op::Dup, self.line);
1930 let idx_const =
1931 self.chunk.add_constant(Constant::Int(i as i64));
1932 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1933 self.chunk.emit(Op::Subscript, self.line);
1934 self.compile_node(elem)?;
1935 self.chunk.emit(Op::Equal, self.line);
1936 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1937 self.chunk.emit(Op::Pop, self.line);
1938 constraint_skips.push(skip);
1939 }
1940 }
1941 }
1942
1943 for (i, name) in &bindings {
1945 self.chunk.emit(Op::Dup, self.line);
1946 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1947 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1948 self.chunk.emit(Op::Subscript, self.line);
1949 let name_idx =
1950 self.chunk.add_constant(Constant::String(name.clone()));
1951 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1952 }
1953
1954 if let Some(ref guard) = arm.guard {
1956 self.compile_node(guard)?;
1957 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1958 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1961 self.end_scope();
1962 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1963 self.chunk.patch_jump(guard_skip);
1964 self.chunk.emit(Op::Pop, self.line); } else {
1966 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1968 self.end_scope();
1969 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1970 }
1971
1972 let pre_scope_fail_target = self.chunk.code.len();
1973 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1975 let scoped_fail_target = self.chunk.code.len();
1976 self.chunk.emit(Op::PopScope, self.line);
1977 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1979 for skip in constraint_skips {
1980 self.chunk.patch_jump_to(skip, scoped_fail_target);
1981 }
1982 self.chunk.patch_jump_to(skip_len, pre_scope_fail_target);
1983 self.chunk.patch_jump_to(skip_type, pre_scope_fail_target);
1984 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1985 }
1986 _ => {
1988 self.chunk.emit(Op::Dup, self.line);
1989 self.compile_node(&arm.pattern)?;
1990 self.chunk.emit(Op::Equal, self.line);
1991 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1992 self.chunk.emit(Op::Pop, self.line); if let Some(ref guard) = arm.guard {
1995 self.compile_node(guard)?;
1996 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1997 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1999 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
2001 self.end_scope();
2002 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2003 self.chunk.patch_jump(guard_skip);
2004 self.chunk.emit(Op::Pop, self.line); } else {
2006 self.begin_scope();
2007 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
2009 self.end_scope();
2010 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2011 }
2012 self.chunk.patch_jump(skip);
2013 self.chunk.emit(Op::Pop, self.line); }
2015 }
2016 }
2017 let msg_idx = self.chunk.add_constant(Constant::String(
2019 "No match arm matched the value".to_string(),
2020 ));
2021 self.chunk.emit(Op::Pop, self.line);
2022 self.chunk.emit_u16(Op::Constant, msg_idx, self.line);
2023 self.chunk.emit(Op::Throw, self.line);
2024 for j in end_jumps {
2025 self.chunk.patch_jump(j);
2026 }
2027 }
2028
2029 Node::RangeExpr {
2030 start,
2031 end,
2032 inclusive,
2033 } => {
2034 let name_idx = self
2036 .chunk
2037 .add_constant(Constant::String("__range__".to_string()));
2038 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2039 self.compile_node(start)?;
2040 self.compile_node(end)?;
2041 if *inclusive {
2042 self.chunk.emit(Op::True, self.line);
2043 } else {
2044 self.chunk.emit(Op::False, self.line);
2045 }
2046 self.chunk.emit_u8(Op::Call, 3, self.line);
2047 }
2048
2049 Node::GuardStmt {
2050 condition,
2051 else_body,
2052 } => {
2053 self.compile_node(condition)?;
2056 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2057 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_block(else_body)?;
2060 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
2062 self.chunk.emit(Op::Pop, self.line);
2063 }
2064 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2065 self.chunk.patch_jump(skip_jump);
2066 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
2068 self.chunk.emit(Op::Nil, self.line);
2069 }
2070
2071 Node::RequireStmt { condition, message } => {
2072 self.compile_node(condition)?;
2073 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2074 self.chunk.emit(Op::Pop, self.line);
2075 if let Some(message) = message {
2076 self.compile_node(message)?;
2077 } else {
2078 let idx = self
2079 .chunk
2080 .add_constant(Constant::String("require condition failed".to_string()));
2081 self.chunk.emit_u16(Op::Constant, idx, self.line);
2082 }
2083 self.chunk.emit(Op::Throw, self.line);
2084 self.chunk.patch_jump(ok_jump);
2085 self.chunk.emit(Op::Pop, self.line);
2086 }
2087
2088 Node::Block(stmts) => {
2089 self.compile_scoped_block(stmts)?;
2090 }
2091
2092 Node::DeadlineBlock { duration, body } => {
2093 self.compile_node(duration)?;
2094 self.chunk.emit(Op::DeadlineSetup, self.line);
2095 self.compile_scoped_block(body)?;
2096 self.chunk.emit(Op::DeadlineEnd, self.line);
2097 }
2098
2099 Node::MutexBlock { body } => {
2100 self.begin_scope();
2102 for sn in body {
2103 self.compile_node(sn)?;
2104 if Self::produces_value(&sn.node) {
2105 self.chunk.emit(Op::Pop, self.line);
2106 }
2107 }
2108 self.chunk.emit(Op::Nil, self.line);
2109 self.end_scope();
2110 }
2111
2112 Node::DeferStmt { body } => {
2113 self.finally_bodies.push(body.clone());
2115 self.chunk.emit(Op::Nil, self.line);
2116 }
2117
2118 Node::YieldExpr { value } => {
2119 if let Some(val) = value {
2120 self.compile_node(val)?;
2121 } else {
2122 self.chunk.emit(Op::Nil, self.line);
2123 }
2124 self.chunk.emit(Op::Yield, self.line);
2125 }
2126
2127 Node::EnumConstruct {
2128 enum_name,
2129 variant,
2130 args,
2131 } => {
2132 for arg in args {
2134 self.compile_node(arg)?;
2135 }
2136 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
2137 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
2138 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
2140 let hi = (var_idx >> 8) as u8;
2141 let lo = var_idx as u8;
2142 self.chunk.code.push(hi);
2143 self.chunk.code.push(lo);
2144 self.chunk.lines.push(self.line);
2145 self.chunk.columns.push(self.column);
2146 self.chunk.lines.push(self.line);
2147 self.chunk.columns.push(self.column);
2148 let fc = args.len() as u16;
2149 let fhi = (fc >> 8) as u8;
2150 let flo = fc as u8;
2151 self.chunk.code.push(fhi);
2152 self.chunk.code.push(flo);
2153 self.chunk.lines.push(self.line);
2154 self.chunk.columns.push(self.column);
2155 self.chunk.lines.push(self.line);
2156 self.chunk.columns.push(self.column);
2157 }
2158
2159 Node::StructConstruct {
2160 struct_name,
2161 fields,
2162 } => {
2163 let make_idx = self
2165 .chunk
2166 .add_constant(Constant::String("__make_struct".to_string()));
2167 let struct_name_idx = self
2168 .chunk
2169 .add_constant(Constant::String(struct_name.clone()));
2170 self.chunk.emit_u16(Op::Constant, make_idx, self.line);
2171 self.chunk
2172 .emit_u16(Op::Constant, struct_name_idx, self.line);
2173
2174 for entry in fields {
2175 self.compile_node(&entry.key)?;
2176 self.compile_node(&entry.value)?;
2177 }
2178 self.chunk
2179 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
2180 self.chunk.emit_u8(Op::Call, 2, self.line);
2181 }
2182
2183 Node::ImportDecl { path } => {
2184 let idx = self.chunk.add_constant(Constant::String(path.clone()));
2185 self.chunk.emit_u16(Op::Import, idx, self.line);
2186 }
2187
2188 Node::SelectiveImport { names, path } => {
2189 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
2190 let names_str = names.join(",");
2191 let names_idx = self.chunk.add_constant(Constant::String(names_str));
2192 self.chunk
2193 .emit_u16(Op::SelectiveImport, path_idx, self.line);
2194 let hi = (names_idx >> 8) as u8;
2195 let lo = names_idx as u8;
2196 self.chunk.code.push(hi);
2197 self.chunk.code.push(lo);
2198 self.chunk.lines.push(self.line);
2199 self.chunk.columns.push(self.column);
2200 self.chunk.lines.push(self.line);
2201 self.chunk.columns.push(self.column);
2202 }
2203
2204 Node::TryOperator { operand } => {
2205 self.compile_node(operand)?;
2206 self.chunk.emit(Op::TryUnwrap, self.line);
2207 }
2208
2209 Node::ImplBlock { type_name, methods } => {
2210 for method_sn in methods {
2213 if let Node::FnDecl {
2214 name, params, body, ..
2215 } = &method_sn.node
2216 {
2217 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
2219 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2220
2221 let mut fn_compiler = Compiler::new();
2223 fn_compiler.enum_names = self.enum_names.clone();
2224 fn_compiler.emit_default_preamble(params)?;
2225 fn_compiler.emit_type_checks(params);
2226 fn_compiler.compile_block(body)?;
2227 fn_compiler.chunk.emit(Op::Nil, self.line);
2228 fn_compiler.chunk.emit(Op::Return, self.line);
2229
2230 let func = CompiledFunction {
2231 name: format!("{}.{}", type_name, name),
2232 params: TypedParam::names(params),
2233 default_start: TypedParam::default_start(params),
2234 chunk: fn_compiler.chunk,
2235 is_generator: false,
2236 has_rest_param: false,
2237 };
2238 let fn_idx = self.chunk.functions.len();
2239 self.chunk.functions.push(func);
2240 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2241 }
2242 }
2243 let method_count = methods
2244 .iter()
2245 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
2246 .count();
2247 self.chunk
2248 .emit_u16(Op::BuildDict, method_count as u16, self.line);
2249 let impl_name = format!("__impl_{}", type_name);
2250 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
2251 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2252 }
2253
2254 Node::StructDecl { name, .. } => {
2255 let mut fn_compiler = Compiler::new();
2257 fn_compiler.enum_names = self.enum_names.clone();
2258 let params = vec![TypedParam::untyped("__fields")];
2259 fn_compiler.emit_default_preamble(¶ms)?;
2260
2261 let make_idx = fn_compiler
2263 .chunk
2264 .add_constant(Constant::String("__make_struct".into()));
2265 fn_compiler
2266 .chunk
2267 .emit_u16(Op::Constant, make_idx, self.line);
2268 let sname_idx = fn_compiler
2269 .chunk
2270 .add_constant(Constant::String(name.clone()));
2271 fn_compiler
2272 .chunk
2273 .emit_u16(Op::Constant, sname_idx, self.line);
2274 let fields_idx = fn_compiler
2275 .chunk
2276 .add_constant(Constant::String("__fields".into()));
2277 fn_compiler
2278 .chunk
2279 .emit_u16(Op::GetVar, fields_idx, self.line);
2280 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
2281 fn_compiler.chunk.emit(Op::Return, self.line);
2282
2283 let func = CompiledFunction {
2284 name: name.clone(),
2285 params: TypedParam::names(¶ms),
2286 default_start: None,
2287 chunk: fn_compiler.chunk,
2288 is_generator: false,
2289 has_rest_param: false,
2290 };
2291 let fn_idx = self.chunk.functions.len();
2292 self.chunk.functions.push(func);
2293 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2294 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
2295 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2296 }
2297
2298 Node::Pipeline { .. }
2300 | Node::OverrideDecl { .. }
2301 | Node::TypeDecl { .. }
2302 | Node::EnumDecl { .. }
2303 | Node::InterfaceDecl { .. } => {
2304 self.chunk.emit(Op::Nil, self.line);
2305 }
2306
2307 Node::TryCatch {
2308 body,
2309 error_var,
2310 error_type,
2311 catch_body,
2312 finally_body,
2313 } => {
2314 let type_name = error_type.as_ref().and_then(|te| {
2316 if let harn_parser::TypeExpr::Named(name) = te {
2317 Some(name.clone())
2318 } else {
2319 None
2320 }
2321 });
2322
2323 let type_name_idx = if let Some(ref tn) = type_name {
2324 self.chunk.add_constant(Constant::String(tn.clone()))
2325 } else {
2326 self.chunk.add_constant(Constant::String(String::new()))
2327 };
2328
2329 let has_catch = !catch_body.is_empty() || error_var.is_some();
2330 let has_finally = finally_body.is_some();
2331
2332 if has_catch && has_finally {
2333 let finally_body = finally_body.as_ref().unwrap();
2335
2336 self.finally_bodies.push(finally_body.clone());
2338
2339 self.handler_depth += 1;
2341 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2342 self.emit_type_name_extra(type_name_idx);
2343
2344 self.compile_try_body(body)?;
2346
2347 self.handler_depth -= 1;
2349 self.chunk.emit(Op::PopHandler, self.line);
2350 self.compile_finally_inline(finally_body)?;
2351 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2352
2353 self.chunk.patch_jump(catch_jump);
2355 self.begin_scope();
2356 self.compile_catch_binding(error_var)?;
2357
2358 self.handler_depth += 1;
2360 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2361 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2362 self.emit_type_name_extra(empty_type);
2363
2364 self.compile_try_body(catch_body)?;
2366
2367 self.handler_depth -= 1;
2369 self.chunk.emit(Op::PopHandler, self.line);
2370 self.compile_finally_inline(finally_body)?;
2371 self.end_scope();
2372 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
2373
2374 self.chunk.patch_jump(rethrow_jump);
2376 self.compile_rethrow_with_finally(finally_body)?;
2377 self.end_scope();
2378
2379 self.chunk.patch_jump(end_jump);
2380 self.chunk.patch_jump(end_jump2);
2381
2382 self.finally_bodies.pop();
2383 } else if has_finally {
2384 let finally_body = finally_body.as_ref().unwrap();
2386
2387 self.finally_bodies.push(finally_body.clone());
2388
2389 self.handler_depth += 1;
2391 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2392 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2393 self.emit_type_name_extra(empty_type);
2394
2395 self.compile_try_body(body)?;
2397
2398 self.handler_depth -= 1;
2400 self.chunk.emit(Op::PopHandler, self.line);
2401 self.compile_finally_inline(finally_body)?;
2402 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2403
2404 self.chunk.patch_jump(error_jump);
2406 self.compile_rethrow_with_finally(finally_body)?;
2407
2408 self.chunk.patch_jump(end_jump);
2409
2410 self.finally_bodies.pop();
2411 } else {
2412 self.handler_depth += 1;
2416 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2417 self.emit_type_name_extra(type_name_idx);
2418
2419 self.compile_try_body(body)?;
2421
2422 self.handler_depth -= 1;
2424 self.chunk.emit(Op::PopHandler, self.line);
2425 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2426
2427 self.chunk.patch_jump(catch_jump);
2429 self.begin_scope();
2430 self.compile_catch_binding(error_var)?;
2431
2432 self.compile_try_body(catch_body)?;
2434 self.end_scope();
2435
2436 self.chunk.patch_jump(end_jump);
2438 }
2439 }
2440
2441 Node::TryExpr { body } => {
2442 self.handler_depth += 1;
2446 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2447 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2448 self.emit_type_name_extra(empty_type);
2449
2450 self.compile_try_body(body)?;
2452
2453 self.handler_depth -= 1;
2455 self.chunk.emit(Op::PopHandler, self.line);
2456
2457 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
2459 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
2460 self.chunk.emit(Op::Swap, self.line);
2461 self.chunk.emit_u8(Op::Call, 1, self.line);
2462
2463 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2465
2466 self.chunk.patch_jump(catch_jump);
2468
2469 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
2471 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
2472 self.chunk.emit(Op::Swap, self.line);
2473 self.chunk.emit_u8(Op::Call, 1, self.line);
2474
2475 self.chunk.patch_jump(end_jump);
2477 }
2478
2479 Node::Retry { count, body } => {
2480 self.compile_node(count)?;
2482 let counter_name = "__retry_counter__";
2483 let counter_idx = self
2484 .chunk
2485 .add_constant(Constant::String(counter_name.to_string()));
2486 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
2487
2488 self.chunk.emit(Op::Nil, self.line);
2490 let err_name = "__retry_last_error__";
2491 let err_idx = self
2492 .chunk
2493 .add_constant(Constant::String(err_name.to_string()));
2494 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
2495
2496 let loop_start = self.chunk.current_offset();
2498
2499 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2501 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2503 let hi = (empty_type >> 8) as u8;
2504 let lo = empty_type as u8;
2505 self.chunk.code.push(hi);
2506 self.chunk.code.push(lo);
2507 self.chunk.lines.push(self.line);
2508 self.chunk.columns.push(self.column);
2509 self.chunk.lines.push(self.line);
2510 self.chunk.columns.push(self.column);
2511
2512 self.compile_block(body)?;
2514
2515 self.chunk.emit(Op::PopHandler, self.line);
2517 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2518
2519 self.chunk.patch_jump(catch_jump);
2521 self.chunk.emit(Op::Dup, self.line);
2523 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
2524 self.chunk.emit(Op::Pop, self.line);
2526
2527 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
2529 let one_idx = self.chunk.add_constant(Constant::Int(1));
2530 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
2531 self.chunk.emit(Op::Sub, self.line);
2532 self.chunk.emit(Op::Dup, self.line);
2533 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
2534
2535 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2537 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2538 self.chunk.emit(Op::Greater, self.line);
2539 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2540 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
2542
2543 self.chunk.patch_jump(retry_jump);
2545 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
2547 self.chunk.emit(Op::Throw, self.line);
2548
2549 self.chunk.patch_jump(end_jump);
2550 self.chunk.emit(Op::Nil, self.line);
2552 }
2553
2554 Node::Parallel {
2555 mode,
2556 expr,
2557 variable,
2558 body,
2559 options,
2560 } => {
2561 let cap_expr = options
2566 .iter()
2567 .find(|(key, _)| key == "max_concurrent")
2568 .map(|(_, value)| value);
2569 if let Some(cap_expr) = cap_expr {
2570 self.compile_node(cap_expr)?;
2571 } else {
2572 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2573 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2574 }
2575 self.compile_node(expr)?;
2576 let mut fn_compiler = Compiler::new();
2577 fn_compiler.enum_names = self.enum_names.clone();
2578 fn_compiler.compile_block(body)?;
2579 fn_compiler.chunk.emit(Op::Return, self.line);
2580 let (fn_name, params) = match mode {
2581 ParallelMode::Count => (
2582 "<parallel>",
2583 vec![variable.clone().unwrap_or_else(|| "__i__".to_string())],
2584 ),
2585 ParallelMode::Each => (
2586 "<parallel_each>",
2587 vec![variable.clone().unwrap_or_else(|| "__item__".to_string())],
2588 ),
2589 ParallelMode::Settle => (
2590 "<parallel_settle>",
2591 vec![variable.clone().unwrap_or_else(|| "__item__".to_string())],
2592 ),
2593 };
2594 let func = CompiledFunction {
2595 name: fn_name.to_string(),
2596 params,
2597 default_start: None,
2598 chunk: fn_compiler.chunk,
2599 is_generator: false,
2600 has_rest_param: false,
2601 };
2602 let fn_idx = self.chunk.functions.len();
2603 self.chunk.functions.push(func);
2604 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2605 let op = match mode {
2606 ParallelMode::Count => Op::Parallel,
2607 ParallelMode::Each => Op::ParallelMap,
2608 ParallelMode::Settle => Op::ParallelSettle,
2609 };
2610 self.chunk.emit(op, self.line);
2611 }
2612
2613 Node::SpawnExpr { body } => {
2614 let mut fn_compiler = Compiler::new();
2615 fn_compiler.enum_names = self.enum_names.clone();
2616 fn_compiler.compile_block(body)?;
2617 fn_compiler.chunk.emit(Op::Return, self.line);
2618 let func = CompiledFunction {
2619 name: "<spawn>".to_string(),
2620 params: vec![],
2621 default_start: None,
2622 chunk: fn_compiler.chunk,
2623 is_generator: false,
2624 has_rest_param: false,
2625 };
2626 let fn_idx = self.chunk.functions.len();
2627 self.chunk.functions.push(func);
2628 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2629 self.chunk.emit(Op::Spawn, self.line);
2630 }
2631 Node::SelectExpr {
2632 cases,
2633 timeout,
2634 default_body,
2635 } => {
2636 let builtin_name = if timeout.is_some() {
2643 "__select_timeout"
2644 } else if default_body.is_some() {
2645 "__select_try"
2646 } else {
2647 "__select_list"
2648 };
2649
2650 let name_idx = self
2652 .chunk
2653 .add_constant(Constant::String(builtin_name.into()));
2654 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2655
2656 for case in cases {
2658 self.compile_node(&case.channel)?;
2659 }
2660 self.chunk
2661 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2662
2663 if let Some((duration_expr, _)) = timeout {
2665 self.compile_node(duration_expr)?;
2666 self.chunk.emit_u8(Op::Call, 2, self.line);
2667 } else {
2668 self.chunk.emit_u8(Op::Call, 1, self.line);
2669 }
2670
2671 self.temp_counter += 1;
2673 let result_name = format!("__sel_result_{}__", self.temp_counter);
2674 let result_idx = self
2675 .chunk
2676 .add_constant(Constant::String(result_name.clone()));
2677 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2678
2679 let mut end_jumps = Vec::new();
2681
2682 for (i, case) in cases.iter().enumerate() {
2683 let get_r = self
2684 .chunk
2685 .add_constant(Constant::String(result_name.clone()));
2686 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2687 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2688 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2689 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2690 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2691 self.chunk.emit(Op::Equal, self.line);
2692 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2693 self.chunk.emit(Op::Pop, self.line);
2694 self.begin_scope();
2695
2696 let get_r2 = self
2698 .chunk
2699 .add_constant(Constant::String(result_name.clone()));
2700 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2701 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2702 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2703 let var_idx = self
2704 .chunk
2705 .add_constant(Constant::String(case.variable.clone()));
2706 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2707
2708 self.compile_try_body(&case.body)?;
2709 self.end_scope();
2710 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2711 self.chunk.patch_jump(skip);
2712 self.chunk.emit(Op::Pop, self.line);
2713 }
2714
2715 if let Some((_, ref timeout_body)) = timeout {
2717 self.compile_try_body(timeout_body)?;
2718 } else if let Some(ref def_body) = default_body {
2719 self.compile_try_body(def_body)?;
2720 } else {
2721 self.chunk.emit(Op::Nil, self.line);
2722 }
2723
2724 for ej in end_jumps {
2725 self.chunk.patch_jump(ej);
2726 }
2727 }
2728 Node::Spread(_) => {
2729 return Err(CompileError {
2730 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2731 line: self.line,
2732 });
2733 }
2734 }
2735 Ok(())
2736 }
2737
2738 fn compile_destructuring(
2742 &mut self,
2743 pattern: &BindingPattern,
2744 is_mutable: bool,
2745 ) -> Result<(), CompileError> {
2746 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2747 match pattern {
2748 BindingPattern::Identifier(name) => {
2749 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2751 self.chunk.emit_u16(def_op, idx, self.line);
2752 }
2753 BindingPattern::Dict(fields) => {
2754 self.chunk.emit(Op::Dup, self.line);
2757 let assert_idx = self
2758 .chunk
2759 .add_constant(Constant::String("__assert_dict".into()));
2760 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2761 self.chunk.emit(Op::Swap, self.line);
2762 self.chunk.emit_u8(Op::Call, 1, self.line);
2763 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2768 let rest_field = fields.iter().find(|f| f.is_rest);
2769
2770 for field in &non_rest {
2771 self.chunk.emit(Op::Dup, self.line);
2772 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2773 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2774 self.chunk.emit(Op::Subscript, self.line);
2775 if let Some(default_expr) = &field.default_value {
2777 self.chunk.emit(Op::Dup, self.line);
2778 self.chunk.emit(Op::Nil, self.line);
2779 self.chunk.emit(Op::NotEqual, self.line);
2780 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2781 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2784 let end = self.chunk.emit_jump(Op::Jump, self.line);
2785 self.chunk.patch_jump(skip_default);
2786 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2788 }
2789 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2790 let name_idx = self
2791 .chunk
2792 .add_constant(Constant::String(binding_name.to_string()));
2793 self.chunk.emit_u16(def_op, name_idx, self.line);
2794 }
2795
2796 if let Some(rest) = rest_field {
2797 let fn_idx = self
2800 .chunk
2801 .add_constant(Constant::String("__dict_rest".into()));
2802 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2803 self.chunk.emit(Op::Swap, self.line);
2805 for field in &non_rest {
2807 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2808 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2809 }
2810 self.chunk
2811 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2812 self.chunk.emit_u8(Op::Call, 2, self.line);
2814 let rest_name = &rest.key;
2815 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2816 self.chunk.emit_u16(def_op, rest_idx, self.line);
2817 } else {
2818 self.chunk.emit(Op::Pop, self.line);
2820 }
2821 }
2822 BindingPattern::Pair(first_name, second_name) => {
2823 self.chunk.emit(Op::Dup, self.line);
2826 let first_key_idx = self
2827 .chunk
2828 .add_constant(Constant::String("first".to_string()));
2829 self.chunk
2830 .emit_u16(Op::GetProperty, first_key_idx, self.line);
2831 let first_name_idx = self
2832 .chunk
2833 .add_constant(Constant::String(first_name.clone()));
2834 self.chunk.emit_u16(def_op, first_name_idx, self.line);
2835
2836 let second_key_idx = self
2838 .chunk
2839 .add_constant(Constant::String("second".to_string()));
2840 self.chunk
2841 .emit_u16(Op::GetProperty, second_key_idx, self.line);
2842 let second_name_idx = self
2843 .chunk
2844 .add_constant(Constant::String(second_name.clone()));
2845 self.chunk.emit_u16(def_op, second_name_idx, self.line);
2846 }
2850 BindingPattern::List(elements) => {
2851 self.chunk.emit(Op::Dup, self.line);
2854 let assert_idx = self
2855 .chunk
2856 .add_constant(Constant::String("__assert_list".into()));
2857 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2858 self.chunk.emit(Op::Swap, self.line);
2859 self.chunk.emit_u8(Op::Call, 1, self.line);
2860 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2863 let rest_elem = elements.iter().find(|e| e.is_rest);
2864
2865 for (i, elem) in non_rest.iter().enumerate() {
2866 self.chunk.emit(Op::Dup, self.line);
2867 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2868 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2869 self.chunk.emit(Op::Subscript, self.line);
2870 if let Some(default_expr) = &elem.default_value {
2872 self.chunk.emit(Op::Dup, self.line);
2873 self.chunk.emit(Op::Nil, self.line);
2874 self.chunk.emit(Op::NotEqual, self.line);
2875 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2876 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2879 let end = self.chunk.emit_jump(Op::Jump, self.line);
2880 self.chunk.patch_jump(skip_default);
2881 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2883 }
2884 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2885 self.chunk.emit_u16(def_op, name_idx, self.line);
2886 }
2887
2888 if let Some(rest) = rest_elem {
2889 let start_idx = self
2893 .chunk
2894 .add_constant(Constant::Int(non_rest.len() as i64));
2895 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2896 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2898 let rest_name_idx =
2899 self.chunk.add_constant(Constant::String(rest.name.clone()));
2900 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2901 } else {
2902 self.chunk.emit(Op::Pop, self.line);
2904 }
2905 }
2906 }
2907 Ok(())
2908 }
2909
2910 fn produces_value(node: &Node) -> bool {
2912 match node {
2913 Node::LetBinding { .. }
2915 | Node::VarBinding { .. }
2916 | Node::Assignment { .. }
2917 | Node::ReturnStmt { .. }
2918 | Node::FnDecl { .. }
2919 | Node::ToolDecl { .. }
2920 | Node::ImplBlock { .. }
2921 | Node::StructDecl { .. }
2922 | Node::EnumDecl { .. }
2923 | Node::InterfaceDecl { .. }
2924 | Node::TypeDecl { .. }
2925 | Node::ThrowStmt { .. }
2926 | Node::BreakStmt
2927 | Node::ContinueStmt
2928 | Node::RequireStmt { .. }
2929 | Node::DeferStmt { .. } => false,
2930 Node::TryCatch { .. }
2932 | Node::TryExpr { .. }
2933 | Node::Retry { .. }
2934 | Node::GuardStmt { .. }
2935 | Node::DeadlineBlock { .. }
2936 | Node::MutexBlock { .. }
2937 | Node::Spread(_) => true,
2938 _ => true,
2940 }
2941 }
2942}
2943
2944impl Compiler {
2945 pub fn compile_fn_body(
2958 &mut self,
2959 params: &[TypedParam],
2960 body: &[SNode],
2961 source_file: Option<String>,
2962 ) -> Result<CompiledFunction, CompileError> {
2963 let mut fn_compiler = Compiler::new();
2964 fn_compiler.enum_names = self.enum_names.clone();
2965 fn_compiler.emit_default_preamble(params)?;
2966 fn_compiler.emit_type_checks(params);
2967 let is_gen = body_contains_yield(body);
2968 fn_compiler.compile_block(body)?;
2969 fn_compiler.chunk.emit(Op::Nil, 0);
2970 fn_compiler.chunk.emit(Op::Return, 0);
2971 fn_compiler.chunk.source_file = source_file;
2972 Ok(CompiledFunction {
2973 name: String::new(),
2974 params: TypedParam::names(params),
2975 default_start: TypedParam::default_start(params),
2976 chunk: fn_compiler.chunk,
2977 is_generator: is_gen,
2978 has_rest_param: false,
2979 })
2980 }
2981
2982 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2984 self.begin_scope();
2985 if body.is_empty() {
2986 self.chunk.emit(Op::Nil, self.line);
2987 } else {
2988 self.compile_block(body)?;
2989 if !Self::produces_value(&body.last().unwrap().node) {
2990 self.chunk.emit(Op::Nil, self.line);
2991 }
2992 }
2993 self.end_scope();
2994 Ok(())
2995 }
2996
2997 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2999 match op {
3000 "+" => self.chunk.emit(Op::Add, self.line),
3001 "-" => self.chunk.emit(Op::Sub, self.line),
3002 "*" => self.chunk.emit(Op::Mul, self.line),
3003 "/" => self.chunk.emit(Op::Div, self.line),
3004 "%" => self.chunk.emit(Op::Mod, self.line),
3005 _ => {
3006 return Err(CompileError {
3007 message: format!("Unknown compound operator: {op}"),
3008 line: self.line,
3009 })
3010 }
3011 }
3012 Ok(())
3013 }
3014
3015 fn root_var_name(&self, node: &SNode) -> Option<String> {
3017 match &node.node {
3018 Node::Identifier(name) => Some(name.clone()),
3019 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
3020 self.root_var_name(object)
3021 }
3022 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
3023 _ => None,
3024 }
3025 }
3026
3027 fn compile_top_level_declarations(&mut self, program: &[SNode]) -> Result<(), CompileError> {
3028 for sn in program {
3036 if matches!(&sn.node, Node::LetBinding { .. } | Node::VarBinding { .. }) {
3037 self.compile_node(sn)?;
3038 }
3039 }
3040 for sn in program {
3044 if matches!(
3045 &sn.node,
3046 Node::FnDecl { .. }
3047 | Node::ToolDecl { .. }
3048 | Node::ImplBlock { .. }
3049 | Node::StructDecl { .. }
3050 | Node::EnumDecl { .. }
3051 | Node::InterfaceDecl { .. }
3052 | Node::TypeDecl { .. }
3053 ) {
3054 self.compile_node(sn)?;
3055 }
3056 }
3057 Ok(())
3058 }
3059}
3060
3061impl Compiler {
3062 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
3064 for sn in nodes {
3065 match &sn.node {
3066 Node::EnumDecl { name, .. } => {
3067 names.insert(name.clone());
3068 }
3069 Node::Pipeline { body, .. } => {
3070 Self::collect_enum_names(body, names);
3071 }
3072 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
3073 Self::collect_enum_names(body, names);
3074 }
3075 Node::Block(stmts) => {
3076 Self::collect_enum_names(stmts, names);
3077 }
3078 _ => {}
3079 }
3080 }
3081 }
3082
3083 fn collect_interface_methods(
3084 nodes: &[SNode],
3085 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
3086 ) {
3087 for sn in nodes {
3088 match &sn.node {
3089 Node::InterfaceDecl { name, methods, .. } => {
3090 let method_names: Vec<String> =
3091 methods.iter().map(|m| m.name.clone()).collect();
3092 interfaces.insert(name.clone(), method_names);
3093 }
3094 Node::Pipeline { body, .. }
3095 | Node::FnDecl { body, .. }
3096 | Node::ToolDecl { body, .. } => {
3097 Self::collect_interface_methods(body, interfaces);
3098 }
3099 Node::Block(stmts) => {
3100 Self::collect_interface_methods(stmts, interfaces);
3101 }
3102 _ => {}
3103 }
3104 }
3105 }
3106}
3107
3108impl Default for Compiler {
3109 fn default() -> Self {
3110 Self::new()
3111 }
3112}
3113
3114fn body_contains_yield(nodes: &[SNode]) -> bool {
3116 nodes.iter().any(|sn| node_contains_yield(&sn.node))
3117}
3118
3119fn node_contains_yield(node: &Node) -> bool {
3120 match node {
3121 Node::YieldExpr { .. } => true,
3122 Node::FnDecl { .. } | Node::Closure { .. } => false,
3125 Node::Block(stmts) => body_contains_yield(stmts),
3126 Node::IfElse {
3127 condition,
3128 then_body,
3129 else_body,
3130 } => {
3131 node_contains_yield(&condition.node)
3132 || body_contains_yield(then_body)
3133 || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
3134 }
3135 Node::WhileLoop { condition, body } => {
3136 node_contains_yield(&condition.node) || body_contains_yield(body)
3137 }
3138 Node::ForIn { iterable, body, .. } => {
3139 node_contains_yield(&iterable.node) || body_contains_yield(body)
3140 }
3141 Node::TryCatch {
3142 body, catch_body, ..
3143 } => body_contains_yield(body) || body_contains_yield(catch_body),
3144 Node::TryExpr { body } => body_contains_yield(body),
3145 _ => false,
3146 }
3147}
3148
3149fn contains_pipe_placeholder(node: &SNode) -> bool {
3151 match &node.node {
3152 Node::Identifier(name) if name == "_" => true,
3153 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
3154 Node::MethodCall { object, args, .. } => {
3155 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
3156 }
3157 Node::BinaryOp { left, right, .. } => {
3158 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
3159 }
3160 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
3161 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
3162 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
3163 Node::SubscriptAccess { object, index } => {
3164 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
3165 }
3166 _ => false,
3167 }
3168}
3169
3170fn replace_pipe_placeholder(node: &SNode) -> SNode {
3172 let new_node = match &node.node {
3173 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
3174 Node::FunctionCall { name, args } => Node::FunctionCall {
3175 name: name.clone(),
3176 args: args.iter().map(replace_pipe_placeholder).collect(),
3177 },
3178 Node::MethodCall {
3179 object,
3180 method,
3181 args,
3182 } => Node::MethodCall {
3183 object: Box::new(replace_pipe_placeholder(object)),
3184 method: method.clone(),
3185 args: args.iter().map(replace_pipe_placeholder).collect(),
3186 },
3187 Node::BinaryOp { op, left, right } => Node::BinaryOp {
3188 op: op.clone(),
3189 left: Box::new(replace_pipe_placeholder(left)),
3190 right: Box::new(replace_pipe_placeholder(right)),
3191 },
3192 Node::UnaryOp { op, operand } => Node::UnaryOp {
3193 op: op.clone(),
3194 operand: Box::new(replace_pipe_placeholder(operand)),
3195 },
3196 Node::ListLiteral(items) => {
3197 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
3198 }
3199 Node::PropertyAccess { object, property } => Node::PropertyAccess {
3200 object: Box::new(replace_pipe_placeholder(object)),
3201 property: property.clone(),
3202 },
3203 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
3204 object: Box::new(replace_pipe_placeholder(object)),
3205 index: Box::new(replace_pipe_placeholder(index)),
3206 },
3207 _ => return node.clone(),
3208 };
3209 SNode::new(new_node, node.span)
3210}
3211
3212#[cfg(test)]
3213mod tests {
3214 use super::*;
3215 use harn_lexer::Lexer;
3216 use harn_parser::Parser;
3217
3218 fn compile_source(source: &str) -> Chunk {
3219 let mut lexer = Lexer::new(source);
3220 let tokens = lexer.tokenize().unwrap();
3221 let mut parser = Parser::new(tokens);
3222 let program = parser.parse().unwrap();
3223 Compiler::new().compile(&program).unwrap()
3224 }
3225
3226 #[test]
3227 fn test_compile_arithmetic() {
3228 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
3229 assert!(!chunk.code.is_empty());
3230 assert!(chunk.constants.contains(&Constant::Int(2)));
3232 assert!(chunk.constants.contains(&Constant::Int(3)));
3233 }
3234
3235 #[test]
3236 fn test_compile_function_call() {
3237 let chunk = compile_source("pipeline test(task) { log(42) }");
3238 let disasm = chunk.disassemble("test");
3239 assert!(disasm.contains("CALL"));
3240 }
3241
3242 #[test]
3243 fn test_compile_if_else() {
3244 let chunk =
3245 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
3246 let disasm = chunk.disassemble("test");
3247 assert!(disasm.contains("JUMP_IF_FALSE"));
3248 assert!(disasm.contains("JUMP"));
3249 }
3250
3251 #[test]
3252 fn test_compile_while() {
3253 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
3254 let disasm = chunk.disassemble("test");
3255 assert!(disasm.contains("JUMP_IF_FALSE"));
3256 assert!(disasm.contains("JUMP"));
3258 }
3259
3260 #[test]
3261 fn test_compile_closure() {
3262 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
3263 assert!(!chunk.functions.is_empty());
3264 assert_eq!(chunk.functions[0].params, vec!["x"]);
3265 }
3266
3267 #[test]
3268 fn test_compile_list() {
3269 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
3270 let disasm = chunk.disassemble("test");
3271 assert!(disasm.contains("BUILD_LIST"));
3272 }
3273
3274 #[test]
3275 fn test_compile_dict() {
3276 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
3277 let disasm = chunk.disassemble("test");
3278 assert!(disasm.contains("BUILD_DICT"));
3279 }
3280
3281 #[test]
3282 fn test_disassemble() {
3283 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
3284 let disasm = chunk.disassemble("test");
3285 assert!(disasm.contains("CONSTANT"));
3287 assert!(disasm.contains("ADD"));
3288 assert!(disasm.contains("CALL"));
3289 }
3290}