microcad_lang/eval/
mod.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Evaluation of parsed content.
5//!
6//! To be able to evaluate (run) a source file, it must be loaded, parsed and resolved.
7//! To do so a [`EvalContext`] can be created with [`EvalContext::new()`] based on an already resolved symbol or
8//! by using [`EvalContext::from_source()`] or `ContextBuilder::from_source_captured()` which both automatically
9//! load and resolve the source file and build a context around it which then can be evaluated with [`EvalContext::eval()`]:
10//!
11//! ```ignore
12//! use microcad_lang::eval::EvalContext;
13//! use microcad_lang::diag::Diag;
14//! use std::io::stdout;
15//!
16//! // create a context for evaluation of the source file
17//! let mut context = EvalContext::from_source(
18//!     "my.µcad",              // root file name
19//!     builtin_module(),    // `__builtin` library
20//!     &["./std/lib".into()]       // list of std library path
21//! ).expect("successful load, parse and resolve");
22//!
23//! // evaluate the source file in it's context
24//! let node = context.eval().expect("successful evaluation");
25//!
26//! // print any error
27//! println!("{}", context.diagnosis());
28//! ```
29
30mod argument_match;
31mod attribute;
32mod body;
33mod call;
34mod eval_context;
35mod eval_error;
36mod expression;
37mod format_string;
38mod function;
39mod grant;
40mod init;
41mod literal;
42mod locals;
43mod module_definition;
44mod output;
45mod parameter;
46mod source_file;
47mod sources;
48mod statements;
49mod tuple;
50mod workbench;
51
52pub use argument_match::*;
53pub use attribute::*;
54pub use call::*;
55pub use eval_context::*;
56pub use eval_error::*;
57pub use output::*;
58
59use grant::*;
60use locals::*;
61use statements::*;
62
63use crate::{diag::*, resolve::*, src_ref::*, syntax::*, ty::*, value::*};
64
65/// Evaluation trait.
66///
67/// The return type `T` defines to which output type the type is evaluated.
68/// Usually, these output types are used in specific context:
69///
70/// | Return type `T`     | Context / Scope                                     | Return value on error   | Description                                                             |
71/// | ------------------- | --------------------------------------------------- | ----------------------- | ----------------------------------------------------------------------- |
72/// | `()`                | [`Assignment`].                                     | `()`                    | An assignment returns nothing but alters the symbol table.              |
73/// |                     |                                                     |                         |
74/// | `Value`             | Function calls, module statements,                  | `Value::None`           | These trait implementations are   
75/// |                     | parameter lists, argument lists.                    |                         | mostly used when evaluating functions.
76/// |                     |                                                     |                         |
77/// | `Option<Model>`     | Workbenches, object bodies, source files, if.       | `None`                  | Something is evaluated into a single model.                              |
78/// |                     |                                                     |                         |
79/// | `Models`            | Statement, statement list, body, multiplicities.    | `Models::default()`     | A collection of models . |
80pub trait Eval<T = Value> {
81    /// Evaluate a syntax element into a type `T`.
82    fn eval(&self, context: &mut EvalContext) -> EvalResult<T>;
83}
84
85impl MethodCall {
86    /// Evaluate method call.
87    ///
88    /// Examples:
89    /// ```microcad
90    /// assert([2.0, 2.0].all_equal(), "All elements in this list must be equal.");
91    /// ```
92    fn eval(&self, context: &mut EvalContext, lhs: &Expression) -> EvalResult<Value> {
93        let value: Value = lhs.eval(context)?;
94        if let Value::Model(model) = &value {
95            if model.has_no_output() {
96                context.warning(&lhs, EvalError::EmptyModelExpression)?;
97            }
98        }
99        let args = self.argument_list.eval(context)?;
100        value.call_method(&self.name, &args, context)
101    }
102}
103
104/// Like `todo!()` but within a evaluation context
105///
106/// emits a diagnostic error instead of panicking.
107#[macro_export]
108macro_rules! eval_todo {
109    ($context: ident, $refer: ident, $($arg:tt)*) => {{
110        $context.error($refer, EvalError::Todo(format_args!($($arg)*).to_string()))?;
111        Ok(Value::None)
112    }}
113}
114
115pub use eval_todo;