1use crate::*;
2
3
4#[derive(Copy, Clone, Debug, Eq, PartialEq)]
5pub enum AsmBuiltinSymbol
6{
7 ProgramCounter,
8}
9
10
11pub fn eval(
12 report: &mut diagn::Report,
13 fileserver: &mut dyn util::FileServer,
14 decls: &asm::ItemDecls,
15 defs: &asm::ItemDefs,
16 ctx: &asm::ResolverContext,
17 eval_ctx: &mut expr::EvalContext,
18 expr: &expr::Expr)
19 -> Result<expr::Value, ()>
20{
21 let mut provider = |query: expr::EvalQuery|
22 {
23 match query
24 {
25 expr::EvalQuery::CtxLabel(query_ctxlabel) =>
26 asm::resolver::eval_ctxlabel(
27 decls,
28 defs,
29 ctx,
30 query_ctxlabel),
31
32 expr::EvalQuery::Variable(query_var) =>
33 asm::resolver::eval_variable(
34 decls,
35 defs,
36 ctx,
37 query_var),
38
39 expr::EvalQuery::Member(query_member) =>
40 asm::resolver::eval_member(
41 decls,
42 defs,
43 Some(ctx),
44 query_member),
45
46 expr::EvalQuery::Function(query_fn) =>
47 asm::resolver::eval_fn(
48 fileserver,
49 decls,
50 defs,
51 ctx,
52 query_fn),
53
54 expr::EvalQuery::AsmBlock(query_asm) =>
55 asm::resolver::eval_asm(
56 fileserver,
57 decls,
58 defs,
59 ctx,
60 query_asm),
61 }
62 };
63
64 expr.eval_with_ctx(
65 report,
66 eval_ctx,
67 &mut provider)
68}
69
70
71pub fn eval_simple(
75 report: &mut diagn::Report,
76 opts: &asm::AssemblyOptions,
77 decls: &asm::ItemDecls,
78 defs: &asm::ItemDefs,
79 expr: &expr::Expr)
80 -> Result<expr::Value, ()>
81{
82 let mut provider = |query: expr::EvalQuery|
83 {
84 match query
85 {
86 expr::EvalQuery::CtxLabel(_) =>
87 Ok(expr::Value::make_unknown()),
88
89 expr::EvalQuery::Variable(query_var) =>
90 asm::resolver::eval_variable_simple(
91 decls,
92 defs,
93 query_var),
94
95 expr::EvalQuery::Member(query_member) =>
96 asm::resolver::eval_member(
97 decls,
98 defs,
99 None,
100 query_member),
101
102 expr::EvalQuery::Function(_) =>
103 Ok(expr::Value::make_unknown()),
104
105 expr::EvalQuery::AsmBlock(_) =>
106 Ok(expr::Value::make_unknown()),
107 }
108 };
109
110 let result = expr.eval_with_ctx(
111 report,
112 &mut expr::EvalContext::new(opts),
113 &mut provider)?;
114
115 match result
116 {
117 expr::Value::FailedConstraint(_, msg) =>
118 {
119 report.message(msg);
120 Err(())
121 }
122
123 _ => Ok(result)
124 }
125}
126
127
128pub fn eval_certain(
129 report: &mut diagn::Report,
130 opts: &asm::AssemblyOptions,
131 decls: &asm::ItemDecls,
132 defs: &asm::ItemDefs,
133 expr: &expr::Expr)
134 -> Result<expr::Value, ()>
135{
136 let mut provider = |query: expr::EvalQuery|
137 {
138 match query
139 {
140 expr::EvalQuery::CtxLabel(_) =>
141 Ok(expr::Value::make_unknown()),
142
143 expr::EvalQuery::Variable(query_var) =>
144 asm::resolver::eval_variable_certain(
145 decls,
146 defs,
147 query_var),
148
149 expr::EvalQuery::Member(query_member) =>
150 asm::resolver::eval_member(
151 decls,
152 defs,
153 None,
154 query_member),
155
156 expr::EvalQuery::Function(_) =>
157 Ok(expr::Value::make_unknown()),
158
159 expr::EvalQuery::AsmBlock(_) =>
160 Ok(expr::Value::make_unknown()),
161 }
162 };
163
164 let result = expr.eval_with_ctx(
165 report,
166 &mut expr::EvalContext::new(opts),
167 &mut provider)?;
168
169 match result
170 {
171 expr::Value::Unknown(_) =>
172 {
173 report.error_span(
174 "cannot resolve expression",
175 expr.span());
176
177 Err(())
178 }
179
180 expr::Value::FailedConstraint(_, msg) =>
181 {
182 report.message(msg);
183 Err(())
184 }
185
186 _ => Ok(result)
187 }
188}
189
190
191pub fn eval_ctxlabel(
192 decls: &asm::ItemDecls,
193 defs: &asm::ItemDefs,
194 ctx: &asm::ResolverContext,
195 query: &mut expr::EvalCtxLabelQuery)
196 -> Result<expr::Value, ()>
197{
198 let symbol_ref = decls.symbols.get_current_label(
199 query.report,
200 query.span,
201 ctx.symbol_ctx,
202 query.nesting_level)?;
203
204 let symbol = defs.symbols.get(symbol_ref);
205
206 if symbol.value.is_unknown() &&
207 !ctx.can_guess()
208 {
209 query.report.error_span(
210 format!(
211 "unresolved symbol `{}`",
212 decls.symbols.get_displayable_name(
213 0,
214 ctx.symbol_ctx.get_hierarchy())),
215 query.span);
216
217 return Err(());
218 }
219
220 Ok(symbol.value.clone())
221}
222
223
224pub fn eval_variable(
225 decls: &asm::ItemDecls,
226 defs: &asm::ItemDefs,
227 ctx: &asm::ResolverContext,
228 query: &mut expr::EvalVariableQuery)
229 -> Result<expr::Value, ()>
230{
231 if query.hierarchy_level == 0
232 {
233 let maybe_builtin = eval_builtin_symbol(
234 decls,
235 defs,
236 ctx,
237 query,
238 query.hierarchy[0].as_ref())?;
239
240 if let Some(builtin) = maybe_builtin
241 {
242 return Ok(builtin);
243 }
244 }
245
246 let symbol_ref = decls.symbols.get_by_name(
247 query.report,
248 query.span,
249 ctx.symbol_ctx,
250 query.hierarchy_level,
251 query.hierarchy)?;
252
253 let symbol = defs.symbols.get(symbol_ref);
254
255 if symbol.value.is_unknown() &&
256 !ctx.can_guess()
257 {
258 query.report.error_span(
259 format!(
260 "unresolved symbol `{}`",
261 decls.symbols.get_displayable_name(
262 query.hierarchy_level,
263 query.hierarchy)),
264 query.span);
265
266 return Err(());
267 }
268
269 Ok(symbol.value.clone())
270}
271
272
273pub fn eval_variable_simple(
276 decls: &asm::ItemDecls,
277 defs: &asm::ItemDefs,
278 query: &mut expr::EvalVariableQuery)
279 -> Result<expr::Value, ()>
280{
281 if query.hierarchy_level == 0
282 {
283 match query.hierarchy[0].as_ref()
284 {
285 "$" | "pc" => return Ok(expr::Value::make_unknown()),
286 _ => {}
287 }
288 }
289
290 let symbol_ref = decls.symbols.try_get_by_name(
291 &util::SymbolContext::new_global(),
292 query.hierarchy_level,
293 query.hierarchy);
294
295 match symbol_ref
296 .map(|s| defs.symbols.maybe_get(s))
297 .flatten()
298 {
299 Some(symbol) => Ok(symbol.value.clone()),
300 None => Ok(expr::Value::make_unknown()),
301 }
302}
303
304
305pub fn eval_variable_certain(
306 decls: &asm::ItemDecls,
307 defs: &asm::ItemDefs,
308 query: &mut expr::EvalVariableQuery)
309 -> Result<expr::Value, ()>
310{
311 if query.hierarchy_level == 0
312 {
313 match query.hierarchy[0].as_ref()
314 {
315 "$" | "pc" =>
316 {
317 query.report.error_span(
318 "cannot get address in this context",
319 query.span);
320
321 return Err(());
322 }
323
324 _ => {}
325 }
326 }
327
328 let symbol_ref = decls.symbols.get_by_name(
329 query.report,
330 query.span,
331 &util::SymbolContext::new_global(),
332 query.hierarchy_level,
333 query.hierarchy)?;
334
335 let value = defs.symbols
336 .maybe_get(symbol_ref)
337 .map_or(expr::Value::make_unknown(), |s| s.value.clone());
338
339 if value.is_unknown()
340 {
341 query.report.error_span(
342 format!(
343 "unresolved symbol `{}`",
344 decls.symbols.get_displayable_name(
345 query.hierarchy_level,
346 query.hierarchy)),
347 query.span);
348
349 return Err(());
350 }
351
352 Ok(value)
353}
354
355
356pub fn resolve_builtin_symbol(
357 name: &str,
358 opts: &asm::AssemblyOptions)
359 -> Option<AsmBuiltinSymbol>
360{
361 if name == "$" ||
362 (name == "$pc" && !opts.use_legacy_behavior) ||
363 (name == "pc" && opts.use_legacy_behavior)
364 {
365 Some(AsmBuiltinSymbol::ProgramCounter)
366 }
367 else
368 {
369 None
370 }
371}
372
373
374fn eval_builtin_symbol(
375 _decls: &asm::ItemDecls,
376 defs: &asm::ItemDefs,
377 ctx: &asm::ResolverContext,
378 query: &mut expr::EvalVariableQuery,
379 name: &str)
380 -> Result<Option<expr::Value>, ()>
381{
382 if let Some(builtin_var) = resolve_builtin_symbol(name, query.opts)
383 {
384 match builtin_var
385 {
386 AsmBuiltinSymbol::ProgramCounter => {
387 let addr = ctx.eval_address(
388 query.report,
389 query.span,
390 defs,
391 ctx.can_guess())?;
392
393 Ok(Some(expr::Value::make_integer(addr)
394 .with_bank_ref(ctx.bank_ref)))
395 }
396 }
397 }
398 else
399 {
400 if let Some(builtin_fn) = asm::resolver::resolve_builtin_fn(name, query.opts)
401 {
402 Ok(Some(expr::Value::AsmBuiltinFn(
403 expr::Value::make_metadata(),
404 builtin_fn)))
405 }
406 else
407 {
408 Ok(None)
409 }
410 }
411}
412
413
414pub fn eval_member(
415 decls: &asm::ItemDecls,
416 defs: &asm::ItemDefs,
417 _ctx: Option<&asm::ResolverContext>,
418 query: &mut expr::EvalMemberQuery)
419 -> Result<expr::Value, ()>
420{
421 if let Some(item_ref) = query.value.get_metadata().symbol_ref
422 {
423 decls.symbols.get(item_ref);
424
425 let subsymbol_ref = decls.symbols.traverse(
426 Some(item_ref),
427 &[query.member_name]);
428
429 if let Some(subsymbol_ref) = subsymbol_ref
430 {
431 let subsymbol = defs.symbols.get(subsymbol_ref);
432 return Ok(subsymbol.value.clone());
433 }
434 }
435
436 if let Some(value) = eval_member_bankdef(decls, defs, _ctx, query)?
437 {
438 return Ok(value);
439 }
440
441 if let Some(value) = expr::resolve_builtin_member(query)?
442 {
443 return Ok(value);
444 }
445
446 query.report.error_span(
447 format!(
448 "unknown symbol `{}`",
449 query.member_name),
450 query.span);
451
452 Err(())
453}
454
455
456pub fn eval_member_bankdef(
457 _decls: &asm::ItemDecls,
458 defs: &asm::ItemDefs,
459 _ctx: Option<&asm::ResolverContext>,
460 query: &mut expr::EvalMemberQuery)
461 -> Result<Option<expr::Value>, ()>
462{
463 let expr::Value::Bankdef(_, bank_ref) = query.value
464 else { return Ok(None); };
465
466 let bank = defs.bankdefs.get(bank_ref);
467
468 match query.member_name
469 {
470 "bits" => Ok(Some(expr::Value::make_integer(bank.addr_unit))),
471 "addr" => Ok(Some(expr::Value::make_integer(bank.addr_start.clone()))),
472 "outp" => Ok(Some(expr::Value::make_maybe_integer(bank.output_offset))),
473 "size" => Ok(Some(expr::Value::make_maybe_integer(bank.size_in_units))),
474 "size_b" => Ok(Some(expr::Value::make_maybe_integer(bank.size_in_bits))),
475 "data" => Ok(Some(bank.userdata.clone())),
476 _ => Ok(None)
477 }
478}