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]) {
362 for (param_index, param) in params.iter().enumerate() {
363 if let Some(type_expr) = ¶m.type_expr {
364 let check_type = if param.rest {
365 harn_parser::TypeExpr::List(Box::new(type_expr.clone()))
366 } else {
367 type_expr.clone()
368 };
369
370 if let harn_parser::TypeExpr::Named(name) = &check_type {
371 if let Some(methods) = self.interface_methods.get(name).cloned() {
372 let fn_idx = self
373 .chunk
374 .add_constant(Constant::String("__assert_interface".into()));
375 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
376 self.emit_get_binding(¶m.name);
377 let name_idx = self
378 .chunk
379 .add_constant(Constant::String(param.name.clone()));
380 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
381 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
382 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
383 let methods_str = methods.join(",");
384 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
385 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
386 self.chunk.emit_u8(Op::Call, 4, self.line);
387 self.chunk.emit(Op::Pop, self.line);
388 continue;
389 }
390 }
391
392 if param.default_value.is_some() {
393 if let Some(schema) = Self::type_expr_to_schema_value(&check_type) {
394 self.emit_default_param_schema_check(param_index, param, &schema);
395 }
396 }
397 }
398 }
399 }
400
401 fn emit_default_param_schema_check(
402 &mut self,
403 param_index: usize,
404 param: &TypedParam,
405 schema: &VmValue,
406 ) {
407 self.chunk.emit(Op::GetArgc, self.line);
408 let threshold_idx = self
409 .chunk
410 .add_constant(Constant::Int((param_index + 1) as i64));
411 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
412 self.chunk.emit(Op::GreaterEqual, self.line);
413 let supplied_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
414 self.chunk.emit(Op::Pop, self.line);
415 self.emit_schema_assert_call(param, schema);
416 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
417 self.chunk.patch_jump(supplied_jump);
418 self.chunk.emit(Op::Pop, self.line);
419 self.chunk.patch_jump(end_jump);
420 }
421
422 fn emit_schema_assert_call(&mut self, param: &TypedParam, schema: &VmValue) {
423 let fn_idx = self
424 .chunk
425 .add_constant(Constant::String("__assert_schema".into()));
426 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
427 self.emit_get_binding(¶m.name);
428 let name_idx = self
429 .chunk
430 .add_constant(Constant::String(param.name.clone()));
431 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
432 self.emit_vm_value_literal(schema);
433 self.chunk.emit_u8(Op::Call, 3, self.line);
434 self.chunk.emit(Op::Pop, self.line);
435 }
436
437 pub(crate) fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
438 match type_expr {
439 harn_parser::TypeExpr::Named(name) => match name.as_str() {
440 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
441 | "closure" | "bytes" => Some(VmValue::Dict(Rc::new(BTreeMap::from([(
442 "type".to_string(),
443 VmValue::String(Rc::from(name.as_str())),
444 )])))),
445 _ => None,
446 },
447 harn_parser::TypeExpr::Shape(fields) => {
448 let mut properties = BTreeMap::new();
449 let mut required = Vec::new();
450 for field in fields {
451 let field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
452 properties.insert(field.name.clone(), field_schema);
453 if !field.optional {
454 required.push(VmValue::String(Rc::from(field.name.as_str())));
455 }
456 }
457 let mut out = BTreeMap::new();
458 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
459 out.insert("properties".to_string(), VmValue::Dict(Rc::new(properties)));
460 if !required.is_empty() {
461 out.insert("required".to_string(), VmValue::List(Rc::new(required)));
462 }
463 Some(VmValue::Dict(Rc::new(out)))
464 }
465 harn_parser::TypeExpr::List(inner) => {
466 let mut out = BTreeMap::new();
467 out.insert("type".to_string(), VmValue::String(Rc::from("list")));
468 if let Some(item_schema) = Self::type_expr_to_schema_value(inner) {
469 out.insert("items".to_string(), item_schema);
470 }
471 Some(VmValue::Dict(Rc::new(out)))
472 }
473 harn_parser::TypeExpr::DictType(key, value) => {
474 let mut out = BTreeMap::new();
475 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
476 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
477 if let Some(value_schema) = Self::type_expr_to_schema_value(value) {
478 out.insert("additional_properties".to_string(), value_schema);
479 }
480 }
481 Some(VmValue::Dict(Rc::new(out)))
482 }
483 harn_parser::TypeExpr::Union(members) => {
484 if !members.is_empty()
489 && members
490 .iter()
491 .all(|m| matches!(m, harn_parser::TypeExpr::LitString(_)))
492 {
493 let values = members
494 .iter()
495 .map(|m| match m {
496 harn_parser::TypeExpr::LitString(s) => {
497 VmValue::String(Rc::from(s.as_str()))
498 }
499 _ => unreachable!(),
500 })
501 .collect::<Vec<_>>();
502 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
503 ("type".to_string(), VmValue::String(Rc::from("string"))),
504 ("enum".to_string(), VmValue::List(Rc::new(values))),
505 ]))));
506 }
507 if !members.is_empty()
508 && members
509 .iter()
510 .all(|m| matches!(m, harn_parser::TypeExpr::LitInt(_)))
511 {
512 let values = members
513 .iter()
514 .map(|m| match m {
515 harn_parser::TypeExpr::LitInt(v) => VmValue::Int(*v),
516 _ => unreachable!(),
517 })
518 .collect::<Vec<_>>();
519 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
520 ("type".to_string(), VmValue::String(Rc::from("int"))),
521 ("enum".to_string(), VmValue::List(Rc::new(values))),
522 ]))));
523 }
524 let branches = members
525 .iter()
526 .filter_map(Self::type_expr_to_schema_value)
527 .collect::<Vec<_>>();
528 if branches.is_empty() {
529 None
530 } else {
531 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
532 "union".to_string(),
533 VmValue::List(Rc::new(branches)),
534 )]))))
535 }
536 }
537 harn_parser::TypeExpr::Intersection(members) => {
538 let branches = members
542 .iter()
543 .filter_map(Self::type_expr_to_schema_value)
544 .collect::<Vec<_>>();
545 if branches.is_empty() {
546 None
547 } else {
548 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
549 "all_of".to_string(),
550 VmValue::List(Rc::new(branches)),
551 )]))))
552 }
553 }
554 harn_parser::TypeExpr::FnType { .. } => {
555 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
556 "type".to_string(),
557 VmValue::String(Rc::from("closure")),
558 )]))))
559 }
560 harn_parser::TypeExpr::Applied { .. } => None,
561 harn_parser::TypeExpr::Iter(_)
562 | harn_parser::TypeExpr::Generator(_)
563 | harn_parser::TypeExpr::Stream(_) => None,
564 harn_parser::TypeExpr::Never => None,
565 harn_parser::TypeExpr::LitString(s) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
566 ("type".to_string(), VmValue::String(Rc::from("string"))),
567 ("const".to_string(), VmValue::String(Rc::from(s.as_str()))),
568 ])))),
569 harn_parser::TypeExpr::LitInt(v) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
570 ("type".to_string(), VmValue::String(Rc::from("int"))),
571 ("const".to_string(), VmValue::Int(*v)),
572 ])))),
573 harn_parser::TypeExpr::Owned(inner) => Self::type_expr_to_schema_value(inner),
574 }
575 }
576
577 pub(super) fn emit_vm_value_literal(&mut self, value: &VmValue) {
578 match value {
579 VmValue::String(text) => {
580 let idx = self.chunk.add_constant(Constant::String(text.to_string()));
581 self.chunk.emit_u16(Op::Constant, idx, self.line);
582 }
583 VmValue::Int(number) => {
584 let idx = self.chunk.add_constant(Constant::Int(*number));
585 self.chunk.emit_u16(Op::Constant, idx, self.line);
586 }
587 VmValue::Float(number) => {
588 let idx = self.chunk.add_constant(Constant::Float(*number));
589 self.chunk.emit_u16(Op::Constant, idx, self.line);
590 }
591 VmValue::Bool(value) => {
592 let idx = self.chunk.add_constant(Constant::Bool(*value));
593 self.chunk.emit_u16(Op::Constant, idx, self.line);
594 }
595 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
596 VmValue::List(items) => {
597 for item in items.iter() {
598 self.emit_vm_value_literal(item);
599 }
600 self.chunk
601 .emit_u16(Op::BuildList, items.len() as u16, self.line);
602 }
603 VmValue::Dict(entries) => {
604 for (key, item) in entries.iter() {
605 let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
606 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
607 self.emit_vm_value_literal(item);
608 }
609 self.chunk
610 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
611 }
612 _ => {}
613 }
614 }
615
616 pub(super) fn emit_type_name_extra(&mut self, type_name_idx: u16) {
618 let hi = (type_name_idx >> 8) as u8;
619 let lo = type_name_idx as u8;
620 self.chunk.code.push(hi);
621 self.chunk.code.push(lo);
622 self.chunk.lines.push(self.line);
623 self.chunk.columns.push(self.column);
624 self.chunk.lines.push(self.line);
625 self.chunk.columns.push(self.column);
626 }
627
628 pub(super) fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
630 if body.is_empty() {
631 self.chunk.emit(Op::Nil, self.line);
632 } else {
633 self.compile_scoped_block(body)?;
634 }
635 Ok(())
636 }
637
638 pub(super) fn compile_catch_binding(
640 &mut self,
641 error_var: &Option<String>,
642 ) -> Result<(), CompileError> {
643 if let Some(var_name) = error_var {
644 self.emit_define_binding(var_name, false);
645 } else {
646 self.chunk.emit(Op::Pop, self.line);
647 }
648 Ok(())
649 }
650
651 pub(super) fn compile_finally_inline(
658 &mut self,
659 finally_body: &[SNode],
660 ) -> Result<(), CompileError> {
661 if !finally_body.is_empty() {
662 self.compile_scoped_block(finally_body)?;
663 self.chunk.emit(Op::Pop, self.line);
664 }
665 Ok(())
666 }
667
668 pub(super) fn pending_finallys_until_barrier(&self) -> Vec<Vec<SNode>> {
673 let mut out = Vec::new();
674 for entry in self.finally_bodies.iter().rev() {
675 match entry {
676 FinallyEntry::CatchBarrier => break,
677 FinallyEntry::Finally(body) => out.push(body.clone()),
678 }
679 }
680 out
681 }
682
683 pub(super) fn pending_finallys_down_to(&self, floor: usize) -> Vec<Vec<SNode>> {
689 let mut out = Vec::new();
690 for entry in self.finally_bodies[floor..].iter().rev() {
691 if let FinallyEntry::Finally(body) = entry {
692 out.push(body.clone());
693 }
694 }
695 out
696 }
697
698 pub(super) fn all_pending_finallys(&self) -> Vec<Vec<SNode>> {
700 self.pending_finallys_down_to(0)
701 }
702
703 pub(super) fn has_pending_finally(&self) -> bool {
705 self.finally_bodies
706 .iter()
707 .any(|e| matches!(e, FinallyEntry::Finally(_)))
708 }
709
710 pub(super) fn compile_plain_rethrow(&mut self) -> Result<(), CompileError> {
719 self.temp_counter += 1;
720 let temp_name = format!("__finally_err_{}__", self.temp_counter);
721 self.emit_define_binding(&temp_name, true);
722 self.emit_get_binding(&temp_name);
723 self.chunk.emit(Op::Throw, self.line);
724 Ok(())
725 }
726
727 pub(super) fn declare_param_slots(&mut self, params: &[TypedParam]) {
728 for param in params {
729 self.define_local_slot(¶m.name, false);
730 }
731 }
732
733 fn define_local_slot(&mut self, name: &str, mutable: bool) -> Option<u16> {
734 if self.module_level || harn_parser::is_discard_name(name) {
735 return None;
736 }
737 let current = self.local_scopes.last_mut()?;
738 if let Some(existing) = current.get_mut(name) {
739 if existing.mutable || mutable {
740 if mutable {
741 existing.mutable = true;
742 if let Some(info) = self.chunk.local_slots.get_mut(existing.slot as usize) {
743 info.mutable = true;
744 }
745 }
746 return Some(existing.slot);
747 }
748 return None;
749 }
750 let slot = self
751 .chunk
752 .add_local_slot(name.to_string(), mutable, self.scope_depth);
753 current.insert(name.to_string(), super::LocalBinding { slot, mutable });
754 Some(slot)
755 }
756
757 pub(super) fn resolve_local_slot(&self, name: &str) -> Option<super::LocalBinding> {
758 if self.module_level {
759 return None;
760 }
761 self.local_scopes
762 .iter()
763 .rev()
764 .find_map(|scope| scope.get(name).copied())
765 }
766
767 pub(super) fn emit_get_binding(&mut self, name: &str) {
768 if let Some(binding) = self.resolve_local_slot(name) {
769 self.chunk
770 .emit_u16(Op::GetLocalSlot, binding.slot, self.line);
771 } else {
772 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
773 self.chunk.emit_u16(Op::GetVar, idx, self.line);
774 }
775 }
776
777 pub(super) fn emit_define_binding(&mut self, name: &str, mutable: bool) {
778 if let Some(slot) = self.define_local_slot(name, mutable) {
779 self.chunk.emit_u16(Op::DefLocalSlot, slot, self.line);
780 } else {
781 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
782 let op = if mutable { Op::DefVar } else { Op::DefLet };
783 self.chunk.emit_u16(op, idx, self.line);
784 }
785 }
786
787 pub(super) fn emit_init_or_define_binding(&mut self, name: &str, mutable: bool) {
788 if let Some(binding) = self.resolve_local_slot(name) {
789 self.chunk
790 .emit_u16(Op::DefLocalSlot, binding.slot, self.line);
791 } else {
792 self.emit_define_binding(name, mutable);
793 }
794 }
795
796 pub(super) fn emit_set_binding(&mut self, name: &str) {
797 if let Some(binding) = self.resolve_local_slot(name) {
798 let _ = binding.mutable;
799 self.chunk
800 .emit_u16(Op::SetLocalSlot, binding.slot, self.line);
801 } else {
802 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
803 self.chunk.emit_u16(Op::SetVar, idx, self.line);
804 }
805 }
806
807 pub(super) fn begin_scope(&mut self) {
808 self.chunk.emit(Op::PushScope, self.line);
809 self.scope_depth += 1;
810 self.type_scopes.push(std::collections::HashMap::new());
811 self.local_scopes.push(std::collections::HashMap::new());
812 }
813
814 pub(super) fn end_scope(&mut self) {
815 if self.scope_depth > 0 {
816 self.chunk.emit(Op::PopScope, self.line);
817 self.scope_depth -= 1;
818 self.type_scopes.pop();
819 self.local_scopes.pop();
820 }
821 }
822
823 pub(super) fn unwind_scopes_to(&mut self, target_depth: usize) {
824 while self.scope_depth > target_depth {
825 self.chunk.emit(Op::PopScope, self.line);
826 self.scope_depth -= 1;
827 self.type_scopes.pop();
828 self.local_scopes.pop();
829 }
830 }
831
832 pub(super) fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
833 self.begin_scope();
834 let finally_floor = self.finally_bodies.len();
835 if stmts.is_empty() {
836 self.chunk.emit(Op::Nil, self.line);
837 } else {
838 self.compile_block(stmts)?;
839 }
840 self.drain_finallys_to_floor(finally_floor)?;
841 self.end_scope();
842 Ok(())
843 }
844
845 pub(super) fn compile_scoped_statements(
846 &mut self,
847 stmts: &[SNode],
848 ) -> Result<(), CompileError> {
849 self.begin_scope();
850 let finally_floor = self.finally_bodies.len();
851 for sn in stmts {
852 self.compile_node(sn)?;
853 if Self::produces_value(&sn.node) {
854 self.chunk.emit(Op::Pop, self.line);
855 }
856 }
857 self.drain_finallys_to_floor(finally_floor)?;
858 self.end_scope();
859 Ok(())
860 }
861
862 pub(super) fn drain_finallys_to_floor(&mut self, floor: usize) -> Result<(), CompileError> {
867 while self.finally_bodies.len() > floor {
868 let entry = self.finally_bodies.pop().expect("non-empty by guard");
869 if let FinallyEntry::Finally(body) = entry {
870 self.compile_finally_inline(&body)?;
871 }
872 }
873 Ok(())
874 }
875
876 pub(super) fn maybe_register_owned_drop(
881 &mut self,
882 pattern: &harn_parser::BindingPattern,
883 type_ann: Option<&TypeExpr>,
884 span: harn_lexer::Span,
885 ) {
886 let Some(ty) = type_ann else {
892 return;
893 };
894 if !matches!(ty, TypeExpr::Owned(_)) {
895 return;
896 }
897 let harn_parser::BindingPattern::Identifier(name) = pattern else {
898 return;
899 };
900 if harn_parser::is_discard_name(name) {
901 return;
902 }
903 let call = harn_parser::spanned(
904 Node::FunctionCall {
905 name: "drop".to_string(),
906 args: vec![harn_parser::spanned(Node::Identifier(name.clone()), span)],
907 type_args: Vec::new(),
908 },
909 span,
910 );
911 self.finally_bodies.push(FinallyEntry::Finally(vec![call]));
912 }
913
914 pub(super) fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
915 for (i, snode) in stmts.iter().enumerate() {
916 self.compile_node(snode)?;
917 let is_last = i == stmts.len() - 1;
918 if is_last {
919 if !Self::produces_value(&snode.node) {
921 self.chunk.emit(Op::Nil, self.line);
922 }
923 } else if Self::produces_value(&snode.node) {
924 self.chunk.emit(Op::Pop, self.line);
925 }
926 }
927 Ok(())
928 }
929
930 pub(super) fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
932 self.begin_scope();
933 let finally_floor = self.finally_bodies.len();
934 if body.is_empty() {
935 self.chunk.emit(Op::Nil, self.line);
936 } else {
937 self.compile_block(body)?;
938 if !Self::produces_value(&body.last().unwrap().node) {
939 self.chunk.emit(Op::Nil, self.line);
940 }
941 }
942 self.drain_finallys_to_floor(finally_floor)?;
943 self.end_scope();
944 Ok(())
945 }
946
947 pub(super) fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
949 match op {
950 "+" => self.chunk.emit(Op::Add, self.line),
951 "-" => self.chunk.emit(Op::Sub, self.line),
952 "*" => self.chunk.emit(Op::Mul, self.line),
953 "/" => self.chunk.emit(Op::Div, self.line),
954 "%" => self.chunk.emit(Op::Mod, self.line),
955 _ => {
956 return Err(CompileError {
957 message: format!("Unknown compound operator: {op}"),
958 line: self.line,
959 })
960 }
961 }
962 Ok(())
963 }
964
965 pub(super) fn root_var_name(&self, node: &SNode) -> Option<String> {
967 match &node.node {
968 Node::Identifier(name) => Some(name.clone()),
969 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
970 self.root_var_name(object)
971 }
972 Node::SubscriptAccess { object, .. } | Node::OptionalSubscriptAccess { object, .. } => {
973 self.root_var_name(object)
974 }
975 _ => None,
976 }
977 }
978
979 pub(super) fn compile_top_level_declarations(
980 &mut self,
981 program: &[SNode],
982 ) -> Result<(), CompileError> {
983 for sn in program {
991 if matches!(
992 &sn.node,
993 Node::LetBinding { .. } | Node::VarBinding { .. } | Node::ConstBinding { .. }
994 ) {
995 self.compile_node(sn)?;
996 }
997 }
998 for sn in program {
1004 let inner_kind = match &sn.node {
1005 Node::AttributedDecl { inner, .. } => &inner.node,
1006 other => other,
1007 };
1008 match inner_kind {
1009 Node::EvalPackDecl {
1010 binding_name,
1011 pack_id,
1012 fields,
1013 body,
1014 summarize,
1015 ..
1016 } => {
1017 self.compile_eval_pack_decl(
1018 binding_name,
1019 pack_id,
1020 fields,
1021 body,
1022 summarize,
1023 false,
1024 )?;
1025 }
1026 Node::FnDecl { .. }
1027 | Node::ToolDecl { .. }
1028 | Node::SkillDecl { .. }
1029 | Node::ImplBlock { .. }
1030 | Node::StructDecl { .. }
1031 | Node::EnumDecl { .. }
1032 | Node::InterfaceDecl { .. }
1033 | Node::TypeDecl { .. } => {
1034 self.compile_node(sn)?;
1035 }
1036 _ => {}
1037 }
1038 }
1039 Ok(())
1040 }
1041
1042 pub(super) fn collect_enum_names(
1044 nodes: &[SNode],
1045 names: &mut std::collections::HashSet<String>,
1046 ) {
1047 for sn in nodes {
1048 match &sn.node {
1049 Node::EnumDecl { name, .. } => {
1050 names.insert(name.clone());
1051 }
1052 Node::Pipeline { body, .. } => {
1053 Self::collect_enum_names(body, names);
1054 }
1055 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
1056 Self::collect_enum_names(body, names);
1057 }
1058 Node::SkillDecl { fields, .. } => {
1059 for (_k, v) in fields {
1060 Self::collect_enum_names(std::slice::from_ref(v), names);
1061 }
1062 }
1063 Node::EvalPackDecl {
1064 fields,
1065 body,
1066 summarize,
1067 ..
1068 } => {
1069 for (_k, v) in fields {
1070 Self::collect_enum_names(std::slice::from_ref(v), names);
1071 }
1072 Self::collect_enum_names(body, names);
1073 if let Some(summary_body) = summarize {
1074 Self::collect_enum_names(summary_body, names);
1075 }
1076 }
1077 Node::Block(stmts) => {
1078 Self::collect_enum_names(stmts, names);
1079 }
1080 Node::AttributedDecl { inner, .. } => {
1081 Self::collect_enum_names(std::slice::from_ref(inner), names);
1082 }
1083 _ => {}
1084 }
1085 }
1086 }
1087
1088 pub(super) fn collect_struct_layouts(
1089 nodes: &[SNode],
1090 layouts: &mut std::collections::HashMap<String, Vec<String>>,
1091 ) {
1092 for sn in nodes {
1093 match &sn.node {
1094 Node::StructDecl { name, fields, .. } => {
1095 layouts.insert(
1096 name.clone(),
1097 fields.iter().map(|field| field.name.clone()).collect(),
1098 );
1099 }
1100 Node::Pipeline { body, .. }
1101 | Node::FnDecl { body, .. }
1102 | Node::ToolDecl { body, .. } => {
1103 Self::collect_struct_layouts(body, layouts);
1104 }
1105 Node::SkillDecl { fields, .. } => {
1106 for (_k, v) in fields {
1107 Self::collect_struct_layouts(std::slice::from_ref(v), layouts);
1108 }
1109 }
1110 Node::EvalPackDecl {
1111 fields,
1112 body,
1113 summarize,
1114 ..
1115 } => {
1116 for (_k, v) in fields {
1117 Self::collect_struct_layouts(std::slice::from_ref(v), layouts);
1118 }
1119 Self::collect_struct_layouts(body, layouts);
1120 if let Some(summary_body) = summarize {
1121 Self::collect_struct_layouts(summary_body, layouts);
1122 }
1123 }
1124 Node::Block(stmts) => {
1125 Self::collect_struct_layouts(stmts, layouts);
1126 }
1127 Node::AttributedDecl { inner, .. } => {
1128 Self::collect_struct_layouts(std::slice::from_ref(inner), layouts);
1129 }
1130 _ => {}
1131 }
1132 }
1133 }
1134
1135 pub(super) fn collect_interface_methods(
1136 nodes: &[SNode],
1137 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
1138 ) {
1139 for sn in nodes {
1140 match &sn.node {
1141 Node::InterfaceDecl { name, methods, .. } => {
1142 let method_names: Vec<String> =
1143 methods.iter().map(|m| m.name.clone()).collect();
1144 interfaces.insert(name.clone(), method_names);
1145 }
1146 Node::Pipeline { body, .. }
1147 | Node::FnDecl { body, .. }
1148 | Node::ToolDecl { body, .. } => {
1149 Self::collect_interface_methods(body, interfaces);
1150 }
1151 Node::SkillDecl { fields, .. } => {
1152 for (_k, v) in fields {
1153 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
1154 }
1155 }
1156 Node::EvalPackDecl {
1157 fields,
1158 body,
1159 summarize,
1160 ..
1161 } => {
1162 for (_k, v) in fields {
1163 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
1164 }
1165 Self::collect_interface_methods(body, interfaces);
1166 if let Some(summary_body) = summarize {
1167 Self::collect_interface_methods(summary_body, interfaces);
1168 }
1169 }
1170 Node::Block(stmts) => {
1171 Self::collect_interface_methods(stmts, interfaces);
1172 }
1173 Node::AttributedDecl { inner, .. } => {
1174 Self::collect_interface_methods(std::slice::from_ref(inner), interfaces);
1175 }
1176 _ => {}
1177 }
1178 }
1179 }
1180
1181 pub fn compile_fn_body(
1194 &mut self,
1195 type_params: &[harn_parser::TypeParam],
1196 params: &[TypedParam],
1197 body: &[SNode],
1198 source_file: Option<String>,
1199 ) -> Result<CompiledFunction, CompileError> {
1200 let mut fn_compiler = self.nested_body();
1201 fn_compiler.enum_names = self.enum_names.clone();
1202 fn_compiler.interface_methods = self.interface_methods.clone();
1203 fn_compiler.type_aliases = self.type_aliases.clone();
1204 fn_compiler.struct_layouts = self.struct_layouts.clone();
1205 fn_compiler.declare_param_slots(params);
1206 fn_compiler.record_param_types(params);
1207 fn_compiler.emit_default_preamble(params)?;
1208 fn_compiler.emit_type_checks(params);
1209 let is_gen = body_contains_yield(body);
1210 fn_compiler.compile_block(body)?;
1211 fn_compiler.chunk.emit(Op::Nil, 0);
1212 fn_compiler.chunk.emit(Op::Return, 0);
1213 fn_compiler.chunk.source_file = source_file;
1214 let param_slots = crate::chunk::ParamSlot::vec_from_typed(params);
1215 let has_runtime_type_checks =
1216 CompiledFunction::has_runtime_type_checks_for_params(¶m_slots);
1217 Ok(CompiledFunction {
1218 name: String::new(),
1219 type_params: type_params.iter().map(|param| param.name.clone()).collect(),
1220 nominal_type_names: fn_compiler.nominal_type_names(),
1221 params: param_slots,
1222 default_start: TypedParam::default_start(params),
1223 chunk: Rc::new(fn_compiler.chunk),
1224 is_generator: is_gen,
1225 is_stream: false,
1226 has_rest_param: false,
1227 has_runtime_type_checks,
1228 })
1229 }
1230
1231 pub(super) fn produces_value(node: &Node) -> bool {
1233 match node {
1234 Node::LetBinding { .. }
1235 | Node::VarBinding { .. }
1236 | Node::ConstBinding { .. }
1237 | Node::Assignment { .. }
1238 | Node::ReturnStmt { .. }
1239 | Node::FnDecl { .. }
1240 | Node::ToolDecl { .. }
1241 | Node::SkillDecl { .. }
1242 | Node::EvalPackDecl { .. }
1243 | Node::ImplBlock { .. }
1244 | Node::StructDecl { .. }
1245 | Node::EnumDecl { .. }
1246 | Node::InterfaceDecl { .. }
1247 | Node::TypeDecl { .. }
1248 | Node::ThrowStmt { .. }
1249 | Node::BreakStmt
1250 | Node::ContinueStmt
1251 | Node::RequireStmt { .. }
1252 | Node::DeferStmt { .. } => false,
1253 Node::TryCatch { has_catch: _, .. }
1254 | Node::TryExpr { .. }
1255 | Node::Retry { .. }
1256 | Node::GuardStmt { .. }
1257 | Node::DeadlineBlock { .. }
1258 | Node::MutexBlock { .. }
1259 | Node::Spread(_) => true,
1260 _ => true,
1261 }
1262 }
1263}
1264
1265impl Default for Compiler {
1266 fn default() -> Self {
1267 Self::new()
1268 }
1269}