1use core::mem::take;
2
3use crate::alloc::prelude::*;
4use crate::alloc::{try_format, Box, Vec};
5use crate::ast::{self, Span, Spanned};
6use crate::compile::ir;
7use crate::compile::{self, ErrorKind, WithSpan};
8use crate::hir;
9use crate::query::Query;
10use crate::runtime::{Bytes, Value};
11use crate::SourceId;
12
13use tracing::instrument_ast;
14
15pub(crate) struct Ctxt<'a, 'arena> {
17 #[cfg_attr(not(feature = "tracing"), allow(unused))]
19 pub(crate) source_id: SourceId,
20 pub(crate) q: Query<'a, 'arena>,
22}
23
24#[instrument_ast]
25pub(crate) fn expr(hir: &hir::Expr<'_>, c: &mut Ctxt<'_, '_>) -> compile::Result<ir::Ir> {
26 let span = hir.span();
27
28 Ok(match hir.kind {
29 hir::ExprKind::Vec(hir) => ir::Ir::new(span, expr_vec(span, c, hir)?),
30 hir::ExprKind::Tuple(hir) => expr_tuple(c, span, hir)?,
31 hir::ExprKind::Object(hir) => ir::Ir::new(span, expr_object(span, c, hir)?),
32 hir::ExprKind::Group(hir) => expr(hir, c)?,
33 hir::ExprKind::Binary(hir) => expr_binary(span, c, hir)?,
34 hir::ExprKind::Assign(hir) => expr_assign(span, c, hir)?,
35 hir::ExprKind::Call(hir) => ir::Ir::new(span, expr_call(span, c, hir)?),
36 hir::ExprKind::If(hir) => ir::Ir::new(span, expr_if(span, c, hir)?),
37 hir::ExprKind::Loop(hir) => ir::Ir::new(span, expr_loop(span, c, hir)?),
38 hir::ExprKind::Lit(hir) => lit(c, span, hir)?,
39 hir::ExprKind::Block(hir) => ir::Ir::new(span, block(hir, c)?),
40 hir::ExprKind::FieldAccess(..) => ir::Ir::new(span, ir_target(hir)?),
41 hir::ExprKind::Break(hir) => ir::Ir::new(span, ir::IrBreak::compile_ast(span, c, hir)?),
42 hir::ExprKind::Template(template) => {
43 let ir_template = builtin_template(template, c)?;
44 ir::Ir::new(hir.span(), ir_template)
45 }
46 hir::ExprKind::Const(hash) => {
47 let Some(value) = c.q.get_const_value(hash) else {
48 return Err(compile::Error::msg(
49 hir,
50 try_format!("Missing constant for hash {hash}"),
51 ));
52 };
53
54 let value = value.to_value_with(c.q.context).with_span(span)?;
55 ir::Ir::new(span, value)
56 }
57 hir::ExprKind::Variable(name) => {
58 return Ok(ir::Ir::new(span, name));
59 }
60 _ => {
61 return Err(compile::Error::msg(
62 hir,
63 "Expression kind not supported yet in constant contexts",
64 ))
65 }
66 })
67}
68
69fn ir_target(expr: &hir::Expr<'_>) -> compile::Result<ir::IrTarget> {
71 match expr.kind {
72 hir::ExprKind::Variable(name) => {
73 return Ok(ir::IrTarget {
74 span: expr.span(),
75 kind: ir::IrTargetKind::Name(name),
76 });
77 }
78 hir::ExprKind::FieldAccess(expr_field_access) => {
79 let target = ir_target(&expr_field_access.expr)?;
80
81 match expr_field_access.expr_field {
82 hir::ExprField::Ident(name) => {
83 return Ok(ir::IrTarget {
84 span: expr.span(),
85 kind: ir::IrTargetKind::Field(Box::try_new(target)?, name.try_into()?),
86 });
87 }
88 hir::ExprField::Index(index) => {
89 return Ok(ir::IrTarget {
90 span: expr.span(),
91 kind: ir::IrTargetKind::Index(Box::try_new(target)?, index),
92 });
93 }
94 _ => {
95 return Err(compile::Error::new(expr, ErrorKind::BadFieldAccess));
96 }
97 }
98 }
99 _ => (),
100 }
101
102 Err(compile::Error::msg(expr, "Not supported as a target"))
103}
104
105#[instrument_ast]
106fn expr_assign(
107 span: Span,
108 c: &mut Ctxt<'_, '_>,
109 hir: &hir::ExprAssign<'_>,
110) -> compile::Result<ir::Ir> {
111 let target = ir_target(&hir.lhs)?;
112
113 Ok(ir::Ir::new(
114 span,
115 ir::IrSet {
116 span,
117 target,
118 value: Box::try_new(expr(&hir.rhs, c)?)?,
119 },
120 ))
121}
122
123#[instrument_ast]
124fn expr_call(
125 span: Span,
126 c: &mut Ctxt<'_, '_>,
127 hir: &hir::ExprCall<'_>,
128) -> compile::Result<ir::IrCall> {
129 let mut args = Vec::try_with_capacity(hir.args.len())?;
130
131 for e in hir.args {
132 args.try_push(expr(e, c)?)?;
133 }
134
135 if let hir::Call::ConstFn { id, .. } = hir.call {
136 return Ok(ir::IrCall { span, id, args });
137 }
138
139 Err(compile::Error::msg(
140 span,
141 "Call not supported in constant contexts",
142 ))
143}
144
145#[instrument_ast]
146fn expr_binary(
147 span: Span,
148 c: &mut Ctxt<'_, '_>,
149 hir: &hir::ExprBinary<'_>,
150) -> compile::Result<ir::Ir> {
151 if hir.op.is_assign() {
152 let op = match hir.op {
153 ast::BinOp::AddAssign(..) => ir::IrAssignOp::Add,
154 ast::BinOp::SubAssign(..) => ir::IrAssignOp::Sub,
155 ast::BinOp::MulAssign(..) => ir::IrAssignOp::Mul,
156 ast::BinOp::DivAssign(..) => ir::IrAssignOp::Div,
157 ast::BinOp::ShlAssign(..) => ir::IrAssignOp::Shl,
158 ast::BinOp::ShrAssign(..) => ir::IrAssignOp::Shr,
159 _ => return Err(compile::Error::msg(hir.op, "op not supported yet")),
160 };
161
162 let target = ir_target(&hir.lhs)?;
163
164 return Ok(ir::Ir::new(
165 span,
166 ir::IrAssign {
167 span,
168 target,
169 value: Box::try_new(expr(&hir.rhs, c)?)?,
170 op,
171 },
172 ));
173 }
174
175 let lhs = expr(&hir.lhs, c)?;
176 let rhs = expr(&hir.rhs, c)?;
177
178 let op = match hir.op {
179 ast::BinOp::Add(..) => ir::IrBinaryOp::Add,
180 ast::BinOp::Sub(..) => ir::IrBinaryOp::Sub,
181 ast::BinOp::Mul(..) => ir::IrBinaryOp::Mul,
182 ast::BinOp::Div(..) => ir::IrBinaryOp::Div,
183 ast::BinOp::Shl(..) => ir::IrBinaryOp::Shl,
184 ast::BinOp::Shr(..) => ir::IrBinaryOp::Shr,
185 ast::BinOp::Lt(..) => ir::IrBinaryOp::Lt,
186 ast::BinOp::Lte(..) => ir::IrBinaryOp::Lte,
187 ast::BinOp::Eq(..) => ir::IrBinaryOp::Eq,
188 ast::BinOp::Gt(..) => ir::IrBinaryOp::Gt,
189 ast::BinOp::Gte(..) => ir::IrBinaryOp::Gte,
190 _ => return Err(compile::Error::msg(hir.op, "op not supported yet")),
191 };
192
193 Ok(ir::Ir::new(
194 span,
195 ir::IrBinary {
196 span,
197 op,
198 lhs: Box::try_new(lhs)?,
199 rhs: Box::try_new(rhs)?,
200 },
201 ))
202}
203
204#[instrument_ast(span = span)]
205fn lit(_c: &mut Ctxt<'_, '_>, span: Span, hir: hir::Lit<'_>) -> compile::Result<ir::Ir> {
206 Ok(match hir {
207 hir::Lit::Bool(boolean) => {
208 let value = Value::from(boolean);
209 ir::Ir::new(span, value)
210 }
211 hir::Lit::Unsigned(n) => {
212 let value = Value::from(n);
213 ir::Ir::new(span, value)
214 }
215 hir::Lit::Signed(n) => {
216 let value = Value::from(n);
217 ir::Ir::new(span, value)
218 }
219 hir::Lit::Float(n) => {
220 let value = Value::from(n);
221 ir::Ir::new(span, value)
222 }
223 hir::Lit::Char(c) => {
224 let value = Value::from(c);
225 ir::Ir::new(span, value)
226 }
227 hir::Lit::Str(string) => {
228 let string = string.try_to_owned().with_span(span)?;
229 let value = Value::try_from(string).with_span(span)?;
230 ir::Ir::new(span, value)
231 }
232 hir::Lit::ByteStr(byte_str) => {
233 let value = Bytes::from_vec(Vec::try_from(byte_str).with_span(span)?);
234 let value = Value::try_from(value).with_span(span)?;
235 ir::Ir::new(span, value)
236 }
237 })
238}
239
240#[instrument_ast(span = span)]
241fn expr_tuple(c: &mut Ctxt<'_, '_>, span: Span, hir: &hir::ExprSeq<'_>) -> compile::Result<ir::Ir> {
242 if hir.items.is_empty() {
243 let value = Value::unit();
244 return Ok(ir::Ir::new(span, value));
245 }
246
247 let mut items = Vec::new();
248
249 for e in hir.items {
250 items.try_push(expr(e, c)?)?;
251 }
252
253 Ok(ir::Ir::new(
254 span,
255 ir::Tuple {
256 span,
257 items: items.try_into_boxed_slice()?,
258 },
259 ))
260}
261
262#[instrument_ast]
263fn expr_vec(
264 span: Span,
265 c: &mut Ctxt<'_, '_>,
266 hir: &hir::ExprSeq<'_>,
267) -> compile::Result<ir::IrVec> {
268 let mut items = Vec::new();
269
270 for e in hir.items {
271 items.try_push(expr(e, c)?)?;
272 }
273
274 Ok(ir::IrVec {
275 span,
276 items: items.try_into_boxed_slice()?,
277 })
278}
279
280#[instrument_ast]
281fn expr_object(
282 span: Span,
283 c: &mut Ctxt<'_, '_>,
284 hir: &hir::ExprObject<'_>,
285) -> compile::Result<ir::IrObject> {
286 let mut assignments = Vec::new();
287
288 for assign in hir.assignments {
289 let (_, key) = assign.key;
290 let ir = expr(&assign.assign, c)?;
291 assignments.try_push((key.try_into()?, ir))?
292 }
293
294 Ok(ir::IrObject {
295 span,
296 assignments: assignments.try_into_boxed_slice()?,
297 })
298}
299
300#[instrument_ast]
301pub(crate) fn block(hir: &hir::Block<'_>, c: &mut Ctxt<'_, '_>) -> compile::Result<ir::IrScope> {
302 let span = hir.span();
303
304 let mut last = None::<(&hir::Expr<'_>, bool)>;
305 let mut instructions = Vec::new();
306
307 for stmt in hir.statements {
308 match stmt {
309 hir::Stmt::Local(l) => {
310 if let Some((e, _)) = take(&mut last) {
311 instructions.try_push(expr(e, c)?)?;
312 }
313
314 instructions.try_push(local(l, c)?)?;
315 }
316 hir::Stmt::Expr(e) => {
317 instructions.try_push(expr(e, c)?)?;
318 }
319 };
320 }
321
322 let last = if let Some(e) = hir.value {
323 Some(Box::try_new(expr(e, c)?)?)
324 } else {
325 None
326 };
327
328 Ok(ir::IrScope {
329 span,
330 instructions,
331 last,
332 })
333}
334
335#[instrument_ast]
336fn builtin_template(
337 template: &hir::BuiltInTemplate,
338 c: &mut Ctxt<'_, '_>,
339) -> compile::Result<ir::IrTemplate> {
340 let span = template.span;
341 let mut components = Vec::new();
342
343 for e in template.exprs {
344 if let hir::ExprKind::Lit(hir::Lit::Str(s)) = e.kind {
345 components.try_push(ir::IrTemplateComponent::String(s.try_into()?))?;
346 continue;
347 }
348
349 let ir = expr(e, c)?;
350 components.try_push(ir::IrTemplateComponent::Ir(ir))?;
351 }
352
353 Ok(ir::IrTemplate { span, components })
354}
355
356#[instrument_ast]
357fn local(hir: &hir::Local<'_>, c: &mut Ctxt<'_, '_>) -> compile::Result<ir::Ir> {
358 let span = hir.span();
359
360 let name = match hir.pat.pat.kind {
361 hir::PatKind::Ignore => {
362 return expr(&hir.expr, c);
363 }
364 hir::PatKind::Path(&hir::PatPathKind::Ident(name)) => name,
365 _ => {
366 return Err(compile::Error::msg(span, "not supported yet"));
367 }
368 };
369
370 Ok(ir::Ir::new(
371 span,
372 ir::IrDecl {
373 span,
374 name,
375 value: Box::try_new(expr(&hir.expr, c)?)?,
376 },
377 ))
378}
379
380#[instrument_ast]
381fn condition(hir: &hir::Condition<'_>, c: &mut Ctxt<'_, '_>) -> compile::Result<ir::IrCondition> {
382 match hir {
383 hir::Condition::Expr(e) => Ok(ir::IrCondition::Ir(expr(e, c)?)),
384 hir::Condition::ExprLet(hir) => {
385 let pat = ir::IrPat::compile_ast(&hir.pat.pat)?;
386 let ir = expr(&hir.expr, c)?;
387
388 Ok(ir::IrCondition::Let(ir::IrLet {
389 span: hir.span(),
390 pat,
391 ir,
392 }))
393 }
394 }
395}
396
397#[instrument_ast]
398fn expr_if(
399 span: Span,
400 c: &mut Ctxt<'_, '_>,
401 hir: &hir::Conditional<'_>,
402) -> compile::Result<ir::IrBranches> {
403 let mut branches = Vec::new();
404
405 for hir in hir.branches {
406 let cond = condition(hir.condition, c)?;
407 let ir = block(&hir.block, c)?;
408 branches.try_push((cond, ir))?;
409 }
410
411 let default_branch = match hir.fallback {
412 Some(hir) => Some(block(hir, c)?),
413 None => None,
414 };
415
416 Ok(ir::IrBranches {
417 span,
418 branches,
419 default_branch,
420 })
421}
422
423#[instrument_ast]
424fn expr_loop(
425 span: Span,
426 c: &mut Ctxt<'_, '_>,
427 hir: &hir::ExprLoop<'_>,
428) -> compile::Result<ir::IrLoop> {
429 Ok(ir::IrLoop {
430 span,
431 label: hir.label.map(TryInto::try_into).transpose()?,
432 condition: match hir.condition {
433 Some(hir) => Some(Box::try_new(condition(hir, c)?)?),
434 None => None,
435 },
436 body: block(&hir.body, c)?,
437 })
438}