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