1use crate::value::VmDictExt;
2use std::collections::BTreeMap;
3use std::sync::Arc;
4
5use harn_parser::{Node, SNode, ShapeField, TypeExpr, TypedParam};
6
7use crate::chunk::{Chunk, CompiledFunction, Constant, Op};
8use crate::value::VmValue;
9
10use super::error::CompileError;
11use super::yield_scan::body_contains_yield;
12use super::{peel_node, Compiler, CompilerOptions, FinallyEntry};
13
14#[cfg(test)]
15thread_local! {
16 pub(super) static FORCE_DISCARDED_PRODUCES_VALUE: std::cell::Cell<Option<bool>> =
23 const { std::cell::Cell::new(None) };
24}
25
26impl Compiler {
27 pub fn new() -> Self {
28 Self::with_options(CompilerOptions::from_env())
29 }
30
31 pub fn with_options(options: CompilerOptions) -> Self {
32 Self {
33 options,
34 chunk: Chunk::new(),
35 line: 1,
36 column: 1,
37 enum_names: std::collections::HashSet::new(),
38 enum_variant_owners: std::collections::HashMap::new(),
39 struct_layouts: std::collections::HashMap::new(),
40 interface_methods: std::collections::HashMap::new(),
41 loop_stack: Vec::new(),
42 handler_depth: 0,
43 finally_bodies: Vec::new(),
44 temp_counter: 0,
45 scope_depth: 0,
46 type_aliases: std::collections::HashMap::new(),
47 type_scopes: vec![std::collections::HashMap::new()],
48 monomorphic_bindings: std::collections::HashSet::new(),
49 string_constants: std::collections::HashMap::new(),
50 local_scopes: vec![std::collections::HashMap::new()],
51 module_level: true,
52 }
53 }
54
55 pub(super) fn for_nested_body(options: CompilerOptions) -> Self {
59 let mut c = Self::with_options(options);
60 c.module_level = false;
61 c
62 }
63
64 pub(super) fn nested_body(&self) -> Self {
65 Self::for_nested_body(self.options)
66 }
67
68 pub(super) fn nominal_type_names(&self) -> Vec<String> {
69 let mut names: Vec<String> = self
70 .struct_layouts
71 .keys()
72 .chain(self.enum_names.iter())
73 .cloned()
74 .collect();
75 names.sort();
76 names.dedup();
77 names
78 }
79
80 pub(super) fn string_constant(&mut self, value: &str) -> u16 {
81 if let Some(idx) = self.string_constants.get(value) {
82 return *idx;
83 }
84 let owned = value.to_string();
85 let idx = self.chunk.add_constant(Constant::String(owned.clone()));
86 self.string_constants.insert(owned, idx);
87 idx
88 }
89
90 pub(super) fn owned_string_constant(&mut self, value: String) -> u16 {
91 if let Some(idx) = self.string_constants.get(value.as_str()) {
92 return *idx;
93 }
94 let idx = self.chunk.add_constant(Constant::String(value.clone()));
95 self.string_constants.insert(value, idx);
96 idx
97 }
98
99 pub(super) fn collect_type_aliases(&mut self, program: &[SNode]) {
103 for sn in program {
104 if let Node::TypeDecl {
105 name,
106 type_expr,
107 type_params: _,
108 is_pub: _,
109 } = &sn.node
110 {
111 self.type_aliases.insert(name.clone(), type_expr.clone());
112 }
113 }
114 }
115
116 pub(super) fn expand_alias(&self, ty: &TypeExpr) -> TypeExpr {
120 match ty {
121 TypeExpr::Named(name) => {
122 if let Some(target) = self.type_aliases.get(name) {
123 self.expand_alias(target)
124 } else {
125 TypeExpr::Named(name.clone())
126 }
127 }
128 TypeExpr::Union(types) => {
129 TypeExpr::Union(types.iter().map(|t| self.expand_alias(t)).collect())
130 }
131 TypeExpr::Intersection(types) => {
132 TypeExpr::Intersection(types.iter().map(|t| self.expand_alias(t)).collect())
133 }
134 TypeExpr::Shape(fields) => TypeExpr::Shape(
135 fields
136 .iter()
137 .map(|field| ShapeField {
138 name: field.name.clone(),
139 type_expr: self.expand_alias(&field.type_expr),
140 optional: field.optional,
141 })
142 .collect(),
143 ),
144 TypeExpr::OpenShape { fields, rests } => TypeExpr::OpenShape {
145 fields: fields
146 .iter()
147 .map(|field| ShapeField {
148 name: field.name.clone(),
149 type_expr: self.expand_alias(&field.type_expr),
150 optional: field.optional,
151 })
152 .collect(),
153 rests: rests.iter().map(|r| self.expand_alias(r)).collect(),
154 },
155 TypeExpr::List(inner) => TypeExpr::List(Box::new(self.expand_alias(inner))),
156 TypeExpr::Iter(inner) => TypeExpr::Iter(Box::new(self.expand_alias(inner))),
157 TypeExpr::Generator(inner) => TypeExpr::Generator(Box::new(self.expand_alias(inner))),
158 TypeExpr::Stream(inner) => TypeExpr::Stream(Box::new(self.expand_alias(inner))),
159 TypeExpr::DictType(k, v) => TypeExpr::DictType(
160 Box::new(self.expand_alias(k)),
161 Box::new(self.expand_alias(v)),
162 ),
163 TypeExpr::FnType {
164 params,
165 return_type,
166 } => TypeExpr::FnType {
167 params: params.iter().map(|p| self.expand_alias(p)).collect(),
168 return_type: Box::new(self.expand_alias(return_type)),
169 },
170 TypeExpr::Applied { name, args } => TypeExpr::Applied {
171 name: name.clone(),
172 args: args.iter().map(|a| self.expand_alias(a)).collect(),
173 },
174 TypeExpr::Never => TypeExpr::Never,
175 TypeExpr::LitString(s) => TypeExpr::LitString(s.clone()),
176 TypeExpr::LitInt(v) => TypeExpr::LitInt(*v),
177 TypeExpr::Owned(inner) => TypeExpr::Owned(Box::new(self.expand_alias(inner))),
178 }
179 }
180
181 pub(super) fn schema_value_for_alias(&self, name: &str) -> Option<VmValue> {
184 let ty = self.type_aliases.get(name)?;
185 let expanded = self.expand_alias(ty);
186 Self::type_expr_to_schema_value(&expanded)
187 }
188
189 pub fn lower_public_type_schemas(
196 program: &[SNode],
197 ) -> std::collections::BTreeMap<String, VmValue> {
198 let mut compiler = Compiler::new();
199 compiler.collect_type_aliases(program);
200 let mut schemas = std::collections::BTreeMap::new();
201 for sn in program {
202 let inner = peel_node(sn);
203 if let Node::TypeDecl {
204 name, is_pub: true, ..
205 } = inner
206 {
207 if let Some(schema) = compiler.schema_value_for_alias(name) {
208 schemas.insert(name.clone(), schema);
209 }
210 }
211 }
212 schemas
213 }
214
215 pub(super) fn is_schema_guard(name: &str) -> bool {
219 matches!(
220 name,
221 "schema_is"
222 | "schema_expect"
223 | "schema_parse"
224 | "schema_check"
225 | "schema_report"
226 | "is_type"
227 | "json_validate"
228 )
229 }
230
231 pub(super) fn entry_key_is(key: &SNode, keyword: &str) -> bool {
234 matches!(
235 &key.node,
236 Node::Identifier(name) | Node::StringLiteral(name) | Node::RawStringLiteral(name)
237 if name == keyword
238 )
239 }
240
241 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
244 Self::collect_enum_names(program, &mut self.enum_names);
247 self.enum_names.insert("Result".to_string());
248 Self::collect_enum_variant_owners(program, &mut self.enum_variant_owners);
249 Self::seed_builtin_variant_owners(&mut self.enum_variant_owners);
250 Self::collect_struct_layouts(program, &mut self.struct_layouts);
251 Self::collect_interface_methods(program, &mut self.interface_methods);
252 self.collect_type_aliases(program);
253
254 for sn in program {
255 match &sn.node {
256 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
257 self.compile_node(sn)?;
258 }
259 _ => {}
260 }
261 }
262 let main = program
263 .iter()
264 .find(|sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == "default"))
265 .or_else(|| {
266 program
267 .iter()
268 .find(|sn| matches!(peel_node(sn), Node::Pipeline { .. }))
269 });
270
271 let mut pipeline_emits_value = false;
275 if let Some(sn) = main {
276 self.compile_top_level_declarations(program)?;
277 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
278 if let Some(parent_name) = extends {
279 self.compile_parent_pipeline(program, parent_name)?;
280 }
281 let saved = std::mem::replace(&mut self.module_level, false);
282 self.compile_block(body)?;
283 self.module_level = saved;
284 pipeline_emits_value = true;
285 }
286 } else {
287 let top_level: Vec<&SNode> = program
289 .iter()
290 .filter(|sn| {
291 !matches!(
292 &sn.node,
293 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
294 )
295 })
296 .collect();
297 for sn in &top_level {
298 self.compile_discarded_stmt(sn)?;
299 }
300 if Self::has_top_level_fn_main(program) {
305 let harness_name = self.string_constant("harness");
306 self.chunk.emit_u16(Op::GetVar, harness_name, self.line);
307 self.emit_named_call("main", 1);
308 pipeline_emits_value = true;
309 }
310 }
311
312 self.drain_finallys_to_floor(0)?;
313 if !pipeline_emits_value {
314 self.chunk.emit(Op::Nil, self.line);
315 }
316 self.chunk.emit(Op::Return, self.line);
317 super::ensure_chunk_addressable(&self.chunk, "the program body", self.line)?;
318 Ok(self.chunk)
319 }
320
321 fn has_top_level_fn_main(program: &[SNode]) -> bool {
325 program
326 .iter()
327 .any(|sn| matches!(peel_node(sn), Node::FnDecl { name, .. } if name == "main"))
328 }
329
330 pub fn compile_named(
332 mut self,
333 program: &[SNode],
334 pipeline_name: &str,
335 ) -> Result<Chunk, CompileError> {
336 Self::collect_enum_names(program, &mut self.enum_names);
337 self.enum_names.insert("Result".to_string());
338 Self::collect_enum_variant_owners(program, &mut self.enum_variant_owners);
339 Self::seed_builtin_variant_owners(&mut self.enum_variant_owners);
340 Self::collect_struct_layouts(program, &mut self.struct_layouts);
341 Self::collect_interface_methods(program, &mut self.interface_methods);
342 self.collect_type_aliases(program);
343
344 for sn in program {
345 if matches!(
346 &sn.node,
347 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
348 ) {
349 self.compile_node(sn)?;
350 }
351 }
352 let target = program.iter().find(
353 |sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == pipeline_name),
354 );
355
356 if let Some(sn) = target {
357 self.compile_top_level_declarations(program)?;
358 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
359 if let Some(parent_name) = extends {
360 self.compile_parent_pipeline(program, parent_name)?;
361 }
362 let saved = std::mem::replace(&mut self.module_level, false);
363 self.compile_block(body)?;
364 self.module_level = saved;
365 }
366 }
367
368 self.drain_finallys_to_floor(0)?;
369 self.chunk.emit(Op::Nil, self.line);
370 self.chunk.emit(Op::Return, self.line);
371 super::ensure_chunk_addressable(&self.chunk, "the pipeline body", self.line)?;
372 Ok(self.chunk)
373 }
374
375 pub(super) fn compile_parent_pipeline(
377 &mut self,
378 program: &[SNode],
379 parent_name: &str,
380 ) -> Result<(), CompileError> {
381 let parent = program
382 .iter()
383 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
384 if let Some(sn) = parent {
385 if let Node::Pipeline { body, extends, .. } = &sn.node {
386 if let Some(grandparent) = extends {
387 self.compile_parent_pipeline(program, grandparent)?;
388 }
389 for stmt in body {
390 self.compile_discarded_stmt(stmt)?;
391 }
392 }
393 }
394 Ok(())
395 }
396
397 pub(super) fn emit_default_preamble(
402 &mut self,
403 params: &[TypedParam],
404 ) -> Result<(), CompileError> {
405 for (i, param) in params.iter().enumerate() {
406 if let Some(default_expr) = ¶m.default_value {
407 self.chunk.emit(Op::GetArgc, self.line);
408 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
409 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
410 self.chunk.emit(Op::GreaterEqual, self.line);
411 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
412 self.chunk.emit(Op::Pop, self.line);
414 let masked = self.mask_param_names(¶ms[i..]);
423 let result = self.compile_node(default_expr);
424 self.restore_param_names(masked);
425 result?;
426 self.emit_init_or_define_binding(¶m.name, false);
427 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
428 self.chunk.patch_jump(skip_jump);
429 self.chunk.emit(Op::Pop, self.line);
430 self.chunk.patch_jump(end_jump);
431 }
432 }
433 Ok(())
434 }
435
436 pub(super) fn emit_type_checks(&mut self, params: &[TypedParam]) {
443 for (param_index, param) in params.iter().enumerate() {
444 if let Some(type_expr) = ¶m.type_expr {
445 let check_type = if param.rest {
446 harn_parser::TypeExpr::List(Box::new(type_expr.clone()))
447 } else {
448 type_expr.clone()
449 };
450
451 if let harn_parser::TypeExpr::Named(name) = &check_type {
452 if let Some(methods) = self.interface_methods.get(name).cloned() {
453 let fn_idx = self.string_constant("__assert_interface");
454 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
455 self.emit_get_binding(¶m.name);
456 let name_idx = self.string_constant(¶m.name);
457 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
458 let iface_idx = self.string_constant(name);
459 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
460 let methods_str = methods.join(",");
461 let methods_idx = self.owned_string_constant(methods_str);
462 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
463 self.chunk.emit_u8(Op::Call, 4, self.line);
464 self.chunk.emit(Op::Pop, self.line);
465 continue;
466 }
467 }
468
469 if param.default_value.is_some() {
470 if let Some(schema) = Self::type_expr_to_schema_value(&check_type) {
471 self.emit_default_param_schema_check(param_index, param, &schema);
472 }
473 }
474 }
475 }
476 }
477
478 fn emit_default_param_schema_check(
479 &mut self,
480 param_index: usize,
481 param: &TypedParam,
482 schema: &VmValue,
483 ) {
484 self.chunk.emit(Op::GetArgc, self.line);
485 let threshold_idx = self
486 .chunk
487 .add_constant(Constant::Int((param_index + 1) as i64));
488 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
489 self.chunk.emit(Op::GreaterEqual, self.line);
490 let supplied_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
491 self.chunk.emit(Op::Pop, self.line);
492 self.emit_schema_assert_call(param, schema);
493 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
494 self.chunk.patch_jump(supplied_jump);
495 self.chunk.emit(Op::Pop, self.line);
496 self.chunk.patch_jump(end_jump);
497 }
498
499 fn emit_schema_assert_call(&mut self, param: &TypedParam, schema: &VmValue) {
500 let fn_idx = self.string_constant("__assert_schema");
501 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
502 self.emit_get_binding(¶m.name);
503 let name_idx = self.string_constant(¶m.name);
504 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
505 self.emit_vm_value_literal(schema);
506 self.chunk.emit_u8(Op::Call, 3, self.line);
507 self.chunk.emit(Op::Pop, self.line);
508 }
509
510 pub(crate) fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
511 match type_expr {
512 harn_parser::TypeExpr::Named(name) => match name.as_str() {
513 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
514 | "closure" | "bytes" => Some(VmValue::dict(BTreeMap::from([(
515 "type".to_string(),
516 VmValue::String(arcstr::ArcStr::from(name.as_str())),
517 )]))),
518 _ => None,
519 },
520 harn_parser::TypeExpr::Shape(fields)
521 | harn_parser::TypeExpr::OpenShape { fields, .. } => {
522 let mut properties = BTreeMap::new();
523 let mut required = Vec::new();
524 for field in fields {
525 let field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
526 properties.insert(field.name.clone(), field_schema);
527 if !field.optional {
528 required.push(VmValue::String(arcstr::ArcStr::from(field.name.as_str())));
529 }
530 }
531 let mut out = BTreeMap::new();
532 out.put_str("type", "dict");
533 out.insert("properties".to_string(), VmValue::dict(properties));
534 if !required.is_empty() {
535 out.insert(
536 "required".to_string(),
537 VmValue::List(std::sync::Arc::new(required)),
538 );
539 }
540 Some(VmValue::dict(out))
541 }
542 harn_parser::TypeExpr::List(inner) => {
543 let mut out = BTreeMap::new();
544 out.put_str("type", "list");
545 if let Some(item_schema) = Self::type_expr_to_schema_value(inner) {
546 out.insert("items".to_string(), item_schema);
547 }
548 Some(VmValue::dict(out))
549 }
550 harn_parser::TypeExpr::DictType(key, value) => {
551 let mut out = BTreeMap::new();
552 out.put_str("type", "dict");
553 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
554 if let Some(value_schema) = Self::type_expr_to_schema_value(value) {
555 out.insert("additional_properties".to_string(), value_schema);
556 }
557 }
558 Some(VmValue::dict(out))
559 }
560 harn_parser::TypeExpr::Union(members) => {
561 if !members.is_empty()
566 && members
567 .iter()
568 .all(|m| matches!(m, harn_parser::TypeExpr::LitString(_)))
569 {
570 let values = members
571 .iter()
572 .map(|m| match m {
573 harn_parser::TypeExpr::LitString(s) => {
574 VmValue::String(arcstr::ArcStr::from(s.as_str()))
575 }
576 _ => unreachable!(),
577 })
578 .collect::<Vec<_>>();
579 return Some(VmValue::dict(BTreeMap::from([
580 (
581 "type".to_string(),
582 VmValue::String(arcstr::ArcStr::from("string")),
583 ),
584 (
585 "enum".to_string(),
586 VmValue::List(std::sync::Arc::new(values)),
587 ),
588 ])));
589 }
590 if !members.is_empty()
591 && members
592 .iter()
593 .all(|m| matches!(m, harn_parser::TypeExpr::LitInt(_)))
594 {
595 let values = members
596 .iter()
597 .map(|m| match m {
598 harn_parser::TypeExpr::LitInt(v) => VmValue::Int(*v),
599 _ => unreachable!(),
600 })
601 .collect::<Vec<_>>();
602 return Some(VmValue::dict(BTreeMap::from([
603 (
604 "type".to_string(),
605 VmValue::String(arcstr::ArcStr::from("int")),
606 ),
607 (
608 "enum".to_string(),
609 VmValue::List(std::sync::Arc::new(values)),
610 ),
611 ])));
612 }
613 let branches = members
614 .iter()
615 .filter_map(Self::type_expr_to_schema_value)
616 .collect::<Vec<_>>();
617 if branches.is_empty() {
618 None
619 } else {
620 Some(VmValue::dict(BTreeMap::from([(
621 "union".to_string(),
622 VmValue::List(std::sync::Arc::new(branches)),
623 )])))
624 }
625 }
626 harn_parser::TypeExpr::Intersection(members) => {
627 let branches = members
631 .iter()
632 .filter_map(Self::type_expr_to_schema_value)
633 .collect::<Vec<_>>();
634 if branches.is_empty() {
635 None
636 } else {
637 Some(VmValue::dict(BTreeMap::from([(
638 "all_of".to_string(),
639 VmValue::List(std::sync::Arc::new(branches)),
640 )])))
641 }
642 }
643 harn_parser::TypeExpr::FnType { .. } => Some(VmValue::dict(BTreeMap::from([(
644 "type".to_string(),
645 VmValue::String(arcstr::ArcStr::from("closure")),
646 )]))),
647 harn_parser::TypeExpr::Applied { .. } => None,
648 harn_parser::TypeExpr::Iter(_)
649 | harn_parser::TypeExpr::Generator(_)
650 | harn_parser::TypeExpr::Stream(_) => None,
651 harn_parser::TypeExpr::Never => None,
652 harn_parser::TypeExpr::LitString(s) => Some(VmValue::dict(BTreeMap::from([
653 (
654 "type".to_string(),
655 VmValue::String(arcstr::ArcStr::from("string")),
656 ),
657 (
658 "const".to_string(),
659 VmValue::String(arcstr::ArcStr::from(s.as_str())),
660 ),
661 ]))),
662 harn_parser::TypeExpr::LitInt(v) => Some(VmValue::dict(BTreeMap::from([
663 (
664 "type".to_string(),
665 VmValue::String(arcstr::ArcStr::from("int")),
666 ),
667 ("const".to_string(), VmValue::Int(*v)),
668 ]))),
669 harn_parser::TypeExpr::Owned(inner) => Self::type_expr_to_schema_value(inner),
670 }
671 }
672
673 pub(super) fn emit_vm_value_literal(&mut self, value: &VmValue) {
674 match value {
675 VmValue::String(text) => {
676 let idx = self.string_constant(text);
677 self.chunk.emit_u16(Op::Constant, idx, self.line);
678 }
679 VmValue::Int(number) => {
680 let idx = self.chunk.add_constant(Constant::Int(*number));
681 self.chunk.emit_u16(Op::Constant, idx, self.line);
682 }
683 VmValue::Float(number) => {
684 let idx = self.chunk.add_constant(Constant::Float(*number));
685 self.chunk.emit_u16(Op::Constant, idx, self.line);
686 }
687 VmValue::Bool(value) => {
688 let idx = self.chunk.add_constant(Constant::Bool(*value));
689 self.chunk.emit_u16(Op::Constant, idx, self.line);
690 }
691 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
692 VmValue::List(items) => {
693 for item in items.iter() {
694 self.emit_vm_value_literal(item);
695 }
696 self.chunk
697 .emit_u16(Op::BuildList, items.len() as u16, self.line);
698 }
699 VmValue::Dict(entries) => {
700 for (key, item) in entries.iter() {
701 let key_idx = self.string_constant(key);
702 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
703 self.emit_vm_value_literal(item);
704 }
705 self.chunk
706 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
707 }
708 _ => {}
709 }
710 }
711
712 pub(super) fn emit_type_name_extra(&mut self, type_name_idx: u16) {
714 let hi = (type_name_idx >> 8) as u8;
715 let lo = type_name_idx as u8;
716 self.chunk.code.push(hi);
717 self.chunk.code.push(lo);
718 self.chunk.lines.push(self.line);
719 self.chunk.columns.push(self.column);
720 self.chunk.lines.push(self.line);
721 self.chunk.columns.push(self.column);
722 }
723
724 pub(super) fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
726 if body.is_empty() {
727 self.chunk.emit(Op::Nil, self.line);
728 } else {
729 self.compile_scoped_block(body)?;
730 }
731 Ok(())
732 }
733
734 pub(super) fn compile_catch_binding(
736 &mut self,
737 error_var: &Option<String>,
738 ) -> Result<(), CompileError> {
739 if let Some(var_name) = error_var {
740 self.emit_define_binding(var_name, false);
741 } else {
742 self.chunk.emit(Op::Pop, self.line);
743 }
744 Ok(())
745 }
746
747 pub(super) fn compile_finally_inline(
754 &mut self,
755 finally_body: &[SNode],
756 ) -> Result<(), CompileError> {
757 if !finally_body.is_empty() {
758 self.compile_scoped_block(finally_body)?;
759 self.chunk.emit(Op::Pop, self.line);
760 }
761 Ok(())
762 }
763
764 pub(super) fn pending_finallys_until_barrier(&self) -> Vec<Vec<SNode>> {
769 let mut out = Vec::new();
770 for entry in self.finally_bodies.iter().rev() {
771 match entry {
772 FinallyEntry::CatchBarrier => break,
773 FinallyEntry::Finally(body) => out.push(body.clone()),
774 }
775 }
776 out
777 }
778
779 pub(super) fn has_pending_finally(&self) -> bool {
781 self.finally_bodies
782 .iter()
783 .any(|e| matches!(e, FinallyEntry::Finally(_)))
784 }
785
786 pub(super) fn compile_plain_rethrow(&mut self) -> Result<(), CompileError> {
795 self.temp_counter += 1;
796 let temp_name = format!("__finally_err_{}__", self.temp_counter);
797 self.emit_define_binding(&temp_name, true);
798 self.emit_get_binding(&temp_name);
799 self.chunk.emit(Op::Throw, self.line);
800 Ok(())
801 }
802
803 pub(super) fn declare_param_slots(&mut self, params: &[TypedParam]) {
804 for param in params {
805 self.define_local_slot(¶m.name, false);
806 }
807 }
808
809 fn mask_param_names(&mut self, params: &[TypedParam]) -> Vec<(String, super::LocalBinding)> {
815 let mut removed = Vec::new();
816 if let Some(scope) = self.local_scopes.last_mut() {
817 for param in params {
818 if let Some(binding) = scope.remove(¶m.name) {
819 removed.push((param.name.clone(), binding));
820 }
821 }
822 }
823 removed
824 }
825
826 fn restore_param_names(&mut self, removed: Vec<(String, super::LocalBinding)>) {
828 if let Some(scope) = self.local_scopes.last_mut() {
829 for (name, binding) in removed {
830 scope.insert(name, binding);
831 }
832 }
833 }
834
835 fn define_local_slot(&mut self, name: &str, mutable: bool) -> Option<u16> {
836 if self.module_level || harn_parser::is_discard_name(name) {
837 return None;
838 }
839 let current = self.local_scopes.last_mut()?;
840 if let Some(existing) = current.get_mut(name) {
841 if existing.mutable || mutable {
842 if mutable {
843 existing.mutable = true;
844 if let Some(info) = self.chunk.local_slots.get_mut(existing.slot as usize) {
845 info.mutable = true;
846 }
847 }
848 return Some(existing.slot);
849 }
850 return None;
851 }
852 let slot = self
853 .chunk
854 .add_local_slot(name.to_string(), mutable, self.scope_depth);
855 current.insert(name.to_string(), super::LocalBinding { slot, mutable });
856 Some(slot)
857 }
858
859 pub(super) fn resolve_local_slot(&self, name: &str) -> Option<super::LocalBinding> {
860 if self.module_level {
861 return None;
862 }
863 self.local_scopes
864 .iter()
865 .rev()
866 .find_map(|scope| scope.get(name).copied())
867 }
868
869 pub(super) fn emit_get_binding(&mut self, name: &str) {
870 if let Some(binding) = self.resolve_local_slot(name) {
871 self.chunk
872 .emit_u16(Op::GetLocalSlot, binding.slot, self.line);
873 } else {
874 let idx = self.string_constant(name);
875 self.chunk.emit_u16(Op::GetVar, idx, self.line);
876 }
877 }
878
879 pub(super) fn emit_define_binding(&mut self, name: &str, mutable: bool) {
880 if let Some(slot) = self.define_local_slot(name, mutable) {
881 self.chunk.emit_u16(Op::DefLocalSlot, slot, self.line);
882 } else {
883 let idx = self.string_constant(name);
884 let op = if mutable { Op::DefVar } else { Op::DefLet };
885 self.chunk.emit_u16(op, idx, self.line);
886 }
887 }
888
889 pub(super) fn emit_init_or_define_binding(&mut self, name: &str, mutable: bool) {
890 if let Some(binding) = self.resolve_local_slot(name) {
891 self.chunk
892 .emit_u16(Op::DefLocalSlot, binding.slot, self.line);
893 } else {
894 self.emit_define_binding(name, mutable);
895 }
896 }
897
898 pub(super) fn emit_set_binding(&mut self, name: &str) {
899 if let Some(binding) = self.resolve_local_slot(name) {
900 let _ = binding.mutable;
901 self.chunk
902 .emit_u16(Op::SetLocalSlot, binding.slot, self.line);
903 } else {
904 let idx = self.string_constant(name);
905 self.chunk.emit_u16(Op::SetVar, idx, self.line);
906 }
907 }
908
909 pub(super) fn begin_scope(&mut self) {
910 self.chunk.emit(Op::PushScope, self.line);
911 self.scope_depth += 1;
912 self.type_scopes.push(std::collections::HashMap::new());
913 self.local_scopes.push(std::collections::HashMap::new());
914 }
915
916 pub(super) fn end_scope(&mut self) {
917 if self.scope_depth > 0 {
918 self.chunk.emit(Op::PopScope, self.line);
919 self.scope_depth -= 1;
920 self.type_scopes.pop();
921 self.local_scopes.pop();
922 }
923 }
924
925 pub(super) fn emit_scope_unwind_to(&mut self, target_depth: usize) {
928 for _ in target_depth..self.scope_depth {
929 self.chunk.emit(Op::PopScope, self.line);
930 }
931 }
932
933 pub(super) fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
934 self.begin_scope();
935 let finally_floor = self.finally_bodies.len();
936 if stmts.is_empty() {
937 self.chunk.emit(Op::Nil, self.line);
938 } else {
939 self.compile_block(stmts)?;
940 }
941 self.drain_finallys_to_floor(finally_floor)?;
942 self.end_scope();
943 Ok(())
944 }
945
946 pub(super) fn compile_scoped_statements(
947 &mut self,
948 stmts: &[SNode],
949 ) -> Result<(), CompileError> {
950 self.begin_scope();
951 self.record_monomorphic_var_bindings(stmts);
952 let finally_floor = self.finally_bodies.len();
953 for sn in stmts {
954 self.compile_discarded_stmt(sn)?;
955 }
956 self.drain_finallys_to_floor(finally_floor)?;
957 self.end_scope();
958 Ok(())
959 }
960
961 pub(super) fn drain_finallys_to_floor(&mut self, floor: usize) -> Result<(), CompileError> {
966 while self.finally_bodies.len() > floor {
967 let entry = self.finally_bodies.pop().expect("non-empty by guard");
968 if let FinallyEntry::Finally(body) = entry {
969 self.compile_finally_inline(&body)?;
970 }
971 }
972 Ok(())
973 }
974
975 pub(super) fn run_pending_finallys_for_transfer(
988 &mut self,
989 floor: usize,
990 ) -> Result<(), CompileError> {
991 if self.finally_bodies.len() <= floor {
992 return Ok(());
993 }
994 let saved = self.finally_bodies[floor..].to_vec();
995 let result = self.drain_finallys_to_floor(floor);
996 self.finally_bodies.extend(saved);
997 result
998 }
999
1000 pub(super) fn run_pending_finallys_until_barrier(&mut self) -> Result<(), CompileError> {
1005 let floor = self
1006 .finally_bodies
1007 .iter()
1008 .rposition(|e| matches!(e, FinallyEntry::CatchBarrier))
1009 .map(|i| i + 1)
1010 .unwrap_or(0);
1011 self.run_pending_finallys_for_transfer(floor)
1012 }
1013
1014 pub(super) fn maybe_register_owned_drop(
1019 &mut self,
1020 pattern: &harn_parser::BindingPattern,
1021 type_ann: Option<&TypeExpr>,
1022 span: harn_lexer::Span,
1023 ) {
1024 let Some(ty) = type_ann else {
1030 return;
1031 };
1032 if !matches!(ty, TypeExpr::Owned(_)) {
1033 return;
1034 }
1035 let harn_parser::BindingPattern::Identifier(name) = pattern else {
1036 return;
1037 };
1038 if harn_parser::is_discard_name(name) {
1039 return;
1040 }
1041 let call = harn_parser::spanned(
1042 Node::FunctionCall {
1043 name: "drop".to_string(),
1044 args: vec![harn_parser::spanned(Node::Identifier(name.clone()), span)],
1045 type_args: Vec::new(),
1046 },
1047 span,
1048 );
1049 self.finally_bodies.push(FinallyEntry::Finally(vec![call]));
1050 }
1051
1052 pub(super) fn compile_discarded_stmt(&mut self, sn: &SNode) -> Result<(), CompileError> {
1068 #[cfg(debug_assertions)]
1069 let probe = self.chunk.balance_probe();
1070 self.compile_node(sn)?;
1071 #[allow(unused_mut)]
1072 let mut produces = Self::produces_value(&sn.node);
1073 #[cfg(test)]
1077 if let Some(forced) = FORCE_DISCARDED_PRODUCES_VALUE.with(std::cell::Cell::get) {
1078 produces = forced;
1079 }
1080 #[cfg(debug_assertions)]
1081 if let Some(delta) = self.chunk.balance_delta_since(probe) {
1082 let expected = i32::from(produces);
1083 debug_assert_eq!(
1084 delta, expected,
1085 "operand-stack imbalance at line {}: produces_value={produces} but the \
1086 node's emitted bytecode netted {delta} (expected {expected}). A \
1087 `produces_value` arm is out of sync with this node's codegen — see #2622.\n\
1088 node: {:?}",
1089 self.line, sn.node,
1090 );
1091 }
1092 if produces {
1093 self.chunk.emit(Op::Pop, self.line);
1094 }
1095 Ok(())
1096 }
1097
1098 pub(super) fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
1099 self.record_monomorphic_var_bindings(stmts);
1100 for (i, snode) in stmts.iter().enumerate() {
1101 if i == stmts.len() - 1 {
1102 self.compile_node(snode)?;
1106 if !Self::produces_value(&snode.node) {
1107 self.chunk.emit(Op::Nil, self.line);
1108 }
1109 } else {
1110 self.compile_discarded_stmt(snode)?;
1111 }
1112 }
1113 Ok(())
1114 }
1115
1116 pub(super) fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
1118 self.begin_scope();
1119 let finally_floor = self.finally_bodies.len();
1120 if body.is_empty() {
1121 self.chunk.emit(Op::Nil, self.line);
1122 } else {
1123 self.compile_block(body)?;
1124 if !Self::produces_value(&body.last().unwrap().node) {
1125 self.chunk.emit(Op::Nil, self.line);
1126 }
1127 }
1128 self.drain_finallys_to_floor(finally_floor)?;
1129 self.end_scope();
1130 Ok(())
1131 }
1132
1133 pub(super) fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
1135 match op {
1136 "+" => self.chunk.emit(Op::Add, self.line),
1137 "-" => self.chunk.emit(Op::Sub, self.line),
1138 "*" => self.chunk.emit(Op::Mul, self.line),
1139 "/" => self.chunk.emit(Op::Div, self.line),
1140 "%" => self.chunk.emit(Op::Mod, self.line),
1141 _ => {
1142 return Err(CompileError {
1143 message: format!("Unknown compound operator: {op}"),
1144 line: self.line,
1145 })
1146 }
1147 }
1148 Ok(())
1149 }
1150
1151 pub(super) fn compile_top_level_declarations(
1152 &mut self,
1153 program: &[SNode],
1154 ) -> Result<(), CompileError> {
1155 for sn in program {
1167 let handled_elsewhere = matches!(
1168 peel_node(sn),
1169 Node::Pipeline { .. }
1170 | Node::ImportDecl { .. }
1171 | Node::SelectiveImport { .. }
1172 | Node::OverrideDecl { .. }
1173 | Node::EvalPackDecl { .. }
1174 | Node::FnDecl { .. }
1175 | Node::ToolDecl { .. }
1176 | Node::SkillDecl { .. }
1177 | Node::ImplBlock { .. }
1178 | Node::StructDecl { .. }
1179 | Node::EnumDecl { .. }
1180 | Node::InterfaceDecl { .. }
1181 | Node::TypeDecl { .. }
1182 );
1183 if !handled_elsewhere {
1184 self.compile_discarded_stmt(sn)?;
1185 }
1186 }
1187 for sn in program {
1193 let inner_kind = match &sn.node {
1194 Node::AttributedDecl { inner, .. } => &inner.node,
1195 other => other,
1196 };
1197 match inner_kind {
1198 Node::EvalPackDecl {
1199 binding_name,
1200 pack_id,
1201 fields,
1202 body,
1203 summarize,
1204 ..
1205 } => {
1206 self.compile_eval_pack_decl(
1207 binding_name,
1208 pack_id,
1209 fields,
1210 body,
1211 summarize,
1212 false,
1213 )?;
1214 }
1215 Node::FnDecl { .. }
1216 | Node::ToolDecl { .. }
1217 | Node::SkillDecl { .. }
1218 | Node::ImplBlock { .. }
1219 | Node::StructDecl { .. }
1220 | Node::EnumDecl { .. }
1221 | Node::InterfaceDecl { .. }
1222 | Node::TypeDecl { .. } => {
1223 self.compile_node(sn)?;
1224 }
1225 _ => {}
1226 }
1227 }
1228 Ok(())
1229 }
1230
1231 pub(super) fn collect_enum_names(
1233 nodes: &[SNode],
1234 names: &mut std::collections::HashSet<String>,
1235 ) {
1236 for sn in nodes {
1237 match &sn.node {
1238 Node::EnumDecl { name, .. } => {
1239 names.insert(name.clone());
1240 }
1241 Node::Pipeline { body, .. } => {
1242 Self::collect_enum_names(body, names);
1243 }
1244 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
1245 Self::collect_enum_names(body, names);
1246 }
1247 Node::SkillDecl { fields, .. } => {
1248 for (_k, v) in fields {
1249 Self::collect_enum_names(std::slice::from_ref(v), names);
1250 }
1251 }
1252 Node::EvalPackDecl {
1253 fields,
1254 body,
1255 summarize,
1256 ..
1257 } => {
1258 for (_k, v) in fields {
1259 Self::collect_enum_names(std::slice::from_ref(v), names);
1260 }
1261 Self::collect_enum_names(body, names);
1262 if let Some(summary_body) = summarize {
1263 Self::collect_enum_names(summary_body, names);
1264 }
1265 }
1266 Node::Block(stmts) => {
1267 Self::collect_enum_names(stmts, names);
1268 }
1269 Node::AttributedDecl { inner, .. } => {
1270 Self::collect_enum_names(std::slice::from_ref(inner), names);
1271 }
1272 _ => {}
1273 }
1274 }
1275 }
1276
1277 pub(super) fn collect_enum_variant_owners(
1282 nodes: &[SNode],
1283 owners: &mut std::collections::HashMap<String, Vec<String>>,
1284 ) {
1285 harn_parser::visit::walk_program(nodes, &mut |sn| {
1286 if let Node::EnumDecl { name, variants, .. } = &sn.node {
1287 for variant in variants {
1288 let entry = owners.entry(variant.name.clone()).or_default();
1289 if !entry.contains(name) {
1290 entry.push(name.clone());
1291 }
1292 }
1293 }
1294 });
1295 }
1296
1297 pub(super) fn seed_builtin_variant_owners(
1300 owners: &mut std::collections::HashMap<String, Vec<String>>,
1301 ) {
1302 for variant in ["Ok", "Err"] {
1303 let entry = owners.entry(variant.to_string()).or_default();
1304 if !entry.contains(&"Result".to_string()) {
1305 entry.push("Result".to_string());
1306 }
1307 }
1308 }
1309
1310 pub(super) fn collect_struct_layouts(
1311 nodes: &[SNode],
1312 layouts: &mut std::collections::HashMap<String, Vec<String>>,
1313 ) {
1314 for sn in nodes {
1315 match &sn.node {
1316 Node::StructDecl { name, fields, .. } => {
1317 layouts.insert(
1318 name.clone(),
1319 fields.iter().map(|field| field.name.clone()).collect(),
1320 );
1321 }
1322 Node::Pipeline { body, .. }
1323 | Node::FnDecl { body, .. }
1324 | Node::ToolDecl { body, .. } => {
1325 Self::collect_struct_layouts(body, layouts);
1326 }
1327 Node::SkillDecl { fields, .. } => {
1328 for (_k, v) in fields {
1329 Self::collect_struct_layouts(std::slice::from_ref(v), layouts);
1330 }
1331 }
1332 Node::EvalPackDecl {
1333 fields,
1334 body,
1335 summarize,
1336 ..
1337 } => {
1338 for (_k, v) in fields {
1339 Self::collect_struct_layouts(std::slice::from_ref(v), layouts);
1340 }
1341 Self::collect_struct_layouts(body, layouts);
1342 if let Some(summary_body) = summarize {
1343 Self::collect_struct_layouts(summary_body, layouts);
1344 }
1345 }
1346 Node::Block(stmts) => {
1347 Self::collect_struct_layouts(stmts, layouts);
1348 }
1349 Node::AttributedDecl { inner, .. } => {
1350 Self::collect_struct_layouts(std::slice::from_ref(inner), layouts);
1351 }
1352 _ => {}
1353 }
1354 }
1355 }
1356
1357 pub(super) fn collect_interface_methods(
1358 nodes: &[SNode],
1359 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
1360 ) {
1361 for sn in nodes {
1362 match &sn.node {
1363 Node::InterfaceDecl { name, methods, .. } => {
1364 let method_names: Vec<String> =
1365 methods.iter().map(|m| m.name.clone()).collect();
1366 interfaces.insert(name.clone(), method_names);
1367 }
1368 Node::Pipeline { body, .. }
1369 | Node::FnDecl { body, .. }
1370 | Node::ToolDecl { body, .. } => {
1371 Self::collect_interface_methods(body, interfaces);
1372 }
1373 Node::SkillDecl { fields, .. } => {
1374 for (_k, v) in fields {
1375 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
1376 }
1377 }
1378 Node::EvalPackDecl {
1379 fields,
1380 body,
1381 summarize,
1382 ..
1383 } => {
1384 for (_k, v) in fields {
1385 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
1386 }
1387 Self::collect_interface_methods(body, interfaces);
1388 if let Some(summary_body) = summarize {
1389 Self::collect_interface_methods(summary_body, interfaces);
1390 }
1391 }
1392 Node::Block(stmts) => {
1393 Self::collect_interface_methods(stmts, interfaces);
1394 }
1395 Node::AttributedDecl { inner, .. } => {
1396 Self::collect_interface_methods(std::slice::from_ref(inner), interfaces);
1397 }
1398 _ => {}
1399 }
1400 }
1401 }
1402
1403 pub fn compile_fn_body(
1416 &mut self,
1417 type_params: &[harn_parser::TypeParam],
1418 params: &[TypedParam],
1419 body: &[SNode],
1420 source_file: Option<String>,
1421 ) -> Result<CompiledFunction, CompileError> {
1422 let mut fn_compiler = self.nested_body();
1423 fn_compiler.enum_names = self.enum_names.clone();
1424 fn_compiler.enum_variant_owners = self.enum_variant_owners.clone();
1425 fn_compiler.interface_methods = self.interface_methods.clone();
1426 fn_compiler.type_aliases = self.type_aliases.clone();
1427 fn_compiler.struct_layouts = self.struct_layouts.clone();
1428 fn_compiler.declare_param_slots(params);
1429 fn_compiler.record_param_types(params);
1430 fn_compiler.emit_default_preamble(params)?;
1431 fn_compiler.emit_type_checks(params);
1432 let is_gen = body_contains_yield(body);
1433 fn_compiler.compile_block(body)?;
1434 fn_compiler.chunk.emit(Op::Nil, 0);
1435 fn_compiler.chunk.emit(Op::Return, 0);
1436 fn_compiler.chunk.source_file = source_file;
1437 let param_slots = crate::chunk::ParamSlot::vec_from_typed(params);
1438 let has_runtime_type_checks =
1439 CompiledFunction::has_runtime_type_checks_for_params(¶m_slots);
1440 super::ensure_chunk_addressable(&fn_compiler.chunk, "function body", self.line)?;
1441 Ok(CompiledFunction {
1442 name: String::new(),
1443 type_params: type_params.iter().map(|param| param.name.clone()).collect(),
1444 nominal_type_names: fn_compiler.nominal_type_names(),
1445 params: param_slots,
1446 default_start: TypedParam::default_start(params),
1447 chunk: Arc::new(fn_compiler.chunk),
1448 is_generator: is_gen,
1449 is_stream: false,
1450 has_rest_param: false,
1451 has_runtime_type_checks,
1452 })
1453 }
1454
1455 pub(super) fn produces_value(node: &Node) -> bool {
1457 match node {
1458 Node::AttributedDecl { inner, .. } => Self::produces_value(&inner.node),
1465 Node::LetBinding { .. }
1466 | Node::VarBinding { .. }
1467 | Node::ConstBinding { .. }
1468 | Node::Assignment { .. }
1469 | Node::ReturnStmt { .. }
1470 | Node::FnDecl { .. }
1471 | Node::ToolDecl { .. }
1472 | Node::SkillDecl { .. }
1473 | Node::EvalPackDecl { .. }
1474 | Node::ImplBlock { .. }
1475 | Node::StructDecl { .. }
1476 | Node::EnumDecl { .. }
1477 | Node::InterfaceDecl { .. }
1478 | Node::TypeDecl { .. }
1479 | Node::OverrideDecl { .. }
1482 | Node::Pipeline { .. }
1483 | Node::ThrowStmt { .. }
1484 | Node::BreakStmt
1485 | Node::ContinueStmt
1486 | Node::RequireStmt { .. }
1487 | Node::DeferStmt { .. } => false,
1488 Node::TryCatch { has_catch: _, .. }
1489 | Node::TryExpr { .. }
1490 | Node::Retry { .. }
1491 | Node::GuardStmt { .. }
1492 | Node::DeadlineBlock { .. }
1493 | Node::MutexBlock { .. }
1494 | Node::Spread(_) => true,
1495 _ => true,
1496 }
1497 }
1498}
1499
1500impl Default for Compiler {
1501 fn default() -> Self {
1502 Self::new()
1503 }
1504}