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