1use crate::alloc::prelude::*;
2use crate::alloc::{try_format, Vec};
3use crate::ast::Spanned;
4use crate::compile::ir;
5use crate::compile::ir::scopes::MissingLocal;
6use crate::compile::meta;
7use crate::compile::{self, IrErrorKind, ItemId, ModId, WithSpan};
8use crate::hir;
9use crate::query::{Query, Used};
10use crate::runtime::{self, ConstValue, Object, OwnedTuple, Repr, Value};
11use crate::TypeHash;
12
13pub struct Interpreter<'a, 'arena> {
15 pub(crate) budget: Budget,
18 pub(crate) module: ModId,
20 pub(crate) item: ItemId,
22 pub(crate) scopes: ir::Scopes,
24 pub(crate) q: Query<'a, 'arena>,
26}
27
28impl Interpreter<'_, '_> {
29 pub(crate) fn eval_const(&mut self, ir: &ir::Ir, used: Used) -> compile::Result<ConstValue> {
31 tracing::trace!("processing constant: {}", self.q.pool.item(self.item));
32
33 if let Some(const_value) = self.q.consts.get(self.item) {
34 return Ok(const_value.try_clone()?);
35 }
36
37 if !self.q.consts.mark(self.item)? {
38 return Err(compile::Error::new(ir, IrErrorKind::ConstCycle));
39 }
40
41 let ir_value = match ir::eval_ir(ir, self, used) {
42 Ok(ir_value) => ir_value,
43 Err(outcome) => match outcome {
44 ir::EvalOutcome::Error(error) => {
45 return Err(error);
46 }
47 ir::EvalOutcome::NotConst(span) => {
48 return Err(compile::Error::new(span, IrErrorKind::NotConst))
49 }
50 ir::EvalOutcome::Break(span, _, _) => {
51 return Err(compile::Error::new(span, IrErrorKind::BreakOutsideOfLoop))
52 }
53 },
54 };
55
56 let const_value: ConstValue = crate::from_value(ir_value).with_span(ir)?;
57
58 if self
59 .q
60 .consts
61 .insert(self.item, const_value.try_clone()?)?
62 .is_some()
63 {
64 return Err(compile::Error::new(ir, IrErrorKind::ConstCycle));
65 }
66
67 Ok(const_value)
68 }
69
70 pub(crate) fn eval_value(&mut self, ir: &ir::Ir, used: Used) -> compile::Result<Value> {
72 match ir::eval_ir(ir, self, used) {
73 Ok(ir_value) => Ok(ir_value),
74 Err(outcome) => match outcome {
75 ir::EvalOutcome::Error(error) => Err(error),
76 ir::EvalOutcome::NotConst(span) => {
77 Err(compile::Error::new(span, IrErrorKind::NotConst))
78 }
79 ir::EvalOutcome::Break(span, _, _) => {
80 Err(compile::Error::new(span, IrErrorKind::BreakOutsideOfLoop))
81 }
82 },
83 }
84 }
85
86 pub(crate) fn resolve_var(
91 &mut self,
92 span: &dyn Spanned,
93 name: &hir::Variable,
94 used: Used,
95 ) -> compile::Result<Value> {
96 if let Some(ir_value) = self.scopes.try_get(name) {
97 return Ok(ir_value.try_clone()?);
98 }
99
100 let mut base = self.q.pool.item(self.item).try_to_owned()?;
101
102 loop {
103 let item = self
104 .q
105 .pool
106 .alloc_item(base.extended(name.try_to_string()?)?)?;
107
108 if let Some(const_value) = self.q.consts.get(item) {
109 return Ok(const_value.to_value_with(self.q.context).with_span(span)?);
110 }
111
112 if let Some(meta) = self.q.query_meta(span, item, used)? {
113 match &meta.kind {
114 meta::Kind::Const => {
115 let Some(const_value) = self.q.get_const_value(meta.hash) else {
116 return Err(compile::Error::msg(
117 span,
118 try_format!("Missing constant for hash {}", meta.hash),
119 ));
120 };
121
122 return Ok(const_value.to_value_with(self.q.context).with_span(span)?);
123 }
124 _ => {
125 return Err(compile::Error::new(
126 span,
127 IrErrorKind::UnsupportedMeta {
128 meta: meta.info(self.q.pool)?,
129 },
130 ));
131 }
132 }
133 }
134
135 if !base.pop() {
136 break;
137 }
138 }
139
140 Err(compile::Error::new(
141 span,
142 MissingLocal(name.try_to_string()?.try_into_boxed_str()?),
143 ))
144 }
145
146 pub(crate) fn call_const_fn<S>(
147 &mut self,
148 spanned: S,
149 id: ItemId,
150 args: Vec<Value>,
151 used: Used,
152 ) -> compile::Result<Value>
153 where
154 S: Copy + Spanned,
155 {
156 let span = Spanned::span(&spanned);
157 let const_fn = self.q.const_fn_for(id).with_span(span)?;
158
159 if const_fn.ir_fn.args.len() != args.len() {
160 return Err(compile::Error::new(
161 span,
162 IrErrorKind::ArgumentCountMismatch {
163 actual: args.len(),
164 expected: const_fn.ir_fn.args.len(),
165 },
166 ));
167 }
168
169 let guard = self.scopes.isolate()?;
170
171 for (name, value) in const_fn.ir_fn.args.iter().zip(args) {
172 self.scopes.decl(*name, value).with_span(span)?;
173 }
174
175 let value = self.eval_value(&const_fn.ir_fn.ir, used)?;
176 self.scopes.pop(guard).with_span(span)?;
177 Ok(value)
178 }
179}
180
181impl ir::Scopes {
182 pub(crate) fn get_target(&self, ir_target: &ir::IrTarget) -> compile::Result<Value> {
184 match &ir_target.kind {
185 ir::IrTargetKind::Name(name) => Ok(self.get_name(name, ir_target)?.clone()),
186 ir::IrTargetKind::Field(ir_target, field) => {
187 let value = self.get_target(ir_target)?;
188 let object = value.borrow_ref::<Object>().with_span(ir_target)?;
189
190 let Some(value) = object.get(field.as_ref()) else {
191 return Err(compile::Error::new(
192 ir_target,
193 IrErrorKind::MissingField {
194 field: field.try_clone()?,
195 },
196 ));
197 };
198
199 Ok(value.clone())
200 }
201 ir::IrTargetKind::Index(target, index) => {
202 let value = self.get_target(target)?;
203
204 match value.type_hash() {
205 runtime::Vec::HASH => {
206 let vec = value.borrow_ref::<runtime::Vec>().with_span(ir_target)?;
207
208 if let Some(value) = vec.get(*index) {
209 return Ok(value.clone());
210 }
211 }
212 runtime::OwnedTuple::HASH => {
213 let tuple = value
214 .borrow_ref::<runtime::OwnedTuple>()
215 .with_span(ir_target)?;
216
217 if let Some(value) = tuple.get(*index) {
218 return Ok(value.clone());
219 }
220 }
221 _ => {
222 return Err(compile::Error::expected_type::<OwnedTuple>(
223 ir_target,
224 value.type_info(),
225 ));
226 }
227 }
228
229 Err(compile::Error::new(
230 ir_target,
231 IrErrorKind::MissingIndex { index: *index },
232 ))
233 }
234 }
235 }
236
237 pub(crate) fn set_target(
239 &mut self,
240 ir_target: &ir::IrTarget,
241 value: Value,
242 ) -> compile::Result<()> {
243 match &ir_target.kind {
244 ir::IrTargetKind::Name(name) => {
245 *self.get_name_mut(name, ir_target)? = value;
246 Ok(())
247 }
248 ir::IrTargetKind::Field(target, field) => {
249 let target = self.get_target(target)?;
250 let mut object = target.borrow_mut::<Object>().with_span(ir_target)?;
251 let field = field.as_ref().try_to_owned()?;
252 object.insert(field, value).with_span(ir_target)?;
253 Ok(())
254 }
255 ir::IrTargetKind::Index(target, index) => {
256 let target = self.get_target(target)?;
257
258 match target.as_ref() {
259 Repr::Inline(value) => {
260 return Err(compile::Error::expected_type::<OwnedTuple>(
261 ir_target,
262 value.type_info(),
263 ));
264 }
265 Repr::Dynamic(value) => {
266 return Err(compile::Error::expected_type::<OwnedTuple>(
267 ir_target,
268 value.type_info(),
269 ));
270 }
271 Repr::Any(any) => match any.type_hash() {
272 runtime::Vec::HASH => {
273 let mut vec = any.borrow_mut::<runtime::Vec>().with_span(ir_target)?;
274
275 if let Some(current) = vec.get_mut(*index) {
276 *current = value;
277 return Ok(());
278 }
279 }
280 runtime::OwnedTuple::HASH => {
281 let mut tuple = any
282 .borrow_mut::<runtime::OwnedTuple>()
283 .with_span(ir_target)?;
284
285 if let Some(current) = tuple.get_mut(*index) {
286 *current = value;
287 return Ok(());
288 }
289 }
290 _ => {
291 return Err(compile::Error::expected_type::<OwnedTuple>(
292 ir_target,
293 any.type_info(),
294 ));
295 }
296 },
297 };
298
299 Err(compile::Error::msg(ir_target, "missing index"))
300 }
301 }
302 }
303
304 pub(crate) fn mut_target(
306 &mut self,
307 ir_target: &ir::IrTarget,
308 op: impl FnOnce(&mut Value) -> compile::Result<()>,
309 ) -> compile::Result<()> {
310 match &ir_target.kind {
311 ir::IrTargetKind::Name(name) => {
312 let value = self.get_name_mut(name, ir_target)?;
313 op(value)
314 }
315 ir::IrTargetKind::Field(target, field) => {
316 let value = self.get_target(target)?;
317 let mut object = value.borrow_mut::<Object>().with_span(ir_target)?;
318
319 let Some(value) = object.get_mut(field.as_ref()) else {
320 return Err(compile::Error::new(
321 ir_target,
322 IrErrorKind::MissingField {
323 field: field.try_clone()?,
324 },
325 ));
326 };
327
328 op(value)
329 }
330 ir::IrTargetKind::Index(target, index) => {
331 let current = self.get_target(target)?;
332
333 match current.as_ref() {
334 Repr::Dynamic(value) => Err(compile::Error::expected_type::<OwnedTuple>(
335 ir_target,
336 value.type_info(),
337 )),
338 Repr::Any(value) => match value.type_hash() {
339 runtime::Vec::HASH => {
340 let mut vec =
341 value.borrow_mut::<runtime::Vec>().with_span(ir_target)?;
342
343 let value = vec.get_mut(*index).ok_or_else(|| {
344 compile::Error::new(
345 ir_target,
346 IrErrorKind::MissingIndex { index: *index },
347 )
348 })?;
349
350 op(value)
351 }
352 runtime::OwnedTuple::HASH => {
353 let mut tuple = value
354 .borrow_mut::<runtime::OwnedTuple>()
355 .with_span(ir_target)?;
356
357 let value = tuple.get_mut(*index).ok_or_else(|| {
358 compile::Error::new(
359 ir_target,
360 IrErrorKind::MissingIndex { index: *index },
361 )
362 })?;
363
364 op(value)
365 }
366 _ => Err(compile::Error::expected_type::<OwnedTuple>(
367 ir_target,
368 value.type_info(),
369 )),
370 },
371 actual => Err(compile::Error::expected_type::<OwnedTuple>(
372 ir_target,
373 actual.type_info(),
374 )),
375 }
376 }
377 }
378 }
379}
380
381pub(crate) struct Budget {
383 budget: usize,
384}
385
386impl Budget {
387 pub(crate) fn new(budget: usize) -> Self {
389 Self { budget }
390 }
391
392 pub(crate) fn take<S>(&mut self, spanned: S) -> compile::Result<()>
394 where
395 S: Spanned,
396 {
397 if self.budget == 0 {
398 return Err(compile::Error::new(spanned, IrErrorKind::BudgetExceeded));
399 }
400
401 self.budget -= 1;
402 Ok(())
403 }
404}