1use std::path::{Path, PathBuf};
2
3use thiserror::Error;
4
5use super::pos::Position;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum ErrorKind {
9 Eof,
10 NullCharacter,
11 UnescapedSpecialCharacter(char),
12 UnterminatedSingleQuotedString,
13 UnterminatedDoubleQuotedString,
14 UnsupportedShellParameter(String),
15 UnterminatedExpansion,
16 UnsupportedCommandExpansion,
17 UnsupportedCommandOrArithmeticExpansion,
18 InvalidCharacter(char),
19}
20
21impl std::fmt::Display for ErrorKind {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 Self::Eof => write!(f, "Unexpected end of input"),
25 Self::NullCharacter => write!(f, "Unexpected <NUL> character"),
26 Self::UnescapedSpecialCharacter(ch) => {
27 write!(f, "Unescaped special shell character '{ch}'")
28 }
29 Self::UnterminatedSingleQuotedString => {
30 write!(f, "Unterminated single-quoted string")
31 }
32 Self::UnterminatedDoubleQuotedString => {
33 write!(f, "Unterminated double-quoted string")
34 }
35 Self::UnsupportedShellParameter(p) => {
36 write!(f, "Unsupported special shell parameter: {p}")
37 }
38 Self::UnterminatedExpansion => write!(f, "Unterminated expansion"),
39 Self::UnsupportedCommandExpansion => write!(f, "Unsupported command expansion"),
40 Self::UnsupportedCommandOrArithmeticExpansion => {
41 write!(f, "Unsupported command or arithmetic expansion")
42 }
43 Self::InvalidCharacter(ch) => write!(f, "Invalid character '{ch}'"),
44 }
45 }
46}
47
48#[derive(Error, Debug, PartialEq, Eq)]
49pub struct SyntaxError {
50 kind: ErrorKind,
51 position: Position,
52 filename: Option<PathBuf>,
53}
54
55impl std::fmt::Display for SyntaxError {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 self.kind().fmt(f)?;
58 if let Some(file) = self.file() {
59 write!(f, " in {}", file.display())?;
60 }
61 write!(f, " on line {}, column {}", self.line(), self.column())
62 }
63}
64
65impl SyntaxError {
66 pub fn new(kind: ErrorKind, position: Position, filename: Option<PathBuf>) -> Self {
67 Self {
68 kind,
69 position,
70 filename,
71 }
72 }
73
74 pub fn kind(&self) -> &ErrorKind {
75 &self.kind
76 }
77
78 pub fn line(&self) -> usize {
79 self.position.line
80 }
81
82 pub fn column(&self) -> usize {
83 self.position.column
84 }
85
86 pub fn file(&self) -> Option<&Path> {
87 self.filename.as_deref()
88 }
89}