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 collect_type_aliases(&mut self, program: &[SNode]) {
47 for sn in program {
48 if let Node::TypeDecl {
49 name,
50 type_expr,
51 type_params: _,
52 } = &sn.node
53 {
54 self.type_aliases.insert(name.clone(), type_expr.clone());
55 }
56 }
57 }
58
59 pub(super) fn expand_alias(&self, ty: &TypeExpr) -> TypeExpr {
63 match ty {
64 TypeExpr::Named(name) => {
65 if let Some(target) = self.type_aliases.get(name) {
66 self.expand_alias(target)
67 } else {
68 TypeExpr::Named(name.clone())
69 }
70 }
71 TypeExpr::Union(types) => {
72 TypeExpr::Union(types.iter().map(|t| self.expand_alias(t)).collect())
73 }
74 TypeExpr::Shape(fields) => TypeExpr::Shape(
75 fields
76 .iter()
77 .map(|field| ShapeField {
78 name: field.name.clone(),
79 type_expr: self.expand_alias(&field.type_expr),
80 optional: field.optional,
81 })
82 .collect(),
83 ),
84 TypeExpr::List(inner) => TypeExpr::List(Box::new(self.expand_alias(inner))),
85 TypeExpr::Iter(inner) => TypeExpr::Iter(Box::new(self.expand_alias(inner))),
86 TypeExpr::DictType(k, v) => TypeExpr::DictType(
87 Box::new(self.expand_alias(k)),
88 Box::new(self.expand_alias(v)),
89 ),
90 TypeExpr::FnType {
91 params,
92 return_type,
93 } => TypeExpr::FnType {
94 params: params.iter().map(|p| self.expand_alias(p)).collect(),
95 return_type: Box::new(self.expand_alias(return_type)),
96 },
97 TypeExpr::Applied { name, args } => TypeExpr::Applied {
98 name: name.clone(),
99 args: args.iter().map(|a| self.expand_alias(a)).collect(),
100 },
101 TypeExpr::Never => TypeExpr::Never,
102 TypeExpr::LitString(s) => TypeExpr::LitString(s.clone()),
103 TypeExpr::LitInt(v) => TypeExpr::LitInt(*v),
104 }
105 }
106
107 pub(super) fn schema_value_for_alias(&self, name: &str) -> Option<VmValue> {
110 let ty = self.type_aliases.get(name)?;
111 let expanded = self.expand_alias(ty);
112 Self::type_expr_to_schema_value(&expanded)
113 }
114
115 pub(super) fn is_schema_guard(name: &str) -> bool {
119 matches!(
120 name,
121 "schema_is"
122 | "schema_expect"
123 | "schema_parse"
124 | "schema_check"
125 | "is_type"
126 | "json_validate"
127 )
128 }
129
130 pub(super) fn entry_key_is(key: &SNode, keyword: &str) -> bool {
133 matches!(
134 &key.node,
135 Node::Identifier(name) | Node::StringLiteral(name) | Node::RawStringLiteral(name)
136 if name == keyword
137 )
138 }
139
140 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
143 Self::collect_enum_names(program, &mut self.enum_names);
146 self.enum_names.insert("Result".to_string());
147 Self::collect_struct_layouts(program, &mut self.struct_layouts);
148 Self::collect_interface_methods(program, &mut self.interface_methods);
149 self.collect_type_aliases(program);
150
151 for sn in program {
152 match &sn.node {
153 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
154 self.compile_node(sn)?;
155 }
156 _ => {}
157 }
158 }
159 let main = program
160 .iter()
161 .find(|sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == "default"))
162 .or_else(|| {
163 program
164 .iter()
165 .find(|sn| matches!(peel_node(sn), Node::Pipeline { .. }))
166 });
167
168 let mut pipeline_emits_value = false;
172 if let Some(sn) = main {
173 self.compile_top_level_declarations(program)?;
174 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
175 if let Some(parent_name) = extends {
176 self.compile_parent_pipeline(program, parent_name)?;
177 }
178 let saved = std::mem::replace(&mut self.module_level, false);
179 self.compile_block(body)?;
180 self.module_level = saved;
181 pipeline_emits_value = true;
182 }
183 } else {
184 let top_level: Vec<&SNode> = program
186 .iter()
187 .filter(|sn| {
188 !matches!(
189 &sn.node,
190 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
191 )
192 })
193 .collect();
194 for sn in &top_level {
195 self.compile_node(sn)?;
196 if Self::produces_value(&sn.node) {
197 self.chunk.emit(Op::Pop, self.line);
198 }
199 }
200 }
201
202 for fb in self.all_pending_finallys() {
203 self.compile_finally_inline(&fb)?;
204 }
205 if !pipeline_emits_value {
206 self.chunk.emit(Op::Nil, self.line);
207 }
208 self.chunk.emit(Op::Return, self.line);
209 Ok(self.chunk)
210 }
211
212 pub fn compile_named(
214 mut self,
215 program: &[SNode],
216 pipeline_name: &str,
217 ) -> Result<Chunk, CompileError> {
218 Self::collect_enum_names(program, &mut self.enum_names);
219 self.enum_names.insert("Result".to_string());
220 Self::collect_struct_layouts(program, &mut self.struct_layouts);
221 Self::collect_interface_methods(program, &mut self.interface_methods);
222 self.collect_type_aliases(program);
223
224 for sn in program {
225 if matches!(
226 &sn.node,
227 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
228 ) {
229 self.compile_node(sn)?;
230 }
231 }
232 let target = program.iter().find(
233 |sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == pipeline_name),
234 );
235
236 if let Some(sn) = target {
237 self.compile_top_level_declarations(program)?;
238 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
239 if let Some(parent_name) = extends {
240 self.compile_parent_pipeline(program, parent_name)?;
241 }
242 let saved = std::mem::replace(&mut self.module_level, false);
243 self.compile_block(body)?;
244 self.module_level = saved;
245 }
246 }
247
248 for fb in self.all_pending_finallys() {
249 self.compile_finally_inline(&fb)?;
250 }
251 self.chunk.emit(Op::Nil, self.line);
252 self.chunk.emit(Op::Return, self.line);
253 Ok(self.chunk)
254 }
255
256 pub(super) fn compile_parent_pipeline(
258 &mut self,
259 program: &[SNode],
260 parent_name: &str,
261 ) -> Result<(), CompileError> {
262 let parent = program
263 .iter()
264 .find(|sn| matches!(&sn.node, Node::Pipeline { name, .. } if name == parent_name));
265 if let Some(sn) = parent {
266 if let Node::Pipeline { body, extends, .. } = &sn.node {
267 if let Some(grandparent) = extends {
268 self.compile_parent_pipeline(program, grandparent)?;
269 }
270 for stmt in body {
271 self.compile_node(stmt)?;
272 if Self::produces_value(&stmt.node) {
273 self.chunk.emit(Op::Pop, self.line);
274 }
275 }
276 }
277 }
278 Ok(())
279 }
280
281 pub(super) fn emit_default_preamble(
286 &mut self,
287 params: &[TypedParam],
288 ) -> Result<(), CompileError> {
289 for (i, param) in params.iter().enumerate() {
290 if let Some(default_expr) = ¶m.default_value {
291 self.chunk.emit(Op::GetArgc, self.line);
292 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
293 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
294 self.chunk.emit(Op::GreaterEqual, self.line);
295 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
296 self.chunk.emit(Op::Pop, self.line);
298 self.compile_node(default_expr)?;
299 self.emit_init_or_define_binding(¶m.name, false);
300 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
301 self.chunk.patch_jump(skip_jump);
302 self.chunk.emit(Op::Pop, self.line);
303 self.chunk.patch_jump(end_jump);
304 }
305 }
306 Ok(())
307 }
308
309 pub(super) fn emit_type_checks(&mut self, params: &[TypedParam]) {
314 for param in params {
315 if let Some(type_expr) = ¶m.type_expr {
316 if let harn_parser::TypeExpr::Named(name) = type_expr {
317 if let Some(methods) = self.interface_methods.get(name).cloned() {
318 let fn_idx = self
319 .chunk
320 .add_constant(Constant::String("__assert_interface".into()));
321 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
322 self.emit_get_binding(¶m.name);
323 let name_idx = self
324 .chunk
325 .add_constant(Constant::String(param.name.clone()));
326 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
327 let iface_idx = self.chunk.add_constant(Constant::String(name.clone()));
328 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
329 let methods_str = methods.join(",");
330 let methods_idx = self.chunk.add_constant(Constant::String(methods_str));
331 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
332 self.chunk.emit_u8(Op::Call, 4, self.line);
333 self.chunk.emit(Op::Pop, self.line);
334 continue;
335 }
336 }
337
338 if let Some(schema) = Self::type_expr_to_schema_value(type_expr) {
339 let fn_idx = self
340 .chunk
341 .add_constant(Constant::String("__assert_schema".into()));
342 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
343 self.emit_get_binding(¶m.name);
344 let name_idx = self
345 .chunk
346 .add_constant(Constant::String(param.name.clone()));
347 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
348 self.emit_vm_value_literal(&schema);
349 self.chunk.emit_u8(Op::Call, 3, self.line);
350 self.chunk.emit(Op::Pop, self.line);
351 }
352 }
353 }
354 }
355
356 pub(crate) fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
357 match type_expr {
358 harn_parser::TypeExpr::Named(name) => match name.as_str() {
359 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
360 | "closure" | "bytes" => Some(VmValue::Dict(Rc::new(BTreeMap::from([(
361 "type".to_string(),
362 VmValue::String(Rc::from(name.as_str())),
363 )])))),
364 _ => None,
365 },
366 harn_parser::TypeExpr::Shape(fields) => {
367 let mut properties = BTreeMap::new();
368 let mut required = Vec::new();
369 for field in fields {
370 let field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
371 properties.insert(field.name.clone(), field_schema);
372 if !field.optional {
373 required.push(VmValue::String(Rc::from(field.name.as_str())));
374 }
375 }
376 let mut out = BTreeMap::new();
377 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
378 out.insert("properties".to_string(), VmValue::Dict(Rc::new(properties)));
379 if !required.is_empty() {
380 out.insert("required".to_string(), VmValue::List(Rc::new(required)));
381 }
382 Some(VmValue::Dict(Rc::new(out)))
383 }
384 harn_parser::TypeExpr::List(inner) => {
385 let mut out = BTreeMap::new();
386 out.insert("type".to_string(), VmValue::String(Rc::from("list")));
387 if let Some(item_schema) = Self::type_expr_to_schema_value(inner) {
388 out.insert("items".to_string(), item_schema);
389 }
390 Some(VmValue::Dict(Rc::new(out)))
391 }
392 harn_parser::TypeExpr::DictType(key, value) => {
393 let mut out = BTreeMap::new();
394 out.insert("type".to_string(), VmValue::String(Rc::from("dict")));
395 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
396 if let Some(value_schema) = Self::type_expr_to_schema_value(value) {
397 out.insert("additional_properties".to_string(), value_schema);
398 }
399 }
400 Some(VmValue::Dict(Rc::new(out)))
401 }
402 harn_parser::TypeExpr::Union(members) => {
403 if !members.is_empty()
408 && members
409 .iter()
410 .all(|m| matches!(m, harn_parser::TypeExpr::LitString(_)))
411 {
412 let values = members
413 .iter()
414 .map(|m| match m {
415 harn_parser::TypeExpr::LitString(s) => {
416 VmValue::String(Rc::from(s.as_str()))
417 }
418 _ => unreachable!(),
419 })
420 .collect::<Vec<_>>();
421 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
422 ("type".to_string(), VmValue::String(Rc::from("string"))),
423 ("enum".to_string(), VmValue::List(Rc::new(values))),
424 ]))));
425 }
426 if !members.is_empty()
427 && members
428 .iter()
429 .all(|m| matches!(m, harn_parser::TypeExpr::LitInt(_)))
430 {
431 let values = members
432 .iter()
433 .map(|m| match m {
434 harn_parser::TypeExpr::LitInt(v) => VmValue::Int(*v),
435 _ => unreachable!(),
436 })
437 .collect::<Vec<_>>();
438 return Some(VmValue::Dict(Rc::new(BTreeMap::from([
439 ("type".to_string(), VmValue::String(Rc::from("int"))),
440 ("enum".to_string(), VmValue::List(Rc::new(values))),
441 ]))));
442 }
443 let branches = members
444 .iter()
445 .filter_map(Self::type_expr_to_schema_value)
446 .collect::<Vec<_>>();
447 if branches.is_empty() {
448 None
449 } else {
450 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
451 "union".to_string(),
452 VmValue::List(Rc::new(branches)),
453 )]))))
454 }
455 }
456 harn_parser::TypeExpr::FnType { .. } => {
457 Some(VmValue::Dict(Rc::new(BTreeMap::from([(
458 "type".to_string(),
459 VmValue::String(Rc::from("closure")),
460 )]))))
461 }
462 harn_parser::TypeExpr::Applied { .. } => None,
463 harn_parser::TypeExpr::Iter(_) => None,
464 harn_parser::TypeExpr::Never => None,
465 harn_parser::TypeExpr::LitString(s) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
466 ("type".to_string(), VmValue::String(Rc::from("string"))),
467 ("const".to_string(), VmValue::String(Rc::from(s.as_str()))),
468 ])))),
469 harn_parser::TypeExpr::LitInt(v) => Some(VmValue::Dict(Rc::new(BTreeMap::from([
470 ("type".to_string(), VmValue::String(Rc::from("int"))),
471 ("const".to_string(), VmValue::Int(*v)),
472 ])))),
473 }
474 }
475
476 pub(super) fn emit_vm_value_literal(&mut self, value: &VmValue) {
477 match value {
478 VmValue::String(text) => {
479 let idx = self.chunk.add_constant(Constant::String(text.to_string()));
480 self.chunk.emit_u16(Op::Constant, idx, self.line);
481 }
482 VmValue::Int(number) => {
483 let idx = self.chunk.add_constant(Constant::Int(*number));
484 self.chunk.emit_u16(Op::Constant, idx, self.line);
485 }
486 VmValue::Float(number) => {
487 let idx = self.chunk.add_constant(Constant::Float(*number));
488 self.chunk.emit_u16(Op::Constant, idx, self.line);
489 }
490 VmValue::Bool(value) => {
491 let idx = self.chunk.add_constant(Constant::Bool(*value));
492 self.chunk.emit_u16(Op::Constant, idx, self.line);
493 }
494 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
495 VmValue::List(items) => {
496 for item in items.iter() {
497 self.emit_vm_value_literal(item);
498 }
499 self.chunk
500 .emit_u16(Op::BuildList, items.len() as u16, self.line);
501 }
502 VmValue::Dict(entries) => {
503 for (key, item) in entries.iter() {
504 let key_idx = self.chunk.add_constant(Constant::String(key.clone()));
505 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
506 self.emit_vm_value_literal(item);
507 }
508 self.chunk
509 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
510 }
511 _ => {}
512 }
513 }
514
515 pub(super) fn emit_type_name_extra(&mut self, type_name_idx: u16) {
517 let hi = (type_name_idx >> 8) as u8;
518 let lo = type_name_idx as u8;
519 self.chunk.code.push(hi);
520 self.chunk.code.push(lo);
521 self.chunk.lines.push(self.line);
522 self.chunk.columns.push(self.column);
523 self.chunk.lines.push(self.line);
524 self.chunk.columns.push(self.column);
525 }
526
527 pub(super) fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
529 if body.is_empty() {
530 self.chunk.emit(Op::Nil, self.line);
531 } else {
532 self.compile_scoped_block(body)?;
533 }
534 Ok(())
535 }
536
537 pub(super) fn compile_catch_binding(
539 &mut self,
540 error_var: &Option<String>,
541 ) -> Result<(), CompileError> {
542 if let Some(var_name) = error_var {
543 self.emit_define_binding(var_name, false);
544 } else {
545 self.chunk.emit(Op::Pop, self.line);
546 }
547 Ok(())
548 }
549
550 pub(super) fn compile_finally_inline(
557 &mut self,
558 finally_body: &[SNode],
559 ) -> Result<(), CompileError> {
560 if !finally_body.is_empty() {
561 self.compile_scoped_block(finally_body)?;
562 self.chunk.emit(Op::Pop, self.line);
563 }
564 Ok(())
565 }
566
567 pub(super) fn pending_finallys_until_barrier(&self) -> Vec<Vec<SNode>> {
572 let mut out = Vec::new();
573 for entry in self.finally_bodies.iter().rev() {
574 match entry {
575 FinallyEntry::CatchBarrier => break,
576 FinallyEntry::Finally(body) => out.push(body.clone()),
577 }
578 }
579 out
580 }
581
582 pub(super) fn pending_finallys_down_to(&self, floor: usize) -> Vec<Vec<SNode>> {
588 let mut out = Vec::new();
589 for entry in self.finally_bodies[floor..].iter().rev() {
590 if let FinallyEntry::Finally(body) = entry {
591 out.push(body.clone());
592 }
593 }
594 out
595 }
596
597 pub(super) fn all_pending_finallys(&self) -> Vec<Vec<SNode>> {
599 self.pending_finallys_down_to(0)
600 }
601
602 pub(super) fn has_pending_finally(&self) -> bool {
604 self.finally_bodies
605 .iter()
606 .any(|e| matches!(e, FinallyEntry::Finally(_)))
607 }
608
609 pub(super) fn compile_plain_rethrow(&mut self) -> Result<(), CompileError> {
618 self.temp_counter += 1;
619 let temp_name = format!("__finally_err_{}__", self.temp_counter);
620 self.emit_define_binding(&temp_name, true);
621 self.emit_get_binding(&temp_name);
622 self.chunk.emit(Op::Throw, self.line);
623 Ok(())
624 }
625
626 pub(super) fn declare_param_slots(&mut self, params: &[TypedParam]) {
627 for param in params {
628 self.define_local_slot(¶m.name, false);
629 }
630 }
631
632 fn define_local_slot(&mut self, name: &str, mutable: bool) -> Option<u16> {
633 if self.module_level || harn_parser::is_discard_name(name) {
634 return None;
635 }
636 let current = self.local_scopes.last_mut()?;
637 if let Some(existing) = current.get_mut(name) {
638 if existing.mutable || mutable {
639 if mutable {
640 existing.mutable = true;
641 if let Some(info) = self.chunk.local_slots.get_mut(existing.slot as usize) {
642 info.mutable = true;
643 }
644 }
645 return Some(existing.slot);
646 }
647 return None;
648 }
649 let slot = self
650 .chunk
651 .add_local_slot(name.to_string(), mutable, self.scope_depth);
652 current.insert(name.to_string(), super::LocalBinding { slot, mutable });
653 Some(slot)
654 }
655
656 pub(super) fn resolve_local_slot(&self, name: &str) -> Option<super::LocalBinding> {
657 if self.module_level {
658 return None;
659 }
660 self.local_scopes
661 .iter()
662 .rev()
663 .find_map(|scope| scope.get(name).copied())
664 }
665
666 pub(super) fn emit_get_binding(&mut self, name: &str) {
667 if let Some(binding) = self.resolve_local_slot(name) {
668 self.chunk
669 .emit_u16(Op::GetLocalSlot, binding.slot, self.line);
670 } else {
671 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
672 self.chunk.emit_u16(Op::GetVar, idx, self.line);
673 }
674 }
675
676 pub(super) fn emit_define_binding(&mut self, name: &str, mutable: bool) {
677 if let Some(slot) = self.define_local_slot(name, mutable) {
678 self.chunk.emit_u16(Op::DefLocalSlot, slot, self.line);
679 } else {
680 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
681 let op = if mutable { Op::DefVar } else { Op::DefLet };
682 self.chunk.emit_u16(op, idx, self.line);
683 }
684 }
685
686 pub(super) fn emit_init_or_define_binding(&mut self, name: &str, mutable: bool) {
687 if let Some(binding) = self.resolve_local_slot(name) {
688 self.chunk
689 .emit_u16(Op::DefLocalSlot, binding.slot, self.line);
690 } else {
691 self.emit_define_binding(name, mutable);
692 }
693 }
694
695 pub(super) fn emit_set_binding(&mut self, name: &str) {
696 if let Some(binding) = self.resolve_local_slot(name) {
697 let _ = binding.mutable;
698 self.chunk
699 .emit_u16(Op::SetLocalSlot, binding.slot, self.line);
700 } else {
701 let idx = self.chunk.add_constant(Constant::String(name.to_string()));
702 self.chunk.emit_u16(Op::SetVar, idx, self.line);
703 }
704 }
705
706 pub(super) fn begin_scope(&mut self) {
707 self.chunk.emit(Op::PushScope, self.line);
708 self.scope_depth += 1;
709 self.type_scopes.push(std::collections::HashMap::new());
710 self.local_scopes.push(std::collections::HashMap::new());
711 }
712
713 pub(super) fn end_scope(&mut self) {
714 if self.scope_depth > 0 {
715 self.chunk.emit(Op::PopScope, self.line);
716 self.scope_depth -= 1;
717 self.type_scopes.pop();
718 self.local_scopes.pop();
719 }
720 }
721
722 pub(super) fn unwind_scopes_to(&mut self, target_depth: usize) {
723 while self.scope_depth > target_depth {
724 self.chunk.emit(Op::PopScope, self.line);
725 self.scope_depth -= 1;
726 self.type_scopes.pop();
727 self.local_scopes.pop();
728 }
729 }
730
731 pub(super) fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
732 self.begin_scope();
733 if stmts.is_empty() {
734 self.chunk.emit(Op::Nil, self.line);
735 } else {
736 self.compile_block(stmts)?;
737 }
738 self.end_scope();
739 Ok(())
740 }
741
742 pub(super) fn compile_scoped_statements(
743 &mut self,
744 stmts: &[SNode],
745 ) -> Result<(), CompileError> {
746 self.begin_scope();
747 for sn in stmts {
748 self.compile_node(sn)?;
749 if Self::produces_value(&sn.node) {
750 self.chunk.emit(Op::Pop, self.line);
751 }
752 }
753 self.end_scope();
754 Ok(())
755 }
756
757 pub(super) fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
758 for (i, snode) in stmts.iter().enumerate() {
759 self.compile_node(snode)?;
760 let is_last = i == stmts.len() - 1;
761 if is_last {
762 if !Self::produces_value(&snode.node) {
764 self.chunk.emit(Op::Nil, self.line);
765 }
766 } else if Self::produces_value(&snode.node) {
767 self.chunk.emit(Op::Pop, self.line);
768 }
769 }
770 Ok(())
771 }
772
773 pub(super) fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
775 self.begin_scope();
776 if body.is_empty() {
777 self.chunk.emit(Op::Nil, self.line);
778 } else {
779 self.compile_block(body)?;
780 if !Self::produces_value(&body.last().unwrap().node) {
781 self.chunk.emit(Op::Nil, self.line);
782 }
783 }
784 self.end_scope();
785 Ok(())
786 }
787
788 pub(super) fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
790 match op {
791 "+" => self.chunk.emit(Op::Add, self.line),
792 "-" => self.chunk.emit(Op::Sub, self.line),
793 "*" => self.chunk.emit(Op::Mul, self.line),
794 "/" => self.chunk.emit(Op::Div, self.line),
795 "%" => self.chunk.emit(Op::Mod, self.line),
796 _ => {
797 return Err(CompileError {
798 message: format!("Unknown compound operator: {op}"),
799 line: self.line,
800 })
801 }
802 }
803 Ok(())
804 }
805
806 pub(super) fn root_var_name(&self, node: &SNode) -> Option<String> {
808 match &node.node {
809 Node::Identifier(name) => Some(name.clone()),
810 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
811 self.root_var_name(object)
812 }
813 Node::SubscriptAccess { object, .. } | Node::OptionalSubscriptAccess { object, .. } => {
814 self.root_var_name(object)
815 }
816 _ => None,
817 }
818 }
819
820 pub(super) fn compile_top_level_declarations(
821 &mut self,
822 program: &[SNode],
823 ) -> Result<(), CompileError> {
824 for sn in program {
832 if matches!(&sn.node, Node::LetBinding { .. } | Node::VarBinding { .. }) {
833 self.compile_node(sn)?;
834 }
835 }
836 for sn in program {
842 let inner_kind = match &sn.node {
843 Node::AttributedDecl { inner, .. } => &inner.node,
844 other => other,
845 };
846 if matches!(
847 inner_kind,
848 Node::FnDecl { .. }
849 | Node::ToolDecl { .. }
850 | Node::SkillDecl { .. }
851 | Node::ImplBlock { .. }
852 | Node::StructDecl { .. }
853 | Node::EnumDecl { .. }
854 | Node::InterfaceDecl { .. }
855 | Node::TypeDecl { .. }
856 ) {
857 self.compile_node(sn)?;
858 }
859 }
860 Ok(())
861 }
862
863 pub(super) fn collect_enum_names(
865 nodes: &[SNode],
866 names: &mut std::collections::HashSet<String>,
867 ) {
868 for sn in nodes {
869 match &sn.node {
870 Node::EnumDecl { name, .. } => {
871 names.insert(name.clone());
872 }
873 Node::Pipeline { body, .. } => {
874 Self::collect_enum_names(body, names);
875 }
876 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
877 Self::collect_enum_names(body, names);
878 }
879 Node::SkillDecl { fields, .. } => {
880 for (_k, v) in fields {
881 Self::collect_enum_names(std::slice::from_ref(v), names);
882 }
883 }
884 Node::Block(stmts) => {
885 Self::collect_enum_names(stmts, names);
886 }
887 Node::AttributedDecl { inner, .. } => {
888 Self::collect_enum_names(std::slice::from_ref(inner), names);
889 }
890 _ => {}
891 }
892 }
893 }
894
895 pub(super) fn collect_struct_layouts(
896 nodes: &[SNode],
897 layouts: &mut std::collections::HashMap<String, Vec<String>>,
898 ) {
899 for sn in nodes {
900 match &sn.node {
901 Node::StructDecl { name, fields, .. } => {
902 layouts.insert(
903 name.clone(),
904 fields.iter().map(|field| field.name.clone()).collect(),
905 );
906 }
907 Node::Pipeline { body, .. }
908 | Node::FnDecl { body, .. }
909 | Node::ToolDecl { body, .. } => {
910 Self::collect_struct_layouts(body, layouts);
911 }
912 Node::SkillDecl { fields, .. } => {
913 for (_k, v) in fields {
914 Self::collect_struct_layouts(std::slice::from_ref(v), layouts);
915 }
916 }
917 Node::Block(stmts) => {
918 Self::collect_struct_layouts(stmts, layouts);
919 }
920 Node::AttributedDecl { inner, .. } => {
921 Self::collect_struct_layouts(std::slice::from_ref(inner), layouts);
922 }
923 _ => {}
924 }
925 }
926 }
927
928 pub(super) fn collect_interface_methods(
929 nodes: &[SNode],
930 interfaces: &mut std::collections::HashMap<String, Vec<String>>,
931 ) {
932 for sn in nodes {
933 match &sn.node {
934 Node::InterfaceDecl { name, methods, .. } => {
935 let method_names: Vec<String> =
936 methods.iter().map(|m| m.name.clone()).collect();
937 interfaces.insert(name.clone(), method_names);
938 }
939 Node::Pipeline { body, .. }
940 | Node::FnDecl { body, .. }
941 | Node::ToolDecl { body, .. } => {
942 Self::collect_interface_methods(body, interfaces);
943 }
944 Node::SkillDecl { fields, .. } => {
945 for (_k, v) in fields {
946 Self::collect_interface_methods(std::slice::from_ref(v), interfaces);
947 }
948 }
949 Node::Block(stmts) => {
950 Self::collect_interface_methods(stmts, interfaces);
951 }
952 Node::AttributedDecl { inner, .. } => {
953 Self::collect_interface_methods(std::slice::from_ref(inner), interfaces);
954 }
955 _ => {}
956 }
957 }
958 }
959
960 pub fn compile_fn_body(
973 &mut self,
974 params: &[TypedParam],
975 body: &[SNode],
976 source_file: Option<String>,
977 ) -> Result<CompiledFunction, CompileError> {
978 let mut fn_compiler = Compiler::for_nested_body();
979 fn_compiler.enum_names = self.enum_names.clone();
980 fn_compiler.interface_methods = self.interface_methods.clone();
981 fn_compiler.type_aliases = self.type_aliases.clone();
982 fn_compiler.struct_layouts = self.struct_layouts.clone();
983 fn_compiler.declare_param_slots(params);
984 fn_compiler.record_param_types(params);
985 fn_compiler.emit_default_preamble(params)?;
986 fn_compiler.emit_type_checks(params);
987 let is_gen = body_contains_yield(body);
988 fn_compiler.compile_block(body)?;
989 fn_compiler.chunk.emit(Op::Nil, 0);
990 fn_compiler.chunk.emit(Op::Return, 0);
991 fn_compiler.chunk.source_file = source_file;
992 Ok(CompiledFunction {
993 name: String::new(),
994 params: TypedParam::names(params),
995 default_start: TypedParam::default_start(params),
996 chunk: Rc::new(fn_compiler.chunk),
997 is_generator: is_gen,
998 has_rest_param: false,
999 })
1000 }
1001
1002 pub(super) fn produces_value(node: &Node) -> bool {
1004 match node {
1005 Node::LetBinding { .. }
1006 | Node::VarBinding { .. }
1007 | Node::Assignment { .. }
1008 | Node::ReturnStmt { .. }
1009 | Node::FnDecl { .. }
1010 | Node::ToolDecl { .. }
1011 | Node::SkillDecl { .. }
1012 | Node::ImplBlock { .. }
1013 | Node::StructDecl { .. }
1014 | Node::EnumDecl { .. }
1015 | Node::InterfaceDecl { .. }
1016 | Node::TypeDecl { .. }
1017 | Node::ThrowStmt { .. }
1018 | Node::BreakStmt
1019 | Node::ContinueStmt
1020 | Node::RequireStmt { .. }
1021 | Node::DeferStmt { .. } => false,
1022 Node::TryCatch { .. }
1023 | Node::TryExpr { .. }
1024 | Node::Retry { .. }
1025 | Node::GuardStmt { .. }
1026 | Node::DeadlineBlock { .. }
1027 | Node::MutexBlock { .. }
1028 | Node::Spread(_) => true,
1029 _ => true,
1030 }
1031 }
1032}
1033
1034impl Default for Compiler {
1035 fn default() -> Self {
1036 Self::new()
1037 }
1038}