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