microcad_lang/parse/
parse_error.rs

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