1use std::sync::Arc;
5use tl_ast::*;
6use tl_errors::{RuntimeError, Span, TlError};
7
8use crate::chunk::*;
9use crate::opcode::*;
10
11#[derive(Debug, Clone)]
13enum FoldedConst {
14 Int(i64),
15 Float(f64),
16 Bool(bool),
17 String(String),
18}
19
20fn compile_err(msg: String) -> TlError {
22 TlError::Runtime(RuntimeError {
23 message: msg,
24 span: None,
25 stack_trace: vec![],
26 })
27}
28
29#[derive(Debug, Clone)]
31struct Local {
32 name: String,
33 depth: u32,
34 register: u8,
35 is_captured: bool,
36}
37
38#[derive(Debug, Clone)]
40struct CompilerUpvalue {
41 is_local: bool,
42 index: u8,
43}
44
45#[derive(Debug, Clone)]
47struct LoopCtx {
48 break_jumps: Vec<usize>,
50 loop_start: usize,
52}
53
54struct CompilerState {
56 proto: Prototype,
57 locals: Vec<Local>,
58 upvalues: Vec<CompilerUpvalue>,
59 scope_depth: u32,
60 next_register: u8,
61 loop_stack: Vec<LoopCtx>,
62 has_yield: bool,
63 current_line: u32,
65 dead_code: bool,
67}
68
69impl CompilerState {
70 fn new(name: String) -> Self {
71 CompilerState {
72 proto: Prototype::new(name),
73 locals: Vec::new(),
74 upvalues: Vec::new(),
75 scope_depth: 0,
76 next_register: 0,
77 loop_stack: Vec::new(),
78 has_yield: false,
79 current_line: 0,
80 dead_code: false,
81 }
82 }
83
84 fn alloc_register(&mut self) -> u8 {
85 if self.next_register == 255 {
86 panic!("Register overflow: function too complex (max 255 registers)");
87 }
88 let r = self.next_register;
89 self.next_register += 1;
90 if self.next_register > self.proto.num_registers {
91 self.proto.num_registers = self.next_register;
92 }
93 r
94 }
95
96 fn free_register(&mut self) {
97 if self.next_register > 0 {
98 self.next_register -= 1;
99 }
100 }
101
102 fn emit(&mut self, inst: u32) {
103 self.proto.code.push(inst);
104 self.proto.lines.push(self.current_line);
105 }
106
107 fn emit_abc(&mut self, op: Op, a: u8, b: u8, c: u8, _line: u32) {
108 self.emit(encode_abc(op, a, b, c));
109 }
110
111 fn emit_abx(&mut self, op: Op, a: u8, bx: u16, _line: u32) {
112 self.emit(encode_abx(op, a, bx));
113 }
114
115 fn add_constant(&mut self, c: Constant) -> u16 {
116 let idx = self.proto.constants.len();
117 if idx >= 65535 {
118 panic!("Constant pool overflow: too many constants (max 65535)");
119 }
120 self.proto.constants.push(c);
121 idx as u16
122 }
123
124 fn current_pos(&self) -> usize {
125 self.proto.code.len()
126 }
127
128 fn patch_jump(&mut self, inst_pos: usize) {
129 let target = self.current_pos();
130 let offset = (target as i32 - inst_pos as i32 - 1) as i16;
131 let old = self.proto.code[inst_pos];
132 let op = (old >> 24) as u8;
133 let a = ((old >> 16) & 0xFF) as u8;
134 self.proto.code[inst_pos] = encode_abx(
135 Op::try_from(op).expect("patching valid instruction"),
136 a,
137 offset as u16,
138 );
139 }
140}
141
142pub struct Compiler {
144 states: Vec<CompilerState>,
145 line_offsets: Vec<usize>,
147 current_line: u32,
149}
150
151impl Compiler {
152 fn current(&mut self) -> &mut CompilerState {
153 self.states.last_mut().unwrap()
154 }
155
156 fn build_line_offsets(source: &str) -> Vec<usize> {
158 let mut offsets = vec![0]; for (i, ch) in source.as_bytes().iter().enumerate() {
160 if *ch == b'\n' {
161 offsets.push(i + 1);
162 }
163 }
164 offsets
165 }
166
167 fn line_of(&self, byte_offset: usize) -> u32 {
169 match self.line_offsets.binary_search(&byte_offset) {
170 Ok(idx) => idx as u32 + 1,
171 Err(idx) => idx as u32, }
173 }
174
175 #[allow(dead_code)]
177 fn line(&self) -> u32 {
178 self.current_line
179 }
180
181 fn begin_scope(&mut self) {
182 self.current().scope_depth += 1;
183 }
184
185 fn end_scope(&mut self) {
186 let state = self.current();
187 state.scope_depth -= 1;
188 while let Some(local) = state.locals.last() {
190 if local.depth <= state.scope_depth {
191 break;
192 }
193 let reg = local.register;
194 let captured = local.is_captured;
195 state.locals.pop();
196 if captured {
197 }
200 if reg + 1 == state.next_register {
202 state.next_register = reg;
203 }
204 }
205 }
206
207 fn add_local(&mut self, name: String) -> u8 {
208 let state = self.current();
209 let reg = state.alloc_register();
210 let depth = state.scope_depth;
211 state.locals.push(Local {
212 name,
213 depth,
214 register: reg,
215 is_captured: false,
216 });
217 state.proto.num_locals = state.proto.num_locals.max(state.locals.len() as u8);
218 reg
219 }
220
221 fn resolve_local(&self, name: &str) -> Option<u8> {
222 let state = self.states.last().unwrap();
223 for local in state.locals.iter().rev() {
224 if local.name == name {
225 return Some(local.register);
226 }
227 }
228 None
229 }
230
231 fn resolve_upvalue(&mut self, name: &str) -> Option<u8> {
232 let n = self.states.len();
233 if n < 2 {
234 return None;
235 }
236 self.resolve_upvalue_recursive(n - 1, name)
237 }
238
239 fn resolve_upvalue_recursive(&mut self, state_idx: usize, name: &str) -> Option<u8> {
240 if state_idx == 0 {
241 return None;
242 }
243 let enclosing = &mut self.states[state_idx - 1];
245 for i in (0..enclosing.locals.len()).rev() {
246 if enclosing.locals[i].name == name {
247 enclosing.locals[i].is_captured = true;
248 let reg = enclosing.locals[i].register;
249 return Some(self.add_upvalue(state_idx, true, reg));
250 }
251 }
252 if let Some(uv_idx) = self.resolve_upvalue_recursive(state_idx - 1, name) {
254 return Some(self.add_upvalue(state_idx, false, uv_idx));
255 }
256 None
257 }
258
259 fn add_upvalue(&mut self, state_idx: usize, is_local: bool, index: u8) -> u8 {
260 let state = &mut self.states[state_idx];
261 for (i, uv) in state.upvalues.iter().enumerate() {
263 if uv.is_local == is_local && uv.index == index {
264 return i as u8;
265 }
266 }
267 let idx = state.upvalues.len() as u8;
268 state.upvalues.push(CompilerUpvalue { is_local, index });
269 idx
270 }
271
272 fn compile_stmt(&mut self, stmt: &Stmt) -> Result<(), TlError> {
273 if self.current().dead_code {
275 return Ok(());
276 }
277
278 let line = self.line_of(stmt.span.start);
280 self.current_line = line;
281 self.current().current_line = line;
282 match &stmt.kind {
283 StmtKind::Let { name, value, .. } => {
284 let reg = self.add_local(name.clone());
285 self.compile_expr(value, reg)?;
286 Ok(())
287 }
288 StmtKind::FnDecl {
289 name, params, body, ..
290 } => {
291 let reg = self.add_local(name.clone());
292 self.compile_function(name.clone(), params, body, false)?;
293 let _ = reg; Ok(())
298 }
299 StmtKind::Expr(expr) => {
300 let reg = self.current().alloc_register();
301 self.compile_expr(expr, reg)?;
302 self.current().free_register();
303 Ok(())
304 }
305 StmtKind::Return(expr) => {
306 let reg = self.current().alloc_register();
307 match expr {
308 Some(e) => self.compile_expr(e, reg)?,
309 None => self.current().emit_abx(Op::LoadNone, reg, 0, 0),
310 }
311 self.current().emit_abc(Op::Return, reg, 0, 0, 0);
312 self.current().free_register();
313 self.current().dead_code = true;
314 Ok(())
315 }
316 StmtKind::If {
317 condition,
318 then_body,
319 else_ifs,
320 else_body,
321 } => self.compile_if(condition, then_body, else_ifs, else_body),
322 StmtKind::While { condition, body } => self.compile_while(condition, body),
323 StmtKind::For { name, iter, body } => self.compile_for(name, iter, body),
324 StmtKind::ParallelFor { name, iter, body } => {
325 self.compile_for(name, iter, body)
327 }
328 StmtKind::Schema {
329 name,
330 fields,
331 version,
332 ..
333 } => self.compile_schema(name, fields, version),
334 StmtKind::Train {
335 name,
336 algorithm,
337 config,
338 } => self.compile_train(name, algorithm, config),
339 StmtKind::Pipeline {
340 name,
341 extract,
342 transform,
343 load,
344 schedule,
345 timeout,
346 retries,
347 on_failure,
348 on_success,
349 } => self.compile_pipeline(
350 name, extract, transform, load, schedule, timeout, retries, on_failure, on_success,
351 ),
352 StmtKind::StreamDecl {
353 name,
354 source,
355 transform: _,
356 sink: _,
357 window,
358 watermark,
359 } => self.compile_stream_decl(name, source, window, watermark),
360 StmtKind::SourceDecl {
361 name,
362 connector_type,
363 config,
364 } => self.compile_connector_decl(name, connector_type, config),
365 StmtKind::SinkDecl {
366 name,
367 connector_type,
368 config,
369 } => self.compile_connector_decl(name, connector_type, config),
370 StmtKind::StructDecl { name, fields, .. } => {
371 let reg = self.add_local(name.clone());
372 let field_names: Vec<Arc<str>> =
373 fields.iter().map(|f| Arc::from(f.name.as_str())).collect();
374 let name_idx = self
375 .current()
376 .add_constant(Constant::String(Arc::from(name.as_str())));
377 let fields_idx = self.current().add_constant(Constant::AstExprList(
379 field_names
380 .iter()
381 .map(|f| tl_ast::Expr::String(f.to_string()))
382 .collect(),
383 ));
384 self.current().emit_abc(
386 Op::NewStruct,
387 reg,
388 name_idx as u8,
389 (fields_idx as u8) | 0x80,
390 0,
391 );
392 Ok(())
393 }
394 StmtKind::EnumDecl { name, variants, .. } => {
395 let reg = self.add_local(name.clone());
396 let name_idx = self
397 .current()
398 .add_constant(Constant::String(Arc::from(name.as_str())));
399 let variant_info: Vec<tl_ast::Expr> = variants
401 .iter()
402 .map(|v| tl_ast::Expr::String(format!("{}:{}", v.name, v.fields.len())))
403 .collect();
404 let variants_idx = self
405 .current()
406 .add_constant(Constant::AstExprList(variant_info));
407 self.current().emit_abc(
409 Op::NewStruct,
410 reg,
411 name_idx as u8,
412 (variants_idx as u8) | 0x80,
413 0,
414 );
415 let global_idx = self.current().add_constant(Constant::String(Arc::from(
418 format!("__enum_{name}").as_str(),
419 )));
420 self.current().emit_abx(Op::SetGlobal, reg, global_idx, 0);
421 let name_g = self
423 .current()
424 .add_constant(Constant::String(Arc::from(name.as_str())));
425 self.current().emit_abx(Op::SetGlobal, reg, name_g, 0);
426 Ok(())
427 }
428 StmtKind::ImplBlock {
429 type_name, methods, ..
430 } => {
431 for method in methods {
433 if let StmtKind::FnDecl {
434 name: mname,
435 params,
436 body,
437 ..
438 } = &method.kind
439 {
440 let mangled = format!("{type_name}::{mname}");
441 let reg = self.add_local(mangled.clone());
443 self.compile_function(mangled.clone(), params, body, false)?;
444 let idx = self
446 .current()
447 .add_constant(Constant::String(Arc::from(mangled.as_str())));
448 self.current().emit_abx(Op::SetGlobal, reg, idx, 0);
449 }
450 }
451 Ok(())
452 }
453 StmtKind::TryCatch {
454 try_body,
455 catch_var,
456 catch_body,
457 finally_body,
458 } => {
459 let try_begin_pos = self.current().current_pos();
461 self.current().emit_abx(Op::TryBegin, 0, 0, 0); let saved_dead_code = self.current().dead_code;
465 self.begin_scope();
466 self.current().dead_code = false;
467 for stmt in try_body {
468 self.compile_stmt(stmt)?;
469 }
470 self.end_scope();
471
472 self.current().emit_abx(Op::TryEnd, 0, 0, 0);
474
475 if let Some(finally_stmts) = &finally_body {
477 self.begin_scope();
478 for stmt in finally_stmts {
479 self.compile_stmt(stmt)?;
480 }
481 self.end_scope();
482 }
483
484 let jump_over_pos = self.current().current_pos();
486 self.current().emit_abx(Op::Jump, 0, 0, 0);
487
488 self.current().patch_jump(try_begin_pos);
490
491 self.begin_scope();
493 self.current().dead_code = false;
494 let catch_reg = self.add_local(catch_var.clone());
495 self.current().emit_abx(Op::LoadNone, catch_reg, 0, 0);
498 for stmt in catch_body {
499 self.compile_stmt(stmt)?;
500 }
501 self.end_scope();
502
503 if let Some(finally_stmts) = &finally_body {
505 self.begin_scope();
506 for stmt in finally_stmts {
507 self.compile_stmt(stmt)?;
508 }
509 self.end_scope();
510 }
511
512 self.current().patch_jump(jump_over_pos);
514
515 self.current().dead_code = saved_dead_code;
517 Ok(())
518 }
519 StmtKind::Throw(expr) => {
520 let reg = self.current().alloc_register();
521 self.compile_expr(expr, reg)?;
522 self.current().emit_abc(Op::Throw, reg, 0, 0, 0);
523 self.current().free_register();
524 Ok(())
525 }
526 StmtKind::Import { path, alias } => {
527 let reg = self.current().alloc_register();
528 let path_idx = self
529 .current()
530 .add_constant(Constant::String(Arc::from(path.as_str())));
531 let alias_idx = if let Some(a) = alias {
532 self.current()
533 .add_constant(Constant::String(Arc::from(a.as_str())))
534 } else {
535 self.current().add_constant(Constant::String(Arc::from("")))
536 };
537 self.current().emit_abx(Op::Import, reg, path_idx, 0);
538 self.current().emit_abc(Op::Move, alias_idx as u8, 0, 0, 0);
540 self.current().free_register();
541 Ok(())
542 }
543 StmtKind::Test { .. } => {
544 Ok(())
546 }
547 StmtKind::Use { item, .. } => self.compile_use(item),
548 StmtKind::ModDecl { .. } => {
549 Ok(())
551 }
552 StmtKind::TraitDef { .. } => {
553 Ok(())
555 }
556 StmtKind::TraitImpl {
557 type_name, methods, ..
558 } => {
559 for method in methods {
561 if let StmtKind::FnDecl {
562 name: mname,
563 params,
564 body,
565 ..
566 } = &method.kind
567 {
568 let mangled = format!("{type_name}::{mname}");
569 let reg = self.add_local(mangled.clone());
570 self.compile_function(mangled.clone(), params, body, false)?;
571 let idx = self
572 .current()
573 .add_constant(Constant::String(Arc::from(mangled.as_str())));
574 self.current().emit_abx(Op::SetGlobal, reg, idx, 0);
575 }
576 }
577 Ok(())
578 }
579 StmtKind::Break => {
580 let state = self.current();
581 let pos = state.current_pos();
582 state.emit_abx(Op::Jump, 0, 0, 0);
583 if let Some(loop_ctx) = state.loop_stack.last_mut() {
584 loop_ctx.break_jumps.push(pos);
585 }
586 self.current().dead_code = true;
587 Ok(())
588 }
589 StmtKind::Continue => {
590 let state = self.current();
591 if let Some(loop_ctx) = state.loop_stack.last() {
592 let loop_start = loop_ctx.loop_start;
593 let current = state.current_pos();
594 let offset = (loop_start as i32 - current as i32 - 1) as i16;
595 state.emit_abx(Op::Jump, 0, offset as u16, 0);
596 }
597 self.current().dead_code = true;
598 Ok(())
599 }
600 StmtKind::LetDestructure { pattern, value, .. } => {
601 let val_reg = self.current().alloc_register();
602 self.compile_expr(value, val_reg)?;
603 self.compile_let_destructure(pattern, val_reg)?;
604 Ok(())
607 }
608 StmtKind::TypeAlias { .. } => {
609 Ok(())
611 }
612 StmtKind::Migrate {
613 schema_name,
614 from_version,
615 to_version,
616 operations,
617 } => self.compile_migrate(schema_name, *from_version, *to_version, operations),
618 StmtKind::Agent {
619 name,
620 model,
621 system_prompt,
622 tools,
623 max_turns,
624 temperature,
625 max_tokens,
626 base_url,
627 api_key,
628 output_format,
629 on_tool_call,
630 on_complete,
631 mcp_servers,
632 } => self.compile_agent(
633 name,
634 model,
635 system_prompt,
636 tools,
637 max_turns,
638 temperature,
639 max_tokens,
640 base_url,
641 api_key,
642 output_format,
643 on_tool_call,
644 on_complete,
645 mcp_servers,
646 ),
647 }
648 }
649
650 fn compile_let_destructure(&mut self, pattern: &Pattern, val_reg: u8) -> Result<(), TlError> {
652 match pattern {
653 Pattern::Binding(name) => {
654 let local = self.add_local(name.clone());
655 self.current().emit_abc(Op::Move, local, val_reg, 0, 0);
656 }
657 Pattern::Wildcard => {} Pattern::Struct { fields, .. } => {
659 for field in fields {
660 let fname_const = self
661 .current()
662 .add_constant(Constant::String(Arc::from(field.name.as_str())));
663 let bind_name = match &field.pattern {
664 Some(Pattern::Binding(n)) => n.clone(),
665 _ => field.name.clone(),
666 };
667 let local = self.add_local(bind_name);
668 self.current().emit_abc(
669 Op::ExtractNamedField,
670 local,
671 val_reg,
672 fname_const as u8,
673 0,
674 );
675 }
676 }
677 Pattern::List { elements, rest } => {
678 for (i, elem_pat) in elements.iter().enumerate() {
679 match elem_pat {
680 Pattern::Binding(name) => {
681 let local = self.add_local(name.clone());
682 self.current()
683 .emit_abc(Op::ExtractField, local, val_reg, i as u8, 0);
684 }
685 Pattern::Wildcard => {}
686 _ => {}
687 }
688 }
689 if let Some(rest_name) = rest {
690 let local = self.add_local(rest_name.clone());
691 self.current().emit_abc(
692 Op::ExtractField,
693 local,
694 val_reg,
695 (elements.len() as u8) | 0x80,
696 0,
697 );
698 }
699 }
700 Pattern::Enum { args, .. } => {
701 for (i, arg_pat) in args.iter().enumerate() {
703 if let Pattern::Binding(name) = arg_pat {
704 let local = self.add_local(name.clone());
705 self.current()
706 .emit_abc(Op::ExtractField, local, val_reg, i as u8, 0);
707 }
708 }
709 }
710 _ => {}
711 }
712 Ok(())
713 }
714
715 fn compile_function(
716 &mut self,
717 name: String,
718 params: &[Param],
719 body: &[Stmt],
720 is_closure_expr: bool,
721 ) -> Result<u8, TlError> {
722 let dest_reg = if is_closure_expr {
724 0 } else {
728 let state = self.states.last().unwrap();
730 state
731 .locals
732 .iter()
733 .rev()
734 .find(|l| l.name == name)
735 .map(|l| l.register)
736 .unwrap_or(0)
737 };
738
739 let mut fn_state = CompilerState::new(name);
741 fn_state.proto.arity = params.len() as u8;
742 fn_state.scope_depth = 1;
743
744 for param in params {
746 let reg = fn_state.alloc_register();
747 fn_state.locals.push(Local {
748 name: param.name.clone(),
749 depth: 1,
750 register: reg,
751 is_captured: false,
752 });
753 }
754 fn_state.proto.num_locals = params.len() as u8;
755
756 self.states.push(fn_state);
757
758 let mut last_expr_reg: Option<u8> = None;
761 for (i, stmt) in body.iter().enumerate() {
762 if self.current().dead_code {
764 break;
765 }
766 let is_last = i == body.len() - 1;
767 let line = self.line_of(stmt.span.start);
769 self.current_line = line;
770 self.current().current_line = line;
771 match &stmt.kind {
772 StmtKind::Expr(expr) if is_last => {
773 let reg = self.current().alloc_register();
775 self.compile_expr(expr, reg)?;
776 last_expr_reg = Some(reg);
777 }
779 StmtKind::If {
780 condition,
781 then_body,
782 else_ifs,
783 else_body,
784 } if is_last && else_body.is_some() => {
785 let dest = self.current().alloc_register();
787 self.compile_if_as_expr(condition, then_body, else_ifs, else_body, dest)?;
788 last_expr_reg = Some(dest);
789 }
790 _ => {
791 self.compile_stmt(stmt)?;
792 }
793 }
794 }
795
796 let needs_return = {
798 let state = self.states.last().unwrap();
799 if state.proto.code.is_empty() {
800 true
801 } else {
802 let last = *state.proto.code.last().unwrap();
803 decode_op(last) != Op::Return
804 }
805 };
806 if needs_return {
807 if let Some(reg) = last_expr_reg {
808 self.current().emit_abc(Op::Return, reg, 0, 0, 0);
810 } else {
811 let state = self.current();
812 let reg = state.alloc_register();
813 state.emit_abx(Op::LoadNone, reg, 0, 0);
814 state.emit_abc(Op::Return, reg, 0, 0, 0);
815 state.free_register();
816 }
817 }
818
819 let fn_state = self.states.pop().unwrap();
821 let mut proto = fn_state.proto;
822 proto.is_generator = fn_state.has_yield;
823 proto.upvalue_defs = fn_state
824 .upvalues
825 .iter()
826 .map(|uv| UpvalueDef {
827 is_local: uv.is_local,
828 index: uv.index,
829 })
830 .collect();
831
832 let proto_arc = Arc::new(proto);
834 let const_idx = self.current().add_constant(Constant::Prototype(proto_arc));
835
836 self.current().emit_abx(Op::Closure, dest_reg, const_idx, 0);
838
839 Ok(dest_reg)
840 }
841
842 fn compile_closure_expr(
843 &mut self,
844 params: &[Param],
845 body: &ClosureBody,
846 dest: u8,
847 ) -> Result<(), TlError> {
848 let body_stmts = match body {
850 ClosureBody::Expr(e) => {
851 vec![Stmt {
853 kind: StmtKind::Return(Some(e.as_ref().clone())),
854 span: Span::new(0, 0),
855 doc_comment: None,
856 }]
857 }
858 ClosureBody::Block { stmts, expr } => {
859 let mut all = stmts.clone();
861 if let Some(e) = expr {
862 all.push(Stmt {
863 kind: StmtKind::Return(Some(e.as_ref().clone())),
864 span: Span::new(0, 0),
865 doc_comment: None,
866 });
867 }
868 all
869 }
870 };
871
872 let mut fn_state = CompilerState::new("<closure>".to_string());
874 fn_state.proto.arity = params.len() as u8;
875 fn_state.scope_depth = 1;
876
877 for param in params {
878 let reg = fn_state.alloc_register();
879 fn_state.locals.push(Local {
880 name: param.name.clone(),
881 depth: 1,
882 register: reg,
883 is_captured: false,
884 });
885 }
886 fn_state.proto.num_locals = params.len() as u8;
887
888 self.states.push(fn_state);
889
890 for stmt in &body_stmts {
891 self.compile_stmt(stmt)?;
892 }
893
894 let needs_return = {
896 let state = self.states.last().unwrap();
897 if state.proto.code.is_empty() {
898 true
899 } else {
900 let last = *state.proto.code.last().unwrap();
901 decode_op(last) != Op::Return
902 }
903 };
904 if needs_return {
905 let state = self.current();
906 let reg = state.alloc_register();
907 state.emit_abx(Op::LoadNone, reg, 0, 0);
908 state.emit_abc(Op::Return, reg, 0, 0, 0);
909 state.free_register();
910 }
911
912 let fn_state = self.states.pop().unwrap();
913 let mut proto = fn_state.proto;
914 proto.upvalue_defs = fn_state
915 .upvalues
916 .iter()
917 .map(|uv| UpvalueDef {
918 is_local: uv.is_local,
919 index: uv.index,
920 })
921 .collect();
922
923 let proto_arc = Arc::new(proto);
924 let const_idx = self.current().add_constant(Constant::Prototype(proto_arc));
925 self.current().emit_abx(Op::Closure, dest, const_idx, 0);
926
927 Ok(())
928 }
929
930 fn compile_if(
931 &mut self,
932 condition: &Expr,
933 then_body: &[Stmt],
934 else_ifs: &[(Expr, Vec<Stmt>)],
935 else_body: &Option<Vec<Stmt>>,
936 ) -> Result<(), TlError> {
937 let saved_dead_code = self.current().dead_code;
938 let cond_reg = self.current().alloc_register();
939 self.compile_expr(condition, cond_reg)?;
940
941 let jump_false_pos = self.current().current_pos();
943 self.current().emit_abx(Op::JumpIfFalse, cond_reg, 0, 0);
944 self.current().free_register(); self.begin_scope();
948 self.current().dead_code = false;
949 for stmt in then_body {
950 self.compile_stmt(stmt)?;
951 }
952 let then_dead = self.current().dead_code;
953 self.end_scope();
954
955 let mut end_jumps = Vec::new();
957 let jump_end_pos = self.current().current_pos();
958 self.current().emit_abx(Op::Jump, 0, 0, 0);
959 end_jumps.push(jump_end_pos);
960
961 self.current().patch_jump(jump_false_pos);
963
964 let mut all_branches_dead = then_dead;
966
967 for (ei_cond, ei_body) in else_ifs {
969 let cond_reg = self.current().alloc_register();
970 self.compile_expr(ei_cond, cond_reg)?;
971 let jf_pos = self.current().current_pos();
972 self.current().emit_abx(Op::JumpIfFalse, cond_reg, 0, 0);
973 self.current().free_register();
974
975 self.begin_scope();
976 self.current().dead_code = false;
977 for stmt in ei_body {
978 self.compile_stmt(stmt)?;
979 }
980 all_branches_dead = all_branches_dead && self.current().dead_code;
981 self.end_scope();
982
983 let je_pos = self.current().current_pos();
984 self.current().emit_abx(Op::Jump, 0, 0, 0);
985 end_jumps.push(je_pos);
986
987 self.current().patch_jump(jf_pos);
988 }
989
990 if let Some(body) = else_body {
992 self.begin_scope();
993 self.current().dead_code = false;
994 for stmt in body {
995 self.compile_stmt(stmt)?;
996 }
997 all_branches_dead = all_branches_dead && self.current().dead_code;
998 self.end_scope();
999 } else {
1000 all_branches_dead = false;
1002 }
1003
1004 for pos in end_jumps {
1006 self.current().patch_jump(pos);
1007 }
1008
1009 self.current().dead_code = saved_dead_code || all_branches_dead;
1011
1012 Ok(())
1013 }
1014
1015 fn compile_if_as_expr(
1017 &mut self,
1018 condition: &Expr,
1019 then_body: &[Stmt],
1020 else_ifs: &[(Expr, Vec<Stmt>)],
1021 else_body: &Option<Vec<Stmt>>,
1022 dest: u8,
1023 ) -> Result<(), TlError> {
1024 let cond_reg = self.current().alloc_register();
1025 self.compile_expr(condition, cond_reg)?;
1026 let jump_false_pos = self.current().current_pos();
1027 self.current().emit_abx(Op::JumpIfFalse, cond_reg, 0, 0);
1028 self.current().free_register(); self.begin_scope();
1032 self.compile_body_with_result(then_body, dest)?;
1033 self.end_scope();
1034
1035 let mut end_jumps = Vec::new();
1036 let jump_end_pos = self.current().current_pos();
1037 self.current().emit_abx(Op::Jump, 0, 0, 0);
1038 end_jumps.push(jump_end_pos);
1039
1040 self.current().patch_jump(jump_false_pos);
1041
1042 for (ei_cond, ei_body) in else_ifs {
1044 let cond_reg = self.current().alloc_register();
1045 self.compile_expr(ei_cond, cond_reg)?;
1046 let jf_pos = self.current().current_pos();
1047 self.current().emit_abx(Op::JumpIfFalse, cond_reg, 0, 0);
1048 self.current().free_register();
1049
1050 self.begin_scope();
1051 self.compile_body_with_result(ei_body, dest)?;
1052 self.end_scope();
1053
1054 let je_pos = self.current().current_pos();
1055 self.current().emit_abx(Op::Jump, 0, 0, 0);
1056 end_jumps.push(je_pos);
1057
1058 self.current().patch_jump(jf_pos);
1059 }
1060
1061 if let Some(body) = else_body {
1063 self.begin_scope();
1064 self.compile_body_with_result(body, dest)?;
1065 self.end_scope();
1066 }
1067
1068 for pos in end_jumps {
1069 self.current().patch_jump(pos);
1070 }
1071
1072 Ok(())
1073 }
1074
1075 fn compile_body_with_result(&mut self, body: &[Stmt], dest: u8) -> Result<(), TlError> {
1077 for (i, stmt) in body.iter().enumerate() {
1078 let is_last = i == body.len() - 1;
1079 let line = self.line_of(stmt.span.start);
1080 self.current_line = line;
1081 self.current().current_line = line;
1082 match &stmt.kind {
1083 StmtKind::Expr(expr) if is_last => {
1084 self.compile_expr(expr, dest)?;
1085 }
1086 StmtKind::If {
1087 condition,
1088 then_body,
1089 else_ifs,
1090 else_body,
1091 } if is_last && else_body.is_some() => {
1092 self.compile_if_as_expr(condition, then_body, else_ifs, else_body, dest)?;
1093 }
1094 _ => {
1095 self.compile_stmt(stmt)?;
1096 }
1097 }
1098 }
1099 Ok(())
1100 }
1101
1102 fn compile_while(&mut self, condition: &Expr, body: &[Stmt]) -> Result<(), TlError> {
1103 let loop_start = self.current().current_pos();
1104
1105 self.current().loop_stack.push(LoopCtx {
1106 break_jumps: Vec::new(),
1107 loop_start,
1108 });
1109
1110 let cond_reg = self.current().alloc_register();
1111 self.compile_expr(condition, cond_reg)?;
1112 let exit_jump = self.current().current_pos();
1113 self.current().emit_abx(Op::JumpIfFalse, cond_reg, 0, 0);
1114 self.current().free_register();
1115
1116 self.begin_scope();
1117 let saved_dead_code = self.current().dead_code;
1119 self.current().dead_code = false;
1120 for stmt in body {
1121 self.compile_stmt(stmt)?;
1122 }
1123 self.end_scope();
1124
1125 let current = self.current().current_pos();
1127 let offset = (loop_start as i32 - current as i32 - 1) as i16;
1128 self.current().emit_abx(Op::Jump, 0, offset as u16, 0);
1129
1130 self.current().patch_jump(exit_jump);
1132
1133 let loop_ctx = self.current().loop_stack.pop().unwrap();
1135 for pos in loop_ctx.break_jumps {
1136 self.current().patch_jump(pos);
1137 }
1138
1139 self.current().dead_code = saved_dead_code;
1141
1142 Ok(())
1143 }
1144
1145 fn compile_for(&mut self, name: &str, iter: &Expr, body: &[Stmt]) -> Result<(), TlError> {
1146 let list_reg = self.current().alloc_register();
1148 self.compile_expr(iter, list_reg)?;
1149
1150 let iter_reg = self.current().alloc_register();
1152 let zero_const = self.current().add_constant(Constant::Int(0));
1153 self.current()
1154 .emit_abx(Op::LoadConst, iter_reg, zero_const, 0);
1155
1156 let loop_start = self.current().current_pos();
1157 self.current().loop_stack.push(LoopCtx {
1158 break_jumps: Vec::new(),
1159 loop_start,
1160 });
1161
1162 self.begin_scope();
1164 let val_reg = self.add_local(name.to_string());
1165 self.current()
1166 .emit_abc(Op::ForIter, iter_reg, list_reg, val_reg, 0);
1167 let exit_jump = self.current().current_pos();
1169 self.current().emit_abx(Op::Jump, 0, 0, 0); let saved_dead_code = self.current().dead_code;
1173 self.current().dead_code = false;
1174 for stmt in body {
1175 self.compile_stmt(stmt)?;
1176 }
1177
1178 self.end_scope();
1179
1180 let current = self.current().current_pos();
1182 let offset = (loop_start as i32 - current as i32 - 1) as i16;
1183 self.current().emit_abx(Op::Jump, 0, offset as u16, 0);
1184
1185 self.current().patch_jump(exit_jump);
1187
1188 let loop_ctx = self.current().loop_stack.pop().unwrap();
1190 for pos in loop_ctx.break_jumps {
1191 self.current().patch_jump(pos);
1192 }
1193
1194 self.current().free_register(); self.current().free_register(); self.current().dead_code = saved_dead_code;
1200
1201 Ok(())
1202 }
1203
1204 fn compile_schema(
1205 &mut self,
1206 name: &str,
1207 fields: &[SchemaField],
1208 version: &Option<i64>,
1209 ) -> Result<(), TlError> {
1210 let reg = self.current().alloc_register();
1213
1214 let ver_str = version.map_or(String::new(), |v| format!(":v{}", v));
1216 let schema_str = format!(
1217 "__schema__:{}{}:{}",
1218 name,
1219 ver_str,
1220 fields
1221 .iter()
1222 .map(|f| format!("{}:{:?}", f.name, f.type_ann))
1223 .collect::<Vec<_>>()
1224 .join(",")
1225 );
1226 let const_idx = self
1227 .current()
1228 .add_constant(Constant::String(Arc::from(schema_str.as_str())));
1229 self.current().emit_abx(Op::LoadConst, reg, const_idx, 0);
1230
1231 let name_idx = self
1232 .current()
1233 .add_constant(Constant::String(Arc::from(name)));
1234 self.current().emit_abx(Op::SetGlobal, reg, name_idx, 0);
1235 self.current().free_register();
1236
1237 let local_reg = self.add_local(name.to_string());
1239 self.current()
1240 .emit_abx(Op::LoadConst, local_reg, const_idx, 0);
1241
1242 Ok(())
1243 }
1244
1245 fn compile_migrate(
1246 &mut self,
1247 schema_name: &str,
1248 from_version: i64,
1249 to_version: i64,
1250 operations: &[MigrateOp],
1251 ) -> Result<(), TlError> {
1252 let mut ops_str = Vec::new();
1254 for op in operations {
1255 let s = match op {
1256 MigrateOp::AddColumn {
1257 name,
1258 type_ann,
1259 default,
1260 } => {
1261 let def_str = if let Some(d) = default {
1262 format!(",default:{d:?}")
1263 } else {
1264 String::new()
1265 };
1266 format!("add:{name}:{type_ann:?}{def_str}")
1267 }
1268 MigrateOp::DropColumn { name } => format!("drop:{name}"),
1269 MigrateOp::RenameColumn { from, to } => format!("rename:{from}:{to}"),
1270 MigrateOp::AlterType { column, new_type } => format!("alter:{column}:{new_type:?}"),
1271 MigrateOp::AddConstraint { column, constraint } => {
1272 format!("add_constraint:{column}:{constraint}")
1273 }
1274 MigrateOp::DropConstraint { column, constraint } => {
1275 format!("drop_constraint:{column}:{constraint}")
1276 }
1277 };
1278 ops_str.push(s);
1279 }
1280 let migrate_str = format!(
1281 "__migrate__:{}:{}:{}:{}",
1282 schema_name,
1283 from_version,
1284 to_version,
1285 ops_str.join(";")
1286 );
1287 let reg = self.current().alloc_register();
1288 let const_idx = self
1289 .current()
1290 .add_constant(Constant::String(Arc::from(migrate_str.as_str())));
1291 self.current().emit_abx(Op::LoadConst, reg, const_idx, 0);
1292 let name_key = format!("__migrate_{schema_name}_{from_version}_{to_version}");
1294 let name_idx = self
1295 .current()
1296 .add_constant(Constant::String(Arc::from(name_key.as_str())));
1297 self.current().emit_abx(Op::SetGlobal, reg, name_idx, 0);
1298 self.current().free_register();
1299 Ok(())
1300 }
1301
1302 fn compile_train(
1303 &mut self,
1304 name: &str,
1305 algorithm: &str,
1306 config: &[(String, Expr)],
1307 ) -> Result<(), TlError> {
1308 let dest = self.add_local(name.to_string());
1309
1310 let algo_idx = self
1312 .current()
1313 .add_constant(Constant::String(Arc::from(algorithm)));
1314
1315 for (_key, val) in config {
1318 if let Expr::Ident(ident_name) = val
1319 && let Some(reg) = self.resolve_local(ident_name)
1320 {
1321 let name_idx = self
1322 .current()
1323 .add_constant(Constant::String(Arc::from(ident_name.as_str())));
1324 self.current().emit_abx(Op::SetGlobal, reg, name_idx, 0);
1325 }
1326 }
1327
1328 let config_exprs: Vec<tl_ast::Expr> = config
1330 .iter()
1331 .map(|(k, v)| {
1332 tl_ast::Expr::NamedArg {
1334 name: k.clone(),
1335 value: Box::new(v.clone()),
1336 }
1337 })
1338 .collect();
1339 let config_idx = self
1340 .current()
1341 .add_constant(Constant::AstExprList(config_exprs));
1342
1343 self.current()
1345 .emit_abc(Op::Train, dest, algo_idx as u8, config_idx as u8, 0);
1346
1347 let name_idx = self
1349 .current()
1350 .add_constant(Constant::String(Arc::from(name)));
1351 self.current().emit_abx(Op::SetGlobal, dest, name_idx, 0);
1352
1353 Ok(())
1354 }
1355
1356 #[allow(clippy::too_many_arguments)]
1357 fn compile_pipeline(
1358 &mut self,
1359 name: &str,
1360 extract: &[Stmt],
1361 transform: &[Stmt],
1362 load: &[Stmt],
1363 schedule: &Option<String>,
1364 timeout: &Option<String>,
1365 retries: &Option<i64>,
1366 _on_failure: &Option<Vec<Stmt>>,
1367 on_success: &Option<Vec<Stmt>>,
1368 ) -> Result<(), TlError> {
1369 let dest = self.add_local(name.to_string());
1370
1371 let name_const = self
1373 .current()
1374 .add_constant(Constant::String(Arc::from(name)));
1375
1376 let mut all_stmts = Vec::new();
1379 all_stmts.extend(extract.to_vec());
1380 all_stmts.extend(transform.to_vec());
1381 all_stmts.extend(load.to_vec());
1382
1383 let mut config_exprs: Vec<tl_ast::Expr> = Vec::new();
1385
1386 if let Some(s) = schedule {
1387 config_exprs.push(tl_ast::Expr::NamedArg {
1388 name: "schedule".to_string(),
1389 value: Box::new(tl_ast::Expr::String(s.clone())),
1390 });
1391 }
1392 if let Some(t) = timeout {
1393 config_exprs.push(tl_ast::Expr::NamedArg {
1394 name: "timeout".to_string(),
1395 value: Box::new(tl_ast::Expr::String(t.clone())),
1396 });
1397 }
1398 if let Some(r) = retries {
1399 config_exprs.push(tl_ast::Expr::NamedArg {
1400 name: "retries".to_string(),
1401 value: Box::new(tl_ast::Expr::Int(*r)),
1402 });
1403 }
1404 let config_idx = self
1405 .current()
1406 .add_constant(Constant::AstExprList(config_exprs));
1407
1408 for stmt in extract {
1410 self.compile_stmt(stmt)?;
1411 }
1412 for stmt in transform {
1413 self.compile_stmt(stmt)?;
1414 }
1415 for stmt in load {
1416 self.compile_stmt(stmt)?;
1417 }
1418
1419 if let Some(success_block) = on_success {
1421 for stmt in success_block {
1422 self.compile_stmt(stmt)?;
1423 }
1424 }
1425
1426 self.current().emit_abc(
1428 Op::PipelineExec,
1429 dest,
1430 name_const as u8,
1431 config_idx as u8,
1432 0,
1433 );
1434
1435 let gname = self
1437 .current()
1438 .add_constant(Constant::String(Arc::from(name)));
1439 self.current().emit_abx(Op::SetGlobal, dest, gname, 0);
1440
1441 Ok(())
1442 }
1443
1444 #[allow(clippy::too_many_arguments)]
1445 fn compile_agent(
1446 &mut self,
1447 name: &str,
1448 model: &str,
1449 system_prompt: &Option<String>,
1450 tools: &[(String, tl_ast::Expr)],
1451 max_turns: &Option<i64>,
1452 temperature: &Option<f64>,
1453 max_tokens: &Option<i64>,
1454 base_url: &Option<String>,
1455 api_key: &Option<String>,
1456 output_format: &Option<String>,
1457 on_tool_call: &Option<Vec<tl_ast::Stmt>>,
1458 on_complete: &Option<Vec<tl_ast::Stmt>>,
1459 mcp_servers: &[tl_ast::Expr],
1460 ) -> Result<(), TlError> {
1461 let dest = self.add_local(name.to_string());
1462
1463 let name_const = self
1465 .current()
1466 .add_constant(Constant::String(Arc::from(name)));
1467
1468 let mut config_exprs: Vec<tl_ast::Expr> = Vec::new();
1470
1471 config_exprs.push(tl_ast::Expr::NamedArg {
1473 name: "model".to_string(),
1474 value: Box::new(tl_ast::Expr::String(model.to_string())),
1475 });
1476
1477 if let Some(sys) = system_prompt {
1478 config_exprs.push(tl_ast::Expr::NamedArg {
1479 name: "system".to_string(),
1480 value: Box::new(tl_ast::Expr::String(sys.clone())),
1481 });
1482 }
1483
1484 if let Some(n) = max_turns {
1485 config_exprs.push(tl_ast::Expr::NamedArg {
1486 name: "max_turns".to_string(),
1487 value: Box::new(tl_ast::Expr::Int(*n)),
1488 });
1489 }
1490
1491 if let Some(t) = temperature {
1492 config_exprs.push(tl_ast::Expr::NamedArg {
1493 name: "temperature".to_string(),
1494 value: Box::new(tl_ast::Expr::Float(*t)),
1495 });
1496 }
1497
1498 if let Some(n) = max_tokens {
1499 config_exprs.push(tl_ast::Expr::NamedArg {
1500 name: "max_tokens".to_string(),
1501 value: Box::new(tl_ast::Expr::Int(*n)),
1502 });
1503 }
1504
1505 if let Some(url) = base_url {
1506 config_exprs.push(tl_ast::Expr::NamedArg {
1507 name: "base_url".to_string(),
1508 value: Box::new(tl_ast::Expr::String(url.clone())),
1509 });
1510 }
1511
1512 if let Some(key) = api_key {
1513 config_exprs.push(tl_ast::Expr::NamedArg {
1514 name: "api_key".to_string(),
1515 value: Box::new(tl_ast::Expr::String(key.clone())),
1516 });
1517 }
1518
1519 if let Some(fmt) = output_format {
1520 config_exprs.push(tl_ast::Expr::NamedArg {
1521 name: "output_format".to_string(),
1522 value: Box::new(tl_ast::Expr::String(fmt.clone())),
1523 });
1524 }
1525
1526 for (tool_name, tool_expr) in tools {
1528 config_exprs.push(tl_ast::Expr::NamedArg {
1529 name: format!("tool:{tool_name}"),
1530 value: Box::new(tool_expr.clone()),
1531 });
1532 }
1533
1534 for (i, expr) in mcp_servers.iter().enumerate() {
1536 config_exprs.push(tl_ast::Expr::NamedArg {
1537 name: format!("mcp_server:{i}"),
1538 value: Box::new(expr.clone()),
1539 });
1540 }
1541
1542 if on_tool_call.is_some() {
1544 config_exprs.push(tl_ast::Expr::NamedArg {
1545 name: "on_tool_call".to_string(),
1546 value: Box::new(tl_ast::Expr::Bool(true)),
1547 });
1548 }
1549 if on_complete.is_some() {
1550 config_exprs.push(tl_ast::Expr::NamedArg {
1551 name: "on_complete".to_string(),
1552 value: Box::new(tl_ast::Expr::Bool(true)),
1553 });
1554 }
1555
1556 let config_idx = self
1557 .current()
1558 .add_constant(Constant::AstExprList(config_exprs));
1559
1560 self.current()
1562 .emit_abc(Op::AgentExec, dest, name_const as u8, config_idx as u8, 0);
1563
1564 let gname = self
1566 .current()
1567 .add_constant(Constant::String(Arc::from(name)));
1568 self.current().emit_abx(Op::SetGlobal, dest, gname, 0);
1569
1570 if let Some(stmts) = on_tool_call {
1572 let hook_name = format!("__agent_{name}_on_tool_call__");
1573 let params = vec![
1574 tl_ast::Param {
1575 name: "tool_name".into(),
1576 type_ann: None,
1577 },
1578 tl_ast::Param {
1579 name: "tool_args".into(),
1580 type_ann: None,
1581 },
1582 tl_ast::Param {
1583 name: "tool_result".into(),
1584 type_ann: None,
1585 },
1586 ];
1587 self.compile_agent_hook(&hook_name, ¶ms, stmts)?;
1588 }
1589 if let Some(stmts) = on_complete {
1590 let hook_name = format!("__agent_{name}_on_complete__");
1591 let params = vec![tl_ast::Param {
1592 name: "result".into(),
1593 type_ann: None,
1594 }];
1595 self.compile_agent_hook(&hook_name, ¶ms, stmts)?;
1596 }
1597
1598 Ok(())
1599 }
1600
1601 fn compile_agent_hook(
1602 &mut self,
1603 hook_name: &str,
1604 params: &[tl_ast::Param],
1605 body: &[tl_ast::Stmt],
1606 ) -> Result<(), TlError> {
1607 let local = self.add_local(hook_name.to_string());
1609
1610 let dest = self.compile_function(hook_name.to_string(), params, body, false)?;
1612
1613 let gname = self
1615 .current()
1616 .add_constant(Constant::String(Arc::from(hook_name)));
1617 self.current().emit_abx(Op::SetGlobal, local, gname, 0);
1618 let _ = dest;
1619
1620 Ok(())
1621 }
1622
1623 fn compile_stream_decl(
1624 &mut self,
1625 name: &str,
1626 source: &Expr,
1627 window: &Option<tl_ast::WindowSpec>,
1628 watermark: &Option<String>,
1629 ) -> Result<(), TlError> {
1630 let dest = self.add_local(name.to_string());
1631
1632 let src_reg = self.current().alloc_register();
1634 self.compile_expr(source, src_reg)?;
1635
1636 let mut config_exprs: Vec<tl_ast::Expr> = Vec::new();
1638 config_exprs.push(tl_ast::Expr::NamedArg {
1639 name: "name".to_string(),
1640 value: Box::new(tl_ast::Expr::String(name.to_string())),
1641 });
1642
1643 if let Some(w) = window {
1644 let window_str = match w {
1645 tl_ast::WindowSpec::Tumbling(d) => format!("tumbling:{d}"),
1646 tl_ast::WindowSpec::Sliding(w, s) => format!("sliding:{w}:{s}"),
1647 tl_ast::WindowSpec::Session(g) => format!("session:{g}"),
1648 };
1649 config_exprs.push(tl_ast::Expr::NamedArg {
1650 name: "window".to_string(),
1651 value: Box::new(tl_ast::Expr::String(window_str)),
1652 });
1653 }
1654 if let Some(wm) = watermark {
1655 config_exprs.push(tl_ast::Expr::NamedArg {
1656 name: "watermark".to_string(),
1657 value: Box::new(tl_ast::Expr::String(wm.clone())),
1658 });
1659 }
1660 let config_idx = self
1661 .current()
1662 .add_constant(Constant::AstExprList(config_exprs));
1663
1664 self.current()
1666 .emit_abc(Op::StreamExec, dest, config_idx as u8, src_reg, 0);
1667 self.current().free_register(); let gname = self
1671 .current()
1672 .add_constant(Constant::String(Arc::from(name)));
1673 self.current().emit_abx(Op::SetGlobal, dest, gname, 0);
1674
1675 Ok(())
1676 }
1677
1678 fn compile_connector_decl(
1679 &mut self,
1680 name: &str,
1681 connector_type: &str,
1682 config: &[(String, Expr)],
1683 ) -> Result<(), TlError> {
1684 let dest = self.add_local(name.to_string());
1685
1686 let type_idx = self
1688 .current()
1689 .add_constant(Constant::String(Arc::from(connector_type)));
1690
1691 let config_exprs: Vec<tl_ast::Expr> = config
1693 .iter()
1694 .map(|(k, v)| tl_ast::Expr::NamedArg {
1695 name: k.clone(),
1696 value: Box::new(v.clone()),
1697 })
1698 .collect();
1699 let config_idx = self
1700 .current()
1701 .add_constant(Constant::AstExprList(config_exprs));
1702
1703 self.current()
1705 .emit_abc(Op::ConnectorDecl, dest, type_idx as u8, config_idx as u8, 0);
1706
1707 let gname = self
1709 .current()
1710 .add_constant(Constant::String(Arc::from(name)));
1711 self.current().emit_abx(Op::SetGlobal, dest, gname, 0);
1712
1713 Ok(())
1714 }
1715
1716 fn try_fold_const(expr: &Expr) -> Option<FoldedConst> {
1721 match expr {
1722 Expr::Int(n) => Some(FoldedConst::Int(*n)),
1723 Expr::Float(f) => Some(FoldedConst::Float(*f)),
1724 Expr::Bool(b) => Some(FoldedConst::Bool(*b)),
1725 Expr::String(s) if !s.contains('{') && !s.contains('\\') => {
1728 Some(FoldedConst::String(s.clone()))
1729 }
1730 Expr::String(_) => None,
1731
1732 Expr::UnaryOp {
1733 op: UnaryOp::Neg,
1734 expr,
1735 } => match Self::try_fold_const(expr)? {
1736 FoldedConst::Int(n) => Some(FoldedConst::Int(-n)),
1737 FoldedConst::Float(f) => Some(FoldedConst::Float(-f)),
1738 _ => None,
1739 },
1740 Expr::UnaryOp {
1741 op: UnaryOp::Not,
1742 expr,
1743 } => match Self::try_fold_const(expr)? {
1744 FoldedConst::Bool(b) => Some(FoldedConst::Bool(!b)),
1745 _ => None,
1746 },
1747
1748 Expr::BinOp { left, op, right } => {
1749 let l = Self::try_fold_const(left)?;
1750 let r = Self::try_fold_const(right)?;
1751 Self::fold_binop(&l, op, &r)
1752 }
1753
1754 _ => None,
1756 }
1757 }
1758
1759 fn fold_binop(left: &FoldedConst, op: &BinOp, right: &FoldedConst) -> Option<FoldedConst> {
1761 match (left, right) {
1762 (FoldedConst::Int(a), FoldedConst::Int(b)) => match op {
1764 BinOp::Add => Some(FoldedConst::Int(a.checked_add(*b)?)),
1765 BinOp::Sub => Some(FoldedConst::Int(a.checked_sub(*b)?)),
1766 BinOp::Mul => Some(FoldedConst::Int(a.checked_mul(*b)?)),
1767 BinOp::Div => {
1768 if *b == 0 {
1769 return None;
1770 } Some(FoldedConst::Int(a / b))
1772 }
1773 BinOp::Mod => {
1774 if *b == 0 {
1775 return None;
1776 }
1777 Some(FoldedConst::Int(a % b))
1778 }
1779 BinOp::Pow => {
1780 if *b >= 0 {
1781 Some(FoldedConst::Int(a.pow(*b as u32)))
1782 } else {
1783 Some(FoldedConst::Float((*a as f64).powf(*b as f64)))
1784 }
1785 }
1786 BinOp::Eq => Some(FoldedConst::Bool(a == b)),
1787 BinOp::Neq => Some(FoldedConst::Bool(a != b)),
1788 BinOp::Lt => Some(FoldedConst::Bool(a < b)),
1789 BinOp::Gt => Some(FoldedConst::Bool(a > b)),
1790 BinOp::Lte => Some(FoldedConst::Bool(a <= b)),
1791 BinOp::Gte => Some(FoldedConst::Bool(a >= b)),
1792 BinOp::And | BinOp::Or => None,
1793 },
1794
1795 (FoldedConst::Float(a), FoldedConst::Float(b)) => match op {
1797 BinOp::Add => Some(FoldedConst::Float(a + b)),
1798 BinOp::Sub => Some(FoldedConst::Float(a - b)),
1799 BinOp::Mul => Some(FoldedConst::Float(a * b)),
1800 BinOp::Div => {
1801 if *b == 0.0 {
1802 return None;
1803 }
1804 Some(FoldedConst::Float(a / b))
1805 }
1806 BinOp::Mod => {
1807 if *b == 0.0 {
1808 return None;
1809 }
1810 Some(FoldedConst::Float(a % b))
1811 }
1812 BinOp::Pow => Some(FoldedConst::Float(a.powf(*b))),
1813 BinOp::Eq => Some(FoldedConst::Bool(a == b)),
1814 BinOp::Neq => Some(FoldedConst::Bool(a != b)),
1815 BinOp::Lt => Some(FoldedConst::Bool(a < b)),
1816 BinOp::Gt => Some(FoldedConst::Bool(a > b)),
1817 BinOp::Lte => Some(FoldedConst::Bool(a <= b)),
1818 BinOp::Gte => Some(FoldedConst::Bool(a >= b)),
1819 BinOp::And | BinOp::Or => None,
1820 },
1821
1822 (FoldedConst::Int(a), FoldedConst::Float(b)) => {
1824 let a = *a as f64;
1825 Self::fold_binop(&FoldedConst::Float(a), op, &FoldedConst::Float(*b))
1826 }
1827 (FoldedConst::Float(a), FoldedConst::Int(b)) => {
1828 let b = *b as f64;
1829 Self::fold_binop(&FoldedConst::Float(*a), op, &FoldedConst::Float(b))
1830 }
1831
1832 (FoldedConst::String(a), FoldedConst::String(b)) if matches!(op, BinOp::Add) => {
1834 Some(FoldedConst::String(format!("{a}{b}")))
1835 }
1836 (FoldedConst::String(a), FoldedConst::String(b)) => match op {
1837 BinOp::Eq => Some(FoldedConst::Bool(a == b)),
1838 BinOp::Neq => Some(FoldedConst::Bool(a != b)),
1839 _ => None,
1840 },
1841
1842 (FoldedConst::Bool(a), FoldedConst::Bool(b)) => match op {
1844 BinOp::And => Some(FoldedConst::Bool(*a && *b)),
1845 BinOp::Or => Some(FoldedConst::Bool(*a || *b)),
1846 BinOp::Eq => Some(FoldedConst::Bool(a == b)),
1847 BinOp::Neq => Some(FoldedConst::Bool(a != b)),
1848 _ => None,
1849 },
1850
1851 _ => None,
1852 }
1853 }
1854
1855 fn compile_expr(&mut self, expr: &Expr, dest: u8) -> Result<(), TlError> {
1858 if let Some(folded) = Self::try_fold_const(expr) {
1860 match folded {
1861 FoldedConst::Int(n) => {
1862 let idx = self.current().add_constant(Constant::Int(n));
1863 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
1864 }
1865 FoldedConst::Float(f) => {
1866 let idx = self.current().add_constant(Constant::Float(f));
1867 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
1868 }
1869 FoldedConst::Bool(true) => {
1870 self.current().emit_abx(Op::LoadTrue, dest, 0, 0);
1871 }
1872 FoldedConst::Bool(false) => {
1873 self.current().emit_abx(Op::LoadFalse, dest, 0, 0);
1874 }
1875 FoldedConst::String(s) => {
1876 let idx = self
1877 .current()
1878 .add_constant(Constant::String(Arc::from(s.as_str())));
1879 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
1880 }
1881 }
1882 return Ok(());
1883 }
1884
1885 match expr {
1886 Expr::Int(n) => {
1887 let idx = self.current().add_constant(Constant::Int(*n));
1888 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
1889 }
1890 Expr::Float(f) => {
1891 let idx = self.current().add_constant(Constant::Float(*f));
1892 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
1893 }
1894 Expr::Decimal(s) => {
1895 let idx = self
1896 .current()
1897 .add_constant(Constant::Decimal(Arc::from(s.as_str())));
1898 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
1899 }
1900 Expr::String(s) => {
1901 self.compile_string_interpolation(s, dest)?;
1902 }
1903 Expr::Bool(true) => {
1904 self.current().emit_abx(Op::LoadTrue, dest, 0, 0);
1905 }
1906 Expr::Bool(false) => {
1907 self.current().emit_abx(Op::LoadFalse, dest, 0, 0);
1908 }
1909 Expr::None => {
1910 self.current().emit_abx(Op::LoadNone, dest, 0, 0);
1911 }
1912 Expr::Ident(name) => {
1913 if let Some(reg) = self.resolve_local(name) {
1915 if reg != dest {
1916 self.current().emit_abc(Op::Move, dest, reg, 0, 0);
1917 }
1918 } else if let Some(uv) = self.resolve_upvalue(name) {
1919 self.current().emit_abc(Op::GetUpvalue, dest, uv, 0, 0);
1920 } else {
1921 let idx = self
1922 .current()
1923 .add_constant(Constant::String(Arc::from(name.as_str())));
1924 self.current().emit_abx(Op::GetGlobal, dest, idx, 0);
1925 }
1926 }
1927 Expr::BinOp { left, op, right } => {
1928 match op {
1930 BinOp::And => {
1931 self.compile_expr(left, dest)?;
1932 let jump_pos = self.current().current_pos();
1933 self.current().emit_abx(Op::JumpIfFalse, dest, 0, 0);
1934 self.compile_expr(right, dest)?;
1935 self.current().patch_jump(jump_pos);
1936 return Ok(());
1937 }
1938 BinOp::Or => {
1939 self.compile_expr(left, dest)?;
1940 let jump_pos = self.current().current_pos();
1941 self.current().emit_abx(Op::JumpIfTrue, dest, 0, 0);
1942 self.compile_expr(right, dest)?;
1943 self.current().patch_jump(jump_pos);
1944 return Ok(());
1945 }
1946 _ => {}
1947 }
1948
1949 let left_reg = self.current().alloc_register();
1950 let right_reg = self.current().alloc_register();
1951 self.compile_expr(left, left_reg)?;
1952 self.compile_expr(right, right_reg)?;
1953
1954 let vm_op = match op {
1955 BinOp::Add => Op::Add,
1956 BinOp::Sub => Op::Sub,
1957 BinOp::Mul => Op::Mul,
1958 BinOp::Div => Op::Div,
1959 BinOp::Mod => Op::Mod,
1960 BinOp::Pow => Op::Pow,
1961 BinOp::Eq => Op::Eq,
1962 BinOp::Neq => Op::Neq,
1963 BinOp::Lt => Op::Lt,
1964 BinOp::Gt => Op::Gt,
1965 BinOp::Lte => Op::Lte,
1966 BinOp::Gte => Op::Gte,
1967 BinOp::And | BinOp::Or => unreachable!(), };
1969 self.current().emit_abc(vm_op, dest, left_reg, right_reg, 0);
1970 self.current().free_register(); self.current().free_register(); }
1973 Expr::UnaryOp { op, expr } => {
1974 let src = self.current().alloc_register();
1975 self.compile_expr(expr, src)?;
1976 match op {
1977 UnaryOp::Neg => self.current().emit_abc(Op::Neg, dest, src, 0, 0),
1978 UnaryOp::Not => self.current().emit_abc(Op::Not, dest, src, 0, 0),
1979 UnaryOp::Ref => {
1980 self.current().emit_abc(Op::MakeRef, dest, src, 0, 0);
1981 }
1982 }
1983 self.current().free_register();
1984 }
1985 Expr::Await(expr) => {
1986 let src = self.current().alloc_register();
1987 self.compile_expr(expr, src)?;
1988 self.current().emit_abc(Op::Await, dest, src, 0, 0);
1989 self.current().free_register();
1990 }
1991 Expr::Try(inner) => {
1992 let src = self.current().alloc_register();
1993 self.compile_expr(inner, src)?;
1994 self.current().emit_abc(Op::TryPropagate, dest, src, 0, 0);
1995 self.current().free_register();
1996 }
1997 Expr::Yield(opt_expr) => {
1998 self.current().has_yield = true;
1999 match opt_expr {
2000 Some(expr) => {
2001 self.compile_expr(expr, dest)?;
2002 }
2003 None => {
2004 self.current().emit_abx(Op::LoadNone, dest, 0, 0);
2005 }
2006 }
2007 self.current().emit_abc(Op::Yield, dest, 0, 0, 0);
2008 }
2009 Expr::Call { function, args } => {
2010 self.compile_call(function, args, dest)?;
2011 }
2012 Expr::Pipe { left, right } => {
2013 self.compile_pipe(left, right, dest)?;
2014 }
2015 Expr::List(elements) => {
2016 if elements.is_empty() {
2017 let start = self.current().next_register;
2018 self.current().emit_abc(Op::NewList, dest, start, 0, 0);
2019 } else {
2020 let start = self.current().next_register;
2021 for el in elements {
2022 let r = self.current().alloc_register();
2023 self.compile_expr(el, r)?;
2024 }
2025 self.current()
2026 .emit_abc(Op::NewList, dest, start, elements.len() as u8, 0);
2027 for _ in elements {
2028 self.current().free_register();
2029 }
2030 }
2031 }
2032 Expr::Map(pairs) => {
2033 if pairs.is_empty() {
2034 let start = self.current().next_register;
2035 self.current().emit_abc(Op::NewMap, dest, start, 0, 0);
2036 } else {
2037 let start = self.current().next_register;
2038 for (key, val) in pairs {
2039 let kr = self.current().alloc_register();
2040 self.compile_expr(key, kr)?;
2041 let vr = self.current().alloc_register();
2042 self.compile_expr(val, vr)?;
2043 }
2044 self.current()
2045 .emit_abc(Op::NewMap, dest, start, pairs.len() as u8, 0);
2046 for _ in 0..pairs.len() * 2 {
2047 self.current().free_register();
2048 }
2049 }
2050 }
2051 Expr::Index { object, index } => {
2052 let obj_reg = self.current().alloc_register();
2053 let idx_reg = self.current().alloc_register();
2054 self.compile_expr(object, obj_reg)?;
2055 self.compile_expr(index, idx_reg)?;
2056 self.current()
2057 .emit_abc(Op::GetIndex, dest, obj_reg, idx_reg, 0);
2058 self.current().free_register();
2059 self.current().free_register();
2060 }
2061 Expr::Block { stmts, expr } => {
2062 self.begin_scope();
2063 for stmt in stmts {
2064 self.compile_stmt(stmt)?;
2065 }
2066 if let Some(e) = expr {
2067 self.compile_expr(e, dest)?;
2068 } else {
2069 self.current().emit_abx(Op::LoadNone, dest, 0, 0);
2070 }
2071 self.end_scope();
2072 }
2073 Expr::Case { arms } => {
2074 self.compile_case(arms, dest)?;
2075 }
2076 Expr::Match { subject, arms } => {
2077 self.compile_match(subject, arms, dest)?;
2078 }
2079 Expr::Closure { params, body, .. } => {
2080 self.compile_closure_expr(params, body, dest)?;
2081 }
2082 Expr::Range { start, end } => {
2083 let start_reg = self.current().alloc_register();
2085 let end_reg = self.current().alloc_register();
2086 self.compile_expr(start, start_reg)?;
2087 self.compile_expr(end, end_reg)?;
2088 self.current()
2090 .emit_abx(Op::CallBuiltin, dest, BuiltinId::Range as u16, 0);
2091 self.current().emit_abc(Op::Move, 2, start_reg, 0, 0);
2092 self.current().free_register();
2093 self.current().free_register();
2094 }
2095 Expr::NullCoalesce { expr, default } => {
2096 self.compile_expr(expr, dest)?;
2097 let skip_jump = self.current().current_pos();
2098 self.current().emit_abx(Op::JumpIfTrue, dest, 0, 0);
2100 self.compile_expr(default, dest)?;
2101 self.current().patch_jump(skip_jump);
2102 }
2103 Expr::Assign { target, value } => {
2104 if let Expr::Ident(name) = target.as_ref() {
2105 self.compile_expr(value, dest)?;
2106 if let Some(reg) = self.resolve_local(name) {
2107 if reg != dest {
2108 self.current().emit_abc(Op::Move, reg, dest, 0, 0);
2109 }
2110 } else if let Some(uv) = self.resolve_upvalue(name) {
2111 self.current().emit_abc(Op::SetUpvalue, dest, uv, 0, 0);
2112 } else {
2113 let idx = self
2114 .current()
2115 .add_constant(Constant::String(Arc::from(name.as_str())));
2116 self.current().emit_abx(Op::SetGlobal, dest, idx, 0);
2117 }
2118 } else if let Expr::Index { object, index } = target.as_ref() {
2119 let obj_reg = self.current().alloc_register();
2121 self.compile_expr(object, obj_reg)?;
2122 let idx_reg = self.current().alloc_register();
2123 self.compile_expr(index, idx_reg)?;
2124 self.compile_expr(value, dest)?;
2125 self.current()
2127 .emit_abc(Op::SetIndex, dest, obj_reg, idx_reg, 0);
2128 if let Expr::Ident(name) = object.as_ref() {
2130 if let Some(reg) = self.resolve_local(name) {
2131 self.current().emit_abc(Op::Move, reg, obj_reg, 0, 0);
2132 } else {
2133 let c_idx = self
2134 .current()
2135 .add_constant(Constant::String(Arc::from(name.as_str())));
2136 self.current().emit_abx(Op::SetGlobal, obj_reg, c_idx, 0);
2137 }
2138 }
2139 self.current().free_register(); self.current().free_register(); } else if let Expr::Member { object, field } = target.as_ref() {
2142 if let Expr::Ident(name) = object.as_ref() {
2144 let obj_reg = self.current().alloc_register();
2145 self.compile_expr(object, obj_reg)?;
2146 self.compile_expr(value, dest)?;
2148 let key_reg = self.current().alloc_register();
2150 let key_idx = self
2151 .current()
2152 .add_constant(Constant::String(Arc::from(field.as_str())));
2153 self.current().emit_abx(Op::LoadConst, key_reg, key_idx, 0);
2154 self.current()
2155 .emit_abc(Op::SetIndex, dest, obj_reg, key_reg, 0);
2156 if let Some(reg) = self.resolve_local(name) {
2158 self.current().emit_abc(Op::Move, reg, obj_reg, 0, 0);
2159 } else {
2160 let c_idx = self
2161 .current()
2162 .add_constant(Constant::String(Arc::from(name.as_str())));
2163 self.current().emit_abx(Op::SetGlobal, obj_reg, c_idx, 0);
2164 }
2165 self.current().free_register(); self.current().free_register(); } else {
2168 return Err(compile_err("Invalid assignment target".to_string()));
2169 }
2170 } else {
2171 return Err(compile_err("Invalid assignment target".to_string()));
2172 }
2173 }
2174 Expr::Member { object, field } => {
2175 let obj_reg = self.current().alloc_register();
2176 self.compile_expr(object, obj_reg)?;
2177 let field_idx = self
2178 .current()
2179 .add_constant(Constant::String(Arc::from(field.as_str())));
2180 self.current()
2181 .emit_abc(Op::GetMember, dest, obj_reg, field_idx as u8, 0);
2182 self.current().free_register();
2183 }
2184 Expr::NamedArg { .. } => {
2185 self.current().emit_abx(Op::LoadNone, dest, 0, 0);
2187 }
2188 Expr::StructInit { name, fields } => {
2189 let name_idx = self
2191 .current()
2192 .add_constant(Constant::String(Arc::from(name.as_str())));
2193 let start = self.current().next_register;
2194 for (fname, fexpr) in fields {
2195 let fname_reg = self.current().alloc_register();
2196 let fname_idx = self
2197 .current()
2198 .add_constant(Constant::String(Arc::from(fname.as_str())));
2199 self.current()
2200 .emit_abx(Op::LoadConst, fname_reg, fname_idx, 0);
2201 let fval_reg = self.current().alloc_register();
2202 self.compile_expr(fexpr, fval_reg)?;
2203 }
2204 self.current()
2205 .emit_abc(Op::NewStruct, dest, name_idx as u8, fields.len() as u8, 0);
2206 self.current().emit_abc(Op::Move, start, 0, 0, 0);
2208 for _ in 0..fields.len() * 2 {
2209 self.current().free_register();
2210 }
2211 }
2212 Expr::EnumVariant {
2213 enum_name,
2214 variant,
2215 args,
2216 } => {
2217 let full_name = format!("{enum_name}::{variant}");
2218 let name_idx = self
2219 .current()
2220 .add_constant(Constant::String(Arc::from(full_name.as_str())));
2221 let start = self.current().next_register;
2222 for arg in args {
2223 let r = self.current().alloc_register();
2224 self.compile_expr(arg, r)?;
2225 }
2226 self.current()
2227 .emit_abc(Op::NewEnum, dest, name_idx as u8, start, 0);
2228 self.current().emit_abc(Op::Move, args.len() as u8, 0, 0, 0);
2230 for _ in args {
2231 self.current().free_register();
2232 }
2233 }
2234 }
2235 Ok(())
2236 }
2237
2238 fn compile_call(&mut self, function: &Expr, args: &[Expr], dest: u8) -> Result<(), TlError> {
2239 if let Expr::Ident(name) = function
2241 && let Some(builtin_id) = BuiltinId::from_name(name)
2242 {
2243 return self.compile_builtin_call(builtin_id, args, dest);
2244 }
2245
2246 if let Expr::Member { object, field } = function {
2248 let obj_reg = self.current().alloc_register();
2249 self.compile_expr(object, obj_reg)?;
2250 let method_idx = self
2251 .current()
2252 .add_constant(Constant::String(Arc::from(field.as_str())));
2253 let args_start = self.current().next_register;
2254 for arg in args {
2255 let r = self.current().alloc_register();
2256 self.compile_expr(arg, r)?;
2257 }
2258 self.current()
2259 .emit_abc(Op::MethodCall, dest, obj_reg, method_idx as u8, 0);
2260 self.current()
2262 .emit_abc(Op::Move, args_start, args.len() as u8, 0, 0);
2263 for _ in args {
2264 self.current().free_register();
2265 }
2266 self.current().free_register(); return Ok(());
2268 }
2269
2270 let func_reg = self.current().alloc_register();
2272 self.compile_expr(function, func_reg)?;
2273
2274 let args_start = self.current().next_register;
2275 for arg in args {
2276 let r = self.current().alloc_register();
2277 self.compile_expr(arg, r)?;
2278 }
2279
2280 self.current()
2281 .emit_abc(Op::Call, func_reg, args_start, args.len() as u8, 0);
2282
2283 for _ in args {
2285 self.current().free_register();
2286 }
2287
2288 if func_reg != dest {
2290 self.current().emit_abc(Op::Move, dest, func_reg, 0, 0);
2291 }
2292 self.current().free_register(); Ok(())
2295 }
2296
2297 fn compile_builtin_call(
2298 &mut self,
2299 builtin_id: BuiltinId,
2300 args: &[Expr],
2301 dest: u8,
2302 ) -> Result<(), TlError> {
2303 let args_start = self.current().next_register;
2304 for arg in args {
2305 let r = self.current().alloc_register();
2306 self.compile_expr(arg, r)?;
2307 }
2308
2309 self.current()
2310 .emit_abx(Op::CallBuiltin, dest, builtin_id as u16, 0);
2311 self.current()
2313 .emit_abc(Op::Move, args.len() as u8, args_start, 0, 0);
2314
2315 for _ in args {
2316 self.current().free_register();
2317 }
2318
2319 Ok(())
2320 }
2321
2322 fn emit_pipe_move(&mut self, left: &Expr) {
2324 if let Expr::Ident(name) = left {
2325 if let Some(local_reg) = self.resolve_local(name) {
2326 self.current().emit_abc(Op::LoadMoved, local_reg, 0, 0, 0);
2327 } else {
2328 let moved_reg = self.current().alloc_register();
2329 self.current().emit_abc(Op::LoadMoved, moved_reg, 0, 0, 0);
2330 let idx = self
2331 .current()
2332 .add_constant(Constant::String(Arc::from(name.as_str())));
2333 self.current().emit_abx(Op::SetGlobal, moved_reg, idx, 0);
2334 self.current().free_register();
2335 }
2336 }
2337 }
2338
2339 const TABLE_OPS: &'static [&'static str] = &[
2342 "filter",
2343 "select",
2344 "sort",
2345 "with",
2346 "aggregate",
2347 "join",
2348 "head",
2349 "limit",
2350 "collect",
2351 "show",
2352 "describe",
2353 "write_csv",
2354 "write_parquet",
2355 "sample",
2356 "window",
2357 "union",
2358 ];
2359
2360 fn compile_pipe(&mut self, left: &Expr, right: &Expr, dest: u8) -> Result<(), TlError> {
2361 if let Some((source, ops)) = self.try_extract_table_pipe_chain(left, right)
2363 && let Ok(plan) = tl_ir::build_query_plan(&source, &ops)
2364 {
2365 let optimized = tl_ir::optimize(plan);
2366 let lowered = tl_ir::lower_plan(&optimized);
2367 return self.emit_optimized_plan(left, &source, &lowered, dest);
2368 }
2369 self.compile_pipe_legacy(left, right, dest)
2371 }
2372
2373 fn try_extract_table_pipe_chain(
2376 &self,
2377 left: &Expr,
2378 right: &Expr,
2379 ) -> Option<(Expr, Vec<(String, Vec<Expr>)>)> {
2380 let mut ops = Vec::new();
2381
2382 let (fname, args) = match right {
2384 Expr::Call { function, args } => {
2385 if let Expr::Ident(fname) = function.as_ref() {
2386 (fname.as_str(), args.clone())
2387 } else {
2388 return None;
2389 }
2390 }
2391 _ => return None,
2392 };
2393
2394 if !Self::TABLE_OPS.contains(&fname) {
2395 return None;
2396 }
2397
2398 ops.push((fname.to_string(), args));
2399
2400 let source = self.extract_pipe_chain_left(left, &mut ops)?;
2402
2403 ops.reverse();
2405
2406 Some((source, ops))
2407 }
2408
2409 fn extract_pipe_chain_left(
2411 &self,
2412 expr: &Expr,
2413 ops: &mut Vec<(String, Vec<Expr>)>,
2414 ) -> Option<Expr> {
2415 match expr {
2416 Expr::Pipe { left, right } => {
2417 let (fname, args) = match right.as_ref() {
2419 Expr::Call { function, args } => {
2420 if let Expr::Ident(fname) = function.as_ref() {
2421 (fname.as_str(), args.clone())
2422 } else {
2423 return None;
2424 }
2425 }
2426 _ => return None,
2427 };
2428
2429 if !Self::TABLE_OPS.contains(&fname) {
2430 return None;
2431 }
2432
2433 ops.push((fname.to_string(), args));
2434
2435 self.extract_pipe_chain_left(left, ops)
2437 }
2438 other => Some(other.clone()),
2440 }
2441 }
2442
2443 fn emit_optimized_plan(
2445 &mut self,
2446 _original_left: &Expr,
2447 source: &Expr,
2448 lowered_ops: &[(String, Vec<Expr>)],
2449 dest: u8,
2450 ) -> Result<(), TlError> {
2451 let left_reg = self.current().alloc_register();
2452 self.compile_expr(source, left_reg)?;
2453
2454 self.emit_pipe_move(source);
2456
2457 for (op_name, args) in lowered_ops {
2459 let args_idx = self
2460 .current()
2461 .add_constant(Constant::AstExprList(args.clone()));
2462 let op_idx = self
2463 .current()
2464 .add_constant(Constant::String(Arc::from(op_name.as_str())));
2465 self.current()
2466 .emit_abc(Op::TablePipe, left_reg, op_idx as u8, args_idx as u8, 0);
2467 }
2468
2469 if left_reg != dest {
2470 self.current().emit_abc(Op::Move, dest, left_reg, 0, 0);
2471 }
2472 self.current().free_register();
2473 Ok(())
2474 }
2475
2476 fn compile_pipe_legacy(&mut self, left: &Expr, right: &Expr, dest: u8) -> Result<(), TlError> {
2477 let left_reg = self.current().alloc_register();
2478 self.compile_expr(left, left_reg)?;
2479
2480 self.emit_pipe_move(left);
2482
2483 match right {
2484 Expr::Call { function, args } => {
2485 if let Expr::Ident(fname) = function.as_ref() {
2486 if Self::TABLE_OPS.contains(&fname.as_str()) {
2488 let args_idx = self
2490 .current()
2491 .add_constant(Constant::AstExprList(args.clone()));
2492 let op_idx = self
2493 .current()
2494 .add_constant(Constant::String(Arc::from(fname.as_str())));
2495 self.current().emit_abc(
2496 Op::TablePipe,
2497 left_reg,
2498 op_idx as u8,
2499 args_idx as u8,
2500 0,
2501 );
2502 if left_reg != dest {
2503 self.current().emit_abc(Op::Move, dest, left_reg, 0, 0);
2504 }
2505 self.current().free_register();
2506 return Ok(());
2507 }
2508
2509 if let Some(builtin_id) = BuiltinId::from_name(fname) {
2511 let args_start = left_reg; for arg in args {
2514 let r = self.current().alloc_register();
2515 self.compile_expr(arg, r)?;
2516 }
2517 self.current()
2518 .emit_abx(Op::CallBuiltin, dest, builtin_id as u16, 0);
2519 self.current()
2520 .emit_abc(Op::Move, (args.len() + 1) as u8, args_start, 0, 0);
2521 for _ in args {
2522 self.current().free_register();
2523 }
2524 self.current().free_register(); return Ok(());
2526 }
2527 }
2528
2529 let func_reg = self.current().alloc_register();
2531 self.compile_expr(function, func_reg)?;
2532
2533 let args_start = self.current().next_register;
2535 let first_arg = self.current().alloc_register();
2536 self.current().emit_abc(Op::Move, first_arg, left_reg, 0, 0);
2537
2538 for arg in args {
2539 let r = self.current().alloc_register();
2540 self.compile_expr(arg, r)?;
2541 }
2542
2543 let total_args = args.len() + 1;
2544 self.current()
2545 .emit_abc(Op::Call, func_reg, args_start, total_args as u8, 0);
2546
2547 for _ in 0..total_args {
2548 self.current().free_register();
2549 }
2550
2551 if func_reg != dest {
2552 self.current().emit_abc(Op::Move, dest, func_reg, 0, 0);
2553 }
2554 self.current().free_register(); }
2556 Expr::Ident(name) => {
2557 if let Some(builtin_id) = BuiltinId::from_name(name) {
2559 self.current()
2560 .emit_abx(Op::CallBuiltin, dest, builtin_id as u16, 0);
2561 self.current().emit_abc(Op::Move, 1, left_reg, 0, 0);
2562 } else {
2563 let func_reg = self.current().alloc_register();
2564 let name_idx = self
2565 .current()
2566 .add_constant(Constant::String(Arc::from(name.as_str())));
2567 self.current()
2568 .emit_abx(Op::GetGlobal, func_reg, name_idx, 0);
2569
2570 let args_start = self.current().next_register;
2571 let first_arg = self.current().alloc_register();
2572 self.current().emit_abc(Op::Move, first_arg, left_reg, 0, 0);
2573
2574 self.current()
2575 .emit_abc(Op::Call, func_reg, args_start, 1, 0);
2576 if func_reg != dest {
2577 self.current().emit_abc(Op::Move, dest, func_reg, 0, 0);
2578 }
2579 self.current().free_register(); self.current().free_register(); }
2582 }
2583 _ => {
2584 return Err(compile_err(
2585 "Right side of |> must be a function call".to_string(),
2586 ));
2587 }
2588 }
2589
2590 self.current().free_register(); Ok(())
2592 }
2593
2594 fn compile_case(&mut self, arms: &[MatchArm], dest: u8) -> Result<(), TlError> {
2595 let mut end_jumps = Vec::new();
2596 let mut has_default = false;
2597
2598 for arm in arms {
2599 if let Some(ref guard) = arm.guard {
2600 let cond_reg = self.current().alloc_register();
2602 self.compile_expr(guard, cond_reg)?;
2603 let jump_false = self.current().current_pos();
2604 self.current().emit_abx(Op::JumpIfFalse, cond_reg, 0, 0);
2605 self.current().free_register();
2606
2607 self.compile_expr(&arm.body, dest)?;
2608 let jump_end = self.current().current_pos();
2609 self.current().emit_abx(Op::Jump, 0, 0, 0);
2610 end_jumps.push(jump_end);
2611
2612 self.current().patch_jump(jump_false);
2613 } else {
2614 self.compile_expr(&arm.body, dest)?;
2616 has_default = true;
2617 break;
2618 }
2619 }
2620
2621 if !has_default {
2622 self.current().emit_abx(Op::LoadNone, dest, 0, 0);
2623 }
2624
2625 for pos in end_jumps {
2626 self.current().patch_jump(pos);
2627 }
2628
2629 Ok(())
2630 }
2631
2632 fn compile_match(
2633 &mut self,
2634 subject: &Expr,
2635 arms: &[MatchArm],
2636 dest: u8,
2637 ) -> Result<(), TlError> {
2638 let subj_reg = self.current().alloc_register();
2639 self.compile_expr(subject, subj_reg)?;
2640
2641 let mut end_jumps = Vec::new();
2642 let mut has_unconditional = false;
2643
2644 for arm in arms {
2645 let is_unconditional = match &arm.pattern {
2647 Pattern::Wildcard => true,
2648 Pattern::Binding(_) if arm.guard.is_none() => true,
2649 Pattern::Struct { name: None, .. } if arm.guard.is_none() => true,
2650 _ => false,
2651 };
2652
2653 self.compile_match_arm(arm, subj_reg, dest, &mut end_jumps)?;
2654
2655 if is_unconditional {
2656 has_unconditional = true;
2657 break; }
2659 }
2660
2661 if !has_unconditional {
2662 self.current().emit_abx(Op::LoadNone, dest, 0, 0);
2664 self.current().free_register(); }
2666
2667 for pos in end_jumps {
2668 self.current().patch_jump(pos);
2669 }
2670
2671 Ok(())
2672 }
2673
2674 fn compile_match_arm(
2677 &mut self,
2678 arm: &MatchArm,
2679 subj_reg: u8,
2680 dest: u8,
2681 end_jumps: &mut Vec<usize>,
2682 ) -> Result<(), TlError> {
2683 match &arm.pattern {
2684 Pattern::Wildcard => {
2685 self.compile_expr(&arm.body, dest)?;
2686 self.current().free_register(); for pos in end_jumps.drain(..) {
2688 self.current().patch_jump(pos);
2689 }
2690 return Ok(());
2694 }
2695 Pattern::Binding(name) => {
2696 let local = self.add_local(name.clone());
2697 self.current().emit_abc(Op::Move, local, subj_reg, 0, 0);
2698
2699 if let Some(guard) = &arm.guard {
2700 let guard_reg = self.current().alloc_register();
2701 self.compile_expr(guard, guard_reg)?;
2702 let jf = self.current().current_pos();
2703 self.current().emit_abx(Op::JumpIfFalse, guard_reg, 0, 0);
2704 self.current().free_register();
2705
2706 self.compile_expr(&arm.body, dest)?;
2707 let jump_end = self.current().current_pos();
2708 self.current().emit_abx(Op::Jump, 0, 0, 0);
2709 end_jumps.push(jump_end);
2710 self.current().patch_jump(jf);
2711 } else {
2712 self.compile_expr(&arm.body, dest)?;
2714 self.current().free_register(); for pos in end_jumps.drain(..) {
2716 self.current().patch_jump(pos);
2717 }
2718 return Ok(());
2719 }
2720 }
2721 Pattern::Literal(expr) => {
2722 let pat_reg = self.current().alloc_register();
2723 self.compile_expr(expr, pat_reg)?;
2724 let result_reg = self.current().alloc_register();
2725 self.current()
2726 .emit_abc(Op::TestMatch, subj_reg, pat_reg, result_reg, 0);
2727
2728 let jump_false = self.current().current_pos();
2729 self.current().emit_abx(Op::JumpIfFalse, result_reg, 0, 0);
2730 self.current().free_register(); self.current().free_register(); self.compile_guard_and_body(arm, dest, end_jumps, jump_false)?;
2734 }
2735 Pattern::Enum {
2736 type_name: _,
2737 variant,
2738 args,
2739 } => {
2740 let variant_const = self
2741 .current()
2742 .add_constant(Constant::String(Arc::from(variant.as_str())));
2743 let result_reg = self.current().alloc_register();
2744 self.current().emit_abc(
2745 Op::MatchEnum,
2746 subj_reg,
2747 variant_const as u8,
2748 result_reg,
2749 0,
2750 );
2751
2752 let jump_false = self.current().current_pos();
2753 self.current().emit_abx(Op::JumpIfFalse, result_reg, 0, 0);
2754 self.current().free_register(); for (i, arg_pat) in args.iter().enumerate() {
2758 match arg_pat {
2759 Pattern::Binding(name) => {
2760 let local = self.add_local(name.clone());
2761 self.current()
2762 .emit_abc(Op::ExtractField, local, subj_reg, i as u8, 0);
2763 }
2764 Pattern::Wildcard => {}
2765 _ => {}
2766 }
2767 }
2768
2769 self.compile_guard_and_body(arm, dest, end_jumps, jump_false)?;
2770 }
2771 Pattern::Struct {
2772 name: struct_name,
2773 fields,
2774 } => {
2775 let jump_false = if let Some(sname) = struct_name {
2777 let name_const = self
2778 .current()
2779 .add_constant(Constant::String(Arc::from(sname.as_str())));
2780 let name_reg = self.current().alloc_register();
2781 self.current()
2782 .emit_abx(Op::LoadConst, name_reg, name_const, 0);
2783 let result_reg = self.current().alloc_register();
2784 self.current()
2785 .emit_abc(Op::TestMatch, subj_reg, name_reg, result_reg, 0);
2786 let jf = self.current().current_pos();
2787 self.current().emit_abx(Op::JumpIfFalse, result_reg, 0, 0);
2788 self.current().free_register(); self.current().free_register(); jf
2791 } else {
2792 usize::MAX
2793 };
2794
2795 for field in fields {
2797 let fname_const = self
2798 .current()
2799 .add_constant(Constant::String(Arc::from(field.name.as_str())));
2800 match &field.pattern {
2801 None | Some(Pattern::Binding(_)) => {
2802 let bind_name = match &field.pattern {
2803 Some(Pattern::Binding(n)) => n.clone(),
2804 _ => field.name.clone(),
2805 };
2806 let local = self.add_local(bind_name);
2807 self.current().emit_abc(
2808 Op::ExtractNamedField,
2809 local,
2810 subj_reg,
2811 fname_const as u8,
2812 0,
2813 );
2814 }
2815 Some(Pattern::Wildcard) => {}
2816 _ => {
2817 let local = self.add_local(field.name.clone());
2818 self.current().emit_abc(
2819 Op::ExtractNamedField,
2820 local,
2821 subj_reg,
2822 fname_const as u8,
2823 0,
2824 );
2825 }
2826 }
2827 }
2828
2829 if jump_false != usize::MAX {
2830 self.compile_guard_and_body(arm, dest, end_jumps, jump_false)?;
2831 } else {
2832 if let Some(guard) = &arm.guard {
2834 let guard_reg = self.current().alloc_register();
2835 self.compile_expr(guard, guard_reg)?;
2836 let gj = self.current().current_pos();
2837 self.current().emit_abx(Op::JumpIfFalse, guard_reg, 0, 0);
2838 self.current().free_register();
2839
2840 self.compile_expr(&arm.body, dest)?;
2841 let jump_end = self.current().current_pos();
2842 self.current().emit_abx(Op::Jump, 0, 0, 0);
2843 end_jumps.push(jump_end);
2844 self.current().patch_jump(gj);
2845 } else {
2846 self.compile_expr(&arm.body, dest)?;
2848 self.current().free_register(); for pos in end_jumps.drain(..) {
2850 self.current().patch_jump(pos);
2851 }
2852 return Ok(());
2853 }
2854 }
2855 }
2856 Pattern::List { elements, rest } => {
2857 let len_builtin_reg = self.current().alloc_register();
2859 self.current()
2861 .emit_abx(Op::CallBuiltin, len_builtin_reg, BuiltinId::Len as u16, 0);
2862 self.current().emit_abc(Op::Move, 1, subj_reg, 0, 0); let expected_len_reg = self.current().alloc_register();
2865 let len_val = elements.len() as i64;
2866 let len_const = self.current().add_constant(Constant::Int(len_val));
2867 self.current()
2868 .emit_abx(Op::LoadConst, expected_len_reg, len_const, 0);
2869
2870 let cmp_reg = self.current().alloc_register();
2871 if rest.is_some() {
2872 self.current()
2873 .emit_abc(Op::Gte, cmp_reg, len_builtin_reg, expected_len_reg, 0);
2874 } else {
2875 self.current()
2876 .emit_abc(Op::Eq, cmp_reg, len_builtin_reg, expected_len_reg, 0);
2877 }
2878 let jump_false = self.current().current_pos();
2879 self.current().emit_abx(Op::JumpIfFalse, cmp_reg, 0, 0);
2880 self.current().free_register(); self.current().free_register(); self.current().free_register(); for (i, elem_pat) in elements.iter().enumerate() {
2886 match elem_pat {
2887 Pattern::Binding(name) => {
2888 let local = self.add_local(name.clone());
2889 self.current()
2890 .emit_abc(Op::ExtractField, local, subj_reg, i as u8, 0);
2891 }
2892 Pattern::Wildcard => {}
2893 _ => {}
2894 }
2895 }
2896
2897 if let Some(rest_name) = rest {
2899 let local = self.add_local(rest_name.clone());
2900 self.current().emit_abc(
2901 Op::ExtractField,
2902 local,
2903 subj_reg,
2904 (elements.len() as u8) | 0x80,
2905 0,
2906 );
2907 }
2908
2909 self.compile_guard_and_body(arm, dest, end_jumps, jump_false)?;
2910 }
2911 Pattern::Or(patterns) => {
2912 let mut match_jumps = Vec::new();
2913
2914 for sub_pat in patterns {
2915 match sub_pat {
2916 Pattern::Literal(expr) => {
2917 let pat_reg = self.current().alloc_register();
2918 self.compile_expr(expr, pat_reg)?;
2919 let result_reg = self.current().alloc_register();
2920 self.current().emit_abc(
2921 Op::TestMatch,
2922 subj_reg,
2923 pat_reg,
2924 result_reg,
2925 0,
2926 );
2927 let jump_true = self.current().current_pos();
2928 self.current().emit_abx(Op::JumpIfTrue, result_reg, 0, 0);
2929 match_jumps.push(jump_true);
2930 self.current().free_register(); self.current().free_register(); }
2933 Pattern::Enum { variant, .. } => {
2934 let variant_const = self
2935 .current()
2936 .add_constant(Constant::String(Arc::from(variant.as_str())));
2937 let result_reg = self.current().alloc_register();
2938 self.current().emit_abc(
2939 Op::MatchEnum,
2940 subj_reg,
2941 variant_const as u8,
2942 result_reg,
2943 0,
2944 );
2945 let jump_true = self.current().current_pos();
2946 self.current().emit_abx(Op::JumpIfTrue, result_reg, 0, 0);
2947 match_jumps.push(jump_true);
2948 self.current().free_register();
2949 }
2950 Pattern::Wildcard | Pattern::Binding(_) => {
2951 let jump = self.current().current_pos();
2952 self.current().emit_abx(Op::Jump, 0, 0, 0);
2953 match_jumps.push(jump);
2954 }
2955 _ => {}
2956 }
2957 }
2958
2959 let jump_skip = self.current().current_pos();
2961 self.current().emit_abx(Op::Jump, 0, 0, 0);
2962
2963 for jt in &match_jumps {
2965 self.current().patch_jump(*jt);
2966 }
2967
2968 if let Some(guard) = &arm.guard {
2970 let guard_reg = self.current().alloc_register();
2971 self.compile_expr(guard, guard_reg)?;
2972 let gj = self.current().current_pos();
2973 self.current().emit_abx(Op::JumpIfFalse, guard_reg, 0, 0);
2974 self.current().free_register();
2975
2976 self.compile_expr(&arm.body, dest)?;
2977 let jump_end = self.current().current_pos();
2978 self.current().emit_abx(Op::Jump, 0, 0, 0);
2979 end_jumps.push(jump_end);
2980 self.current().patch_jump(gj);
2981 } else {
2982 self.compile_expr(&arm.body, dest)?;
2983 let jump_end = self.current().current_pos();
2984 self.current().emit_abx(Op::Jump, 0, 0, 0);
2985 end_jumps.push(jump_end);
2986 }
2987
2988 self.current().patch_jump(jump_skip);
2989 }
2990 }
2991 Ok(())
2992 }
2993
2994 fn compile_guard_and_body(
2996 &mut self,
2997 arm: &MatchArm,
2998 dest: u8,
2999 end_jumps: &mut Vec<usize>,
3000 jump_false: usize,
3001 ) -> Result<(), TlError> {
3002 let guard_jump = if let Some(guard) = &arm.guard {
3003 let guard_reg = self.current().alloc_register();
3004 self.compile_expr(guard, guard_reg)?;
3005 let jf = self.current().current_pos();
3006 self.current().emit_abx(Op::JumpIfFalse, guard_reg, 0, 0);
3007 self.current().free_register();
3008 Some(jf)
3009 } else {
3010 None
3011 };
3012
3013 self.compile_expr(&arm.body, dest)?;
3014 let jump_end = self.current().current_pos();
3015 self.current().emit_abx(Op::Jump, 0, 0, 0);
3016 end_jumps.push(jump_end);
3017
3018 self.current().patch_jump(jump_false);
3019 if let Some(gj) = guard_jump {
3020 self.current().patch_jump(gj);
3021 }
3022 Ok(())
3023 }
3024
3025 fn compile_string_interpolation(&mut self, s: &str, dest: u8) -> Result<(), TlError> {
3028 let mut segments: Vec<StringSegment> = Vec::new();
3030 let mut chars = s.chars().peekable();
3031 let mut current_literal = String::new();
3032
3033 while let Some(ch) = chars.next() {
3034 if ch == '{' {
3035 let mut var_name = String::new();
3036 let mut depth = 1;
3037 for c in chars.by_ref() {
3038 if c == '{' {
3039 depth += 1;
3040 } else if c == '}' {
3041 depth -= 1;
3042 if depth == 0 {
3043 break;
3044 }
3045 }
3046 var_name.push(c);
3047 }
3048 if !current_literal.is_empty() {
3049 segments.push(StringSegment::Literal(std::mem::take(&mut current_literal)));
3050 }
3051 segments.push(StringSegment::Variable(var_name));
3052 } else if ch == '\\' {
3053 match chars.next() {
3054 Some('n') => current_literal.push('\n'),
3055 Some('t') => current_literal.push('\t'),
3056 Some('\\') => current_literal.push('\\'),
3057 Some('"') => current_literal.push('"'),
3058 Some(c) => {
3059 current_literal.push('\\');
3060 current_literal.push(c);
3061 }
3062 None => current_literal.push('\\'),
3063 }
3064 } else {
3065 current_literal.push(ch);
3066 }
3067 }
3068 if !current_literal.is_empty() {
3069 segments.push(StringSegment::Literal(current_literal));
3070 }
3071
3072 if segments.is_empty() {
3073 let idx = self.current().add_constant(Constant::String(Arc::from("")));
3075 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
3076 return Ok(());
3077 }
3078
3079 if segments.len() == 1
3081 && let StringSegment::Literal(ref lit) = segments[0]
3082 {
3083 let idx = self
3084 .current()
3085 .add_constant(Constant::String(Arc::from(lit.as_str())));
3086 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
3087 return Ok(());
3088 }
3089
3090 self.compile_string_segment(&segments[0], dest)?;
3092
3093 for segment in &segments[1..] {
3095 let tmp = self.current().alloc_register();
3096 self.compile_string_segment(segment, tmp)?;
3097 self.current().emit_abc(Op::Concat, dest, dest, tmp, 0);
3098 self.current().free_register();
3099 }
3100
3101 Ok(())
3102 }
3103
3104 fn compile_string_segment(&mut self, seg: &StringSegment, dest: u8) -> Result<(), TlError> {
3105 match seg {
3106 StringSegment::Literal(s) => {
3107 let idx = self
3108 .current()
3109 .add_constant(Constant::String(Arc::from(s.as_str())));
3110 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
3111 }
3112 StringSegment::Variable(name) => {
3113 let var_reg = self.current().alloc_register();
3115 if let Some(reg) = self.resolve_local(name) {
3116 if reg != var_reg {
3117 self.current().emit_abc(Op::Move, var_reg, reg, 0, 0);
3118 }
3119 } else if let Some(uv) = self.resolve_upvalue(name) {
3120 self.current().emit_abc(Op::GetUpvalue, var_reg, uv, 0, 0);
3121 } else {
3122 let idx = self
3123 .current()
3124 .add_constant(Constant::String(Arc::from(name.as_str())));
3125 self.current().emit_abx(Op::GetGlobal, var_reg, idx, 0);
3126 }
3127 self.current()
3129 .emit_abx(Op::CallBuiltin, dest, BuiltinId::Str as u16, 0);
3130 self.current().emit_abc(Op::Move, 1, var_reg, 0, 0); self.current().free_register(); }
3133 }
3134 Ok(())
3135 }
3136
3137 fn compile_use(&mut self, item: &UseItem) -> Result<(), TlError> {
3138 let reg = self.current().alloc_register();
3141 match item {
3142 UseItem::Single(path) => {
3143 let path_str = path.join(".");
3144 let path_idx = self
3145 .current()
3146 .add_constant(Constant::String(Arc::from(path_str.as_str())));
3147 self.current().emit_abx(Op::Import, reg, path_idx, 0);
3148 self.current().emit_abc(Op::Move, 0, 0, 0xAB, 0);
3150 }
3151 UseItem::Group(prefix, names) => {
3152 let path_str = format!("{}.{{{}}}", prefix.join("."), names.join(","));
3153 let path_idx = self
3154 .current()
3155 .add_constant(Constant::String(Arc::from(path_str.as_str())));
3156 self.current().emit_abx(Op::Import, reg, path_idx, 0);
3157 self.current().emit_abc(Op::Move, 0, 1, 0xAB, 0); }
3159 UseItem::Wildcard(path) => {
3160 let path_str = format!("{}.*", path.join("."));
3161 let path_idx = self
3162 .current()
3163 .add_constant(Constant::String(Arc::from(path_str.as_str())));
3164 self.current().emit_abx(Op::Import, reg, path_idx, 0);
3165 self.current().emit_abc(Op::Move, 0, 2, 0xAB, 0); }
3167 UseItem::Aliased(path, alias) => {
3168 let path_str = path.join(".");
3169 let path_idx = self
3170 .current()
3171 .add_constant(Constant::String(Arc::from(path_str.as_str())));
3172 let alias_idx = self
3173 .current()
3174 .add_constant(Constant::String(Arc::from(alias.as_str())));
3175 self.current().emit_abx(Op::Import, reg, path_idx, 0);
3176 self.current()
3177 .emit_abc(Op::Move, alias_idx as u8, 3, 0xAB, 0); }
3179 }
3180 self.current().free_register();
3181 Ok(())
3182 }
3183}
3184
3185enum StringSegment {
3186 Literal(String),
3187 Variable(String),
3188}
3189
3190pub fn compile(program: &Program) -> Result<Prototype, TlError> {
3193 compile_with_source(program, "")
3194}
3195
3196pub fn compile_with_source(program: &Program, source: &str) -> Result<Prototype, TlError> {
3198 let line_offsets = Compiler::build_line_offsets(source);
3199 let mut compiler = Compiler {
3200 states: vec![CompilerState::new("<main>".to_string())],
3201 line_offsets,
3202 current_line: 0,
3203 };
3204
3205 let stmts = &program.statements;
3206 let mut last_expr_reg: Option<u8> = None;
3207
3208 for (i, stmt) in stmts.iter().enumerate() {
3209 let is_last = i == stmts.len() - 1;
3210 let line = compiler.line_of(stmt.span.start);
3212 compiler.current_line = line;
3213 compiler.current().current_line = line;
3214 match &stmt.kind {
3215 StmtKind::Expr(expr) if is_last => {
3216 let reg = compiler.current().alloc_register();
3218 compiler.compile_expr(expr, reg)?;
3219 last_expr_reg = Some(reg);
3220 }
3221 StmtKind::If {
3222 condition,
3223 then_body,
3224 else_ifs,
3225 else_body,
3226 } if is_last && else_body.is_some() => {
3227 let dest = compiler.current().alloc_register();
3228 compiler.compile_if_as_expr(condition, then_body, else_ifs, else_body, dest)?;
3229 last_expr_reg = Some(dest);
3230 }
3231 _ => {
3232 compiler.compile_stmt(stmt)?;
3233 }
3234 }
3235 }
3236
3237 {
3239 let state = &compiler.states[0];
3240 let top_locals: Vec<(String, u8)> = state
3241 .locals
3242 .iter()
3243 .filter(|l| l.depth == 0)
3244 .map(|l| (l.name.clone(), l.register))
3245 .collect();
3246 compiler.states[0].proto.top_level_locals = top_locals;
3247 }
3248
3249 let state = &mut compiler.states[0];
3251 let needs_return = if state.proto.code.is_empty() {
3252 true
3253 } else {
3254 let last = *state.proto.code.last().unwrap();
3255 decode_op(last) != Op::Return
3256 };
3257 if needs_return {
3258 if let Some(reg) = last_expr_reg {
3259 state.emit_abc(Op::Return, reg, 0, 0, 0);
3260 } else {
3261 let reg = state.alloc_register();
3262 state.emit_abx(Op::LoadNone, reg, 0, 0);
3263 state.emit_abc(Op::Return, reg, 0, 0, 0);
3264 }
3265 }
3266
3267 let state = compiler.states.pop().unwrap();
3268 Ok(state.proto)
3269}
3270
3271#[cfg(test)]
3272mod tests {
3273 use super::*;
3274 use tl_parser::parse;
3275
3276 #[test]
3277 fn test_compile_int_literal() {
3278 let program = parse("42").unwrap();
3279 let proto = compile(&program).unwrap();
3280 assert!(!proto.code.is_empty());
3281 assert!(
3282 proto
3283 .constants
3284 .iter()
3285 .any(|c| matches!(c, Constant::Int(42)))
3286 );
3287 }
3288
3289 #[test]
3290 fn test_compile_add() {
3291 let program = parse("1 + 2").unwrap();
3293 let proto = compile(&program).unwrap();
3294 assert!(!proto.code.is_empty());
3295 assert!(
3297 proto
3298 .constants
3299 .iter()
3300 .any(|c| matches!(c, Constant::Int(3)))
3301 );
3302 }
3303
3304 #[test]
3305 fn test_compile_function() {
3306 let program = parse("fn add(a, b) { a + b }").unwrap();
3307 let proto = compile(&program).unwrap();
3308 let has_closure = proto
3310 .code
3311 .iter()
3312 .any(|&inst| decode_op(inst) == Op::Closure);
3313 assert!(has_closure);
3314 }
3315
3316 #[test]
3317 fn test_compile_closure() {
3318 let program = parse("let f = (x) => x * 2").unwrap();
3319 let proto = compile(&program).unwrap();
3320 let has_closure = proto
3321 .code
3322 .iter()
3323 .any(|&inst| decode_op(inst) == Op::Closure);
3324 assert!(has_closure);
3325 }
3326
3327 #[test]
3330 fn test_fold_int_addition() {
3331 let program = parse("2 + 3").unwrap();
3333 let proto = compile(&program).unwrap();
3334 assert!(
3335 proto
3336 .constants
3337 .iter()
3338 .any(|c| matches!(c, Constant::Int(5)))
3339 );
3340 let has_add = proto.code.iter().any(|&inst| decode_op(inst) == Op::Add);
3342 assert!(
3343 !has_add,
3344 "Folded expression should not have Add instruction"
3345 );
3346 }
3347
3348 #[test]
3349 fn test_fold_float_multiplication() {
3350 let program = parse("2.0 * 3.0").unwrap();
3351 let proto = compile(&program).unwrap();
3352 assert!(
3353 proto
3354 .constants
3355 .iter()
3356 .any(|c| matches!(c, Constant::Float(f) if (*f - 6.0).abs() < f64::EPSILON))
3357 );
3358 }
3359
3360 #[test]
3361 fn test_fold_string_concatenation() {
3362 let program = parse("\"hello\" + \" world\"").unwrap();
3363 let proto = compile(&program).unwrap();
3364 assert!(
3365 proto
3366 .constants
3367 .iter()
3368 .any(|c| matches!(c, Constant::String(s) if s.as_ref() == "hello world"))
3369 );
3370 }
3371
3372 #[test]
3373 fn test_fold_negation() {
3374 let program = parse("-42").unwrap();
3375 let proto = compile(&program).unwrap();
3376 assert!(
3377 proto
3378 .constants
3379 .iter()
3380 .any(|c| matches!(c, Constant::Int(-42)))
3381 );
3382 }
3383
3384 #[test]
3385 fn test_fold_not() {
3386 let program = parse("not true").unwrap();
3387 let proto = compile(&program).unwrap();
3388 let has_load_false = proto
3390 .code
3391 .iter()
3392 .any(|&inst| decode_op(inst) == Op::LoadFalse);
3393 assert!(has_load_false, "'not true' should fold to false");
3394 }
3395
3396 #[test]
3397 fn test_fold_nested_arithmetic() {
3398 let program = parse("2 + 3 * 4").unwrap();
3400 let proto = compile(&program).unwrap();
3401 assert!(
3402 proto
3403 .constants
3404 .iter()
3405 .any(|c| matches!(c, Constant::Int(14)))
3406 );
3407 }
3408
3409 #[test]
3410 fn test_no_fold_division_by_zero() {
3411 let program = parse("10 / 0").unwrap();
3413 let proto = compile(&program).unwrap();
3414 let has_div = proto.code.iter().any(|&inst| decode_op(inst) == Op::Div);
3416 assert!(has_div, "Division by zero should not be folded");
3417 }
3418
3419 #[test]
3420 fn test_no_fold_variable_reference() {
3421 let program = parse("let x = 5\nx + 1").unwrap();
3423 let proto = compile(&program).unwrap();
3424 let has_add = proto.code.iter().any(|&inst| decode_op(inst) == Op::Add);
3425 assert!(has_add, "Expression with variables should not be folded");
3426 }
3427
3428 #[test]
3429 fn test_no_fold_interpolated_string() {
3430 let program = parse("let x = 5\n\"{x} hello\"").unwrap();
3432 let proto = compile(&program).unwrap();
3433 assert!(!proto.code.is_empty());
3436 }
3437
3438 #[test]
3439 fn test_fold_transparent_to_runtime() {
3440 use crate::Vm;
3443 let program = parse("2 + 3 * 4").unwrap();
3444 let proto = compile(&program).unwrap();
3445 let mut vm = Vm::new();
3446 let result = vm.execute(&proto).unwrap();
3447 assert_eq!(result.to_string(), "14");
3448 }
3449
3450 #[test]
3453 fn test_dce_after_return() {
3454 let program = parse("fn f() {\n return 1\n print(\"dead\")\n}").unwrap();
3456 let proto = compile(&program).unwrap();
3457 let fn_proto = proto
3459 .constants
3460 .iter()
3461 .find_map(|c| {
3462 if let Constant::Prototype(p) = c {
3463 Some(p.clone())
3464 } else {
3465 None
3466 }
3467 })
3468 .expect("should have function prototype");
3469 let return_pos = fn_proto
3471 .code
3472 .iter()
3473 .position(|&inst| decode_op(inst) == Op::Return);
3474 assert!(return_pos.is_some(), "Should have a Return instruction");
3475 let after_return: Vec<_> = fn_proto.code[return_pos.unwrap() + 1..]
3477 .iter()
3478 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3479 .collect();
3480 assert!(
3481 after_return.is_empty(),
3482 "Should not have CallBuiltin after Return"
3483 );
3484 }
3485
3486 #[test]
3487 fn test_dce_after_break() {
3488 let program =
3490 parse("fn f() {\n while true {\n break\n print(\"dead\")\n }\n}").unwrap();
3491 let proto = compile(&program).unwrap();
3492 let fn_proto = proto
3493 .constants
3494 .iter()
3495 .find_map(|c| {
3496 if let Constant::Prototype(p) = c {
3497 Some(p.clone())
3498 } else {
3499 None
3500 }
3501 })
3502 .expect("should have function prototype");
3503 let call_builtins: Vec<_> = fn_proto
3505 .code
3506 .iter()
3507 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3508 .collect();
3509 assert!(
3510 call_builtins.is_empty(),
3511 "Should not have CallBuiltin after break"
3512 );
3513 }
3514
3515 #[test]
3516 fn test_dce_after_continue() {
3517 let program =
3519 parse("fn f() {\n while true {\n continue\n print(\"dead\")\n }\n}").unwrap();
3520 let proto = compile(&program).unwrap();
3521 let fn_proto = proto
3522 .constants
3523 .iter()
3524 .find_map(|c| {
3525 if let Constant::Prototype(p) = c {
3526 Some(p.clone())
3527 } else {
3528 None
3529 }
3530 })
3531 .expect("should have function prototype");
3532 let call_builtins: Vec<_> = fn_proto
3533 .code
3534 .iter()
3535 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3536 .collect();
3537 assert!(
3538 call_builtins.is_empty(),
3539 "Should not have CallBuiltin after continue"
3540 );
3541 }
3542
3543 #[test]
3544 fn test_dce_if_both_branches_return() {
3545 let program = parse("fn f(x) {\n if x {\n return 1\n } else {\n return 2\n }\n print(\"dead\")\n}").unwrap();
3547 let proto = compile(&program).unwrap();
3548 let fn_proto = proto
3549 .constants
3550 .iter()
3551 .find_map(|c| {
3552 if let Constant::Prototype(p) = c {
3553 Some(p.clone())
3554 } else {
3555 None
3556 }
3557 })
3558 .expect("should have function prototype");
3559 let call_builtins: Vec<_> = fn_proto
3560 .code
3561 .iter()
3562 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3563 .collect();
3564 assert!(
3565 call_builtins.is_empty(),
3566 "Should not have CallBuiltin after if where both branches return"
3567 );
3568 }
3569
3570 #[test]
3571 fn test_dce_if_one_branch_returns() {
3572 let program =
3574 parse("fn f(x) {\n if x {\n return 1\n }\n print(\"alive\")\n return 0\n}")
3575 .unwrap();
3576 let proto = compile(&program).unwrap();
3577 let fn_proto = proto
3578 .constants
3579 .iter()
3580 .find_map(|c| {
3581 if let Constant::Prototype(p) = c {
3582 Some(p.clone())
3583 } else {
3584 None
3585 }
3586 })
3587 .expect("should have function prototype");
3588 let call_builtins: Vec<_> = fn_proto
3589 .code
3590 .iter()
3591 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3592 .collect();
3593 assert!(
3594 !call_builtins.is_empty(),
3595 "Should have CallBuiltin after if where only one branch returns"
3596 );
3597 }
3598
3599 #[test]
3600 fn test_dce_nested_function() {
3601 let program = parse("fn outer() {\n fn inner() {\n return 1\n print(\"dead\")\n }\n print(\"alive\")\n}").unwrap();
3603 let proto = compile(&program).unwrap();
3604 let outer_fn = proto
3606 .constants
3607 .iter()
3608 .find_map(|c| {
3609 if let Constant::Prototype(p) = c {
3610 if p.name == "outer" {
3611 Some(p.clone())
3612 } else {
3613 None
3614 }
3615 } else {
3616 None
3617 }
3618 })
3619 .expect("should have outer function");
3620 let call_builtins: Vec<_> = outer_fn
3621 .code
3622 .iter()
3623 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3624 .collect();
3625 assert!(
3626 !call_builtins.is_empty(),
3627 "Outer function should have CallBuiltin"
3628 );
3629 }
3630
3631 #[test]
3632 fn test_dce_try_catch_independent() {
3633 let program =
3635 parse("fn f() {\n try {\n return 1\n } catch e {\n print(e)\n }\n}").unwrap();
3636 let proto = compile(&program).unwrap();
3637 let fn_proto = proto
3638 .constants
3639 .iter()
3640 .find_map(|c| {
3641 if let Constant::Prototype(p) = c {
3642 Some(p.clone())
3643 } else {
3644 None
3645 }
3646 })
3647 .expect("should have function prototype");
3648 let call_builtins: Vec<_> = fn_proto
3650 .code
3651 .iter()
3652 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3653 .collect();
3654 assert!(
3655 !call_builtins.is_empty(),
3656 "Catch block should not be eliminated by try body return"
3657 );
3658 }
3659
3660 #[test]
3661 fn test_dce_existing_tests_unaffected() {
3662 use crate::Vm;
3664 let tests = [
3665 ("let x = 42\nx", "42"),
3666 ("fn add(a, b) { a + b }\nadd(1, 2)", "3"),
3667 ("if true { 1 } else { 2 }", "1"),
3668 ];
3669 for (src, expected) in tests {
3670 let program = parse(src).unwrap();
3671 let proto = compile(&program).unwrap();
3672 let mut vm = Vm::new();
3673 let result = vm.execute(&proto).unwrap();
3674 assert_eq!(result.to_string(), expected, "Failed for: {src}");
3675 }
3676 }
3677}