1use std::collections::BTreeMap;
2use std::rc::Rc;
3
4use harn_parser::{Node, SNode, ShapeField, TypeExpr, TypedParam};
5
6use crate::chunk::{Chunk, CompiledFunction, Constant, Op};
7use crate::value::VmValue;
8
9use super::error::CompileError;
10use super::yield_scan::body_contains_yield;
11use super::{peel_node, Compiler, FinallyEntry};
12
13impl Compiler {
14 pub fn new() -> Self {
15 Self {
16 chunk: Chunk::new(),
17 line: 1,
18 column: 1,
19 enum_names: std::collections::HashSet::new(),
20 interface_methods: std::collections::HashMap::new(),
21 loop_stack: Vec::new(),
22 handler_depth: 0,
23 finally_bodies: Vec::new(),
24 temp_counter: 0,
25 scope_depth: 0,
26 type_aliases: std::collections::HashMap::new(),
27 module_level: true,
28 }
29 }
30
31 pub(super) fn for_nested_body() -> Self {
35 let mut c = Self::new();
36 c.module_level = false;
37 c
38 }
39
40 pub(super) fn collect_type_aliases(&mut self, program: &[SNode]) {
44 for sn in program {
45 if let Node::TypeDecl {
46 name,
47 type_expr,
48 type_params: _,
49 } = &sn.node
50 {
51 self.type_aliases.insert(name.clone(), type_expr.clone());
52 }
53 }
54 }
55
56 pub(super) fn expand_alias(&self, ty: &TypeExpr) -> TypeExpr {
60 match ty {
61 TypeExpr::Named(name) => {
62 if let Some(target) = self.type_aliases.get(name) {
63 self.expand_alias(target)
64 } else {
65 TypeExpr::Named(name.clone())
66 }
67 }
68 TypeExpr::Union(types) => {
69 TypeExpr::Union(types.iter().map(|t| self.expand_alias(t)).collect())
70 }
71 TypeExpr::Shape(fields) => TypeExpr::Shape(
72 fields
73 .iter()
74 .map(|field| ShapeField {
75 name: field.name.clone(),
76 type_expr: self.expand_alias(&field.type_expr),
77 optional: field.optional,
78 })
79 .collect(),
80 ),
81 TypeExpr::List(inner) => TypeExpr::List(Box::new(self.expand_alias(inner))),
82 TypeExpr::Iter(inner) => TypeExpr::Iter(Box::new(self.expand_alias(inner))),
83 TypeExpr::DictType(k, v) => TypeExpr::DictType(
84 Box::new(self.expand_alias(k)),
85 Box::new(self.expand_alias(v)),
86 ),
87 TypeExpr::FnType {
88 params,
89 return_type,
90 } => TypeExpr::FnType {
91 params: params.iter().map(|p| self.expand_alias(p)).collect(),
92 return_type: Box::new(self.expand_alias(return_type)),
93 },
94 TypeExpr::Applied { name, args } => TypeExpr::Applied {
95 name: name.clone(),
96 args: args.iter().map(|a| self.expand_alias(a)).collect(),
97 },
98 TypeExpr::Never => TypeExpr::Never,
99 TypeExpr::LitString(s) => TypeExpr::LitString(s.clone()),
100 TypeExpr::LitInt(v) => TypeExpr::LitInt(*v),
101 }
102 }
103
104 pub(super) fn schema_value_for_alias(&self, name: &str) -> Option<VmValue> {
107 let ty = self.type_aliases.get(name)?;
108 let expanded = self.expand_alias(ty);
109 Self::type_expr_to_schema_value(&expanded)
110 }
111
112 pub(super) fn is_schema_guard(name: &str) -> bool {
116 matches!(
117 name,
118 "schema_is"
119 | "schema_expect"
120 | "schema_parse"
121 | "schema_check"
122 | "is_type"
123 | "json_validate"
124 )
125 }
126
127 pub(super) fn entry_key_is(key: &SNode, keyword: &str) -> bool {
130 matches!(
131 &key.node,
132 Node::Identifier(name) | Node::StringLiteral(name) | Node::RawStringLiteral(name)
133 if name == keyword
134 )
135 }
136
137 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
140 Self::collect_enum_names(program, &mut self.enum_names);
143 self.enum_names.insert("Result".to_string());
144 Self::collect_interface_methods(program, &mut self.interface_methods);
145 self.collect_type_aliases(program);
146
147 for sn in program {
148 match &sn.node {
149 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
150 self.compile_node(sn)?;
151 }
152 _ => {}
153 }
154 }
155 let main = program
156 .iter()
157 .find(|sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == "default"))
158 .or_else(|| {
159 program
160 .iter()
161 .find(|sn| matches!(peel_node(sn), Node::Pipeline { .. }))
162 });
163
164 if let Some(sn) = main {
165 self.compile_top_level_declarations(program)?;
166 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
167 if let Some(parent_name) = extends {
168 self.compile_parent_pipeline(program, parent_name)?;
169 }
170 let saved = std::mem::replace(&mut self.module_level, false);
171 self.compile_block(body)?;
172 self.module_level = saved;
173 }
174 } else {
175 let top_level: Vec<&SNode> = program
177 .iter()
178 .filter(|sn| {
179 !matches!(
180 &sn.node,
181 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
182 )
183 })
184 .collect();
185 for sn in &top_level {
186 self.compile_node(sn)?;
187 if Self::produces_value(&sn.node) {
188 self.chunk.emit(Op::Pop, self.line);
189 }
190 }
191 }
192
193 for fb in self.all_pending_finallys() {
194 self.compile_finally_inline(&fb)?;
195 }
196 self.chunk.emit(Op::Nil, self.line);
197 self.chunk.emit(Op::Return, self.line);
198 Ok(self.chunk)
199 }
200
201 pub fn compile_named(
203 mut self,
204 program: &[SNode],
205 pipeline_name: &str,
206 ) -> Result<Chunk, CompileError> {
207 Self::collect_enum_names(program, &mut self.enum_names);
208 Self::collect_interface_methods(program, &mut self.interface_methods);
209 self.collect_type_aliases(program);
210
211 for sn in program {
212 if matches!(
213 &sn.node,
214 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
215 ) {
216 self.compile_node(sn)?;
217 }
218 }
219 let target = program.iter().find(
220 |sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == pipeline_name),
221 );
222
223 if let Some(sn) = target {
224 self.compile_top_level_declarations(program)?;
225 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
226 if let Some(parent_name) = extends {
227 self.compile_parent_pipeline(program, parent_name)?;
228 }
229 let saved = std::mem::replace(&mut self.module_level, false);
230 self.compile_block(body)?;
231 self.module_level = saved;
232 }
233 }
234
235 for fb in self.all_pending_finallys() {
236 self.compile_finally_inline(&fb)?;
237 }
238 self.chunk.emit(Op::Nil, self.line);
239 self.chunk.emit(Op::Return, self.line);
240 Ok(self.chunk)
241 }
242
243 pub(super) fn compile_parent_pipeline(
245 &mut self,
246 program: &[SNode],
247 parent_name: &str,
248 ) -> Result<(), CompileError> {
249 let parent = program
250 .iter()
251 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
252 if let Some(sn) = parent {
253 if let Node::Pipeline { body, extends, .. } = &sn.node {
254 if let Some(grandparent) = extends {
255 self.compile_parent_pipeline(program, grandparent)?;
256 }
257 for stmt in body {
258 self.compile_node(stmt)?;
259 if Self::produces_value(&stmt.node) {
260 self.chunk.emit(Op::Pop, self.line);
261 }
262 }
263 }
264 }
265 Ok(())
266 }
267
268 pub(super) fn emit_default_preamble(
273 &mut self,
274 params: &[TypedParam],
275 ) -> Result<(), CompileError> {
276 for (i, param) in params.iter().enumerate() {
277 if let Some(default_expr) = ¶m.default_value {
278 self.chunk.emit(Op::GetArgc, self.line);
279 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
280 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
281 self.chunk.emit(Op::GreaterEqual, self.line);
282 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
283 self.chunk.emit(Op::Pop, self.line);
285 self.compile_node(default_expr)?;
286 let name_idx = self
287 .chunk
288 .add_constant(Constant::String(param.name.clone()));
289 self.chunk.emit_u16(Op::DefLet, name_idx, self.line);
290 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
291 self.chunk.patch_jump(skip_jump);
292 self.chunk.emit(Op::Pop, self.line);
293 self.chunk.patch_jump(end_jump);
294 }
295 }
296 Ok(())
297 }
298
299 pub(super) fn emit_type_checks(&mut self, params: &[TypedParam]) {
304 for param in params {
305 if let Some(type_expr) = ¶m.type_expr {
306 if let harn_parser::TypeExpr::Named(name) = type_expr {
307 if let Some(methods) = self.interface_methods.get(name) {
308 let fn_idx = self
309 .chunk
310 .add_constant(Constant::String("__assert_interface".into()));
311 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
312 let var_idx = self
313 .chunk
314 .add_constant(Constant::String(param.name.clone()));
315 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
316 let name_idx = self
317 .chunk
318 .add_constant(Constant::String(param.name.clone()));
319 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
320 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
321 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
322 let methods_str = methods.join(",");
323 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
324 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
325 self.chunk.emit_u8(Op::Call, 4, self.line);
326 self.chunk.emit(Op::Pop, self.line);
327 continue;
328 }
329 }
330
331 if let Some(schema) = Self::type_expr_to_schema_value(type_expr) {
332 let fn_idx = self
333 .chunk
334 .add_constant(Constant::String("__assert_schema".into()));
335 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
336 let var_idx = self
337 .chunk
338 .add_constant(Constant::String(param.name.clone()));
339 self.chunk.emit_u16(Op::GetVar, var_idx, self.line);
340 let name_idx = self
341 .chunk
342 .add_constant(Constant::String(param.name.clone()));
343 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
344 self.emit_vm_value_literal(&schema);
345 self.chunk.emit_u8(Op::Call, 3, self.line);
346 self.chunk.emit(Op::Pop, self.line);
347 }
348 }
349 }
350 }
351
352 pub(crate) fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
353 match type_expr {
354 harn_parser::TypeExpr::Named(name) => match name.as_str() {
355 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
356 | "closure" | "bytes" => Some(VmValue::Dict(Rc::new(BTreeMap::from([(
357 "type".to_string(),
358 VmValue::String(Rc::from(name.as_str())),
359 )])))),
360 _ => None,
361 },
362 harn_parser::TypeExpr::Shape(fields) => {
363 let mut properties = BTreeMap::new();
364 let mut required = Vec::new();
365 for field in fields {
366 let field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
367 properties.insert(field.name.clone(), field_schema);
368 if !field.optional {
369 required.push(VmValue::String(Rc::from(field.name.as_str())));
370 }
371 }
372 let mut out = BTreeMap::new();
373 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
374 out.insert("properties".to_string(), VmValue::Dict(Rc::new(properties)));
375 if !required.is_empty() {
376 out.insert("required".to_string(), VmValue::List(Rc::new(required)));
377 }
378 Some(VmValue::Dict(Rc::new(out)))
379 }
380 harn_parser::TypeExpr::List(inner) => {
381 let mut out = BTreeMap::new();
382 out.insert("type".to_string(), VmValue::String(Rc::from("list")));
383 if let Some(item_schema) = Self::type_expr_to_schema_value(inner) {
384 out.insert("items".to_string(), item_schema);
385 }
386 Some(VmValue::Dict(Rc::new(out)))
387 }
388 harn_parser::TypeExpr::DictType(key, value) => {
389 let mut out = BTreeMap::new();
390 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
391 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
392 if let Some(value_schema) = Self::type_expr_to_schema_value(value) {
393 out.insert("additional_properties".to_string(), value_schema);
394 }
395 }
396 Some(VmValue::Dict(Rc::new(out)))
397 }
398 harn_parser::TypeExpr::Union(members) => {
399 if !members.is_empty()
404 && members
405 .iter()
406 .all(|m| matches!(m, harn_parser::TypeExpr::LitString(_)))
407 {
408 let values = members
409 .iter()
410 .map(|m| match m {
411 harn_parser::TypeExpr::LitString(s) => {
412 VmValue::String(Rc::from(s.as_str()))
413 }
414 _ => unreachable!(),
415 })
416 .collect::<Vec<_>>();
417 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
418 ("type".to_string(), VmValue::String(Rc::from("string"))),
419 ("enum".to_string(), VmValue::List(Rc::new(values))),
420 ]))));
421 }
422 if !members.is_empty()
423 && members
424 .iter()
425 .all(|m| matches!(m, harn_parser::TypeExpr::LitInt(_)))
426 {
427 let values = members
428 .iter()
429 .map(|m| match m {
430 harn_parser::TypeExpr::LitInt(v) => VmValue::Int(*v),
431 _ => unreachable!(),
432 })
433 .collect::<Vec<_>>();
434 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
435 ("type".to_string(), VmValue::String(Rc::from("int"))),
436 ("enum".to_string(), VmValue::List(Rc::new(values))),
437 ]))));
438 }
439 let branches = members
440 .iter()
441 .filter_map(Self::type_expr_to_schema_value)
442 .collect::<Vec<_>>();
443 if branches.is_empty() {
444 None
445 } else {
446 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
447 "union".to_string(),
448 VmValue::List(Rc::new(branches)),
449 )]))))
450 }
451 }
452 harn_parser::TypeExpr::FnType { .. } => {
453 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
454 "type".to_string(),
455 VmValue::String(Rc::from("closure")),
456 )]))))
457 }
458 harn_parser::TypeExpr::Applied { .. } => None,
459 harn_parser::TypeExpr::Iter(_) => None,
460 harn_parser::TypeExpr::Never => None,
461 harn_parser::TypeExpr::LitString(s) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
462 ("type".to_string(), VmValue::String(Rc::from("string"))),
463 ("const".to_string(), VmValue::String(Rc::from(s.as_str()))),
464 ])))),
465 harn_parser::TypeExpr::LitInt(v) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
466 ("type".to_string(), VmValue::String(Rc::from("int"))),
467 ("const".to_string(), VmValue::Int(*v)),
468 ])))),
469 }
470 }
471
472 pub(super) fn emit_vm_value_literal(&mut self, value: &VmValue) {
473 match value {
474 VmValue::String(text) => {
475 let idx = self.chunk.add_constant(Constant::String(text.to_string()));
476 self.chunk.emit_u16(Op::Constant, idx, self.line);
477 }
478 VmValue::Int(number) => {
479 let idx = self.chunk.add_constant(Constant::Int(*number));
480 self.chunk.emit_u16(Op::Constant, idx, self.line);
481 }
482 VmValue::Float(number) => {
483 let idx = self.chunk.add_constant(Constant::Float(*number));
484 self.chunk.emit_u16(Op::Constant, idx, self.line);
485 }
486 VmValue::Bool(value) => {
487 let idx = self.chunk.add_constant(Constant::Bool(*value));
488 self.chunk.emit_u16(Op::Constant, idx, self.line);
489 }
490 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
491 VmValue::List(items) => {
492 for item in items.iter() {
493 self.emit_vm_value_literal(item);
494 }
495 self.chunk
496 .emit_u16(Op::BuildList, items.len() as u16, self.line);
497 }
498 VmValue::Dict(entries) => {
499 for (key, item) in entries.iter() {
500 let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
501 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
502 self.emit_vm_value_literal(item);
503 }
504 self.chunk
505 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
506 }
507 _ => {}
508 }
509 }
510
511 pub(super) fn emit_type_name_extra(&mut self, type_name_idx: u16) {
513 let hi = (type_name_idx >> 8) as u8;
514 let lo = type_name_idx as u8;
515 self.chunk.code.push(hi);
516 self.chunk.code.push(lo);
517 self.chunk.lines.push(self.line);
518 self.chunk.columns.push(self.column);
519 self.chunk.lines.push(self.line);
520 self.chunk.columns.push(self.column);
521 }
522
523 pub(super) fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
525 if body.is_empty() {
526 self.chunk.emit(Op::Nil, self.line);
527 } else {
528 self.compile_scoped_block(body)?;
529 }
530 Ok(())
531 }
532
533 pub(super) fn compile_catch_binding(
535 &mut self,
536 error_var: &Option<String>,
537 ) -> Result<(), CompileError> {
538 if let Some(var_name) = error_var {
539 let idx = self.chunk.add_constant(Constant::String(var_name.clone()));
540 self.chunk.emit_u16(Op::DefLet, idx, self.line);
541 } else {
542 self.chunk.emit(Op::Pop, self.line);
543 }
544 Ok(())
545 }
546
547 pub(super) fn compile_finally_inline(
554 &mut self,
555 finally_body: &[SNode],
556 ) -> Result<(), CompileError> {
557 if !finally_body.is_empty() {
558 self.compile_scoped_block(finally_body)?;
559 self.chunk.emit(Op::Pop, self.line);
560 }
561 Ok(())
562 }
563
564 pub(super) fn pending_finallys_until_barrier(&self) -> Vec<Vec<SNode>> {
569 let mut out = Vec::new();
570 for entry in self.finally_bodies.iter().rev() {
571 match entry {
572 FinallyEntry::CatchBarrier => break,
573 FinallyEntry::Finally(body) => out.push(body.clone()),
574 }
575 }
576 out
577 }
578
579 pub(super) fn pending_finallys_down_to(&self, floor: usize) -> Vec<Vec<SNode>> {
585 let mut out = Vec::new();
586 for entry in self.finally_bodies[floor..].iter().rev() {
587 if let FinallyEntry::Finally(body) = entry {
588 out.push(body.clone());
589 }
590 }
591 out
592 }
593
594 pub(super) fn all_pending_finallys(&self) -> Vec<Vec<SNode>> {
596 self.pending_finallys_down_to(0)
597 }
598
599 pub(super) fn has_pending_finally(&self) -> bool {
601 self.finally_bodies
602 .iter()
603 .any(|e| matches!(e, FinallyEntry::Finally(_)))
604 }
605
606 pub(super) fn compile_plain_rethrow(&mut self) -> Result<(), CompileError> {
615 self.temp_counter += 1;
616 let temp_name = format!("__finally_err_{}__", self.temp_counter);
617 let err_idx = self.chunk.add_constant(Constant::String(temp_name.clone()));
618 self.chunk.emit_u16(Op::DefVar, err_idx, self.line);
619 let get_idx = self.chunk.add_constant(Constant::String(temp_name));
620 self.chunk.emit_u16(Op::GetVar, get_idx, self.line);
621 self.chunk.emit(Op::Throw, self.line);
622 Ok(())
623 }
624
625 pub(super) fn begin_scope(&mut self) {
626 self.chunk.emit(Op::PushScope, self.line);
627 self.scope_depth += 1;
628 }
629
630 pub(super) fn end_scope(&mut self) {
631 if self.scope_depth > 0 {
632 self.chunk.emit(Op::PopScope, self.line);
633 self.scope_depth -= 1;
634 }
635 }
636
637 pub(super) fn unwind_scopes_to(&mut self, target_depth: usize) {
638 while self.scope_depth > target_depth {
639 self.chunk.emit(Op::PopScope, self.line);
640 self.scope_depth -= 1;
641 }
642 }
643
644 pub(super) fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
645 self.begin_scope();
646 if stmts.is_empty() {
647 self.chunk.emit(Op::Nil, self.line);
648 } else {
649 self.compile_block(stmts)?;
650 }
651 self.end_scope();
652 Ok(())
653 }
654
655 pub(super) fn compile_scoped_statements(
656 &mut self,
657 stmts: &[SNode],
658 ) -> Result<(), CompileError> {
659 self.begin_scope();
660 for sn in stmts {
661 self.compile_node(sn)?;
662 if Self::produces_value(&sn.node) {
663 self.chunk.emit(Op::Pop, self.line);
664 }
665 }
666 self.end_scope();
667 Ok(())
668 }
669
670 pub(super) fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
671 for (i, snode) in stmts.iter().enumerate() {
672 self.compile_node(snode)?;
673 let is_last = i == stmts.len() - 1;
674 if is_last {
675 if !Self::produces_value(&snode.node) {
677 self.chunk.emit(Op::Nil, self.line);
678 }
679 } else if Self::produces_value(&snode.node) {
680 self.chunk.emit(Op::Pop, self.line);
681 }
682 }
683 Ok(())
684 }
685
686 pub(super) fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
688 self.begin_scope();
689 if body.is_empty() {
690 self.chunk.emit(Op::Nil, self.line);
691 } else {
692 self.compile_block(body)?;
693 if !Self::produces_value(&body.last().unwrap().node) {
694 self.chunk.emit(Op::Nil, self.line);
695 }
696 }
697 self.end_scope();
698 Ok(())
699 }
700
701 pub(super) fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
703 match op {
704 "+" => self.chunk.emit(Op::Add, self.line),
705 "-" => self.chunk.emit(Op::Sub, self.line),
706 "*" => self.chunk.emit(Op::Mul, self.line),
707 "/" => self.chunk.emit(Op::Div, self.line),
708 "%" => self.chunk.emit(Op::Mod, self.line),
709 _ => {
710 return Err(CompileError {
711 message: format!("Unknown compound operator: {op}"),
712 line: self.line,
713 })
714 }
715 }
716 Ok(())
717 }
718
719 pub(super) fn root_var_name(&self, node: &SNode) -> Option<String> {
721 match &node.node {
722 Node::Identifier(name) => Some(name.clone()),
723 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
724 self.root_var_name(object)
725 }
726 Node::SubscriptAccess { object, .. } => self.root_var_name(object),
727 _ => None,
728 }
729 }
730
731 pub(super) fn compile_top_level_declarations(
732 &mut self,
733 program: &[SNode],
734 ) -> Result<(), CompileError> {
735 for sn in program {
743 if matches!(&sn.node, Node::LetBinding { .. } | Node::VarBinding { .. }) {
744 self.compile_node(sn)?;
745 }
746 }
747 for sn in program {
753 let inner_kind = match &sn.node {
754 Node::AttributedDecl { inner, .. } => &inner.node,
755 other => other,
756 };
757 if matches!(
758 inner_kind,
759 Node::FnDecl { .. }
760 | Node::ToolDecl { .. }
761 | Node::SkillDecl { .. }
762 | Node::ImplBlock { .. }
763 | Node::StructDecl { .. }
764 | Node::EnumDecl { .. }
765 | Node::InterfaceDecl { .. }
766 | Node::TypeDecl { .. }
767 ) {
768 self.compile_node(sn)?;
769 }
770 }
771 Ok(())
772 }
773
774 pub(super) fn collect_enum_names(
776 nodes: &[SNode],
777 names: &mut std::collections::HashSet<String>,
778 ) {
779 for sn in nodes {
780 match &sn.node {
781 Node::EnumDecl { name, .. } => {
782 names.insert(name.clone());
783 }
784 Node::Pipeline { body, .. } => {
785 Self::collect_enum_names(body, names);
786 }
787 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
788 Self::collect_enum_names(body, names);
789 }
790 Node::SkillDecl { fields, .. } => {
791 for (_k, v) in fields {
792 Self::collect_enum_names(std::slice::from_ref(v), names);
793 }
794 }
795 Node::Block(stmts) => {
796 Self::collect_enum_names(stmts, names);
797 }
798 Node::AttributedDecl { inner, .. } => {
799 Self::collect_enum_names(std::slice::from_ref(inner), names);
800 }
801 _ => {}
802 }
803 }
804 }
805
806 pub(super) fn collect_interface_methods(
807 nodes: &[SNode],
808 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
809 ) {
810 for sn in nodes {
811 match &sn.node {
812 Node::InterfaceDecl { name, methods, .. } => {
813 let method_names: Vec<String> =
814 methods.iter().map(|m| m.name.clone()).collect();
815 interfaces.insert(name.clone(), method_names);
816 }
817 Node::Pipeline { body, .. }
818 | Node::FnDecl { body, .. }
819 | Node::ToolDecl { body, .. } => {
820 Self::collect_interface_methods(body, interfaces);
821 }
822 Node::SkillDecl { fields, .. } => {
823 for (_k, v) in fields {
824 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
825 }
826 }
827 Node::Block(stmts) => {
828 Self::collect_interface_methods(stmts, interfaces);
829 }
830 Node::AttributedDecl { inner, .. } => {
831 Self::collect_interface_methods(std::slice::from_ref(inner), interfaces);
832 }
833 _ => {}
834 }
835 }
836 }
837
838 pub fn compile_fn_body(
851 &mut self,
852 params: &[TypedParam],
853 body: &[SNode],
854 source_file: Option<String>,
855 ) -> Result<CompiledFunction, CompileError> {
856 let mut fn_compiler = Compiler::for_nested_body();
857 fn_compiler.enum_names = self.enum_names.clone();
858 fn_compiler.emit_default_preamble(params)?;
859 fn_compiler.emit_type_checks(params);
860 let is_gen = body_contains_yield(body);
861 fn_compiler.compile_block(body)?;
862 fn_compiler.chunk.emit(Op::Nil, 0);
863 fn_compiler.chunk.emit(Op::Return, 0);
864 fn_compiler.chunk.source_file = source_file;
865 Ok(CompiledFunction {
866 name: String::new(),
867 params: TypedParam::names(params),
868 default_start: TypedParam::default_start(params),
869 chunk: fn_compiler.chunk,
870 is_generator: is_gen,
871 has_rest_param: false,
872 })
873 }
874
875 pub(super) fn produces_value(node: &Node) -> bool {
877 match node {
878 Node::LetBinding { .. }
879 | Node::VarBinding { .. }
880 | Node::Assignment { .. }
881 | Node::ReturnStmt { .. }
882 | Node::FnDecl { .. }
883 | Node::ToolDecl { .. }
884 | Node::SkillDecl { .. }
885 | Node::ImplBlock { .. }
886 | Node::StructDecl { .. }
887 | Node::EnumDecl { .. }
888 | Node::InterfaceDecl { .. }
889 | Node::TypeDecl { .. }
890 | Node::ThrowStmt { .. }
891 | Node::BreakStmt
892 | Node::ContinueStmt
893 | Node::RequireStmt { .. }
894 | Node::DeferStmt { .. } => false,
895 Node::TryCatch { .. }
896 | Node::TryExpr { .. }
897 | Node::Retry { .. }
898 | Node::GuardStmt { .. }
899 | Node::DeadlineBlock { .. }
900 | Node::MutexBlock { .. }
901 | Node::Spread(_) => true,
902 _ => true,
903 }
904 }
905}
906
907impl Default for Compiler {
908 fn default() -> Self {
909 Self::new()
910 }
911}