Skip to main content

microcad_lang/parse/
parse_error.rs

1// Copyright © 2024-2026 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Parser errors
5
6use crate::{parse::*, ty::*};
7use microcad_syntax::ast::LiteralErrorKind;
8use miette::{Diagnostic, LabeledSpan, SourceCode};
9use std::iter::once;
10use thiserror::Error;
11
12/// Parsing errors
13#[derive(Debug, Error)]
14#[allow(missing_docs)]
15pub enum ParseError {
16    /// Error parsing floating point literal
17    #[error("Error parsing floating point literal: {0}")]
18    ParseFloatError(Refer<std::num::ParseFloatError>),
19
20    /// Error parsing integer literal
21    #[error("Error parsing integer literal: {0}")]
22    ParseIntError(Refer<std::num::ParseIntError>),
23
24    /// IO Error
25    #[error("IO Error: {0}")]
26    IoError(Refer<std::io::Error>),
27
28    /// Error parsing color literal
29    #[error("Error parsing color: {0}")]
30    ParseColorError(Refer<microcad_core::ParseColorError>),
31
32    /// Unknown color name
33    #[error("Unknown color: {0}")]
34    UnknownColorName(Refer<String>),
35
36    /// Unknown unit
37    #[error("Unknown unit: {0}")]
38    UnknownUnit(Refer<String>),
39
40    /// Unexpected token
41    #[error("Unexpected token")]
42    UnexpectedToken(SrcRef),
43
44    /// Tuple expression contains both named and positional arguments
45    #[error("Tuple expression contains both named and positional arguments")]
46    MixedTupleArguments(SrcRef),
47
48    /// Duplicate named argument
49    #[error("Duplicate named argument: {0}")]
50    DuplicateNamedArgument(Identifier),
51
52    /// Positional argument after named argument
53    #[error("Positional argument after named argument")]
54    PositionalArgumentAfterNamed(SrcRef),
55
56    /// Empty tuple expression
57    #[error("Empty tuple expression")]
58    EmptyTupleExpression(SrcRef),
59
60    /// Missing type or value for definition parameter
61    #[error("Missing type or value for definition parameter: {0}")]
62    ParameterMissingTypeOrValue(Identifier),
63
64    /// Duplicate parameter
65    #[error("Duplicate parameter: {0}")]
66    DuplicateParameter(Identifier),
67
68    /// Duplicate argument
69    #[error("Duplicate argument: {0}")]
70    DuplicateArgument(Identifier),
71
72    /// Duplicated type name in map
73    #[error("Duplicated type name in map: {0}")]
74    DuplicatedMapType(Identifier),
75
76    /// Duplicate id
77    #[error("Duplicate id: {0}")]
78    DuplicateIdentifier(Identifier),
79
80    /// Duplicate id in tuple
81    #[error("Duplicate id in tuple: {0}")]
82    DuplicateTupleIdentifier(Identifier),
83
84    /// Duplicate unnamed type in tuple
85    #[error("Duplicate unnamed type in tuple: {0}")]
86    DuplicateTupleType(Refer<Type>),
87
88    /// Missing format expression
89    #[error("Missing format expression")]
90    MissingFormatExpression(SrcRef),
91
92    /// Statement between two init statements
93    #[error("Statement between two init statements")]
94    StatementBetweenInit(SrcRef),
95
96    /// Loading of a source file failed
97    #[error("Loading of source file {1:?} failed: {2}")]
98    LoadSource(SrcRef, std::path::PathBuf, std::io::Error),
99
100    /// Grammar rule error
101    #[error("Grammar rule error {0}")]
102    GrammarRuleError(Refer<String>),
103
104    /// Grammar rule error
105    #[error("Invalid qualified name '{0}'")]
106    InvalidQualifiedName(Refer<String>),
107
108    /// Grammar rule error
109    #[error("Invalid id '{0}'")]
110    InvalidIdentifier(Refer<String>),
111
112    /// Qualified name cannot be converted into an Id
113    #[error("Qualified name {0} cannot be converted into an Id")]
114    QualifiedNameIsNoId(QualifiedName),
115
116    /// Element is not available
117    #[error("Element is not available")]
118    NotAvailable(SrcRef),
119
120    /// Unknown type
121    #[error("Unknown type: {0}")]
122    UnknownType(Refer<String>),
123
124    /// Matrix type with invalid dimenstions
125    #[error("Invalid matrix type: {0}")]
126    InvalidMatrixType(Refer<String>),
127
128    /// If expression is missing an `else`
129    #[error("If expression must return a value in all cases")]
130    IncompleteIfExpression(SrcRef),
131
132    /// Invalid glob pattern
133    #[error("Invalid glob pattern, wildcard must be at the end of the pattern")]
134    InvalidGlobPattern(SrcRef),
135
136    /// A glob import is given an alias
137    #[error("Glob imports can't be given an alias")]
138    UseGlobAlias(SrcRef),
139
140    /// A parser from the AST builder
141    #[error("{error}")]
142    AstParser {
143        src_ref: SrcRef,
144        error: microcad_syntax::ParseError,
145    },
146
147    /// Call attribute with a non-identifier name
148    #[error("Call attributes must have a plain identifier as name")]
149    InvalidAttributeCall(QualifiedName),
150
151    /// An invalid literal was encountered
152    #[error("Invalid literal: {error}")]
153    InvalidLiteral {
154        error: LiteralErrorKind,
155        src_ref: SrcRef,
156    },
157
158    /// An invalid expression was encountered
159    #[error("Invalid expression")]
160    InvalidExpression { src_ref: SrcRef },
161
162    /// An invalid state was encountered
163    #[error("Invalid statement")]
164    InvalidStatement { src_ref: SrcRef },
165
166    /// A type range between non-integer literals
167    #[error("range expressions must be between integers")]
168    InvalidRangeType { src_ref: SrcRef },
169}
170
171/// Result with parse error
172pub type ParseResult<T> = Result<T, ParseError>;
173
174impl SrcReferrer for ParseError {
175    fn src_ref(&self) -> SrcRef {
176        match self {
177            ParseError::DuplicateNamedArgument(id)
178            | ParseError::ParameterMissingTypeOrValue(id)
179            | ParseError::DuplicateParameter(id)
180            | ParseError::DuplicateArgument(id)
181            | ParseError::DuplicatedMapType(id)
182            | ParseError::DuplicateIdentifier(id)
183            | ParseError::DuplicateTupleIdentifier(id) => id.src_ref(),
184            ParseError::QualifiedNameIsNoId(name) | ParseError::InvalidAttributeCall(name) => {
185                name.src_ref()
186            }
187            ParseError::UnexpectedToken(src_ref)
188            | ParseError::MixedTupleArguments(src_ref)
189            | ParseError::PositionalArgumentAfterNamed(src_ref)
190            | ParseError::EmptyTupleExpression(src_ref)
191            | ParseError::MissingFormatExpression(src_ref)
192            | ParseError::StatementBetweenInit(src_ref)
193            | ParseError::NotAvailable(src_ref)
194            | ParseError::IncompleteIfExpression(src_ref)
195            | ParseError::LoadSource(src_ref, ..)
196            | ParseError::InvalidGlobPattern(src_ref)
197            | ParseError::UseGlobAlias(src_ref)
198            | ParseError::AstParser { src_ref, .. }
199            | ParseError::InvalidLiteral { src_ref, .. }
200            | ParseError::InvalidExpression { src_ref }
201            | ParseError::InvalidStatement { src_ref }
202            | ParseError::InvalidRangeType { src_ref } => src_ref.clone(),
203            ParseError::ParseFloatError(parse_float_error) => parse_float_error.src_ref(),
204            ParseError::ParseIntError(parse_int_error) => parse_int_error.src_ref(),
205            ParseError::IoError(error) => error.src_ref(),
206            ParseError::ParseColorError(parse_color_error) => parse_color_error.src_ref(),
207            ParseError::UnknownColorName(name) => name.src_ref(),
208            ParseError::UnknownUnit(unit) => unit.src_ref(),
209            ParseError::DuplicateTupleType(ty) => ty.src_ref(),
210            ParseError::GrammarRuleError(rule) => rule.src_ref(),
211            ParseError::InvalidQualifiedName(name) => name.src_ref(),
212            ParseError::InvalidIdentifier(id) => id.src_ref(),
213            ParseError::UnknownType(ty) => ty.src_ref(),
214            ParseError::InvalidMatrixType(ty) => ty.src_ref(),
215        }
216    }
217}
218
219impl ParseError {
220    /// Add source code to the error
221    pub fn with_source(self, source: String) -> ParseErrorsWithSource {
222        ParseErrorsWithSource {
223            errors: vec![self],
224            source_code: Some(source),
225            source_hash: 0,
226        }
227    }
228}
229
230impl Diagnostic for ParseError {
231    fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
232        let src_ref = self.src_ref().0?;
233        let message = match self {
234            ParseError::AstParser { error, .. } => {
235                return error.labels();
236            }
237            _ => self.to_string(),
238        };
239        let label = LabeledSpan::new(Some(message), src_ref.range.start, src_ref.range.len());
240        Some(Box::new(once(label)))
241    }
242}
243
244/// Parse errors, possibly with source code
245#[derive(Debug, Error)]
246#[error("Failed to parse")] // todo
247pub struct ParseErrorsWithSource {
248    /// The errors encountered during parsing
249    pub errors: Vec<ParseError>,
250    /// The parsed source code
251    pub source_code: Option<String>,
252    /// The hash of the parsed source
253    pub source_hash: u64,
254}
255
256impl From<ParseError> for ParseErrorsWithSource {
257    fn from(value: ParseError) -> Self {
258        ParseErrorsWithSource {
259            errors: vec![value],
260            source_code: None,
261            source_hash: 0,
262        }
263    }
264}
265
266impl From<Vec<ParseError>> for ParseErrorsWithSource {
267    fn from(value: Vec<ParseError>) -> Self {
268        ParseErrorsWithSource {
269            errors: value,
270            source_code: None,
271            source_hash: 0,
272        }
273    }
274}
275
276impl Diagnostic for ParseErrorsWithSource {
277    fn source_code(&self) -> Option<&dyn SourceCode> {
278        self.source_code
279            .as_ref()
280            .map(|source| source as &dyn SourceCode)
281    }
282
283    fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
284        Some(Box::new(
285            self.errors.iter().map(|e| -> &dyn Diagnostic { e }),
286        ))
287    }
288}
289
290impl SrcReferrer for ParseErrorsWithSource {
291    fn src_ref(&self) -> SrcRef {
292        self.errors[0].src_ref()
293    }
294}