1use crate::Token;
2use crate::tokenizer;
3
4#[derive(thiserror::Error, Debug)]
6pub enum ParseError {
7 #[error("syntax error near token `{}' (line {} col {})", .0.to_str(), .0.location().start.line, .0.location().start.column)]
9 ParsingNearToken(Token),
10
11 #[error("syntax error at end of input")]
13 ParsingAtEndOfInput,
14
15 #[error("{} (detected near {})", .inner, .position.as_ref().map_or_else(|| String::from("<unknown position>"), |p| std::format!("line {} col {}", p.line, p.column)))]
17 Tokenizing {
18 inner: tokenizer::TokenizerError,
20 position: Option<tokenizer::SourcePosition>,
22 },
23}
24
25#[cfg(feature = "diagnostics")]
26#[allow(clippy::cast_sign_loss)]
27#[allow(unused)] pub mod miette {
29 use super::ParseError;
30 use miette::SourceOffset;
31
32 impl ParseError {
33 pub fn to_pretty_error(self, input: impl Into<String>) -> PrettyError {
35 let input = input.into();
36 let location = match self {
37 Self::ParsingNearToken(ref token) => Some(SourceOffset::from_location(
38 &input,
39 token.location().start.line,
40 token.location().start.column,
41 )),
42 Self::Tokenizing { ref position, .. } => position
43 .as_ref()
44 .map(|p| SourceOffset::from_location(&input, p.line, p.column)),
45 Self::ParsingAtEndOfInput => {
46 Some(SourceOffset::from_location(&input, usize::MAX, usize::MAX))
47 }
48 };
49
50 PrettyError {
51 cause: self,
52 input,
53 location,
54 }
55 }
56 }
57
58 #[derive(thiserror::Error, Debug, miette::Diagnostic)]
60 #[error("Cannot parse the input script")]
61 pub struct PrettyError {
62 cause: ParseError,
63 #[source_code]
64 input: String,
65 #[label("{cause}")]
66 location: Option<SourceOffset>,
67 }
68}
69
70#[derive(Debug, thiserror::Error)]
72#[error(transparent)]
73pub struct ParseErrorLocation {
74 #[from]
75 inner: peg::error::ParseError<peg::str::LineCol>,
76}
77
78#[derive(Debug, thiserror::Error)]
80pub enum WordParseError {
81 #[error("failed to parse arithmetic expression")]
83 ArithmeticExpression(ParseErrorLocation),
84
85 #[error("failed to parse pattern")]
87 Pattern(ParseErrorLocation),
88
89 #[error("failed to parse prompt string")]
91 Prompt(ParseErrorLocation),
92
93 #[error("failed to parse parameter '{0}'")]
95 Parameter(String, ParseErrorLocation),
96
97 #[error("failed to parse for brace expansion: '{0}'")]
99 BraceExpansion(String, ParseErrorLocation),
100
101 #[error("failed to parse word '{0}'")]
103 Word(String, ParseErrorLocation),
104}
105
106#[derive(Debug, thiserror::Error)]
108#[error(transparent)]
109pub struct TestCommandParseError(#[from] peg::error::ParseError<usize>);
110
111#[derive(Debug, thiserror::Error)]
113pub enum BindingParseError {
114 #[error("unknown error while parsing key-binding: '{0}'")]
116 Unknown(String),
117
118 #[error("missing key code in key-binding")]
120 MissingKeyCode,
121}
122
123pub(crate) fn convert_peg_parse_error(
124 err: &peg::error::ParseError<usize>,
125 tokens: &[Token],
126) -> ParseError {
127 let approx_token_index = err.location;
128
129 if approx_token_index < tokens.len() {
130 ParseError::ParsingNearToken(tokens[approx_token_index].clone())
131 } else {
132 ParseError::ParsingAtEndOfInput
133 }
134}