1use crate::ast::{self, SourceFile};
11use crate::ir::*;
12use std::collections::HashMap;
13
14struct IdGenerator {
16 counters: HashMap<String, usize>,
17}
18
19impl IdGenerator {
20 fn new() -> Self {
21 Self {
22 counters: HashMap::new(),
23 }
24 }
25
26 fn next(&mut self, prefix: &str) -> String {
27 let count = self.counters.entry(prefix.to_string()).or_insert(0);
28 *count += 1;
29 format!("{}_{:03}", prefix, count)
30 }
31}
32
33pub struct LoweringContext {
35 ids: IdGenerator,
36 scope: Vec<HashMap<String, String>>,
38 type_cache: HashMap<String, IrType>,
40}
41
42impl LoweringContext {
43 pub fn new() -> Self {
44 Self {
45 ids: IdGenerator::new(),
46 scope: vec![HashMap::new()],
47 type_cache: HashMap::new(),
48 }
49 }
50
51 fn push_scope(&mut self) {
52 self.scope.push(HashMap::new());
53 }
54
55 fn pop_scope(&mut self) {
56 self.scope.pop();
57 }
58
59 fn bind_var(&mut self, name: &str) -> String {
60 let id = self.ids.next("var");
61 if let Some(scope) = self.scope.last_mut() {
62 scope.insert(name.to_string(), id.clone());
63 }
64 id
65 }
66
67 fn lookup_var(&self, name: &str) -> Option<String> {
68 for scope in self.scope.iter().rev() {
69 if let Some(id) = scope.get(name) {
70 return Some(id.clone());
71 }
72 }
73 None
74 }
75}
76
77impl Default for LoweringContext {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83pub fn lower_source_file(source: &str, ast: &SourceFile) -> IrModule {
85 let mut ctx = LoweringContext::new();
86 let mut module = IrModule::new(source.to_string());
87
88 for item in &ast.items {
89 lower_item(&mut ctx, &mut module, &item.node);
90 }
91
92 module
93}
94
95
96fn collect_free_variables(expr: &ast::Expr, bound: &mut std::collections::HashSet<String>) -> Vec<String> {
99 let mut free = Vec::new();
100 collect_free_vars_inner(expr, bound, &mut free);
101 free
102}
103
104fn collect_free_vars_inner(
105 expr: &ast::Expr,
106 bound: &std::collections::HashSet<String>,
107 free: &mut Vec<String>,
108) {
109 match expr {
110 ast::Expr::Path(type_path) => {
111 if type_path.segments.len() == 1 && type_path.segments[0].generics.is_none() {
113 let name = &type_path.segments[0].ident.name;
114 if !bound.contains(name) && !free.contains(name) {
115 free.push(name.clone());
116 }
117 }
118 }
119 ast::Expr::Binary { left, right, .. } => {
120 collect_free_vars_inner(left, bound, free);
121 collect_free_vars_inner(right, bound, free);
122 }
123 ast::Expr::Unary { expr, .. } => {
124 collect_free_vars_inner(expr, bound, free);
125 }
126 ast::Expr::Call { func, args, .. } => {
127 collect_free_vars_inner(func, bound, free);
128 for arg in args {
129 collect_free_vars_inner(arg, bound, free);
130 }
131 }
132 ast::Expr::MethodCall { receiver, args, .. } => {
133 collect_free_vars_inner(receiver, bound, free);
134 for arg in args {
135 collect_free_vars_inner(arg, bound, free);
136 }
137 }
138 ast::Expr::Field { expr, .. } => {
139 collect_free_vars_inner(expr, bound, free);
140 }
141 ast::Expr::Index { expr, index, .. } => {
142 collect_free_vars_inner(expr, bound, free);
143 collect_free_vars_inner(index, bound, free);
144 }
145 ast::Expr::Block(block) => {
146 collect_free_vars_block(block, bound, free);
147 }
148 ast::Expr::If { condition, then_branch, else_branch, .. } => {
149 collect_free_vars_inner(condition, bound, free);
150 collect_free_vars_block(then_branch, bound, free);
151 if let Some(else_expr) = else_branch {
152 collect_free_vars_inner(else_expr, bound, free);
153 }
154 }
155 ast::Expr::Match { expr, arms, .. } => {
156 collect_free_vars_inner(expr, bound, free);
157 for arm in arms {
158 let mut arm_bound = bound.clone();
160 collect_pattern_bindings(&arm.pattern, &mut arm_bound);
161 if let Some(guard) = &arm.guard {
162 collect_free_vars_inner(guard, &arm_bound, free);
163 }
164 collect_free_vars_inner(&arm.body, &arm_bound, free);
165 }
166 }
167 ast::Expr::Closure { params, body, .. } => {
168 let mut closure_bound = bound.clone();
170 for param in params {
171 if let Some(name) = extract_pattern_name_opt(¶m.pattern) {
172 closure_bound.insert(name);
173 }
174 }
175 collect_free_vars_inner(body, &closure_bound, free);
176 }
177 ast::Expr::Tuple(elements) | ast::Expr::Array(elements) => {
178 for elem in elements {
179 collect_free_vars_inner(elem, bound, free);
180 }
181 }
182 ast::Expr::Struct { fields, rest, .. } => {
183 for field in fields {
184 if let Some(v) = &field.value { collect_free_vars_inner(v, bound, free); } else { let name = &field.name.name; if !bound.contains(name) && !free.contains(name) { free.push(name.clone()); } }
185 }
186 if let Some(r) = rest {
187 collect_free_vars_inner(r, bound, free);
188 }
189 }
190 ast::Expr::Range { start, end, .. } => {
191 if let Some(s) = start {
192 collect_free_vars_inner(s, bound, free);
193 }
194 if let Some(e) = end {
195 collect_free_vars_inner(e, bound, free);
196 }
197 }
198 ast::Expr::Return(Some(expr)) | ast::Expr::Try(expr) | ast::Expr::Deref(expr) => {
199 collect_free_vars_inner(expr, bound, free);
200 }
201 ast::Expr::Cast { expr, .. } => {
202 collect_free_vars_inner(expr, bound, free);
203 }
204 ast::Expr::Assign { target, value, .. } => {
205 collect_free_vars_inner(target, bound, free);
206 collect_free_vars_inner(value, bound, free);
207 }
208 ast::Expr::AddrOf { expr, .. } => {
209 collect_free_vars_inner(expr, bound, free);
210 }
211 ast::Expr::While { condition, body, .. } => {
212 collect_free_vars_inner(condition, bound, free);
213 collect_free_vars_block(body, bound, free);
214 }
215 ast::Expr::For { pattern, iter, body, .. } => {
216 collect_free_vars_inner(iter, bound, free);
217 let mut for_bound = bound.clone();
218 collect_pattern_bindings(pattern, &mut for_bound);
219 collect_free_vars_block(body, &for_bound, free);
220 }
221 ast::Expr::Loop { body: block, .. } | ast::Expr::Unsafe(block) | ast::Expr::Async { block, .. } => {
222 collect_free_vars_block(block, bound, free);
223 }
224 ast::Expr::Await { expr, .. } => {
225 collect_free_vars_inner(expr, bound, free);
226 }
227 ast::Expr::Evidential { expr, .. } => {
228 collect_free_vars_inner(expr, bound, free);
229 }
230 ast::Expr::Let { value, .. } => {
231 collect_free_vars_inner(value, bound, free);
232 }
233 ast::Expr::Break { value: Some(expr), .. } => {
234 collect_free_vars_inner(expr, bound, free);
235 }
236 ast::Expr::Literal(_)
238 | ast::Expr::Return(None)
239 | ast::Expr::Continue { .. }
240 | ast::Expr::Break { value: None, .. } => {}
241 _ => {}
243 }
244}
245
246fn collect_free_vars_block(
247 block: &ast::Block,
248 bound: &std::collections::HashSet<String>,
249 free: &mut Vec<String>,
250) {
251 let mut block_bound = bound.clone();
252 for stmt in &block.stmts {
253 collect_free_vars_stmt(stmt, &mut block_bound, free);
254 }
255 if let Some(expr) = &block.expr {
256 collect_free_vars_inner(expr, &block_bound, free);
257 }
258}
259
260fn collect_free_vars_stmt(
261 stmt: &ast::Stmt,
262 bound: &mut std::collections::HashSet<String>,
263 free: &mut Vec<String>,
264) {
265 match stmt {
266 ast::Stmt::Let { pattern, init, .. } => {
267 if let Some(v) = init {
269 collect_free_vars_inner(v, bound, free);
270 }
271 collect_pattern_bindings(pattern, bound);
273 }
274 ast::Stmt::LetElse { pattern, init, else_branch, .. } => {
275 collect_free_vars_inner(init, bound, free);
276 collect_free_vars_inner(else_branch, bound, free);
277 collect_pattern_bindings(pattern, bound);
278 }
279 ast::Stmt::Expr(expr) | ast::Stmt::Semi(expr) => {
280 collect_free_vars_inner(expr, bound, free);
281 }
282 ast::Stmt::Item(_) => {}
283 }
284}
285
286fn collect_pattern_bindings(pattern: &ast::Pattern, bound: &mut std::collections::HashSet<String>) {
287 match pattern {
288 ast::Pattern::Ident { name, .. } => {
289 bound.insert(name.name.clone());
290 }
291 ast::Pattern::Tuple(patterns) => {
292 for p in patterns {
293 collect_pattern_bindings(p, bound);
294 }
295 }
296 ast::Pattern::Struct { fields, .. } => {
297 for field in fields {
298 if let Some(p) = &field.pattern {
299 collect_pattern_bindings(p, bound);
300 } else {
301 bound.insert(field.name.name.clone());
303 }
304 }
305 }
306 ast::Pattern::TupleStruct { fields, .. } => {
307 for p in fields {
308 collect_pattern_bindings(p, bound);
309 }
310 }
311 ast::Pattern::Or(patterns) => {
312 for p in patterns {
314 collect_pattern_bindings(p, bound);
315 }
316 }
317 ast::Pattern::Slice(patterns) => {
318 for p in patterns {
319 collect_pattern_bindings(p, bound);
320 }
321 }
322 _ => {}
323 }
324}
325
326fn extract_pattern_name_opt(pattern: &ast::Pattern) -> Option<String> {
327 match pattern {
328 ast::Pattern::Ident { name, .. } => Some(name.name.clone()),
329 _ => None,
330 }
331}
332
333fn lower_item(ctx: &mut LoweringContext, module: &mut IrModule, item: &ast::Item) {
334 match item {
335 ast::Item::Function(f) => {
336 if let Some(ir_fn) = lower_function(ctx, f) {
337 module.functions.push(ir_fn);
338 }
339 }
340 ast::Item::Struct(s) => {
341 module.types.push(lower_struct_def(ctx, s));
342 }
343 ast::Item::Enum(e) => {
344 module.types.push(lower_enum_def(ctx, e));
345 }
346 ast::Item::Trait(t) => {
347 module.traits.push(lower_trait_def(ctx, t));
348 }
349 ast::Item::Impl(i) => {
350 module.impls.push(lower_impl_block(ctx, i));
351 }
352 ast::Item::TypeAlias(t) => {
353 module.types.push(lower_type_alias(ctx, t));
354 }
355 ast::Item::Const(c) => {
356 module.constants.push(lower_const_def(ctx, c));
357 }
358 ast::Item::Module(m) => {
359 if let Some(items) = &m.items {
360 for item in items {
361 lower_item(ctx, module, &item.node);
362 }
363 }
364 }
365 ast::Item::Static(_)
366 | ast::Item::Actor(_)
367 | ast::Item::Use(_)
368 | ast::Item::ExternBlock(_)
369 | ast::Item::Macro(_)
370 | ast::Item::MacroInvocation(_)
371 | ast::Item::Plurality(_) => {
372 }
374 }
375}
376
377fn lower_function(ctx: &mut LoweringContext, f: &ast::Function) -> Option<IrFunction> {
378 let id = ctx.ids.next("fn");
379 ctx.push_scope();
380
381 let params: Vec<IrParam> = f.params.iter().map(|p| lower_param(ctx, p)).collect();
382
383 let return_type = f
384 .return_type
385 .as_ref()
386 .map(|t| lower_type_expr(t))
387 .unwrap_or(IrType::Unit);
388
389 let body = f.body.as_ref().map(|b| lower_block(ctx, b));
390
391 let mut attributes = Vec::new();
392 if f.is_async {
393 attributes.push("async".to_string());
394 }
395 if f.attrs.inline.is_some() {
396 attributes.push("inline".to_string());
397 }
398 if f.attrs.naked {
399 attributes.push("naked".to_string());
400 }
401 if f.attrs.no_mangle {
402 attributes.push("no_mangle".to_string());
403 }
404
405 ctx.pop_scope();
406
407 Some(IrFunction {
408 name: f.name.name.clone(),
409 id,
410 visibility: lower_visibility(f.visibility),
411 generics: lower_generics(&f.generics),
412 params,
413 return_type,
414 body,
415 attributes,
416 is_async: f.is_async,
417 span: None,
418 })
419}
420
421fn lower_param(ctx: &mut LoweringContext, p: &ast::Param) -> IrParam {
422 let name = extract_pattern_name(&p.pattern);
423 let evidence = extract_pattern_evidence(&p.pattern);
424 ctx.bind_var(&name);
425
426 IrParam {
427 name,
428 ty: lower_type_expr(&p.ty),
429 evidence,
430 }
431}
432
433fn extract_pattern_name(p: &ast::Pattern) -> String {
434 match p {
435 ast::Pattern::Ident { name, .. } => name.name.clone(),
436 ast::Pattern::Tuple(pats) if !pats.is_empty() => extract_pattern_name(&pats[0]),
437 _ => "_".to_string(),
438 }
439}
440
441fn extract_pattern_evidence(p: &ast::Pattern) -> IrEvidence {
442 match p {
443 ast::Pattern::Ident { evidentiality, .. } => evidentiality
444 .map(lower_evidentiality)
445 .unwrap_or(IrEvidence::Known),
446 _ => IrEvidence::Known,
447 }
448}
449
450fn lower_visibility(v: ast::Visibility) -> IrVisibility {
451 match v {
452 ast::Visibility::Public => IrVisibility::Public,
453 ast::Visibility::Private => IrVisibility::Private,
454 ast::Visibility::Crate => IrVisibility::Crate,
455 ast::Visibility::Super => IrVisibility::Private,
456 }
457}
458
459fn lower_evidentiality(e: ast::Evidentiality) -> IrEvidence {
460 match e {
461 ast::Evidentiality::Known => IrEvidence::Known,
462 ast::Evidentiality::Uncertain | ast::Evidentiality::Predicted => IrEvidence::Uncertain,
463 ast::Evidentiality::Reported => IrEvidence::Reported,
464 ast::Evidentiality::Paradox => IrEvidence::Paradox,
465 }
466}
467
468fn lower_generics(g: &Option<ast::Generics>) -> Vec<IrGenericParam> {
469 g.as_ref()
470 .map(|g| {
471 g.params
472 .iter()
473 .filter_map(|p| match p {
474 ast::GenericParam::Type { name, bounds, .. } => Some(IrGenericParam {
475 name: name.name.clone(),
476 bounds: bounds.iter().map(type_expr_to_string).collect(),
477 }),
478 ast::GenericParam::Const { name, .. } => Some(IrGenericParam {
479 name: name.name.clone(),
480 bounds: vec!["const".to_string()],
481 }),
482 ast::GenericParam::Lifetime(l) => Some(IrGenericParam {
483 name: l.clone(),
484 bounds: vec!["lifetime".to_string()],
485 }),
486 })
487 .collect()
488 })
489 .unwrap_or_default()
490}
491
492fn type_expr_to_string(t: &ast::TypeExpr) -> String {
493 match t {
494 ast::TypeExpr::Path(p) => p
495 .segments
496 .iter()
497 .map(|s| s.ident.name.clone())
498 .collect::<Vec<_>>()
499 .join("::"),
500 _ => "?".to_string(),
501 }
502}
503
504fn lower_type_expr(t: &ast::TypeExpr) -> IrType {
505 match t {
506 ast::TypeExpr::Path(p) => {
507 let name = p
508 .segments
509 .iter()
510 .map(|s| s.ident.name.clone())
511 .collect::<Vec<_>>()
512 .join("::");
513
514 match name.as_str() {
516 "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64"
517 | "u128" | "usize" | "f32" | "f64" | "bool" | "char" | "str" => {
518 IrType::Primitive { name }
519 }
520 "()" => IrType::Unit,
521 "!" | "never" => IrType::Never,
522 _ => {
523 let generics = p
524 .segments
525 .last()
526 .and_then(|s| s.generics.as_ref())
527 .map(|gs| gs.iter().map(lower_type_expr).collect())
528 .unwrap_or_default();
529
530 IrType::Named { name, generics }
531 }
532 }
533 }
534 ast::TypeExpr::Reference { lifetime, mutable, inner } => IrType::Reference {
535 lifetime: lifetime.clone(),
536 mutable: *mutable,
537 inner: Box::new(lower_type_expr(inner)),
538 },
539 ast::TypeExpr::Pointer { mutable, inner } => IrType::Pointer {
540 mutable: *mutable,
541 inner: Box::new(lower_type_expr(inner)),
542 },
543 ast::TypeExpr::Array { element, size: _ } => IrType::Array {
544 element: Box::new(lower_type_expr(element)),
545 size: None, },
547 ast::TypeExpr::Slice(inner) => IrType::Slice {
548 element: Box::new(lower_type_expr(inner)),
549 },
550 ast::TypeExpr::Tuple(elements) => IrType::Tuple {
551 elements: elements.iter().map(lower_type_expr).collect(),
552 },
553 ast::TypeExpr::Function {
554 params,
555 return_type,
556 } => IrType::Function {
557 params: params.iter().map(lower_type_expr).collect(),
558 return_type: Box::new(
559 return_type
560 .as_ref()
561 .map(|r| lower_type_expr(r))
562 .unwrap_or(IrType::Unit),
563 ),
564 is_async: false,
565 },
566 ast::TypeExpr::Evidential {
567 inner,
568 evidentiality,
569 error_type,
570 } => {
571 let _ = error_type; IrType::Evidential {
575 inner: Box::new(lower_type_expr(inner)),
576 evidence: lower_evidentiality(*evidentiality),
577 }
578 }
579 ast::TypeExpr::Cycle { .. } => IrType::Cycle { modulus: 0 },
580 ast::TypeExpr::Simd { element, lanes } => IrType::Simd {
581 element: Box::new(lower_type_expr(element)),
582 lanes: *lanes as usize,
583 },
584 ast::TypeExpr::Atomic(inner) => IrType::Atomic {
585 inner: Box::new(lower_type_expr(inner)),
586 },
587 ast::TypeExpr::Never => IrType::Never,
588 ast::TypeExpr::Infer => IrType::Infer,
589 ast::TypeExpr::Lifetime(name) => IrType::Lifetime { name: name.clone() },
590 ast::TypeExpr::TraitObject(bounds) => IrType::TraitObject {
591 bounds: bounds.iter().map(lower_type_expr).collect(),
592 },
593 ast::TypeExpr::Hrtb { lifetimes, bound } => IrType::Hrtb {
594 lifetimes: lifetimes.clone(),
595 bound: Box::new(lower_type_expr(bound)),
596 },
597 ast::TypeExpr::InlineStruct { fields } => IrType::InlineStruct {
598 fields: fields
599 .iter()
600 .map(|f| (f.name.name.clone(), lower_type_expr(&f.ty)))
601 .collect(),
602 },
603 ast::TypeExpr::ImplTrait(bounds) => IrType::ImplTrait {
604 bounds: bounds.iter().map(lower_type_expr).collect(),
605 },
606 ast::TypeExpr::InlineEnum { variants } => IrType::InlineEnum {
607 variants: variants.iter().map(|v| v.name.name.clone()).collect(),
608 },
609 ast::TypeExpr::AssocTypeBinding { name, ty } => IrType::AssocTypeBinding {
610 name: name.name.clone(),
611 ty: Box::new(lower_type_expr(ty)),
612 },
613 ast::TypeExpr::ConstExpr(_) => {
614 IrType::Infer
617 }
618 ast::TypeExpr::QualifiedPath { self_type, trait_path, item_path } => {
619 let trait_part = trait_path.as_ref()
622 .map(|tp| tp.segments.iter().map(|s| s.ident.name.clone()).collect::<Vec<_>>().join("::"))
623 .unwrap_or_default();
624 let item_part = item_path.segments.iter().map(|s| s.ident.name.clone()).collect::<Vec<_>>().join("::");
625 let name = if trait_part.is_empty() {
626 format!("<_>::{}", item_part)
627 } else {
628 format!("<_ as {}>::{}", trait_part, item_part)
629 };
630 IrType::Named {
631 name,
632 generics: vec![lower_type_expr(self_type)],
633 }
634 }
635 }
636}
637
638fn lower_block(ctx: &mut LoweringContext, block: &ast::Block) -> IrOperation {
639 ctx.push_scope();
640
641 let mut statements: Vec<IrOperation> = block
642 .stmts
643 .iter()
644 .filter_map(|s| lower_stmt(ctx, s))
645 .collect();
646
647 if let Some(expr) = &block.expr {
648 statements.push(lower_expr(ctx, expr));
649 }
650
651 ctx.pop_scope();
652
653 IrOperation::Block {
654 statements,
655 ty: IrType::Infer,
656 evidence: IrEvidence::Known,
657 }
658}
659
660fn lower_stmt(ctx: &mut LoweringContext, stmt: &ast::Stmt) -> Option<IrOperation> {
661 match stmt {
662 ast::Stmt::Let { pattern, ty, init } => {
663 let ir_pattern = lower_pattern(ctx, pattern);
664 let type_annotation = ty.as_ref().map(lower_type_expr);
665 let init_expr = init
666 .as_ref()
667 .map(|e| Box::new(lower_expr(ctx, e)))
668 .unwrap_or_else(|| {
669 Box::new(IrOperation::Literal {
670 variant: LiteralVariant::Null,
671 value: serde_json::Value::Null,
672 ty: IrType::Unit,
673 evidence: IrEvidence::Known,
674 })
675 });
676
677 Some(IrOperation::Let {
678 pattern: ir_pattern,
679 type_annotation,
680 init: init_expr,
681 evidence: IrEvidence::Known,
682 })
683 }
684 ast::Stmt::LetElse { pattern, ty, init, else_branch } => {
685 let ir_pattern = lower_pattern(ctx, pattern);
688 let type_annotation = ty.as_ref().map(lower_type_expr);
689 let init_expr = Box::new(lower_expr(ctx, init));
690 Some(IrOperation::Let {
692 pattern: ir_pattern,
693 type_annotation,
694 init: init_expr,
695 evidence: IrEvidence::Known,
696 })
697 }
698 ast::Stmt::Expr(e) => Some(lower_expr(ctx, e)),
699 ast::Stmt::Semi(e) => Some(lower_expr(ctx, e)),
700 ast::Stmt::Item(_) => None, }
702}
703
704fn lower_pattern(ctx: &mut LoweringContext, pattern: &ast::Pattern) -> IrPattern {
705 match pattern {
706 ast::Pattern::Ident {
707 mutable,
708 name,
709 evidentiality,
710 } => {
711 ctx.bind_var(&name.name);
712 IrPattern::Ident {
713 name: name.name.clone(),
714 mutable: *mutable,
715 evidence: evidentiality.map(lower_evidentiality),
716 }
717 }
718 ast::Pattern::Tuple(pats) => IrPattern::Tuple {
719 elements: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
720 },
721 ast::Pattern::Struct { path, fields, rest } => IrPattern::Struct {
722 path: path
723 .segments
724 .iter()
725 .map(|s| s.ident.name.clone())
726 .collect::<Vec<_>>()
727 .join("::"),
728 fields: fields
729 .iter()
730 .map(|f| {
731 (
732 f.name.name.clone(),
733 f.pattern.as_ref().map(|p| lower_pattern(ctx, p)).unwrap_or(
734 IrPattern::Ident {
735 name: f.name.name.clone(),
736 mutable: false,
737 evidence: None,
738 },
739 ),
740 )
741 })
742 .collect(),
743 rest: *rest,
744 },
745 ast::Pattern::TupleStruct { path, fields } => IrPattern::TupleStruct {
746 path: path
747 .segments
748 .iter()
749 .map(|s| s.ident.name.clone())
750 .collect::<Vec<_>>()
751 .join("::"),
752 fields: fields.iter().map(|p| lower_pattern(ctx, p)).collect(),
753 },
754 ast::Pattern::Slice(pats) => IrPattern::Slice {
755 elements: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
756 },
757 ast::Pattern::Or(pats) => IrPattern::Or {
758 patterns: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
759 },
760 ast::Pattern::Literal(lit) => IrPattern::Literal {
761 value: lower_literal_value(lit),
762 },
763 ast::Pattern::Range {
764 start,
765 end,
766 inclusive,
767 } => IrPattern::Range {
768 start: start.as_ref().map(|p| Box::new(lower_pattern(ctx, p))),
769 end: end.as_ref().map(|p| Box::new(lower_pattern(ctx, p))),
770 inclusive: *inclusive,
771 },
772 ast::Pattern::Wildcard | ast::Pattern::Rest => IrPattern::Wildcard,
773 ast::Pattern::Ref { mutable: _, pattern } => {
774 lower_pattern(ctx, pattern)
776 }
777 ast::Pattern::RefBinding {
778 mutable,
779 name,
780 evidentiality,
781 } => {
782 IrPattern::Ident {
784 name: name.name.clone(),
785 mutable: *mutable,
786 evidence: evidentiality.map(lower_evidentiality),
787 }
788 }
789 ast::Pattern::Path(path) => {
790 let name = path
792 .segments
793 .iter()
794 .map(|s| s.ident.name.clone())
795 .collect::<Vec<_>>()
796 .join("::");
797 IrPattern::TupleStruct {
798 path: name,
799 fields: vec![],
800 }
801 }
802 }
803}
804
805fn lower_expr(ctx: &mut LoweringContext, expr: &ast::Expr) -> IrOperation {
806 match expr {
807 ast::Expr::Literal(lit) => lower_literal(lit),
808
809 ast::Expr::Path(path) => {
810 let name = path
811 .segments
812 .iter()
813 .map(|s| s.ident.name.clone())
814 .collect::<Vec<_>>()
815 .join("::");
816 let id = ctx.lookup_var(&name).unwrap_or_else(|| name.clone());
817
818 IrOperation::Var {
819 name,
820 id,
821 ty: IrType::Infer,
822 evidence: IrEvidence::Known,
823 }
824 }
825
826 ast::Expr::Binary { left, op, right } => {
827 let left_ir = lower_expr(ctx, left);
828 let right_ir = lower_expr(ctx, right);
829 let left_ev = get_operation_evidence(&left_ir);
830 let right_ev = get_operation_evidence(&right_ir);
831
832 IrOperation::Binary {
833 operator: lower_binop(*op),
834 left: Box::new(left_ir),
835 right: Box::new(right_ir),
836 ty: IrType::Infer,
837 evidence: left_ev.join(right_ev),
838 }
839 }
840
841 ast::Expr::Unary { op, expr: inner } => {
842 let inner_ir = lower_expr(ctx, inner);
843 let evidence = get_operation_evidence(&inner_ir);
844
845 IrOperation::Unary {
846 operator: lower_unaryop(*op),
847 operand: Box::new(inner_ir),
848 ty: IrType::Infer,
849 evidence,
850 }
851 }
852
853 ast::Expr::Call { func, args } => {
854 let func_name = match func.as_ref() {
855 ast::Expr::Path(p) => p
856 .segments
857 .iter()
858 .map(|s| s.ident.name.clone())
859 .collect::<Vec<_>>()
860 .join("::"),
861 _ => "anonymous".to_string(),
862 };
863
864 let args_ir: Vec<IrOperation> = args.iter().map(|a| lower_expr(ctx, a)).collect();
865 let evidence = args_ir
866 .iter()
867 .map(get_operation_evidence)
868 .fold(IrEvidence::Known, |acc, e| acc.join(e));
869
870 IrOperation::Call {
871 function: func_name.clone(),
872 function_id: format!("fn_{}", func_name),
873 args: args_ir,
874 type_args: vec![],
875 ty: IrType::Infer,
876 evidence,
877 }
878 }
879
880 ast::Expr::MethodCall {
881 receiver,
882 method,
883 args,
884 ..
885 } => {
886 let receiver_ir = lower_expr(ctx, receiver);
887 let args_ir: Vec<IrOperation> = args.iter().map(|a| lower_expr(ctx, a)).collect();
888 let evidence = std::iter::once(get_operation_evidence(&receiver_ir))
889 .chain(args_ir.iter().map(get_operation_evidence))
890 .fold(IrEvidence::Known, |acc, e| acc.join(e));
891
892 IrOperation::MethodCall {
893 receiver: Box::new(receiver_ir),
894 method: method.name.clone(),
895 args: args_ir,
896 type_args: vec![],
897 ty: IrType::Infer,
898 evidence,
899 }
900 }
901
902 ast::Expr::Pipe { expr, operations } => lower_pipeline(ctx, expr, operations),
903
904 ast::Expr::If {
905 condition,
906 then_branch,
907 else_branch,
908 } => {
909 let cond_ir = lower_expr(ctx, condition);
910 let then_ir = lower_block(ctx, then_branch);
911 let else_ir = else_branch.as_ref().map(|e| Box::new(lower_expr(ctx, e)));
912
913 let evidence = get_operation_evidence(&cond_ir)
914 .join(get_operation_evidence(&then_ir))
915 .join(
916 else_ir
917 .as_ref()
918 .map(|e| get_operation_evidence(e))
919 .unwrap_or(IrEvidence::Known),
920 );
921
922 IrOperation::If {
923 condition: Box::new(cond_ir),
924 then_branch: Box::new(then_ir),
925 else_branch: else_ir,
926 ty: IrType::Infer,
927 evidence,
928 }
929 }
930
931 ast::Expr::Match {
932 expr: scrutinee,
933 arms,
934 } => {
935 let scrutinee_ir = lower_expr(ctx, scrutinee);
936 let arms_ir: Vec<IrMatchArm> = arms
937 .iter()
938 .map(|arm| {
939 ctx.push_scope();
940 let pattern = lower_pattern(ctx, &arm.pattern);
941 let guard = arm.guard.as_ref().map(|g| lower_expr(ctx, g));
942 let body = lower_expr(ctx, &arm.body);
943 ctx.pop_scope();
944 IrMatchArm {
945 pattern,
946 guard,
947 body,
948 }
949 })
950 .collect();
951
952 IrOperation::Match {
953 scrutinee: Box::new(scrutinee_ir),
954 arms: arms_ir,
955 ty: IrType::Infer,
956 evidence: IrEvidence::Known,
957 }
958 }
959
960 ast::Expr::Loop { body: block, .. } => IrOperation::Loop {
961 variant: LoopVariant::Infinite,
962 condition: None,
963 iterator: None,
964 body: Box::new(lower_block(ctx, block)),
965 ty: IrType::Never,
966 evidence: IrEvidence::Known,
967 },
968
969 ast::Expr::While { condition, body, .. } => IrOperation::Loop {
970 variant: LoopVariant::While,
971 condition: Some(Box::new(lower_expr(ctx, condition))),
972 iterator: None,
973 body: Box::new(lower_block(ctx, body)),
974 ty: IrType::Unit,
975 evidence: IrEvidence::Known,
976 },
977
978 ast::Expr::For {
979 pattern,
980 iter,
981 body,
982 ..
983 } => {
984 ctx.push_scope();
985 let pat = lower_pattern(ctx, pattern);
986 let iter_ir = lower_expr(ctx, iter);
987 let body_ir = lower_block(ctx, body);
988 ctx.pop_scope();
989
990 IrOperation::Loop {
991 variant: LoopVariant::For,
992 condition: None,
993 iterator: Some(IrForIterator {
994 pattern: pat,
995 iterable: Box::new(iter_ir),
996 }),
997 body: Box::new(body_ir),
998 ty: IrType::Unit,
999 evidence: IrEvidence::Known,
1000 }
1001 }
1002
1003 ast::Expr::Closure { params, body, .. } => {
1004 let mut bound: std::collections::HashSet<String> = std::collections::HashSet::new();
1006 for scope in &ctx.scope {
1007 for name in scope.keys() {
1008 bound.insert(name.clone());
1009 }
1010 }
1011 for p in params {
1013 if let Some(name) = extract_pattern_name_opt(&p.pattern) {
1014 bound.insert(name.clone());
1015 }
1016 }
1017 let free_vars = collect_free_variables(body, &mut bound.clone());
1019 let captures: Vec<String> = free_vars
1021 .into_iter()
1022 .filter(|name| {
1023 for scope in &ctx.scope {
1025 if scope.contains_key(name) {
1026 return true;
1027 }
1028 }
1029 false
1030 })
1031 .collect();
1032
1033 ctx.push_scope();
1034 let params_ir: Vec<IrParam> = params
1035 .iter()
1036 .map(|p| {
1037 let name = extract_pattern_name(&p.pattern);
1038 ctx.bind_var(&name);
1039 IrParam {
1040 name,
1041 ty: p.ty.as_ref().map(lower_type_expr).unwrap_or(IrType::Infer),
1042 evidence: IrEvidence::Known,
1043 }
1044 })
1045 .collect();
1046 let body_ir = lower_expr(ctx, body);
1047 ctx.pop_scope();
1048
1049 IrOperation::Closure {
1050 params: params_ir,
1051 body: Box::new(body_ir),
1052 captures,
1053 ty: IrType::Infer,
1054 evidence: IrEvidence::Known,
1055 }
1056 }
1057
1058 ast::Expr::Block(block) => lower_block(ctx, block),
1059
1060 ast::Expr::Array(elements) => {
1061 let elements_ir: Vec<IrOperation> =
1062 elements.iter().map(|e| lower_expr(ctx, e)).collect();
1063 IrOperation::Array {
1064 elements: elements_ir,
1065 ty: IrType::Infer,
1066 evidence: IrEvidence::Known,
1067 }
1068 }
1069
1070 ast::Expr::Tuple(elements) => {
1071 let elements_ir: Vec<IrOperation> =
1072 elements.iter().map(|e| lower_expr(ctx, e)).collect();
1073 IrOperation::Tuple {
1074 elements: elements_ir,
1075 ty: IrType::Infer,
1076 evidence: IrEvidence::Known,
1077 }
1078 }
1079
1080 ast::Expr::Struct { path, fields, rest } => {
1081 let name = path
1082 .segments
1083 .iter()
1084 .map(|s| s.ident.name.clone())
1085 .collect::<Vec<_>>()
1086 .join("::");
1087
1088 let fields_ir: Vec<(String, IrOperation)> = fields
1089 .iter()
1090 .map(|f| {
1091 let value = f
1092 .value
1093 .as_ref()
1094 .map(|v| lower_expr(ctx, v))
1095 .unwrap_or_else(|| IrOperation::Var {
1096 name: f.name.name.clone(),
1097 id: ctx.lookup_var(&f.name.name).unwrap_or_default(),
1098 ty: IrType::Infer,
1099 evidence: IrEvidence::Known,
1100 });
1101 (f.name.name.clone(), value)
1102 })
1103 .collect();
1104
1105 IrOperation::StructInit {
1106 name,
1107 fields: fields_ir,
1108 rest: rest.as_ref().map(|r| Box::new(lower_expr(ctx, r))),
1109 ty: IrType::Infer,
1110 evidence: IrEvidence::Known,
1111 }
1112 }
1113
1114 ast::Expr::Field { expr: inner, field } => IrOperation::Field {
1115 expr: Box::new(lower_expr(ctx, inner)),
1116 field: field.name.clone(),
1117 ty: IrType::Infer,
1118 evidence: IrEvidence::Known,
1119 },
1120
1121 ast::Expr::Index { expr: inner, index } => IrOperation::Index {
1122 expr: Box::new(lower_expr(ctx, inner)),
1123 index: Box::new(lower_expr(ctx, index)),
1124 ty: IrType::Infer,
1125 evidence: IrEvidence::Known,
1126 },
1127
1128 ast::Expr::Assign { target, value } => IrOperation::Assign {
1129 target: Box::new(lower_expr(ctx, target)),
1130 value: Box::new(lower_expr(ctx, value)),
1131 evidence: IrEvidence::Known,
1132 },
1133
1134 ast::Expr::Return(value) => IrOperation::Return {
1135 value: value.as_ref().map(|v| Box::new(lower_expr(ctx, v))),
1136 evidence: IrEvidence::Known,
1137 },
1138
1139 ast::Expr::Break { value, .. } => IrOperation::Break {
1140 value: value.as_ref().map(|v| Box::new(lower_expr(ctx, v))),
1141 evidence: IrEvidence::Known,
1142 },
1143
1144 ast::Expr::Continue { .. } => IrOperation::Continue {
1145 evidence: IrEvidence::Known,
1146 },
1147
1148 ast::Expr::Await {
1149 expr: inner,
1150 evidentiality,
1151 } => {
1152 let inner_ir = lower_expr(ctx, inner);
1153 let evidence = match evidentiality {
1155 Some(ast::Evidentiality::Known) => IrEvidence::Known,
1156 Some(ast::Evidentiality::Uncertain) | Some(ast::Evidentiality::Predicted) => IrEvidence::Uncertain,
1157 Some(ast::Evidentiality::Reported) => IrEvidence::Reported,
1158 Some(ast::Evidentiality::Paradox) => IrEvidence::Uncertain, None => get_operation_evidence(&inner_ir),
1160 };
1161 IrOperation::Await {
1162 expr: Box::new(inner_ir),
1163 ty: IrType::Infer,
1164 evidence,
1165 }
1166 }
1167
1168 ast::Expr::Try(inner) => {
1169 let inner_ir = lower_expr(ctx, inner);
1170 IrOperation::Try {
1171 expr: Box::new(inner_ir),
1172 ty: IrType::Infer,
1173 evidence: IrEvidence::Uncertain, }
1175 }
1176
1177 ast::Expr::Unsafe(block) => IrOperation::Unsafe {
1178 body: Box::new(lower_block(ctx, block)),
1179 ty: IrType::Infer,
1180 evidence: IrEvidence::Paradox, },
1182
1183 ast::Expr::Async { block, is_move } => IrOperation::Async {
1184 body: Box::new(lower_block(ctx, block)),
1185 is_move: *is_move,
1186 ty: IrType::Infer,
1187 evidence: IrEvidence::Reported, },
1189
1190 ast::Expr::Cast { expr: inner, ty } => {
1191 let inner_ir = lower_expr(ctx, inner);
1192 let evidence = get_operation_evidence(&inner_ir);
1193 IrOperation::Cast {
1194 expr: Box::new(inner_ir),
1195 target_type: lower_type_expr(ty),
1196 ty: lower_type_expr(ty),
1197 evidence,
1198 }
1199 }
1200
1201 ast::Expr::Evidential {
1202 expr: inner,
1203 evidentiality,
1204 } => {
1205 let inner_ir = lower_expr(ctx, inner);
1206 let from_evidence = get_operation_evidence(&inner_ir);
1207 let to_evidence = lower_evidentiality(*evidentiality);
1208
1209 IrOperation::EvidenceCoerce {
1210 operation: EvidenceOp::Mark,
1211 expr: Box::new(inner_ir),
1212 from_evidence,
1213 to_evidence,
1214 ty: IrType::Infer,
1215 }
1216 }
1217
1218 ast::Expr::Morpheme { kind, body } => {
1219 let body_ir = lower_expr(ctx, body);
1220 IrOperation::Morpheme {
1221 morpheme: lower_morpheme_kind(*kind),
1222 symbol: morpheme_symbol(*kind).to_string(),
1223 input: Box::new(IrOperation::Var {
1224 name: "_".to_string(),
1225 id: "implicit_input".to_string(),
1226 ty: IrType::Infer,
1227 evidence: IrEvidence::Known,
1228 }),
1229 body: Some(Box::new(body_ir)),
1230 ty: IrType::Infer,
1231 evidence: IrEvidence::Known,
1232 }
1233 }
1234
1235 ast::Expr::Incorporation { segments } => {
1236 let segs: Vec<IncorporationSegment> = segments
1237 .iter()
1238 .map(|s| {
1239 if s.args.is_some() {
1240 IncorporationSegment::Verb {
1241 name: s.name.name.clone(),
1242 }
1243 } else {
1244 IncorporationSegment::Noun {
1245 name: s.name.name.clone(),
1246 }
1247 }
1248 })
1249 .collect();
1250
1251 let mut args: Vec<IrOperation> = Vec::new();
1253 for seg in segments {
1254 if let Some(ref seg_args) = seg.args {
1255 for a in seg_args {
1256 args.push(lower_expr(ctx, a));
1257 }
1258 }
1259 }
1260
1261 IrOperation::Incorporation {
1262 segments: segs,
1263 args,
1264 ty: IrType::Infer,
1265 evidence: IrEvidence::Known,
1266 }
1267 }
1268
1269 ast::Expr::HttpRequest {
1271 method,
1272 url,
1273 headers,
1274 body,
1275 timeout,
1276 } => IrOperation::HttpRequest {
1277 method: lower_http_method(*method),
1278 url: Box::new(lower_expr(ctx, url)),
1279 headers: if headers.is_empty() {
1280 None
1281 } else {
1282 Some(Box::new(IrOperation::Array {
1283 elements: headers
1284 .iter()
1285 .map(|(k, v)| IrOperation::Tuple {
1286 elements: vec![lower_expr(ctx, k), lower_expr(ctx, v)],
1287 ty: IrType::Infer,
1288 evidence: IrEvidence::Known,
1289 })
1290 .collect(),
1291 ty: IrType::Infer,
1292 evidence: IrEvidence::Known,
1293 }))
1294 },
1295 body: body.as_ref().map(|b| Box::new(lower_expr(ctx, b))),
1296 timeout: timeout.as_ref().map(|t| Box::new(lower_expr(ctx, t))),
1297 ty: IrType::Infer,
1298 evidence: IrEvidence::Reported,
1299 },
1300
1301 ast::Expr::GrpcCall {
1302 service,
1303 method,
1304 message,
1305 metadata,
1306 timeout,
1307 } => IrOperation::GrpcCall {
1308 service: expr_to_string(service),
1309 method: expr_to_string(method),
1310 message: Box::new(message.as_ref().map(|m| lower_expr(ctx, m)).unwrap_or(
1311 IrOperation::Literal {
1312 variant: LiteralVariant::Null,
1313 value: serde_json::Value::Null,
1314 ty: IrType::Unit,
1315 evidence: IrEvidence::Known,
1316 },
1317 )),
1318 metadata: if metadata.is_empty() {
1319 None
1320 } else {
1321 Some(Box::new(IrOperation::Array {
1322 elements: metadata
1323 .iter()
1324 .map(|(k, v)| IrOperation::Tuple {
1325 elements: vec![lower_expr(ctx, k), lower_expr(ctx, v)],
1326 ty: IrType::Infer,
1327 evidence: IrEvidence::Known,
1328 })
1329 .collect(),
1330 ty: IrType::Infer,
1331 evidence: IrEvidence::Known,
1332 }))
1333 },
1334 timeout: timeout.as_ref().map(|t| Box::new(lower_expr(ctx, t))),
1335 ty: IrType::Infer,
1336 evidence: IrEvidence::Reported,
1337 },
1338
1339 ast::Expr::Let { pattern, value } => {
1342 ctx.push_scope();
1343 let pattern_ir = lower_pattern(ctx, pattern);
1344 let value_ir = lower_expr(ctx, value);
1345 let evidence = get_operation_evidence(&value_ir);
1346 ctx.pop_scope();
1347
1348 IrOperation::LetMatch {
1349 pattern: pattern_ir,
1350 value: Box::new(value_ir),
1351 ty: IrType::Primitive { name: "bool".to_string() },
1352 evidence,
1353 }
1354 }
1355
1356 _ => IrOperation::Literal {
1358 variant: LiteralVariant::Null,
1359 value: serde_json::json!({"unhandled": format!("{:?}", std::mem::discriminant(expr))}),
1360 ty: IrType::Infer,
1361 evidence: IrEvidence::Known,
1362 },
1363 }
1364}
1365
1366fn lower_pipeline(
1367 ctx: &mut LoweringContext,
1368 input: &ast::Expr,
1369 operations: &[ast::PipeOp],
1370) -> IrOperation {
1371 let input_ir = lower_expr(ctx, input);
1372 let mut evidence = get_operation_evidence(&input_ir);
1373
1374 let steps: Vec<IrPipelineStep> = operations
1375 .iter()
1376 .map(|op| {
1377 let step = lower_pipe_op(ctx, op);
1378 evidence = evidence.join(pipe_op_evidence(op));
1380 step
1381 })
1382 .collect();
1383
1384 IrOperation::Pipeline {
1385 input: Box::new(input_ir),
1386 steps,
1387 ty: IrType::Infer,
1388 evidence,
1389 }
1390}
1391
1392fn lower_pipe_op(ctx: &mut LoweringContext, op: &ast::PipeOp) -> IrPipelineStep {
1393 match op {
1394 ast::PipeOp::Transform(body) => IrPipelineStep::Morpheme {
1395 morpheme: MorphemeKind::Transform,
1396 symbol: "τ".to_string(),
1397 body: Some(Box::new(lower_expr(ctx, body))),
1398 },
1399 ast::PipeOp::Filter(body) => IrPipelineStep::Morpheme {
1400 morpheme: MorphemeKind::Filter,
1401 symbol: "φ".to_string(),
1402 body: Some(Box::new(lower_expr(ctx, body))),
1403 },
1404 ast::PipeOp::Sort(field) => IrPipelineStep::Morpheme {
1405 morpheme: MorphemeKind::Sort,
1406 symbol: "σ".to_string(),
1407 body: field.as_ref().map(|f| {
1408 Box::new(IrOperation::Var {
1409 name: f.name.clone(),
1410 id: f.name.clone(),
1411 ty: IrType::Infer,
1412 evidence: IrEvidence::Known,
1413 })
1414 }),
1415 },
1416 ast::PipeOp::Reduce(body) => IrPipelineStep::Morpheme {
1417 morpheme: MorphemeKind::Reduce,
1418 symbol: "ρ".to_string(),
1419 body: Some(Box::new(lower_expr(ctx, body))),
1420 },
1421 ast::PipeOp::ReduceSum => IrPipelineStep::Morpheme {
1422 morpheme: MorphemeKind::Sum,
1423 symbol: "ρ+".to_string(),
1424 body: None,
1425 },
1426 ast::PipeOp::ReduceProd => IrPipelineStep::Morpheme {
1427 morpheme: MorphemeKind::Product,
1428 symbol: "ρ*".to_string(),
1429 body: None,
1430 },
1431 ast::PipeOp::ReduceMin => IrPipelineStep::Morpheme {
1432 morpheme: MorphemeKind::Min,
1433 symbol: "ρ_min".to_string(),
1434 body: None,
1435 },
1436 ast::PipeOp::ReduceMax => IrPipelineStep::Morpheme {
1437 morpheme: MorphemeKind::Max,
1438 symbol: "ρ_max".to_string(),
1439 body: None,
1440 },
1441 ast::PipeOp::ReduceConcat => IrPipelineStep::Morpheme {
1442 morpheme: MorphemeKind::Concat,
1443 symbol: "ρ++".to_string(),
1444 body: None,
1445 },
1446 ast::PipeOp::ReduceAll => IrPipelineStep::Morpheme {
1447 morpheme: MorphemeKind::All,
1448 symbol: "ρ&".to_string(),
1449 body: None,
1450 },
1451 ast::PipeOp::ReduceAny => IrPipelineStep::Morpheme {
1452 morpheme: MorphemeKind::Any,
1453 symbol: "ρ|".to_string(),
1454 body: None,
1455 },
1456 ast::PipeOp::First => IrPipelineStep::Morpheme {
1457 morpheme: MorphemeKind::First,
1458 symbol: "α".to_string(),
1459 body: None,
1460 },
1461 ast::PipeOp::Last => IrPipelineStep::Morpheme {
1462 morpheme: MorphemeKind::Last,
1463 symbol: "ω".to_string(),
1464 body: None,
1465 },
1466 ast::PipeOp::Middle => IrPipelineStep::Morpheme {
1467 morpheme: MorphemeKind::Middle,
1468 symbol: "μ".to_string(),
1469 body: None,
1470 },
1471 ast::PipeOp::Choice => IrPipelineStep::Morpheme {
1472 morpheme: MorphemeKind::Choice,
1473 symbol: "χ".to_string(),
1474 body: None,
1475 },
1476 ast::PipeOp::Nth(n) => IrPipelineStep::Morpheme {
1477 morpheme: MorphemeKind::Nth,
1478 symbol: "ν".to_string(),
1479 body: Some(Box::new(lower_expr(ctx, n))),
1480 },
1481 ast::PipeOp::Next => IrPipelineStep::Morpheme {
1482 morpheme: MorphemeKind::Next,
1483 symbol: "ξ".to_string(),
1484 body: None,
1485 },
1486 ast::PipeOp::Method { name, type_args: _, args } => IrPipelineStep::Method {
1487 name: name.name.clone(),
1488 args: args.iter().map(|a| lower_expr(ctx, a)).collect(),
1489 },
1490 ast::PipeOp::Await => IrPipelineStep::Await,
1491 ast::PipeOp::Match(_) => {
1492 IrPipelineStep::Identity
1495 }
1496 ast::PipeOp::TryMap(_) => {
1497 IrPipelineStep::Identity
1499 }
1500 ast::PipeOp::Named { prefix, body } => {
1501 let fn_name = prefix
1502 .iter()
1503 .map(|i| i.name.clone())
1504 .collect::<Vec<_>>()
1505 .join("·");
1506 IrPipelineStep::Call {
1507 function: fn_name,
1508 args: body
1509 .as_ref()
1510 .map(|b| vec![lower_expr(ctx, b)])
1511 .unwrap_or_default(),
1512 }
1513 }
1514 ast::PipeOp::Send(data) => IrPipelineStep::Protocol {
1516 operation: ProtocolOp::Send,
1517 config: Some(Box::new(lower_expr(ctx, data))),
1518 },
1519 ast::PipeOp::Recv => IrPipelineStep::Protocol {
1520 operation: ProtocolOp::Recv,
1521 config: None,
1522 },
1523 ast::PipeOp::Stream(handler) => IrPipelineStep::Protocol {
1524 operation: ProtocolOp::Stream,
1525 config: Some(Box::new(lower_expr(ctx, handler))),
1526 },
1527 ast::PipeOp::Connect(config) => IrPipelineStep::Protocol {
1528 operation: ProtocolOp::Connect,
1529 config: config.as_ref().map(|c| Box::new(lower_expr(ctx, c))),
1530 },
1531 ast::PipeOp::Close => IrPipelineStep::Protocol {
1532 operation: ProtocolOp::Close,
1533 config: None,
1534 },
1535 ast::PipeOp::Timeout(ms) => IrPipelineStep::Protocol {
1536 operation: ProtocolOp::Timeout,
1537 config: Some(Box::new(lower_expr(ctx, ms))),
1538 },
1539 ast::PipeOp::Retry { count, strategy } => IrPipelineStep::Protocol {
1540 operation: ProtocolOp::Retry,
1541 config: Some(Box::new(IrOperation::Tuple {
1542 elements: vec![
1543 lower_expr(ctx, count),
1544 strategy
1545 .as_ref()
1546 .map(|s| lower_expr(ctx, s))
1547 .unwrap_or(IrOperation::Literal {
1548 variant: LiteralVariant::String,
1549 value: serde_json::json!("exponential"),
1550 ty: IrType::Primitive {
1551 name: "str".to_string(),
1552 },
1553 evidence: IrEvidence::Known,
1554 }),
1555 ],
1556 ty: IrType::Infer,
1557 evidence: IrEvidence::Known,
1558 })),
1559 },
1560 _ => IrPipelineStep::Identity,
1561 }
1562}
1563
1564fn pipe_op_evidence(op: &ast::PipeOp) -> IrEvidence {
1565 match op {
1566 ast::PipeOp::Send(_)
1568 | ast::PipeOp::Recv
1569 | ast::PipeOp::Stream(_)
1570 | ast::PipeOp::Connect(_)
1571 | ast::PipeOp::Close => IrEvidence::Reported,
1572
1573 ast::PipeOp::Validate {
1575 target_evidence, ..
1576 } => ast_evidence_to_ir(*target_evidence),
1577 ast::PipeOp::Assume {
1578 target_evidence, ..
1579 } => ast_evidence_to_ir(*target_evidence),
1580 ast::PipeOp::AssertEvidence(_) => IrEvidence::Known, _ => IrEvidence::Known,
1583 }
1584}
1585
1586fn ast_evidence_to_ir(ev: ast::Evidentiality) -> IrEvidence {
1587 match ev {
1588 ast::Evidentiality::Known => IrEvidence::Known,
1589 ast::Evidentiality::Uncertain | ast::Evidentiality::Predicted => IrEvidence::Uncertain,
1590 ast::Evidentiality::Reported => IrEvidence::Reported,
1591 ast::Evidentiality::Paradox => IrEvidence::Paradox,
1592 }
1593}
1594
1595fn lower_literal(lit: &ast::Literal) -> IrOperation {
1596 match lit {
1597 ast::Literal::Int {
1598 value,
1599 base,
1600 suffix,
1601 } => IrOperation::Literal {
1602 variant: LiteralVariant::Int,
1603 value: serde_json::json!({
1604 "value": value,
1605 "base": format!("{:?}", base),
1606 "suffix": suffix
1607 }),
1608 ty: suffix
1609 .as_ref()
1610 .map(|s| IrType::Primitive { name: s.clone() })
1611 .unwrap_or(IrType::Primitive {
1612 name: "i64".to_string(),
1613 }),
1614 evidence: IrEvidence::Known,
1615 },
1616 ast::Literal::Float { value, suffix } => IrOperation::Literal {
1617 variant: LiteralVariant::Float,
1618 value: serde_json::json!({"value": value, "suffix": suffix}),
1619 ty: suffix
1620 .as_ref()
1621 .map(|s| IrType::Primitive { name: s.clone() })
1622 .unwrap_or(IrType::Primitive {
1623 name: "f64".to_string(),
1624 }),
1625 evidence: IrEvidence::Known,
1626 },
1627 ast::Literal::String(s) | ast::Literal::MultiLineString(s) | ast::Literal::RawString(s) => {
1628 IrOperation::Literal {
1629 variant: LiteralVariant::String,
1630 value: serde_json::Value::String(s.clone()),
1631 ty: IrType::Primitive {
1632 name: "str".to_string(),
1633 },
1634 evidence: IrEvidence::Known,
1635 }
1636 }
1637 ast::Literal::Char(c) => IrOperation::Literal {
1638 variant: LiteralVariant::Char,
1639 value: serde_json::Value::String(c.to_string()),
1640 ty: IrType::Primitive {
1641 name: "char".to_string(),
1642 },
1643 evidence: IrEvidence::Known,
1644 },
1645 ast::Literal::Bool(b) => IrOperation::Literal {
1646 variant: LiteralVariant::Bool,
1647 value: serde_json::Value::Bool(*b),
1648 ty: IrType::Primitive {
1649 name: "bool".to_string(),
1650 },
1651 evidence: IrEvidence::Known,
1652 },
1653 ast::Literal::Null | ast::Literal::Empty => IrOperation::Literal {
1654 variant: LiteralVariant::Null,
1655 value: serde_json::Value::Null,
1656 ty: IrType::Unit,
1657 evidence: IrEvidence::Known,
1658 },
1659 _ => IrOperation::Literal {
1660 variant: LiteralVariant::Null,
1661 value: serde_json::Value::Null,
1662 ty: IrType::Infer,
1663 evidence: IrEvidence::Known,
1664 },
1665 }
1666}
1667
1668fn lower_literal_value(lit: &ast::Literal) -> serde_json::Value {
1669 match lit {
1670 ast::Literal::Int { value, .. } => serde_json::json!(value),
1671 ast::Literal::Float { value, .. } => serde_json::json!(value),
1672 ast::Literal::String(s) => serde_json::Value::String(s.clone()),
1673 ast::Literal::Bool(b) => serde_json::Value::Bool(*b),
1674 ast::Literal::Char(c) => serde_json::Value::String(c.to_string()),
1675 ast::Literal::Null => serde_json::Value::Null,
1676 _ => serde_json::Value::Null,
1677 }
1678}
1679
1680fn lower_binop(op: ast::BinOp) -> BinaryOp {
1681 match op {
1682 ast::BinOp::Add => BinaryOp::Add,
1683 ast::BinOp::Sub => BinaryOp::Sub,
1684 ast::BinOp::Mul => BinaryOp::Mul,
1685 ast::BinOp::Div => BinaryOp::Div,
1686 ast::BinOp::Rem => BinaryOp::Rem,
1687 ast::BinOp::Pow => BinaryOp::Pow,
1688 ast::BinOp::And => BinaryOp::And,
1689 ast::BinOp::Or => BinaryOp::Or,
1690 ast::BinOp::BitAnd => BinaryOp::BitAnd,
1691 ast::BinOp::BitOr => BinaryOp::BitOr,
1692 ast::BinOp::BitXor => BinaryOp::BitXor,
1693 ast::BinOp::Shl => BinaryOp::Shl,
1694 ast::BinOp::Shr => BinaryOp::Shr,
1695 ast::BinOp::Eq => BinaryOp::Eq,
1696 ast::BinOp::Ne => BinaryOp::Ne,
1697 ast::BinOp::Lt => BinaryOp::Lt,
1698 ast::BinOp::Le => BinaryOp::Le,
1699 ast::BinOp::Gt => BinaryOp::Gt,
1700 ast::BinOp::Ge => BinaryOp::Ge,
1701 ast::BinOp::Concat => BinaryOp::Concat,
1702 ast::BinOp::MatMul => BinaryOp::MatMul,
1703 ast::BinOp::Hadamard => BinaryOp::Hadamard,
1704 ast::BinOp::TensorProd => BinaryOp::TensorProd,
1705 }
1706}
1707
1708fn lower_unaryop(op: ast::UnaryOp) -> UnaryOp {
1709 match op {
1710 ast::UnaryOp::Neg => UnaryOp::Neg,
1711 ast::UnaryOp::Not => UnaryOp::Not,
1712 ast::UnaryOp::Deref => UnaryOp::Deref,
1713 ast::UnaryOp::Ref => UnaryOp::Ref,
1714 ast::UnaryOp::RefMut => UnaryOp::RefMut,
1715 }
1716}
1717
1718fn lower_morpheme_kind(kind: ast::MorphemeKind) -> MorphemeKind {
1719 match kind {
1720 ast::MorphemeKind::Transform => MorphemeKind::Transform,
1721 ast::MorphemeKind::Filter => MorphemeKind::Filter,
1722 ast::MorphemeKind::Sort => MorphemeKind::Sort,
1723 ast::MorphemeKind::Reduce => MorphemeKind::Reduce,
1724 ast::MorphemeKind::Lambda => MorphemeKind::Lambda,
1725 ast::MorphemeKind::Sum => MorphemeKind::Sum,
1726 ast::MorphemeKind::Product => MorphemeKind::Product,
1727 ast::MorphemeKind::First => MorphemeKind::First,
1728 ast::MorphemeKind::Last => MorphemeKind::Last,
1729 ast::MorphemeKind::Middle => MorphemeKind::Middle,
1730 ast::MorphemeKind::Choice => MorphemeKind::Choice,
1731 ast::MorphemeKind::Nth => MorphemeKind::Nth,
1732 ast::MorphemeKind::Next => MorphemeKind::Next,
1733 }
1734}
1735
1736fn morpheme_symbol(kind: ast::MorphemeKind) -> &'static str {
1737 match kind {
1738 ast::MorphemeKind::Transform => "τ",
1739 ast::MorphemeKind::Filter => "φ",
1740 ast::MorphemeKind::Sort => "σ",
1741 ast::MorphemeKind::Reduce => "ρ",
1742 ast::MorphemeKind::Lambda => "λ",
1743 ast::MorphemeKind::Sum => "Σ",
1744 ast::MorphemeKind::Product => "Π",
1745 ast::MorphemeKind::First => "α",
1746 ast::MorphemeKind::Last => "ω",
1747 ast::MorphemeKind::Middle => "μ",
1748 ast::MorphemeKind::Choice => "χ",
1749 ast::MorphemeKind::Nth => "ν",
1750 ast::MorphemeKind::Next => "ξ",
1751 }
1752}
1753
1754fn lower_http_method(method: ast::HttpMethod) -> HttpMethod {
1755 match method {
1756 ast::HttpMethod::Get => HttpMethod::Get,
1757 ast::HttpMethod::Post => HttpMethod::Post,
1758 ast::HttpMethod::Put => HttpMethod::Put,
1759 ast::HttpMethod::Delete => HttpMethod::Delete,
1760 ast::HttpMethod::Patch => HttpMethod::Patch,
1761 ast::HttpMethod::Head => HttpMethod::Head,
1762 ast::HttpMethod::Options => HttpMethod::Options,
1763 ast::HttpMethod::Connect => HttpMethod::Connect,
1764 ast::HttpMethod::Trace => HttpMethod::Trace,
1765 }
1766}
1767
1768fn get_operation_evidence(op: &IrOperation) -> IrEvidence {
1769 match op {
1770 IrOperation::Literal { evidence, .. }
1771 | IrOperation::Var { evidence, .. }
1772 | IrOperation::Let { evidence, .. }
1773 | IrOperation::Binary { evidence, .. }
1774 | IrOperation::Unary { evidence, .. }
1775 | IrOperation::Call { evidence, .. }
1776 | IrOperation::MethodCall { evidence, .. }
1777 | IrOperation::Closure { evidence, .. }
1778 | IrOperation::If { evidence, .. }
1779 | IrOperation::Match { evidence, .. }
1780 | IrOperation::LetMatch { evidence, .. }
1781 | IrOperation::Loop { evidence, .. }
1782 | IrOperation::Block { evidence, .. }
1783 | IrOperation::Pipeline { evidence, .. }
1784 | IrOperation::Morpheme { evidence, .. }
1785 | IrOperation::Fork { evidence, .. }
1786 | IrOperation::Identity { evidence, .. }
1787 | IrOperation::Array { evidence, .. }
1788 | IrOperation::Tuple { evidence, .. }
1789 | IrOperation::StructInit { evidence, .. }
1790 | IrOperation::Field { evidence, .. }
1791 | IrOperation::Index { evidence, .. }
1792 | IrOperation::Assign { evidence, .. }
1793 | IrOperation::Break { evidence, .. }
1794 | IrOperation::Continue { evidence }
1795 | IrOperation::Return { evidence, .. }
1796 | IrOperation::Incorporation { evidence, .. }
1797 | IrOperation::Affect { evidence, .. }
1798 | IrOperation::HttpRequest { evidence, .. }
1799 | IrOperation::GrpcCall { evidence, .. }
1800 | IrOperation::WebSocket { evidence, .. }
1801 | IrOperation::KafkaOp { evidence, .. }
1802 | IrOperation::Await { evidence, .. }
1803 | IrOperation::Unsafe { evidence, .. }
1804 | IrOperation::Async { evidence, .. }
1805 | IrOperation::Cast { evidence, .. }
1806 | IrOperation::Try { evidence, .. } => *evidence,
1807 IrOperation::EvidenceCoerce { to_evidence, .. } => *to_evidence,
1808 }
1809}
1810
1811fn expr_to_string(e: &ast::Expr) -> String {
1812 match e {
1813 ast::Expr::Path(p) => p
1814 .segments
1815 .iter()
1816 .map(|s| s.ident.name.clone())
1817 .collect::<Vec<_>>()
1818 .join("::"),
1819 ast::Expr::Literal(ast::Literal::String(s)) => s.clone(),
1820 _ => "dynamic".to_string(),
1821 }
1822}
1823
1824fn lower_struct_def(_ctx: &mut LoweringContext, s: &ast::StructDef) -> IrTypeDef {
1827 let fields = match &s.fields {
1828 ast::StructFields::Named(fields) => fields
1829 .iter()
1830 .map(|f| IrField {
1831 name: f.name.name.clone(),
1832 ty: lower_type_expr(&f.ty),
1833 visibility: lower_visibility(f.visibility),
1834 })
1835 .collect(),
1836 ast::StructFields::Tuple(types) => types
1837 .iter()
1838 .enumerate()
1839 .map(|(i, t)| IrField {
1840 name: format!("{}", i),
1841 ty: lower_type_expr(t),
1842 visibility: IrVisibility::Public,
1843 })
1844 .collect(),
1845 ast::StructFields::Unit => vec![],
1846 };
1847
1848 IrTypeDef::Struct {
1849 name: s.name.name.clone(),
1850 generics: lower_generics(&s.generics),
1851 fields,
1852 span: None,
1853 }
1854}
1855
1856fn lower_enum_def(_ctx: &mut LoweringContext, e: &ast::EnumDef) -> IrTypeDef {
1857 let variants = e
1858 .variants
1859 .iter()
1860 .map(|v| {
1861 let fields = match &v.fields {
1862 ast::StructFields::Named(fields) => Some(
1863 fields
1864 .iter()
1865 .map(|f| IrField {
1866 name: f.name.name.clone(),
1867 ty: lower_type_expr(&f.ty),
1868 visibility: lower_visibility(f.visibility),
1869 })
1870 .collect(),
1871 ),
1872 ast::StructFields::Tuple(types) => Some(
1873 types
1874 .iter()
1875 .enumerate()
1876 .map(|(i, t)| IrField {
1877 name: format!("{}", i),
1878 ty: lower_type_expr(t),
1879 visibility: IrVisibility::Public,
1880 })
1881 .collect(),
1882 ),
1883 ast::StructFields::Unit => None,
1884 };
1885
1886 IrVariant {
1887 name: v.name.name.clone(),
1888 fields,
1889 discriminant: None,
1890 }
1891 })
1892 .collect();
1893
1894 IrTypeDef::Enum {
1895 name: e.name.name.clone(),
1896 generics: lower_generics(&e.generics),
1897 variants,
1898 span: None,
1899 }
1900}
1901
1902fn lower_type_alias(_ctx: &mut LoweringContext, t: &ast::TypeAlias) -> IrTypeDef {
1903 IrTypeDef::TypeAlias {
1904 name: t.name.name.clone(),
1905 generics: lower_generics(&t.generics),
1906 target: lower_type_expr(&t.ty),
1907 span: None,
1908 }
1909}
1910
1911fn lower_trait_def(ctx: &mut LoweringContext, t: &ast::TraitDef) -> IrTraitDef {
1912 let methods = t
1913 .items
1914 .iter()
1915 .filter_map(|item| match item {
1916 ast::TraitItem::Function(f) => lower_function(ctx, f),
1917 _ => None,
1918 })
1919 .collect();
1920
1921 IrTraitDef {
1922 name: t.name.name.clone(),
1923 generics: lower_generics(&t.generics),
1924 super_traits: t.supertraits.iter().map(type_expr_to_string).collect(),
1925 methods,
1926 span: None,
1927 }
1928}
1929
1930fn lower_impl_block(ctx: &mut LoweringContext, i: &ast::ImplBlock) -> IrImplBlock {
1931 let methods = i
1932 .items
1933 .iter()
1934 .filter_map(|item| match item {
1935 ast::ImplItem::Function(f) => lower_function(ctx, f),
1936 _ => None,
1937 })
1938 .collect();
1939
1940 IrImplBlock {
1941 trait_name: i.trait_.as_ref().map(|t| {
1942 t.segments
1943 .iter()
1944 .map(|s| s.ident.name.clone())
1945 .collect::<Vec<_>>()
1946 .join("::")
1947 }),
1948 target_type: lower_type_expr(&i.self_ty),
1949 generics: lower_generics(&i.generics),
1950 methods,
1951 span: None,
1952 }
1953}
1954
1955fn lower_const_def(ctx: &mut LoweringContext, c: &ast::ConstDef) -> IrConstant {
1956 IrConstant {
1957 name: c.name.name.clone(),
1958 ty: lower_type_expr(&c.ty),
1959 value: lower_expr(ctx, &c.value),
1960 visibility: lower_visibility(c.visibility),
1961 span: None,
1962 }
1963}