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 struct_layouts: std::collections::HashMap::new(),
21 interface_methods: std::collections::HashMap::new(),
22 loop_stack: Vec::new(),
23 handler_depth: 0,
24 finally_bodies: Vec::new(),
25 temp_counter: 0,
26 scope_depth: 0,
27 type_aliases: std::collections::HashMap::new(),
28 type_scopes: vec![std::collections::HashMap::new()],
29 local_scopes: vec![std::collections::HashMap::new()],
30 module_level: true,
31 }
32 }
33
34 pub(super) fn for_nested_body() -> Self {
38 let mut c = Self::new();
39 c.module_level = false;
40 c
41 }
42
43 pub(super) fn collect_type_aliases(&mut self, program: &[SNode]) {
47 for sn in program {
48 if let Node::TypeDecl {
49 name,
50 type_expr,
51 type_params: _,
52 } = &sn.node
53 {
54 self.type_aliases.insert(name.clone(), type_expr.clone());
55 }
56 }
57 }
58
59 pub(super) fn expand_alias(&self, ty: &TypeExpr) -> TypeExpr {
63 match ty {
64 TypeExpr::Named(name) => {
65 if let Some(target) = self.type_aliases.get(name) {
66 self.expand_alias(target)
67 } else {
68 TypeExpr::Named(name.clone())
69 }
70 }
71 TypeExpr::Union(types) => {
72 TypeExpr::Union(types.iter().map(|t| self.expand_alias(t)).collect())
73 }
74 TypeExpr::Shape(fields) => TypeExpr::Shape(
75 fields
76 .iter()
77 .map(|field| ShapeField {
78 name: field.name.clone(),
79 type_expr: self.expand_alias(&field.type_expr),
80 optional: field.optional,
81 })
82 .collect(),
83 ),
84 TypeExpr::List(inner) => TypeExpr::List(Box::new(self.expand_alias(inner))),
85 TypeExpr::Iter(inner) => TypeExpr::Iter(Box::new(self.expand_alias(inner))),
86 TypeExpr::Generator(inner) => TypeExpr::Generator(Box::new(self.expand_alias(inner))),
87 TypeExpr::Stream(inner) => TypeExpr::Stream(Box::new(self.expand_alias(inner))),
88 TypeExpr::DictType(k, v) => TypeExpr::DictType(
89 Box::new(self.expand_alias(k)),
90 Box::new(self.expand_alias(v)),
91 ),
92 TypeExpr::FnType {
93 params,
94 return_type,
95 } => TypeExpr::FnType {
96 params: params.iter().map(|p| self.expand_alias(p)).collect(),
97 return_type: Box::new(self.expand_alias(return_type)),
98 },
99 TypeExpr::Applied { name, args } => TypeExpr::Applied {
100 name: name.clone(),
101 args: args.iter().map(|a| self.expand_alias(a)).collect(),
102 },
103 TypeExpr::Never => TypeExpr::Never,
104 TypeExpr::LitString(s) => TypeExpr::LitString(s.clone()),
105 TypeExpr::LitInt(v) => TypeExpr::LitInt(*v),
106 }
107 }
108
109 pub(super) fn schema_value_for_alias(&self, name: &str) -> Option<VmValue> {
112 let ty = self.type_aliases.get(name)?;
113 let expanded = self.expand_alias(ty);
114 Self::type_expr_to_schema_value(&expanded)
115 }
116
117 pub(super) fn is_schema_guard(name: &str) -> bool {
121 matches!(
122 name,
123 "schema_is"
124 | "schema_expect"
125 | "schema_parse"
126 | "schema_check"
127 | "is_type"
128 | "json_validate"
129 )
130 }
131
132 pub(super) fn entry_key_is(key: &SNode, keyword: &str) -> bool {
135 matches!(
136 &key.node,
137 Node::Identifier(name) | Node::StringLiteral(name) | Node::RawStringLiteral(name)
138 if name == keyword
139 )
140 }
141
142 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
145 Self::collect_enum_names(program, &mut self.enum_names);
148 self.enum_names.insert("Result".to_string());
149 Self::collect_struct_layouts(program, &mut self.struct_layouts);
150 Self::collect_interface_methods(program, &mut self.interface_methods);
151 self.collect_type_aliases(program);
152
153 for sn in program {
154 match &sn.node {
155 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
156 self.compile_node(sn)?;
157 }
158 _ => {}
159 }
160 }
161 let main = program
162 .iter()
163 .find(|sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == "default"))
164 .or_else(|| {
165 program
166 .iter()
167 .find(|sn| matches!(peel_node(sn), Node::Pipeline { .. }))
168 });
169
170 let mut pipeline_emits_value = false;
174 if let Some(sn) = main {
175 self.compile_top_level_declarations(program)?;
176 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
177 if let Some(parent_name) = extends {
178 self.compile_parent_pipeline(program, parent_name)?;
179 }
180 let saved = std::mem::replace(&mut self.module_level, false);
181 self.compile_block(body)?;
182 self.module_level = saved;
183 pipeline_emits_value = true;
184 }
185 } else {
186 let top_level: Vec<&SNode> = program
188 .iter()
189 .filter(|sn| {
190 !matches!(
191 &sn.node,
192 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
193 )
194 })
195 .collect();
196 for sn in &top_level {
197 self.compile_node(sn)?;
198 if Self::produces_value(&sn.node) {
199 self.chunk.emit(Op::Pop, self.line);
200 }
201 }
202 }
203
204 for fb in self.all_pending_finallys() {
205 self.compile_finally_inline(&fb)?;
206 }
207 if !pipeline_emits_value {
208 self.chunk.emit(Op::Nil, self.line);
209 }
210 self.chunk.emit(Op::Return, self.line);
211 Ok(self.chunk)
212 }
213
214 pub fn compile_named(
216 mut self,
217 program: &[SNode],
218 pipeline_name: &str,
219 ) -> Result<Chunk, CompileError> {
220 Self::collect_enum_names(program, &mut self.enum_names);
221 self.enum_names.insert("Result".to_string());
222 Self::collect_struct_layouts(program, &mut self.struct_layouts);
223 Self::collect_interface_methods(program, &mut self.interface_methods);
224 self.collect_type_aliases(program);
225
226 for sn in program {
227 if matches!(
228 &sn.node,
229 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
230 ) {
231 self.compile_node(sn)?;
232 }
233 }
234 let target = program.iter().find(
235 |sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == pipeline_name),
236 );
237
238 if let Some(sn) = target {
239 self.compile_top_level_declarations(program)?;
240 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
241 if let Some(parent_name) = extends {
242 self.compile_parent_pipeline(program, parent_name)?;
243 }
244 let saved = std::mem::replace(&mut self.module_level, false);
245 self.compile_block(body)?;
246 self.module_level = saved;
247 }
248 }
249
250 for fb in self.all_pending_finallys() {
251 self.compile_finally_inline(&fb)?;
252 }
253 self.chunk.emit(Op::Nil, self.line);
254 self.chunk.emit(Op::Return, self.line);
255 Ok(self.chunk)
256 }
257
258 pub(super) fn compile_parent_pipeline(
260 &mut self,
261 program: &[SNode],
262 parent_name: &str,
263 ) -> Result<(), CompileError> {
264 let parent = program
265 .iter()
266 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
267 if let Some(sn) = parent {
268 if let Node::Pipeline { body, extends, .. } = &sn.node {
269 if let Some(grandparent) = extends {
270 self.compile_parent_pipeline(program, grandparent)?;
271 }
272 for stmt in body {
273 self.compile_node(stmt)?;
274 if Self::produces_value(&stmt.node) {
275 self.chunk.emit(Op::Pop, self.line);
276 }
277 }
278 }
279 }
280 Ok(())
281 }
282
283 pub(super) fn emit_default_preamble(
288 &mut self,
289 params: &[TypedParam],
290 ) -> Result<(), CompileError> {
291 for (i, param) in params.iter().enumerate() {
292 if let Some(default_expr) = ¶m.default_value {
293 self.chunk.emit(Op::GetArgc, self.line);
294 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
295 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
296 self.chunk.emit(Op::GreaterEqual, self.line);
297 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
298 self.chunk.emit(Op::Pop, self.line);
300 self.compile_node(default_expr)?;
301 self.emit_init_or_define_binding(¶m.name, false);
302 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
303 self.chunk.patch_jump(skip_jump);
304 self.chunk.emit(Op::Pop, self.line);
305 self.chunk.patch_jump(end_jump);
306 }
307 }
308 Ok(())
309 }
310
311 pub(super) fn emit_type_checks(&mut self, params: &[TypedParam]) {
316 for param in params {
317 if let Some(type_expr) = ¶m.type_expr {
318 if let harn_parser::TypeExpr::Named(name) = type_expr {
319 if let Some(methods) = self.interface_methods.get(name).cloned() {
320 let fn_idx = self
321 .chunk
322 .add_constant(Constant::String("__assert_interface".into()));
323 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
324 self.emit_get_binding(¶m.name);
325 let name_idx = self
326 .chunk
327 .add_constant(Constant::String(param.name.clone()));
328 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
329 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
330 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
331 let methods_str = methods.join(",");
332 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
333 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
334 self.chunk.emit_u8(Op::Call, 4, self.line);
335 self.chunk.emit(Op::Pop, self.line);
336 continue;
337 }
338 }
339
340 if let Some(schema) = Self::type_expr_to_schema_value(type_expr) {
341 let fn_idx = self
342 .chunk
343 .add_constant(Constant::String("__assert_schema".into()));
344 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
345 self.emit_get_binding(¶m.name);
346 let name_idx = self
347 .chunk
348 .add_constant(Constant::String(param.name.clone()));
349 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
350 self.emit_vm_value_literal(&schema);
351 self.chunk.emit_u8(Op::Call, 3, self.line);
352 self.chunk.emit(Op::Pop, self.line);
353 }
354 }
355 }
356 }
357
358 pub(crate) fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
359 match type_expr {
360 harn_parser::TypeExpr::Named(name) => match name.as_str() {
361 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
362 | "closure" | "bytes" => Some(VmValue::Dict(Rc::new(BTreeMap::from([(
363 "type".to_string(),
364 VmValue::String(Rc::from(name.as_str())),
365 )])))),
366 _ => None,
367 },
368 harn_parser::TypeExpr::Shape(fields) => {
369 let mut properties = BTreeMap::new();
370 let mut required = Vec::new();
371 for field in fields {
372 let field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
373 properties.insert(field.name.clone(), field_schema);
374 if !field.optional {
375 required.push(VmValue::String(Rc::from(field.name.as_str())));
376 }
377 }
378 let mut out = BTreeMap::new();
379 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
380 out.insert("properties".to_string(), VmValue::Dict(Rc::new(properties)));
381 if !required.is_empty() {
382 out.insert("required".to_string(), VmValue::List(Rc::new(required)));
383 }
384 Some(VmValue::Dict(Rc::new(out)))
385 }
386 harn_parser::TypeExpr::List(inner) => {
387 let mut out = BTreeMap::new();
388 out.insert("type".to_string(), VmValue::String(Rc::from("list")));
389 if let Some(item_schema) = Self::type_expr_to_schema_value(inner) {
390 out.insert("items".to_string(), item_schema);
391 }
392 Some(VmValue::Dict(Rc::new(out)))
393 }
394 harn_parser::TypeExpr::DictType(key, value) => {
395 let mut out = BTreeMap::new();
396 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
397 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
398 if let Some(value_schema) = Self::type_expr_to_schema_value(value) {
399 out.insert("additional_properties".to_string(), value_schema);
400 }
401 }
402 Some(VmValue::Dict(Rc::new(out)))
403 }
404 harn_parser::TypeExpr::Union(members) => {
405 if !members.is_empty()
410 && members
411 .iter()
412 .all(|m| matches!(m, harn_parser::TypeExpr::LitString(_)))
413 {
414 let values = members
415 .iter()
416 .map(|m| match m {
417 harn_parser::TypeExpr::LitString(s) => {
418 VmValue::String(Rc::from(s.as_str()))
419 }
420 _ => unreachable!(),
421 })
422 .collect::<Vec<_>>();
423 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
424 ("type".to_string(), VmValue::String(Rc::from("string"))),
425 ("enum".to_string(), VmValue::List(Rc::new(values))),
426 ]))));
427 }
428 if !members.is_empty()
429 && members
430 .iter()
431 .all(|m| matches!(m, harn_parser::TypeExpr::LitInt(_)))
432 {
433 let values = members
434 .iter()
435 .map(|m| match m {
436 harn_parser::TypeExpr::LitInt(v) => VmValue::Int(*v),
437 _ => unreachable!(),
438 })
439 .collect::<Vec<_>>();
440 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
441 ("type".to_string(), VmValue::String(Rc::from("int"))),
442 ("enum".to_string(), VmValue::List(Rc::new(values))),
443 ]))));
444 }
445 let branches = members
446 .iter()
447 .filter_map(Self::type_expr_to_schema_value)
448 .collect::<Vec<_>>();
449 if branches.is_empty() {
450 None
451 } else {
452 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
453 "union".to_string(),
454 VmValue::List(Rc::new(branches)),
455 )]))))
456 }
457 }
458 harn_parser::TypeExpr::FnType { .. } => {
459 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
460 "type".to_string(),
461 VmValue::String(Rc::from("closure")),
462 )]))))
463 }
464 harn_parser::TypeExpr::Applied { .. } => None,
465 harn_parser::TypeExpr::Iter(_)
466 | harn_parser::TypeExpr::Generator(_)
467 | harn_parser::TypeExpr::Stream(_) => None,
468 harn_parser::TypeExpr::Never => None,
469 harn_parser::TypeExpr::LitString(s) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
470 ("type".to_string(), VmValue::String(Rc::from("string"))),
471 ("const".to_string(), VmValue::String(Rc::from(s.as_str()))),
472 ])))),
473 harn_parser::TypeExpr::LitInt(v) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
474 ("type".to_string(), VmValue::String(Rc::from("int"))),
475 ("const".to_string(), VmValue::Int(*v)),
476 ])))),
477 }
478 }
479
480 pub(super) fn emit_vm_value_literal(&mut self, value: &VmValue) {
481 match value {
482 VmValue::String(text) => {
483 let idx = self.chunk.add_constant(Constant::String(text.to_string()));
484 self.chunk.emit_u16(Op::Constant, idx, self.line);
485 }
486 VmValue::Int(number) => {
487 let idx = self.chunk.add_constant(Constant::Int(*number));
488 self.chunk.emit_u16(Op::Constant, idx, self.line);
489 }
490 VmValue::Float(number) => {
491 let idx = self.chunk.add_constant(Constant::Float(*number));
492 self.chunk.emit_u16(Op::Constant, idx, self.line);
493 }
494 VmValue::Bool(value) => {
495 let idx = self.chunk.add_constant(Constant::Bool(*value));
496 self.chunk.emit_u16(Op::Constant, idx, self.line);
497 }
498 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
499 VmValue::List(items) => {
500 for item in items.iter() {
501 self.emit_vm_value_literal(item);
502 }
503 self.chunk
504 .emit_u16(Op::BuildList, items.len() as u16, self.line);
505 }
506 VmValue::Dict(entries) => {
507 for (key, item) in entries.iter() {
508 let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
509 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
510 self.emit_vm_value_literal(item);
511 }
512 self.chunk
513 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
514 }
515 _ => {}
516 }
517 }
518
519 pub(super) fn emit_type_name_extra(&mut self, type_name_idx: u16) {
521 let hi = (type_name_idx >> 8) as u8;
522 let lo = type_name_idx as u8;
523 self.chunk.code.push(hi);
524 self.chunk.code.push(lo);
525 self.chunk.lines.push(self.line);
526 self.chunk.columns.push(self.column);
527 self.chunk.lines.push(self.line);
528 self.chunk.columns.push(self.column);
529 }
530
531 pub(super) fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
533 if body.is_empty() {
534 self.chunk.emit(Op::Nil, self.line);
535 } else {
536 self.compile_scoped_block(body)?;
537 }
538 Ok(())
539 }
540
541 pub(super) fn compile_catch_binding(
543 &mut self,
544 error_var: &Option<String>,
545 ) -> Result<(), CompileError> {
546 if let Some(var_name) = error_var {
547 self.emit_define_binding(var_name, false);
548 } else {
549 self.chunk.emit(Op::Pop, self.line);
550 }
551 Ok(())
552 }
553
554 pub(super) fn compile_finally_inline(
561 &mut self,
562 finally_body: &[SNode],
563 ) -> Result<(), CompileError> {
564 if !finally_body.is_empty() {
565 self.compile_scoped_block(finally_body)?;
566 self.chunk.emit(Op::Pop, self.line);
567 }
568 Ok(())
569 }
570
571 pub(super) fn pending_finallys_until_barrier(&self) -> Vec<Vec<SNode>> {
576 let mut out = Vec::new();
577 for entry in self.finally_bodies.iter().rev() {
578 match entry {
579 FinallyEntry::CatchBarrier => break,
580 FinallyEntry::Finally(body) => out.push(body.clone()),
581 }
582 }
583 out
584 }
585
586 pub(super) fn pending_finallys_down_to(&self, floor: usize) -> Vec<Vec<SNode>> {
592 let mut out = Vec::new();
593 for entry in self.finally_bodies[floor..].iter().rev() {
594 if let FinallyEntry::Finally(body) = entry {
595 out.push(body.clone());
596 }
597 }
598 out
599 }
600
601 pub(super) fn all_pending_finallys(&self) -> Vec<Vec<SNode>> {
603 self.pending_finallys_down_to(0)
604 }
605
606 pub(super) fn has_pending_finally(&self) -> bool {
608 self.finally_bodies
609 .iter()
610 .any(|e| matches!(e, FinallyEntry::Finally(_)))
611 }
612
613 pub(super) fn compile_plain_rethrow(&mut self) -> Result<(), CompileError> {
622 self.temp_counter += 1;
623 let temp_name = format!("__finally_err_{}__", self.temp_counter);
624 self.emit_define_binding(&temp_name, true);
625 self.emit_get_binding(&temp_name);
626 self.chunk.emit(Op::Throw, self.line);
627 Ok(())
628 }
629
630 pub(super) fn declare_param_slots(&mut self, params: &[TypedParam]) {
631 for param in params {
632 self.define_local_slot(¶m.name, false);
633 }
634 }
635
636 fn define_local_slot(&mut self, name: &str, mutable: bool) -> Option<u16> {
637 if self.module_level || harn_parser::is_discard_name(name) {
638 return None;
639 }
640 let current = self.local_scopes.last_mut()?;
641 if let Some(existing) = current.get_mut(name) {
642 if existing.mutable || mutable {
643 if mutable {
644 existing.mutable = true;
645 if let Some(info) = self.chunk.local_slots.get_mut(existing.slot as usize) {
646 info.mutable = true;
647 }
648 }
649 return Some(existing.slot);
650 }
651 return None;
652 }
653 let slot = self
654 .chunk
655 .add_local_slot(name.to_string(), mutable, self.scope_depth);
656 current.insert(name.to_string(), super::LocalBinding { slot, mutable });
657 Some(slot)
658 }
659
660 pub(super) fn resolve_local_slot(&self, name: &str) -> Option<super::LocalBinding> {
661 if self.module_level {
662 return None;
663 }
664 self.local_scopes
665 .iter()
666 .rev()
667 .find_map(|scope| scope.get(name).copied())
668 }
669
670 pub(super) fn emit_get_binding(&mut self, name: &str) {
671 if let Some(binding) = self.resolve_local_slot(name) {
672 self.chunk
673 .emit_u16(Op::GetLocalSlot, binding.slot, self.line);
674 } else {
675 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
676 self.chunk.emit_u16(Op::GetVar, idx, self.line);
677 }
678 }
679
680 pub(super) fn emit_define_binding(&mut self, name: &str, mutable: bool) {
681 if let Some(slot) = self.define_local_slot(name, mutable) {
682 self.chunk.emit_u16(Op::DefLocalSlot, slot, self.line);
683 } else {
684 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
685 let op = if mutable { Op::DefVar } else { Op::DefLet };
686 self.chunk.emit_u16(op, idx, self.line);
687 }
688 }
689
690 pub(super) fn emit_init_or_define_binding(&mut self, name: &str, mutable: bool) {
691 if let Some(binding) = self.resolve_local_slot(name) {
692 self.chunk
693 .emit_u16(Op::DefLocalSlot, binding.slot, self.line);
694 } else {
695 self.emit_define_binding(name, mutable);
696 }
697 }
698
699 pub(super) fn emit_set_binding(&mut self, name: &str) {
700 if let Some(binding) = self.resolve_local_slot(name) {
701 let _ = binding.mutable;
702 self.chunk
703 .emit_u16(Op::SetLocalSlot, binding.slot, self.line);
704 } else {
705 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
706 self.chunk.emit_u16(Op::SetVar, idx, self.line);
707 }
708 }
709
710 pub(super) fn begin_scope(&mut self) {
711 self.chunk.emit(Op::PushScope, self.line);
712 self.scope_depth += 1;
713 self.type_scopes.push(std::collections::HashMap::new());
714 self.local_scopes.push(std::collections::HashMap::new());
715 }
716
717 pub(super) fn end_scope(&mut self) {
718 if self.scope_depth > 0 {
719 self.chunk.emit(Op::PopScope, self.line);
720 self.scope_depth -= 1;
721 self.type_scopes.pop();
722 self.local_scopes.pop();
723 }
724 }
725
726 pub(super) fn unwind_scopes_to(&mut self, target_depth: usize) {
727 while self.scope_depth > target_depth {
728 self.chunk.emit(Op::PopScope, self.line);
729 self.scope_depth -= 1;
730 self.type_scopes.pop();
731 self.local_scopes.pop();
732 }
733 }
734
735 pub(super) fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
736 self.begin_scope();
737 if stmts.is_empty() {
738 self.chunk.emit(Op::Nil, self.line);
739 } else {
740 self.compile_block(stmts)?;
741 }
742 self.end_scope();
743 Ok(())
744 }
745
746 pub(super) fn compile_scoped_statements(
747 &mut self,
748 stmts: &[SNode],
749 ) -> Result<(), CompileError> {
750 self.begin_scope();
751 for sn in stmts {
752 self.compile_node(sn)?;
753 if Self::produces_value(&sn.node) {
754 self.chunk.emit(Op::Pop, self.line);
755 }
756 }
757 self.end_scope();
758 Ok(())
759 }
760
761 pub(super) fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
762 for (i, snode) in stmts.iter().enumerate() {
763 self.compile_node(snode)?;
764 let is_last = i == stmts.len() - 1;
765 if is_last {
766 if !Self::produces_value(&snode.node) {
768 self.chunk.emit(Op::Nil, self.line);
769 }
770 } else if Self::produces_value(&snode.node) {
771 self.chunk.emit(Op::Pop, self.line);
772 }
773 }
774 Ok(())
775 }
776
777 pub(super) fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
779 self.begin_scope();
780 if body.is_empty() {
781 self.chunk.emit(Op::Nil, self.line);
782 } else {
783 self.compile_block(body)?;
784 if !Self::produces_value(&body.last().unwrap().node) {
785 self.chunk.emit(Op::Nil, self.line);
786 }
787 }
788 self.end_scope();
789 Ok(())
790 }
791
792 pub(super) fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
794 match op {
795 "+" => self.chunk.emit(Op::Add, self.line),
796 "-" => self.chunk.emit(Op::Sub, self.line),
797 "*" => self.chunk.emit(Op::Mul, self.line),
798 "/" => self.chunk.emit(Op::Div, self.line),
799 "%" => self.chunk.emit(Op::Mod, self.line),
800 _ => {
801 return Err(CompileError {
802 message: format!("Unknown compound operator: {op}"),
803 line: self.line,
804 })
805 }
806 }
807 Ok(())
808 }
809
810 pub(super) fn root_var_name(&self, node: &SNode) -> Option<String> {
812 match &node.node {
813 Node::Identifier(name) => Some(name.clone()),
814 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
815 self.root_var_name(object)
816 }
817 Node::SubscriptAccess { object, .. } | Node::OptionalSubscriptAccess { object, .. } => {
818 self.root_var_name(object)
819 }
820 _ => None,
821 }
822 }
823
824 pub(super) fn compile_top_level_declarations(
825 &mut self,
826 program: &[SNode],
827 ) -> Result<(), CompileError> {
828 for sn in program {
836 if matches!(&sn.node, Node::LetBinding { .. } | Node::VarBinding { .. }) {
837 self.compile_node(sn)?;
838 }
839 }
840 for sn in program {
846 let inner_kind = match &sn.node {
847 Node::AttributedDecl { inner, .. } => &inner.node,
848 other => other,
849 };
850 if matches!(
851 inner_kind,
852 Node::FnDecl { .. }
853 | Node::ToolDecl { .. }
854 | Node::SkillDecl { .. }
855 | Node::ImplBlock { .. }
856 | Node::StructDecl { .. }
857 | Node::EnumDecl { .. }
858 | Node::InterfaceDecl { .. }
859 | Node::TypeDecl { .. }
860 ) {
861 self.compile_node(sn)?;
862 }
863 }
864 Ok(())
865 }
866
867 pub(super) fn collect_enum_names(
869 nodes: &[SNode],
870 names: &mut std::collections::HashSet<String>,
871 ) {
872 for sn in nodes {
873 match &sn.node {
874 Node::EnumDecl { name, .. } => {
875 names.insert(name.clone());
876 }
877 Node::Pipeline { body, .. } => {
878 Self::collect_enum_names(body, names);
879 }
880 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
881 Self::collect_enum_names(body, names);
882 }
883 Node::SkillDecl { fields, .. } => {
884 for (_k, v) in fields {
885 Self::collect_enum_names(std::slice::from_ref(v), names);
886 }
887 }
888 Node::Block(stmts) => {
889 Self::collect_enum_names(stmts, names);
890 }
891 Node::AttributedDecl { inner, .. } => {
892 Self::collect_enum_names(std::slice::from_ref(inner), names);
893 }
894 _ => {}
895 }
896 }
897 }
898
899 pub(super) fn collect_struct_layouts(
900 nodes: &[SNode],
901 layouts: &mut std::collections::HashMap<String, Vec<String>>,
902 ) {
903 for sn in nodes {
904 match &sn.node {
905 Node::StructDecl { name, fields, .. } => {
906 layouts.insert(
907 name.clone(),
908 fields.iter().map(|field| field.name.clone()).collect(),
909 );
910 }
911 Node::Pipeline { body, .. }
912 | Node::FnDecl { body, .. }
913 | Node::ToolDecl { body, .. } => {
914 Self::collect_struct_layouts(body, layouts);
915 }
916 Node::SkillDecl { fields, .. } => {
917 for (_k, v) in fields {
918 Self::collect_struct_layouts(std::slice::from_ref(v), layouts);
919 }
920 }
921 Node::Block(stmts) => {
922 Self::collect_struct_layouts(stmts, layouts);
923 }
924 Node::AttributedDecl { inner, .. } => {
925 Self::collect_struct_layouts(std::slice::from_ref(inner), layouts);
926 }
927 _ => {}
928 }
929 }
930 }
931
932 pub(super) fn collect_interface_methods(
933 nodes: &[SNode],
934 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
935 ) {
936 for sn in nodes {
937 match &sn.node {
938 Node::InterfaceDecl { name, methods, .. } => {
939 let method_names: Vec<String> =
940 methods.iter().map(|m| m.name.clone()).collect();
941 interfaces.insert(name.clone(), method_names);
942 }
943 Node::Pipeline { body, .. }
944 | Node::FnDecl { body, .. }
945 | Node::ToolDecl { body, .. } => {
946 Self::collect_interface_methods(body, interfaces);
947 }
948 Node::SkillDecl { fields, .. } => {
949 for (_k, v) in fields {
950 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
951 }
952 }
953 Node::Block(stmts) => {
954 Self::collect_interface_methods(stmts, interfaces);
955 }
956 Node::AttributedDecl { inner, .. } => {
957 Self::collect_interface_methods(std::slice::from_ref(inner), interfaces);
958 }
959 _ => {}
960 }
961 }
962 }
963
964 pub fn compile_fn_body(
977 &mut self,
978 params: &[TypedParam],
979 body: &[SNode],
980 source_file: Option<String>,
981 ) -> Result<CompiledFunction, CompileError> {
982 let mut fn_compiler = Compiler::for_nested_body();
983 fn_compiler.enum_names = self.enum_names.clone();
984 fn_compiler.interface_methods = self.interface_methods.clone();
985 fn_compiler.type_aliases = self.type_aliases.clone();
986 fn_compiler.struct_layouts = self.struct_layouts.clone();
987 fn_compiler.declare_param_slots(params);
988 fn_compiler.record_param_types(params);
989 fn_compiler.emit_default_preamble(params)?;
990 fn_compiler.emit_type_checks(params);
991 let is_gen = body_contains_yield(body);
992 fn_compiler.compile_block(body)?;
993 fn_compiler.chunk.emit(Op::Nil, 0);
994 fn_compiler.chunk.emit(Op::Return, 0);
995 fn_compiler.chunk.source_file = source_file;
996 Ok(CompiledFunction {
997 name: String::new(),
998 params: TypedParam::names(params),
999 default_start: TypedParam::default_start(params),
1000 chunk: Rc::new(fn_compiler.chunk),
1001 is_generator: is_gen,
1002 is_stream: false,
1003 has_rest_param: false,
1004 })
1005 }
1006
1007 pub(super) fn produces_value(node: &Node) -> bool {
1009 match node {
1010 Node::LetBinding { .. }
1011 | Node::VarBinding { .. }
1012 | Node::Assignment { .. }
1013 | Node::ReturnStmt { .. }
1014 | Node::FnDecl { .. }
1015 | Node::ToolDecl { .. }
1016 | Node::SkillDecl { .. }
1017 | Node::ImplBlock { .. }
1018 | Node::StructDecl { .. }
1019 | Node::EnumDecl { .. }
1020 | Node::InterfaceDecl { .. }
1021 | Node::TypeDecl { .. }
1022 | Node::ThrowStmt { .. }
1023 | Node::BreakStmt
1024 | Node::ContinueStmt
1025 | Node::RequireStmt { .. }
1026 | Node::DeferStmt { .. } => false,
1027 Node::TryCatch { .. }
1028 | Node::TryExpr { .. }
1029 | Node::Retry { .. }
1030 | Node::GuardStmt { .. }
1031 | Node::DeadlineBlock { .. }
1032 | Node::MutexBlock { .. }
1033 | Node::Spread(_) => true,
1034 _ => true,
1035 }
1036 }
1037}
1038
1039impl Default for Compiler {
1040 fn default() -> Self {
1041 Self::new()
1042 }
1043}