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