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