1use std::collections::{HashMap, HashSet};
11use std::hash::{Hash, Hasher};
12
13use crate::analysis::DiscoveryPass;
14use crate::arena::Arena;
15use crate::arena_ctx::AstContext;
16use crate::ast::stmt::{BinaryOpKind, Block, Expr, Literal, Stmt, TypeExpr};
17use crate::drs::WorldState;
18use crate::error::ParseError;
19use crate::intern::{Interner, Symbol};
20use crate::lexer::Lexer;
21use crate::parser::Parser;
22
23#[derive(Debug, Clone)]
28pub enum BindingTime {
29 Static(Literal),
30 Dynamic,
31}
32
33impl PartialEq for BindingTime {
34 fn eq(&self, other: &Self) -> bool {
35 match (self, other) {
36 (BindingTime::Static(a), BindingTime::Static(b)) => match (a, b) {
37 (Literal::Float(x), Literal::Float(y)) => x.to_bits() == y.to_bits(),
38 _ => a == b,
39 },
40 (BindingTime::Dynamic, BindingTime::Dynamic) => true,
41 _ => false,
42 }
43 }
44}
45
46impl Eq for BindingTime {}
47
48impl Hash for BindingTime {
49 fn hash<H: Hasher>(&self, state: &mut H) {
50 std::mem::discriminant(self).hash(state);
51 match self {
52 BindingTime::Static(lit) => hash_literal(lit, state),
53 BindingTime::Dynamic => {}
54 }
55 }
56}
57
58fn hash_literal<H: Hasher>(lit: &Literal, state: &mut H) {
59 std::mem::discriminant(lit).hash(state);
60 match lit {
61 Literal::Number(n) => n.hash(state),
62 Literal::Float(f) => f.to_bits().hash(state),
63 Literal::Text(s) => s.hash(state),
64 Literal::Boolean(b) => b.hash(state),
65 Literal::Nothing => {}
66 Literal::Char(c) => c.hash(state),
67 Literal::Duration(d) => d.hash(state),
68 Literal::Date(d) => d.hash(state),
69 Literal::Moment(m) => m.hash(state),
70 Literal::Span { months, days } => {
71 months.hash(state);
72 days.hash(state);
73 }
74 Literal::Time(t) => t.hash(state),
75 }
76}
77
78impl BindingTime {
79 pub fn is_static(&self) -> bool {
80 matches!(self, BindingTime::Static(_))
81 }
82
83 pub fn is_dynamic(&self) -> bool {
84 matches!(self, BindingTime::Dynamic)
85 }
86}
87
88pub type Division = HashMap<Symbol, BindingTime>;
89
90#[derive(Debug, Clone, PartialEq)]
91pub struct BtaResult {
92 pub division: Division,
93 pub return_bt: BindingTime,
94}
95
96pub type BtaCache = HashMap<(Symbol, Vec<BindingTime>), BtaResult>;
97
98pub fn analyze_expr(expr: &Expr, division: &Division) -> BindingTime {
103 match expr {
104 Expr::Literal(lit) => BindingTime::Static(lit.clone()),
105
106 Expr::Identifier(sym) => {
107 division.get(sym).cloned().unwrap_or(BindingTime::Dynamic)
108 }
109
110 Expr::BinaryOp { op, left, right } => {
111 let bt_left = analyze_expr(left, division);
112 let bt_right = analyze_expr(right, division);
113 match (&bt_left, &bt_right) {
114 (BindingTime::Static(l), BindingTime::Static(r)) => {
115 match eval_literal_binop(*op, l, r) {
116 Some(result) => BindingTime::Static(result),
117 None => BindingTime::Dynamic,
118 }
119 }
120 _ => BindingTime::Dynamic,
121 }
122 }
123
124 Expr::Not { operand } => {
125 match analyze_expr(operand, division) {
126 BindingTime::Static(Literal::Boolean(b)) => {
127 BindingTime::Static(Literal::Boolean(!b))
128 }
129 _ => BindingTime::Dynamic,
130 }
131 }
132
133 Expr::Length { .. } => BindingTime::Dynamic,
134 Expr::Index { .. } => BindingTime::Dynamic,
135
136 Expr::Call { .. } => BindingTime::Dynamic,
137
138 _ => BindingTime::Dynamic,
139 }
140}
141
142fn eval_literal_binop(op: BinaryOpKind, left: &Literal, right: &Literal) -> Option<Literal> {
143 match (op, left, right) {
144 (BinaryOpKind::Add, Literal::Number(a), Literal::Number(b)) => {
146 a.checked_add(*b).map(Literal::Number)
147 }
148 (BinaryOpKind::Subtract, Literal::Number(a), Literal::Number(b)) => {
149 a.checked_sub(*b).map(Literal::Number)
150 }
151 (BinaryOpKind::Multiply, Literal::Number(a), Literal::Number(b)) => {
152 a.checked_mul(*b).map(Literal::Number)
153 }
154 (BinaryOpKind::Divide, Literal::Number(a), Literal::Number(b)) if *b != 0 => {
155 a.checked_div(*b).map(Literal::Number)
156 }
157 (BinaryOpKind::Modulo, Literal::Number(a), Literal::Number(b)) if *b != 0 => {
158 a.checked_rem(*b).map(Literal::Number)
159 }
160 (BinaryOpKind::Eq, Literal::Number(a), Literal::Number(b)) => {
161 Some(Literal::Boolean(a == b))
162 }
163 (BinaryOpKind::NotEq, Literal::Number(a), Literal::Number(b)) => {
164 Some(Literal::Boolean(a != b))
165 }
166 (BinaryOpKind::Lt, Literal::Number(a), Literal::Number(b)) => {
167 Some(Literal::Boolean(a < b))
168 }
169 (BinaryOpKind::Gt, Literal::Number(a), Literal::Number(b)) => {
170 Some(Literal::Boolean(a > b))
171 }
172 (BinaryOpKind::LtEq, Literal::Number(a), Literal::Number(b)) => {
173 Some(Literal::Boolean(a <= b))
174 }
175 (BinaryOpKind::GtEq, Literal::Number(a), Literal::Number(b)) => {
176 Some(Literal::Boolean(a >= b))
177 }
178 (BinaryOpKind::Add, Literal::Float(a), Literal::Float(b)) => {
179 Some(Literal::Float(a + b))
180 }
181 (BinaryOpKind::Subtract, Literal::Float(a), Literal::Float(b)) => {
182 Some(Literal::Float(a - b))
183 }
184 (BinaryOpKind::Multiply, Literal::Float(a), Literal::Float(b)) => {
185 Some(Literal::Float(a * b))
186 }
187 (BinaryOpKind::Divide, Literal::Float(a), Literal::Float(b)) if *b != 0.0 => {
188 Some(Literal::Float(a / b))
189 }
190 (BinaryOpKind::And, Literal::Boolean(a), Literal::Boolean(b)) => {
191 Some(Literal::Boolean(*a && *b))
192 }
193 (BinaryOpKind::Or, Literal::Boolean(a), Literal::Boolean(b)) => {
194 Some(Literal::Boolean(*a || *b))
195 }
196 _ => None,
197 }
198}
199
200fn analyze_block<'a>(
205 stmts: &[Stmt<'a>],
206 division: &mut Division,
207 funcs: &HashMap<Symbol, FuncDef<'a>>,
208 cache: &mut BtaCache,
209) -> BindingTime {
210 let mut return_bt = BindingTime::Dynamic;
211 for stmt in stmts {
212 if let Some(bt) = analyze_stmt(stmt, division, funcs, cache) {
213 return_bt = bt;
214 }
215 }
216 return_bt
217}
218
219fn analyze_stmt<'a>(
220 stmt: &Stmt<'a>,
221 division: &mut Division,
222 funcs: &HashMap<Symbol, FuncDef<'a>>,
223 cache: &mut BtaCache,
224) -> Option<BindingTime> {
225 match stmt {
226 Stmt::Let { var, value, .. } => {
227 let bt = analyze_expr(value, division);
228 division.insert(*var, bt);
229 None
230 }
231 Stmt::Set { target, value } => {
232 let bt = analyze_expr(value, division);
233 division.insert(*target, bt);
234 None
235 }
236 Stmt::Return { value } => {
237 let bt = match value {
238 Some(expr) => analyze_expr(expr, division),
239 None => BindingTime::Static(Literal::Nothing),
240 };
241 Some(bt)
242 }
243 Stmt::Show { .. } => None,
244 Stmt::Call { function, args } => {
245 for arg in args {
246 analyze_expr(arg, division);
247 }
248 None
249 }
250 other => {
251 for var in effectful_bound_vars(other) {
254 division.insert(var, BindingTime::Dynamic);
255 }
256 None
257 }
258 }
259}
260
261struct FuncDef<'a> {
266 params: Vec<(Symbol, &'a TypeExpr<'a>)>,
267 body: Block<'a>,
268}
269
270pub fn analyze_function_bt<'a>(
271 func_name: Symbol,
272 params: &[(Symbol, &'a TypeExpr<'a>)],
273 body: &[Stmt<'a>],
274 arg_bts: &[BindingTime],
275 funcs: &HashMap<Symbol, FuncDef<'a>>,
276 cache: &mut BtaCache,
277) -> BtaResult {
278 let cache_key = (func_name, arg_bts.to_vec());
279 if let Some(cached) = cache.get(&cache_key) {
280 return cached.clone();
281 }
282
283 let mut division = Division::new();
284 for (i, (param_sym, _)) in params.iter().enumerate() {
285 if let Some(bt) = arg_bts.get(i) {
286 division.insert(*param_sym, bt.clone());
287 } else {
288 division.insert(*param_sym, BindingTime::Dynamic);
289 }
290 }
291
292 let return_bt = analyze_block(body, &mut division, funcs, cache);
293
294 let result = BtaResult {
295 division,
296 return_bt,
297 };
298 cache.insert(cache_key, result.clone());
299 result
300}
301
302pub fn analyze_with_sccs<'a>(
307 stmts: &[Stmt<'a>],
308 interner: &Interner,
309) -> BtaCache {
310 use crate::analysis::callgraph::CallGraph;
311
312 let cg = CallGraph::build(stmts, interner);
313
314 let mut funcs: HashMap<Symbol, FuncDef<'a>> = HashMap::new();
315 for stmt in stmts {
316 if let Stmt::FunctionDef { name, params, body, is_native: false, .. } = stmt {
317 funcs.insert(*name, FuncDef { params: params.clone(), body });
318 }
319 }
320
321 let mut cache = BtaCache::new();
322
323 for scc in cg.sccs.iter().rev() {
325 if scc.len() == 1 {
326 let sym = scc[0];
328 if let Some(func_def) = funcs.get(&sym) {
329 let arg_bts: Vec<BindingTime> = func_def.params.iter()
330 .map(|_| BindingTime::Dynamic)
331 .collect();
332 analyze_function_bt(sym, &func_def.params, func_def.body, &arg_bts, &funcs, &mut cache);
333 }
334 } else {
335 for _iteration in 0..10 {
337 let mut changed = false;
338 for &sym in scc {
339 if let Some(func_def) = funcs.get(&sym) {
340 let arg_bts: Vec<BindingTime> = func_def.params.iter()
341 .map(|_| BindingTime::Dynamic)
342 .collect();
343 let key = (sym, arg_bts.clone());
344 let old = cache.get(&key).cloned();
345 let result = analyze_function_bt(sym, &func_def.params, func_def.body, &arg_bts, &funcs, &mut cache);
346 if old.as_ref() != Some(&result) {
347 changed = true;
348 }
349 }
350 }
351 if !changed {
352 break;
353 }
354 }
355 }
356 }
357
358 cache
359}
360
361pub struct BtaEnv {
366 interner: Interner,
367 main_stmts: Vec<BtaStmt>,
368 functions: Vec<BtaFunc>,
369}
370
371#[derive(Clone)]
372struct BtaStmt {
373 kind: BtaStmtKind,
374}
375
376#[derive(Clone)]
377enum BtaStmtKind {
378 Let { var: Symbol, value: BtaExpr },
379 Set { target: Symbol, value: BtaExpr },
380 Return { value: Option<BtaExpr> },
381 Show,
382 Call { function: Symbol, args: Vec<BtaExpr> },
383 If { cond: BtaExpr, then_block: Vec<BtaStmt>, else_block: Option<Vec<BtaStmt>> },
384 While { cond: BtaExpr, body: Vec<BtaStmt> },
385 ClobberDynamic { vars: Vec<Symbol> },
389 Other,
390}
391
392#[derive(Clone)]
393enum BtaExpr {
394 Literal(Literal),
395 Identifier(Symbol),
396 BinaryOp { op: BinaryOpKind, left: Box<BtaExpr>, right: Box<BtaExpr> },
397 Not { operand: Box<BtaExpr> },
398 Length { collection: Box<BtaExpr> },
399 Index { collection: Box<BtaExpr>, index: Box<BtaExpr> },
400 Call { function: Symbol, args: Vec<BtaExpr> },
401 Other,
402}
403
404#[derive(Clone)]
405struct BtaFunc {
406 name: Symbol,
407 params: Vec<(Symbol, bool)>, body: Vec<BtaStmt>,
409}
410
411struct BtaContext {
412 cache: BtaCache,
413 on_stack: HashSet<(Symbol, Vec<BindingTime>)>,
414}
415
416fn convert_expr(expr: &Expr) -> BtaExpr {
417 match expr {
418 Expr::Literal(lit) => BtaExpr::Literal(lit.clone()),
419 Expr::Identifier(sym) => BtaExpr::Identifier(*sym),
420 Expr::BinaryOp { op, left, right } => BtaExpr::BinaryOp {
421 op: *op,
422 left: Box::new(convert_expr(left)),
423 right: Box::new(convert_expr(right)),
424 },
425 Expr::Not { operand } => BtaExpr::Not {
426 operand: Box::new(convert_expr(operand)),
427 },
428 Expr::Length { collection } => BtaExpr::Length {
429 collection: Box::new(convert_expr(collection)),
430 },
431 Expr::Index { collection, index } => BtaExpr::Index {
432 collection: Box::new(convert_expr(collection)),
433 index: Box::new(convert_expr(index)),
434 },
435 Expr::Call { function, args } => BtaExpr::Call {
436 function: *function,
437 args: args.iter().map(|a| convert_expr(a)).collect(),
438 },
439 _ => BtaExpr::Other,
440 }
441}
442
443fn effectful_bound_vars(stmt: &Stmt) -> Vec<Symbol> {
447 match stmt {
448 Stmt::ReceivePipe { var, .. }
449 | Stmt::TryReceivePipe { var, .. }
450 | Stmt::ReadFrom { var, .. }
451 | Stmt::Mount { var, .. }
452 | Stmt::CreatePipe { var, .. }
453 | Stmt::LetPeerAgent { var, .. } => vec![*var],
454 Stmt::AwaitMessage { into, .. } => vec![*into],
455 Stmt::LaunchTaskWithHandle { handle, .. } => vec![*handle],
456 Stmt::Spawn { name, .. } => vec![*name],
457 _ => Vec::new(),
458 }
459}
460
461fn convert_stmt(stmt: &Stmt) -> BtaStmt {
462 let clobbered = effectful_bound_vars(stmt);
463 if !clobbered.is_empty() {
464 return BtaStmt { kind: BtaStmtKind::ClobberDynamic { vars: clobbered } };
465 }
466 let kind = match stmt {
467 Stmt::Let { var, value, .. } => BtaStmtKind::Let {
468 var: *var,
469 value: convert_expr(value),
470 },
471 Stmt::Set { target, value } => BtaStmtKind::Set {
472 target: *target,
473 value: convert_expr(value),
474 },
475 Stmt::Return { value } => BtaStmtKind::Return {
476 value: value.map(|v| convert_expr(v)),
477 },
478 Stmt::Show { .. } => BtaStmtKind::Show,
479 Stmt::Call { function, args } => BtaStmtKind::Call {
480 function: *function,
481 args: args.iter().map(|a| convert_expr(a)).collect(),
482 },
483 Stmt::If { cond, then_block, else_block } => BtaStmtKind::If {
484 cond: convert_expr(cond),
485 then_block: then_block.iter().map(|s| convert_stmt(s)).collect(),
486 else_block: else_block.map(|eb| eb.iter().map(|s| convert_stmt(s)).collect()),
487 },
488 Stmt::While { cond, body, .. } => BtaStmtKind::While {
489 cond: convert_expr(cond),
490 body: body.iter().map(|s| convert_stmt(s)).collect(),
491 },
492 _ => BtaStmtKind::Other,
493 };
494 BtaStmt { kind }
495}
496
497fn convert_block(block: &[Stmt]) -> Vec<BtaStmt> {
498 block.iter().map(|s| convert_stmt(s)).collect()
499}
500
501fn is_collection_type(ty: &TypeExpr) -> bool {
502 match ty {
503 TypeExpr::Generic { .. } => true,
504 _ => false,
505 }
506}
507
508fn analyze_bta_expr(
509 expr: &BtaExpr,
510 division: &Division,
511 functions: &[BtaFunc],
512 ctx: &mut BtaContext,
513) -> BindingTime {
514 match expr {
515 BtaExpr::Literal(lit) => BindingTime::Static(lit.clone()),
516 BtaExpr::Identifier(sym) => {
517 division.get(sym).cloned().unwrap_or(BindingTime::Dynamic)
518 }
519 BtaExpr::BinaryOp { op, left, right } => {
520 let bt_left = analyze_bta_expr(left, division, functions, ctx);
521 let bt_right = analyze_bta_expr(right, division, functions, ctx);
522 match (&bt_left, &bt_right) {
523 (BindingTime::Static(l), BindingTime::Static(r)) => {
524 match eval_literal_binop(*op, l, r) {
525 Some(result) => BindingTime::Static(result),
526 None => BindingTime::Dynamic,
527 }
528 }
529 _ => BindingTime::Dynamic,
530 }
531 }
532 BtaExpr::Not { operand } => {
533 match analyze_bta_expr(operand, division, functions, ctx) {
534 BindingTime::Static(Literal::Boolean(b)) => {
535 BindingTime::Static(Literal::Boolean(!b))
536 }
537 _ => BindingTime::Dynamic,
538 }
539 }
540 BtaExpr::Length { .. } => BindingTime::Dynamic,
541 BtaExpr::Index { .. } => BindingTime::Dynamic,
542 BtaExpr::Call { function, args } => {
543 let arg_bts: Vec<BindingTime> = args.iter()
544 .map(|a| analyze_bta_expr(a, division, functions, ctx))
545 .collect();
546
547 let cache_key = (*function, arg_bts.clone());
548
549 if let Some(cached) = ctx.cache.get(&cache_key) {
550 return cached.return_bt.clone();
551 }
552
553 if ctx.on_stack.contains(&cache_key) {
554 return BindingTime::Dynamic;
555 }
556
557 let func = match functions.iter().find(|f| f.name == *function) {
558 Some(f) => f.clone(),
559 None => return BindingTime::Dynamic,
560 };
561
562 ctx.on_stack.insert(cache_key.clone());
563
564 let mut func_div = Division::new();
565 for (i, (param_sym, is_collection)) in func.params.iter().enumerate() {
566 if *is_collection {
567 func_div.insert(*param_sym, BindingTime::Dynamic);
568 } else if let Some(bt) = arg_bts.get(i) {
569 func_div.insert(*param_sym, bt.clone());
570 } else {
571 func_div.insert(*param_sym, BindingTime::Dynamic);
572 }
573 }
574
575 let return_bt = analyze_bta_block(&func.body, &mut func_div, functions, ctx);
576
577 ctx.on_stack.remove(&cache_key);
578
579 let result = BtaResult {
580 division: func_div,
581 return_bt: return_bt.clone(),
582 };
583 ctx.cache.insert(cache_key, result);
584
585 return_bt
586 }
587 BtaExpr::Other => BindingTime::Dynamic,
588 }
589}
590
591fn analyze_bta_block(
592 stmts: &[BtaStmt],
593 division: &mut Division,
594 functions: &[BtaFunc],
595 ctx: &mut BtaContext,
596) -> BindingTime {
597 analyze_bta_block_opt(stmts, division, functions, ctx)
598 .unwrap_or(BindingTime::Dynamic)
599}
600
601fn analyze_bta_block_opt(
602 stmts: &[BtaStmt],
603 division: &mut Division,
604 functions: &[BtaFunc],
605 ctx: &mut BtaContext,
606) -> Option<BindingTime> {
607 for stmt in stmts {
608 if let Some(bt) = analyze_bta_stmt(stmt, division, functions, ctx) {
609 return Some(bt);
610 }
611 }
612 None
613}
614
615fn join_bt(a: &BindingTime, b: &BindingTime) -> BindingTime {
616 match (a, b) {
617 (BindingTime::Static(l1), BindingTime::Static(l2)) => {
618 if l1 == l2 {
619 a.clone()
620 } else {
621 BindingTime::Dynamic
622 }
623 }
624 _ => BindingTime::Dynamic,
625 }
626}
627
628fn join_divisions(a: &Division, b: &Division) -> Division {
629 let mut joined = Division::new();
630 for (sym, bt_a) in a {
631 if let Some(bt_b) = b.get(sym) {
632 joined.insert(*sym, join_bt(bt_a, bt_b));
633 }
634 }
635 for (sym, _) in b {
638 if !a.contains_key(sym) {
639 joined.insert(*sym, BindingTime::Dynamic);
640 }
641 }
642 for (sym, _) in a {
643 if !b.contains_key(sym) {
644 joined.insert(*sym, BindingTime::Dynamic);
645 }
646 }
647 joined
648}
649
650fn analyze_bta_stmt(
651 stmt: &BtaStmt,
652 division: &mut Division,
653 functions: &[BtaFunc],
654 ctx: &mut BtaContext,
655) -> Option<BindingTime> {
656 match &stmt.kind {
657 BtaStmtKind::Let { var, value } => {
658 let bt = analyze_bta_expr(value, division, functions, ctx);
659 division.insert(*var, bt);
660 None
661 }
662 BtaStmtKind::Set { target, value } => {
663 let bt = analyze_bta_expr(value, division, functions, ctx);
664 division.insert(*target, bt);
665 None
666 }
667 BtaStmtKind::Return { value } => {
668 let bt = match value {
669 Some(expr) => analyze_bta_expr(expr, division, functions, ctx),
670 None => BindingTime::Static(Literal::Nothing),
671 };
672 Some(bt)
673 }
674 BtaStmtKind::Show => None,
675 BtaStmtKind::Call { .. } => None,
676 BtaStmtKind::If { cond, then_block, else_block } => {
677 let cond_bt = analyze_bta_expr(cond, division, functions, ctx);
678 match cond_bt {
679 BindingTime::Static(Literal::Boolean(true)) => {
680 analyze_bta_block_opt(then_block, division, functions, ctx)
681 }
682 BindingTime::Static(Literal::Boolean(false)) => {
683 if let Some(else_b) = else_block {
684 analyze_bta_block_opt(else_b, division, functions, ctx)
685 } else {
686 None
687 }
688 }
689 _ => {
690 let snapshot = division.clone();
691
692 let then_ret = analyze_bta_block_opt(then_block, division, functions, ctx);
693 let then_div = division.clone();
694
695 *division = snapshot;
696 let else_ret = if let Some(else_b) = else_block {
697 analyze_bta_block_opt(else_b, division, functions, ctx)
698 } else {
699 None
700 };
701 let else_div = division.clone();
702
703 *division = join_divisions(&then_div, &else_div);
704 match (then_ret, else_ret) {
705 (Some(t), Some(e)) => Some(join_bt(&t, &e)),
706 _ => None,
707 }
708 }
709 }
710 }
711 BtaStmtKind::While { cond, body } => {
712 let max_iterations = 256;
713 for _ in 0..max_iterations {
714 let cond_bt = analyze_bta_expr(cond, division, functions, ctx);
715
716 match cond_bt {
717 BindingTime::Static(Literal::Boolean(false)) => break,
718 BindingTime::Static(Literal::Boolean(true)) => {
719 analyze_bta_block(body, division, functions, ctx);
721 }
722 _ => {
723 let snapshot = division.clone();
725 let mut body_div = snapshot.clone();
726 analyze_bta_block(body, &mut body_div, functions, ctx);
727
728 let joined = join_divisions(&snapshot, &body_div);
729 if joined == *division {
730 break;
731 }
732 *division = joined;
733 }
734 }
735 }
736 None
737 }
738 BtaStmtKind::ClobberDynamic { vars } => {
739 for var in vars {
740 division.insert(*var, BindingTime::Dynamic);
741 }
742 None
743 }
744 BtaStmtKind::Other => None,
745 }
746}
747
748impl BtaEnv {
749 pub fn analyze_source(source: &str) -> Result<Self, ParseError> {
750 let mut interner = Interner::new();
751 let mut lexer = Lexer::new(source, &mut interner);
752 let tokens = lexer.tokenize();
753
754 let type_registry = {
755 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
756 let result = discovery.run_full();
757 result.types
758 };
759
760 let mut world_state = WorldState::new();
761 let expr_arena = Arena::new();
762 let term_arena = Arena::new();
763 let np_arena = Arena::new();
764 let sym_arena = Arena::new();
765 let role_arena = Arena::new();
766 let pp_arena = Arena::new();
767 let stmt_arena = Arena::new();
768 let imperative_expr_arena = Arena::new();
769 let type_expr_arena = Arena::new();
770
771 let ast_ctx = AstContext::with_types(
772 &expr_arena,
773 &term_arena,
774 &np_arena,
775 &sym_arena,
776 &role_arena,
777 &pp_arena,
778 &stmt_arena,
779 &imperative_expr_arena,
780 &type_expr_arena,
781 );
782
783 let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
784 let stmts = parser.parse_program()?;
785
786 let mut main_stmts = Vec::new();
787 let mut functions = Vec::new();
788
789 for stmt in &stmts {
790 match stmt {
791 Stmt::FunctionDef { name, params, body, is_native, .. } => {
792 if !is_native {
793 let param_info: Vec<(Symbol, bool)> = params.iter().map(|(sym, ty)| {
794 (*sym, is_collection_type(ty))
795 }).collect();
796 functions.push(BtaFunc {
797 name: *name,
798 params: param_info,
799 body: convert_block(body),
800 });
801 }
802 }
803 _ => {
804 main_stmts.push(convert_stmt(stmt));
805 }
806 }
807 }
808
809 Ok(BtaEnv {
810 interner,
811 main_stmts,
812 functions,
813 })
814 }
815
816 pub fn analyze_main(&mut self) -> BtaResult {
817 let mut division = Division::new();
818 let mut ctx = BtaContext {
819 cache: BtaCache::new(),
820 on_stack: HashSet::new(),
821 };
822 let return_bt = analyze_bta_block(&self.main_stmts, &mut division, &self.functions, &mut ctx);
823 BtaResult { division, return_bt }
824 }
825
826 pub fn analyze_function(&mut self, func_name: &str, arg_bts: Vec<BindingTime>) -> BtaResult {
827 let func_sym = self.interner.lookup(func_name)
828 .unwrap_or_else(|| panic!("Function '{}' not found in interner", func_name));
829
830 let func = self.functions.iter()
831 .find(|f| f.name == func_sym)
832 .unwrap_or_else(|| panic!("Function '{}' not found", func_name))
833 .clone();
834
835 let mut division = Division::new();
836 for (i, (param_sym, is_collection)) in func.params.iter().enumerate() {
837 if *is_collection {
838 division.insert(*param_sym, BindingTime::Dynamic);
839 } else if let Some(bt) = arg_bts.get(i) {
840 division.insert(*param_sym, bt.clone());
841 } else {
842 division.insert(*param_sym, BindingTime::Dynamic);
843 }
844 }
845
846 let mut ctx = BtaContext {
847 cache: BtaCache::new(),
848 on_stack: HashSet::new(),
849 };
850 let return_bt = analyze_bta_block(&func.body, &mut division, &self.functions, &mut ctx);
851 BtaResult { division, return_bt }
852 }
853
854 pub fn lookup(&self, name: &str) -> Option<Symbol> {
855 self.interner.lookup(name)
856 }
857}