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