1use cranelift_entity::PrimaryMap;
4use std::collections::BTreeMap;
5
6use ruka_frontend::{Block, CallArg, Expr, IntrinsicId, Program, Stmt, TypeRef};
7use ruka_hir::{
8 DuplicateExternFunction, ExprId, FuncId, HirEnumDecl, HirExpr, HirExternFunction, HirFunction,
9 HirMatchArm, HirParam, HirProgram, HirStmt, HirStructDecl, HirType, StmtId,
10};
11
12pub fn lower_program(program: &Program) -> HirProgram {
14 let mut lowerer = Lowerer {
15 functions: PrimaryMap::new(),
16 extern_functions: BTreeMap::new(),
17 duplicate_extern_functions: Vec::new(),
18 statements: PrimaryMap::new(),
19 expressions: PrimaryMap::new(),
20 expr_spans: PrimaryMap::new(),
21 };
22
23 for module in &program.extern_modules {
24 for function in &module.functions {
25 let full_name = format!("{}::{}", module.name, function.name);
26 if let Some(existing) = lowerer.extern_functions.get(&full_name) {
27 lowerer
28 .duplicate_extern_functions
29 .push(DuplicateExternFunction {
30 name: full_name,
31 first_span: existing.span,
32 duplicate_span: function.span,
33 });
34 continue;
35 }
36
37 lowerer.extern_functions.insert(
38 full_name,
39 HirExternFunction {
40 symbol: format!("{}::{}", module.name, function.symbol),
41 params: function
42 .params
43 .iter()
44 .map(|param| HirParam {
45 name: param.name.clone(),
46 name_span: param.name_span,
47 ty: lower_type_ref(¶m.ty),
48 })
49 .collect(),
50 return_type: lower_type_ref(&function.return_type),
51 span: function.span,
52 },
53 );
54 }
55 }
56
57 for function in &program.functions {
58 let body = lowerer.lower_block(&function.body);
59 let params = function
60 .params
61 .iter()
62 .map(|param| HirParam {
63 name: param.name.clone(),
64 name_span: param.name_span,
65 ty: lower_type_ref(¶m.ty),
66 })
67 .collect();
68 let return_type = lower_type_ref(&function.return_type);
69
70 let _func_id: FuncId = lowerer.functions.push(HirFunction {
71 name: function.name.clone(),
72 params,
73 return_type,
74 body,
75 });
76 }
77
78 HirProgram {
79 functions: lowerer.functions,
80 extern_functions: lowerer.extern_functions,
81 duplicate_extern_functions: lowerer.duplicate_extern_functions,
82 structs: program
83 .structs
84 .iter()
85 .map(|decl| HirStructDecl {
86 name: decl.name.clone(),
87 type_params: decl.type_params.clone(),
88 fields: decl.fields.clone(),
89 })
90 .collect(),
91 enums: program
92 .enums
93 .iter()
94 .map(|decl| HirEnumDecl {
95 name: decl.name.clone(),
96 type_params: decl.type_params.clone(),
97 variants: decl.variants.clone(),
98 })
99 .collect(),
100 statements: lowerer.statements,
101 expressions: lowerer.expressions,
102 expr_spans: lowerer.expr_spans,
103 }
104}
105
106fn lower_type_ref(ty: &TypeRef) -> HirType {
107 HirType {
108 mode: ty.mode,
109 ty: ty.ty.clone(),
110 }
111}
112
113struct Lowerer {
114 functions: PrimaryMap<FuncId, HirFunction>,
115 extern_functions: BTreeMap<String, HirExternFunction>,
116 duplicate_extern_functions: Vec<DuplicateExternFunction>,
117 statements: PrimaryMap<StmtId, HirStmt>,
118 expressions: PrimaryMap<ExprId, HirExpr>,
119 expr_spans: PrimaryMap<ExprId, Option<ruka_frontend::SourceSpan>>,
120}
121
122impl Lowerer {
123 fn lower_block(&mut self, block: &Block) -> Vec<StmtId> {
124 block
125 .statements
126 .iter()
127 .map(|stmt| self.lower_stmt(stmt))
128 .collect()
129 }
130
131 fn lower_stmt(&mut self, stmt: &Stmt) -> StmtId {
132 let lowered = match stmt {
133 Stmt::Let {
134 name,
135 name_span,
136 ownership,
137 mode,
138 value,
139 } => {
140 let value = self.lower_expr(value);
141 HirStmt::Let {
142 name: name.clone(),
143 name_span: *name_span,
144 ownership: *ownership,
145 mode: *mode,
146 value,
147 }
148 }
149 Stmt::Assign {
150 target,
151 mode,
152 value,
153 } => {
154 let value = self.lower_expr(value);
155 HirStmt::Assign {
156 target: target.clone(),
157 mode: *mode,
158 value,
159 }
160 }
161 Stmt::If {
162 condition,
163 then_block,
164 else_block,
165 } => {
166 let condition = self.lower_expr(condition);
167 let then_body = self.lower_block(then_block);
168 let else_body = else_block.as_ref().map(|block| self.lower_block(block));
169 HirStmt::If {
170 condition,
171 then_body,
172 else_body,
173 }
174 }
175 Stmt::For {
176 binding,
177 iterable,
178 body,
179 } => {
180 let iterable = self.lower_expr(iterable);
181 let body = self.lower_block(body);
182 HirStmt::For {
183 binding_name: binding.name.clone(),
184 binding_name_span: binding.span,
185 binding_is_mut_borrow: binding.is_mut_borrow,
186 iterable,
187 body,
188 }
189 }
190 Stmt::While { condition, body } => {
191 let condition = self.lower_expr(condition);
192 let body = self.lower_block(body);
193 HirStmt::While { condition, body }
194 }
195 Stmt::Match { value, arms } => {
196 let value = self.lower_expr(value);
197 let arms = arms
198 .iter()
199 .map(|arm| HirMatchArm {
200 pattern: arm.pattern.clone(),
201 body: self.lower_block(&arm.body),
202 })
203 .collect();
204 HirStmt::Match { value, arms }
205 }
206 Stmt::Meta { body } => HirStmt::Meta {
207 body_len: body.statements.len(),
208 },
209 Stmt::Expr { expr, has_semi } => {
210 let expr = self.lower_expr(expr);
211 HirStmt::Expr {
212 expr,
213 has_semi: *has_semi,
214 }
215 }
216 };
217 self.statements.push(lowered)
218 }
219
220 fn lower_expr(&mut self, expr: &Expr) -> ExprId {
221 let span = expr_source_span(expr);
222 let lowered = match expr {
223 Expr::Ident { name, span } => HirExpr::Ident {
224 name: name.clone(),
225 span: *span,
226 },
227 Expr::Int(value) => HirExpr::Number(ruka_frontend::NumberLiteral {
228 raw: value.to_string(),
229 }),
230 Expr::Number(value) => HirExpr::Number(value.clone()),
231 Expr::String(value) => HirExpr::String(value.clone()),
232 Expr::Bool(value) => HirExpr::Bool(*value),
233 Expr::Array { items, .. } => {
234 let item_ids = items.iter().map(|item| self.lower_expr(item)).collect();
235 HirExpr::Array(item_ids)
236 }
237 Expr::Tuple { items, .. } => {
238 let item_ids = items.iter().map(|item| self.lower_expr(item)).collect();
239 HirExpr::Tuple(item_ids)
240 }
241 Expr::StructLit { name, fields, .. } => {
242 let lowered_fields = fields
243 .iter()
244 .map(|field| (field.name.clone(), self.lower_expr(&field.value)))
245 .collect();
246 HirExpr::StructLit {
247 name: name.clone(),
248 fields: lowered_fields,
249 }
250 }
251 Expr::Call { callee, args, .. } => {
252 let callee = self.lower_expr(callee);
253 let args = args
254 .iter()
255 .map(|arg| match arg {
256 CallArg::Expr(expr) => self.lower_expr(expr),
257 CallArg::Spread(_) => {
258 panic!("tuple spread should be resolved during elaboration")
259 }
260 CallArg::Type(_) => {
261 panic!("type call arguments should be resolved during elaboration")
262 }
263 })
264 .collect();
265 HirExpr::Call { callee, args }
266 }
267 Expr::IntrinsicCall { name, args, .. } => {
268 let mut lowered_args = Vec::new();
269 let mut type_args = Vec::new();
270 for arg in args {
271 match arg {
272 CallArg::Expr(expr) => lowered_args.push(self.lower_expr(expr)),
273 CallArg::Spread(_) => {
274 panic!("tuple spread should be resolved during elaboration")
275 }
276 CallArg::Type(ty) => type_args.push(ty.clone()),
277 }
278 }
279 HirExpr::IntrinsicCall {
280 intrinsic: IntrinsicId::parse(name),
281 type_args,
282 args: lowered_args,
283 }
284 }
285 Expr::Field { base, field } => {
286 let base = self.lower_expr(base);
287 HirExpr::Field {
288 base,
289 field: field.clone(),
290 }
291 }
292 Expr::Index { base, index } => {
293 let base = self.lower_expr(base);
294 let index = self.lower_expr(index);
295 HirExpr::Index { base, index }
296 }
297 Expr::SliceRange { base, start, end } => {
298 let base = self.lower_expr(base);
299 let start = start.as_ref().map(|expr| self.lower_expr(expr));
300 let end = end.as_ref().map(|expr| self.lower_expr(expr));
301 HirExpr::SliceRange { base, start, end }
302 }
303 Expr::Prefix { op, value } => {
304 let value = self.lower_expr(value);
305 HirExpr::Prefix { op: *op, value }
306 }
307 Expr::Binary { op, lhs, rhs } => {
308 let lhs = self.lower_expr(lhs);
309 let rhs = self.lower_expr(rhs);
310 HirExpr::Binary { op: *op, lhs, rhs }
311 }
312 Expr::Relational { op, lhs, rhs } => {
313 let lhs = self.lower_expr(lhs);
314 let rhs = self.lower_expr(rhs);
315 HirExpr::Relational { op: *op, lhs, rhs }
316 }
317 Expr::Block(block) => {
318 let body = self.lower_block(block);
319 HirExpr::Block { body }
320 }
321 Expr::Splice(value) => HirExpr::SpliceMeta {
322 value: format!("{value:?}"),
323 },
324 };
325 let expr_id = self.expressions.push(lowered);
326 let span_slot = self.expr_spans.push(span);
327 assert_eq!(
328 expr_id, span_slot,
329 "expression and span arenas should stay aligned"
330 );
331 expr_id
332 }
333}
334
335fn expr_source_span(expr: &Expr) -> Option<ruka_frontend::SourceSpan> {
337 match expr {
338 Expr::Ident { span, .. } => Some(*span),
339 Expr::Array { span, .. } => Some(*span),
340 Expr::Tuple { span, .. } => Some(*span),
341 Expr::StructLit { span, .. } => Some(*span),
342 Expr::Call { span, .. } => Some(*span),
343 Expr::IntrinsicCall { span, .. } => Some(*span),
344 _ => None,
345 }
346}