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