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 lower_item(ctx: &mut LoweringContext, module: &mut IrModule, item: &ast::Item) {
96 match item {
97 ast::Item::Function(f) => {
98 if let Some(ir_fn) = lower_function(ctx, f) {
99 module.functions.push(ir_fn);
100 }
101 }
102 ast::Item::Struct(s) => {
103 module.types.push(lower_struct_def(ctx, s));
104 }
105 ast::Item::Enum(e) => {
106 module.types.push(lower_enum_def(ctx, e));
107 }
108 ast::Item::Trait(t) => {
109 module.traits.push(lower_trait_def(ctx, t));
110 }
111 ast::Item::Impl(i) => {
112 module.impls.push(lower_impl_block(ctx, i));
113 }
114 ast::Item::TypeAlias(t) => {
115 module.types.push(lower_type_alias(ctx, t));
116 }
117 ast::Item::Const(c) => {
118 module.constants.push(lower_const_def(ctx, c));
119 }
120 ast::Item::Module(m) => {
121 if let Some(items) = &m.items {
122 for item in items {
123 lower_item(ctx, module, &item.node);
124 }
125 }
126 }
127 ast::Item::Static(_)
128 | ast::Item::Actor(_)
129 | ast::Item::Use(_)
130 | ast::Item::ExternBlock(_) => {
131 }
133 }
134}
135
136fn lower_function(ctx: &mut LoweringContext, f: &ast::Function) -> Option<IrFunction> {
137 let id = ctx.ids.next("fn");
138 ctx.push_scope();
139
140 let params: Vec<IrParam> = f.params.iter().map(|p| lower_param(ctx, p)).collect();
141
142 let return_type = f
143 .return_type
144 .as_ref()
145 .map(|t| lower_type_expr(t))
146 .unwrap_or(IrType::Unit);
147
148 let body = f.body.as_ref().map(|b| lower_block(ctx, b));
149
150 let mut attributes = Vec::new();
151 if f.is_async {
152 attributes.push("async".to_string());
153 }
154 if f.attrs.inline.is_some() {
155 attributes.push("inline".to_string());
156 }
157 if f.attrs.naked {
158 attributes.push("naked".to_string());
159 }
160 if f.attrs.no_mangle {
161 attributes.push("no_mangle".to_string());
162 }
163
164 ctx.pop_scope();
165
166 Some(IrFunction {
167 name: f.name.name.clone(),
168 id,
169 visibility: lower_visibility(f.visibility),
170 generics: lower_generics(&f.generics),
171 params,
172 return_type,
173 body,
174 attributes,
175 is_async: f.is_async,
176 span: None,
177 })
178}
179
180fn lower_param(ctx: &mut LoweringContext, p: &ast::Param) -> IrParam {
181 let name = extract_pattern_name(&p.pattern);
182 let evidence = extract_pattern_evidence(&p.pattern);
183 ctx.bind_var(&name);
184
185 IrParam {
186 name,
187 ty: lower_type_expr(&p.ty),
188 evidence,
189 }
190}
191
192fn extract_pattern_name(p: &ast::Pattern) -> String {
193 match p {
194 ast::Pattern::Ident { name, .. } => name.name.clone(),
195 ast::Pattern::Tuple(pats) if !pats.is_empty() => extract_pattern_name(&pats[0]),
196 _ => "_".to_string(),
197 }
198}
199
200fn extract_pattern_evidence(p: &ast::Pattern) -> IrEvidence {
201 match p {
202 ast::Pattern::Ident { evidentiality, .. } => evidentiality
203 .map(lower_evidentiality)
204 .unwrap_or(IrEvidence::Known),
205 _ => IrEvidence::Known,
206 }
207}
208
209fn lower_visibility(v: ast::Visibility) -> IrVisibility {
210 match v {
211 ast::Visibility::Public => IrVisibility::Public,
212 ast::Visibility::Private => IrVisibility::Private,
213 ast::Visibility::Crate => IrVisibility::Crate,
214 ast::Visibility::Super => IrVisibility::Private,
215 }
216}
217
218fn lower_evidentiality(e: ast::Evidentiality) -> IrEvidence {
219 match e {
220 ast::Evidentiality::Known => IrEvidence::Known,
221 ast::Evidentiality::Uncertain => IrEvidence::Uncertain,
222 ast::Evidentiality::Reported => IrEvidence::Reported,
223 ast::Evidentiality::Paradox => IrEvidence::Paradox,
224 }
225}
226
227fn lower_generics(g: &Option<ast::Generics>) -> Vec<IrGenericParam> {
228 g.as_ref()
229 .map(|g| {
230 g.params
231 .iter()
232 .filter_map(|p| match p {
233 ast::GenericParam::Type { name, bounds, .. } => Some(IrGenericParam {
234 name: name.name.clone(),
235 bounds: bounds.iter().map(type_expr_to_string).collect(),
236 }),
237 ast::GenericParam::Const { name, .. } => Some(IrGenericParam {
238 name: name.name.clone(),
239 bounds: vec!["const".to_string()],
240 }),
241 ast::GenericParam::Lifetime(l) => Some(IrGenericParam {
242 name: l.clone(),
243 bounds: vec!["lifetime".to_string()],
244 }),
245 })
246 .collect()
247 })
248 .unwrap_or_default()
249}
250
251fn type_expr_to_string(t: &ast::TypeExpr) -> String {
252 match t {
253 ast::TypeExpr::Path(p) => p
254 .segments
255 .iter()
256 .map(|s| s.ident.name.clone())
257 .collect::<Vec<_>>()
258 .join("::"),
259 _ => "?".to_string(),
260 }
261}
262
263fn lower_type_expr(t: &ast::TypeExpr) -> IrType {
264 match t {
265 ast::TypeExpr::Path(p) => {
266 let name = p
267 .segments
268 .iter()
269 .map(|s| s.ident.name.clone())
270 .collect::<Vec<_>>()
271 .join("::");
272
273 match name.as_str() {
275 "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64"
276 | "u128" | "usize" | "f32" | "f64" | "bool" | "char" | "str" => {
277 IrType::Primitive { name }
278 }
279 "()" => IrType::Unit,
280 "!" | "never" => IrType::Never,
281 _ => {
282 let generics = p
283 .segments
284 .last()
285 .and_then(|s| s.generics.as_ref())
286 .map(|gs| gs.iter().map(lower_type_expr).collect())
287 .unwrap_or_default();
288
289 IrType::Named { name, generics }
290 }
291 }
292 }
293 ast::TypeExpr::Reference { mutable, inner } => IrType::Reference {
294 mutable: *mutable,
295 inner: Box::new(lower_type_expr(inner)),
296 },
297 ast::TypeExpr::Pointer { mutable, inner } => IrType::Pointer {
298 mutable: *mutable,
299 inner: Box::new(lower_type_expr(inner)),
300 },
301 ast::TypeExpr::Array { element, size: _ } => IrType::Array {
302 element: Box::new(lower_type_expr(element)),
303 size: None, },
305 ast::TypeExpr::Slice(inner) => IrType::Slice {
306 element: Box::new(lower_type_expr(inner)),
307 },
308 ast::TypeExpr::Tuple(elements) => IrType::Tuple {
309 elements: elements.iter().map(lower_type_expr).collect(),
310 },
311 ast::TypeExpr::Function {
312 params,
313 return_type,
314 } => IrType::Function {
315 params: params.iter().map(lower_type_expr).collect(),
316 return_type: Box::new(
317 return_type
318 .as_ref()
319 .map(|r| lower_type_expr(r))
320 .unwrap_or(IrType::Unit),
321 ),
322 is_async: false,
323 },
324 ast::TypeExpr::Evidential {
325 inner,
326 evidentiality,
327 error_type,
328 } => {
329 let _ = error_type; IrType::Evidential {
333 inner: Box::new(lower_type_expr(inner)),
334 evidence: lower_evidentiality(*evidentiality),
335 }
336 }
337 ast::TypeExpr::Cycle { .. } => IrType::Cycle { modulus: 0 },
338 ast::TypeExpr::Simd { element, lanes } => IrType::Simd {
339 element: Box::new(lower_type_expr(element)),
340 lanes: *lanes as usize,
341 },
342 ast::TypeExpr::Atomic(inner) => IrType::Atomic {
343 inner: Box::new(lower_type_expr(inner)),
344 },
345 ast::TypeExpr::Never => IrType::Never,
346 ast::TypeExpr::Infer => IrType::Infer,
347 }
348}
349
350fn lower_block(ctx: &mut LoweringContext, block: &ast::Block) -> IrOperation {
351 ctx.push_scope();
352
353 let mut statements: Vec<IrOperation> = block
354 .stmts
355 .iter()
356 .filter_map(|s| lower_stmt(ctx, s))
357 .collect();
358
359 if let Some(expr) = &block.expr {
360 statements.push(lower_expr(ctx, expr));
361 }
362
363 ctx.pop_scope();
364
365 IrOperation::Block {
366 statements,
367 ty: IrType::Infer,
368 evidence: IrEvidence::Known,
369 }
370}
371
372fn lower_stmt(ctx: &mut LoweringContext, stmt: &ast::Stmt) -> Option<IrOperation> {
373 match stmt {
374 ast::Stmt::Let { pattern, ty, init } => {
375 let ir_pattern = lower_pattern(ctx, pattern);
376 let type_annotation = ty.as_ref().map(lower_type_expr);
377 let init_expr = init
378 .as_ref()
379 .map(|e| Box::new(lower_expr(ctx, e)))
380 .unwrap_or_else(|| {
381 Box::new(IrOperation::Literal {
382 variant: LiteralVariant::Null,
383 value: serde_json::Value::Null,
384 ty: IrType::Unit,
385 evidence: IrEvidence::Known,
386 })
387 });
388
389 Some(IrOperation::Let {
390 pattern: ir_pattern,
391 type_annotation,
392 init: init_expr,
393 evidence: IrEvidence::Known,
394 })
395 }
396 ast::Stmt::Expr(e) => Some(lower_expr(ctx, e)),
397 ast::Stmt::Semi(e) => Some(lower_expr(ctx, e)),
398 ast::Stmt::Item(_) => None, }
400}
401
402fn lower_pattern(ctx: &mut LoweringContext, pattern: &ast::Pattern) -> IrPattern {
403 match pattern {
404 ast::Pattern::Ident {
405 mutable,
406 name,
407 evidentiality,
408 } => {
409 ctx.bind_var(&name.name);
410 IrPattern::Ident {
411 name: name.name.clone(),
412 mutable: *mutable,
413 evidence: evidentiality.map(lower_evidentiality),
414 }
415 }
416 ast::Pattern::Tuple(pats) => IrPattern::Tuple {
417 elements: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
418 },
419 ast::Pattern::Struct { path, fields, rest } => IrPattern::Struct {
420 path: path
421 .segments
422 .iter()
423 .map(|s| s.ident.name.clone())
424 .collect::<Vec<_>>()
425 .join("::"),
426 fields: fields
427 .iter()
428 .map(|f| {
429 (
430 f.name.name.clone(),
431 f.pattern.as_ref().map(|p| lower_pattern(ctx, p)).unwrap_or(
432 IrPattern::Ident {
433 name: f.name.name.clone(),
434 mutable: false,
435 evidence: None,
436 },
437 ),
438 )
439 })
440 .collect(),
441 rest: *rest,
442 },
443 ast::Pattern::TupleStruct { path, fields } => IrPattern::TupleStruct {
444 path: path
445 .segments
446 .iter()
447 .map(|s| s.ident.name.clone())
448 .collect::<Vec<_>>()
449 .join("::"),
450 fields: fields.iter().map(|p| lower_pattern(ctx, p)).collect(),
451 },
452 ast::Pattern::Slice(pats) => IrPattern::Slice {
453 elements: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
454 },
455 ast::Pattern::Or(pats) => IrPattern::Or {
456 patterns: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
457 },
458 ast::Pattern::Literal(lit) => IrPattern::Literal {
459 value: lower_literal_value(lit),
460 },
461 ast::Pattern::Range {
462 start,
463 end,
464 inclusive,
465 } => IrPattern::Range {
466 start: start.as_ref().map(|p| Box::new(lower_pattern(ctx, p))),
467 end: end.as_ref().map(|p| Box::new(lower_pattern(ctx, p))),
468 inclusive: *inclusive,
469 },
470 ast::Pattern::Wildcard | ast::Pattern::Rest => IrPattern::Wildcard,
471 }
472}
473
474fn lower_expr(ctx: &mut LoweringContext, expr: &ast::Expr) -> IrOperation {
475 match expr {
476 ast::Expr::Literal(lit) => lower_literal(lit),
477
478 ast::Expr::Path(path) => {
479 let name = path
480 .segments
481 .iter()
482 .map(|s| s.ident.name.clone())
483 .collect::<Vec<_>>()
484 .join("::");
485 let id = ctx.lookup_var(&name).unwrap_or_else(|| name.clone());
486
487 IrOperation::Var {
488 name,
489 id,
490 ty: IrType::Infer,
491 evidence: IrEvidence::Known,
492 }
493 }
494
495 ast::Expr::Binary { left, op, right } => {
496 let left_ir = lower_expr(ctx, left);
497 let right_ir = lower_expr(ctx, right);
498 let left_ev = get_operation_evidence(&left_ir);
499 let right_ev = get_operation_evidence(&right_ir);
500
501 IrOperation::Binary {
502 operator: lower_binop(*op),
503 left: Box::new(left_ir),
504 right: Box::new(right_ir),
505 ty: IrType::Infer,
506 evidence: left_ev.join(right_ev),
507 }
508 }
509
510 ast::Expr::Unary { op, expr: inner } => {
511 let inner_ir = lower_expr(ctx, inner);
512 let evidence = get_operation_evidence(&inner_ir);
513
514 IrOperation::Unary {
515 operator: lower_unaryop(*op),
516 operand: Box::new(inner_ir),
517 ty: IrType::Infer,
518 evidence,
519 }
520 }
521
522 ast::Expr::Call { func, args } => {
523 let func_name = match func.as_ref() {
524 ast::Expr::Path(p) => p
525 .segments
526 .iter()
527 .map(|s| s.ident.name.clone())
528 .collect::<Vec<_>>()
529 .join("::"),
530 _ => "anonymous".to_string(),
531 };
532
533 let args_ir: Vec<IrOperation> = args.iter().map(|a| lower_expr(ctx, a)).collect();
534 let evidence = args_ir
535 .iter()
536 .map(get_operation_evidence)
537 .fold(IrEvidence::Known, |acc, e| acc.join(e));
538
539 IrOperation::Call {
540 function: func_name.clone(),
541 function_id: format!("fn_{}", func_name),
542 args: args_ir,
543 type_args: vec![],
544 ty: IrType::Infer,
545 evidence,
546 }
547 }
548
549 ast::Expr::MethodCall {
550 receiver,
551 method,
552 args,
553 } => {
554 let receiver_ir = lower_expr(ctx, receiver);
555 let args_ir: Vec<IrOperation> = args.iter().map(|a| lower_expr(ctx, a)).collect();
556 let evidence = std::iter::once(get_operation_evidence(&receiver_ir))
557 .chain(args_ir.iter().map(get_operation_evidence))
558 .fold(IrEvidence::Known, |acc, e| acc.join(e));
559
560 IrOperation::MethodCall {
561 receiver: Box::new(receiver_ir),
562 method: method.name.clone(),
563 args: args_ir,
564 type_args: vec![],
565 ty: IrType::Infer,
566 evidence,
567 }
568 }
569
570 ast::Expr::Pipe { expr, operations } => lower_pipeline(ctx, expr, operations),
571
572 ast::Expr::If {
573 condition,
574 then_branch,
575 else_branch,
576 } => {
577 let cond_ir = lower_expr(ctx, condition);
578 let then_ir = lower_block(ctx, then_branch);
579 let else_ir = else_branch.as_ref().map(|e| Box::new(lower_expr(ctx, e)));
580
581 let evidence = get_operation_evidence(&cond_ir)
582 .join(get_operation_evidence(&then_ir))
583 .join(
584 else_ir
585 .as_ref()
586 .map(|e| get_operation_evidence(e))
587 .unwrap_or(IrEvidence::Known),
588 );
589
590 IrOperation::If {
591 condition: Box::new(cond_ir),
592 then_branch: Box::new(then_ir),
593 else_branch: else_ir,
594 ty: IrType::Infer,
595 evidence,
596 }
597 }
598
599 ast::Expr::Match {
600 expr: scrutinee,
601 arms,
602 } => {
603 let scrutinee_ir = lower_expr(ctx, scrutinee);
604 let arms_ir: Vec<IrMatchArm> = arms
605 .iter()
606 .map(|arm| {
607 ctx.push_scope();
608 let pattern = lower_pattern(ctx, &arm.pattern);
609 let guard = arm.guard.as_ref().map(|g| lower_expr(ctx, g));
610 let body = lower_expr(ctx, &arm.body);
611 ctx.pop_scope();
612 IrMatchArm {
613 pattern,
614 guard,
615 body,
616 }
617 })
618 .collect();
619
620 IrOperation::Match {
621 scrutinee: Box::new(scrutinee_ir),
622 arms: arms_ir,
623 ty: IrType::Infer,
624 evidence: IrEvidence::Known,
625 }
626 }
627
628 ast::Expr::Loop(block) => IrOperation::Loop {
629 variant: LoopVariant::Infinite,
630 condition: None,
631 iterator: None,
632 body: Box::new(lower_block(ctx, block)),
633 ty: IrType::Never,
634 evidence: IrEvidence::Known,
635 },
636
637 ast::Expr::While { condition, body } => IrOperation::Loop {
638 variant: LoopVariant::While,
639 condition: Some(Box::new(lower_expr(ctx, condition))),
640 iterator: None,
641 body: Box::new(lower_block(ctx, body)),
642 ty: IrType::Unit,
643 evidence: IrEvidence::Known,
644 },
645
646 ast::Expr::For {
647 pattern,
648 iter,
649 body,
650 } => {
651 ctx.push_scope();
652 let pat = lower_pattern(ctx, pattern);
653 let iter_ir = lower_expr(ctx, iter);
654 let body_ir = lower_block(ctx, body);
655 ctx.pop_scope();
656
657 IrOperation::Loop {
658 variant: LoopVariant::For,
659 condition: None,
660 iterator: Some(IrForIterator {
661 pattern: pat,
662 iterable: Box::new(iter_ir),
663 }),
664 body: Box::new(body_ir),
665 ty: IrType::Unit,
666 evidence: IrEvidence::Known,
667 }
668 }
669
670 ast::Expr::Closure { params, body } => {
671 ctx.push_scope();
672 let params_ir: Vec<IrParam> = params
673 .iter()
674 .map(|p| {
675 let name = extract_pattern_name(&p.pattern);
676 ctx.bind_var(&name);
677 IrParam {
678 name,
679 ty: p.ty.as_ref().map(lower_type_expr).unwrap_or(IrType::Infer),
680 evidence: IrEvidence::Known,
681 }
682 })
683 .collect();
684 let body_ir = lower_expr(ctx, body);
685 ctx.pop_scope();
686
687 IrOperation::Closure {
688 params: params_ir,
689 body: Box::new(body_ir),
690 captures: vec![],
691 ty: IrType::Infer,
692 evidence: IrEvidence::Known,
693 }
694 }
695
696 ast::Expr::Block(block) => lower_block(ctx, block),
697
698 ast::Expr::Array(elements) => {
699 let elements_ir: Vec<IrOperation> =
700 elements.iter().map(|e| lower_expr(ctx, e)).collect();
701 IrOperation::Array {
702 elements: elements_ir,
703 ty: IrType::Infer,
704 evidence: IrEvidence::Known,
705 }
706 }
707
708 ast::Expr::Tuple(elements) => {
709 let elements_ir: Vec<IrOperation> =
710 elements.iter().map(|e| lower_expr(ctx, e)).collect();
711 IrOperation::Tuple {
712 elements: elements_ir,
713 ty: IrType::Infer,
714 evidence: IrEvidence::Known,
715 }
716 }
717
718 ast::Expr::Struct { path, fields, rest } => {
719 let name = path
720 .segments
721 .iter()
722 .map(|s| s.ident.name.clone())
723 .collect::<Vec<_>>()
724 .join("::");
725
726 let fields_ir: Vec<(String, IrOperation)> = fields
727 .iter()
728 .map(|f| {
729 let value = f
730 .value
731 .as_ref()
732 .map(|v| lower_expr(ctx, v))
733 .unwrap_or_else(|| IrOperation::Var {
734 name: f.name.name.clone(),
735 id: ctx.lookup_var(&f.name.name).unwrap_or_default(),
736 ty: IrType::Infer,
737 evidence: IrEvidence::Known,
738 });
739 (f.name.name.clone(), value)
740 })
741 .collect();
742
743 IrOperation::StructInit {
744 name,
745 fields: fields_ir,
746 rest: rest.as_ref().map(|r| Box::new(lower_expr(ctx, r))),
747 ty: IrType::Infer,
748 evidence: IrEvidence::Known,
749 }
750 }
751
752 ast::Expr::Field { expr: inner, field } => IrOperation::Field {
753 expr: Box::new(lower_expr(ctx, inner)),
754 field: field.name.clone(),
755 ty: IrType::Infer,
756 evidence: IrEvidence::Known,
757 },
758
759 ast::Expr::Index { expr: inner, index } => IrOperation::Index {
760 expr: Box::new(lower_expr(ctx, inner)),
761 index: Box::new(lower_expr(ctx, index)),
762 ty: IrType::Infer,
763 evidence: IrEvidence::Known,
764 },
765
766 ast::Expr::Assign { target, value } => IrOperation::Assign {
767 target: Box::new(lower_expr(ctx, target)),
768 value: Box::new(lower_expr(ctx, value)),
769 evidence: IrEvidence::Known,
770 },
771
772 ast::Expr::Return(value) => IrOperation::Return {
773 value: value.as_ref().map(|v| Box::new(lower_expr(ctx, v))),
774 evidence: IrEvidence::Known,
775 },
776
777 ast::Expr::Break(value) => IrOperation::Break {
778 value: value.as_ref().map(|v| Box::new(lower_expr(ctx, v))),
779 evidence: IrEvidence::Known,
780 },
781
782 ast::Expr::Continue => IrOperation::Continue {
783 evidence: IrEvidence::Known,
784 },
785
786 ast::Expr::Await {
787 expr: inner,
788 evidentiality,
789 } => {
790 let inner_ir = lower_expr(ctx, inner);
791 let evidence = match evidentiality {
793 Some(ast::Evidentiality::Known) => IrEvidence::Known,
794 Some(ast::Evidentiality::Uncertain) => IrEvidence::Uncertain,
795 Some(ast::Evidentiality::Reported) => IrEvidence::Reported,
796 Some(ast::Evidentiality::Paradox) => IrEvidence::Uncertain, None => get_operation_evidence(&inner_ir),
798 };
799 IrOperation::Await {
800 expr: Box::new(inner_ir),
801 ty: IrType::Infer,
802 evidence,
803 }
804 }
805
806 ast::Expr::Try(inner) => {
807 let inner_ir = lower_expr(ctx, inner);
808 IrOperation::Try {
809 expr: Box::new(inner_ir),
810 ty: IrType::Infer,
811 evidence: IrEvidence::Uncertain, }
813 }
814
815 ast::Expr::Unsafe(block) => IrOperation::Unsafe {
816 body: Box::new(lower_block(ctx, block)),
817 ty: IrType::Infer,
818 evidence: IrEvidence::Paradox, },
820
821 ast::Expr::Cast { expr: inner, ty } => {
822 let inner_ir = lower_expr(ctx, inner);
823 let evidence = get_operation_evidence(&inner_ir);
824 IrOperation::Cast {
825 expr: Box::new(inner_ir),
826 target_type: lower_type_expr(ty),
827 ty: lower_type_expr(ty),
828 evidence,
829 }
830 }
831
832 ast::Expr::Evidential {
833 expr: inner,
834 evidentiality,
835 } => {
836 let inner_ir = lower_expr(ctx, inner);
837 let from_evidence = get_operation_evidence(&inner_ir);
838 let to_evidence = lower_evidentiality(*evidentiality);
839
840 IrOperation::EvidenceCoerce {
841 operation: EvidenceOp::Mark,
842 expr: Box::new(inner_ir),
843 from_evidence,
844 to_evidence,
845 ty: IrType::Infer,
846 }
847 }
848
849 ast::Expr::Morpheme { kind, body } => {
850 let body_ir = lower_expr(ctx, body);
851 IrOperation::Morpheme {
852 morpheme: lower_morpheme_kind(*kind),
853 symbol: morpheme_symbol(*kind).to_string(),
854 input: Box::new(IrOperation::Var {
855 name: "_".to_string(),
856 id: "implicit_input".to_string(),
857 ty: IrType::Infer,
858 evidence: IrEvidence::Known,
859 }),
860 body: Some(Box::new(body_ir)),
861 ty: IrType::Infer,
862 evidence: IrEvidence::Known,
863 }
864 }
865
866 ast::Expr::Incorporation { segments } => {
867 let segs: Vec<IncorporationSegment> = segments
868 .iter()
869 .map(|s| {
870 if s.args.is_some() {
871 IncorporationSegment::Verb {
872 name: s.name.name.clone(),
873 }
874 } else {
875 IncorporationSegment::Noun {
876 name: s.name.name.clone(),
877 }
878 }
879 })
880 .collect();
881
882 let mut args: Vec<IrOperation> = Vec::new();
884 for seg in segments {
885 if let Some(ref seg_args) = seg.args {
886 for a in seg_args {
887 args.push(lower_expr(ctx, a));
888 }
889 }
890 }
891
892 IrOperation::Incorporation {
893 segments: segs,
894 args,
895 ty: IrType::Infer,
896 evidence: IrEvidence::Known,
897 }
898 }
899
900 ast::Expr::HttpRequest {
902 method,
903 url,
904 headers,
905 body,
906 timeout,
907 } => IrOperation::HttpRequest {
908 method: lower_http_method(*method),
909 url: Box::new(lower_expr(ctx, url)),
910 headers: if headers.is_empty() {
911 None
912 } else {
913 Some(Box::new(IrOperation::Array {
914 elements: headers
915 .iter()
916 .map(|(k, v)| IrOperation::Tuple {
917 elements: vec![lower_expr(ctx, k), lower_expr(ctx, v)],
918 ty: IrType::Infer,
919 evidence: IrEvidence::Known,
920 })
921 .collect(),
922 ty: IrType::Infer,
923 evidence: IrEvidence::Known,
924 }))
925 },
926 body: body.as_ref().map(|b| Box::new(lower_expr(ctx, b))),
927 timeout: timeout.as_ref().map(|t| Box::new(lower_expr(ctx, t))),
928 ty: IrType::Infer,
929 evidence: IrEvidence::Reported,
930 },
931
932 ast::Expr::GrpcCall {
933 service,
934 method,
935 message,
936 metadata,
937 timeout,
938 } => IrOperation::GrpcCall {
939 service: expr_to_string(service),
940 method: expr_to_string(method),
941 message: Box::new(message.as_ref().map(|m| lower_expr(ctx, m)).unwrap_or(
942 IrOperation::Literal {
943 variant: LiteralVariant::Null,
944 value: serde_json::Value::Null,
945 ty: IrType::Unit,
946 evidence: IrEvidence::Known,
947 },
948 )),
949 metadata: if metadata.is_empty() {
950 None
951 } else {
952 Some(Box::new(IrOperation::Array {
953 elements: metadata
954 .iter()
955 .map(|(k, v)| IrOperation::Tuple {
956 elements: vec![lower_expr(ctx, k), lower_expr(ctx, v)],
957 ty: IrType::Infer,
958 evidence: IrEvidence::Known,
959 })
960 .collect(),
961 ty: IrType::Infer,
962 evidence: IrEvidence::Known,
963 }))
964 },
965 timeout: timeout.as_ref().map(|t| Box::new(lower_expr(ctx, t))),
966 ty: IrType::Infer,
967 evidence: IrEvidence::Reported,
968 },
969
970 _ => IrOperation::Literal {
972 variant: LiteralVariant::Null,
973 value: serde_json::json!({"unhandled": format!("{:?}", std::mem::discriminant(expr))}),
974 ty: IrType::Infer,
975 evidence: IrEvidence::Known,
976 },
977 }
978}
979
980fn lower_pipeline(
981 ctx: &mut LoweringContext,
982 input: &ast::Expr,
983 operations: &[ast::PipeOp],
984) -> IrOperation {
985 let input_ir = lower_expr(ctx, input);
986 let mut evidence = get_operation_evidence(&input_ir);
987
988 let steps: Vec<IrPipelineStep> = operations
989 .iter()
990 .map(|op| {
991 let step = lower_pipe_op(ctx, op);
992 evidence = evidence.join(pipe_op_evidence(op));
994 step
995 })
996 .collect();
997
998 IrOperation::Pipeline {
999 input: Box::new(input_ir),
1000 steps,
1001 ty: IrType::Infer,
1002 evidence,
1003 }
1004}
1005
1006fn lower_pipe_op(ctx: &mut LoweringContext, op: &ast::PipeOp) -> IrPipelineStep {
1007 match op {
1008 ast::PipeOp::Transform(body) => IrPipelineStep::Morpheme {
1009 morpheme: MorphemeKind::Transform,
1010 symbol: "τ".to_string(),
1011 body: Some(Box::new(lower_expr(ctx, body))),
1012 },
1013 ast::PipeOp::Filter(body) => IrPipelineStep::Morpheme {
1014 morpheme: MorphemeKind::Filter,
1015 symbol: "φ".to_string(),
1016 body: Some(Box::new(lower_expr(ctx, body))),
1017 },
1018 ast::PipeOp::Sort(field) => IrPipelineStep::Morpheme {
1019 morpheme: MorphemeKind::Sort,
1020 symbol: "σ".to_string(),
1021 body: field.as_ref().map(|f| {
1022 Box::new(IrOperation::Var {
1023 name: f.name.clone(),
1024 id: f.name.clone(),
1025 ty: IrType::Infer,
1026 evidence: IrEvidence::Known,
1027 })
1028 }),
1029 },
1030 ast::PipeOp::Reduce(body) => IrPipelineStep::Morpheme {
1031 morpheme: MorphemeKind::Reduce,
1032 symbol: "ρ".to_string(),
1033 body: Some(Box::new(lower_expr(ctx, body))),
1034 },
1035 ast::PipeOp::ReduceSum => IrPipelineStep::Morpheme {
1036 morpheme: MorphemeKind::Sum,
1037 symbol: "ρ+".to_string(),
1038 body: None,
1039 },
1040 ast::PipeOp::ReduceProd => IrPipelineStep::Morpheme {
1041 morpheme: MorphemeKind::Product,
1042 symbol: "ρ*".to_string(),
1043 body: None,
1044 },
1045 ast::PipeOp::ReduceMin => IrPipelineStep::Morpheme {
1046 morpheme: MorphemeKind::Min,
1047 symbol: "ρ_min".to_string(),
1048 body: None,
1049 },
1050 ast::PipeOp::ReduceMax => IrPipelineStep::Morpheme {
1051 morpheme: MorphemeKind::Max,
1052 symbol: "ρ_max".to_string(),
1053 body: None,
1054 },
1055 ast::PipeOp::ReduceConcat => IrPipelineStep::Morpheme {
1056 morpheme: MorphemeKind::Concat,
1057 symbol: "ρ++".to_string(),
1058 body: None,
1059 },
1060 ast::PipeOp::ReduceAll => IrPipelineStep::Morpheme {
1061 morpheme: MorphemeKind::All,
1062 symbol: "ρ&".to_string(),
1063 body: None,
1064 },
1065 ast::PipeOp::ReduceAny => IrPipelineStep::Morpheme {
1066 morpheme: MorphemeKind::Any,
1067 symbol: "ρ|".to_string(),
1068 body: None,
1069 },
1070 ast::PipeOp::First => IrPipelineStep::Morpheme {
1071 morpheme: MorphemeKind::First,
1072 symbol: "α".to_string(),
1073 body: None,
1074 },
1075 ast::PipeOp::Last => IrPipelineStep::Morpheme {
1076 morpheme: MorphemeKind::Last,
1077 symbol: "ω".to_string(),
1078 body: None,
1079 },
1080 ast::PipeOp::Middle => IrPipelineStep::Morpheme {
1081 morpheme: MorphemeKind::Middle,
1082 symbol: "μ".to_string(),
1083 body: None,
1084 },
1085 ast::PipeOp::Choice => IrPipelineStep::Morpheme {
1086 morpheme: MorphemeKind::Choice,
1087 symbol: "χ".to_string(),
1088 body: None,
1089 },
1090 ast::PipeOp::Nth(n) => IrPipelineStep::Morpheme {
1091 morpheme: MorphemeKind::Nth,
1092 symbol: "ν".to_string(),
1093 body: Some(Box::new(lower_expr(ctx, n))),
1094 },
1095 ast::PipeOp::Next => IrPipelineStep::Morpheme {
1096 morpheme: MorphemeKind::Next,
1097 symbol: "ξ".to_string(),
1098 body: None,
1099 },
1100 ast::PipeOp::Method { name, args } => IrPipelineStep::Method {
1101 name: name.name.clone(),
1102 args: args.iter().map(|a| lower_expr(ctx, a)).collect(),
1103 },
1104 ast::PipeOp::Await => IrPipelineStep::Await,
1105 ast::PipeOp::Match(_) => {
1106 IrPipelineStep::Identity
1109 }
1110 ast::PipeOp::TryMap(_) => {
1111 IrPipelineStep::Identity
1113 }
1114 ast::PipeOp::Named { prefix, body } => {
1115 let fn_name = prefix
1116 .iter()
1117 .map(|i| i.name.clone())
1118 .collect::<Vec<_>>()
1119 .join("·");
1120 IrPipelineStep::Call {
1121 function: fn_name,
1122 args: body
1123 .as_ref()
1124 .map(|b| vec![lower_expr(ctx, b)])
1125 .unwrap_or_default(),
1126 }
1127 }
1128 ast::PipeOp::Send(data) => IrPipelineStep::Protocol {
1130 operation: ProtocolOp::Send,
1131 config: Some(Box::new(lower_expr(ctx, data))),
1132 },
1133 ast::PipeOp::Recv => IrPipelineStep::Protocol {
1134 operation: ProtocolOp::Recv,
1135 config: None,
1136 },
1137 ast::PipeOp::Stream(handler) => IrPipelineStep::Protocol {
1138 operation: ProtocolOp::Stream,
1139 config: Some(Box::new(lower_expr(ctx, handler))),
1140 },
1141 ast::PipeOp::Connect(config) => IrPipelineStep::Protocol {
1142 operation: ProtocolOp::Connect,
1143 config: config.as_ref().map(|c| Box::new(lower_expr(ctx, c))),
1144 },
1145 ast::PipeOp::Close => IrPipelineStep::Protocol {
1146 operation: ProtocolOp::Close,
1147 config: None,
1148 },
1149 ast::PipeOp::Timeout(ms) => IrPipelineStep::Protocol {
1150 operation: ProtocolOp::Timeout,
1151 config: Some(Box::new(lower_expr(ctx, ms))),
1152 },
1153 ast::PipeOp::Retry { count, strategy } => IrPipelineStep::Protocol {
1154 operation: ProtocolOp::Retry,
1155 config: Some(Box::new(IrOperation::Tuple {
1156 elements: vec![
1157 lower_expr(ctx, count),
1158 strategy
1159 .as_ref()
1160 .map(|s| lower_expr(ctx, s))
1161 .unwrap_or(IrOperation::Literal {
1162 variant: LiteralVariant::String,
1163 value: serde_json::json!("exponential"),
1164 ty: IrType::Primitive {
1165 name: "str".to_string(),
1166 },
1167 evidence: IrEvidence::Known,
1168 }),
1169 ],
1170 ty: IrType::Infer,
1171 evidence: IrEvidence::Known,
1172 })),
1173 },
1174 _ => IrPipelineStep::Identity,
1175 }
1176}
1177
1178fn pipe_op_evidence(op: &ast::PipeOp) -> IrEvidence {
1179 match op {
1180 ast::PipeOp::Send(_)
1182 | ast::PipeOp::Recv
1183 | ast::PipeOp::Stream(_)
1184 | ast::PipeOp::Connect(_)
1185 | ast::PipeOp::Close => IrEvidence::Reported,
1186
1187 ast::PipeOp::Validate {
1189 target_evidence, ..
1190 } => ast_evidence_to_ir(*target_evidence),
1191 ast::PipeOp::Assume {
1192 target_evidence, ..
1193 } => ast_evidence_to_ir(*target_evidence),
1194 ast::PipeOp::AssertEvidence(_) => IrEvidence::Known, _ => IrEvidence::Known,
1197 }
1198}
1199
1200fn ast_evidence_to_ir(ev: ast::Evidentiality) -> IrEvidence {
1201 match ev {
1202 ast::Evidentiality::Known => IrEvidence::Known,
1203 ast::Evidentiality::Uncertain => IrEvidence::Uncertain,
1204 ast::Evidentiality::Reported => IrEvidence::Reported,
1205 ast::Evidentiality::Paradox => IrEvidence::Paradox,
1206 }
1207}
1208
1209fn lower_literal(lit: &ast::Literal) -> IrOperation {
1210 match lit {
1211 ast::Literal::Int {
1212 value,
1213 base,
1214 suffix,
1215 } => IrOperation::Literal {
1216 variant: LiteralVariant::Int,
1217 value: serde_json::json!({
1218 "value": value,
1219 "base": format!("{:?}", base),
1220 "suffix": suffix
1221 }),
1222 ty: suffix
1223 .as_ref()
1224 .map(|s| IrType::Primitive { name: s.clone() })
1225 .unwrap_or(IrType::Primitive {
1226 name: "i64".to_string(),
1227 }),
1228 evidence: IrEvidence::Known,
1229 },
1230 ast::Literal::Float { value, suffix } => IrOperation::Literal {
1231 variant: LiteralVariant::Float,
1232 value: serde_json::json!({"value": value, "suffix": suffix}),
1233 ty: suffix
1234 .as_ref()
1235 .map(|s| IrType::Primitive { name: s.clone() })
1236 .unwrap_or(IrType::Primitive {
1237 name: "f64".to_string(),
1238 }),
1239 evidence: IrEvidence::Known,
1240 },
1241 ast::Literal::String(s) | ast::Literal::MultiLineString(s) | ast::Literal::RawString(s) => {
1242 IrOperation::Literal {
1243 variant: LiteralVariant::String,
1244 value: serde_json::Value::String(s.clone()),
1245 ty: IrType::Primitive {
1246 name: "str".to_string(),
1247 },
1248 evidence: IrEvidence::Known,
1249 }
1250 }
1251 ast::Literal::Char(c) => IrOperation::Literal {
1252 variant: LiteralVariant::Char,
1253 value: serde_json::Value::String(c.to_string()),
1254 ty: IrType::Primitive {
1255 name: "char".to_string(),
1256 },
1257 evidence: IrEvidence::Known,
1258 },
1259 ast::Literal::Bool(b) => IrOperation::Literal {
1260 variant: LiteralVariant::Bool,
1261 value: serde_json::Value::Bool(*b),
1262 ty: IrType::Primitive {
1263 name: "bool".to_string(),
1264 },
1265 evidence: IrEvidence::Known,
1266 },
1267 ast::Literal::Null | ast::Literal::Empty => IrOperation::Literal {
1268 variant: LiteralVariant::Null,
1269 value: serde_json::Value::Null,
1270 ty: IrType::Unit,
1271 evidence: IrEvidence::Known,
1272 },
1273 _ => IrOperation::Literal {
1274 variant: LiteralVariant::Null,
1275 value: serde_json::Value::Null,
1276 ty: IrType::Infer,
1277 evidence: IrEvidence::Known,
1278 },
1279 }
1280}
1281
1282fn lower_literal_value(lit: &ast::Literal) -> serde_json::Value {
1283 match lit {
1284 ast::Literal::Int { value, .. } => serde_json::json!(value),
1285 ast::Literal::Float { value, .. } => serde_json::json!(value),
1286 ast::Literal::String(s) => serde_json::Value::String(s.clone()),
1287 ast::Literal::Bool(b) => serde_json::Value::Bool(*b),
1288 ast::Literal::Char(c) => serde_json::Value::String(c.to_string()),
1289 ast::Literal::Null => serde_json::Value::Null,
1290 _ => serde_json::Value::Null,
1291 }
1292}
1293
1294fn lower_binop(op: ast::BinOp) -> BinaryOp {
1295 match op {
1296 ast::BinOp::Add => BinaryOp::Add,
1297 ast::BinOp::Sub => BinaryOp::Sub,
1298 ast::BinOp::Mul => BinaryOp::Mul,
1299 ast::BinOp::Div => BinaryOp::Div,
1300 ast::BinOp::Rem => BinaryOp::Rem,
1301 ast::BinOp::Pow => BinaryOp::Pow,
1302 ast::BinOp::And => BinaryOp::And,
1303 ast::BinOp::Or => BinaryOp::Or,
1304 ast::BinOp::BitAnd => BinaryOp::BitAnd,
1305 ast::BinOp::BitOr => BinaryOp::BitOr,
1306 ast::BinOp::BitXor => BinaryOp::BitXor,
1307 ast::BinOp::Shl => BinaryOp::Shl,
1308 ast::BinOp::Shr => BinaryOp::Shr,
1309 ast::BinOp::Eq => BinaryOp::Eq,
1310 ast::BinOp::Ne => BinaryOp::Ne,
1311 ast::BinOp::Lt => BinaryOp::Lt,
1312 ast::BinOp::Le => BinaryOp::Le,
1313 ast::BinOp::Gt => BinaryOp::Gt,
1314 ast::BinOp::Ge => BinaryOp::Ge,
1315 ast::BinOp::Concat => BinaryOp::Concat,
1316 }
1317}
1318
1319fn lower_unaryop(op: ast::UnaryOp) -> UnaryOp {
1320 match op {
1321 ast::UnaryOp::Neg => UnaryOp::Neg,
1322 ast::UnaryOp::Not => UnaryOp::Not,
1323 ast::UnaryOp::Deref => UnaryOp::Deref,
1324 ast::UnaryOp::Ref => UnaryOp::Ref,
1325 ast::UnaryOp::RefMut => UnaryOp::RefMut,
1326 }
1327}
1328
1329fn lower_morpheme_kind(kind: ast::MorphemeKind) -> MorphemeKind {
1330 match kind {
1331 ast::MorphemeKind::Transform => MorphemeKind::Transform,
1332 ast::MorphemeKind::Filter => MorphemeKind::Filter,
1333 ast::MorphemeKind::Sort => MorphemeKind::Sort,
1334 ast::MorphemeKind::Reduce => MorphemeKind::Reduce,
1335 ast::MorphemeKind::Lambda => MorphemeKind::Lambda,
1336 ast::MorphemeKind::Sum => MorphemeKind::Sum,
1337 ast::MorphemeKind::Product => MorphemeKind::Product,
1338 ast::MorphemeKind::First => MorphemeKind::First,
1339 ast::MorphemeKind::Last => MorphemeKind::Last,
1340 ast::MorphemeKind::Middle => MorphemeKind::Middle,
1341 ast::MorphemeKind::Choice => MorphemeKind::Choice,
1342 ast::MorphemeKind::Nth => MorphemeKind::Nth,
1343 ast::MorphemeKind::Next => MorphemeKind::Next,
1344 }
1345}
1346
1347fn morpheme_symbol(kind: ast::MorphemeKind) -> &'static str {
1348 match kind {
1349 ast::MorphemeKind::Transform => "τ",
1350 ast::MorphemeKind::Filter => "φ",
1351 ast::MorphemeKind::Sort => "σ",
1352 ast::MorphemeKind::Reduce => "ρ",
1353 ast::MorphemeKind::Lambda => "λ",
1354 ast::MorphemeKind::Sum => "Σ",
1355 ast::MorphemeKind::Product => "Π",
1356 ast::MorphemeKind::First => "α",
1357 ast::MorphemeKind::Last => "ω",
1358 ast::MorphemeKind::Middle => "μ",
1359 ast::MorphemeKind::Choice => "χ",
1360 ast::MorphemeKind::Nth => "ν",
1361 ast::MorphemeKind::Next => "ξ",
1362 }
1363}
1364
1365fn lower_http_method(method: ast::HttpMethod) -> HttpMethod {
1366 match method {
1367 ast::HttpMethod::Get => HttpMethod::Get,
1368 ast::HttpMethod::Post => HttpMethod::Post,
1369 ast::HttpMethod::Put => HttpMethod::Put,
1370 ast::HttpMethod::Delete => HttpMethod::Delete,
1371 ast::HttpMethod::Patch => HttpMethod::Patch,
1372 ast::HttpMethod::Head => HttpMethod::Head,
1373 ast::HttpMethod::Options => HttpMethod::Options,
1374 ast::HttpMethod::Connect => HttpMethod::Connect,
1375 ast::HttpMethod::Trace => HttpMethod::Trace,
1376 }
1377}
1378
1379fn get_operation_evidence(op: &IrOperation) -> IrEvidence {
1380 match op {
1381 IrOperation::Literal { evidence, .. }
1382 | IrOperation::Var { evidence, .. }
1383 | IrOperation::Let { evidence, .. }
1384 | IrOperation::Binary { evidence, .. }
1385 | IrOperation::Unary { evidence, .. }
1386 | IrOperation::Call { evidence, .. }
1387 | IrOperation::MethodCall { evidence, .. }
1388 | IrOperation::Closure { evidence, .. }
1389 | IrOperation::If { evidence, .. }
1390 | IrOperation::Match { evidence, .. }
1391 | IrOperation::Loop { evidence, .. }
1392 | IrOperation::Block { evidence, .. }
1393 | IrOperation::Pipeline { evidence, .. }
1394 | IrOperation::Morpheme { evidence, .. }
1395 | IrOperation::Fork { evidence, .. }
1396 | IrOperation::Identity { evidence, .. }
1397 | IrOperation::Array { evidence, .. }
1398 | IrOperation::Tuple { evidence, .. }
1399 | IrOperation::StructInit { evidence, .. }
1400 | IrOperation::Field { evidence, .. }
1401 | IrOperation::Index { evidence, .. }
1402 | IrOperation::Assign { evidence, .. }
1403 | IrOperation::Break { evidence, .. }
1404 | IrOperation::Continue { evidence }
1405 | IrOperation::Return { evidence, .. }
1406 | IrOperation::Incorporation { evidence, .. }
1407 | IrOperation::Affect { evidence, .. }
1408 | IrOperation::HttpRequest { evidence, .. }
1409 | IrOperation::GrpcCall { evidence, .. }
1410 | IrOperation::WebSocket { evidence, .. }
1411 | IrOperation::KafkaOp { evidence, .. }
1412 | IrOperation::Await { evidence, .. }
1413 | IrOperation::Unsafe { evidence, .. }
1414 | IrOperation::Cast { evidence, .. }
1415 | IrOperation::Try { evidence, .. } => *evidence,
1416 IrOperation::EvidenceCoerce { to_evidence, .. } => *to_evidence,
1417 }
1418}
1419
1420fn expr_to_string(e: &ast::Expr) -> String {
1421 match e {
1422 ast::Expr::Path(p) => p
1423 .segments
1424 .iter()
1425 .map(|s| s.ident.name.clone())
1426 .collect::<Vec<_>>()
1427 .join("::"),
1428 ast::Expr::Literal(ast::Literal::String(s)) => s.clone(),
1429 _ => "dynamic".to_string(),
1430 }
1431}
1432
1433fn lower_struct_def(_ctx: &mut LoweringContext, s: &ast::StructDef) -> IrTypeDef {
1436 let fields = match &s.fields {
1437 ast::StructFields::Named(fields) => fields
1438 .iter()
1439 .map(|f| IrField {
1440 name: f.name.name.clone(),
1441 ty: lower_type_expr(&f.ty),
1442 visibility: lower_visibility(f.visibility),
1443 })
1444 .collect(),
1445 ast::StructFields::Tuple(types) => types
1446 .iter()
1447 .enumerate()
1448 .map(|(i, t)| IrField {
1449 name: format!("{}", i),
1450 ty: lower_type_expr(t),
1451 visibility: IrVisibility::Public,
1452 })
1453 .collect(),
1454 ast::StructFields::Unit => vec![],
1455 };
1456
1457 IrTypeDef::Struct {
1458 name: s.name.name.clone(),
1459 generics: lower_generics(&s.generics),
1460 fields,
1461 span: None,
1462 }
1463}
1464
1465fn lower_enum_def(_ctx: &mut LoweringContext, e: &ast::EnumDef) -> IrTypeDef {
1466 let variants = e
1467 .variants
1468 .iter()
1469 .map(|v| {
1470 let fields = match &v.fields {
1471 ast::StructFields::Named(fields) => Some(
1472 fields
1473 .iter()
1474 .map(|f| IrField {
1475 name: f.name.name.clone(),
1476 ty: lower_type_expr(&f.ty),
1477 visibility: lower_visibility(f.visibility),
1478 })
1479 .collect(),
1480 ),
1481 ast::StructFields::Tuple(types) => Some(
1482 types
1483 .iter()
1484 .enumerate()
1485 .map(|(i, t)| IrField {
1486 name: format!("{}", i),
1487 ty: lower_type_expr(t),
1488 visibility: IrVisibility::Public,
1489 })
1490 .collect(),
1491 ),
1492 ast::StructFields::Unit => None,
1493 };
1494
1495 IrVariant {
1496 name: v.name.name.clone(),
1497 fields,
1498 discriminant: None,
1499 }
1500 })
1501 .collect();
1502
1503 IrTypeDef::Enum {
1504 name: e.name.name.clone(),
1505 generics: lower_generics(&e.generics),
1506 variants,
1507 span: None,
1508 }
1509}
1510
1511fn lower_type_alias(_ctx: &mut LoweringContext, t: &ast::TypeAlias) -> IrTypeDef {
1512 IrTypeDef::TypeAlias {
1513 name: t.name.name.clone(),
1514 generics: lower_generics(&t.generics),
1515 target: lower_type_expr(&t.ty),
1516 span: None,
1517 }
1518}
1519
1520fn lower_trait_def(ctx: &mut LoweringContext, t: &ast::TraitDef) -> IrTraitDef {
1521 let methods = t
1522 .items
1523 .iter()
1524 .filter_map(|item| match item {
1525 ast::TraitItem::Function(f) => lower_function(ctx, f),
1526 _ => None,
1527 })
1528 .collect();
1529
1530 IrTraitDef {
1531 name: t.name.name.clone(),
1532 generics: lower_generics(&t.generics),
1533 super_traits: t.supertraits.iter().map(type_expr_to_string).collect(),
1534 methods,
1535 span: None,
1536 }
1537}
1538
1539fn lower_impl_block(ctx: &mut LoweringContext, i: &ast::ImplBlock) -> IrImplBlock {
1540 let methods = i
1541 .items
1542 .iter()
1543 .filter_map(|item| match item {
1544 ast::ImplItem::Function(f) => lower_function(ctx, f),
1545 _ => None,
1546 })
1547 .collect();
1548
1549 IrImplBlock {
1550 trait_name: i.trait_.as_ref().map(|t| {
1551 t.segments
1552 .iter()
1553 .map(|s| s.ident.name.clone())
1554 .collect::<Vec<_>>()
1555 .join("::")
1556 }),
1557 target_type: lower_type_expr(&i.self_ty),
1558 generics: lower_generics(&i.generics),
1559 methods,
1560 span: None,
1561 }
1562}
1563
1564fn lower_const_def(ctx: &mut LoweringContext, c: &ast::ConstDef) -> IrConstant {
1565 IrConstant {
1566 name: c.name.name.clone(),
1567 ty: lower_type_expr(&c.ty),
1568 value: lower_expr(ctx, &c.value),
1569 visibility: lower_visibility(c.visibility),
1570 span: None,
1571 }
1572}