1use harn_parser::{Node, SNode, TypeExpr};
2
3mod closures;
4mod concurrency;
5mod decls;
6mod error;
7mod error_handling;
8mod expressions;
9mod hitl;
10mod optimizer;
11mod patterns;
12mod pipe;
13mod state;
14mod statements;
15#[cfg(test)]
16mod tests;
17mod type_facts;
18mod yield_scan;
19
20pub use error::CompileError;
21
22use crate::chunk::{Chunk, Constant, Op};
23
24pub const HARN_DISABLE_OPTIMIZATIONS_ENV: &str = "HARN_DISABLE_OPTIMIZATIONS";
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub struct CompilerOptions {
34 optimize: bool,
35}
36
37impl CompilerOptions {
38 pub fn optimized() -> Self {
39 Self { optimize: true }
40 }
41
42 pub fn without_optimizations() -> Self {
43 Self { optimize: false }
44 }
45
46 pub fn from_env() -> Self {
47 if std::env::var_os(HARN_DISABLE_OPTIMIZATIONS_ENV).is_some() {
48 Self::without_optimizations()
49 } else {
50 Self::optimized()
51 }
52 }
53
54 pub fn optimizations_enabled(self) -> bool {
55 self.optimize
56 }
57}
58
59impl Default for CompilerOptions {
60 fn default() -> Self {
61 Self::optimized()
62 }
63}
64
65fn peel_node(sn: &SNode) -> &Node {
69 match &sn.node {
70 Node::AttributedDecl { inner, .. } => &inner.node,
71 other => other,
72 }
73}
74
75#[derive(Clone, Debug)]
78enum FinallyEntry {
79 Finally(Vec<SNode>),
80 CatchBarrier,
81}
82
83struct LoopContext {
85 start_offset: usize,
87 break_patches: Vec<usize>,
89 has_iterator: bool,
91 handler_depth: usize,
93 finally_depth: usize,
95 scope_depth: usize,
97}
98
99#[derive(Clone, Copy, Debug)]
100struct LocalBinding {
101 slot: u16,
102 mutable: bool,
103}
104
105pub struct Compiler {
107 options: CompilerOptions,
108 chunk: Chunk,
109 line: u32,
110 column: u32,
111 enum_names: std::collections::HashSet<String>,
113 struct_layouts: std::collections::HashMap<String, Vec<String>>,
115 interface_methods: std::collections::HashMap<String, Vec<String>>,
117 loop_stack: Vec<LoopContext>,
119 handler_depth: usize,
121 finally_bodies: Vec<FinallyEntry>,
133 temp_counter: usize,
135 scope_depth: usize,
137 type_aliases: std::collections::HashMap<String, TypeExpr>,
140 type_scopes: Vec<std::collections::HashMap<String, TypeExpr>>,
145 monomorphic_bindings: std::collections::HashSet<(usize, usize)>,
157 string_constants: std::collections::HashMap<String, u16>,
160 local_scopes: Vec<std::collections::HashMap<String, LocalBinding>>,
164 module_level: bool,
170}
171
172impl Compiler {
173 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
177 self.line = snode.span.line as u32;
178 self.column = snode.span.column as u32;
179 self.chunk.set_column(self.column);
180 if self.options.optimizations_enabled() {
181 if let Some(folded) = optimizer::fold_constant_expr(snode) {
182 if folded.node != snode.node {
183 return self.compile_node(&folded);
184 }
185 }
186 }
187 match &snode.node {
188 Node::IntLiteral(n) => {
189 let idx = self.chunk.add_constant(Constant::Int(*n));
190 self.chunk.emit_u16(Op::Constant, idx, self.line);
191 }
192 Node::FloatLiteral(n) => {
193 let idx = self.chunk.add_constant(Constant::Float(*n));
194 self.chunk.emit_u16(Op::Constant, idx, self.line);
195 }
196 Node::StringLiteral(s) | Node::RawStringLiteral(s) => {
197 let idx = self.string_constant(s);
198 self.chunk.emit_u16(Op::Constant, idx, self.line);
199 }
200 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
201 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
202 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
203 Node::DurationLiteral(ms) => {
204 let ms = i64::try_from(*ms).map_err(|_| CompileError {
205 message: "duration literal is too large".to_string(),
206 line: self.line,
207 })?;
208 let idx = self.chunk.add_constant(Constant::Duration(ms));
209 self.chunk.emit_u16(Op::Constant, idx, self.line);
210 }
211 Node::Identifier(name) => {
212 self.emit_get_binding(name);
213 }
214 Node::LetBinding { pattern, value, .. } => {
215 let binding_type = match &snode.node {
216 Node::LetBinding {
217 type_ann: Some(type_ann),
218 ..
219 } => Some(type_ann.clone()),
220 _ => self.infer_expr_type(value),
221 };
222 self.compile_node(value)?;
223 self.compile_destructuring(pattern, false)?;
224 self.record_binding_type(pattern, binding_type.clone());
225 self.maybe_register_owned_drop(pattern, binding_type.as_ref(), snode.span);
226 }
227 Node::VarBinding { pattern, value, .. } => {
228 let binding_type = match &snode.node {
229 Node::VarBinding {
230 type_ann: Some(type_ann),
231 ..
232 } => Some(type_ann.clone()),
233 _ => self.infer_expr_type(value),
234 };
235 self.compile_node(value)?;
236 self.compile_destructuring(pattern, true)?;
237 let binding_type = self.gate_mutable_primitive_type(snode.span, binding_type);
245 self.record_binding_type(pattern, binding_type.clone());
246 self.maybe_register_owned_drop(pattern, binding_type.as_ref(), snode.span);
247 }
248 Node::ConstBinding {
249 name,
250 type_ann,
251 value,
252 } => {
253 let binding_type = type_ann.clone().or_else(|| self.infer_expr_type(value));
260 self.compile_node(value)?;
261 let pattern = harn_parser::BindingPattern::Identifier(name.clone());
262 self.compile_destructuring(&pattern, false)?;
263 self.record_binding_type(&pattern, binding_type.clone());
264 self.maybe_register_owned_drop(&pattern, binding_type.as_ref(), snode.span);
265 }
266 Node::Assignment {
267 target, value, op, ..
268 } => {
269 self.compile_assignment(target, value, op)?;
270 }
271 Node::BinaryOp { op, left, right } => {
272 self.compile_binary_op(op, left, right)?;
273 }
274 Node::UnaryOp { op, operand } => {
275 self.compile_node(operand)?;
276 match op.as_str() {
277 "-" => self.chunk.emit(Op::Negate, self.line),
278 "!" => self.chunk.emit(Op::Not, self.line),
279 _ => {}
280 }
281 }
282 Node::Ternary {
283 condition,
284 true_expr,
285 false_expr,
286 } => {
287 self.compile_node(condition)?;
288 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
289 self.chunk.emit(Op::Pop, self.line);
290 self.compile_node(true_expr)?;
291 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
292 self.chunk.patch_jump(else_jump);
293 self.chunk.emit(Op::Pop, self.line);
294 self.compile_node(false_expr)?;
295 self.chunk.patch_jump(end_jump);
296 }
297 Node::FunctionCall { name, args, .. } => {
298 self.compile_function_call(name, args)?;
299 }
300 Node::MethodCall {
301 object,
302 method,
303 args,
304 } => {
305 self.compile_method_call(object, method, args)?;
306 }
307 Node::OptionalMethodCall {
308 object,
309 method,
310 args,
311 } => {
312 self.compile_node(object)?;
313 for arg in args {
314 self.compile_node(arg)?;
315 }
316 let name_idx = self.string_constant(method);
317 self.chunk
318 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
319 }
320 Node::PropertyAccess { object, property } => {
321 self.compile_property_access(object, property)?;
322 }
323 Node::OptionalPropertyAccess { object, property } => {
324 self.compile_node(object)?;
325 let idx = self.string_constant(property);
326 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
327 }
328 Node::SubscriptAccess { object, index } => {
329 self.compile_node(object)?;
330 self.compile_node(index)?;
331 self.chunk.emit(Op::Subscript, self.line);
332 }
333 Node::OptionalSubscriptAccess { object, index } => {
334 self.compile_node(object)?;
335 self.compile_node(index)?;
336 self.chunk.emit(Op::SubscriptOpt, self.line);
337 }
338 Node::SliceAccess { object, start, end } => {
339 self.compile_node(object)?;
340 if let Some(s) = start {
341 self.compile_node(s)?;
342 } else {
343 self.chunk.emit(Op::Nil, self.line);
344 }
345 if let Some(e) = end {
346 self.compile_node(e)?;
347 } else {
348 self.chunk.emit(Op::Nil, self.line);
349 }
350 self.chunk.emit(Op::Slice, self.line);
351 }
352 Node::IfElse {
353 condition,
354 then_body,
355 else_body,
356 } => {
357 self.compile_if_else(condition, then_body, else_body)?;
358 }
359 Node::WhileLoop { condition, body } => {
360 self.compile_while_loop(condition, body)?;
361 }
362 Node::ForIn {
363 pattern,
364 iterable,
365 body,
366 } => {
367 self.compile_for_in(pattern, iterable, body)?;
368 }
369 Node::ReturnStmt { value } => {
370 self.compile_return_stmt(value)?;
371 }
372 Node::BreakStmt => {
373 self.compile_break_stmt()?;
374 }
375 Node::ContinueStmt => {
376 self.compile_continue_stmt()?;
377 }
378 Node::ListLiteral(elements) => {
379 self.compile_list_literal(elements)?;
380 }
381 Node::DictLiteral(entries) => {
382 self.compile_dict_literal(entries)?;
383 }
384 Node::InterpolatedString(segments) => {
385 self.compile_interpolated_string(segments)?;
386 }
387 Node::FnDecl {
388 name,
389 type_params,
390 params,
391 body,
392 is_stream,
393 ..
394 } => {
395 self.compile_fn_decl(name, type_params, params, body, *is_stream)?;
396 }
397 Node::ToolDecl {
398 name,
399 description,
400 params,
401 return_type,
402 body,
403 ..
404 } => {
405 self.compile_tool_decl(name, description, params, return_type, body)?;
406 }
407 Node::SkillDecl { name, fields, .. } => {
408 self.compile_skill_decl(name, fields)?;
409 }
410 Node::EvalPackDecl {
411 binding_name,
412 pack_id,
413 fields,
414 body,
415 summarize,
416 ..
417 } => {
418 self.compile_eval_pack_decl(binding_name, pack_id, fields, body, summarize, true)?;
419 }
420 Node::Closure { params, body, .. } => {
421 self.compile_closure(params, body)?;
422 }
423 Node::ThrowStmt { value } => {
424 self.compile_throw_stmt(value)?;
425 }
426 Node::MatchExpr { value, arms } => {
427 self.compile_match_expr(value, arms)?;
428 }
429 Node::RangeExpr {
430 start,
431 end,
432 inclusive,
433 } => {
434 let name_idx = self.string_constant("__range__");
435 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
436 self.compile_node(start)?;
437 self.compile_node(end)?;
438 if *inclusive {
439 self.chunk.emit(Op::True, self.line);
440 } else {
441 self.chunk.emit(Op::False, self.line);
442 }
443 self.chunk.emit_u8(Op::Call, 3, self.line);
444 }
445 Node::GuardStmt {
446 condition,
447 else_body,
448 } => {
449 self.compile_guard_stmt(condition, else_body)?;
450 }
451 Node::RequireStmt { condition, message } => {
452 self.compile_node(condition)?;
453 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
454 self.chunk.emit(Op::Pop, self.line);
455 if let Some(message) = message {
456 self.compile_node(message)?;
457 } else {
458 let idx = self.string_constant("require condition failed");
459 self.chunk.emit_u16(Op::Constant, idx, self.line);
460 }
461 self.chunk.emit(Op::Throw, self.line);
462 self.chunk.patch_jump(ok_jump);
463 self.chunk.emit(Op::Pop, self.line);
464 }
465 Node::Block(stmts) => {
466 self.compile_scoped_block(stmts)?;
467 }
468 Node::DeadlineBlock { duration, body } => {
469 self.compile_node(duration)?;
470 self.chunk.emit(Op::DeadlineSetup, self.line);
471 self.compile_scoped_block(body)?;
472 self.chunk.emit(Op::DeadlineEnd, self.line);
473 }
474 Node::MutexBlock { key, body } => {
475 self.begin_scope();
476 let finally_floor = self.finally_bodies.len();
477 match key {
478 Some(key_expr) => {
481 self.compile_node(key_expr)?;
482 self.chunk.emit(Op::SyncMutexEnterKeyed, self.line);
483 }
484 None => {
488 self.chunk.emit(Op::SyncMutexEnter, self.line);
489 }
490 }
491 for sn in body {
492 self.compile_discarded_stmt(sn)?;
493 }
494 self.drain_finallys_to_floor(finally_floor)?;
495 self.chunk.emit(Op::Nil, self.line);
496 self.end_scope();
497 }
498 Node::ScopeBlock { body } => {
499 self.begin_scope();
506 let finally_floor = self.finally_bodies.len();
507 self.chunk.emit(Op::TaskScopeEnter, self.line);
508 for sn in body {
509 self.compile_discarded_stmt(sn)?;
510 }
511 self.drain_finallys_to_floor(finally_floor)?;
512 self.chunk.emit(Op::TaskScopeExit, self.line);
513 self.chunk.emit(Op::Nil, self.line);
514 self.end_scope();
515 }
516 Node::DeferStmt { body } => {
517 self.finally_bodies
526 .push(FinallyEntry::Finally(body.clone()));
527 }
528 Node::YieldExpr { value } => {
529 if let Some(val) = value {
530 self.compile_node(val)?;
531 } else {
532 self.chunk.emit(Op::Nil, self.line);
533 }
534 self.chunk.emit(Op::Yield, self.line);
535 }
536 Node::EmitExpr { value } => {
537 self.compile_node(value)?;
538 self.chunk.emit(Op::Yield, self.line);
539 }
540 Node::EnumConstruct {
541 enum_name,
542 variant,
543 args,
544 } => {
545 self.compile_enum_construct(enum_name, variant, args)?;
546 }
547 Node::StructConstruct {
548 struct_name,
549 fields,
550 } => {
551 self.compile_struct_construct(struct_name, fields)?;
552 }
553 Node::ImportDecl { path, .. } => {
554 let idx = self.string_constant(path);
555 self.chunk.emit_u16(Op::Import, idx, self.line);
556 }
557 Node::SelectiveImport { names, path, .. } => {
558 let path_idx = self.string_constant(path);
559 let names_str = names.join(",");
560 let names_idx = self.owned_string_constant(names_str);
561 self.chunk
562 .emit_u16(Op::SelectiveImport, path_idx, self.line);
563 let hi = (names_idx >> 8) as u8;
564 let lo = names_idx as u8;
565 self.chunk.code.push(hi);
566 self.chunk.code.push(lo);
567 self.chunk.lines.push(self.line);
568 self.chunk.columns.push(self.column);
569 self.chunk.lines.push(self.line);
570 self.chunk.columns.push(self.column);
571 }
572 Node::TryOperator { operand } => {
573 self.compile_node(operand)?;
574 self.chunk.emit(Op::TryUnwrap, self.line);
575 }
576 Node::TryStar { operand } => {
592 self.compile_try_star(operand)?;
593 }
594 Node::ImplBlock { type_name, methods } => {
595 self.compile_impl_block(type_name, methods)?;
596 }
597 Node::StructDecl { name, fields, .. } => {
598 self.compile_struct_decl(name, fields)?;
599 }
600 Node::Pipeline { .. }
611 | Node::OverrideDecl { .. }
612 | Node::TypeDecl { .. }
613 | Node::EnumDecl { .. }
614 | Node::InterfaceDecl { .. } => {}
615 Node::TryCatch {
616 has_catch: _,
617 body,
618 error_var,
619 error_type,
620 catch_body,
621 finally_body,
622 } => {
623 self.compile_try_catch(body, error_var, error_type, catch_body, finally_body)?;
624 }
625 Node::TryExpr { body } => {
626 self.compile_try_expr(body)?;
627 }
628 Node::Retry { count, body } => {
629 self.compile_retry(count, body)?;
630 }
631 Node::CostRoute { options, body } => {
632 self.compile_cost_route(options, body)?;
633 }
634 Node::Parallel {
635 mode,
636 expr,
637 variable,
638 body,
639 options,
640 } => {
641 self.compile_parallel(mode, expr, variable, body, options)?;
642 }
643 Node::SpawnExpr { body } => {
644 self.compile_spawn_expr(body)?;
645 }
646 Node::HitlExpr { kind, args } => {
647 self.compile_hitl_expr(*kind, args)?;
648 }
649 Node::SelectExpr {
650 cases,
651 timeout,
652 default_body,
653 } => {
654 self.compile_select_expr(cases, timeout, default_body)?;
655 }
656 Node::Spread(_) => {
657 return Err(CompileError {
658 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
659 line: self.line,
660 });
661 }
662 Node::AttributedDecl { attributes, inner } => {
663 self.compile_attributed_decl(attributes, inner)?;
664 }
665 Node::OrPattern(_) => {
666 return Err(CompileError {
667 message: "or-pattern (|) can only appear as a match arm pattern".into(),
668 line: self.line,
669 });
670 }
671 }
672 Ok(())
673 }
674}