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