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, CompilerOptions, FinallyEntry};
12
13impl Compiler {
14 pub fn new() -> Self {
15 Self::with_options(CompilerOptions::from_env())
16 }
17
18 pub fn with_options(options: CompilerOptions) -> Self {
19 Self {
20 options,
21 chunk: Chunk::new(),
22 line: 1,
23 column: 1,
24 enum_names: std::collections::HashSet::new(),
25 struct_layouts: std::collections::HashMap::new(),
26 interface_methods: std::collections::HashMap::new(),
27 loop_stack: Vec::new(),
28 handler_depth: 0,
29 finally_bodies: Vec::new(),
30 temp_counter: 0,
31 scope_depth: 0,
32 type_aliases: std::collections::HashMap::new(),
33 type_scopes: vec![std::collections::HashMap::new()],
34 local_scopes: vec![std::collections::HashMap::new()],
35 module_level: true,
36 }
37 }
38
39 pub(super) fn for_nested_body(options: CompilerOptions) -> Self {
43 let mut c = Self::with_options(options);
44 c.module_level = false;
45 c
46 }
47
48 pub(super) fn nested_body(&self) -> Self {
49 Self::for_nested_body(self.options)
50 }
51
52 pub(super) fn nominal_type_names(&self) -> Vec<String> {
53 let mut names: Vec<String> = self
54 .struct_layouts
55 .keys()
56 .chain(self.enum_names.iter())
57 .cloned()
58 .collect();
59 names.sort();
60 names.dedup();
61 names
62 }
63
64 pub(super) fn collect_type_aliases(&mut self, program: &[SNode]) {
68 for sn in program {
69 if let Node::TypeDecl {
70 name,
71 type_expr,
72 type_params: _,
73 } = &sn.node
74 {
75 self.type_aliases.insert(name.clone(), type_expr.clone());
76 }
77 }
78 }
79
80 pub(super) fn expand_alias(&self, ty: &TypeExpr) -> TypeExpr {
84 match ty {
85 TypeExpr::Named(name) => {
86 if let Some(target) = self.type_aliases.get(name) {
87 self.expand_alias(target)
88 } else {
89 TypeExpr::Named(name.clone())
90 }
91 }
92 TypeExpr::Union(types) => {
93 TypeExpr::Union(types.iter().map(|t| self.expand_alias(t)).collect())
94 }
95 TypeExpr::Intersection(types) => {
96 TypeExpr::Intersection(types.iter().map(|t| self.expand_alias(t)).collect())
97 }
98 TypeExpr::Shape(fields) => TypeExpr::Shape(
99 fields
100 .iter()
101 .map(|field| ShapeField {
102 name: field.name.clone(),
103 type_expr: self.expand_alias(&field.type_expr),
104 optional: field.optional,
105 })
106 .collect(),
107 ),
108 TypeExpr::List(inner) => TypeExpr::List(Box::new(self.expand_alias(inner))),
109 TypeExpr::Iter(inner) => TypeExpr::Iter(Box::new(self.expand_alias(inner))),
110 TypeExpr::Generator(inner) => TypeExpr::Generator(Box::new(self.expand_alias(inner))),
111 TypeExpr::Stream(inner) => TypeExpr::Stream(Box::new(self.expand_alias(inner))),
112 TypeExpr::DictType(k, v) => TypeExpr::DictType(
113 Box::new(self.expand_alias(k)),
114 Box::new(self.expand_alias(v)),
115 ),
116 TypeExpr::FnType {
117 params,
118 return_type,
119 } => TypeExpr::FnType {
120 params: params.iter().map(|p| self.expand_alias(p)).collect(),
121 return_type: Box::new(self.expand_alias(return_type)),
122 },
123 TypeExpr::Applied { name, args } => TypeExpr::Applied {
124 name: name.clone(),
125 args: args.iter().map(|a| self.expand_alias(a)).collect(),
126 },
127 TypeExpr::Never => TypeExpr::Never,
128 TypeExpr::LitString(s) => TypeExpr::LitString(s.clone()),
129 TypeExpr::LitInt(v) => TypeExpr::LitInt(*v),
130 TypeExpr::Owned(inner) => TypeExpr::Owned(Box::new(self.expand_alias(inner))),
131 }
132 }
133
134 pub(super) fn schema_value_for_alias(&self, name: &str) -> Option<VmValue> {
137 let ty = self.type_aliases.get(name)?;
138 let expanded = self.expand_alias(ty);
139 Self::type_expr_to_schema_value(&expanded)
140 }
141
142 pub(super) fn is_schema_guard(name: &str) -> bool {
146 matches!(
147 name,
148 "schema_is"
149 | "schema_expect"
150 | "schema_parse"
151 | "schema_check"
152 | "is_type"
153 | "json_validate"
154 )
155 }
156
157 pub(super) fn entry_key_is(key: &SNode, keyword: &str) -> bool {
160 matches!(
161 &key.node,
162 Node::Identifier(name) | Node::StringLiteral(name) | Node::RawStringLiteral(name)
163 if name == keyword
164 )
165 }
166
167 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
170 Self::collect_enum_names(program, &mut self.enum_names);
173 self.enum_names.insert("Result".to_string());
174 Self::collect_struct_layouts(program, &mut self.struct_layouts);
175 Self::collect_interface_methods(program, &mut self.interface_methods);
176 self.collect_type_aliases(program);
177
178 for sn in program {
179 match &sn.node {
180 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
181 self.compile_node(sn)?;
182 }
183 _ => {}
184 }
185 }
186 let main = program
187 .iter()
188 .find(|sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == "default"))
189 .or_else(|| {
190 program
191 .iter()
192 .find(|sn| matches!(peel_node(sn), Node::Pipeline { .. }))
193 });
194
195 let mut pipeline_emits_value = false;
199 if let Some(sn) = main {
200 self.compile_top_level_declarations(program)?;
201 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
202 if let Some(parent_name) = extends {
203 self.compile_parent_pipeline(program, parent_name)?;
204 }
205 let saved = std::mem::replace(&mut self.module_level, false);
206 self.compile_block(body)?;
207 self.module_level = saved;
208 pipeline_emits_value = true;
209 }
210 } else {
211 let top_level: Vec<&SNode> = program
213 .iter()
214 .filter(|sn| {
215 !matches!(
216 &sn.node,
217 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
218 )
219 })
220 .collect();
221 for sn in &top_level {
222 self.compile_node(sn)?;
223 if Self::produces_value(&sn.node) {
224 self.chunk.emit(Op::Pop, self.line);
225 }
226 }
227 if Self::has_top_level_fn_main(program) {
232 let harness_name = self.chunk.add_constant(Constant::String("harness".into()));
233 self.chunk.emit_u16(Op::GetVar, harness_name, self.line);
234 self.emit_named_call("main", 1);
235 pipeline_emits_value = true;
236 }
237 }
238
239 for fb in self.all_pending_finallys() {
240 self.compile_finally_inline(&fb)?;
241 }
242 if !pipeline_emits_value {
243 self.chunk.emit(Op::Nil, self.line);
244 }
245 self.chunk.emit(Op::Return, self.line);
246 Ok(self.chunk)
247 }
248
249 fn has_top_level_fn_main(program: &[SNode]) -> bool {
253 program
254 .iter()
255 .any(|sn| matches!(peel_node(sn), Node::FnDecl { name, .. } if name == "main"))
256 }
257
258 pub fn compile_named(
260 mut self,
261 program: &[SNode],
262 pipeline_name: &str,
263 ) -> Result<Chunk, CompileError> {
264 Self::collect_enum_names(program, &mut self.enum_names);
265 self.enum_names.insert("Result".to_string());
266 Self::collect_struct_layouts(program, &mut self.struct_layouts);
267 Self::collect_interface_methods(program, &mut self.interface_methods);
268 self.collect_type_aliases(program);
269
270 for sn in program {
271 if matches!(
272 &sn.node,
273 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
274 ) {
275 self.compile_node(sn)?;
276 }
277 }
278 let target = program.iter().find(
279 |sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == pipeline_name),
280 );
281
282 if let Some(sn) = target {
283 self.compile_top_level_declarations(program)?;
284 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
285 if let Some(parent_name) = extends {
286 self.compile_parent_pipeline(program, parent_name)?;
287 }
288 let saved = std::mem::replace(&mut self.module_level, false);
289 self.compile_block(body)?;
290 self.module_level = saved;
291 }
292 }
293
294 for fb in self.all_pending_finallys() {
295 self.compile_finally_inline(&fb)?;
296 }
297 self.chunk.emit(Op::Nil, self.line);
298 self.chunk.emit(Op::Return, self.line);
299 Ok(self.chunk)
300 }
301
302 pub(super) fn compile_parent_pipeline(
304 &mut self,
305 program: &[SNode],
306 parent_name: &str,
307 ) -> Result<(), CompileError> {
308 let parent = program
309 .iter()
310 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
311 if let Some(sn) = parent {
312 if let Node::Pipeline { body, extends, .. } = &sn.node {
313 if let Some(grandparent) = extends {
314 self.compile_parent_pipeline(program, grandparent)?;
315 }
316 for stmt in body {
317 self.compile_node(stmt)?;
318 if Self::produces_value(&stmt.node) {
319 self.chunk.emit(Op::Pop, self.line);
320 }
321 }
322 }
323 }
324 Ok(())
325 }
326
327 pub(super) fn emit_default_preamble(
332 &mut self,
333 params: &[TypedParam],
334 ) -> Result<(), CompileError> {
335 for (i, param) in params.iter().enumerate() {
336 if let Some(default_expr) = ¶m.default_value {
337 self.chunk.emit(Op::GetArgc, self.line);
338 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
339 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
340 self.chunk.emit(Op::GreaterEqual, self.line);
341 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
342 self.chunk.emit(Op::Pop, self.line);
344 self.compile_node(default_expr)?;
345 self.emit_init_or_define_binding(¶m.name, false);
346 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
347 self.chunk.patch_jump(skip_jump);
348 self.chunk.emit(Op::Pop, self.line);
349 self.chunk.patch_jump(end_jump);
350 }
351 }
352 Ok(())
353 }
354
355 pub(super) fn emit_type_checks(&mut self, params: &[TypedParam]) {
360 for param in params {
361 if let Some(type_expr) = ¶m.type_expr {
362 let check_type = if param.rest {
363 harn_parser::TypeExpr::List(Box::new(type_expr.clone()))
364 } else {
365 type_expr.clone()
366 };
367
368 if let harn_parser::TypeExpr::Named(name) = &check_type {
369 if let Some(methods) = self.interface_methods.get(name).cloned() {
370 let fn_idx = self
371 .chunk
372 .add_constant(Constant::String("__assert_interface".into()));
373 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
374 self.emit_get_binding(¶m.name);
375 let name_idx = self
376 .chunk
377 .add_constant(Constant::String(param.name.clone()));
378 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
379 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
380 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
381 let methods_str = methods.join(",");
382 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
383 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
384 self.chunk.emit_u8(Op::Call, 4, self.line);
385 self.chunk.emit(Op::Pop, self.line);
386 continue;
387 }
388 }
389
390 if let Some(schema) = Self::type_expr_to_schema_value(&check_type) {
391 let fn_idx = self
392 .chunk
393 .add_constant(Constant::String("__assert_schema".into()));
394 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
395 self.emit_get_binding(¶m.name);
396 let name_idx = self
397 .chunk
398 .add_constant(Constant::String(param.name.clone()));
399 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
400 self.emit_vm_value_literal(&schema);
401 self.chunk.emit_u8(Op::Call, 3, self.line);
402 self.chunk.emit(Op::Pop, self.line);
403 }
404 }
405 }
406 }
407
408 pub(crate) fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
409 match type_expr {
410 harn_parser::TypeExpr::Named(name) => match name.as_str() {
411 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
412 | "closure" | "bytes" => Some(VmValue::Dict(Rc::new(BTreeMap::from([(
413 "type".to_string(),
414 VmValue::String(Rc::from(name.as_str())),
415 )])))),
416 _ => None,
417 },
418 harn_parser::TypeExpr::Shape(fields) => {
419 let mut properties = BTreeMap::new();
420 let mut required = Vec::new();
421 for field in fields {
422 let field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
423 properties.insert(field.name.clone(), field_schema);
424 if !field.optional {
425 required.push(VmValue::String(Rc::from(field.name.as_str())));
426 }
427 }
428 let mut out = BTreeMap::new();
429 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
430 out.insert("properties".to_string(), VmValue::Dict(Rc::new(properties)));
431 if !required.is_empty() {
432 out.insert("required".to_string(), VmValue::List(Rc::new(required)));
433 }
434 Some(VmValue::Dict(Rc::new(out)))
435 }
436 harn_parser::TypeExpr::List(inner) => {
437 let mut out = BTreeMap::new();
438 out.insert("type".to_string(), VmValue::String(Rc::from("list")));
439 if let Some(item_schema) = Self::type_expr_to_schema_value(inner) {
440 out.insert("items".to_string(), item_schema);
441 }
442 Some(VmValue::Dict(Rc::new(out)))
443 }
444 harn_parser::TypeExpr::DictType(key, value) => {
445 let mut out = BTreeMap::new();
446 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
447 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
448 if let Some(value_schema) = Self::type_expr_to_schema_value(value) {
449 out.insert("additional_properties".to_string(), value_schema);
450 }
451 }
452 Some(VmValue::Dict(Rc::new(out)))
453 }
454 harn_parser::TypeExpr::Union(members) => {
455 if !members.is_empty()
460 && members
461 .iter()
462 .all(|m| matches!(m, harn_parser::TypeExpr::LitString(_)))
463 {
464 let values = members
465 .iter()
466 .map(|m| match m {
467 harn_parser::TypeExpr::LitString(s) => {
468 VmValue::String(Rc::from(s.as_str()))
469 }
470 _ => unreachable!(),
471 })
472 .collect::<Vec<_>>();
473 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
474 ("type".to_string(), VmValue::String(Rc::from("string"))),
475 ("enum".to_string(), VmValue::List(Rc::new(values))),
476 ]))));
477 }
478 if !members.is_empty()
479 && members
480 .iter()
481 .all(|m| matches!(m, harn_parser::TypeExpr::LitInt(_)))
482 {
483 let values = members
484 .iter()
485 .map(|m| match m {
486 harn_parser::TypeExpr::LitInt(v) => VmValue::Int(*v),
487 _ => unreachable!(),
488 })
489 .collect::<Vec<_>>();
490 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
491 ("type".to_string(), VmValue::String(Rc::from("int"))),
492 ("enum".to_string(), VmValue::List(Rc::new(values))),
493 ]))));
494 }
495 let branches = members
496 .iter()
497 .filter_map(Self::type_expr_to_schema_value)
498 .collect::<Vec<_>>();
499 if branches.is_empty() {
500 None
501 } else {
502 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
503 "union".to_string(),
504 VmValue::List(Rc::new(branches)),
505 )]))))
506 }
507 }
508 harn_parser::TypeExpr::Intersection(members) => {
509 let branches = members
513 .iter()
514 .filter_map(Self::type_expr_to_schema_value)
515 .collect::<Vec<_>>();
516 if branches.is_empty() {
517 None
518 } else {
519 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
520 "all_of".to_string(),
521 VmValue::List(Rc::new(branches)),
522 )]))))
523 }
524 }
525 harn_parser::TypeExpr::FnType { .. } => {
526 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
527 "type".to_string(),
528 VmValue::String(Rc::from("closure")),
529 )]))))
530 }
531 harn_parser::TypeExpr::Applied { .. } => None,
532 harn_parser::TypeExpr::Iter(_)
533 | harn_parser::TypeExpr::Generator(_)
534 | harn_parser::TypeExpr::Stream(_) => None,
535 harn_parser::TypeExpr::Never => None,
536 harn_parser::TypeExpr::LitString(s) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
537 ("type".to_string(), VmValue::String(Rc::from("string"))),
538 ("const".to_string(), VmValue::String(Rc::from(s.as_str()))),
539 ])))),
540 harn_parser::TypeExpr::LitInt(v) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
541 ("type".to_string(), VmValue::String(Rc::from("int"))),
542 ("const".to_string(), VmValue::Int(*v)),
543 ])))),
544 harn_parser::TypeExpr::Owned(inner) => Self::type_expr_to_schema_value(inner),
545 }
546 }
547
548 pub(super) fn emit_vm_value_literal(&mut self, value: &VmValue) {
549 match value {
550 VmValue::String(text) => {
551 let idx = self.chunk.add_constant(Constant::String(text.to_string()));
552 self.chunk.emit_u16(Op::Constant, idx, self.line);
553 }
554 VmValue::Int(number) => {
555 let idx = self.chunk.add_constant(Constant::Int(*number));
556 self.chunk.emit_u16(Op::Constant, idx, self.line);
557 }
558 VmValue::Float(number) => {
559 let idx = self.chunk.add_constant(Constant::Float(*number));
560 self.chunk.emit_u16(Op::Constant, idx, self.line);
561 }
562 VmValue::Bool(value) => {
563 let idx = self.chunk.add_constant(Constant::Bool(*value));
564 self.chunk.emit_u16(Op::Constant, idx, self.line);
565 }
566 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
567 VmValue::List(items) => {
568 for item in items.iter() {
569 self.emit_vm_value_literal(item);
570 }
571 self.chunk
572 .emit_u16(Op::BuildList, items.len() as u16, self.line);
573 }
574 VmValue::Dict(entries) => {
575 for (key, item) in entries.iter() {
576 let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
577 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
578 self.emit_vm_value_literal(item);
579 }
580 self.chunk
581 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
582 }
583 _ => {}
584 }
585 }
586
587 pub(super) fn emit_type_name_extra(&mut self, type_name_idx: u16) {
589 let hi = (type_name_idx >> 8) as u8;
590 let lo = type_name_idx as u8;
591 self.chunk.code.push(hi);
592 self.chunk.code.push(lo);
593 self.chunk.lines.push(self.line);
594 self.chunk.columns.push(self.column);
595 self.chunk.lines.push(self.line);
596 self.chunk.columns.push(self.column);
597 }
598
599 pub(super) fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
601 if body.is_empty() {
602 self.chunk.emit(Op::Nil, self.line);
603 } else {
604 self.compile_scoped_block(body)?;
605 }
606 Ok(())
607 }
608
609 pub(super) fn compile_catch_binding(
611 &mut self,
612 error_var: &Option<String>,
613 ) -> Result<(), CompileError> {
614 if let Some(var_name) = error_var {
615 self.emit_define_binding(var_name, false);
616 } else {
617 self.chunk.emit(Op::Pop, self.line);
618 }
619 Ok(())
620 }
621
622 pub(super) fn compile_finally_inline(
629 &mut self,
630 finally_body: &[SNode],
631 ) -> Result<(), CompileError> {
632 if !finally_body.is_empty() {
633 self.compile_scoped_block(finally_body)?;
634 self.chunk.emit(Op::Pop, self.line);
635 }
636 Ok(())
637 }
638
639 pub(super) fn pending_finallys_until_barrier(&self) -> Vec<Vec<SNode>> {
644 let mut out = Vec::new();
645 for entry in self.finally_bodies.iter().rev() {
646 match entry {
647 FinallyEntry::CatchBarrier => break,
648 FinallyEntry::Finally(body) => out.push(body.clone()),
649 }
650 }
651 out
652 }
653
654 pub(super) fn pending_finallys_down_to(&self, floor: usize) -> Vec<Vec<SNode>> {
660 let mut out = Vec::new();
661 for entry in self.finally_bodies[floor..].iter().rev() {
662 if let FinallyEntry::Finally(body) = entry {
663 out.push(body.clone());
664 }
665 }
666 out
667 }
668
669 pub(super) fn all_pending_finallys(&self) -> Vec<Vec<SNode>> {
671 self.pending_finallys_down_to(0)
672 }
673
674 pub(super) fn has_pending_finally(&self) -> bool {
676 self.finally_bodies
677 .iter()
678 .any(|e| matches!(e, FinallyEntry::Finally(_)))
679 }
680
681 pub(super) fn compile_plain_rethrow(&mut self) -> Result<(), CompileError> {
690 self.temp_counter += 1;
691 let temp_name = format!("__finally_err_{}__", self.temp_counter);
692 self.emit_define_binding(&temp_name, true);
693 self.emit_get_binding(&temp_name);
694 self.chunk.emit(Op::Throw, self.line);
695 Ok(())
696 }
697
698 pub(super) fn declare_param_slots(&mut self, params: &[TypedParam]) {
699 for param in params {
700 self.define_local_slot(¶m.name, false);
701 }
702 }
703
704 fn define_local_slot(&mut self, name: &str, mutable: bool) -> Option<u16> {
705 if self.module_level || harn_parser::is_discard_name(name) {
706 return None;
707 }
708 let current = self.local_scopes.last_mut()?;
709 if let Some(existing) = current.get_mut(name) {
710 if existing.mutable || mutable {
711 if mutable {
712 existing.mutable = true;
713 if let Some(info) = self.chunk.local_slots.get_mut(existing.slot as usize) {
714 info.mutable = true;
715 }
716 }
717 return Some(existing.slot);
718 }
719 return None;
720 }
721 let slot = self
722 .chunk
723 .add_local_slot(name.to_string(), mutable, self.scope_depth);
724 current.insert(name.to_string(), super::LocalBinding { slot, mutable });
725 Some(slot)
726 }
727
728 pub(super) fn resolve_local_slot(&self, name: &str) -> Option<super::LocalBinding> {
729 if self.module_level {
730 return None;
731 }
732 self.local_scopes
733 .iter()
734 .rev()
735 .find_map(|scope| scope.get(name).copied())
736 }
737
738 pub(super) fn emit_get_binding(&mut self, name: &str) {
739 if let Some(binding) = self.resolve_local_slot(name) {
740 self.chunk
741 .emit_u16(Op::GetLocalSlot, binding.slot, self.line);
742 } else {
743 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
744 self.chunk.emit_u16(Op::GetVar, idx, self.line);
745 }
746 }
747
748 pub(super) fn emit_define_binding(&mut self, name: &str, mutable: bool) {
749 if let Some(slot) = self.define_local_slot(name, mutable) {
750 self.chunk.emit_u16(Op::DefLocalSlot, slot, self.line);
751 } else {
752 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
753 let op = if mutable { Op::DefVar } else { Op::DefLet };
754 self.chunk.emit_u16(op, idx, self.line);
755 }
756 }
757
758 pub(super) fn emit_init_or_define_binding(&mut self, name: &str, mutable: bool) {
759 if let Some(binding) = self.resolve_local_slot(name) {
760 self.chunk
761 .emit_u16(Op::DefLocalSlot, binding.slot, self.line);
762 } else {
763 self.emit_define_binding(name, mutable);
764 }
765 }
766
767 pub(super) fn emit_set_binding(&mut self, name: &str) {
768 if let Some(binding) = self.resolve_local_slot(name) {
769 let _ = binding.mutable;
770 self.chunk
771 .emit_u16(Op::SetLocalSlot, binding.slot, self.line);
772 } else {
773 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
774 self.chunk.emit_u16(Op::SetVar, idx, self.line);
775 }
776 }
777
778 pub(super) fn begin_scope(&mut self) {
779 self.chunk.emit(Op::PushScope, self.line);
780 self.scope_depth += 1;
781 self.type_scopes.push(std::collections::HashMap::new());
782 self.local_scopes.push(std::collections::HashMap::new());
783 }
784
785 pub(super) fn end_scope(&mut self) {
786 if self.scope_depth > 0 {
787 self.chunk.emit(Op::PopScope, self.line);
788 self.scope_depth -= 1;
789 self.type_scopes.pop();
790 self.local_scopes.pop();
791 }
792 }
793
794 pub(super) fn unwind_scopes_to(&mut self, target_depth: usize) {
795 while self.scope_depth > target_depth {
796 self.chunk.emit(Op::PopScope, self.line);
797 self.scope_depth -= 1;
798 self.type_scopes.pop();
799 self.local_scopes.pop();
800 }
801 }
802
803 pub(super) fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
804 self.begin_scope();
805 let finally_floor = self.finally_bodies.len();
806 if stmts.is_empty() {
807 self.chunk.emit(Op::Nil, self.line);
808 } else {
809 self.compile_block(stmts)?;
810 }
811 self.drain_finallys_to_floor(finally_floor)?;
812 self.end_scope();
813 Ok(())
814 }
815
816 pub(super) fn compile_scoped_statements(
817 &mut self,
818 stmts: &[SNode],
819 ) -> Result<(), CompileError> {
820 self.begin_scope();
821 let finally_floor = self.finally_bodies.len();
822 for sn in stmts {
823 self.compile_node(sn)?;
824 if Self::produces_value(&sn.node) {
825 self.chunk.emit(Op::Pop, self.line);
826 }
827 }
828 self.drain_finallys_to_floor(finally_floor)?;
829 self.end_scope();
830 Ok(())
831 }
832
833 pub(super) fn drain_finallys_to_floor(&mut self, floor: usize) -> Result<(), CompileError> {
838 while self.finally_bodies.len() > floor {
839 let entry = self.finally_bodies.pop().expect("non-empty by guard");
840 if let FinallyEntry::Finally(body) = entry {
841 self.compile_finally_inline(&body)?;
842 }
843 }
844 Ok(())
845 }
846
847 pub(super) fn maybe_register_owned_drop(
852 &mut self,
853 pattern: &harn_parser::BindingPattern,
854 type_ann: Option<&TypeExpr>,
855 span: harn_lexer::Span,
856 ) {
857 let Some(ty) = type_ann else {
863 return;
864 };
865 if !matches!(ty, TypeExpr::Owned(_)) {
866 return;
867 }
868 let harn_parser::BindingPattern::Identifier(name) = pattern else {
869 return;
870 };
871 if harn_parser::is_discard_name(name) {
872 return;
873 }
874 let call = harn_parser::spanned(
875 Node::FunctionCall {
876 name: "drop".to_string(),
877 args: vec![harn_parser::spanned(Node::Identifier(name.clone()), span)],
878 type_args: Vec::new(),
879 },
880 span,
881 );
882 self.finally_bodies.push(FinallyEntry::Finally(vec![call]));
883 }
884
885 pub(super) fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
886 for (i, snode) in stmts.iter().enumerate() {
887 self.compile_node(snode)?;
888 let is_last = i == stmts.len() - 1;
889 if is_last {
890 if !Self::produces_value(&snode.node) {
892 self.chunk.emit(Op::Nil, self.line);
893 }
894 } else if Self::produces_value(&snode.node) {
895 self.chunk.emit(Op::Pop, self.line);
896 }
897 }
898 Ok(())
899 }
900
901 pub(super) fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
903 self.begin_scope();
904 let finally_floor = self.finally_bodies.len();
905 if body.is_empty() {
906 self.chunk.emit(Op::Nil, self.line);
907 } else {
908 self.compile_block(body)?;
909 if !Self::produces_value(&body.last().unwrap().node) {
910 self.chunk.emit(Op::Nil, self.line);
911 }
912 }
913 self.drain_finallys_to_floor(finally_floor)?;
914 self.end_scope();
915 Ok(())
916 }
917
918 pub(super) fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
920 match op {
921 "+" => self.chunk.emit(Op::Add, self.line),
922 "-" => self.chunk.emit(Op::Sub, self.line),
923 "*" => self.chunk.emit(Op::Mul, self.line),
924 "/" => self.chunk.emit(Op::Div, self.line),
925 "%" => self.chunk.emit(Op::Mod, self.line),
926 _ => {
927 return Err(CompileError {
928 message: format!("Unknown compound operator: {op}"),
929 line: self.line,
930 })
931 }
932 }
933 Ok(())
934 }
935
936 pub(super) fn root_var_name(&self, node: &SNode) -> Option<String> {
938 match &node.node {
939 Node::Identifier(name) => Some(name.clone()),
940 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
941 self.root_var_name(object)
942 }
943 Node::SubscriptAccess { object, .. } | Node::OptionalSubscriptAccess { object, .. } => {
944 self.root_var_name(object)
945 }
946 _ => None,
947 }
948 }
949
950 pub(super) fn compile_top_level_declarations(
951 &mut self,
952 program: &[SNode],
953 ) -> Result<(), CompileError> {
954 for sn in program {
962 if matches!(&sn.node, Node::LetBinding { .. } | Node::VarBinding { .. }) {
963 self.compile_node(sn)?;
964 }
965 }
966 for sn in program {
972 let inner_kind = match &sn.node {
973 Node::AttributedDecl { inner, .. } => &inner.node,
974 other => other,
975 };
976 match inner_kind {
977 Node::EvalPackDecl {
978 binding_name,
979 pack_id,
980 fields,
981 body,
982 summarize,
983 ..
984 } => {
985 self.compile_eval_pack_decl(
986 binding_name,
987 pack_id,
988 fields,
989 body,
990 summarize,
991 false,
992 )?;
993 }
994 Node::FnDecl { .. }
995 | Node::ToolDecl { .. }
996 | Node::SkillDecl { .. }
997 | Node::ImplBlock { .. }
998 | Node::StructDecl { .. }
999 | Node::EnumDecl { .. }
1000 | Node::InterfaceDecl { .. }
1001 | Node::TypeDecl { .. } => {
1002 self.compile_node(sn)?;
1003 }
1004 _ => {}
1005 }
1006 }
1007 Ok(())
1008 }
1009
1010 pub(super) fn collect_enum_names(
1012 nodes: &[SNode],
1013 names: &mut std::collections::HashSet<String>,
1014 ) {
1015 for sn in nodes {
1016 match &sn.node {
1017 Node::EnumDecl { name, .. } => {
1018 names.insert(name.clone());
1019 }
1020 Node::Pipeline { body, .. } => {
1021 Self::collect_enum_names(body, names);
1022 }
1023 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
1024 Self::collect_enum_names(body, names);
1025 }
1026 Node::SkillDecl { fields, .. } => {
1027 for (_k, v) in fields {
1028 Self::collect_enum_names(std::slice::from_ref(v), names);
1029 }
1030 }
1031 Node::EvalPackDecl {
1032 fields,
1033 body,
1034 summarize,
1035 ..
1036 } => {
1037 for (_k, v) in fields {
1038 Self::collect_enum_names(std::slice::from_ref(v), names);
1039 }
1040 Self::collect_enum_names(body, names);
1041 if let Some(summary_body) = summarize {
1042 Self::collect_enum_names(summary_body, names);
1043 }
1044 }
1045 Node::Block(stmts) => {
1046 Self::collect_enum_names(stmts, names);
1047 }
1048 Node::AttributedDecl { inner, .. } => {
1049 Self::collect_enum_names(std::slice::from_ref(inner), names);
1050 }
1051 _ => {}
1052 }
1053 }
1054 }
1055
1056 pub(super) fn collect_struct_layouts(
1057 nodes: &[SNode],
1058 layouts: &mut std::collections::HashMap<String, Vec<String>>,
1059 ) {
1060 for sn in nodes {
1061 match &sn.node {
1062 Node::StructDecl { name, fields, .. } => {
1063 layouts.insert(
1064 name.clone(),
1065 fields.iter().map(|field| field.name.clone()).collect(),
1066 );
1067 }
1068 Node::Pipeline { body, .. }
1069 | Node::FnDecl { body, .. }
1070 | Node::ToolDecl { body, .. } => {
1071 Self::collect_struct_layouts(body, layouts);
1072 }
1073 Node::SkillDecl { fields, .. } => {
1074 for (_k, v) in fields {
1075 Self::collect_struct_layouts(std::slice::from_ref(v), layouts);
1076 }
1077 }
1078 Node::EvalPackDecl {
1079 fields,
1080 body,
1081 summarize,
1082 ..
1083 } => {
1084 for (_k, v) in fields {
1085 Self::collect_struct_layouts(std::slice::from_ref(v), layouts);
1086 }
1087 Self::collect_struct_layouts(body, layouts);
1088 if let Some(summary_body) = summarize {
1089 Self::collect_struct_layouts(summary_body, layouts);
1090 }
1091 }
1092 Node::Block(stmts) => {
1093 Self::collect_struct_layouts(stmts, layouts);
1094 }
1095 Node::AttributedDecl { inner, .. } => {
1096 Self::collect_struct_layouts(std::slice::from_ref(inner), layouts);
1097 }
1098 _ => {}
1099 }
1100 }
1101 }
1102
1103 pub(super) fn collect_interface_methods(
1104 nodes: &[SNode],
1105 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
1106 ) {
1107 for sn in nodes {
1108 match &sn.node {
1109 Node::InterfaceDecl { name, methods, .. } => {
1110 let method_names: Vec<String> =
1111 methods.iter().map(|m| m.name.clone()).collect();
1112 interfaces.insert(name.clone(), method_names);
1113 }
1114 Node::Pipeline { body, .. }
1115 | Node::FnDecl { body, .. }
1116 | Node::ToolDecl { body, .. } => {
1117 Self::collect_interface_methods(body, interfaces);
1118 }
1119 Node::SkillDecl { fields, .. } => {
1120 for (_k, v) in fields {
1121 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
1122 }
1123 }
1124 Node::EvalPackDecl {
1125 fields,
1126 body,
1127 summarize,
1128 ..
1129 } => {
1130 for (_k, v) in fields {
1131 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
1132 }
1133 Self::collect_interface_methods(body, interfaces);
1134 if let Some(summary_body) = summarize {
1135 Self::collect_interface_methods(summary_body, interfaces);
1136 }
1137 }
1138 Node::Block(stmts) => {
1139 Self::collect_interface_methods(stmts, interfaces);
1140 }
1141 Node::AttributedDecl { inner, .. } => {
1142 Self::collect_interface_methods(std::slice::from_ref(inner), interfaces);
1143 }
1144 _ => {}
1145 }
1146 }
1147 }
1148
1149 pub fn compile_fn_body(
1162 &mut self,
1163 type_params: &[harn_parser::TypeParam],
1164 params: &[TypedParam],
1165 body: &[SNode],
1166 source_file: Option<String>,
1167 ) -> Result<CompiledFunction, CompileError> {
1168 let mut fn_compiler = self.nested_body();
1169 fn_compiler.enum_names = self.enum_names.clone();
1170 fn_compiler.interface_methods = self.interface_methods.clone();
1171 fn_compiler.type_aliases = self.type_aliases.clone();
1172 fn_compiler.struct_layouts = self.struct_layouts.clone();
1173 fn_compiler.declare_param_slots(params);
1174 fn_compiler.record_param_types(params);
1175 fn_compiler.emit_default_preamble(params)?;
1176 fn_compiler.emit_type_checks(params);
1177 let is_gen = body_contains_yield(body);
1178 fn_compiler.compile_block(body)?;
1179 fn_compiler.chunk.emit(Op::Nil, 0);
1180 fn_compiler.chunk.emit(Op::Return, 0);
1181 fn_compiler.chunk.source_file = source_file;
1182 Ok(CompiledFunction {
1183 name: String::new(),
1184 type_params: type_params.iter().map(|param| param.name.clone()).collect(),
1185 nominal_type_names: fn_compiler.nominal_type_names(),
1186 params: crate::chunk::ParamSlot::vec_from_typed(params),
1187 default_start: TypedParam::default_start(params),
1188 chunk: Rc::new(fn_compiler.chunk),
1189 is_generator: is_gen,
1190 is_stream: false,
1191 has_rest_param: false,
1192 })
1193 }
1194
1195 pub(super) fn produces_value(node: &Node) -> bool {
1197 match node {
1198 Node::LetBinding { .. }
1199 | Node::VarBinding { .. }
1200 | Node::Assignment { .. }
1201 | Node::ReturnStmt { .. }
1202 | Node::FnDecl { .. }
1203 | Node::ToolDecl { .. }
1204 | Node::SkillDecl { .. }
1205 | Node::EvalPackDecl { .. }
1206 | Node::ImplBlock { .. }
1207 | Node::StructDecl { .. }
1208 | Node::EnumDecl { .. }
1209 | Node::InterfaceDecl { .. }
1210 | Node::TypeDecl { .. }
1211 | Node::ThrowStmt { .. }
1212 | Node::BreakStmt
1213 | Node::ContinueStmt
1214 | Node::RequireStmt { .. }
1215 | Node::DeferStmt { .. } => false,
1216 Node::TryCatch { has_catch: _, .. }
1217 | Node::TryExpr { .. }
1218 | Node::Retry { .. }
1219 | Node::GuardStmt { .. }
1220 | Node::DeadlineBlock { .. }
1221 | Node::MutexBlock { .. }
1222 | Node::Spread(_) => true,
1223 _ => true,
1224 }
1225 }
1226}
1227
1228impl Default for Compiler {
1229 fn default() -> Self {
1230 Self::new()
1231 }
1232}