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 type_params,
297 params,
298 body,
299 is_stream,
300 ..
301 } => {
302 self.compile_fn_decl(name, type_params, params, body, *is_stream)?;
303 }
304 Node::ToolDecl {
305 name,
306 description,
307 params,
308 return_type,
309 body,
310 ..
311 } => {
312 self.compile_tool_decl(name, description, params, return_type, body)?;
313 }
314 Node::SkillDecl { name, fields, .. } => {
315 self.compile_skill_decl(name, fields)?;
316 }
317 Node::EvalPackDecl {
318 binding_name,
319 pack_id,
320 fields,
321 body,
322 summarize,
323 ..
324 } => {
325 self.compile_eval_pack_decl(binding_name, pack_id, fields, body, summarize, true)?;
326 }
327 Node::Closure { params, body, .. } => {
328 self.compile_closure(params, body)?;
329 }
330 Node::ThrowStmt { value } => {
331 self.compile_throw_stmt(value)?;
332 }
333 Node::MatchExpr { value, arms } => {
334 self.compile_match_expr(value, arms)?;
335 }
336 Node::RangeExpr {
337 start,
338 end,
339 inclusive,
340 } => {
341 let name_idx = self
342 .chunk
343 .add_constant(Constant::String("__range__".to_string()));
344 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
345 self.compile_node(start)?;
346 self.compile_node(end)?;
347 if *inclusive {
348 self.chunk.emit(Op::True, self.line);
349 } else {
350 self.chunk.emit(Op::False, self.line);
351 }
352 self.chunk.emit_u8(Op::Call, 3, self.line);
353 }
354 Node::GuardStmt {
355 condition,
356 else_body,
357 } => {
358 self.compile_guard_stmt(condition, else_body)?;
359 }
360 Node::RequireStmt { condition, message } => {
361 self.compile_node(condition)?;
362 let ok_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
363 self.chunk.emit(Op::Pop, self.line);
364 if let Some(message) = message {
365 self.compile_node(message)?;
366 } else {
367 let idx = self
368 .chunk
369 .add_constant(Constant::String("require condition failed".to_string()));
370 self.chunk.emit_u16(Op::Constant, idx, self.line);
371 }
372 self.chunk.emit(Op::Throw, self.line);
373 self.chunk.patch_jump(ok_jump);
374 self.chunk.emit(Op::Pop, self.line);
375 }
376 Node::Block(stmts) => {
377 self.compile_scoped_block(stmts)?;
378 }
379 Node::DeadlineBlock { duration, body } => {
380 self.compile_node(duration)?;
381 self.chunk.emit(Op::DeadlineSetup, self.line);
382 self.compile_scoped_block(body)?;
383 self.chunk.emit(Op::DeadlineEnd, self.line);
384 }
385 Node::MutexBlock { body } => {
386 self.begin_scope();
387 let key_idx = self
388 .chunk
389 .add_constant(Constant::String("__default__".to_string()));
390 self.chunk.emit_u16(Op::SyncMutexEnter, key_idx, self.line);
391 for sn in body {
392 self.compile_node(sn)?;
393 if Self::produces_value(&sn.node) {
394 self.chunk.emit(Op::Pop, self.line);
395 }
396 }
397 self.chunk.emit(Op::Nil, self.line);
398 self.end_scope();
399 }
400 Node::DeferStmt { body } => {
401 self.finally_bodies
403 .push(FinallyEntry::Finally(body.clone()));
404 self.chunk.emit(Op::Nil, self.line);
405 }
406 Node::YieldExpr { value } => {
407 if let Some(val) = value {
408 self.compile_node(val)?;
409 } else {
410 self.chunk.emit(Op::Nil, self.line);
411 }
412 self.chunk.emit(Op::Yield, self.line);
413 }
414 Node::EmitExpr { value } => {
415 self.compile_node(value)?;
416 self.chunk.emit(Op::Yield, self.line);
417 }
418 Node::EnumConstruct {
419 enum_name,
420 variant,
421 args,
422 } => {
423 self.compile_enum_construct(enum_name, variant, args)?;
424 }
425 Node::StructConstruct {
426 struct_name,
427 fields,
428 } => {
429 self.compile_struct_construct(struct_name, fields)?;
430 }
431 Node::ImportDecl { path, .. } => {
432 let idx = self.chunk.add_constant(Constant::String(path.clone()));
433 self.chunk.emit_u16(Op::Import, idx, self.line);
434 }
435 Node::SelectiveImport { names, path, .. } => {
436 let path_idx = self.chunk.add_constant(Constant::String(path.clone()));
437 let names_str = names.join(",");
438 let names_idx = self.chunk.add_constant(Constant::String(names_str));
439 self.chunk
440 .emit_u16(Op::SelectiveImport, path_idx, self.line);
441 let hi = (names_idx >> 8) as u8;
442 let lo = names_idx as u8;
443 self.chunk.code.push(hi);
444 self.chunk.code.push(lo);
445 self.chunk.lines.push(self.line);
446 self.chunk.columns.push(self.column);
447 self.chunk.lines.push(self.line);
448 self.chunk.columns.push(self.column);
449 }
450 Node::TryOperator { operand } => {
451 self.compile_node(operand)?;
452 self.chunk.emit(Op::TryUnwrap, self.line);
453 }
454 Node::TryStar { operand } => {
470 self.compile_try_star(operand)?;
471 }
472 Node::ImplBlock { type_name, methods } => {
473 self.compile_impl_block(type_name, methods)?;
474 }
475 Node::StructDecl { name, fields, .. } => {
476 self.compile_struct_decl(name, fields)?;
477 }
478 Node::Pipeline { .. }
480 | Node::OverrideDecl { .. }
481 | Node::TypeDecl { .. }
482 | Node::EnumDecl { .. }
483 | Node::InterfaceDecl { .. } => {
484 self.chunk.emit(Op::Nil, self.line);
485 }
486 Node::TryCatch {
487 body,
488 error_var,
489 error_type,
490 catch_body,
491 finally_body,
492 } => {
493 self.compile_try_catch(body, error_var, error_type, catch_body, finally_body)?;
494 }
495 Node::TryExpr { body } => {
496 self.compile_try_expr(body)?;
497 }
498 Node::Retry { count, body } => {
499 self.compile_retry(count, body)?;
500 }
501 Node::CostRoute { options, body } => {
502 self.compile_cost_route(options, body)?;
503 }
504 Node::Parallel {
505 mode,
506 expr,
507 variable,
508 body,
509 options,
510 } => {
511 self.compile_parallel(mode, expr, variable, body, options)?;
512 }
513 Node::SpawnExpr { body } => {
514 self.compile_spawn_expr(body)?;
515 }
516 Node::HitlExpr { kind, args } => {
517 self.compile_hitl_expr(*kind, args)?;
518 }
519 Node::SelectExpr {
520 cases,
521 timeout,
522 default_body,
523 } => {
524 self.compile_select_expr(cases, timeout, default_body)?;
525 }
526 Node::Spread(_) => {
527 return Err(CompileError {
528 message: "spread (...) can only be used inside list literals, dict literals, or function call arguments".into(),
529 line: self.line,
530 });
531 }
532 Node::AttributedDecl { attributes, inner } => {
533 self.compile_attributed_decl(attributes, inner)?;
534 }
535 Node::OrPattern(_) => {
536 return Err(CompileError {
537 message: "or-pattern (|) can only appear as a match arm pattern".into(),
538 line: self.line,
539 });
540 }
541 }
542 Ok(())
543 }
544}