Skip to main content

microcad_lang/parse/
parse_error.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Parser errors
5
6use crate::{parse::*, ty::*};
7use microcad_lang_base::{SrcRef, SrcReferrer};
8use microcad_syntax::ast::LiteralErrorKind;
9use miette::{Diagnostic, SourceCode};
10use thiserror::Error;
11
12/// Parsing errors
13#[derive(Debug, Error, Diagnostic)]
14#[allow(missing_docs)]
15pub enum ParseError {
16    #[error("Error parsing floating point literal: {0}")]
17    ParseFloatError(#[label("{0}")] Refer<std::num::ParseFloatError>),
18
19    #[error("Error parsing integer literal: {0}")]
20    ParseIntError(#[label("{0}")] Refer<std::num::ParseIntError>),
21
22    #[error("Unknown unit: {0}")]
23    UnknownUnit(#[label("Unknown unit")] Refer<String>),
24
25    #[error("Unexpected token")]
26    UnexpectedToken(#[label("Unexpected token")] SrcRef),
27
28    #[error("Missing type or value for definition parameter: {0}")]
29    ParameterMissingTypeOrValue(#[label("Missing type or value")] Identifier),
30
31    #[error("Duplicate argument: {id}")]
32    DuplicateArgument {
33        #[label(primary, "Duplicate argument")]
34        id: Identifier,
35        #[label("Previous declaration")]
36        previous: Identifier,
37    },
38
39    #[error("Duplicate id: {id}")]
40    DuplicateIdentifier {
41        #[label(primary, "Duplicate identifier")]
42        id: Identifier,
43        #[label("Previous declaration")]
44        previous: Identifier,
45    },
46
47    #[error("Duplicate id in tuple: {id}")]
48    DuplicateTupleIdentifier {
49        #[label(primary, "Duplicate identifier")]
50        id: Identifier,
51        #[label("Previous declaration")]
52        previous: Identifier,
53    },
54
55    #[error("Duplicate unnamed type in tuple: {ty}")]
56    DuplicateTupleType {
57        #[label(primary, "Duplicate item")]
58        ty: Refer<Type>,
59        #[label("Previous declaration")]
60        previous: Refer<Type>,
61    },
62
63    #[error("Loading of source file {1:?} failed: {2}")]
64    LoadSource(SrcRef, std::path::PathBuf, std::io::Error),
65
66    /// Grammar rule error
67    #[error("Invalid id '{0}'")]
68    InvalidIdentifier(Refer<String>),
69
70    #[error("Element is not available")]
71    NotAvailable(#[label("Element is not available")] SrcRef),
72
73    #[error("Unknown type: {0}")]
74    UnknownType(#[label("Unknown type")] Refer<String>),
75
76    #[error("If expression must return a value in all cases")]
77    IncompleteIfExpression(#[label("Incomplete if expression")] SrcRef),
78
79    /// Matrix type with invalid dimensions
80    #[error("Invalid matrix type: {0}")]
81    InvalidMatrixType(Refer<String>),
82
83    /// Invalid glob pattern
84    #[error("Invalid glob pattern, wildcard must be at the end of the pattern")]
85    InvalidGlobPattern(SrcRef),
86
87    /// A glob import is given an alias
88    #[error("Glob imports can't be given an alias")]
89    UseGlobAlias(SrcRef),
90
91    /// A parser from the AST builder
92    #[error(transparent)]
93    #[diagnostic(transparent)]
94    AstParser(Refer<microcad_syntax::ParseError>),
95
96    /// An invalid literal was encountered
97    #[error("Invalid literal: {error}")]
98    InvalidLiteral {
99        error: LiteralErrorKind,
100        #[label("{error}")]
101        src_ref: SrcRef,
102    },
103
104    /// An invalid expression was encountered
105    #[error("Invalid expression")]
106    InvalidExpression { src_ref: SrcRef },
107
108    /// An invalid state was encountered
109    #[error("Invalid statement")]
110    InvalidStatement { src_ref: SrcRef },
111
112    /// A type range between non-integer literals
113    #[error("range expressions must be between integers")]
114    InvalidRangeType { src_ref: SrcRef },
115}
116
117/// Result with parse error
118pub type ParseResult<T> = Result<T, ParseError>;
119
120impl SrcReferrer for ParseError {
121    fn src_ref(&self) -> SrcRef {
122        match self {
123            ParseError::ParameterMissingTypeOrValue(id)
124            | ParseError::DuplicateArgument { id, .. }
125            | ParseError::DuplicateIdentifier { id, .. }
126            | ParseError::DuplicateTupleIdentifier { id, .. } => id.src_ref(),
127            ParseError::UnexpectedToken(src_ref)
128            | ParseError::NotAvailable(src_ref)
129            | ParseError::IncompleteIfExpression(src_ref)
130            | ParseError::LoadSource(src_ref, ..)
131            | ParseError::InvalidGlobPattern(src_ref)
132            | ParseError::UseGlobAlias(src_ref)
133            | ParseError::InvalidLiteral { src_ref, .. }
134            | ParseError::InvalidExpression { src_ref }
135            | ParseError::InvalidStatement { src_ref }
136            | ParseError::InvalidRangeType { src_ref } => src_ref.clone(),
137            ParseError::ParseFloatError(parse_float_error) => parse_float_error.src_ref(),
138            ParseError::ParseIntError(parse_int_error) => parse_int_error.src_ref(),
139            ParseError::InvalidIdentifier(id) => id.src_ref(),
140            ParseError::UnknownUnit(unit) => unit.src_ref(),
141            ParseError::DuplicateTupleType { ty, .. } => ty.src_ref(),
142            ParseError::UnknownType(ty) => ty.src_ref(),
143            ParseError::InvalidMatrixType(ty) => ty.src_ref(),
144            ParseError::AstParser(err) => err.src_ref(),
145        }
146    }
147}
148
149impl ParseError {
150    /// Add source code to the error
151    pub fn with_source(self, source: String) -> ParseErrorsWithSource {
152        ParseErrorsWithSource {
153            errors: vec![self],
154            source_code: Some(source),
155            source_hash: 0,
156        }
157    }
158}
159
160/// Parse error, possibly with source code
161#[derive(Debug, Error)]
162#[error("Failed to parse")] // todo
163pub struct ParseErrorsWithSource {
164    /// The errors encountered during parsing
165    pub errors: Vec<ParseError>,
166    /// The parsed source code
167    pub source_code: Option<String>,
168    /// The hash of the parsed source
169    pub source_hash: u64,
170}
171
172impl From<ParseError> for ParseErrorsWithSource {
173    fn from(value: ParseError) -> Self {
174        ParseErrorsWithSource {
175            errors: vec![value],
176            source_code: None,
177            source_hash: 0,
178        }
179    }
180}
181
182impl From<Vec<ParseError>> for ParseErrorsWithSource {
183    fn from(value: Vec<ParseError>) -> Self {
184        ParseErrorsWithSource {
185            errors: value,
186            source_code: None,
187            source_hash: 0,
188        }
189    }
190}
191
192impl Diagnostic for ParseErrorsWithSource {
193    fn source_code(&self) -> Option<&dyn SourceCode> {
194        self.source_code
195            .as_ref()
196            .map(|source| source as &dyn SourceCode)
197    }
198
199    fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
200        Some(Box::new(
201            self.errors.iter().map(|e| -> &dyn Diagnostic { e }),
202        ))
203    }
204}
205
206impl SrcReferrer for ParseErrorsWithSource {
207    fn src_ref(&self) -> SrcRef {
208        self.errors[0].src_ref()
209    }
210}