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 "to_rows",
2352 "show",
2353 "describe",
2354 "write_csv",
2355 "write_parquet",
2356 "sample",
2357 "window",
2358 "union",
2359 ];
2360
2361 fn compile_pipe(&mut self, left: &Expr, right: &Expr, dest: u8) -> Result<(), TlError> {
2362 if let Some((source, ops)) = self.try_extract_table_pipe_chain(left, right)
2364 && let Ok(plan) = tl_ir::build_query_plan(&source, &ops)
2365 {
2366 let optimized = tl_ir::optimize(plan);
2367 let lowered = tl_ir::lower_plan(&optimized);
2368 return self.emit_optimized_plan(left, &source, &lowered, dest);
2369 }
2370 self.compile_pipe_legacy(left, right, dest)
2372 }
2373
2374 fn try_extract_table_pipe_chain(
2377 &self,
2378 left: &Expr,
2379 right: &Expr,
2380 ) -> Option<(Expr, Vec<(String, Vec<Expr>)>)> {
2381 let mut ops = Vec::new();
2382
2383 let (fname, args) = match right {
2385 Expr::Call { function, args } => {
2386 if let Expr::Ident(fname) = function.as_ref() {
2387 (fname.as_str(), args.clone())
2388 } else {
2389 return None;
2390 }
2391 }
2392 _ => return None,
2393 };
2394
2395 if !Self::TABLE_OPS.contains(&fname) {
2396 return None;
2397 }
2398
2399 ops.push((fname.to_string(), args));
2400
2401 let source = self.extract_pipe_chain_left(left, &mut ops)?;
2403
2404 ops.reverse();
2406
2407 Some((source, ops))
2408 }
2409
2410 fn extract_pipe_chain_left(
2412 &self,
2413 expr: &Expr,
2414 ops: &mut Vec<(String, Vec<Expr>)>,
2415 ) -> Option<Expr> {
2416 match expr {
2417 Expr::Pipe { left, right } => {
2418 let (fname, args) = match right.as_ref() {
2420 Expr::Call { function, args } => {
2421 if let Expr::Ident(fname) = function.as_ref() {
2422 (fname.as_str(), args.clone())
2423 } else {
2424 return None;
2425 }
2426 }
2427 _ => return None,
2428 };
2429
2430 if !Self::TABLE_OPS.contains(&fname) {
2431 return None;
2432 }
2433
2434 ops.push((fname.to_string(), args));
2435
2436 self.extract_pipe_chain_left(left, ops)
2438 }
2439 other => Some(other.clone()),
2441 }
2442 }
2443
2444 fn emit_optimized_plan(
2446 &mut self,
2447 _original_left: &Expr,
2448 source: &Expr,
2449 lowered_ops: &[(String, Vec<Expr>)],
2450 dest: u8,
2451 ) -> Result<(), TlError> {
2452 let left_reg = self.current().alloc_register();
2453 self.compile_expr(source, left_reg)?;
2454
2455 self.emit_pipe_move(source);
2457
2458 for (op_name, args) in lowered_ops {
2460 let args_idx = self
2461 .current()
2462 .add_constant(Constant::AstExprList(args.clone()));
2463 let op_idx = self
2464 .current()
2465 .add_constant(Constant::String(Arc::from(op_name.as_str())));
2466 self.current()
2467 .emit_abc(Op::TablePipe, left_reg, op_idx as u8, args_idx as u8, 0);
2468 }
2469
2470 if left_reg != dest {
2471 self.current().emit_abc(Op::Move, dest, left_reg, 0, 0);
2472 }
2473 self.current().free_register();
2474 Ok(())
2475 }
2476
2477 fn compile_pipe_legacy(&mut self, left: &Expr, right: &Expr, dest: u8) -> Result<(), TlError> {
2478 let left_reg = self.current().alloc_register();
2479 self.compile_expr(left, left_reg)?;
2480
2481 self.emit_pipe_move(left);
2483
2484 match right {
2485 Expr::Call { function, args } => {
2486 if let Expr::Ident(fname) = function.as_ref() {
2487 if Self::TABLE_OPS.contains(&fname.as_str()) {
2489 let args_idx = self
2491 .current()
2492 .add_constant(Constant::AstExprList(args.clone()));
2493 let op_idx = self
2494 .current()
2495 .add_constant(Constant::String(Arc::from(fname.as_str())));
2496 self.current().emit_abc(
2497 Op::TablePipe,
2498 left_reg,
2499 op_idx as u8,
2500 args_idx as u8,
2501 0,
2502 );
2503 if left_reg != dest {
2504 self.current().emit_abc(Op::Move, dest, left_reg, 0, 0);
2505 }
2506 self.current().free_register();
2507 return Ok(());
2508 }
2509
2510 if let Some(builtin_id) = BuiltinId::from_name(fname) {
2512 let args_start = left_reg; for arg in args {
2515 let r = self.current().alloc_register();
2516 self.compile_expr(arg, r)?;
2517 }
2518 self.current()
2519 .emit_abx(Op::CallBuiltin, dest, builtin_id as u16, 0);
2520 self.current()
2521 .emit_abc(Op::Move, (args.len() + 1) as u8, args_start, 0, 0);
2522 for _ in args {
2523 self.current().free_register();
2524 }
2525 self.current().free_register(); return Ok(());
2527 }
2528 }
2529
2530 let func_reg = self.current().alloc_register();
2532 self.compile_expr(function, func_reg)?;
2533
2534 let args_start = self.current().next_register;
2536 let first_arg = self.current().alloc_register();
2537 self.current().emit_abc(Op::Move, first_arg, left_reg, 0, 0);
2538
2539 for arg in args {
2540 let r = self.current().alloc_register();
2541 self.compile_expr(arg, r)?;
2542 }
2543
2544 let total_args = args.len() + 1;
2545 self.current()
2546 .emit_abc(Op::Call, func_reg, args_start, total_args as u8, 0);
2547
2548 for _ in 0..total_args {
2549 self.current().free_register();
2550 }
2551
2552 if func_reg != dest {
2553 self.current().emit_abc(Op::Move, dest, func_reg, 0, 0);
2554 }
2555 self.current().free_register(); }
2557 Expr::Ident(name) => {
2558 if let Some(builtin_id) = BuiltinId::from_name(name) {
2560 self.current()
2561 .emit_abx(Op::CallBuiltin, dest, builtin_id as u16, 0);
2562 self.current().emit_abc(Op::Move, 1, left_reg, 0, 0);
2563 } else {
2564 let func_reg = self.current().alloc_register();
2565 let name_idx = self
2566 .current()
2567 .add_constant(Constant::String(Arc::from(name.as_str())));
2568 self.current()
2569 .emit_abx(Op::GetGlobal, func_reg, name_idx, 0);
2570
2571 let args_start = self.current().next_register;
2572 let first_arg = self.current().alloc_register();
2573 self.current().emit_abc(Op::Move, first_arg, left_reg, 0, 0);
2574
2575 self.current()
2576 .emit_abc(Op::Call, func_reg, args_start, 1, 0);
2577 if func_reg != dest {
2578 self.current().emit_abc(Op::Move, dest, func_reg, 0, 0);
2579 }
2580 self.current().free_register(); self.current().free_register(); }
2583 }
2584 _ => {
2585 return Err(compile_err(
2586 "Right side of |> must be a function call".to_string(),
2587 ));
2588 }
2589 }
2590
2591 self.current().free_register(); Ok(())
2593 }
2594
2595 fn compile_case(&mut self, arms: &[MatchArm], dest: u8) -> Result<(), TlError> {
2596 let mut end_jumps = Vec::new();
2597 let mut has_default = false;
2598
2599 for arm in arms {
2600 if let Some(ref guard) = arm.guard {
2601 let cond_reg = self.current().alloc_register();
2603 self.compile_expr(guard, cond_reg)?;
2604 let jump_false = self.current().current_pos();
2605 self.current().emit_abx(Op::JumpIfFalse, cond_reg, 0, 0);
2606 self.current().free_register();
2607
2608 self.compile_expr(&arm.body, dest)?;
2609 let jump_end = self.current().current_pos();
2610 self.current().emit_abx(Op::Jump, 0, 0, 0);
2611 end_jumps.push(jump_end);
2612
2613 self.current().patch_jump(jump_false);
2614 } else {
2615 self.compile_expr(&arm.body, dest)?;
2617 has_default = true;
2618 break;
2619 }
2620 }
2621
2622 if !has_default {
2623 self.current().emit_abx(Op::LoadNone, dest, 0, 0);
2624 }
2625
2626 for pos in end_jumps {
2627 self.current().patch_jump(pos);
2628 }
2629
2630 Ok(())
2631 }
2632
2633 fn compile_match(
2634 &mut self,
2635 subject: &Expr,
2636 arms: &[MatchArm],
2637 dest: u8,
2638 ) -> Result<(), TlError> {
2639 let subj_reg = self.current().alloc_register();
2640 self.compile_expr(subject, subj_reg)?;
2641
2642 let mut end_jumps = Vec::new();
2643 let mut has_unconditional = false;
2644
2645 for arm in arms {
2646 let is_unconditional = match &arm.pattern {
2648 Pattern::Wildcard => true,
2649 Pattern::Binding(_) if arm.guard.is_none() => true,
2650 Pattern::Struct { name: None, .. } if arm.guard.is_none() => true,
2651 _ => false,
2652 };
2653
2654 self.compile_match_arm(arm, subj_reg, dest, &mut end_jumps)?;
2655
2656 if is_unconditional {
2657 has_unconditional = true;
2658 break; }
2660 }
2661
2662 if !has_unconditional {
2663 self.current().emit_abx(Op::LoadNone, dest, 0, 0);
2665 self.current().free_register(); }
2667
2668 for pos in end_jumps {
2669 self.current().patch_jump(pos);
2670 }
2671
2672 Ok(())
2673 }
2674
2675 fn compile_match_arm(
2678 &mut self,
2679 arm: &MatchArm,
2680 subj_reg: u8,
2681 dest: u8,
2682 end_jumps: &mut Vec<usize>,
2683 ) -> Result<(), TlError> {
2684 match &arm.pattern {
2685 Pattern::Wildcard => {
2686 self.compile_expr(&arm.body, dest)?;
2687 self.current().free_register(); for pos in end_jumps.drain(..) {
2689 self.current().patch_jump(pos);
2690 }
2691 return Ok(());
2695 }
2696 Pattern::Binding(name) => {
2697 let local = self.add_local(name.clone());
2698 self.current().emit_abc(Op::Move, local, subj_reg, 0, 0);
2699
2700 if let Some(guard) = &arm.guard {
2701 let guard_reg = self.current().alloc_register();
2702 self.compile_expr(guard, guard_reg)?;
2703 let jf = self.current().current_pos();
2704 self.current().emit_abx(Op::JumpIfFalse, guard_reg, 0, 0);
2705 self.current().free_register();
2706
2707 self.compile_expr(&arm.body, dest)?;
2708 let jump_end = self.current().current_pos();
2709 self.current().emit_abx(Op::Jump, 0, 0, 0);
2710 end_jumps.push(jump_end);
2711 self.current().patch_jump(jf);
2712 } else {
2713 self.compile_expr(&arm.body, dest)?;
2715 self.current().free_register(); for pos in end_jumps.drain(..) {
2717 self.current().patch_jump(pos);
2718 }
2719 return Ok(());
2720 }
2721 }
2722 Pattern::Literal(expr) => {
2723 let pat_reg = self.current().alloc_register();
2724 self.compile_expr(expr, pat_reg)?;
2725 let result_reg = self.current().alloc_register();
2726 self.current()
2727 .emit_abc(Op::TestMatch, subj_reg, pat_reg, result_reg, 0);
2728
2729 let jump_false = self.current().current_pos();
2730 self.current().emit_abx(Op::JumpIfFalse, result_reg, 0, 0);
2731 self.current().free_register(); self.current().free_register(); self.compile_guard_and_body(arm, dest, end_jumps, jump_false)?;
2735 }
2736 Pattern::Enum {
2737 type_name: _,
2738 variant,
2739 args,
2740 } => {
2741 let variant_const = self
2742 .current()
2743 .add_constant(Constant::String(Arc::from(variant.as_str())));
2744 let result_reg = self.current().alloc_register();
2745 self.current().emit_abc(
2746 Op::MatchEnum,
2747 subj_reg,
2748 variant_const as u8,
2749 result_reg,
2750 0,
2751 );
2752
2753 let jump_false = self.current().current_pos();
2754 self.current().emit_abx(Op::JumpIfFalse, result_reg, 0, 0);
2755 self.current().free_register(); for (i, arg_pat) in args.iter().enumerate() {
2759 match arg_pat {
2760 Pattern::Binding(name) => {
2761 let local = self.add_local(name.clone());
2762 self.current()
2763 .emit_abc(Op::ExtractField, local, subj_reg, i as u8, 0);
2764 }
2765 Pattern::Wildcard => {}
2766 _ => {}
2767 }
2768 }
2769
2770 self.compile_guard_and_body(arm, dest, end_jumps, jump_false)?;
2771 }
2772 Pattern::Struct {
2773 name: struct_name,
2774 fields,
2775 } => {
2776 let jump_false = if let Some(sname) = struct_name {
2778 let name_const = self
2779 .current()
2780 .add_constant(Constant::String(Arc::from(sname.as_str())));
2781 let name_reg = self.current().alloc_register();
2782 self.current()
2783 .emit_abx(Op::LoadConst, name_reg, name_const, 0);
2784 let result_reg = self.current().alloc_register();
2785 self.current()
2786 .emit_abc(Op::TestMatch, subj_reg, name_reg, result_reg, 0);
2787 let jf = self.current().current_pos();
2788 self.current().emit_abx(Op::JumpIfFalse, result_reg, 0, 0);
2789 self.current().free_register(); self.current().free_register(); jf
2792 } else {
2793 usize::MAX
2794 };
2795
2796 for field in fields {
2798 let fname_const = self
2799 .current()
2800 .add_constant(Constant::String(Arc::from(field.name.as_str())));
2801 match &field.pattern {
2802 None | Some(Pattern::Binding(_)) => {
2803 let bind_name = match &field.pattern {
2804 Some(Pattern::Binding(n)) => n.clone(),
2805 _ => field.name.clone(),
2806 };
2807 let local = self.add_local(bind_name);
2808 self.current().emit_abc(
2809 Op::ExtractNamedField,
2810 local,
2811 subj_reg,
2812 fname_const as u8,
2813 0,
2814 );
2815 }
2816 Some(Pattern::Wildcard) => {}
2817 _ => {
2818 let local = self.add_local(field.name.clone());
2819 self.current().emit_abc(
2820 Op::ExtractNamedField,
2821 local,
2822 subj_reg,
2823 fname_const as u8,
2824 0,
2825 );
2826 }
2827 }
2828 }
2829
2830 if jump_false != usize::MAX {
2831 self.compile_guard_and_body(arm, dest, end_jumps, jump_false)?;
2832 } else {
2833 if let Some(guard) = &arm.guard {
2835 let guard_reg = self.current().alloc_register();
2836 self.compile_expr(guard, guard_reg)?;
2837 let gj = self.current().current_pos();
2838 self.current().emit_abx(Op::JumpIfFalse, guard_reg, 0, 0);
2839 self.current().free_register();
2840
2841 self.compile_expr(&arm.body, dest)?;
2842 let jump_end = self.current().current_pos();
2843 self.current().emit_abx(Op::Jump, 0, 0, 0);
2844 end_jumps.push(jump_end);
2845 self.current().patch_jump(gj);
2846 } else {
2847 self.compile_expr(&arm.body, dest)?;
2849 self.current().free_register(); for pos in end_jumps.drain(..) {
2851 self.current().patch_jump(pos);
2852 }
2853 return Ok(());
2854 }
2855 }
2856 }
2857 Pattern::List { elements, rest } => {
2858 let len_builtin_reg = self.current().alloc_register();
2860 self.current()
2862 .emit_abx(Op::CallBuiltin, len_builtin_reg, BuiltinId::Len as u16, 0);
2863 self.current().emit_abc(Op::Move, 1, subj_reg, 0, 0); let expected_len_reg = self.current().alloc_register();
2866 let len_val = elements.len() as i64;
2867 let len_const = self.current().add_constant(Constant::Int(len_val));
2868 self.current()
2869 .emit_abx(Op::LoadConst, expected_len_reg, len_const, 0);
2870
2871 let cmp_reg = self.current().alloc_register();
2872 if rest.is_some() {
2873 self.current()
2874 .emit_abc(Op::Gte, cmp_reg, len_builtin_reg, expected_len_reg, 0);
2875 } else {
2876 self.current()
2877 .emit_abc(Op::Eq, cmp_reg, len_builtin_reg, expected_len_reg, 0);
2878 }
2879 let jump_false = self.current().current_pos();
2880 self.current().emit_abx(Op::JumpIfFalse, cmp_reg, 0, 0);
2881 self.current().free_register(); self.current().free_register(); self.current().free_register(); for (i, elem_pat) in elements.iter().enumerate() {
2887 match elem_pat {
2888 Pattern::Binding(name) => {
2889 let local = self.add_local(name.clone());
2890 self.current()
2891 .emit_abc(Op::ExtractField, local, subj_reg, i as u8, 0);
2892 }
2893 Pattern::Wildcard => {}
2894 _ => {}
2895 }
2896 }
2897
2898 if let Some(rest_name) = rest {
2900 let local = self.add_local(rest_name.clone());
2901 self.current().emit_abc(
2902 Op::ExtractField,
2903 local,
2904 subj_reg,
2905 (elements.len() as u8) | 0x80,
2906 0,
2907 );
2908 }
2909
2910 self.compile_guard_and_body(arm, dest, end_jumps, jump_false)?;
2911 }
2912 Pattern::Or(patterns) => {
2913 let mut match_jumps = Vec::new();
2914
2915 for sub_pat in patterns {
2916 match sub_pat {
2917 Pattern::Literal(expr) => {
2918 let pat_reg = self.current().alloc_register();
2919 self.compile_expr(expr, pat_reg)?;
2920 let result_reg = self.current().alloc_register();
2921 self.current().emit_abc(
2922 Op::TestMatch,
2923 subj_reg,
2924 pat_reg,
2925 result_reg,
2926 0,
2927 );
2928 let jump_true = self.current().current_pos();
2929 self.current().emit_abx(Op::JumpIfTrue, result_reg, 0, 0);
2930 match_jumps.push(jump_true);
2931 self.current().free_register(); self.current().free_register(); }
2934 Pattern::Enum { variant, .. } => {
2935 let variant_const = self
2936 .current()
2937 .add_constant(Constant::String(Arc::from(variant.as_str())));
2938 let result_reg = self.current().alloc_register();
2939 self.current().emit_abc(
2940 Op::MatchEnum,
2941 subj_reg,
2942 variant_const as u8,
2943 result_reg,
2944 0,
2945 );
2946 let jump_true = self.current().current_pos();
2947 self.current().emit_abx(Op::JumpIfTrue, result_reg, 0, 0);
2948 match_jumps.push(jump_true);
2949 self.current().free_register();
2950 }
2951 Pattern::Wildcard | Pattern::Binding(_) => {
2952 let jump = self.current().current_pos();
2953 self.current().emit_abx(Op::Jump, 0, 0, 0);
2954 match_jumps.push(jump);
2955 }
2956 _ => {}
2957 }
2958 }
2959
2960 let jump_skip = self.current().current_pos();
2962 self.current().emit_abx(Op::Jump, 0, 0, 0);
2963
2964 for jt in &match_jumps {
2966 self.current().patch_jump(*jt);
2967 }
2968
2969 if let Some(guard) = &arm.guard {
2971 let guard_reg = self.current().alloc_register();
2972 self.compile_expr(guard, guard_reg)?;
2973 let gj = self.current().current_pos();
2974 self.current().emit_abx(Op::JumpIfFalse, guard_reg, 0, 0);
2975 self.current().free_register();
2976
2977 self.compile_expr(&arm.body, dest)?;
2978 let jump_end = self.current().current_pos();
2979 self.current().emit_abx(Op::Jump, 0, 0, 0);
2980 end_jumps.push(jump_end);
2981 self.current().patch_jump(gj);
2982 } else {
2983 self.compile_expr(&arm.body, dest)?;
2984 let jump_end = self.current().current_pos();
2985 self.current().emit_abx(Op::Jump, 0, 0, 0);
2986 end_jumps.push(jump_end);
2987 }
2988
2989 self.current().patch_jump(jump_skip);
2990 }
2991 }
2992 Ok(())
2993 }
2994
2995 fn compile_guard_and_body(
2997 &mut self,
2998 arm: &MatchArm,
2999 dest: u8,
3000 end_jumps: &mut Vec<usize>,
3001 jump_false: usize,
3002 ) -> Result<(), TlError> {
3003 let guard_jump = if let Some(guard) = &arm.guard {
3004 let guard_reg = self.current().alloc_register();
3005 self.compile_expr(guard, guard_reg)?;
3006 let jf = self.current().current_pos();
3007 self.current().emit_abx(Op::JumpIfFalse, guard_reg, 0, 0);
3008 self.current().free_register();
3009 Some(jf)
3010 } else {
3011 None
3012 };
3013
3014 self.compile_expr(&arm.body, dest)?;
3015 let jump_end = self.current().current_pos();
3016 self.current().emit_abx(Op::Jump, 0, 0, 0);
3017 end_jumps.push(jump_end);
3018
3019 self.current().patch_jump(jump_false);
3020 if let Some(gj) = guard_jump {
3021 self.current().patch_jump(gj);
3022 }
3023 Ok(())
3024 }
3025
3026 fn compile_string_interpolation(&mut self, s: &str, dest: u8) -> Result<(), TlError> {
3029 let mut segments: Vec<StringSegment> = Vec::new();
3031 let mut chars = s.chars().peekable();
3032 let mut current_literal = String::new();
3033
3034 while let Some(ch) = chars.next() {
3035 if ch == '{' {
3036 let mut var_name = String::new();
3037 let mut depth = 1;
3038 for c in chars.by_ref() {
3039 if c == '{' {
3040 depth += 1;
3041 } else if c == '}' {
3042 depth -= 1;
3043 if depth == 0 {
3044 break;
3045 }
3046 }
3047 var_name.push(c);
3048 }
3049 if !current_literal.is_empty() {
3050 segments.push(StringSegment::Literal(std::mem::take(&mut current_literal)));
3051 }
3052 segments.push(StringSegment::Variable(var_name));
3053 } else if ch == '\\' {
3054 match chars.next() {
3055 Some('n') => current_literal.push('\n'),
3056 Some('t') => current_literal.push('\t'),
3057 Some('\\') => current_literal.push('\\'),
3058 Some('"') => current_literal.push('"'),
3059 Some(c) => {
3060 current_literal.push('\\');
3061 current_literal.push(c);
3062 }
3063 None => current_literal.push('\\'),
3064 }
3065 } else {
3066 current_literal.push(ch);
3067 }
3068 }
3069 if !current_literal.is_empty() {
3070 segments.push(StringSegment::Literal(current_literal));
3071 }
3072
3073 if segments.is_empty() {
3074 let idx = self.current().add_constant(Constant::String(Arc::from("")));
3076 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
3077 return Ok(());
3078 }
3079
3080 if segments.len() == 1
3082 && let StringSegment::Literal(ref lit) = segments[0]
3083 {
3084 let idx = self
3085 .current()
3086 .add_constant(Constant::String(Arc::from(lit.as_str())));
3087 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
3088 return Ok(());
3089 }
3090
3091 self.compile_string_segment(&segments[0], dest)?;
3093
3094 for segment in &segments[1..] {
3096 let tmp = self.current().alloc_register();
3097 self.compile_string_segment(segment, tmp)?;
3098 self.current().emit_abc(Op::Concat, dest, dest, tmp, 0);
3099 self.current().free_register();
3100 }
3101
3102 Ok(())
3103 }
3104
3105 fn compile_string_segment(&mut self, seg: &StringSegment, dest: u8) -> Result<(), TlError> {
3106 match seg {
3107 StringSegment::Literal(s) => {
3108 let idx = self
3109 .current()
3110 .add_constant(Constant::String(Arc::from(s.as_str())));
3111 self.current().emit_abx(Op::LoadConst, dest, idx, 0);
3112 }
3113 StringSegment::Variable(name) => {
3114 let var_reg = self.current().alloc_register();
3116 if let Some(reg) = self.resolve_local(name) {
3117 if reg != var_reg {
3118 self.current().emit_abc(Op::Move, var_reg, reg, 0, 0);
3119 }
3120 } else if let Some(uv) = self.resolve_upvalue(name) {
3121 self.current().emit_abc(Op::GetUpvalue, var_reg, uv, 0, 0);
3122 } else {
3123 let idx = self
3124 .current()
3125 .add_constant(Constant::String(Arc::from(name.as_str())));
3126 self.current().emit_abx(Op::GetGlobal, var_reg, idx, 0);
3127 }
3128 self.current()
3130 .emit_abx(Op::CallBuiltin, dest, BuiltinId::Str as u16, 0);
3131 self.current().emit_abc(Op::Move, 1, var_reg, 0, 0); self.current().free_register(); }
3134 }
3135 Ok(())
3136 }
3137
3138 fn compile_use(&mut self, item: &UseItem) -> Result<(), TlError> {
3139 let reg = self.current().alloc_register();
3142 match item {
3143 UseItem::Single(path) => {
3144 let path_str = path.join(".");
3145 let path_idx = self
3146 .current()
3147 .add_constant(Constant::String(Arc::from(path_str.as_str())));
3148 self.current().emit_abx(Op::Import, reg, path_idx, 0);
3149 self.current().emit_abc(Op::Move, 0, 0, 0xAB, 0);
3151 }
3152 UseItem::Group(prefix, names) => {
3153 let path_str = format!("{}.{{{}}}", prefix.join("."), names.join(","));
3154 let path_idx = self
3155 .current()
3156 .add_constant(Constant::String(Arc::from(path_str.as_str())));
3157 self.current().emit_abx(Op::Import, reg, path_idx, 0);
3158 self.current().emit_abc(Op::Move, 0, 1, 0xAB, 0); }
3160 UseItem::Wildcard(path) => {
3161 let path_str = format!("{}.*", path.join("."));
3162 let path_idx = self
3163 .current()
3164 .add_constant(Constant::String(Arc::from(path_str.as_str())));
3165 self.current().emit_abx(Op::Import, reg, path_idx, 0);
3166 self.current().emit_abc(Op::Move, 0, 2, 0xAB, 0); }
3168 UseItem::Aliased(path, alias) => {
3169 let path_str = path.join(".");
3170 let path_idx = self
3171 .current()
3172 .add_constant(Constant::String(Arc::from(path_str.as_str())));
3173 let alias_idx = self
3174 .current()
3175 .add_constant(Constant::String(Arc::from(alias.as_str())));
3176 self.current().emit_abx(Op::Import, reg, path_idx, 0);
3177 self.current()
3178 .emit_abc(Op::Move, alias_idx as u8, 3, 0xAB, 0); }
3180 }
3181 self.current().free_register();
3182 Ok(())
3183 }
3184}
3185
3186enum StringSegment {
3187 Literal(String),
3188 Variable(String),
3189}
3190
3191pub fn compile(program: &Program) -> Result<Prototype, TlError> {
3194 compile_with_source(program, "")
3195}
3196
3197pub fn compile_with_source(program: &Program, source: &str) -> Result<Prototype, TlError> {
3199 let line_offsets = Compiler::build_line_offsets(source);
3200 let mut compiler = Compiler {
3201 states: vec![CompilerState::new("<main>".to_string())],
3202 line_offsets,
3203 current_line: 0,
3204 };
3205
3206 let stmts = &program.statements;
3207 let mut last_expr_reg: Option<u8> = None;
3208
3209 for (i, stmt) in stmts.iter().enumerate() {
3210 let is_last = i == stmts.len() - 1;
3211 let line = compiler.line_of(stmt.span.start);
3213 compiler.current_line = line;
3214 compiler.current().current_line = line;
3215 match &stmt.kind {
3216 StmtKind::Expr(expr) if is_last => {
3217 let reg = compiler.current().alloc_register();
3219 compiler.compile_expr(expr, reg)?;
3220 last_expr_reg = Some(reg);
3221 }
3222 StmtKind::If {
3223 condition,
3224 then_body,
3225 else_ifs,
3226 else_body,
3227 } if is_last && else_body.is_some() => {
3228 let dest = compiler.current().alloc_register();
3229 compiler.compile_if_as_expr(condition, then_body, else_ifs, else_body, dest)?;
3230 last_expr_reg = Some(dest);
3231 }
3232 _ => {
3233 compiler.compile_stmt(stmt)?;
3234 }
3235 }
3236 }
3237
3238 {
3240 let state = &compiler.states[0];
3241 let top_locals: Vec<(String, u8)> = state
3242 .locals
3243 .iter()
3244 .filter(|l| l.depth == 0)
3245 .map(|l| (l.name.clone(), l.register))
3246 .collect();
3247 compiler.states[0].proto.top_level_locals = top_locals;
3248 }
3249
3250 let state = &mut compiler.states[0];
3252 let needs_return = if state.proto.code.is_empty() {
3253 true
3254 } else {
3255 let last = *state.proto.code.last().unwrap();
3256 decode_op(last) != Op::Return
3257 };
3258 if needs_return {
3259 if let Some(reg) = last_expr_reg {
3260 state.emit_abc(Op::Return, reg, 0, 0, 0);
3261 } else {
3262 let reg = state.alloc_register();
3263 state.emit_abx(Op::LoadNone, reg, 0, 0);
3264 state.emit_abc(Op::Return, reg, 0, 0, 0);
3265 }
3266 }
3267
3268 let state = compiler.states.pop().unwrap();
3269 Ok(state.proto)
3270}
3271
3272#[cfg(test)]
3273mod tests {
3274 use super::*;
3275 use tl_parser::parse;
3276
3277 #[test]
3278 fn test_compile_int_literal() {
3279 let program = parse("42").unwrap();
3280 let proto = compile(&program).unwrap();
3281 assert!(!proto.code.is_empty());
3282 assert!(
3283 proto
3284 .constants
3285 .iter()
3286 .any(|c| matches!(c, Constant::Int(42)))
3287 );
3288 }
3289
3290 #[test]
3291 fn test_compile_add() {
3292 let program = parse("1 + 2").unwrap();
3294 let proto = compile(&program).unwrap();
3295 assert!(!proto.code.is_empty());
3296 assert!(
3298 proto
3299 .constants
3300 .iter()
3301 .any(|c| matches!(c, Constant::Int(3)))
3302 );
3303 }
3304
3305 #[test]
3306 fn test_compile_function() {
3307 let program = parse("fn add(a, b) { a + b }").unwrap();
3308 let proto = compile(&program).unwrap();
3309 let has_closure = proto
3311 .code
3312 .iter()
3313 .any(|&inst| decode_op(inst) == Op::Closure);
3314 assert!(has_closure);
3315 }
3316
3317 #[test]
3318 fn test_compile_closure() {
3319 let program = parse("let f = (x) => x * 2").unwrap();
3320 let proto = compile(&program).unwrap();
3321 let has_closure = proto
3322 .code
3323 .iter()
3324 .any(|&inst| decode_op(inst) == Op::Closure);
3325 assert!(has_closure);
3326 }
3327
3328 #[test]
3331 fn test_fold_int_addition() {
3332 let program = parse("2 + 3").unwrap();
3334 let proto = compile(&program).unwrap();
3335 assert!(
3336 proto
3337 .constants
3338 .iter()
3339 .any(|c| matches!(c, Constant::Int(5)))
3340 );
3341 let has_add = proto.code.iter().any(|&inst| decode_op(inst) == Op::Add);
3343 assert!(
3344 !has_add,
3345 "Folded expression should not have Add instruction"
3346 );
3347 }
3348
3349 #[test]
3350 fn test_fold_float_multiplication() {
3351 let program = parse("2.0 * 3.0").unwrap();
3352 let proto = compile(&program).unwrap();
3353 assert!(
3354 proto
3355 .constants
3356 .iter()
3357 .any(|c| matches!(c, Constant::Float(f) if (*f - 6.0).abs() < f64::EPSILON))
3358 );
3359 }
3360
3361 #[test]
3362 fn test_fold_string_concatenation() {
3363 let program = parse("\"hello\" + \" world\"").unwrap();
3364 let proto = compile(&program).unwrap();
3365 assert!(
3366 proto
3367 .constants
3368 .iter()
3369 .any(|c| matches!(c, Constant::String(s) if s.as_ref() == "hello world"))
3370 );
3371 }
3372
3373 #[test]
3374 fn test_fold_negation() {
3375 let program = parse("-42").unwrap();
3376 let proto = compile(&program).unwrap();
3377 assert!(
3378 proto
3379 .constants
3380 .iter()
3381 .any(|c| matches!(c, Constant::Int(-42)))
3382 );
3383 }
3384
3385 #[test]
3386 fn test_fold_not() {
3387 let program = parse("not true").unwrap();
3388 let proto = compile(&program).unwrap();
3389 let has_load_false = proto
3391 .code
3392 .iter()
3393 .any(|&inst| decode_op(inst) == Op::LoadFalse);
3394 assert!(has_load_false, "'not true' should fold to false");
3395 }
3396
3397 #[test]
3398 fn test_fold_nested_arithmetic() {
3399 let program = parse("2 + 3 * 4").unwrap();
3401 let proto = compile(&program).unwrap();
3402 assert!(
3403 proto
3404 .constants
3405 .iter()
3406 .any(|c| matches!(c, Constant::Int(14)))
3407 );
3408 }
3409
3410 #[test]
3411 fn test_no_fold_division_by_zero() {
3412 let program = parse("10 / 0").unwrap();
3414 let proto = compile(&program).unwrap();
3415 let has_div = proto.code.iter().any(|&inst| decode_op(inst) == Op::Div);
3417 assert!(has_div, "Division by zero should not be folded");
3418 }
3419
3420 #[test]
3421 fn test_no_fold_variable_reference() {
3422 let program = parse("let x = 5\nx + 1").unwrap();
3424 let proto = compile(&program).unwrap();
3425 let has_add = proto.code.iter().any(|&inst| decode_op(inst) == Op::Add);
3426 assert!(has_add, "Expression with variables should not be folded");
3427 }
3428
3429 #[test]
3430 fn test_no_fold_interpolated_string() {
3431 let program = parse("let x = 5\n\"{x} hello\"").unwrap();
3433 let proto = compile(&program).unwrap();
3434 assert!(!proto.code.is_empty());
3437 }
3438
3439 #[test]
3440 fn test_fold_transparent_to_runtime() {
3441 use crate::Vm;
3444 let program = parse("2 + 3 * 4").unwrap();
3445 let proto = compile(&program).unwrap();
3446 let mut vm = Vm::new();
3447 let result = vm.execute(&proto).unwrap();
3448 assert_eq!(result.to_string(), "14");
3449 }
3450
3451 #[test]
3454 fn test_dce_after_return() {
3455 let program = parse("fn f() {\n return 1\n print(\"dead\")\n}").unwrap();
3457 let proto = compile(&program).unwrap();
3458 let fn_proto = proto
3460 .constants
3461 .iter()
3462 .find_map(|c| {
3463 if let Constant::Prototype(p) = c {
3464 Some(p.clone())
3465 } else {
3466 None
3467 }
3468 })
3469 .expect("should have function prototype");
3470 let return_pos = fn_proto
3472 .code
3473 .iter()
3474 .position(|&inst| decode_op(inst) == Op::Return);
3475 assert!(return_pos.is_some(), "Should have a Return instruction");
3476 let after_return: Vec<_> = fn_proto.code[return_pos.unwrap() + 1..]
3478 .iter()
3479 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3480 .collect();
3481 assert!(
3482 after_return.is_empty(),
3483 "Should not have CallBuiltin after Return"
3484 );
3485 }
3486
3487 #[test]
3488 fn test_dce_after_break() {
3489 let program =
3491 parse("fn f() {\n while true {\n break\n print(\"dead\")\n }\n}").unwrap();
3492 let proto = compile(&program).unwrap();
3493 let fn_proto = proto
3494 .constants
3495 .iter()
3496 .find_map(|c| {
3497 if let Constant::Prototype(p) = c {
3498 Some(p.clone())
3499 } else {
3500 None
3501 }
3502 })
3503 .expect("should have function prototype");
3504 let call_builtins: Vec<_> = fn_proto
3506 .code
3507 .iter()
3508 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3509 .collect();
3510 assert!(
3511 call_builtins.is_empty(),
3512 "Should not have CallBuiltin after break"
3513 );
3514 }
3515
3516 #[test]
3517 fn test_dce_after_continue() {
3518 let program =
3520 parse("fn f() {\n while true {\n continue\n print(\"dead\")\n }\n}").unwrap();
3521 let proto = compile(&program).unwrap();
3522 let fn_proto = proto
3523 .constants
3524 .iter()
3525 .find_map(|c| {
3526 if let Constant::Prototype(p) = c {
3527 Some(p.clone())
3528 } else {
3529 None
3530 }
3531 })
3532 .expect("should have function prototype");
3533 let call_builtins: Vec<_> = fn_proto
3534 .code
3535 .iter()
3536 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3537 .collect();
3538 assert!(
3539 call_builtins.is_empty(),
3540 "Should not have CallBuiltin after continue"
3541 );
3542 }
3543
3544 #[test]
3545 fn test_dce_if_both_branches_return() {
3546 let program = parse("fn f(x) {\n if x {\n return 1\n } else {\n return 2\n }\n print(\"dead\")\n}").unwrap();
3548 let proto = compile(&program).unwrap();
3549 let fn_proto = proto
3550 .constants
3551 .iter()
3552 .find_map(|c| {
3553 if let Constant::Prototype(p) = c {
3554 Some(p.clone())
3555 } else {
3556 None
3557 }
3558 })
3559 .expect("should have function prototype");
3560 let call_builtins: Vec<_> = fn_proto
3561 .code
3562 .iter()
3563 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3564 .collect();
3565 assert!(
3566 call_builtins.is_empty(),
3567 "Should not have CallBuiltin after if where both branches return"
3568 );
3569 }
3570
3571 #[test]
3572 fn test_dce_if_one_branch_returns() {
3573 let program =
3575 parse("fn f(x) {\n if x {\n return 1\n }\n print(\"alive\")\n return 0\n}")
3576 .unwrap();
3577 let proto = compile(&program).unwrap();
3578 let fn_proto = proto
3579 .constants
3580 .iter()
3581 .find_map(|c| {
3582 if let Constant::Prototype(p) = c {
3583 Some(p.clone())
3584 } else {
3585 None
3586 }
3587 })
3588 .expect("should have function prototype");
3589 let call_builtins: Vec<_> = fn_proto
3590 .code
3591 .iter()
3592 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3593 .collect();
3594 assert!(
3595 !call_builtins.is_empty(),
3596 "Should have CallBuiltin after if where only one branch returns"
3597 );
3598 }
3599
3600 #[test]
3601 fn test_dce_nested_function() {
3602 let program = parse("fn outer() {\n fn inner() {\n return 1\n print(\"dead\")\n }\n print(\"alive\")\n}").unwrap();
3604 let proto = compile(&program).unwrap();
3605 let outer_fn = proto
3607 .constants
3608 .iter()
3609 .find_map(|c| {
3610 if let Constant::Prototype(p) = c {
3611 if p.name == "outer" {
3612 Some(p.clone())
3613 } else {
3614 None
3615 }
3616 } else {
3617 None
3618 }
3619 })
3620 .expect("should have outer function");
3621 let call_builtins: Vec<_> = outer_fn
3622 .code
3623 .iter()
3624 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3625 .collect();
3626 assert!(
3627 !call_builtins.is_empty(),
3628 "Outer function should have CallBuiltin"
3629 );
3630 }
3631
3632 #[test]
3633 fn test_dce_try_catch_independent() {
3634 let program =
3636 parse("fn f() {\n try {\n return 1\n } catch e {\n print(e)\n }\n}").unwrap();
3637 let proto = compile(&program).unwrap();
3638 let fn_proto = proto
3639 .constants
3640 .iter()
3641 .find_map(|c| {
3642 if let Constant::Prototype(p) = c {
3643 Some(p.clone())
3644 } else {
3645 None
3646 }
3647 })
3648 .expect("should have function prototype");
3649 let call_builtins: Vec<_> = fn_proto
3651 .code
3652 .iter()
3653 .filter(|&&inst| decode_op(inst) == Op::CallBuiltin)
3654 .collect();
3655 assert!(
3656 !call_builtins.is_empty(),
3657 "Catch block should not be eliminated by try body return"
3658 );
3659 }
3660
3661 #[test]
3662 fn test_dce_existing_tests_unaffected() {
3663 use crate::Vm;
3665 let tests = [
3666 ("let x = 42\nx", "42"),
3667 ("fn add(a, b) { a + b }\nadd(1, 2)", "3"),
3668 ("if true { 1 } else { 2 }", "1"),
3669 ];
3670 for (src, expected) in tests {
3671 let program = parse(src).unwrap();
3672 let proto = compile(&program).unwrap();
3673 let mut vm = Vm::new();
3674 let result = vm.execute(&proto).unwrap();
3675 assert_eq!(result.to_string(), expected, "Failed for: {src}");
3676 }
3677 }
3678}