customasm/asm/resolver/
eval.rs

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
71/// Evaluates an expression without relying on
72/// addresses, banks, user-defined functions,
73/// or user-defined instructions.
74pub 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
273/// Evaluates a variable/symbol without relying on
274/// addresses or banks.
275pub 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}