Skip to main content

oni_comb_parser/
error.rs

1use alloc::vec;
2use alloc::vec::Vec;
3use core::fmt;
4
5/// `or` で左右の Backtrack エラーを合成するトレイト。
6pub trait MergeError: Sized {
7  fn merge(self, other: Self) -> Self;
8}
9
10/// `.context()` でコンテキストラベルを積むトレイト。
11pub trait ContextError: Sized {
12  fn add_context(self, context: &'static str) -> Self;
13}
14
15/// パース失敗時の期待トークン。
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum Expected {
18  /// 特定の文字を期待
19  Char(char),
20  /// 特定の文字列タグを期待
21  Tag(&'static str),
22  /// 特定のバイトを期待(将来の bytes 対応用)
23  Byte(u8),
24  /// 特定のバイト列タグを期待(将来の bytes 対応用)
25  ByteTag(&'static [u8]),
26  /// 説明的な期待("digit", "identifier" 等)
27  Description(&'static str),
28  /// 入力の終端を期待
29  Eof,
30}
31
32/// 構造化パースエラー。位置・期待トークン・コンテキストを保持する。
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct ParseError {
35  /// 失敗した byte offset
36  pub position: usize,
37  /// 期待していたトークンの集合
38  pub expected: Vec<Expected>,
39  /// コンテキストスタック(外側から内側の順)
40  pub context: Vec<&'static str>,
41}
42
43impl ParseError {
44  pub fn new(position: usize, expected: Expected) -> Self {
45    ParseError {
46      position,
47      expected: vec![expected],
48      context: Vec::new(),
49    }
50  }
51
52  pub fn expected_char(position: usize, c: char) -> Self {
53    Self::new(position, Expected::Char(c))
54  }
55
56  pub fn expected_tag(position: usize, tag: &'static str) -> Self {
57    Self::new(position, Expected::Tag(tag))
58  }
59
60  pub fn expected_description(position: usize, desc: &'static str) -> Self {
61    Self::new(position, Expected::Description(desc))
62  }
63
64  pub fn expected_eof(position: usize) -> Self {
65    Self::new(position, Expected::Eof)
66  }
67}
68
69impl MergeError for ParseError {
70  fn merge(mut self, other: Self) -> Self {
71    use core::cmp::Ordering;
72    match self.position.cmp(&other.position) {
73      Ordering::Greater => self,
74      Ordering::Less => other,
75      Ordering::Equal => {
76        for e in other.expected {
77          if !self.expected.contains(&e) {
78            self.expected.push(e);
79          }
80        }
81        self
82      }
83    }
84  }
85}
86
87impl ContextError for ParseError {
88  fn add_context(mut self, context: &'static str) -> Self {
89    self.context.push(context);
90    self
91  }
92}
93
94impl fmt::Display for ParseError {
95  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96    write!(f, "parse error at position {}", self.position)?;
97
98    if !self.expected.is_empty() {
99      write!(f, ": expected ")?;
100      for (i, e) in self.expected.iter().enumerate() {
101        if i > 0 {
102          write!(f, " or ")?;
103        }
104        match e {
105          Expected::Char(c) => write!(f, "'{}'", c)?,
106          Expected::Tag(s) => write!(f, "\"{}\"", s)?,
107          Expected::Byte(b) => write!(f, "0x{:02X}", b)?,
108          Expected::ByteTag(bs) => write!(f, "{:?}", bs)?,
109          Expected::Description(d) => write!(f, "{}", d)?,
110          Expected::Eof => write!(f, "end of input")?,
111        }
112      }
113    }
114
115    if !self.context.is_empty() {
116      write!(f, " in ")?;
117      for (i, ctx) in self.context.iter().rev().enumerate() {
118        if i > 0 {
119          write!(f, " > ")?;
120        }
121        write!(f, "{}", ctx)?;
122      }
123    }
124
125    Ok(())
126  }
127}