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::Never => IrType::Never,
622 ast::TypeExpr::Infer => IrType::Infer,
623 ast::TypeExpr::Lifetime(name) => IrType::Lifetime { name: name.clone() },
624 ast::TypeExpr::TraitObject(bounds) => IrType::TraitObject {
625 bounds: bounds.iter().map(lower_type_expr).collect(),
626 },
627 ast::TypeExpr::Hrtb { lifetimes, bound } => IrType::Hrtb {
628 lifetimes: lifetimes.clone(),
629 bound: Box::new(lower_type_expr(bound)),
630 },
631 ast::TypeExpr::InlineStruct { fields } => IrType::InlineStruct {
632 fields: fields
633 .iter()
634 .map(|f| (f.name.name.clone(), lower_type_expr(&f.ty)))
635 .collect(),
636 },
637 ast::TypeExpr::ImplTrait(bounds) => IrType::ImplTrait {
638 bounds: bounds.iter().map(lower_type_expr).collect(),
639 },
640 ast::TypeExpr::InlineEnum { variants } => IrType::InlineEnum {
641 variants: variants.iter().map(|v| v.name.name.clone()).collect(),
642 },
643 ast::TypeExpr::AssocTypeBinding { name, ty } => IrType::AssocTypeBinding {
644 name: name.name.clone(),
645 ty: Box::new(lower_type_expr(ty)),
646 },
647 ast::TypeExpr::ConstExpr(_) => {
648 IrType::Infer
651 }
652 ast::TypeExpr::QualifiedPath {
653 self_type,
654 trait_path,
655 item_path,
656 } => {
657 let trait_part = trait_path
660 .as_ref()
661 .map(|tp| {
662 tp.segments
663 .iter()
664 .map(|s| s.ident.name.clone())
665 .collect::<Vec<_>>()
666 .join("::")
667 })
668 .unwrap_or_default();
669 let item_part = item_path
670 .segments
671 .iter()
672 .map(|s| s.ident.name.clone())
673 .collect::<Vec<_>>()
674 .join("::");
675 let name = if trait_part.is_empty() {
676 format!("<_>::{}", item_part)
677 } else {
678 format!("<_ as {}>::{}", trait_part, item_part)
679 };
680 IrType::Named {
681 name,
682 generics: vec![lower_type_expr(self_type)],
683 }
684 }
685 }
686}
687
688fn lower_block(ctx: &mut LoweringContext, block: &ast::Block) -> IrOperation {
689 ctx.push_scope();
690
691 let mut statements: Vec<IrOperation> = block
692 .stmts
693 .iter()
694 .filter_map(|s| lower_stmt(ctx, s))
695 .collect();
696
697 if let Some(expr) = &block.expr {
698 statements.push(lower_expr(ctx, expr));
699 }
700
701 ctx.pop_scope();
702
703 IrOperation::Block {
704 statements,
705 ty: IrType::Infer,
706 evidence: IrEvidence::Known,
707 }
708}
709
710fn lower_stmt(ctx: &mut LoweringContext, stmt: &ast::Stmt) -> Option<IrOperation> {
711 match stmt {
712 ast::Stmt::Let { pattern, ty, init } => {
713 let ir_pattern = lower_pattern(ctx, pattern);
714 let type_annotation = ty.as_ref().map(lower_type_expr);
715 let init_expr = init
716 .as_ref()
717 .map(|e| Box::new(lower_expr(ctx, e)))
718 .unwrap_or_else(|| {
719 Box::new(IrOperation::Literal {
720 variant: LiteralVariant::Null,
721 value: serde_json::Value::Null,
722 ty: IrType::Unit,
723 evidence: IrEvidence::Known,
724 })
725 });
726
727 Some(IrOperation::Let {
728 pattern: ir_pattern,
729 type_annotation,
730 init: init_expr,
731 evidence: IrEvidence::Known,
732 })
733 }
734 ast::Stmt::LetElse {
735 pattern,
736 ty,
737 init,
738 else_branch,
739 } => {
740 let ir_pattern = lower_pattern(ctx, pattern);
743 let type_annotation = ty.as_ref().map(lower_type_expr);
744 let init_expr = Box::new(lower_expr(ctx, init));
745 Some(IrOperation::Let {
747 pattern: ir_pattern,
748 type_annotation,
749 init: init_expr,
750 evidence: IrEvidence::Known,
751 })
752 }
753 ast::Stmt::Expr(e) => Some(lower_expr(ctx, e)),
754 ast::Stmt::Semi(e) => Some(lower_expr(ctx, e)),
755 ast::Stmt::Item(_) => None, }
757}
758
759fn lower_pattern(ctx: &mut LoweringContext, pattern: &ast::Pattern) -> IrPattern {
760 match pattern {
761 ast::Pattern::Ident {
762 mutable,
763 name,
764 evidentiality,
765 } => {
766 ctx.bind_var(&name.name);
767 IrPattern::Ident {
768 name: name.name.clone(),
769 mutable: *mutable,
770 evidence: evidentiality.map(lower_evidentiality),
771 }
772 }
773 ast::Pattern::Tuple(pats) => IrPattern::Tuple {
774 elements: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
775 },
776 ast::Pattern::Struct { path, fields, rest } => IrPattern::Struct {
777 path: path
778 .segments
779 .iter()
780 .map(|s| s.ident.name.clone())
781 .collect::<Vec<_>>()
782 .join("::"),
783 fields: fields
784 .iter()
785 .map(|f| {
786 (
787 f.name.name.clone(),
788 f.pattern.as_ref().map(|p| lower_pattern(ctx, p)).unwrap_or(
789 IrPattern::Ident {
790 name: f.name.name.clone(),
791 mutable: false,
792 evidence: None,
793 },
794 ),
795 )
796 })
797 .collect(),
798 rest: *rest,
799 },
800 ast::Pattern::TupleStruct { path, fields } => IrPattern::TupleStruct {
801 path: path
802 .segments
803 .iter()
804 .map(|s| s.ident.name.clone())
805 .collect::<Vec<_>>()
806 .join("::"),
807 fields: fields.iter().map(|p| lower_pattern(ctx, p)).collect(),
808 },
809 ast::Pattern::Slice(pats) => IrPattern::Slice {
810 elements: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
811 },
812 ast::Pattern::Or(pats) => IrPattern::Or {
813 patterns: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
814 },
815 ast::Pattern::Literal(lit) => IrPattern::Literal {
816 value: lower_literal_value(lit),
817 },
818 ast::Pattern::Range {
819 start,
820 end,
821 inclusive,
822 } => IrPattern::Range {
823 start: start.as_ref().map(|p| Box::new(lower_pattern(ctx, p))),
824 end: end.as_ref().map(|p| Box::new(lower_pattern(ctx, p))),
825 inclusive: *inclusive,
826 },
827 ast::Pattern::Wildcard | ast::Pattern::Rest => IrPattern::Wildcard,
828 ast::Pattern::Ref {
829 mutable: _,
830 pattern,
831 } => {
832 lower_pattern(ctx, pattern)
834 }
835 ast::Pattern::RefBinding {
836 mutable,
837 name,
838 evidentiality,
839 } => {
840 IrPattern::Ident {
842 name: name.name.clone(),
843 mutable: *mutable,
844 evidence: evidentiality.map(lower_evidentiality),
845 }
846 }
847 ast::Pattern::Path(path) => {
848 let name = path
850 .segments
851 .iter()
852 .map(|s| s.ident.name.clone())
853 .collect::<Vec<_>>()
854 .join("::");
855 IrPattern::TupleStruct {
856 path: name,
857 fields: vec![],
858 }
859 }
860 }
861}
862
863fn lower_expr(ctx: &mut LoweringContext, expr: &ast::Expr) -> IrOperation {
864 match expr {
865 ast::Expr::Literal(lit) => lower_literal(lit),
866
867 ast::Expr::Path(path) => {
868 let name = path
869 .segments
870 .iter()
871 .map(|s| s.ident.name.clone())
872 .collect::<Vec<_>>()
873 .join("::");
874 let id = ctx.lookup_var(&name).unwrap_or_else(|| name.clone());
875
876 IrOperation::Var {
877 name,
878 id,
879 ty: IrType::Infer,
880 evidence: IrEvidence::Known,
881 }
882 }
883
884 ast::Expr::Binary { left, op, right } => {
885 let left_ir = lower_expr(ctx, left);
886 let right_ir = lower_expr(ctx, right);
887 let left_ev = get_operation_evidence(&left_ir);
888 let right_ev = get_operation_evidence(&right_ir);
889
890 IrOperation::Binary {
891 operator: lower_binop(*op),
892 left: Box::new(left_ir),
893 right: Box::new(right_ir),
894 ty: IrType::Infer,
895 evidence: left_ev.join(right_ev),
896 }
897 }
898
899 ast::Expr::Unary { op, expr: inner } => {
900 let inner_ir = lower_expr(ctx, inner);
901 let evidence = get_operation_evidence(&inner_ir);
902
903 IrOperation::Unary {
904 operator: lower_unaryop(*op),
905 operand: Box::new(inner_ir),
906 ty: IrType::Infer,
907 evidence,
908 }
909 }
910
911 ast::Expr::Call { func, args } => {
912 let func_name = match func.as_ref() {
913 ast::Expr::Path(p) => p
914 .segments
915 .iter()
916 .map(|s| s.ident.name.clone())
917 .collect::<Vec<_>>()
918 .join("::"),
919 _ => "anonymous".to_string(),
920 };
921
922 let args_ir: Vec<IrOperation> = args.iter().map(|a| lower_expr(ctx, a)).collect();
923 let evidence = args_ir
924 .iter()
925 .map(get_operation_evidence)
926 .fold(IrEvidence::Known, |acc, e| acc.join(e));
927
928 IrOperation::Call {
929 function: func_name.clone(),
930 function_id: format!("fn_{}", func_name),
931 args: args_ir,
932 type_args: vec![],
933 ty: IrType::Infer,
934 evidence,
935 }
936 }
937
938 ast::Expr::MethodCall {
939 receiver,
940 method,
941 args,
942 ..
943 } => {
944 let receiver_ir = lower_expr(ctx, receiver);
945 let args_ir: Vec<IrOperation> = args.iter().map(|a| lower_expr(ctx, a)).collect();
946 let evidence = std::iter::once(get_operation_evidence(&receiver_ir))
947 .chain(args_ir.iter().map(get_operation_evidence))
948 .fold(IrEvidence::Known, |acc, e| acc.join(e));
949
950 IrOperation::MethodCall {
951 receiver: Box::new(receiver_ir),
952 method: method.name.clone(),
953 args: args_ir,
954 type_args: vec![],
955 ty: IrType::Infer,
956 evidence,
957 }
958 }
959
960 ast::Expr::Pipe { expr, operations } => lower_pipeline(ctx, expr, operations),
961
962 ast::Expr::If {
963 condition,
964 then_branch,
965 else_branch,
966 } => {
967 let cond_ir = lower_expr(ctx, condition);
968 let then_ir = lower_block(ctx, then_branch);
969 let else_ir = else_branch.as_ref().map(|e| Box::new(lower_expr(ctx, e)));
970
971 let evidence = get_operation_evidence(&cond_ir)
972 .join(get_operation_evidence(&then_ir))
973 .join(
974 else_ir
975 .as_ref()
976 .map(|e| get_operation_evidence(e))
977 .unwrap_or(IrEvidence::Known),
978 );
979
980 IrOperation::If {
981 condition: Box::new(cond_ir),
982 then_branch: Box::new(then_ir),
983 else_branch: else_ir,
984 ty: IrType::Infer,
985 evidence,
986 }
987 }
988
989 ast::Expr::Match {
990 expr: scrutinee,
991 arms,
992 } => {
993 let scrutinee_ir = lower_expr(ctx, scrutinee);
994 let arms_ir: Vec<IrMatchArm> = arms
995 .iter()
996 .map(|arm| {
997 ctx.push_scope();
998 let pattern = lower_pattern(ctx, &arm.pattern);
999 let guard = arm.guard.as_ref().map(|g| lower_expr(ctx, g));
1000 let body = lower_expr(ctx, &arm.body);
1001 ctx.pop_scope();
1002 IrMatchArm {
1003 pattern,
1004 guard,
1005 body,
1006 }
1007 })
1008 .collect();
1009
1010 IrOperation::Match {
1011 scrutinee: Box::new(scrutinee_ir),
1012 arms: arms_ir,
1013 ty: IrType::Infer,
1014 evidence: IrEvidence::Known,
1015 }
1016 }
1017
1018 ast::Expr::Loop { body: block, .. } => IrOperation::Loop {
1019 variant: LoopVariant::Infinite,
1020 condition: None,
1021 iterator: None,
1022 body: Box::new(lower_block(ctx, block)),
1023 ty: IrType::Never,
1024 evidence: IrEvidence::Known,
1025 },
1026
1027 ast::Expr::While {
1028 condition, body, ..
1029 } => IrOperation::Loop {
1030 variant: LoopVariant::While,
1031 condition: Some(Box::new(lower_expr(ctx, condition))),
1032 iterator: None,
1033 body: Box::new(lower_block(ctx, body)),
1034 ty: IrType::Unit,
1035 evidence: IrEvidence::Known,
1036 },
1037
1038 ast::Expr::For {
1039 pattern,
1040 iter,
1041 body,
1042 ..
1043 } => {
1044 ctx.push_scope();
1045 let pat = lower_pattern(ctx, pattern);
1046 let iter_ir = lower_expr(ctx, iter);
1047 let body_ir = lower_block(ctx, body);
1048 ctx.pop_scope();
1049
1050 IrOperation::Loop {
1051 variant: LoopVariant::For,
1052 condition: None,
1053 iterator: Some(IrForIterator {
1054 pattern: pat,
1055 iterable: Box::new(iter_ir),
1056 }),
1057 body: Box::new(body_ir),
1058 ty: IrType::Unit,
1059 evidence: IrEvidence::Known,
1060 }
1061 }
1062
1063 ast::Expr::Closure { params, body, .. } => {
1064 let mut bound: std::collections::HashSet<String> = std::collections::HashSet::new();
1066 for scope in &ctx.scope {
1067 for name in scope.keys() {
1068 bound.insert(name.clone());
1069 }
1070 }
1071 for p in params {
1073 if let Some(name) = extract_pattern_name_opt(&p.pattern) {
1074 bound.insert(name.clone());
1075 }
1076 }
1077 let free_vars = collect_free_variables(body, &mut bound.clone());
1079 let captures: Vec<String> = free_vars
1081 .into_iter()
1082 .filter(|name| {
1083 for scope in &ctx.scope {
1085 if scope.contains_key(name) {
1086 return true;
1087 }
1088 }
1089 false
1090 })
1091 .collect();
1092
1093 ctx.push_scope();
1094 let params_ir: Vec<IrParam> = params
1095 .iter()
1096 .map(|p| {
1097 let name = extract_pattern_name(&p.pattern);
1098 ctx.bind_var(&name);
1099 IrParam {
1100 name,
1101 ty: p.ty.as_ref().map(lower_type_expr).unwrap_or(IrType::Infer),
1102 evidence: IrEvidence::Known,
1103 }
1104 })
1105 .collect();
1106 let body_ir = lower_expr(ctx, body);
1107 ctx.pop_scope();
1108
1109 IrOperation::Closure {
1110 params: params_ir,
1111 body: Box::new(body_ir),
1112 captures,
1113 ty: IrType::Infer,
1114 evidence: IrEvidence::Known,
1115 }
1116 }
1117
1118 ast::Expr::Block(block) => lower_block(ctx, block),
1119
1120 ast::Expr::Array(elements) => {
1121 let elements_ir: Vec<IrOperation> =
1122 elements.iter().map(|e| lower_expr(ctx, e)).collect();
1123 IrOperation::Array {
1124 elements: elements_ir,
1125 ty: IrType::Infer,
1126 evidence: IrEvidence::Known,
1127 }
1128 }
1129
1130 ast::Expr::Tuple(elements) => {
1131 let elements_ir: Vec<IrOperation> =
1132 elements.iter().map(|e| lower_expr(ctx, e)).collect();
1133 IrOperation::Tuple {
1134 elements: elements_ir,
1135 ty: IrType::Infer,
1136 evidence: IrEvidence::Known,
1137 }
1138 }
1139
1140 ast::Expr::Struct { path, fields, rest } => {
1141 let name = path
1142 .segments
1143 .iter()
1144 .map(|s| s.ident.name.clone())
1145 .collect::<Vec<_>>()
1146 .join("::");
1147
1148 let fields_ir: Vec<(String, IrOperation)> = fields
1149 .iter()
1150 .map(|f| {
1151 let value = f
1152 .value
1153 .as_ref()
1154 .map(|v| lower_expr(ctx, v))
1155 .unwrap_or_else(|| IrOperation::Var {
1156 name: f.name.name.clone(),
1157 id: ctx.lookup_var(&f.name.name).unwrap_or_default(),
1158 ty: IrType::Infer,
1159 evidence: IrEvidence::Known,
1160 });
1161 (f.name.name.clone(), value)
1162 })
1163 .collect();
1164
1165 IrOperation::StructInit {
1166 name,
1167 fields: fields_ir,
1168 rest: rest.as_ref().map(|r| Box::new(lower_expr(ctx, r))),
1169 ty: IrType::Infer,
1170 evidence: IrEvidence::Known,
1171 }
1172 }
1173
1174 ast::Expr::Field { expr: inner, field } => IrOperation::Field {
1175 expr: Box::new(lower_expr(ctx, inner)),
1176 field: field.name.clone(),
1177 ty: IrType::Infer,
1178 evidence: IrEvidence::Known,
1179 },
1180
1181 ast::Expr::Index { expr: inner, index } => IrOperation::Index {
1182 expr: Box::new(lower_expr(ctx, inner)),
1183 index: Box::new(lower_expr(ctx, index)),
1184 ty: IrType::Infer,
1185 evidence: IrEvidence::Known,
1186 },
1187
1188 ast::Expr::Assign { target, value } => IrOperation::Assign {
1189 target: Box::new(lower_expr(ctx, target)),
1190 value: Box::new(lower_expr(ctx, value)),
1191 evidence: IrEvidence::Known,
1192 },
1193
1194 ast::Expr::Return(value) => IrOperation::Return {
1195 value: value.as_ref().map(|v| Box::new(lower_expr(ctx, v))),
1196 evidence: IrEvidence::Known,
1197 },
1198
1199 ast::Expr::Break { value, .. } => IrOperation::Break {
1200 value: value.as_ref().map(|v| Box::new(lower_expr(ctx, v))),
1201 evidence: IrEvidence::Known,
1202 },
1203
1204 ast::Expr::Continue { .. } => IrOperation::Continue {
1205 evidence: IrEvidence::Known,
1206 },
1207
1208 ast::Expr::Await {
1209 expr: inner,
1210 evidentiality,
1211 } => {
1212 let inner_ir = lower_expr(ctx, inner);
1213 let evidence = match evidentiality {
1215 Some(ast::Evidentiality::Known) => IrEvidence::Known,
1216 Some(ast::Evidentiality::Uncertain) | Some(ast::Evidentiality::Predicted) => {
1217 IrEvidence::Uncertain
1218 }
1219 Some(ast::Evidentiality::Reported) => IrEvidence::Reported,
1220 Some(ast::Evidentiality::Paradox) => IrEvidence::Uncertain, None => get_operation_evidence(&inner_ir),
1222 };
1223 IrOperation::Await {
1224 expr: Box::new(inner_ir),
1225 ty: IrType::Infer,
1226 evidence,
1227 }
1228 }
1229
1230 ast::Expr::Try(inner) => {
1231 let inner_ir = lower_expr(ctx, inner);
1232 IrOperation::Try {
1233 expr: Box::new(inner_ir),
1234 ty: IrType::Infer,
1235 evidence: IrEvidence::Uncertain, }
1237 }
1238
1239 ast::Expr::Unsafe(block) => IrOperation::Unsafe {
1240 body: Box::new(lower_block(ctx, block)),
1241 ty: IrType::Infer,
1242 evidence: IrEvidence::Paradox, },
1244
1245 ast::Expr::Async { block, is_move } => IrOperation::Async {
1246 body: Box::new(lower_block(ctx, block)),
1247 is_move: *is_move,
1248 ty: IrType::Infer,
1249 evidence: IrEvidence::Reported, },
1251
1252 ast::Expr::Cast { expr: inner, ty } => {
1253 let inner_ir = lower_expr(ctx, inner);
1254 let evidence = get_operation_evidence(&inner_ir);
1255 IrOperation::Cast {
1256 expr: Box::new(inner_ir),
1257 target_type: lower_type_expr(ty),
1258 ty: lower_type_expr(ty),
1259 evidence,
1260 }
1261 }
1262
1263 ast::Expr::Evidential {
1264 expr: inner,
1265 evidentiality,
1266 } => {
1267 let inner_ir = lower_expr(ctx, inner);
1268 let from_evidence = get_operation_evidence(&inner_ir);
1269 let to_evidence = lower_evidentiality(*evidentiality);
1270
1271 IrOperation::EvidenceCoerce {
1272 operation: EvidenceOp::Mark,
1273 expr: Box::new(inner_ir),
1274 from_evidence,
1275 to_evidence,
1276 ty: IrType::Infer,
1277 }
1278 }
1279
1280 ast::Expr::Morpheme { kind, body } => {
1281 let body_ir = lower_expr(ctx, body);
1282 IrOperation::Morpheme {
1283 morpheme: lower_morpheme_kind(*kind),
1284 symbol: morpheme_symbol(*kind).to_string(),
1285 input: Box::new(IrOperation::Var {
1286 name: "_".to_string(),
1287 id: "implicit_input".to_string(),
1288 ty: IrType::Infer,
1289 evidence: IrEvidence::Known,
1290 }),
1291 body: Some(Box::new(body_ir)),
1292 ty: IrType::Infer,
1293 evidence: IrEvidence::Known,
1294 }
1295 }
1296
1297 ast::Expr::Incorporation { segments } => {
1298 let segs: Vec<IncorporationSegment> = segments
1299 .iter()
1300 .map(|s| {
1301 if s.args.is_some() {
1302 IncorporationSegment::Verb {
1303 name: s.name.name.clone(),
1304 }
1305 } else {
1306 IncorporationSegment::Noun {
1307 name: s.name.name.clone(),
1308 }
1309 }
1310 })
1311 .collect();
1312
1313 let mut args: Vec<IrOperation> = Vec::new();
1315 for seg in segments {
1316 if let Some(ref seg_args) = seg.args {
1317 for a in seg_args {
1318 args.push(lower_expr(ctx, a));
1319 }
1320 }
1321 }
1322
1323 IrOperation::Incorporation {
1324 segments: segs,
1325 args,
1326 ty: IrType::Infer,
1327 evidence: IrEvidence::Known,
1328 }
1329 }
1330
1331 ast::Expr::HttpRequest {
1333 method,
1334 url,
1335 headers,
1336 body,
1337 timeout,
1338 } => IrOperation::HttpRequest {
1339 method: lower_http_method(*method),
1340 url: Box::new(lower_expr(ctx, url)),
1341 headers: if headers.is_empty() {
1342 None
1343 } else {
1344 Some(Box::new(IrOperation::Array {
1345 elements: headers
1346 .iter()
1347 .map(|(k, v)| IrOperation::Tuple {
1348 elements: vec![lower_expr(ctx, k), lower_expr(ctx, v)],
1349 ty: IrType::Infer,
1350 evidence: IrEvidence::Known,
1351 })
1352 .collect(),
1353 ty: IrType::Infer,
1354 evidence: IrEvidence::Known,
1355 }))
1356 },
1357 body: body.as_ref().map(|b| Box::new(lower_expr(ctx, b))),
1358 timeout: timeout.as_ref().map(|t| Box::new(lower_expr(ctx, t))),
1359 ty: IrType::Infer,
1360 evidence: IrEvidence::Reported,
1361 },
1362
1363 ast::Expr::GrpcCall {
1364 service,
1365 method,
1366 message,
1367 metadata,
1368 timeout,
1369 } => IrOperation::GrpcCall {
1370 service: expr_to_string(service),
1371 method: expr_to_string(method),
1372 message: Box::new(message.as_ref().map(|m| lower_expr(ctx, m)).unwrap_or(
1373 IrOperation::Literal {
1374 variant: LiteralVariant::Null,
1375 value: serde_json::Value::Null,
1376 ty: IrType::Unit,
1377 evidence: IrEvidence::Known,
1378 },
1379 )),
1380 metadata: if metadata.is_empty() {
1381 None
1382 } else {
1383 Some(Box::new(IrOperation::Array {
1384 elements: metadata
1385 .iter()
1386 .map(|(k, v)| IrOperation::Tuple {
1387 elements: vec![lower_expr(ctx, k), lower_expr(ctx, v)],
1388 ty: IrType::Infer,
1389 evidence: IrEvidence::Known,
1390 })
1391 .collect(),
1392 ty: IrType::Infer,
1393 evidence: IrEvidence::Known,
1394 }))
1395 },
1396 timeout: timeout.as_ref().map(|t| Box::new(lower_expr(ctx, t))),
1397 ty: IrType::Infer,
1398 evidence: IrEvidence::Reported,
1399 },
1400
1401 ast::Expr::Let { pattern, value } => {
1404 ctx.push_scope();
1405 let pattern_ir = lower_pattern(ctx, pattern);
1406 let value_ir = lower_expr(ctx, value);
1407 let evidence = get_operation_evidence(&value_ir);
1408 ctx.pop_scope();
1409
1410 IrOperation::LetMatch {
1411 pattern: pattern_ir,
1412 value: Box::new(value_ir),
1413 ty: IrType::Primitive {
1414 name: "bool".to_string(),
1415 },
1416 evidence,
1417 }
1418 }
1419
1420 _ => IrOperation::Literal {
1422 variant: LiteralVariant::Null,
1423 value: serde_json::json!({"unhandled": format!("{:?}", std::mem::discriminant(expr))}),
1424 ty: IrType::Infer,
1425 evidence: IrEvidence::Known,
1426 },
1427 }
1428}
1429
1430fn lower_pipeline(
1431 ctx: &mut LoweringContext,
1432 input: &ast::Expr,
1433 operations: &[ast::PipeOp],
1434) -> IrOperation {
1435 let input_ir = lower_expr(ctx, input);
1436 let mut evidence = get_operation_evidence(&input_ir);
1437
1438 let steps: Vec<IrPipelineStep> = operations
1439 .iter()
1440 .map(|op| {
1441 let step = lower_pipe_op(ctx, op);
1442 evidence = evidence.join(pipe_op_evidence(op));
1444 step
1445 })
1446 .collect();
1447
1448 IrOperation::Pipeline {
1449 input: Box::new(input_ir),
1450 steps,
1451 ty: IrType::Infer,
1452 evidence,
1453 }
1454}
1455
1456fn lower_pipe_op(ctx: &mut LoweringContext, op: &ast::PipeOp) -> IrPipelineStep {
1457 match op {
1458 ast::PipeOp::Transform(body) => IrPipelineStep::Morpheme {
1459 morpheme: MorphemeKind::Transform,
1460 symbol: "τ".to_string(),
1461 body: Some(Box::new(lower_expr(ctx, body))),
1462 },
1463 ast::PipeOp::Filter(body) => IrPipelineStep::Morpheme {
1464 morpheme: MorphemeKind::Filter,
1465 symbol: "φ".to_string(),
1466 body: Some(Box::new(lower_expr(ctx, body))),
1467 },
1468 ast::PipeOp::Sort(field) => IrPipelineStep::Morpheme {
1469 morpheme: MorphemeKind::Sort,
1470 symbol: "σ".to_string(),
1471 body: field.as_ref().map(|f| {
1472 Box::new(IrOperation::Var {
1473 name: f.name.clone(),
1474 id: f.name.clone(),
1475 ty: IrType::Infer,
1476 evidence: IrEvidence::Known,
1477 })
1478 }),
1479 },
1480 ast::PipeOp::Reduce(body) => IrPipelineStep::Morpheme {
1481 morpheme: MorphemeKind::Reduce,
1482 symbol: "ρ".to_string(),
1483 body: Some(Box::new(lower_expr(ctx, body))),
1484 },
1485 ast::PipeOp::ReduceSum => IrPipelineStep::Morpheme {
1486 morpheme: MorphemeKind::Sum,
1487 symbol: "ρ+".to_string(),
1488 body: None,
1489 },
1490 ast::PipeOp::ReduceProd => IrPipelineStep::Morpheme {
1491 morpheme: MorphemeKind::Product,
1492 symbol: "ρ*".to_string(),
1493 body: None,
1494 },
1495 ast::PipeOp::ReduceMin => IrPipelineStep::Morpheme {
1496 morpheme: MorphemeKind::Min,
1497 symbol: "ρ_min".to_string(),
1498 body: None,
1499 },
1500 ast::PipeOp::ReduceMax => IrPipelineStep::Morpheme {
1501 morpheme: MorphemeKind::Max,
1502 symbol: "ρ_max".to_string(),
1503 body: None,
1504 },
1505 ast::PipeOp::ReduceConcat => IrPipelineStep::Morpheme {
1506 morpheme: MorphemeKind::Concat,
1507 symbol: "ρ++".to_string(),
1508 body: None,
1509 },
1510 ast::PipeOp::ReduceAll => IrPipelineStep::Morpheme {
1511 morpheme: MorphemeKind::All,
1512 symbol: "ρ&".to_string(),
1513 body: None,
1514 },
1515 ast::PipeOp::ReduceAny => IrPipelineStep::Morpheme {
1516 morpheme: MorphemeKind::Any,
1517 symbol: "ρ|".to_string(),
1518 body: None,
1519 },
1520 ast::PipeOp::First => IrPipelineStep::Morpheme {
1521 morpheme: MorphemeKind::First,
1522 symbol: "α".to_string(),
1523 body: None,
1524 },
1525 ast::PipeOp::Last => IrPipelineStep::Morpheme {
1526 morpheme: MorphemeKind::Last,
1527 symbol: "ω".to_string(),
1528 body: None,
1529 },
1530 ast::PipeOp::Middle => IrPipelineStep::Morpheme {
1531 morpheme: MorphemeKind::Middle,
1532 symbol: "μ".to_string(),
1533 body: None,
1534 },
1535 ast::PipeOp::Choice => IrPipelineStep::Morpheme {
1536 morpheme: MorphemeKind::Choice,
1537 symbol: "χ".to_string(),
1538 body: None,
1539 },
1540 ast::PipeOp::Nth(n) => IrPipelineStep::Morpheme {
1541 morpheme: MorphemeKind::Nth,
1542 symbol: "ν".to_string(),
1543 body: Some(Box::new(lower_expr(ctx, n))),
1544 },
1545 ast::PipeOp::Next => IrPipelineStep::Morpheme {
1546 morpheme: MorphemeKind::Next,
1547 symbol: "ξ".to_string(),
1548 body: None,
1549 },
1550 ast::PipeOp::Method {
1551 name,
1552 type_args: _,
1553 args,
1554 } => IrPipelineStep::Method {
1555 name: name.name.clone(),
1556 args: args.iter().map(|a| lower_expr(ctx, a)).collect(),
1557 },
1558 ast::PipeOp::Await => IrPipelineStep::Await,
1559 ast::PipeOp::Match(_) => {
1560 IrPipelineStep::Identity
1563 }
1564 ast::PipeOp::TryMap(_) => {
1565 IrPipelineStep::Identity
1567 }
1568 ast::PipeOp::Named { prefix, body } => {
1569 let fn_name = prefix
1570 .iter()
1571 .map(|i| i.name.clone())
1572 .collect::<Vec<_>>()
1573 .join("·");
1574 IrPipelineStep::Call {
1575 function: fn_name,
1576 args: body
1577 .as_ref()
1578 .map(|b| vec![lower_expr(ctx, b)])
1579 .unwrap_or_default(),
1580 }
1581 }
1582 ast::PipeOp::Send(data) => IrPipelineStep::Protocol {
1584 operation: ProtocolOp::Send,
1585 config: Some(Box::new(lower_expr(ctx, data))),
1586 },
1587 ast::PipeOp::Recv => IrPipelineStep::Protocol {
1588 operation: ProtocolOp::Recv,
1589 config: None,
1590 },
1591 ast::PipeOp::Stream(handler) => IrPipelineStep::Protocol {
1592 operation: ProtocolOp::Stream,
1593 config: Some(Box::new(lower_expr(ctx, handler))),
1594 },
1595 ast::PipeOp::Connect(config) => IrPipelineStep::Protocol {
1596 operation: ProtocolOp::Connect,
1597 config: config.as_ref().map(|c| Box::new(lower_expr(ctx, c))),
1598 },
1599 ast::PipeOp::Close => IrPipelineStep::Protocol {
1600 operation: ProtocolOp::Close,
1601 config: None,
1602 },
1603 ast::PipeOp::Timeout(ms) => IrPipelineStep::Protocol {
1604 operation: ProtocolOp::Timeout,
1605 config: Some(Box::new(lower_expr(ctx, ms))),
1606 },
1607 ast::PipeOp::Retry { count, strategy } => IrPipelineStep::Protocol {
1608 operation: ProtocolOp::Retry,
1609 config: Some(Box::new(IrOperation::Tuple {
1610 elements: vec![
1611 lower_expr(ctx, count),
1612 strategy
1613 .as_ref()
1614 .map(|s| lower_expr(ctx, s))
1615 .unwrap_or(IrOperation::Literal {
1616 variant: LiteralVariant::String,
1617 value: serde_json::json!("exponential"),
1618 ty: IrType::Primitive {
1619 name: "str".to_string(),
1620 },
1621 evidence: IrEvidence::Known,
1622 }),
1623 ],
1624 ty: IrType::Infer,
1625 evidence: IrEvidence::Known,
1626 })),
1627 },
1628 _ => IrPipelineStep::Identity,
1629 }
1630}
1631
1632fn pipe_op_evidence(op: &ast::PipeOp) -> IrEvidence {
1633 match op {
1634 ast::PipeOp::Send(_)
1636 | ast::PipeOp::Recv
1637 | ast::PipeOp::Stream(_)
1638 | ast::PipeOp::Connect(_)
1639 | ast::PipeOp::Close => IrEvidence::Reported,
1640
1641 ast::PipeOp::Validate {
1643 target_evidence, ..
1644 } => ast_evidence_to_ir(*target_evidence),
1645 ast::PipeOp::Assume {
1646 target_evidence, ..
1647 } => ast_evidence_to_ir(*target_evidence),
1648 ast::PipeOp::AssertEvidence(_) => IrEvidence::Known, _ => IrEvidence::Known,
1651 }
1652}
1653
1654fn ast_evidence_to_ir(ev: ast::Evidentiality) -> IrEvidence {
1655 match ev {
1656 ast::Evidentiality::Known => IrEvidence::Known,
1657 ast::Evidentiality::Uncertain | ast::Evidentiality::Predicted => IrEvidence::Uncertain,
1658 ast::Evidentiality::Reported => IrEvidence::Reported,
1659 ast::Evidentiality::Paradox => IrEvidence::Paradox,
1660 }
1661}
1662
1663fn lower_literal(lit: &ast::Literal) -> IrOperation {
1664 match lit {
1665 ast::Literal::Int {
1666 value,
1667 base,
1668 suffix,
1669 } => IrOperation::Literal {
1670 variant: LiteralVariant::Int,
1671 value: serde_json::json!({
1672 "value": value,
1673 "base": format!("{:?}", base),
1674 "suffix": suffix
1675 }),
1676 ty: suffix
1677 .as_ref()
1678 .map(|s| IrType::Primitive { name: s.clone() })
1679 .unwrap_or(IrType::Primitive {
1680 name: "i64".to_string(),
1681 }),
1682 evidence: IrEvidence::Known,
1683 },
1684 ast::Literal::Float { value, suffix } => IrOperation::Literal {
1685 variant: LiteralVariant::Float,
1686 value: serde_json::json!({"value": value, "suffix": suffix}),
1687 ty: suffix
1688 .as_ref()
1689 .map(|s| IrType::Primitive { name: s.clone() })
1690 .unwrap_or(IrType::Primitive {
1691 name: "f64".to_string(),
1692 }),
1693 evidence: IrEvidence::Known,
1694 },
1695 ast::Literal::String(s) | ast::Literal::MultiLineString(s) | ast::Literal::RawString(s) => {
1696 IrOperation::Literal {
1697 variant: LiteralVariant::String,
1698 value: serde_json::Value::String(s.clone()),
1699 ty: IrType::Primitive {
1700 name: "str".to_string(),
1701 },
1702 evidence: IrEvidence::Known,
1703 }
1704 }
1705 ast::Literal::Char(c) => IrOperation::Literal {
1706 variant: LiteralVariant::Char,
1707 value: serde_json::Value::String(c.to_string()),
1708 ty: IrType::Primitive {
1709 name: "char".to_string(),
1710 },
1711 evidence: IrEvidence::Known,
1712 },
1713 ast::Literal::Bool(b) => IrOperation::Literal {
1714 variant: LiteralVariant::Bool,
1715 value: serde_json::Value::Bool(*b),
1716 ty: IrType::Primitive {
1717 name: "bool".to_string(),
1718 },
1719 evidence: IrEvidence::Known,
1720 },
1721 ast::Literal::Null | ast::Literal::Empty => IrOperation::Literal {
1722 variant: LiteralVariant::Null,
1723 value: serde_json::Value::Null,
1724 ty: IrType::Unit,
1725 evidence: IrEvidence::Known,
1726 },
1727 _ => IrOperation::Literal {
1728 variant: LiteralVariant::Null,
1729 value: serde_json::Value::Null,
1730 ty: IrType::Infer,
1731 evidence: IrEvidence::Known,
1732 },
1733 }
1734}
1735
1736fn lower_literal_value(lit: &ast::Literal) -> serde_json::Value {
1737 match lit {
1738 ast::Literal::Int { value, .. } => serde_json::json!(value),
1739 ast::Literal::Float { value, .. } => serde_json::json!(value),
1740 ast::Literal::String(s) => serde_json::Value::String(s.clone()),
1741 ast::Literal::Bool(b) => serde_json::Value::Bool(*b),
1742 ast::Literal::Char(c) => serde_json::Value::String(c.to_string()),
1743 ast::Literal::Null => serde_json::Value::Null,
1744 _ => serde_json::Value::Null,
1745 }
1746}
1747
1748fn lower_binop(op: ast::BinOp) -> BinaryOp {
1749 match op {
1750 ast::BinOp::Add => BinaryOp::Add,
1751 ast::BinOp::Sub => BinaryOp::Sub,
1752 ast::BinOp::Mul => BinaryOp::Mul,
1753 ast::BinOp::Div => BinaryOp::Div,
1754 ast::BinOp::Rem => BinaryOp::Rem,
1755 ast::BinOp::Pow => BinaryOp::Pow,
1756 ast::BinOp::And => BinaryOp::And,
1757 ast::BinOp::Or => BinaryOp::Or,
1758 ast::BinOp::BitAnd => BinaryOp::BitAnd,
1759 ast::BinOp::BitOr => BinaryOp::BitOr,
1760 ast::BinOp::BitXor => BinaryOp::BitXor,
1761 ast::BinOp::Shl => BinaryOp::Shl,
1762 ast::BinOp::Shr => BinaryOp::Shr,
1763 ast::BinOp::Eq => BinaryOp::Eq,
1764 ast::BinOp::Ne => BinaryOp::Ne,
1765 ast::BinOp::Lt => BinaryOp::Lt,
1766 ast::BinOp::Le => BinaryOp::Le,
1767 ast::BinOp::Gt => BinaryOp::Gt,
1768 ast::BinOp::Ge => BinaryOp::Ge,
1769 ast::BinOp::Concat => BinaryOp::Concat,
1770 ast::BinOp::MatMul => BinaryOp::MatMul,
1771 ast::BinOp::Hadamard => BinaryOp::Hadamard,
1772 ast::BinOp::TensorProd => BinaryOp::TensorProd,
1773 }
1774}
1775
1776fn lower_unaryop(op: ast::UnaryOp) -> UnaryOp {
1777 match op {
1778 ast::UnaryOp::Neg => UnaryOp::Neg,
1779 ast::UnaryOp::Not => UnaryOp::Not,
1780 ast::UnaryOp::Deref => UnaryOp::Deref,
1781 ast::UnaryOp::Ref => UnaryOp::Ref,
1782 ast::UnaryOp::RefMut => UnaryOp::RefMut,
1783 }
1784}
1785
1786fn lower_morpheme_kind(kind: ast::MorphemeKind) -> MorphemeKind {
1787 match kind {
1788 ast::MorphemeKind::Transform => MorphemeKind::Transform,
1789 ast::MorphemeKind::Filter => MorphemeKind::Filter,
1790 ast::MorphemeKind::Sort => MorphemeKind::Sort,
1791 ast::MorphemeKind::Reduce => MorphemeKind::Reduce,
1792 ast::MorphemeKind::Lambda => MorphemeKind::Lambda,
1793 ast::MorphemeKind::Sum => MorphemeKind::Sum,
1794 ast::MorphemeKind::Product => MorphemeKind::Product,
1795 ast::MorphemeKind::First => MorphemeKind::First,
1796 ast::MorphemeKind::Last => MorphemeKind::Last,
1797 ast::MorphemeKind::Middle => MorphemeKind::Middle,
1798 ast::MorphemeKind::Choice => MorphemeKind::Choice,
1799 ast::MorphemeKind::Nth => MorphemeKind::Nth,
1800 ast::MorphemeKind::Next => MorphemeKind::Next,
1801 }
1802}
1803
1804fn morpheme_symbol(kind: ast::MorphemeKind) -> &'static str {
1805 match kind {
1806 ast::MorphemeKind::Transform => "τ",
1807 ast::MorphemeKind::Filter => "φ",
1808 ast::MorphemeKind::Sort => "σ",
1809 ast::MorphemeKind::Reduce => "ρ",
1810 ast::MorphemeKind::Lambda => "λ",
1811 ast::MorphemeKind::Sum => "Σ",
1812 ast::MorphemeKind::Product => "Π",
1813 ast::MorphemeKind::First => "α",
1814 ast::MorphemeKind::Last => "ω",
1815 ast::MorphemeKind::Middle => "μ",
1816 ast::MorphemeKind::Choice => "χ",
1817 ast::MorphemeKind::Nth => "ν",
1818 ast::MorphemeKind::Next => "ξ",
1819 }
1820}
1821
1822fn lower_http_method(method: ast::HttpMethod) -> HttpMethod {
1823 match method {
1824 ast::HttpMethod::Get => HttpMethod::Get,
1825 ast::HttpMethod::Post => HttpMethod::Post,
1826 ast::HttpMethod::Put => HttpMethod::Put,
1827 ast::HttpMethod::Delete => HttpMethod::Delete,
1828 ast::HttpMethod::Patch => HttpMethod::Patch,
1829 ast::HttpMethod::Head => HttpMethod::Head,
1830 ast::HttpMethod::Options => HttpMethod::Options,
1831 ast::HttpMethod::Connect => HttpMethod::Connect,
1832 ast::HttpMethod::Trace => HttpMethod::Trace,
1833 }
1834}
1835
1836fn get_operation_evidence(op: &IrOperation) -> IrEvidence {
1837 match op {
1838 IrOperation::Literal { evidence, .. }
1839 | IrOperation::Var { evidence, .. }
1840 | IrOperation::Let { evidence, .. }
1841 | IrOperation::Binary { evidence, .. }
1842 | IrOperation::Unary { evidence, .. }
1843 | IrOperation::Call { evidence, .. }
1844 | IrOperation::MethodCall { evidence, .. }
1845 | IrOperation::Closure { evidence, .. }
1846 | IrOperation::If { evidence, .. }
1847 | IrOperation::Match { evidence, .. }
1848 | IrOperation::LetMatch { evidence, .. }
1849 | IrOperation::Loop { evidence, .. }
1850 | IrOperation::Block { evidence, .. }
1851 | IrOperation::Pipeline { evidence, .. }
1852 | IrOperation::Morpheme { evidence, .. }
1853 | IrOperation::Fork { evidence, .. }
1854 | IrOperation::Identity { evidence, .. }
1855 | IrOperation::Array { evidence, .. }
1856 | IrOperation::Tuple { evidence, .. }
1857 | IrOperation::StructInit { evidence, .. }
1858 | IrOperation::Field { evidence, .. }
1859 | IrOperation::Index { evidence, .. }
1860 | IrOperation::Assign { evidence, .. }
1861 | IrOperation::Break { evidence, .. }
1862 | IrOperation::Continue { evidence }
1863 | IrOperation::Return { evidence, .. }
1864 | IrOperation::Incorporation { evidence, .. }
1865 | IrOperation::Affect { evidence, .. }
1866 | IrOperation::HttpRequest { evidence, .. }
1867 | IrOperation::GrpcCall { evidence, .. }
1868 | IrOperation::WebSocket { evidence, .. }
1869 | IrOperation::KafkaOp { evidence, .. }
1870 | IrOperation::Await { evidence, .. }
1871 | IrOperation::Unsafe { evidence, .. }
1872 | IrOperation::Async { evidence, .. }
1873 | IrOperation::Cast { evidence, .. }
1874 | IrOperation::Try { evidence, .. } => *evidence,
1875 IrOperation::EvidenceCoerce { to_evidence, .. } => *to_evidence,
1876 }
1877}
1878
1879fn expr_to_string(e: &ast::Expr) -> String {
1880 match e {
1881 ast::Expr::Path(p) => p
1882 .segments
1883 .iter()
1884 .map(|s| s.ident.name.clone())
1885 .collect::<Vec<_>>()
1886 .join("::"),
1887 ast::Expr::Literal(ast::Literal::String(s)) => s.clone(),
1888 _ => "dynamic".to_string(),
1889 }
1890}
1891
1892fn lower_struct_def(_ctx: &mut LoweringContext, s: &ast::StructDef) -> IrTypeDef {
1895 let fields = match &s.fields {
1896 ast::StructFields::Named(fields) => fields
1897 .iter()
1898 .map(|f| IrField {
1899 name: f.name.name.clone(),
1900 ty: lower_type_expr(&f.ty),
1901 visibility: lower_visibility(f.visibility),
1902 })
1903 .collect(),
1904 ast::StructFields::Tuple(types) => types
1905 .iter()
1906 .enumerate()
1907 .map(|(i, t)| IrField {
1908 name: format!("{}", i),
1909 ty: lower_type_expr(t),
1910 visibility: IrVisibility::Public,
1911 })
1912 .collect(),
1913 ast::StructFields::Unit => vec![],
1914 };
1915
1916 IrTypeDef::Struct {
1917 name: s.name.name.clone(),
1918 generics: lower_generics(&s.generics),
1919 fields,
1920 span: None,
1921 }
1922}
1923
1924fn lower_enum_def(_ctx: &mut LoweringContext, e: &ast::EnumDef) -> IrTypeDef {
1925 let variants = e
1926 .variants
1927 .iter()
1928 .map(|v| {
1929 let fields = match &v.fields {
1930 ast::StructFields::Named(fields) => Some(
1931 fields
1932 .iter()
1933 .map(|f| IrField {
1934 name: f.name.name.clone(),
1935 ty: lower_type_expr(&f.ty),
1936 visibility: lower_visibility(f.visibility),
1937 })
1938 .collect(),
1939 ),
1940 ast::StructFields::Tuple(types) => Some(
1941 types
1942 .iter()
1943 .enumerate()
1944 .map(|(i, t)| IrField {
1945 name: format!("{}", i),
1946 ty: lower_type_expr(t),
1947 visibility: IrVisibility::Public,
1948 })
1949 .collect(),
1950 ),
1951 ast::StructFields::Unit => None,
1952 };
1953
1954 IrVariant {
1955 name: v.name.name.clone(),
1956 fields,
1957 discriminant: None,
1958 }
1959 })
1960 .collect();
1961
1962 IrTypeDef::Enum {
1963 name: e.name.name.clone(),
1964 generics: lower_generics(&e.generics),
1965 variants,
1966 span: None,
1967 }
1968}
1969
1970fn lower_type_alias(_ctx: &mut LoweringContext, t: &ast::TypeAlias) -> IrTypeDef {
1971 IrTypeDef::TypeAlias {
1972 name: t.name.name.clone(),
1973 generics: lower_generics(&t.generics),
1974 target: lower_type_expr(&t.ty),
1975 span: None,
1976 }
1977}
1978
1979fn lower_trait_def(ctx: &mut LoweringContext, t: &ast::TraitDef) -> IrTraitDef {
1980 let methods = t
1981 .items
1982 .iter()
1983 .filter_map(|item| match item {
1984 ast::TraitItem::Function(f) => lower_function(ctx, f),
1985 _ => None,
1986 })
1987 .collect();
1988
1989 IrTraitDef {
1990 name: t.name.name.clone(),
1991 generics: lower_generics(&t.generics),
1992 super_traits: t.supertraits.iter().map(type_expr_to_string).collect(),
1993 methods,
1994 span: None,
1995 }
1996}
1997
1998fn lower_impl_block(ctx: &mut LoweringContext, i: &ast::ImplBlock) -> IrImplBlock {
1999 let methods = i
2000 .items
2001 .iter()
2002 .filter_map(|item| match item {
2003 ast::ImplItem::Function(f) => lower_function(ctx, f),
2004 _ => None,
2005 })
2006 .collect();
2007
2008 IrImplBlock {
2009 trait_name: i.trait_.as_ref().map(|t| {
2010 t.segments
2011 .iter()
2012 .map(|s| s.ident.name.clone())
2013 .collect::<Vec<_>>()
2014 .join("::")
2015 }),
2016 target_type: lower_type_expr(&i.self_ty),
2017 generics: lower_generics(&i.generics),
2018 methods,
2019 span: None,
2020 }
2021}
2022
2023fn lower_const_def(ctx: &mut LoweringContext, c: &ast::ConstDef) -> IrConstant {
2024 IrConstant {
2025 name: c.name.name.clone(),
2026 ty: lower_type_expr(&c.ty),
2027 value: lower_expr(ctx, &c.value),
2028 visibility: lower_visibility(c.visibility),
2029 span: None,
2030 }
2031}