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