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