Skip to main content

microcad_lang/eval/
eval_error.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Evaluation error
5
6#![allow(unused, unused_assignments)]
7
8use crate::{
9    eval::*,
10    lower::{LowerError, ir},
11    model::OutputType,
12    resolve::*,
13    ty::*,
14    value::*,
15};
16use microcad_lang_base::{DiagError, Identifier, SrcRef};
17use miette::Diagnostic;
18use thiserror::Error;
19
20/// Evaluation error.
21#[derive(Debug, Error, Diagnostic)]
22#[allow(missing_docs)]
23pub enum EvalError {
24    /// Can't find a project file by it's qualified name.
25    #[error("Not implemented: {0}")]
26    Todo(String),
27
28    /// List index out of bounds.
29    #[error("List index out of bounds: {index} >= {len}")]
30    ListIndexOutOfBounds {
31        /// Wrong index
32        index: usize,
33        /// Length of list
34        len: usize,
35    },
36
37    /// Parameter type mismatch.
38    #[error("Type mismatch for `{id}`: expected {expected}, got {found}")]
39    TypeMismatch {
40        /// Parameter name
41        id: Identifier,
42        /// Expected type
43        expected: Type,
44        /// Found type
45        found: Type,
46    },
47
48    /// Array elements have different types.
49    #[error("Array elements have different types: {0}")]
50    ArrayElementsDifferentTypes(TypeList),
51
52    /// Symbol not found.
53    #[error("Symbol {0} not found.")]
54    SymbolNotFound(ir::QualifiedName),
55
56    /// The symbol cannot be called, e.g. when it is a source file or a module.
57    #[error("Symbol `{0}` cannot be called.")]
58    SymbolCannotBeCalled(ir::QualifiedName),
59
60    /// Found ambiguous symbols.
61    #[error("Ambiguous symbol {0} might be one of the following: {1}")]
62    AmbiguousSymbol(ir::QualifiedName, ir::QualifiedNames),
63
64    /// Local Symbol not found.
65    #[error("Local symbol not found: {0}")]
66    LocalNotFound(Identifier),
67
68    /// A property of a value was not found.
69    #[error("Property not found: {0}")]
70    PropertyNotFound(Identifier),
71
72    /// A property of a value was not found.
73    #[error("Not a property id: {0}")]
74    NoPropertyId(ir::QualifiedName),
75
76    /// Argument count mismatch.
77    #[error("Argument count mismatch: expected {expected}, got {found} in {args}")]
78    ArgumentCountMismatch {
79        /// Argument list including the error
80        args: String,
81        /// Expected number of arguments
82        expected: usize,
83        /// Found number of arguments
84        found: usize,
85    },
86
87    /// Assertion failed.
88    #[error("Assertion failed: {0}")]
89    AssertionFailed(String),
90
91    /// Different type expected.
92    #[error("Expected type `{expected}`, found type `{found}")]
93    ExpectedType {
94        /// Expected type.
95        expected: Type,
96        /// Found type.
97        found: Type,
98    },
99
100    /// Diagnostic error
101    #[error("Diagnostic error: {0}")]
102    DiagError(#[from] DiagError),
103
104    /// No locals  available on stack.
105    #[error("Local stack needed to store {0}")]
106    LocalStackEmpty(Identifier),
107
108    /// Unexpected stack frame type
109    #[error("Unexpected stack frame of type '{1}' cannot store {0}")]
110    WrongStackFrame(Identifier, &'static str),
111
112    /// Value Error.
113    #[error("Value Error: {0}")]
114    ValueError(#[from] ValueError),
115
116    /// Unknown method.
117    #[error("Unknown method `{0}`")]
118    UnknownMethod(ir::QualifiedName),
119
120    /// Parser Error
121    #[error("Parsing error {0}")]
122    LowerError(#[from] LowerError),
123
124    /// Unexpected element within expression.
125    #[error("Unexpected {0} {1} within expression")]
126    UnexpectedNested(&'static str, Identifier),
127
128    /// Missing arguments
129    #[error("Missing arguments: {0}")]
130    MissingArguments(ir::IdentifierList),
131
132    /// Missing arguments
133    #[error("Too many arguments: {0}")]
134    TooManyArguments(ir::IdentifierList),
135
136    /// Arguments match by identifier but have incompatible types
137    #[error("Arguments match by identifier but have incompatible types: {0}")]
138    IdMatchButNotType(String),
139
140    /// Builtin error
141    #[error("Builtin error: {0}")]
142    BuiltinError(String),
143
144    /// Trying to use multiplicity where it is not allowed
145    #[error("Multiplicity not allowed '{0}'")]
146    MultiplicityNotAllowed(ir::IdentifierList),
147
148    /// An error if you try to mix 2d and 3d geometries.
149    #[error("Cannot mix 2d and 3d geometries")]
150    CannotMixGeometry,
151
152    /// A condition of an if statement is not a boolean
153    #[error("If condition is not a boolean: {condition}")]
154    IfConditionIsNotBool {
155        condition: String,
156        #[label("Not a boolean")]
157        src_ref: SrcRef,
158    },
159
160    /// Workbench didn't find a initialization routine matching the given arguments
161    #[error("Workbench {name} cannot find initialization for those arguments")]
162    #[diagnostic(help("Possible initializations: \n\t{}", possible_params.join("\n\t")))]
163    NoInitializationFound {
164        #[label("Got: {name}( {actual_params} )")]
165        src_ref: SrcRef,
166        name: Identifier,
167        actual_params: String,
168        possible_params: Vec<String>,
169    },
170    /// Workbench didn't find a initialization routine matching the given arguments
171    #[error("Workbench {name} has ambiguous initialization for those arguments")]
172    #[diagnostic(help("Ambiguous initializations: \n\t{}", ambiguous_params.join("\n\t")))]
173    AmbiguousInitialization {
174        #[label("Got: {name}( {actual_params} )")]
175        src_ref: SrcRef,
176        name: Identifier,
177        actual_params: String,
178        ambiguous_params: Vec<String>,
179    },
180
181    /// Initializer missed to set a property from plan
182    #[error("Building plan incomplete. Missing properties: {0}")]
183    BuildingPlanIncomplete(ir::IdentifierList),
184
185    /// This errors happens if the expression is supposed to produce models but did not.
186    #[error("This expression statement did not produce any model")]
187    EmptyModelExpression,
188
189    /// This error happens if the workbench produced a different output type.
190    #[error("The {kind} workbench produced a {produced} output, but expected a {expected} output.")]
191    WorkbenchInvalidOutput {
192        kind: ir::WorkbenchKind,
193        produced: OutputType,
194        expected: OutputType,
195    },
196
197    /// Resolve Error
198    #[error("Resolve error: {0}")]
199    #[diagnostic(transparent)]
200    ResolveError(ResolveError),
201
202    /// Cannot call operation without workpiece, e.g. `op()`.
203    #[error("Cannot call operation without workpiece.")]
204    CannotCallOperationWithoutWorkpiece,
205
206    /// There is no model in this workbench
207    #[error("Missing model in workbench")]
208    NoModelInWorkbench,
209
210    /// Assignment failed because value already has been defined before.
211    #[error("Value {name} already in defined: {value}")]
212    #[diagnostic(help("Values in microcad are immutable"))]
213    ValueAlreadyDefined {
214        /// Location of the error
215        #[label(primary, "{name} is already defined")]
216        location: SrcRef,
217        /// Name of the value
218        name: Identifier,
219        /// Previous value
220        value: String,
221        /// Previous definition
222        #[label("Previously defined here")]
223        previous_location: SrcRef,
224    },
225
226    /// Assignment failed because left side is not an l-value
227    #[error("Assignment failed because {0} is not an l-value")]
228    NotAnLValue(Identifier),
229
230    /// Found symbol but it's not visible to user
231    #[error("Symbol {what} is private from within {within}")]
232    SymbolIsPrivate {
233        /// what was searched
234        what: ir::QualifiedName,
235        /// where it was searched
236        within: ir::QualifiedName,
237    },
238
239    /// Found unused global symbols.
240    #[error("Unused global symbol {0}.")]
241    UnusedGlobalSymbol(String),
242
243    /// Unused local.
244    #[error("Unused local {0}.")]
245    UnusedLocal(Identifier),
246
247    /// Evaluation aborted because of prior resolve errors
248    #[error("Evaluation aborted because of prior resolve errors!")]
249    ResolveFailed,
250
251    /// Bad range (first > last)
252    #[error("Bad range, first number ({0}) must be smaller than last ({1})")]
253    BadRange(i64, i64),
254
255    /// Ambiguous types in tuple
256    #[error("Ambiguous type '{ty}' in tuple")]
257    AmbiguousType {
258        ty: Type,
259        #[label(
260            "Some unnamed values in this tuple share the same type '{ty}'.\nMaybe check the units or use identifiers in this tuple."
261        )]
262        src_ref: SrcRef,
263    },
264}
265
266/// Result type of any evaluation.
267pub type EvalResult<T> = std::result::Result<T, Box<EvalError>>;
268
269impl From<ResolveError> for EvalError {
270    fn from(err: ResolveError) -> Self {
271        match err {
272            ResolveError::SymbolNotFound(name) => EvalError::SymbolNotFound(name),
273            other => EvalError::ResolveError(other),
274        }
275    }
276}
277
278impl From<Box<EvalError>> for miette::Report {
279    fn from(value: Box<EvalError>) -> Self {
280        miette::Report::new(*value)
281    }
282}
283
284impl From<DiagError> for Box<EvalError> {
285    fn from(value: DiagError) -> Self {
286        Box::new(value.into())
287    }
288}
289
290impl From<ValueError> for Box<EvalError> {
291    fn from(value: ValueError) -> Self {
292        Box::new(value.into())
293    }
294}
295
296impl From<ResolveError> for Box<EvalError> {
297    fn from(value: ResolveError) -> Self {
298        Box::new(value.into())
299    }
300}