1use crate::{
4 reporting::{Diagnostic, Highlight},
5 source_tracking::fragment::Fragment,
6};
7use std::borrow::Cow;
8
9#[allow(missing_docs)]
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum ParserErrorKind {
15 EncounteredUnknownToken,
16 EncounteredUnterminatedComment,
17 EncounteredUnterminatedString,
18 ExpectedAtomicTypeSignature,
19 ExpectedBooleanLiteral,
20 ExpectedIdentifier,
21 ExpectedImportDeclaration,
22 ExpectedIntegerLiteral,
23 ExpectedPath,
24 ExpectedReferenceTypeSignature,
25 ExpectedTypeSignature,
26 ExpectedWhitespace,
27 ImportMustEndWithSemicolon,
28}
29
30impl ParserErrorKind {
31 pub const fn describe(self) -> &'static str {
33 use ParserErrorKind::*;
34
35 match self {
36 EncounteredUnknownToken => "encountered unknown token",
37 EncounteredUnterminatedComment => {
38 "encountered unterminated multiline comment while parsing"
39 }
40 EncounteredUnterminatedString => {
41 "encountered unterminated string literal while parsing"
42 }
43 ExpectedAtomicTypeSignature => "expected atomic primitive type",
44 ExpectedBooleanLiteral => "expected boolean literal",
45 ExpectedIdentifier => "expected identifier",
46 ExpectedImportDeclaration => "expected import declaration",
47 ExpectedIntegerLiteral => "expected integer literal",
48 ExpectedPath => "expected path or identifier",
49 ExpectedReferenceTypeSignature => "expected reference type signature",
50 ExpectedTypeSignature => "expected type signature",
51 ExpectedWhitespace => "expected whitespace character(s)",
52 ImportMustEndWithSemicolon => "import declarations must end with a semicolon",
53 }
54 }
55
56 pub const fn at(self, f: Fragment) -> ParserError {
58 ParserError {
59 kind: self,
60 location: f,
61 help: Vec::new(),
62 }
63 }
64}
65
66#[derive(Debug)]
70pub struct ParserError {
71 pub kind: ParserErrorKind,
73
74 pub location: Fragment,
76
77 pub help: Vec<Cow<'static, str>>,
79}
80
81impl ParserError {
82 pub fn with_help(mut self, help: impl Into<Cow<'static, str>>) -> Self {
84 self.help.push(help.into());
85 self
86 }
87
88 pub fn as_diagnostic(self) -> Diagnostic {
90 let description = self.kind.describe();
91
92 let message = if self.location.is_empty_at_end_of_source() {
94 Cow::Borrowed("found end of source here")
95 } else {
96 Cow::Borrowed("")
97 };
98
99 let mut diagnostic = Diagnostic::error()
100 .with_message(description)
101 .with_highlights([Highlight::primary(self.location.clone(), message)]);
102
103 if !self.help.is_empty() {
104 diagnostic = diagnostic.with_notes(self.help);
105 }
106
107 diagnostic
108 }
109}