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 local_scopes: Vec<std::collections::HashMap<String, LocalBinding>>,
149 module_level: bool,
155}
156
157impl Compiler {
158 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
162 self.line = snode.span.line as u32;
163 self.column = snode.span.column as u32;
164 self.chunk.set_column(self.column);
165 if self.options.optimizations_enabled() {
166 if let Some(folded) = optimizer::fold_constant_expr(snode) {
167 if folded.node != snode.node {
168 return self.compile_node(&folded);
169 }
170 }
171 }
172 match &snode.node {
173 Node::IntLiteral(n) => {
174 let idx = self.chunk.add_constant(Constant::Int(*n));
175 self.chunk.emit_u16(Op::Constant, idx, self.line);
176 }
177 Node::FloatLiteral(n) => {
178 let idx = self.chunk.add_constant(Constant::Float(*n));
179 self.chunk.emit_u16(Op::Constant, idx, self.line);
180 }
181 Node::StringLiteral(s) | Node::RawStringLiteral(s) => {
182 let idx = self.chunk.add_constant(Constant::String(s.clone()));
183 self.chunk.emit_u16(Op::Constant, idx, self.line);
184 }
185 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
186 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
187 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
188 Node::DurationLiteral(ms) => {
189 let ms = i64::try_from(*ms).map_err(|_| CompileError {
190 message: "duration literal is too large".to_string(),
191 line: self.line,
192 })?;
193 let idx = self.chunk.add_constant(Constant::Duration(ms));
194 self.chunk.emit_u16(Op::Constant, idx, self.line);
195 }
196 Node::Identifier(name) => {
197 self.emit_get_binding(name);
198 }
199 Node::LetBinding { pattern, value, .. } => {
200 let binding_type = match &snode.node {
201 Node::LetBinding {
202 type_ann: Some(type_ann),
203 ..
204 } => Some(type_ann.clone()),
205 _ => self.infer_expr_type(value),
206 };
207 self.compile_node(value)?;
208 self.compile_destructuring(pattern, false)?;
209 self.record_binding_type(pattern, binding_type.clone());
210 self.maybe_register_owned_drop(pattern, binding_type.as_ref(), snode.span);
211 }
212 Node::VarBinding { pattern, value, .. } => {
213 let binding_type = match &snode.node {
214 Node::VarBinding {
215 type_ann: Some(type_ann),
216 ..
217 } => Some(type_ann.clone()),
218 _ => self.infer_expr_type(value),
219 };
220 self.compile_node(value)?;
221 self.compile_destructuring(pattern, true)?;
222 self.record_binding_type(pattern, binding_type.clone());
223 self.maybe_register_owned_drop(pattern, binding_type.as_ref(), snode.span);
224 }
225 Node::ConstBinding {
226 name,
227 type_ann,
228 value,
229 } => {
230 let binding_type = type_ann.clone().or_else(|| self.infer_expr_type(value));
237 self.compile_node(value)?;
238 let pattern = harn_parser::BindingPattern::Identifier(name.clone());
239 self.compile_destructuring(&pattern, false)?;
240 self.record_binding_type(&pattern, binding_type.clone());
241 self.maybe_register_owned_drop(&pattern, binding_type.as_ref(), snode.span);
242 }
243 Node::Assignment {
244 target, value, op, ..
245 } => {
246 self.compile_assignment(target, value, op)?;
247 }
248 Node::BinaryOp { op, left, right } => {
249 self.compile_binary_op(op, left, right)?;
250 }
251 Node::UnaryOp { op, operand } => {
252 self.compile_node(operand)?;
253 match op.as_str() {
254 "-" => self.chunk.emit(Op::Negate, self.line),
255 "!" => self.chunk.emit(Op::Not, self.line),
256 _ => {}
257 }
258 }
259 Node::Ternary {
260 condition,
261 true_expr,
262 false_expr,
263 } => {
264 self.compile_node(condition)?;
265 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
266 self.chunk.emit(Op::Pop, self.line);
267 self.compile_node(true_expr)?;
268 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
269 self.chunk.patch_jump(else_jump);
270 self.chunk.emit(Op::Pop, self.line);
271 self.compile_node(false_expr)?;
272 self.chunk.patch_jump(end_jump);
273 }
274 Node::FunctionCall { name, args, .. } => {
275 self.compile_function_call(name, args)?;
276 }
277 Node::MethodCall {
278 object,
279 method,
280 args,
281 } => {
282 self.compile_method_call(object, method, args)?;
283 }
284 Node::OptionalMethodCall {
285 object,
286 method,
287 args,
288 } => {
289 self.compile_node(object)?;
290 for arg in args {
291 self.compile_node(arg)?;
292 }
293 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
294 self.chunk
295 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
296 }
297 Node::PropertyAccess { object, property } => {
298 self.compile_property_access(object, property)?;
299 }
300 Node::OptionalPropertyAccess { object, property } => {
301 self.compile_node(object)?;
302 let idx = self.chunk.add_constant(Constant::String(property.clone()));
303 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
304 }
305 Node::SubscriptAccess { object, index } => {
306 self.compile_node(object)?;
307 self.compile_node(index)?;
308 self.chunk.emit(Op::Subscript, self.line);
309 }
310 Node::OptionalSubscriptAccess { object, index } => {
311 self.compile_node(object)?;
312 self.compile_node(index)?;
313 self.chunk.emit(Op::SubscriptOpt, self.line);
314 }
315 Node::SliceAccess { object, start, end } => {
316 self.compile_node(object)?;
317 if let Some(s) = start {
318 self.compile_node(s)?;
319 } else {
320 self.chunk.emit(Op::Nil, self.line);
321 }
322 if let Some(e) = end {
323 self.compile_node(e)?;
324 } else {
325 self.chunk.emit(Op::Nil, self.line);
326 }
327 self.chunk.emit(Op::Slice, self.line);
328 }
329 Node::IfElse {
330 condition,
331 then_body,
332 else_body,
333 } => {
334 self.compile_if_else(condition, then_body, else_body)?;
335 }
336 Node::WhileLoop { condition, body } => {
337 self.compile_while_loop(condition, body)?;
338 }
339 Node::ForIn {
340 pattern,
341 iterable,
342 body,
343 } => {
344 self.compile_for_in(pattern, iterable, body)?;
345 }
346 Node::ReturnStmt { value } => {
347 self.compile_return_stmt(value)?;
348 }
349 Node::BreakStmt => {
350 self.compile_break_stmt()?;
351 }
352 Node::ContinueStmt => {
353 self.compile_continue_stmt()?;
354 }
355 Node::ListLiteral(elements) => {
356 self.compile_list_literal(elements)?;
357 }
358 Node::DictLiteral(entries) => {
359 self.compile_dict_literal(entries)?;
360 }
361 Node::InterpolatedString(segments) => {
362 self.compile_interpolated_string(segments)?;
363 }
364 Node::FnDecl {
365 name,
366 type_params,
367 params,
368 body,
369 is_stream,
370 ..
371 } => {
372 self.compile_fn_decl(name, type_params, params, body, *is_stream)?;
373 }
374 Node::ToolDecl {
375 name,
376 description,
377 params,
378 return_type,
379 body,
380 ..
381 } => {
382 self.compile_tool_decl(name, description, params, return_type, body)?;
383 }
384 Node::SkillDecl { name, fields, .. } => {
385 self.compile_skill_decl(name, fields)?;
386 }
387 Node::EvalPackDecl {
388 binding_name,
389 pack_id,
390 fields,
391 body,
392 summarize,
393 ..
394 } => {
395 self.compile_eval_pack_decl(binding_name, pack_id, fields, body, summarize, true)?;
396 }
397 Node::Closure { params, body, .. } => {
398 self.compile_closure(params, body)?;
399 }
400 Node::ThrowStmt { value } => {
401 self.compile_throw_stmt(value)?;
402 }
403 Node::MatchExpr { value, arms } => {
404 self.compile_match_expr(value, arms)?;
405 }
406 Node::RangeExpr {
407 start,
408 end,
409 inclusive,
410 } => {
411 let name_idx = self
412 .chunk
413 .add_constant(Constant::String("__range__".to_string()));
414 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
415 self.compile_node(start)?;
416 self.compile_node(end)?;
417 if *inclusive {
418 self.chunk.emit(Op::True, self.line);
419 } else {
420 self.chunk.emit(Op::False, self.line);
421 }
422 self.chunk.emit_u8(Op::Call, 3, self.line);
423 }
424 Node::GuardStmt {
425 condition,
426 else_body,
427 } => {
428 self.compile_guard_stmt(condition, else_body)?;
429 }
430 Node::RequireStmt { condition, message } => {
431 self.compile_node(condition)?;
432 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
433 self.chunk.emit(Op::Pop, self.line);
434 if let Some(message) = message {
435 self.compile_node(message)?;
436 } else {
437 let idx = self
438 .chunk
439 .add_constant(Constant::String("require condition failed".to_string()));
440 self.chunk.emit_u16(Op::Constant, idx, self.line);
441 }
442 self.chunk.emit(Op::Throw, self.line);
443 self.chunk.patch_jump(ok_jump);
444 self.chunk.emit(Op::Pop, self.line);
445 }
446 Node::Block(stmts) => {
447 self.compile_scoped_block(stmts)?;
448 }
449 Node::DeadlineBlock { duration, body } => {
450 self.compile_node(duration)?;
451 self.chunk.emit(Op::DeadlineSetup, self.line);
452 self.compile_scoped_block(body)?;
453 self.chunk.emit(Op::DeadlineEnd, self.line);
454 }
455 Node::MutexBlock { body } => {
456 self.begin_scope();
457 let finally_floor = self.finally_bodies.len();
458 let key_idx = self
459 .chunk
460 .add_constant(Constant::String("__default__".to_string()));
461 self.chunk.emit_u16(Op::SyncMutexEnter, key_idx, self.line);
462 for sn in body {
463 self.compile_node(sn)?;
464 if Self::produces_value(&sn.node) {
465 self.chunk.emit(Op::Pop, self.line);
466 }
467 }
468 self.drain_finallys_to_floor(finally_floor)?;
469 self.chunk.emit(Op::Nil, self.line);
470 self.end_scope();
471 }
472 Node::DeferStmt { body } => {
473 self.finally_bodies
475 .push(FinallyEntry::Finally(body.clone()));
476 self.chunk.emit(Op::Nil, self.line);
477 }
478 Node::YieldExpr { value } => {
479 if let Some(val) = value {
480 self.compile_node(val)?;
481 } else {
482 self.chunk.emit(Op::Nil, self.line);
483 }
484 self.chunk.emit(Op::Yield, self.line);
485 }
486 Node::EmitExpr { value } => {
487 self.compile_node(value)?;
488 self.chunk.emit(Op::Yield, self.line);
489 }
490 Node::EnumConstruct {
491 enum_name,
492 variant,
493 args,
494 } => {
495 self.compile_enum_construct(enum_name, variant, args)?;
496 }
497 Node::StructConstruct {
498 struct_name,
499 fields,
500 } => {
501 self.compile_struct_construct(struct_name, fields)?;
502 }
503 Node::ImportDecl { path, .. } => {
504 let idx = self.chunk.add_constant(Constant::String(path.clone()));
505 self.chunk.emit_u16(Op::Import, idx, self.line);
506 }
507 Node::SelectiveImport { names, path, .. } => {
508 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
509 let names_str = names.join(",");
510 let names_idx = self.chunk.add_constant(Constant::String(names_str));
511 self.chunk
512 .emit_u16(Op::SelectiveImport, path_idx, self.line);
513 let hi = (names_idx >> 8) as u8;
514 let lo = names_idx as u8;
515 self.chunk.code.push(hi);
516 self.chunk.code.push(lo);
517 self.chunk.lines.push(self.line);
518 self.chunk.columns.push(self.column);
519 self.chunk.lines.push(self.line);
520 self.chunk.columns.push(self.column);
521 }
522 Node::TryOperator { operand } => {
523 self.compile_node(operand)?;
524 self.chunk.emit(Op::TryUnwrap, self.line);
525 }
526 Node::TryStar { operand } => {
542 self.compile_try_star(operand)?;
543 }
544 Node::ImplBlock { type_name, methods } => {
545 self.compile_impl_block(type_name, methods)?;
546 }
547 Node::StructDecl { name, fields, .. } => {
548 self.compile_struct_decl(name, fields)?;
549 }
550 Node::Pipeline { .. }
552 | Node::OverrideDecl { .. }
553 | Node::TypeDecl { .. }
554 | Node::EnumDecl { .. }
555 | Node::InterfaceDecl { .. } => {
556 self.chunk.emit(Op::Nil, self.line);
557 }
558 Node::TryCatch {
559 has_catch: _,
560 body,
561 error_var,
562 error_type,
563 catch_body,
564 finally_body,
565 } => {
566 self.compile_try_catch(body, error_var, error_type, catch_body, finally_body)?;
567 }
568 Node::TryExpr { body } => {
569 self.compile_try_expr(body)?;
570 }
571 Node::Retry { count, body } => {
572 self.compile_retry(count, body)?;
573 }
574 Node::CostRoute { options, body } => {
575 self.compile_cost_route(options, body)?;
576 }
577 Node::Parallel {
578 mode,
579 expr,
580 variable,
581 body,
582 options,
583 } => {
584 self.compile_parallel(mode, expr, variable, body, options)?;
585 }
586 Node::SpawnExpr { body } => {
587 self.compile_spawn_expr(body)?;
588 }
589 Node::HitlExpr { kind, args } => {
590 self.compile_hitl_expr(*kind, args)?;
591 }
592 Node::SelectExpr {
593 cases,
594 timeout,
595 default_body,
596 } => {
597 self.compile_select_expr(cases, timeout, default_body)?;
598 }
599 Node::Spread(_) => {
600 return Err(CompileError {
601 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
602 line: self.line,
603 });
604 }
605 Node::AttributedDecl { attributes, inner } => {
606 self.compile_attributed_decl(attributes, inner)?;
607 }
608 Node::OrPattern(_) => {
609 return Err(CompileError {
610 message: "or-pattern (|) can only appear as a match arm pattern".into(),
611 line: self.line,
612 });
613 }
614 }
615 Ok(())
616 }
617}