1use harn_parser::{Node, SNode, TypeExpr};
2
3mod closures;
4mod concurrency;
5mod decls;
6mod error;
7mod error_handling;
8mod expressions;
9mod hitl;
10mod patterns;
11mod pipe;
12mod state;
13mod statements;
14#[cfg(test)]
15mod tests;
16mod type_facts;
17mod yield_scan;
18
19pub use error::CompileError;
20
21use crate::chunk::{Chunk, Constant, Op};
22
23fn peel_node(sn: &SNode) -> &Node {
27 match &sn.node {
28 Node::AttributedDecl { inner, .. } => &inner.node,
29 other => other,
30 }
31}
32
33#[derive(Clone, Debug)]
36enum FinallyEntry {
37 Finally(Vec<SNode>),
38 CatchBarrier,
39}
40
41struct LoopContext {
43 start_offset: usize,
45 break_patches: Vec<usize>,
47 has_iterator: bool,
49 handler_depth: usize,
51 finally_depth: usize,
53 scope_depth: usize,
55}
56
57#[derive(Clone, Copy, Debug)]
58struct LocalBinding {
59 slot: u16,
60 mutable: bool,
61}
62
63pub struct Compiler {
65 chunk: Chunk,
66 line: u32,
67 column: u32,
68 enum_names: std::collections::HashSet<String>,
70 struct_layouts: std::collections::HashMap<String, Vec<String>>,
72 interface_methods: std::collections::HashMap<String, Vec<String>>,
74 loop_stack: Vec<LoopContext>,
76 handler_depth: usize,
78 finally_bodies: Vec<FinallyEntry>,
90 temp_counter: usize,
92 scope_depth: usize,
94 type_aliases: std::collections::HashMap<String, TypeExpr>,
97 type_scopes: Vec<std::collections::HashMap<String, TypeExpr>>,
102 local_scopes: Vec<std::collections::HashMap<String, LocalBinding>>,
106 module_level: bool,
112}
113
114impl Compiler {
115 fn compile_node(&mut self, snode: &SNode) -> Result<(), CompileError> {
119 self.line = snode.span.line as u32;
120 self.column = snode.span.column as u32;
121 self.chunk.set_column(self.column);
122 match &snode.node {
123 Node::IntLiteral(n) => {
124 let idx = self.chunk.add_constant(Constant::Int(*n));
125 self.chunk.emit_u16(Op::Constant, idx, self.line);
126 }
127 Node::FloatLiteral(n) => {
128 let idx = self.chunk.add_constant(Constant::Float(*n));
129 self.chunk.emit_u16(Op::Constant, idx, self.line);
130 }
131 Node::StringLiteral(s) | Node::RawStringLiteral(s) => {
132 let idx = self.chunk.add_constant(Constant::String(s.clone()));
133 self.chunk.emit_u16(Op::Constant, idx, self.line);
134 }
135 Node::BoolLiteral(true) => self.chunk.emit(Op::True, self.line),
136 Node::BoolLiteral(false) => self.chunk.emit(Op::False, self.line),
137 Node::NilLiteral => self.chunk.emit(Op::Nil, self.line),
138 Node::DurationLiteral(ms) => {
139 let ms = i64::try_from(*ms).map_err(|_| CompileError {
140 message: "duration literal is too large".to_string(),
141 line: self.line,
142 })?;
143 let idx = self.chunk.add_constant(Constant::Duration(ms));
144 self.chunk.emit_u16(Op::Constant, idx, self.line);
145 }
146 Node::Identifier(name) => {
147 self.emit_get_binding(name);
148 }
149 Node::LetBinding { pattern, value, .. } => {
150 let binding_type = match &snode.node {
151 Node::LetBinding {
152 type_ann: Some(type_ann),
153 ..
154 } => Some(type_ann.clone()),
155 _ => self.infer_expr_type(value),
156 };
157 self.compile_node(value)?;
158 self.compile_destructuring(pattern, false)?;
159 self.record_binding_type(pattern, binding_type);
160 }
161 Node::VarBinding { pattern, value, .. } => {
162 let binding_type = match &snode.node {
163 Node::VarBinding {
164 type_ann: Some(type_ann),
165 ..
166 } => Some(type_ann.clone()),
167 _ => self.infer_expr_type(value),
168 };
169 self.compile_node(value)?;
170 self.compile_destructuring(pattern, true)?;
171 self.record_binding_type(pattern, binding_type);
172 }
173 Node::Assignment {
174 target, value, op, ..
175 } => {
176 self.compile_assignment(target, value, op)?;
177 }
178 Node::BinaryOp { op, left, right } => {
179 self.compile_binary_op(op, left, right)?;
180 }
181 Node::UnaryOp { op, operand } => {
182 self.compile_node(operand)?;
183 match op.as_str() {
184 "-" => self.chunk.emit(Op::Negate, self.line),
185 "!" => self.chunk.emit(Op::Not, self.line),
186 _ => {}
187 }
188 }
189 Node::Ternary {
190 condition,
191 true_expr,
192 false_expr,
193 } => {
194 self.compile_node(condition)?;
195 let else_jump = self.chunk.emit_jump(Op::JumpIfFalse, self.line);
196 self.chunk.emit(Op::Pop, self.line);
197 self.compile_node(true_expr)?;
198 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
199 self.chunk.patch_jump(else_jump);
200 self.chunk.emit(Op::Pop, self.line);
201 self.compile_node(false_expr)?;
202 self.chunk.patch_jump(end_jump);
203 }
204 Node::FunctionCall { name, args, .. } => {
205 self.compile_function_call(name, args)?;
206 }
207 Node::MethodCall {
208 object,
209 method,
210 args,
211 } => {
212 self.compile_method_call(object, method, args)?;
213 }
214 Node::OptionalMethodCall {
215 object,
216 method,
217 args,
218 } => {
219 self.compile_node(object)?;
220 for arg in args {
221 self.compile_node(arg)?;
222 }
223 let name_idx = self.chunk.add_constant(Constant::String(method.clone()));
224 self.chunk
225 .emit_method_call_opt(name_idx, args.len() as u8, self.line);
226 }
227 Node::PropertyAccess { object, property } => {
228 self.compile_property_access(object, property)?;
229 }
230 Node::OptionalPropertyAccess { object, property } => {
231 self.compile_node(object)?;
232 let idx = self.chunk.add_constant(Constant::String(property.clone()));
233 self.chunk.emit_u16(Op::GetPropertyOpt, idx, self.line);
234 }
235 Node::SubscriptAccess { object, index } => {
236 self.compile_node(object)?;
237 self.compile_node(index)?;
238 self.chunk.emit(Op::Subscript, self.line);
239 }
240 Node::OptionalSubscriptAccess { object, index } => {
241 self.compile_node(object)?;
242 self.compile_node(index)?;
243 self.chunk.emit(Op::SubscriptOpt, self.line);
244 }
245 Node::SliceAccess { object, start, end } => {
246 self.compile_node(object)?;
247 if let Some(s) = start {
248 self.compile_node(s)?;
249 } else {
250 self.chunk.emit(Op::Nil, self.line);
251 }
252 if let Some(e) = end {
253 self.compile_node(e)?;
254 } else {
255 self.chunk.emit(Op::Nil, self.line);
256 }
257 self.chunk.emit(Op::Slice, self.line);
258 }
259 Node::IfElse {
260 condition,
261 then_body,
262 else_body,
263 } => {
264 self.compile_if_else(condition, then_body, else_body)?;
265 }
266 Node::WhileLoop { condition, body } => {
267 self.compile_while_loop(condition, body)?;
268 }
269 Node::ForIn {
270 pattern,
271 iterable,
272 body,
273 } => {
274 self.compile_for_in(pattern, iterable, body)?;
275 }
276 Node::ReturnStmt { value } => {
277 self.compile_return_stmt(value)?;
278 }
279 Node::BreakStmt => {
280 self.compile_break_stmt()?;
281 }
282 Node::ContinueStmt => {
283 self.compile_continue_stmt()?;
284 }
285 Node::ListLiteral(elements) => {
286 self.compile_list_literal(elements)?;
287 }
288 Node::DictLiteral(entries) => {
289 self.compile_dict_literal(entries)?;
290 }
291 Node::InterpolatedString(segments) => {
292 self.compile_interpolated_string(segments)?;
293 }
294 Node::FnDecl {
295 name,
296 params,
297 body,
298 is_stream,
299 ..
300 } => {
301 self.compile_fn_decl(name, params, body, *is_stream)?;
302 }
303 Node::ToolDecl {
304 name,
305 description,
306 params,
307 return_type,
308 body,
309 ..
310 } => {
311 self.compile_tool_decl(name, description, params, return_type, body)?;
312 }
313 Node::SkillDecl { name, fields, .. } => {
314 self.compile_skill_decl(name, fields)?;
315 }
316 Node::EvalPackDecl {
317 binding_name,
318 pack_id,
319 fields,
320 body,
321 summarize,
322 ..
323 } => {
324 self.compile_eval_pack_decl(binding_name, pack_id, fields, body, summarize, true)?;
325 }
326 Node::Closure { params, body, .. } => {
327 self.compile_closure(params, body)?;
328 }
329 Node::ThrowStmt { value } => {
330 self.compile_throw_stmt(value)?;
331 }
332 Node::MatchExpr { value, arms } => {
333 self.compile_match_expr(value, arms)?;
334 }
335 Node::RangeExpr {
336 start,
337 end,
338 inclusive,
339 } => {
340 let name_idx = self
341 .chunk
342 .add_constant(Constant::String("__range__".to_string()));
343 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
344 self.compile_node(start)?;
345 self.compile_node(end)?;
346 if *inclusive {
347 self.chunk.emit(Op::True, self.line);
348 } else {
349 self.chunk.emit(Op::False, self.line);
350 }
351 self.chunk.emit_u8(Op::Call, 3, self.line);
352 }
353 Node::GuardStmt {
354 condition,
355 else_body,
356 } => {
357 self.compile_guard_stmt(condition, else_body)?;
358 }
359 Node::RequireStmt { condition, message } => {
360 self.compile_node(condition)?;
361 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
362 self.chunk.emit(Op::Pop, self.line);
363 if let Some(message) = message {
364 self.compile_node(message)?;
365 } else {
366 let idx = self
367 .chunk
368 .add_constant(Constant::String("require condition failed".to_string()));
369 self.chunk.emit_u16(Op::Constant, idx, self.line);
370 }
371 self.chunk.emit(Op::Throw, self.line);
372 self.chunk.patch_jump(ok_jump);
373 self.chunk.emit(Op::Pop, self.line);
374 }
375 Node::Block(stmts) => {
376 self.compile_scoped_block(stmts)?;
377 }
378 Node::DeadlineBlock { duration, body } => {
379 self.compile_node(duration)?;
380 self.chunk.emit(Op::DeadlineSetup, self.line);
381 self.compile_scoped_block(body)?;
382 self.chunk.emit(Op::DeadlineEnd, self.line);
383 }
384 Node::MutexBlock { body } => {
385 self.begin_scope();
386 let key_idx = self
387 .chunk
388 .add_constant(Constant::String("__default__".to_string()));
389 self.chunk.emit_u16(Op::SyncMutexEnter, key_idx, self.line);
390 for sn in body {
391 self.compile_node(sn)?;
392 if Self::produces_value(&sn.node) {
393 self.chunk.emit(Op::Pop, self.line);
394 }
395 }
396 self.chunk.emit(Op::Nil, self.line);
397 self.end_scope();
398 }
399 Node::DeferStmt { body } => {
400 self.finally_bodies
402 .push(FinallyEntry::Finally(body.clone()));
403 self.chunk.emit(Op::Nil, self.line);
404 }
405 Node::YieldExpr { value } => {
406 if let Some(val) = value {
407 self.compile_node(val)?;
408 } else {
409 self.chunk.emit(Op::Nil, self.line);
410 }
411 self.chunk.emit(Op::Yield, self.line);
412 }
413 Node::EmitExpr { value } => {
414 self.compile_node(value)?;
415 self.chunk.emit(Op::Yield, self.line);
416 }
417 Node::EnumConstruct {
418 enum_name,
419 variant,
420 args,
421 } => {
422 self.compile_enum_construct(enum_name, variant, args)?;
423 }
424 Node::StructConstruct {
425 struct_name,
426 fields,
427 } => {
428 self.compile_struct_construct(struct_name, fields)?;
429 }
430 Node::ImportDecl { path, .. } => {
431 let idx = self.chunk.add_constant(Constant::String(path.clone()));
432 self.chunk.emit_u16(Op::Import, idx, self.line);
433 }
434 Node::SelectiveImport { names, path, .. } => {
435 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
436 let names_str = names.join(",");
437 let names_idx = self.chunk.add_constant(Constant::String(names_str));
438 self.chunk
439 .emit_u16(Op::SelectiveImport, path_idx, self.line);
440 let hi = (names_idx >> 8) as u8;
441 let lo = names_idx as u8;
442 self.chunk.code.push(hi);
443 self.chunk.code.push(lo);
444 self.chunk.lines.push(self.line);
445 self.chunk.columns.push(self.column);
446 self.chunk.lines.push(self.line);
447 self.chunk.columns.push(self.column);
448 }
449 Node::TryOperator { operand } => {
450 self.compile_node(operand)?;
451 self.chunk.emit(Op::TryUnwrap, self.line);
452 }
453 Node::TryStar { operand } => {
469 self.compile_try_star(operand)?;
470 }
471 Node::ImplBlock { type_name, methods } => {
472 self.compile_impl_block(type_name, methods)?;
473 }
474 Node::StructDecl { name, fields, .. } => {
475 self.compile_struct_decl(name, fields)?;
476 }
477 Node::Pipeline { .. }
479 | Node::OverrideDecl { .. }
480 | Node::TypeDecl { .. }
481 | Node::EnumDecl { .. }
482 | Node::InterfaceDecl { .. } => {
483 self.chunk.emit(Op::Nil, self.line);
484 }
485 Node::TryCatch {
486 body,
487 error_var,
488 error_type,
489 catch_body,
490 finally_body,
491 } => {
492 self.compile_try_catch(body, error_var, error_type, catch_body, finally_body)?;
493 }
494 Node::TryExpr { body } => {
495 self.compile_try_expr(body)?;
496 }
497 Node::Retry { count, body } => {
498 self.compile_retry(count, body)?;
499 }
500 Node::CostRoute { options, body } => {
501 self.compile_cost_route(options, body)?;
502 }
503 Node::Parallel {
504 mode,
505 expr,
506 variable,
507 body,
508 options,
509 } => {
510 self.compile_parallel(mode, expr, variable, body, options)?;
511 }
512 Node::SpawnExpr { body } => {
513 self.compile_spawn_expr(body)?;
514 }
515 Node::HitlExpr { kind, args } => {
516 self.compile_hitl_expr(*kind, args)?;
517 }
518 Node::SelectExpr {
519 cases,
520 timeout,
521 default_body,
522 } => {
523 self.compile_select_expr(cases, timeout, default_body)?;
524 }
525 Node::Spread(_) => {
526 return Err(CompileError {
527 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
528 line: self.line,
529 });
530 }
531 Node::AttributedDecl { attributes, inner } => {
532 self.compile_attributed_decl(attributes, inner)?;
533 }
534 Node::OrPattern(_) => {
535 return Err(CompileError {
536 message: "or-pattern (|) can only appear as a match arm pattern".into(),
537 line: self.line,
538 });
539 }
540 }
541 Ok(())
542 }
543}