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::Pow, self.line),
707 "==" => self.chunk.emit(Op::Equal, self.line),
708 "!=" => self.chunk.emit(Op::NotEqual, self.line),
709 "<" => self.chunk.emit(Op::Less, self.line),
710 ">" => self.chunk.emit(Op::Greater, self.line),
711 "<=" => self.chunk.emit(Op::LessEqual, self.line),
712 ">=" => self.chunk.emit(Op::GreaterEqual, self.line),
713 "in" => self.chunk.emit(Op::Contains, self.line),
714 "not_in" => {
715 self.chunk.emit(Op::Contains, self.line);
716 self.chunk.emit(Op::Not, self.line);
717 }
718 _ => {
719 return Err(CompileError {
720 message: format!("Unknown operator: {op}"),
721 line: self.line,
722 })
723 }
724 }
725 }
726
727 Node::UnaryOp { op, operand } => {
728 self.compile_node(operand)?;
729 match op.as_str() {
730 "-" => self.chunk.emit(Op::Negate, self.line),
731 "!" => self.chunk.emit(Op::Not, self.line),
732 _ => {}
733 }
734 }
735
736 Node::Ternary {
737 condition,
738 true_expr,
739 false_expr,
740 } => {
741 self.compile_node(condition)?;
742 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
743 self.chunk.emit(Op::Pop, self.line);
744 self.compile_node(true_expr)?;
745 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
746 self.chunk.patch_jump(else_jump);
747 self.chunk.emit(Op::Pop, self.line);
748 self.compile_node(false_expr)?;
749 self.chunk.patch_jump(end_jump);
750 }
751
752 Node::FunctionCall { name, args } => {
753 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
754 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
756 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
757
758 if has_spread {
759 self.chunk.emit_u16(Op::BuildList, 0, self.line);
762 let mut pending = 0u16;
763 for arg in args {
764 if let Node::Spread(inner) = &arg.node {
765 if pending > 0 {
766 self.chunk.emit_u16(Op::BuildList, pending, self.line);
767 self.chunk.emit(Op::Add, self.line);
768 pending = 0;
769 }
770 self.compile_node(inner)?;
771 self.chunk.emit(Op::Dup, self.line);
772 let assert_idx = self
773 .chunk
774 .add_constant(Constant::String("__assert_list".into()));
775 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
776 self.chunk.emit(Op::Swap, self.line);
777 self.chunk.emit_u8(Op::Call, 1, self.line);
778 self.chunk.emit(Op::Pop, self.line);
779 self.chunk.emit(Op::Add, self.line);
780 } else {
781 self.compile_node(arg)?;
782 pending += 1;
783 }
784 }
785 if pending > 0 {
786 self.chunk.emit_u16(Op::BuildList, pending, self.line);
787 self.chunk.emit(Op::Add, self.line);
788 }
789 self.chunk.emit(Op::CallSpread, self.line);
790 } else {
791 for arg in args {
793 self.compile_node(arg)?;
794 }
795 self.chunk.emit_u8(Op::Call, args.len() as u8, self.line);
796 }
797 }
798
799 Node::MethodCall {
800 object,
801 method,
802 args,
803 } => {
804 if let Node::Identifier(name) = &object.node {
806 if self.enum_names.contains(name) {
807 for arg in args {
809 self.compile_node(arg)?;
810 }
811 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
812 let var_idx = self.chunk.add_constant(Constant::String(method.clone()));
813 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
814 let hi = (var_idx >> 8) as u8;
815 let lo = var_idx as u8;
816 self.chunk.code.push(hi);
817 self.chunk.code.push(lo);
818 self.chunk.lines.push(self.line);
819 self.chunk.columns.push(self.column);
820 self.chunk.lines.push(self.line);
821 self.chunk.columns.push(self.column);
822 let fc = args.len() as u16;
823 let fhi = (fc >> 8) as u8;
824 let flo = fc as u8;
825 self.chunk.code.push(fhi);
826 self.chunk.code.push(flo);
827 self.chunk.lines.push(self.line);
828 self.chunk.columns.push(self.column);
829 self.chunk.lines.push(self.line);
830 self.chunk.columns.push(self.column);
831 return Ok(());
832 }
833 }
834 let has_spread = args.iter().any(|a| matches!(&a.node, Node::Spread(_)));
835 self.compile_node(object)?;
836 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
837 if has_spread {
838 self.chunk.emit_u16(Op::BuildList, 0, self.line);
840 let mut pending = 0u16;
841 for arg in args {
842 if let Node::Spread(inner) = &arg.node {
843 if pending > 0 {
844 self.chunk.emit_u16(Op::BuildList, pending, self.line);
845 self.chunk.emit(Op::Add, self.line);
846 pending = 0;
847 }
848 self.compile_node(inner)?;
849 self.chunk.emit(Op::Dup, self.line);
850 let assert_idx = self
851 .chunk
852 .add_constant(Constant::String("__assert_list".into()));
853 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
854 self.chunk.emit(Op::Swap, self.line);
855 self.chunk.emit_u8(Op::Call, 1, self.line);
856 self.chunk.emit(Op::Pop, self.line);
857 self.chunk.emit(Op::Add, self.line);
858 } else {
859 self.compile_node(arg)?;
860 pending += 1;
861 }
862 }
863 if pending > 0 {
864 self.chunk.emit_u16(Op::BuildList, pending, self.line);
865 self.chunk.emit(Op::Add, self.line);
866 }
867 self.chunk
868 .emit_u16(Op::MethodCallSpread, name_idx, self.line);
869 } else {
870 for arg in args {
871 self.compile_node(arg)?;
872 }
873 self.chunk
874 .emit_method_call(name_idx, args.len() as u8, self.line);
875 }
876 }
877
878 Node::OptionalMethodCall {
879 object,
880 method,
881 args,
882 } => {
883 self.compile_node(object)?;
884 for arg in args {
885 self.compile_node(arg)?;
886 }
887 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
888 self.chunk
889 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
890 }
891
892 Node::PropertyAccess { object, property } => {
893 if let Node::Identifier(name) = &object.node {
895 if self.enum_names.contains(name) {
896 let enum_idx = self.chunk.add_constant(Constant::String(name.clone()));
898 let var_idx = self.chunk.add_constant(Constant::String(property.clone()));
899 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
900 let hi = (var_idx >> 8) as u8;
901 let lo = var_idx as u8;
902 self.chunk.code.push(hi);
903 self.chunk.code.push(lo);
904 self.chunk.lines.push(self.line);
905 self.chunk.columns.push(self.column);
906 self.chunk.lines.push(self.line);
907 self.chunk.columns.push(self.column);
908 self.chunk.code.push(0);
910 self.chunk.code.push(0);
911 self.chunk.lines.push(self.line);
912 self.chunk.columns.push(self.column);
913 self.chunk.lines.push(self.line);
914 self.chunk.columns.push(self.column);
915 return Ok(());
916 }
917 }
918 self.compile_node(object)?;
919 let idx = self.chunk.add_constant(Constant::String(property.clone()));
920 self.chunk.emit_u16(Op::GetProperty, idx, self.line);
921 }
922
923 Node::OptionalPropertyAccess { object, property } => {
924 self.compile_node(object)?;
925 let idx = self.chunk.add_constant(Constant::String(property.clone()));
926 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
927 }
928
929 Node::SubscriptAccess { object, index } => {
930 self.compile_node(object)?;
931 self.compile_node(index)?;
932 self.chunk.emit(Op::Subscript, self.line);
933 }
934
935 Node::SliceAccess { object, start, end } => {
936 self.compile_node(object)?;
937 if let Some(s) = start {
938 self.compile_node(s)?;
939 } else {
940 self.chunk.emit(Op::Nil, self.line);
941 }
942 if let Some(e) = end {
943 self.compile_node(e)?;
944 } else {
945 self.chunk.emit(Op::Nil, self.line);
946 }
947 self.chunk.emit(Op::Slice, self.line);
948 }
949
950 Node::IfElse {
951 condition,
952 then_body,
953 else_body,
954 } => {
955 self.compile_node(condition)?;
956 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
957 self.chunk.emit(Op::Pop, self.line);
958 self.compile_scoped_block(then_body)?;
959 if let Some(else_body) = else_body {
960 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
961 self.chunk.patch_jump(else_jump);
962 self.chunk.emit(Op::Pop, self.line);
963 self.compile_scoped_block(else_body)?;
964 self.chunk.patch_jump(end_jump);
965 } else {
966 self.chunk.patch_jump(else_jump);
967 self.chunk.emit(Op::Pop, self.line);
968 self.chunk.emit(Op::Nil, self.line);
969 }
970 }
971
972 Node::WhileLoop { condition, body } => {
973 let loop_start = self.chunk.current_offset();
974 self.loop_stack.push(LoopContext {
975 start_offset: loop_start,
976 break_patches: Vec::new(),
977 has_iterator: false,
978 handler_depth: self.handler_depth,
979 finally_depth: self.finally_bodies.len(),
980 scope_depth: self.scope_depth,
981 });
982 self.compile_node(condition)?;
983 let exit_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
984 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_statements(body)?;
986 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
988 self.chunk.patch_jump(exit_jump);
989 self.chunk.emit(Op::Pop, self.line); let ctx = self.loop_stack.pop().unwrap();
992 for patch_pos in ctx.break_patches {
993 self.chunk.patch_jump(patch_pos);
994 }
995 self.chunk.emit(Op::Nil, self.line);
996 }
997
998 Node::ForIn {
999 pattern,
1000 iterable,
1001 body,
1002 } => {
1003 self.compile_node(iterable)?;
1005 self.chunk.emit(Op::IterInit, self.line);
1007 let loop_start = self.chunk.current_offset();
1008 self.loop_stack.push(LoopContext {
1009 start_offset: loop_start,
1010 break_patches: Vec::new(),
1011 has_iterator: true,
1012 handler_depth: self.handler_depth,
1013 finally_depth: self.finally_bodies.len(),
1014 scope_depth: self.scope_depth,
1015 });
1016 let exit_jump_pos = self.chunk.emit_jump(Op::IterNext, self.line);
1018 self.begin_scope();
1019 self.compile_destructuring(pattern, true)?;
1021 for sn in body {
1022 self.compile_node(sn)?;
1023 if Self::produces_value(&sn.node) {
1024 self.chunk.emit(Op::Pop, self.line);
1025 }
1026 }
1027 self.end_scope();
1028 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1030 self.chunk.patch_jump(exit_jump_pos);
1031 let ctx = self.loop_stack.pop().unwrap();
1033 for patch_pos in ctx.break_patches {
1034 self.chunk.patch_jump(patch_pos);
1035 }
1036 self.chunk.emit(Op::Nil, self.line);
1038 }
1039
1040 Node::ReturnStmt { value } => {
1041 let has_pending_finally = !self.finally_bodies.is_empty();
1042
1043 if has_pending_finally {
1044 if let Some(val) = value {
1047 self.compile_node(val)?;
1048 } else {
1049 self.chunk.emit(Op::Nil, self.line);
1050 }
1051 self.temp_counter += 1;
1052 let temp_name = format!("__return_val_{}__", self.temp_counter);
1053 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
1054 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
1055 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
1057 for fb in &finallys {
1058 self.compile_finally_inline(fb)?;
1059 }
1060 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
1061 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
1062 self.chunk.emit(Op::Return, self.line);
1063 } else {
1064 if let Some(val) = value {
1066 if let Node::FunctionCall { name, args } = &val.node {
1067 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1068 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
1069 for arg in args {
1070 self.compile_node(arg)?;
1071 }
1072 self.chunk
1073 .emit_u8(Op::TailCall, args.len() as u8, self.line);
1074 } else if let Node::BinaryOp { op, left, right } = &val.node {
1075 if op == "|>" {
1076 self.compile_node(left)?;
1077 self.compile_node(right)?;
1078 self.chunk.emit(Op::Swap, self.line);
1079 self.chunk.emit_u8(Op::TailCall, 1, self.line);
1080 } else {
1081 self.compile_node(val)?;
1082 }
1083 } else {
1084 self.compile_node(val)?;
1085 }
1086 } else {
1087 self.chunk.emit(Op::Nil, self.line);
1088 }
1089 self.chunk.emit(Op::Return, self.line);
1090 }
1091 }
1092
1093 Node::BreakStmt => {
1094 if self.loop_stack.is_empty() {
1095 return Err(CompileError {
1096 message: "break outside of loop".to_string(),
1097 line: self.line,
1098 });
1099 }
1100 let ctx = self.loop_stack.last().unwrap();
1102 let finally_depth = ctx.finally_depth;
1103 let handler_depth = ctx.handler_depth;
1104 let has_iterator = ctx.has_iterator;
1105 let scope_depth = ctx.scope_depth;
1106 for _ in handler_depth..self.handler_depth {
1108 self.chunk.emit(Op::PopHandler, self.line);
1109 }
1110 if self.finally_bodies.len() > finally_depth {
1112 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1113 .iter()
1114 .rev()
1115 .cloned()
1116 .collect();
1117 for fb in &finallys {
1118 self.compile_finally_inline(fb)?;
1119 }
1120 }
1121 self.unwind_scopes_to(scope_depth);
1122 if has_iterator {
1123 self.chunk.emit(Op::PopIterator, self.line);
1124 }
1125 let patch = self.chunk.emit_jump(Op::Jump, self.line);
1126 self.loop_stack
1127 .last_mut()
1128 .unwrap()
1129 .break_patches
1130 .push(patch);
1131 }
1132
1133 Node::ContinueStmt => {
1134 if self.loop_stack.is_empty() {
1135 return Err(CompileError {
1136 message: "continue outside of loop".to_string(),
1137 line: self.line,
1138 });
1139 }
1140 let ctx = self.loop_stack.last().unwrap();
1141 let finally_depth = ctx.finally_depth;
1142 let handler_depth = ctx.handler_depth;
1143 let loop_start = ctx.start_offset;
1144 let scope_depth = ctx.scope_depth;
1145 for _ in handler_depth..self.handler_depth {
1146 self.chunk.emit(Op::PopHandler, self.line);
1147 }
1148 if self.finally_bodies.len() > finally_depth {
1149 let finallys: Vec<_> = self.finally_bodies[finally_depth..]
1150 .iter()
1151 .rev()
1152 .cloned()
1153 .collect();
1154 for fb in &finallys {
1155 self.compile_finally_inline(fb)?;
1156 }
1157 }
1158 self.unwind_scopes_to(scope_depth);
1159 self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
1160 }
1161
1162 Node::ListLiteral(elements) => {
1163 let has_spread = elements.iter().any(|e| matches!(&e.node, Node::Spread(_)));
1164 if !has_spread {
1165 for el in elements {
1166 self.compile_node(el)?;
1167 }
1168 self.chunk
1169 .emit_u16(Op::BuildList, elements.len() as u16, self.line);
1170 } else {
1171 self.chunk.emit_u16(Op::BuildList, 0, self.line);
1174 let mut pending = 0u16;
1175 for el in elements {
1176 if let Node::Spread(inner) = &el.node {
1177 if pending > 0 {
1179 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1180 self.chunk.emit(Op::Add, self.line);
1182 pending = 0;
1183 }
1184 self.compile_node(inner)?;
1186 self.chunk.emit(Op::Dup, self.line);
1187 let assert_idx = self
1188 .chunk
1189 .add_constant(Constant::String("__assert_list".into()));
1190 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1191 self.chunk.emit(Op::Swap, self.line);
1192 self.chunk.emit_u8(Op::Call, 1, self.line);
1193 self.chunk.emit(Op::Pop, self.line);
1194 self.chunk.emit(Op::Add, self.line);
1195 } else {
1196 self.compile_node(el)?;
1197 pending += 1;
1198 }
1199 }
1200 if pending > 0 {
1201 self.chunk.emit_u16(Op::BuildList, pending, self.line);
1202 self.chunk.emit(Op::Add, self.line);
1203 }
1204 }
1205 }
1206
1207 Node::DictLiteral(entries) => {
1208 let has_spread = entries
1209 .iter()
1210 .any(|e| matches!(&e.value.node, Node::Spread(_)));
1211 if !has_spread {
1212 for entry in entries {
1213 self.compile_node(&entry.key)?;
1214 self.compile_node(&entry.value)?;
1215 }
1216 self.chunk
1217 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
1218 } else {
1219 self.chunk.emit_u16(Op::BuildDict, 0, self.line);
1221 let mut pending = 0u16;
1222 for entry in entries {
1223 if let Node::Spread(inner) = &entry.value.node {
1224 if pending > 0 {
1226 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1227 self.chunk.emit(Op::Add, self.line);
1228 pending = 0;
1229 }
1230 self.compile_node(inner)?;
1232 self.chunk.emit(Op::Dup, self.line);
1233 let assert_idx = self
1234 .chunk
1235 .add_constant(Constant::String("__assert_dict".into()));
1236 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
1237 self.chunk.emit(Op::Swap, self.line);
1238 self.chunk.emit_u8(Op::Call, 1, self.line);
1239 self.chunk.emit(Op::Pop, self.line);
1240 self.chunk.emit(Op::Add, self.line);
1241 } else {
1242 self.compile_node(&entry.key)?;
1243 self.compile_node(&entry.value)?;
1244 pending += 1;
1245 }
1246 }
1247 if pending > 0 {
1248 self.chunk.emit_u16(Op::BuildDict, pending, self.line);
1249 self.chunk.emit(Op::Add, self.line);
1250 }
1251 }
1252 }
1253
1254 Node::InterpolatedString(segments) => {
1255 let mut part_count = 0u16;
1256 for seg in segments {
1257 match seg {
1258 StringSegment::Literal(s) => {
1259 let idx = self.chunk.add_constant(Constant::String(s.clone()));
1260 self.chunk.emit_u16(Op::Constant, idx, self.line);
1261 part_count += 1;
1262 }
1263 StringSegment::Expression(expr_str, expr_line, expr_col) => {
1264 let mut lexer =
1266 harn_lexer::Lexer::with_position(expr_str, *expr_line, *expr_col);
1267 if let Ok(tokens) = lexer.tokenize() {
1268 let mut parser = harn_parser::Parser::new(tokens);
1269 if let Ok(snode) = parser.parse_single_expression() {
1270 self.compile_node(&snode)?;
1271 let to_str = self
1273 .chunk
1274 .add_constant(Constant::String("to_string".into()));
1275 self.chunk.emit_u16(Op::Constant, to_str, self.line);
1276 self.chunk.emit(Op::Swap, self.line);
1277 self.chunk.emit_u8(Op::Call, 1, self.line);
1278 part_count += 1;
1279 } else {
1280 let idx =
1282 self.chunk.add_constant(Constant::String(expr_str.clone()));
1283 self.chunk.emit_u16(Op::Constant, idx, self.line);
1284 part_count += 1;
1285 }
1286 }
1287 }
1288 }
1289 }
1290 if part_count > 1 {
1291 self.chunk.emit_u16(Op::Concat, part_count, self.line);
1292 }
1293 }
1294
1295 Node::FnDecl {
1296 name, params, body, ..
1297 } => {
1298 let mut fn_compiler = Compiler::new();
1300 fn_compiler.enum_names = self.enum_names.clone();
1301 fn_compiler.emit_default_preamble(params)?;
1302 fn_compiler.emit_type_checks(params);
1303 let is_gen = body_contains_yield(body);
1304 fn_compiler.compile_block(body)?;
1305 if !fn_compiler.finally_bodies.is_empty() {
1307 let finallys: Vec<_> =
1308 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1309 for fb in &finallys {
1310 fn_compiler.compile_finally_inline(fb)?;
1311 }
1312 }
1313 fn_compiler.chunk.emit(Op::Nil, self.line);
1314 fn_compiler.chunk.emit(Op::Return, self.line);
1315
1316 let func = CompiledFunction {
1317 name: name.clone(),
1318 params: TypedParam::names(params),
1319 default_start: TypedParam::default_start(params),
1320 chunk: fn_compiler.chunk,
1321 is_generator: is_gen,
1322 has_rest_param: params.last().is_some_and(|p| p.rest),
1323 };
1324 let fn_idx = self.chunk.functions.len();
1325 self.chunk.functions.push(func);
1326
1327 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1328 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1329 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1330 }
1331
1332 Node::ToolDecl {
1333 name,
1334 description,
1335 params,
1336 return_type,
1337 body,
1338 ..
1339 } => {
1340 let mut fn_compiler = Compiler::new();
1342 fn_compiler.enum_names = self.enum_names.clone();
1343 fn_compiler.emit_default_preamble(params)?;
1344 fn_compiler.emit_type_checks(params);
1345 fn_compiler.compile_block(body)?;
1346 if !fn_compiler.finally_bodies.is_empty() {
1348 let finallys: Vec<_> =
1349 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1350 for fb in &finallys {
1351 fn_compiler.compile_finally_inline(fb)?;
1352 }
1353 }
1354 fn_compiler.chunk.emit(Op::Return, self.line);
1356
1357 let func = CompiledFunction {
1358 name: name.clone(),
1359 params: TypedParam::names(params),
1360 default_start: TypedParam::default_start(params),
1361 chunk: fn_compiler.chunk,
1362 is_generator: false,
1363 has_rest_param: params.last().is_some_and(|p| p.rest),
1364 };
1365 let fn_idx = self.chunk.functions.len();
1366 self.chunk.functions.push(func);
1367
1368 let define_name = self
1370 .chunk
1371 .add_constant(Constant::String("tool_define".into()));
1372 self.chunk.emit_u16(Op::Constant, define_name, self.line);
1373
1374 let reg_name = self
1376 .chunk
1377 .add_constant(Constant::String("tool_registry".into()));
1378 self.chunk.emit_u16(Op::Constant, reg_name, self.line);
1379 self.chunk.emit_u8(Op::Call, 0, self.line);
1380
1381 let tool_name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1383 self.chunk.emit_u16(Op::Constant, tool_name_idx, self.line);
1384
1385 let desc = description.as_deref().unwrap_or("");
1387 let desc_idx = self.chunk.add_constant(Constant::String(desc.to_string()));
1388 self.chunk.emit_u16(Op::Constant, desc_idx, self.line);
1389
1390 let mut param_count: u16 = 0;
1395 for p in params {
1396 let pn_idx = self.chunk.add_constant(Constant::String(p.name.clone()));
1397 self.chunk.emit_u16(Op::Constant, pn_idx, self.line);
1398
1399 let base_schema = p
1400 .type_expr
1401 .as_ref()
1402 .and_then(Self::type_expr_to_schema_value)
1403 .unwrap_or_else(|| {
1404 VmValue::Dict(Rc::new(BTreeMap::from([(
1405 "type".to_string(),
1406 VmValue::String(Rc::from("any")),
1407 )])))
1408 });
1409 let public_schema =
1410 schema::schema_to_json_schema_value(&base_schema).map_err(|error| {
1411 CompileError {
1412 message: format!(
1413 "failed to lower tool parameter schema for '{}': {}",
1414 p.name, error
1415 ),
1416 line: self.line,
1417 }
1418 })?;
1419 let mut param_schema = match public_schema {
1420 VmValue::Dict(map) => (*map).clone(),
1421 _ => BTreeMap::new(),
1422 };
1423
1424 if p.default_value.is_some() {
1425 param_schema.insert("required".to_string(), VmValue::Bool(false));
1426 }
1427
1428 self.emit_vm_value_literal(&VmValue::Dict(Rc::new(param_schema)));
1429
1430 if let Some(default_value) = p.default_value.as_ref() {
1431 let default_key =
1432 self.chunk.add_constant(Constant::String("default".into()));
1433 self.chunk.emit_u16(Op::Constant, default_key, self.line);
1434 self.compile_node(default_value)?;
1435 self.chunk.emit_u16(Op::BuildDict, 1, self.line);
1436 self.chunk.emit(Op::Add, self.line);
1437 }
1438
1439 param_count += 1;
1440 }
1441 self.chunk.emit_u16(Op::BuildDict, param_count, self.line);
1442
1443 let params_key = self
1445 .chunk
1446 .add_constant(Constant::String("parameters".into()));
1447 self.chunk.emit_u16(Op::Constant, params_key, self.line);
1448 self.chunk.emit(Op::Swap, self.line);
1449
1450 let handler_key = self.chunk.add_constant(Constant::String("handler".into()));
1451 self.chunk.emit_u16(Op::Constant, handler_key, self.line);
1452 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1453
1454 let mut config_entries = 2u16;
1455 if let Some(return_type) = return_type
1456 .as_ref()
1457 .and_then(Self::type_expr_to_schema_value)
1458 {
1459 let return_type =
1460 schema::schema_to_json_schema_value(&return_type).map_err(|error| {
1461 CompileError {
1462 message: format!(
1463 "failed to lower tool return schema for '{}': {}",
1464 name, error
1465 ),
1466 line: self.line,
1467 }
1468 })?;
1469 let returns_key = self.chunk.add_constant(Constant::String("returns".into()));
1470 self.chunk.emit_u16(Op::Constant, returns_key, self.line);
1471 self.emit_vm_value_literal(&return_type);
1472 config_entries += 1;
1473 }
1474
1475 self.chunk
1476 .emit_u16(Op::BuildDict, config_entries, self.line);
1477
1478 self.chunk.emit_u8(Op::Call, 4, self.line);
1480
1481 let bind_idx = self.chunk.add_constant(Constant::String(name.clone()));
1483 self.chunk.emit_u16(Op::DefLet, bind_idx, self.line);
1484 }
1485
1486 Node::Closure { params, body, .. } => {
1487 let mut fn_compiler = Compiler::new();
1488 fn_compiler.enum_names = self.enum_names.clone();
1489 fn_compiler.emit_default_preamble(params)?;
1490 fn_compiler.emit_type_checks(params);
1491 let is_gen = body_contains_yield(body);
1492 fn_compiler.compile_block(body)?;
1493 if !fn_compiler.finally_bodies.is_empty() {
1495 let finallys: Vec<_> =
1496 fn_compiler.finally_bodies.iter().rev().cloned().collect();
1497 for fb in &finallys {
1498 fn_compiler.compile_finally_inline(fb)?;
1499 }
1500 }
1501 fn_compiler.chunk.emit(Op::Return, self.line);
1503
1504 let func = CompiledFunction {
1505 name: "<closure>".to_string(),
1506 params: TypedParam::names(params),
1507 default_start: TypedParam::default_start(params),
1508 chunk: fn_compiler.chunk,
1509 is_generator: is_gen,
1510 has_rest_param: false,
1511 };
1512 let fn_idx = self.chunk.functions.len();
1513 self.chunk.functions.push(func);
1514
1515 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
1516 }
1517
1518 Node::ThrowStmt { value } => {
1519 if !self.finally_bodies.is_empty() {
1520 self.compile_node(value)?;
1522 self.temp_counter += 1;
1523 let temp_name = format!("__throw_val_{}__", self.temp_counter);
1524 let save_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
1525 self.chunk.emit_u16(Op::DefVar, save_idx, self.line);
1526 let finallys: Vec<_> = self.finally_bodies.iter().rev().cloned().collect();
1527 for fb in &finallys {
1528 self.compile_finally_inline(fb)?;
1529 }
1530 let restore_idx = self.chunk.add_constant(Constant::String(temp_name));
1531 self.chunk.emit_u16(Op::GetVar, restore_idx, self.line);
1532 self.chunk.emit(Op::Throw, self.line);
1533 } else {
1534 self.compile_node(value)?;
1535 self.chunk.emit(Op::Throw, self.line);
1536 }
1537 }
1538
1539 Node::MatchExpr { value, arms } => {
1540 self.compile_node(value)?;
1541 let mut end_jumps = Vec::new();
1542 for arm in arms {
1543 match &arm.pattern.node {
1544 Node::Identifier(name) if name == "_" => {
1546 if let Some(ref guard) = arm.guard {
1547 self.compile_node(guard)?;
1548 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1549 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1551 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1553 self.end_scope();
1554 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1555 self.chunk.patch_jump(guard_skip);
1556 self.chunk.emit(Op::Pop, self.line); } else {
1558 self.begin_scope();
1559 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1561 self.end_scope();
1562 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1563 }
1564 }
1565 Node::EnumConstruct {
1567 enum_name,
1568 variant,
1569 args: pat_args,
1570 } => {
1571 self.chunk.emit(Op::Dup, self.line);
1573 let en_idx =
1574 self.chunk.add_constant(Constant::String(enum_name.clone()));
1575 let vn_idx = self.chunk.add_constant(Constant::String(variant.clone()));
1576 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1577 let hi = (vn_idx >> 8) as u8;
1578 let lo = vn_idx as u8;
1579 self.chunk.code.push(hi);
1580 self.chunk.code.push(lo);
1581 self.chunk.lines.push(self.line);
1582 self.chunk.columns.push(self.column);
1583 self.chunk.lines.push(self.line);
1584 self.chunk.columns.push(self.column);
1585 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1587 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1589
1590 for (i, pat_arg) in pat_args.iter().enumerate() {
1593 if let Node::Identifier(binding_name) = &pat_arg.node {
1594 self.chunk.emit(Op::Dup, self.line);
1596 let fields_idx = self
1597 .chunk
1598 .add_constant(Constant::String("fields".to_string()));
1599 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1600 let idx_const =
1601 self.chunk.add_constant(Constant::Int(i as i64));
1602 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1603 self.chunk.emit(Op::Subscript, self.line);
1604 let name_idx = self
1605 .chunk
1606 .add_constant(Constant::String(binding_name.clone()));
1607 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1608 }
1609 }
1610
1611 if let Some(ref guard) = arm.guard {
1613 self.compile_node(guard)?;
1614 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1615 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1618 self.end_scope();
1619 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1620 self.chunk.patch_jump(guard_skip);
1621 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1623 } else {
1624 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1626 self.end_scope();
1627 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1628 }
1629 self.chunk.patch_jump(skip);
1630 self.chunk.emit(Op::Pop, self.line); }
1632 Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1634 {
1635 let enum_name = if let Node::Identifier(n) = &object.node {
1636 n.clone()
1637 } else {
1638 unreachable!()
1639 };
1640 self.chunk.emit(Op::Dup, self.line);
1641 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1642 let vn_idx =
1643 self.chunk.add_constant(Constant::String(property.clone()));
1644 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1645 let hi = (vn_idx >> 8) as u8;
1646 let lo = vn_idx as u8;
1647 self.chunk.code.push(hi);
1648 self.chunk.code.push(lo);
1649 self.chunk.lines.push(self.line);
1650 self.chunk.columns.push(self.column);
1651 self.chunk.lines.push(self.line);
1652 self.chunk.columns.push(self.column);
1653 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1654 self.chunk.emit(Op::Pop, self.line); if let Some(ref guard) = arm.guard {
1657 self.compile_node(guard)?;
1658 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1659 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1661 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1663 self.end_scope();
1664 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1665 self.chunk.patch_jump(guard_skip);
1666 self.chunk.emit(Op::Pop, self.line); } else {
1668 self.begin_scope();
1669 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1671 self.end_scope();
1672 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1673 }
1674 self.chunk.patch_jump(skip);
1675 self.chunk.emit(Op::Pop, self.line); }
1677 Node::MethodCall {
1680 object,
1681 method,
1682 args: pat_args,
1683 } if matches!(&object.node, Node::Identifier(n) if self.enum_names.contains(n)) =>
1684 {
1685 let enum_name = if let Node::Identifier(n) = &object.node {
1686 n.clone()
1687 } else {
1688 unreachable!()
1689 };
1690 self.chunk.emit(Op::Dup, self.line);
1692 let en_idx = self.chunk.add_constant(Constant::String(enum_name));
1693 let vn_idx = self.chunk.add_constant(Constant::String(method.clone()));
1694 self.chunk.emit_u16(Op::MatchEnum, en_idx, self.line);
1695 let hi = (vn_idx >> 8) as u8;
1696 let lo = vn_idx as u8;
1697 self.chunk.code.push(hi);
1698 self.chunk.code.push(lo);
1699 self.chunk.lines.push(self.line);
1700 self.chunk.columns.push(self.column);
1701 self.chunk.lines.push(self.line);
1702 self.chunk.columns.push(self.column);
1703 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1704 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1706
1707 for (i, pat_arg) in pat_args.iter().enumerate() {
1709 if let Node::Identifier(binding_name) = &pat_arg.node {
1710 self.chunk.emit(Op::Dup, self.line);
1711 let fields_idx = self
1712 .chunk
1713 .add_constant(Constant::String("fields".to_string()));
1714 self.chunk.emit_u16(Op::GetProperty, fields_idx, self.line);
1715 let idx_const =
1716 self.chunk.add_constant(Constant::Int(i as i64));
1717 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1718 self.chunk.emit(Op::Subscript, self.line);
1719 let name_idx = self
1720 .chunk
1721 .add_constant(Constant::String(binding_name.clone()));
1722 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1723 }
1724 }
1725
1726 if let Some(ref guard) = arm.guard {
1728 self.compile_node(guard)?;
1729 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1730 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1733 self.end_scope();
1734 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1735 self.chunk.patch_jump(guard_skip);
1736 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1738 } else {
1739 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1741 self.end_scope();
1742 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1743 }
1744 self.chunk.patch_jump(skip);
1745 self.chunk.emit(Op::Pop, self.line); }
1747 Node::Identifier(name) => {
1749 self.begin_scope();
1750 self.chunk.emit(Op::Dup, self.line); let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
1753 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1754 if let Some(ref guard) = arm.guard {
1756 self.compile_node(guard)?;
1757 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1758 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1761 self.end_scope();
1762 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1763 self.chunk.patch_jump(guard_skip);
1764 self.chunk.emit(Op::Pop, self.line); self.end_scope();
1766 } else {
1767 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1769 self.end_scope();
1770 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1771 }
1772 }
1773 Node::DictLiteral(entries)
1775 if entries
1776 .iter()
1777 .all(|e| matches!(&e.key.node, Node::StringLiteral(_))) =>
1778 {
1779 self.chunk.emit(Op::Dup, self.line);
1781 let typeof_idx =
1782 self.chunk.add_constant(Constant::String("type_of".into()));
1783 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1784 self.chunk.emit(Op::Swap, self.line);
1785 self.chunk.emit_u8(Op::Call, 1, self.line);
1786 let dict_str = self.chunk.add_constant(Constant::String("dict".into()));
1787 self.chunk.emit_u16(Op::Constant, dict_str, self.line);
1788 self.chunk.emit(Op::Equal, self.line);
1789 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1790 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1794 let mut bindings = Vec::new();
1795 self.begin_scope();
1796 for entry in entries {
1797 if let Node::StringLiteral(key) = &entry.key.node {
1798 match &entry.value.node {
1799 Node::StringLiteral(_)
1801 | Node::IntLiteral(_)
1802 | Node::FloatLiteral(_)
1803 | Node::BoolLiteral(_)
1804 | Node::NilLiteral => {
1805 self.chunk.emit(Op::Dup, self.line);
1806 let key_idx = self
1807 .chunk
1808 .add_constant(Constant::String(key.clone()));
1809 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1810 self.chunk.emit(Op::Subscript, self.line);
1811 self.compile_node(&entry.value)?;
1812 self.chunk.emit(Op::Equal, self.line);
1813 let skip =
1814 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1815 self.chunk.emit(Op::Pop, self.line); constraint_skips.push(skip);
1817 }
1818 Node::Identifier(binding) => {
1820 bindings.push((key.clone(), binding.clone()));
1821 }
1822 _ => {
1823 self.chunk.emit(Op::Dup, self.line);
1825 let key_idx = self
1826 .chunk
1827 .add_constant(Constant::String(key.clone()));
1828 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1829 self.chunk.emit(Op::Subscript, self.line);
1830 self.compile_node(&entry.value)?;
1831 self.chunk.emit(Op::Equal, self.line);
1832 let skip =
1833 self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1834 self.chunk.emit(Op::Pop, self.line);
1835 constraint_skips.push(skip);
1836 }
1837 }
1838 }
1839 }
1840
1841 for (key, binding) in &bindings {
1843 self.chunk.emit(Op::Dup, self.line);
1844 let key_idx =
1845 self.chunk.add_constant(Constant::String(key.clone()));
1846 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
1847 self.chunk.emit(Op::Subscript, self.line);
1848 let name_idx =
1849 self.chunk.add_constant(Constant::String(binding.clone()));
1850 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1851 }
1852
1853 if let Some(ref guard) = arm.guard {
1855 self.compile_node(guard)?;
1856 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1857 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1860 self.end_scope();
1861 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1862 self.chunk.patch_jump(guard_skip);
1863 self.chunk.emit(Op::Pop, self.line); } else {
1867 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1869 self.end_scope();
1870 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1871 }
1872
1873 let type_fail_target = self.chunk.code.len();
1874 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1876 let scoped_fail_target = self.chunk.code.len();
1877 self.chunk.emit(Op::PopScope, self.line);
1878 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1880
1881 for skip in constraint_skips {
1882 self.chunk.patch_jump_to(skip, scoped_fail_target);
1883 }
1884 self.chunk.patch_jump_to(skip_type, type_fail_target);
1885 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1886 }
1887 Node::ListLiteral(elements) => {
1889 self.chunk.emit(Op::Dup, self.line);
1891 let typeof_idx =
1892 self.chunk.add_constant(Constant::String("type_of".into()));
1893 self.chunk.emit_u16(Op::Constant, typeof_idx, self.line);
1894 self.chunk.emit(Op::Swap, self.line);
1895 self.chunk.emit_u8(Op::Call, 1, self.line);
1896 let list_str = self.chunk.add_constant(Constant::String("list".into()));
1897 self.chunk.emit_u16(Op::Constant, list_str, self.line);
1898 self.chunk.emit(Op::Equal, self.line);
1899 let skip_type = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1900 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Dup, self.line);
1904 let len_idx = self.chunk.add_constant(Constant::String("len".into()));
1905 self.chunk.emit_u16(Op::Constant, len_idx, self.line);
1906 self.chunk.emit(Op::Swap, self.line);
1907 self.chunk.emit_u8(Op::Call, 1, self.line);
1908 let count = self
1909 .chunk
1910 .add_constant(Constant::Int(elements.len() as i64));
1911 self.chunk.emit_u16(Op::Constant, count, self.line);
1912 self.chunk.emit(Op::GreaterEqual, self.line);
1913 let skip_len = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1914 self.chunk.emit(Op::Pop, self.line); let mut constraint_skips = Vec::new();
1918 let mut bindings = Vec::new();
1919 self.begin_scope();
1920 for (i, elem) in elements.iter().enumerate() {
1921 match &elem.node {
1922 Node::Identifier(name) if name != "_" => {
1923 bindings.push((i, name.clone()));
1924 }
1925 Node::Identifier(_) => {} _ => {
1928 self.chunk.emit(Op::Dup, self.line);
1929 let idx_const =
1930 self.chunk.add_constant(Constant::Int(i as i64));
1931 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1932 self.chunk.emit(Op::Subscript, self.line);
1933 self.compile_node(elem)?;
1934 self.chunk.emit(Op::Equal, self.line);
1935 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1936 self.chunk.emit(Op::Pop, self.line);
1937 constraint_skips.push(skip);
1938 }
1939 }
1940 }
1941
1942 for (i, name) in &bindings {
1944 self.chunk.emit(Op::Dup, self.line);
1945 let idx_const = self.chunk.add_constant(Constant::Int(*i as i64));
1946 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
1947 self.chunk.emit(Op::Subscript, self.line);
1948 let name_idx =
1949 self.chunk.add_constant(Constant::String(name.clone()));
1950 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
1951 }
1952
1953 if let Some(ref guard) = arm.guard {
1955 self.compile_node(guard)?;
1956 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1957 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1960 self.end_scope();
1961 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1962 self.chunk.patch_jump(guard_skip);
1963 self.chunk.emit(Op::Pop, self.line); } else {
1965 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
1967 self.end_scope();
1968 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
1969 }
1970
1971 let pre_scope_fail_target = self.chunk.code.len();
1972 self.chunk.emit(Op::Pop, self.line); let next_arm_jump = self.chunk.emit_jump(Op::Jump, self.line);
1974 let scoped_fail_target = self.chunk.code.len();
1975 self.chunk.emit(Op::PopScope, self.line);
1976 self.chunk.emit(Op::Pop, self.line); let next_arm_target = self.chunk.code.len();
1978 for skip in constraint_skips {
1979 self.chunk.patch_jump_to(skip, scoped_fail_target);
1980 }
1981 self.chunk.patch_jump_to(skip_len, pre_scope_fail_target);
1982 self.chunk.patch_jump_to(skip_type, pre_scope_fail_target);
1983 self.chunk.patch_jump_to(next_arm_jump, next_arm_target);
1984 }
1985 _ => {
1987 self.chunk.emit(Op::Dup, self.line);
1988 self.compile_node(&arm.pattern)?;
1989 self.chunk.emit(Op::Equal, self.line);
1990 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1991 self.chunk.emit(Op::Pop, self.line); if let Some(ref guard) = arm.guard {
1994 self.compile_node(guard)?;
1995 let guard_skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
1996 self.chunk.emit(Op::Pop, self.line); self.begin_scope();
1998 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
2000 self.end_scope();
2001 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2002 self.chunk.patch_jump(guard_skip);
2003 self.chunk.emit(Op::Pop, self.line); } else {
2005 self.begin_scope();
2006 self.chunk.emit(Op::Pop, self.line); self.compile_match_body(&arm.body)?;
2008 self.end_scope();
2009 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2010 }
2011 self.chunk.patch_jump(skip);
2012 self.chunk.emit(Op::Pop, self.line); }
2014 }
2015 }
2016 let msg_idx = self.chunk.add_constant(Constant::String(
2018 "No match arm matched the value".to_string(),
2019 ));
2020 self.chunk.emit(Op::Pop, self.line);
2021 self.chunk.emit_u16(Op::Constant, msg_idx, self.line);
2022 self.chunk.emit(Op::Throw, self.line);
2023 for j in end_jumps {
2024 self.chunk.patch_jump(j);
2025 }
2026 }
2027
2028 Node::RangeExpr {
2029 start,
2030 end,
2031 inclusive,
2032 } => {
2033 let name_idx = self
2035 .chunk
2036 .add_constant(Constant::String("__range__".to_string()));
2037 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2038 self.compile_node(start)?;
2039 self.compile_node(end)?;
2040 if *inclusive {
2041 self.chunk.emit(Op::True, self.line);
2042 } else {
2043 self.chunk.emit(Op::False, self.line);
2044 }
2045 self.chunk.emit_u8(Op::Call, 3, self.line);
2046 }
2047
2048 Node::GuardStmt {
2049 condition,
2050 else_body,
2051 } => {
2052 self.compile_node(condition)?;
2055 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2056 self.chunk.emit(Op::Pop, self.line); self.compile_scoped_block(else_body)?;
2059 if !else_body.is_empty() && Self::produces_value(&else_body.last().unwrap().node) {
2061 self.chunk.emit(Op::Pop, self.line);
2062 }
2063 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2064 self.chunk.patch_jump(skip_jump);
2065 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end_jump);
2067 self.chunk.emit(Op::Nil, self.line);
2068 }
2069
2070 Node::RequireStmt { condition, message } => {
2071 self.compile_node(condition)?;
2072 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2073 self.chunk.emit(Op::Pop, self.line);
2074 if let Some(message) = message {
2075 self.compile_node(message)?;
2076 } else {
2077 let idx = self
2078 .chunk
2079 .add_constant(Constant::String("require condition failed".to_string()));
2080 self.chunk.emit_u16(Op::Constant, idx, self.line);
2081 }
2082 self.chunk.emit(Op::Throw, self.line);
2083 self.chunk.patch_jump(ok_jump);
2084 self.chunk.emit(Op::Pop, self.line);
2085 }
2086
2087 Node::Block(stmts) => {
2088 self.compile_scoped_block(stmts)?;
2089 }
2090
2091 Node::DeadlineBlock { duration, body } => {
2092 self.compile_node(duration)?;
2093 self.chunk.emit(Op::DeadlineSetup, self.line);
2094 self.compile_scoped_block(body)?;
2095 self.chunk.emit(Op::DeadlineEnd, self.line);
2096 }
2097
2098 Node::MutexBlock { body } => {
2099 self.begin_scope();
2101 for sn in body {
2102 self.compile_node(sn)?;
2103 if Self::produces_value(&sn.node) {
2104 self.chunk.emit(Op::Pop, self.line);
2105 }
2106 }
2107 self.chunk.emit(Op::Nil, self.line);
2108 self.end_scope();
2109 }
2110
2111 Node::DeferStmt { body } => {
2112 self.finally_bodies.push(body.clone());
2114 self.chunk.emit(Op::Nil, self.line);
2115 }
2116
2117 Node::YieldExpr { value } => {
2118 if let Some(val) = value {
2119 self.compile_node(val)?;
2120 } else {
2121 self.chunk.emit(Op::Nil, self.line);
2122 }
2123 self.chunk.emit(Op::Yield, self.line);
2124 }
2125
2126 Node::EnumConstruct {
2127 enum_name,
2128 variant,
2129 args,
2130 } => {
2131 for arg in args {
2133 self.compile_node(arg)?;
2134 }
2135 let enum_idx = self.chunk.add_constant(Constant::String(enum_name.clone()));
2136 let var_idx = self.chunk.add_constant(Constant::String(variant.clone()));
2137 self.chunk.emit_u16(Op::BuildEnum, enum_idx, self.line);
2139 let hi = (var_idx >> 8) as u8;
2140 let lo = var_idx as u8;
2141 self.chunk.code.push(hi);
2142 self.chunk.code.push(lo);
2143 self.chunk.lines.push(self.line);
2144 self.chunk.columns.push(self.column);
2145 self.chunk.lines.push(self.line);
2146 self.chunk.columns.push(self.column);
2147 let fc = args.len() as u16;
2148 let fhi = (fc >> 8) as u8;
2149 let flo = fc as u8;
2150 self.chunk.code.push(fhi);
2151 self.chunk.code.push(flo);
2152 self.chunk.lines.push(self.line);
2153 self.chunk.columns.push(self.column);
2154 self.chunk.lines.push(self.line);
2155 self.chunk.columns.push(self.column);
2156 }
2157
2158 Node::StructConstruct {
2159 struct_name,
2160 fields,
2161 } => {
2162 let make_idx = self
2164 .chunk
2165 .add_constant(Constant::String("__make_struct".to_string()));
2166 let struct_name_idx = self
2167 .chunk
2168 .add_constant(Constant::String(struct_name.clone()));
2169 self.chunk.emit_u16(Op::Constant, make_idx, self.line);
2170 self.chunk
2171 .emit_u16(Op::Constant, struct_name_idx, self.line);
2172
2173 for entry in fields {
2174 self.compile_node(&entry.key)?;
2175 self.compile_node(&entry.value)?;
2176 }
2177 self.chunk
2178 .emit_u16(Op::BuildDict, fields.len() as u16, self.line);
2179 self.chunk.emit_u8(Op::Call, 2, self.line);
2180 }
2181
2182 Node::ImportDecl { path } => {
2183 let idx = self.chunk.add_constant(Constant::String(path.clone()));
2184 self.chunk.emit_u16(Op::Import, idx, self.line);
2185 }
2186
2187 Node::SelectiveImport { names, path } => {
2188 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
2189 let names_str = names.join(",");
2190 let names_idx = self.chunk.add_constant(Constant::String(names_str));
2191 self.chunk
2192 .emit_u16(Op::SelectiveImport, path_idx, self.line);
2193 let hi = (names_idx >> 8) as u8;
2194 let lo = names_idx as u8;
2195 self.chunk.code.push(hi);
2196 self.chunk.code.push(lo);
2197 self.chunk.lines.push(self.line);
2198 self.chunk.columns.push(self.column);
2199 self.chunk.lines.push(self.line);
2200 self.chunk.columns.push(self.column);
2201 }
2202
2203 Node::TryOperator { operand } => {
2204 self.compile_node(operand)?;
2205 self.chunk.emit(Op::TryUnwrap, self.line);
2206 }
2207
2208 Node::ImplBlock { type_name, methods } => {
2209 for method_sn in methods {
2212 if let Node::FnDecl {
2213 name, params, body, ..
2214 } = &method_sn.node
2215 {
2216 let key_idx = self.chunk.add_constant(Constant::String(name.clone()));
2218 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2219
2220 let mut fn_compiler = Compiler::new();
2222 fn_compiler.enum_names = self.enum_names.clone();
2223 fn_compiler.emit_default_preamble(params)?;
2224 fn_compiler.emit_type_checks(params);
2225 fn_compiler.compile_block(body)?;
2226 fn_compiler.chunk.emit(Op::Nil, self.line);
2227 fn_compiler.chunk.emit(Op::Return, self.line);
2228
2229 let func = CompiledFunction {
2230 name: format!("{}.{}", type_name, name),
2231 params: TypedParam::names(params),
2232 default_start: TypedParam::default_start(params),
2233 chunk: fn_compiler.chunk,
2234 is_generator: false,
2235 has_rest_param: false,
2236 };
2237 let fn_idx = self.chunk.functions.len();
2238 self.chunk.functions.push(func);
2239 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2240 }
2241 }
2242 let method_count = methods
2243 .iter()
2244 .filter(|m| matches!(m.node, Node::FnDecl { .. }))
2245 .count();
2246 self.chunk
2247 .emit_u16(Op::BuildDict, method_count as u16, self.line);
2248 let impl_name = format!("__impl_{}", type_name);
2249 let name_idx = self.chunk.add_constant(Constant::String(impl_name));
2250 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2251 }
2252
2253 Node::StructDecl { name, .. } => {
2254 let mut fn_compiler = Compiler::new();
2256 fn_compiler.enum_names = self.enum_names.clone();
2257 let params = vec![TypedParam::untyped("__fields")];
2258 fn_compiler.emit_default_preamble(¶ms)?;
2259
2260 let make_idx = fn_compiler
2262 .chunk
2263 .add_constant(Constant::String("__make_struct".into()));
2264 fn_compiler
2265 .chunk
2266 .emit_u16(Op::Constant, make_idx, self.line);
2267 let sname_idx = fn_compiler
2268 .chunk
2269 .add_constant(Constant::String(name.clone()));
2270 fn_compiler
2271 .chunk
2272 .emit_u16(Op::Constant, sname_idx, self.line);
2273 let fields_idx = fn_compiler
2274 .chunk
2275 .add_constant(Constant::String("__fields".into()));
2276 fn_compiler
2277 .chunk
2278 .emit_u16(Op::GetVar, fields_idx, self.line);
2279 fn_compiler.chunk.emit_u8(Op::Call, 2, self.line);
2280 fn_compiler.chunk.emit(Op::Return, self.line);
2281
2282 let func = CompiledFunction {
2283 name: name.clone(),
2284 params: TypedParam::names(¶ms),
2285 default_start: None,
2286 chunk: fn_compiler.chunk,
2287 is_generator: false,
2288 has_rest_param: false,
2289 };
2290 let fn_idx = self.chunk.functions.len();
2291 self.chunk.functions.push(func);
2292 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2293 let name_idx = self.chunk.add_constant(Constant::String(name.clone()));
2294 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
2295 }
2296
2297 Node::Pipeline { .. }
2299 | Node::OverrideDecl { .. }
2300 | Node::TypeDecl { .. }
2301 | Node::EnumDecl { .. }
2302 | Node::InterfaceDecl { .. } => {
2303 self.chunk.emit(Op::Nil, self.line);
2304 }
2305
2306 Node::TryCatch {
2307 body,
2308 error_var,
2309 error_type,
2310 catch_body,
2311 finally_body,
2312 } => {
2313 let type_name = error_type.as_ref().and_then(|te| {
2315 if let harn_parser::TypeExpr::Named(name) = te {
2316 Some(name.clone())
2317 } else {
2318 None
2319 }
2320 });
2321
2322 let type_name_idx = if let Some(ref tn) = type_name {
2323 self.chunk.add_constant(Constant::String(tn.clone()))
2324 } else {
2325 self.chunk.add_constant(Constant::String(String::new()))
2326 };
2327
2328 let has_catch = !catch_body.is_empty() || error_var.is_some();
2329 let has_finally = finally_body.is_some();
2330
2331 if has_catch && has_finally {
2332 let finally_body = finally_body.as_ref().unwrap();
2334
2335 self.finally_bodies.push(finally_body.clone());
2337
2338 self.handler_depth += 1;
2340 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2341 self.emit_type_name_extra(type_name_idx);
2342
2343 self.compile_try_body(body)?;
2345
2346 self.handler_depth -= 1;
2348 self.chunk.emit(Op::PopHandler, self.line);
2349 self.compile_finally_inline(finally_body)?;
2350 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2351
2352 self.chunk.patch_jump(catch_jump);
2354 self.begin_scope();
2355 self.compile_catch_binding(error_var)?;
2356
2357 self.handler_depth += 1;
2359 let rethrow_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2360 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2361 self.emit_type_name_extra(empty_type);
2362
2363 self.compile_try_body(catch_body)?;
2365
2366 self.handler_depth -= 1;
2368 self.chunk.emit(Op::PopHandler, self.line);
2369 self.compile_finally_inline(finally_body)?;
2370 self.end_scope();
2371 let end_jump2 = self.chunk.emit_jump(Op::Jump, self.line);
2372
2373 self.chunk.patch_jump(rethrow_jump);
2375 self.compile_rethrow_with_finally(finally_body)?;
2376 self.end_scope();
2377
2378 self.chunk.patch_jump(end_jump);
2379 self.chunk.patch_jump(end_jump2);
2380
2381 self.finally_bodies.pop();
2382 } else if has_finally {
2383 let finally_body = finally_body.as_ref().unwrap();
2385
2386 self.finally_bodies.push(finally_body.clone());
2387
2388 self.handler_depth += 1;
2390 let error_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2391 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2392 self.emit_type_name_extra(empty_type);
2393
2394 self.compile_try_body(body)?;
2396
2397 self.handler_depth -= 1;
2399 self.chunk.emit(Op::PopHandler, self.line);
2400 self.compile_finally_inline(finally_body)?;
2401 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2402
2403 self.chunk.patch_jump(error_jump);
2405 self.compile_rethrow_with_finally(finally_body)?;
2406
2407 self.chunk.patch_jump(end_jump);
2408
2409 self.finally_bodies.pop();
2410 } else {
2411 self.handler_depth += 1;
2415 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2416 self.emit_type_name_extra(type_name_idx);
2417
2418 self.compile_try_body(body)?;
2420
2421 self.handler_depth -= 1;
2423 self.chunk.emit(Op::PopHandler, self.line);
2424 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2425
2426 self.chunk.patch_jump(catch_jump);
2428 self.begin_scope();
2429 self.compile_catch_binding(error_var)?;
2430
2431 self.compile_try_body(catch_body)?;
2433 self.end_scope();
2434
2435 self.chunk.patch_jump(end_jump);
2437 }
2438 }
2439
2440 Node::TryExpr { body } => {
2441 self.handler_depth += 1;
2445 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2446 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2447 self.emit_type_name_extra(empty_type);
2448
2449 self.compile_try_body(body)?;
2451
2452 self.handler_depth -= 1;
2454 self.chunk.emit(Op::PopHandler, self.line);
2455
2456 let ok_idx = self.chunk.add_constant(Constant::String("Ok".to_string()));
2458 self.chunk.emit_u16(Op::Constant, ok_idx, self.line);
2459 self.chunk.emit(Op::Swap, self.line);
2460 self.chunk.emit_u8(Op::Call, 1, self.line);
2461
2462 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2464
2465 self.chunk.patch_jump(catch_jump);
2467
2468 let err_idx = self.chunk.add_constant(Constant::String("Err".to_string()));
2470 self.chunk.emit_u16(Op::Constant, err_idx, self.line);
2471 self.chunk.emit(Op::Swap, self.line);
2472 self.chunk.emit_u8(Op::Call, 1, self.line);
2473
2474 self.chunk.patch_jump(end_jump);
2476 }
2477
2478 Node::Retry { count, body } => {
2479 self.compile_node(count)?;
2481 let counter_name = "__retry_counter__";
2482 let counter_idx = self
2483 .chunk
2484 .add_constant(Constant::String(counter_name.to_string()));
2485 self.chunk.emit_u16(Op::DefVar, counter_idx, self.line);
2486
2487 self.chunk.emit(Op::Nil, self.line);
2489 let err_name = "__retry_last_error__";
2490 let err_idx = self
2491 .chunk
2492 .add_constant(Constant::String(err_name.to_string()));
2493 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
2494
2495 let loop_start = self.chunk.current_offset();
2497
2498 let catch_jump = self.chunk.emit_jump(Op::TryCatchSetup, self.line);
2500 let empty_type = self.chunk.add_constant(Constant::String(String::new()));
2502 let hi = (empty_type >> 8) as u8;
2503 let lo = empty_type as u8;
2504 self.chunk.code.push(hi);
2505 self.chunk.code.push(lo);
2506 self.chunk.lines.push(self.line);
2507 self.chunk.columns.push(self.column);
2508 self.chunk.lines.push(self.line);
2509 self.chunk.columns.push(self.column);
2510
2511 self.compile_block(body)?;
2513
2514 self.chunk.emit(Op::PopHandler, self.line);
2516 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
2517
2518 self.chunk.patch_jump(catch_jump);
2520 self.chunk.emit(Op::Dup, self.line);
2522 self.chunk.emit_u16(Op::SetVar, err_idx, self.line);
2523 self.chunk.emit(Op::Pop, self.line);
2525
2526 self.chunk.emit_u16(Op::GetVar, counter_idx, self.line);
2528 let one_idx = self.chunk.add_constant(Constant::Int(1));
2529 self.chunk.emit_u16(Op::Constant, one_idx, self.line);
2530 self.chunk.emit(Op::Sub, self.line);
2531 self.chunk.emit(Op::Dup, self.line);
2532 self.chunk.emit_u16(Op::SetVar, counter_idx, self.line);
2533
2534 let zero_idx = self.chunk.add_constant(Constant::Int(0));
2536 self.chunk.emit_u16(Op::Constant, zero_idx, self.line);
2537 self.chunk.emit(Op::Greater, self.line);
2538 let retry_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2539 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::Jump, loop_start as u16, self.line);
2541
2542 self.chunk.patch_jump(retry_jump);
2544 self.chunk.emit(Op::Pop, self.line); self.chunk.emit_u16(Op::GetVar, err_idx, self.line);
2546 self.chunk.emit(Op::Throw, self.line);
2547
2548 self.chunk.patch_jump(end_jump);
2549 self.chunk.emit(Op::Nil, self.line);
2551 }
2552
2553 Node::Parallel {
2554 mode,
2555 expr,
2556 variable,
2557 body,
2558 } => {
2559 self.compile_node(expr)?;
2560 let mut fn_compiler = Compiler::new();
2561 fn_compiler.enum_names = self.enum_names.clone();
2562 fn_compiler.compile_block(body)?;
2563 fn_compiler.chunk.emit(Op::Return, self.line);
2564 let (fn_name, params) = match mode {
2565 ParallelMode::Count => (
2566 "<parallel>",
2567 vec![variable.clone().unwrap_or_else(|| "__i__".to_string())],
2568 ),
2569 ParallelMode::Each => (
2570 "<parallel_each>",
2571 vec![variable.clone().unwrap_or_else(|| "__item__".to_string())],
2572 ),
2573 ParallelMode::Settle => (
2574 "<parallel_settle>",
2575 vec![variable.clone().unwrap_or_else(|| "__item__".to_string())],
2576 ),
2577 };
2578 let func = CompiledFunction {
2579 name: fn_name.to_string(),
2580 params,
2581 default_start: None,
2582 chunk: fn_compiler.chunk,
2583 is_generator: false,
2584 has_rest_param: false,
2585 };
2586 let fn_idx = self.chunk.functions.len();
2587 self.chunk.functions.push(func);
2588 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2589 let op = match mode {
2590 ParallelMode::Count => Op::Parallel,
2591 ParallelMode::Each => Op::ParallelMap,
2592 ParallelMode::Settle => Op::ParallelSettle,
2593 };
2594 self.chunk.emit(op, self.line);
2595 }
2596
2597 Node::SpawnExpr { body } => {
2598 let mut fn_compiler = Compiler::new();
2599 fn_compiler.enum_names = self.enum_names.clone();
2600 fn_compiler.compile_block(body)?;
2601 fn_compiler.chunk.emit(Op::Return, self.line);
2602 let func = CompiledFunction {
2603 name: "<spawn>".to_string(),
2604 params: vec![],
2605 default_start: None,
2606 chunk: fn_compiler.chunk,
2607 is_generator: false,
2608 has_rest_param: false,
2609 };
2610 let fn_idx = self.chunk.functions.len();
2611 self.chunk.functions.push(func);
2612 self.chunk.emit_u16(Op::Closure, fn_idx as u16, self.line);
2613 self.chunk.emit(Op::Spawn, self.line);
2614 }
2615 Node::SelectExpr {
2616 cases,
2617 timeout,
2618 default_body,
2619 } => {
2620 let builtin_name = if timeout.is_some() {
2627 "__select_timeout"
2628 } else if default_body.is_some() {
2629 "__select_try"
2630 } else {
2631 "__select_list"
2632 };
2633
2634 let name_idx = self
2636 .chunk
2637 .add_constant(Constant::String(builtin_name.into()));
2638 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
2639
2640 for case in cases {
2642 self.compile_node(&case.channel)?;
2643 }
2644 self.chunk
2645 .emit_u16(Op::BuildList, cases.len() as u16, self.line);
2646
2647 if let Some((duration_expr, _)) = timeout {
2649 self.compile_node(duration_expr)?;
2650 self.chunk.emit_u8(Op::Call, 2, self.line);
2651 } else {
2652 self.chunk.emit_u8(Op::Call, 1, self.line);
2653 }
2654
2655 self.temp_counter += 1;
2657 let result_name = format!("__sel_result_{}__", self.temp_counter);
2658 let result_idx = self
2659 .chunk
2660 .add_constant(Constant::String(result_name.clone()));
2661 self.chunk.emit_u16(Op::DefVar, result_idx, self.line);
2662
2663 let mut end_jumps = Vec::new();
2665
2666 for (i, case) in cases.iter().enumerate() {
2667 let get_r = self
2668 .chunk
2669 .add_constant(Constant::String(result_name.clone()));
2670 self.chunk.emit_u16(Op::GetVar, get_r, self.line);
2671 let idx_prop = self.chunk.add_constant(Constant::String("index".into()));
2672 self.chunk.emit_u16(Op::GetProperty, idx_prop, self.line);
2673 let case_i = self.chunk.add_constant(Constant::Int(i as i64));
2674 self.chunk.emit_u16(Op::Constant, case_i, self.line);
2675 self.chunk.emit(Op::Equal, self.line);
2676 let skip = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
2677 self.chunk.emit(Op::Pop, self.line);
2678 self.begin_scope();
2679
2680 let get_r2 = self
2682 .chunk
2683 .add_constant(Constant::String(result_name.clone()));
2684 self.chunk.emit_u16(Op::GetVar, get_r2, self.line);
2685 let val_prop = self.chunk.add_constant(Constant::String("value".into()));
2686 self.chunk.emit_u16(Op::GetProperty, val_prop, self.line);
2687 let var_idx = self
2688 .chunk
2689 .add_constant(Constant::String(case.variable.clone()));
2690 self.chunk.emit_u16(Op::DefLet, var_idx, self.line);
2691
2692 self.compile_try_body(&case.body)?;
2693 self.end_scope();
2694 end_jumps.push(self.chunk.emit_jump(Op::Jump, self.line));
2695 self.chunk.patch_jump(skip);
2696 self.chunk.emit(Op::Pop, self.line);
2697 }
2698
2699 if let Some((_, ref timeout_body)) = timeout {
2701 self.compile_try_body(timeout_body)?;
2702 } else if let Some(ref def_body) = default_body {
2703 self.compile_try_body(def_body)?;
2704 } else {
2705 self.chunk.emit(Op::Nil, self.line);
2706 }
2707
2708 for ej in end_jumps {
2709 self.chunk.patch_jump(ej);
2710 }
2711 }
2712 Node::Spread(_) => {
2713 return Err(CompileError {
2714 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
2715 line: self.line,
2716 });
2717 }
2718 }
2719 Ok(())
2720 }
2721
2722 fn compile_destructuring(
2726 &mut self,
2727 pattern: &BindingPattern,
2728 is_mutable: bool,
2729 ) -> Result<(), CompileError> {
2730 let def_op = if is_mutable { Op::DefVar } else { Op::DefLet };
2731 match pattern {
2732 BindingPattern::Identifier(name) => {
2733 let idx = self.chunk.add_constant(Constant::String(name.clone()));
2735 self.chunk.emit_u16(def_op, idx, self.line);
2736 }
2737 BindingPattern::Dict(fields) => {
2738 self.chunk.emit(Op::Dup, self.line);
2741 let assert_idx = self
2742 .chunk
2743 .add_constant(Constant::String("__assert_dict".into()));
2744 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2745 self.chunk.emit(Op::Swap, self.line);
2746 self.chunk.emit_u8(Op::Call, 1, self.line);
2747 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = fields.iter().filter(|f| !f.is_rest).collect();
2752 let rest_field = fields.iter().find(|f| f.is_rest);
2753
2754 for field in &non_rest {
2755 self.chunk.emit(Op::Dup, self.line);
2756 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2757 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2758 self.chunk.emit(Op::Subscript, self.line);
2759 if let Some(default_expr) = &field.default_value {
2761 self.chunk.emit(Op::Dup, self.line);
2762 self.chunk.emit(Op::Nil, self.line);
2763 self.chunk.emit(Op::NotEqual, self.line);
2764 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2765 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2768 let end = self.chunk.emit_jump(Op::Jump, self.line);
2769 self.chunk.patch_jump(skip_default);
2770 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2772 }
2773 let binding_name = field.alias.as_deref().unwrap_or(&field.key);
2774 let name_idx = self
2775 .chunk
2776 .add_constant(Constant::String(binding_name.to_string()));
2777 self.chunk.emit_u16(def_op, name_idx, self.line);
2778 }
2779
2780 if let Some(rest) = rest_field {
2781 let fn_idx = self
2784 .chunk
2785 .add_constant(Constant::String("__dict_rest".into()));
2786 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
2787 self.chunk.emit(Op::Swap, self.line);
2789 for field in &non_rest {
2791 let key_idx = self.chunk.add_constant(Constant::String(field.key.clone()));
2792 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
2793 }
2794 self.chunk
2795 .emit_u16(Op::BuildList, non_rest.len() as u16, self.line);
2796 self.chunk.emit_u8(Op::Call, 2, self.line);
2798 let rest_name = &rest.key;
2799 let rest_idx = self.chunk.add_constant(Constant::String(rest_name.clone()));
2800 self.chunk.emit_u16(def_op, rest_idx, self.line);
2801 } else {
2802 self.chunk.emit(Op::Pop, self.line);
2804 }
2805 }
2806 BindingPattern::List(elements) => {
2807 self.chunk.emit(Op::Dup, self.line);
2810 let assert_idx = self
2811 .chunk
2812 .add_constant(Constant::String("__assert_list".into()));
2813 self.chunk.emit_u16(Op::Constant, assert_idx, self.line);
2814 self.chunk.emit(Op::Swap, self.line);
2815 self.chunk.emit_u8(Op::Call, 1, self.line);
2816 self.chunk.emit(Op::Pop, self.line); let non_rest: Vec<_> = elements.iter().filter(|e| !e.is_rest).collect();
2819 let rest_elem = elements.iter().find(|e| e.is_rest);
2820
2821 for (i, elem) in non_rest.iter().enumerate() {
2822 self.chunk.emit(Op::Dup, self.line);
2823 let idx_const = self.chunk.add_constant(Constant::Int(i as i64));
2824 self.chunk.emit_u16(Op::Constant, idx_const, self.line);
2825 self.chunk.emit(Op::Subscript, self.line);
2826 if let Some(default_expr) = &elem.default_value {
2828 self.chunk.emit(Op::Dup, self.line);
2829 self.chunk.emit(Op::Nil, self.line);
2830 self.chunk.emit(Op::NotEqual, self.line);
2831 let skip_default = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
2832 self.chunk.emit(Op::Pop, self.line); self.chunk.emit(Op::Pop, self.line); self.compile_node(default_expr)?;
2835 let end = self.chunk.emit_jump(Op::Jump, self.line);
2836 self.chunk.patch_jump(skip_default);
2837 self.chunk.emit(Op::Pop, self.line); self.chunk.patch_jump(end);
2839 }
2840 let name_idx = self.chunk.add_constant(Constant::String(elem.name.clone()));
2841 self.chunk.emit_u16(def_op, name_idx, self.line);
2842 }
2843
2844 if let Some(rest) = rest_elem {
2845 let start_idx = self
2849 .chunk
2850 .add_constant(Constant::Int(non_rest.len() as i64));
2851 self.chunk.emit_u16(Op::Constant, start_idx, self.line);
2852 self.chunk.emit(Op::Nil, self.line); self.chunk.emit(Op::Slice, self.line);
2854 let rest_name_idx =
2855 self.chunk.add_constant(Constant::String(rest.name.clone()));
2856 self.chunk.emit_u16(def_op, rest_name_idx, self.line);
2857 } else {
2858 self.chunk.emit(Op::Pop, self.line);
2860 }
2861 }
2862 }
2863 Ok(())
2864 }
2865
2866 fn produces_value(node: &Node) -> bool {
2868 match node {
2869 Node::LetBinding { .. }
2871 | Node::VarBinding { .. }
2872 | Node::Assignment { .. }
2873 | Node::ReturnStmt { .. }
2874 | Node::FnDecl { .. }
2875 | Node::ToolDecl { .. }
2876 | Node::ImplBlock { .. }
2877 | Node::StructDecl { .. }
2878 | Node::EnumDecl { .. }
2879 | Node::InterfaceDecl { .. }
2880 | Node::TypeDecl { .. }
2881 | Node::ThrowStmt { .. }
2882 | Node::BreakStmt
2883 | Node::ContinueStmt
2884 | Node::RequireStmt { .. }
2885 | Node::DeferStmt { .. } => false,
2886 Node::TryCatch { .. }
2888 | Node::TryExpr { .. }
2889 | Node::Retry { .. }
2890 | Node::GuardStmt { .. }
2891 | Node::DeadlineBlock { .. }
2892 | Node::MutexBlock { .. }
2893 | Node::Spread(_) => true,
2894 _ => true,
2896 }
2897 }
2898}
2899
2900impl Compiler {
2901 pub fn compile_fn_body(
2914 &mut self,
2915 params: &[TypedParam],
2916 body: &[SNode],
2917 source_file: Option<String>,
2918 ) -> Result<CompiledFunction, CompileError> {
2919 let mut fn_compiler = Compiler::new();
2920 fn_compiler.enum_names = self.enum_names.clone();
2921 fn_compiler.emit_default_preamble(params)?;
2922 fn_compiler.emit_type_checks(params);
2923 let is_gen = body_contains_yield(body);
2924 fn_compiler.compile_block(body)?;
2925 fn_compiler.chunk.emit(Op::Nil, 0);
2926 fn_compiler.chunk.emit(Op::Return, 0);
2927 fn_compiler.chunk.source_file = source_file;
2928 Ok(CompiledFunction {
2929 name: String::new(),
2930 params: TypedParam::names(params),
2931 default_start: TypedParam::default_start(params),
2932 chunk: fn_compiler.chunk,
2933 is_generator: is_gen,
2934 has_rest_param: false,
2935 })
2936 }
2937
2938 fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
2940 self.begin_scope();
2941 if body.is_empty() {
2942 self.chunk.emit(Op::Nil, self.line);
2943 } else {
2944 self.compile_block(body)?;
2945 if !Self::produces_value(&body.last().unwrap().node) {
2946 self.chunk.emit(Op::Nil, self.line);
2947 }
2948 }
2949 self.end_scope();
2950 Ok(())
2951 }
2952
2953 fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
2955 match op {
2956 "+" => self.chunk.emit(Op::Add, self.line),
2957 "-" => self.chunk.emit(Op::Sub, self.line),
2958 "*" => self.chunk.emit(Op::Mul, self.line),
2959 "/" => self.chunk.emit(Op::Div, self.line),
2960 "%" => self.chunk.emit(Op::Mod, self.line),
2961 _ => {
2962 return Err(CompileError {
2963 message: format!("Unknown compound operator: {op}"),
2964 line: self.line,
2965 })
2966 }
2967 }
2968 Ok(())
2969 }
2970
2971 fn root_var_name(&self, node: &SNode) -> Option<String> {
2973 match &node.node {
2974 Node::Identifier(name) => Some(name.clone()),
2975 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
2976 self.root_var_name(object)
2977 }
2978 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
2979 _ => None,
2980 }
2981 }
2982
2983 fn compile_top_level_declarations(&mut self, program: &[SNode]) -> Result<(), CompileError> {
2984 for sn in program {
2985 if matches!(
2986 &sn.node,
2987 Node::FnDecl { .. }
2988 | Node::ToolDecl { .. }
2989 | Node::ImplBlock { .. }
2990 | Node::StructDecl { .. }
2991 | Node::EnumDecl { .. }
2992 | Node::InterfaceDecl { .. }
2993 | Node::TypeDecl { .. }
2994 ) {
2995 self.compile_node(sn)?;
2996 }
2997 }
2998 Ok(())
2999 }
3000}
3001
3002impl Compiler {
3003 fn collect_enum_names(nodes: &[SNode], names: &mut std::collections::HashSet<String>) {
3005 for sn in nodes {
3006 match &sn.node {
3007 Node::EnumDecl { name, .. } => {
3008 names.insert(name.clone());
3009 }
3010 Node::Pipeline { body, .. } => {
3011 Self::collect_enum_names(body, names);
3012 }
3013 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
3014 Self::collect_enum_names(body, names);
3015 }
3016 Node::Block(stmts) => {
3017 Self::collect_enum_names(stmts, names);
3018 }
3019 _ => {}
3020 }
3021 }
3022 }
3023
3024 fn collect_interface_methods(
3025 nodes: &[SNode],
3026 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
3027 ) {
3028 for sn in nodes {
3029 match &sn.node {
3030 Node::InterfaceDecl { name, methods, .. } => {
3031 let method_names: Vec<String> =
3032 methods.iter().map(|m| m.name.clone()).collect();
3033 interfaces.insert(name.clone(), method_names);
3034 }
3035 Node::Pipeline { body, .. }
3036 | Node::FnDecl { body, .. }
3037 | Node::ToolDecl { body, .. } => {
3038 Self::collect_interface_methods(body, interfaces);
3039 }
3040 Node::Block(stmts) => {
3041 Self::collect_interface_methods(stmts, interfaces);
3042 }
3043 _ => {}
3044 }
3045 }
3046 }
3047}
3048
3049impl Default for Compiler {
3050 fn default() -> Self {
3051 Self::new()
3052 }
3053}
3054
3055fn body_contains_yield(nodes: &[SNode]) -> bool {
3057 nodes.iter().any(|sn| node_contains_yield(&sn.node))
3058}
3059
3060fn node_contains_yield(node: &Node) -> bool {
3061 match node {
3062 Node::YieldExpr { .. } => true,
3063 Node::FnDecl { .. } | Node::Closure { .. } => false,
3066 Node::Block(stmts) => body_contains_yield(stmts),
3067 Node::IfElse {
3068 condition,
3069 then_body,
3070 else_body,
3071 } => {
3072 node_contains_yield(&condition.node)
3073 || body_contains_yield(then_body)
3074 || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
3075 }
3076 Node::WhileLoop { condition, body } => {
3077 node_contains_yield(&condition.node) || body_contains_yield(body)
3078 }
3079 Node::ForIn { iterable, body, .. } => {
3080 node_contains_yield(&iterable.node) || body_contains_yield(body)
3081 }
3082 Node::TryCatch {
3083 body, catch_body, ..
3084 } => body_contains_yield(body) || body_contains_yield(catch_body),
3085 Node::TryExpr { body } => body_contains_yield(body),
3086 _ => false,
3087 }
3088}
3089
3090fn contains_pipe_placeholder(node: &SNode) -> bool {
3092 match &node.node {
3093 Node::Identifier(name) if name == "_" => true,
3094 Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
3095 Node::MethodCall { object, args, .. } => {
3096 contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
3097 }
3098 Node::BinaryOp { left, right, .. } => {
3099 contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
3100 }
3101 Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
3102 Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
3103 Node::PropertyAccess { object, .. } => contains_pipe_placeholder(object),
3104 Node::SubscriptAccess { object, index } => {
3105 contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
3106 }
3107 _ => false,
3108 }
3109}
3110
3111fn replace_pipe_placeholder(node: &SNode) -> SNode {
3113 let new_node = match &node.node {
3114 Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
3115 Node::FunctionCall { name, args } => Node::FunctionCall {
3116 name: name.clone(),
3117 args: args.iter().map(replace_pipe_placeholder).collect(),
3118 },
3119 Node::MethodCall {
3120 object,
3121 method,
3122 args,
3123 } => Node::MethodCall {
3124 object: Box::new(replace_pipe_placeholder(object)),
3125 method: method.clone(),
3126 args: args.iter().map(replace_pipe_placeholder).collect(),
3127 },
3128 Node::BinaryOp { op, left, right } => Node::BinaryOp {
3129 op: op.clone(),
3130 left: Box::new(replace_pipe_placeholder(left)),
3131 right: Box::new(replace_pipe_placeholder(right)),
3132 },
3133 Node::UnaryOp { op, operand } => Node::UnaryOp {
3134 op: op.clone(),
3135 operand: Box::new(replace_pipe_placeholder(operand)),
3136 },
3137 Node::ListLiteral(items) => {
3138 Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
3139 }
3140 Node::PropertyAccess { object, property } => Node::PropertyAccess {
3141 object: Box::new(replace_pipe_placeholder(object)),
3142 property: property.clone(),
3143 },
3144 Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
3145 object: Box::new(replace_pipe_placeholder(object)),
3146 index: Box::new(replace_pipe_placeholder(index)),
3147 },
3148 _ => return node.clone(),
3149 };
3150 SNode::new(new_node, node.span)
3151}
3152
3153#[cfg(test)]
3154mod tests {
3155 use super::*;
3156 use harn_lexer::Lexer;
3157 use harn_parser::Parser;
3158
3159 fn compile_source(source: &str) -> Chunk {
3160 let mut lexer = Lexer::new(source);
3161 let tokens = lexer.tokenize().unwrap();
3162 let mut parser = Parser::new(tokens);
3163 let program = parser.parse().unwrap();
3164 Compiler::new().compile(&program).unwrap()
3165 }
3166
3167 #[test]
3168 fn test_compile_arithmetic() {
3169 let chunk = compile_source("pipeline test(task) { let x = 2 + 3 }");
3170 assert!(!chunk.code.is_empty());
3171 assert!(chunk.constants.contains(&Constant::Int(2)));
3173 assert!(chunk.constants.contains(&Constant::Int(3)));
3174 }
3175
3176 #[test]
3177 fn test_compile_function_call() {
3178 let chunk = compile_source("pipeline test(task) { log(42) }");
3179 let disasm = chunk.disassemble("test");
3180 assert!(disasm.contains("CALL"));
3181 }
3182
3183 #[test]
3184 fn test_compile_if_else() {
3185 let chunk =
3186 compile_source(r#"pipeline test(task) { if true { log("yes") } else { log("no") } }"#);
3187 let disasm = chunk.disassemble("test");
3188 assert!(disasm.contains("JUMP_IF_FALSE"));
3189 assert!(disasm.contains("JUMP"));
3190 }
3191
3192 #[test]
3193 fn test_compile_while() {
3194 let chunk = compile_source("pipeline test(task) { var i = 0\n while i < 5 { i = i + 1 } }");
3195 let disasm = chunk.disassemble("test");
3196 assert!(disasm.contains("JUMP_IF_FALSE"));
3197 assert!(disasm.contains("JUMP"));
3199 }
3200
3201 #[test]
3202 fn test_compile_closure() {
3203 let chunk = compile_source("pipeline test(task) { let f = { x -> x * 2 } }");
3204 assert!(!chunk.functions.is_empty());
3205 assert_eq!(chunk.functions[0].params, vec!["x"]);
3206 }
3207
3208 #[test]
3209 fn test_compile_list() {
3210 let chunk = compile_source("pipeline test(task) { let a = [1, 2, 3] }");
3211 let disasm = chunk.disassemble("test");
3212 assert!(disasm.contains("BUILD_LIST"));
3213 }
3214
3215 #[test]
3216 fn test_compile_dict() {
3217 let chunk = compile_source(r#"pipeline test(task) { let d = {name: "test"} }"#);
3218 let disasm = chunk.disassemble("test");
3219 assert!(disasm.contains("BUILD_DICT"));
3220 }
3221
3222 #[test]
3223 fn test_disassemble() {
3224 let chunk = compile_source("pipeline test(task) { log(2 + 3) }");
3225 let disasm = chunk.disassemble("test");
3226 assert!(disasm.contains("CONSTANT"));
3228 assert!(disasm.contains("ADD"));
3229 assert!(disasm.contains("CALL"));
3230 }
3231}