Skip to main content

trit_core/
parser.rs

1//! 表达式解析器:递归下降,把文本解析为 [`Expr`]。
2//!
3//! 解析错误带行列位置,便于 CLI 给出可读诊断。
4
5use crate::ast::Expr;
6use crate::value::Ternary;
7
8/// 解析错误类型。
9#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
10pub enum ParseError {
11    /// 表达式意外结束。
12    #[error("表达式在第 {line} 行第 {column} 列意外结束")]
13    UnexpectedEof {
14        /// 行号(1 起)。
15        line: usize,
16        /// 列号(1 起)。
17        column: usize,
18    },
19    /// 遇到无法识别的字符。
20    #[error("第 {line} 行第 {column} 列出现无法识别的字符 {ch:?}")]
21    UnexpectedChar {
22        /// 行号(1 起)。
23        line: usize,
24        /// 列号(1 起)。
25        column: usize,
26        /// 实际字符。
27        ch: char,
28    },
29    /// 括号不匹配。
30    #[error("第 {line} 行第 {column} 列缺少右括号 ')'")]
31    UnclosedParen {
32        /// 行号(1 起)。
33        line: usize,
34        /// 列号(1 起)。
35        column: usize,
36    },
37    /// 标识符非法(空或含非法字符)。
38    #[error("第 {line} 行第 {column} 列的标识符不合法:{name:?}")]
39    InvalidIdentifier {
40        /// 行号(1 起)。
41        line: usize,
42        /// 列号(1 起)。
43        column: usize,
44        /// 标识符原文。
45        name: String,
46    },
47    /// 表达式为空。
48    #[error("表达式为空")]
49    Empty,
50}
51
52/// 递归下降解析器。
53pub struct Parser {
54    input: Vec<char>,
55    pos: usize,
56    line: usize,
57    column: usize,
58}
59
60impl Parser {
61    /// 从字符串创建解析器。
62    pub fn new(input: &str) -> Self {
63        Self {
64            input: input.chars().collect(),
65            pos: 0,
66            line: 1,
67            column: 1,
68        }
69    }
70
71    /// 解析完整表达式,要求输入被完全消费。
72    pub fn parse(input: &str) -> Result<Expr, ParseError> {
73        let mut p = Parser::new(input);
74        p.skip_ws();
75        if p.peek().is_none() {
76            return Err(ParseError::Empty);
77        }
78        let expr = p.parse_equiv()?;
79        if let Some(ch) = p.peek() {
80            return Err(ParseError::UnexpectedChar {
81                line: p.line,
82                column: p.column,
83                ch,
84            });
85        }
86        Ok(expr)
87    }
88
89    fn peek(&self) -> Option<char> {
90        self.input.get(self.pos).copied()
91    }
92
93    fn bump(&mut self) -> Option<char> {
94        let ch = self.input.get(self.pos).copied()?;
95        self.pos += 1;
96        if ch == '\n' {
97            self.line += 1;
98            self.column = 1;
99        } else {
100            self.column += 1;
101        }
102        Some(ch)
103    }
104
105    fn skip_ws(&mut self) {
106        while let Some(ch) = self.peek() {
107            if ch.is_whitespace() {
108                self.bump();
109            } else {
110                break;
111            }
112        }
113    }
114
115    /// 尝试匹配一个运算符关键字(不区分 ASCII 空格要求)。
116    fn eat_op(&mut self, ops: &[&str]) -> bool {
117        self.skip_ws();
118        for op in ops {
119            let chars: Vec<char> = op.chars().collect();
120            if self.input.len() >= self.pos + chars.len()
121                && self.input[self.pos..self.pos + chars.len()] == chars[..]
122            {
123                for _ in 0..chars.len() {
124                    self.bump();
125                }
126                return true;
127            }
128        }
129        false
130    }
131
132    fn parse_equiv(&mut self) -> Result<Expr, ParseError> {
133        let mut left = self.parse_implies()?;
134        loop {
135            self.skip_ws();
136            if self.eat_op(&["<=>", "≡", "=="]) {
137                let right = self.parse_implies()?;
138                left = Expr::equiv(left, right);
139            } else {
140                return Ok(left);
141            }
142        }
143    }
144
145    fn parse_implies(&mut self) -> Result<Expr, ParseError> {
146        let left = self.parse_or()?;
147        self.skip_ws();
148        if self.eat_op(&["=>", "→", "⇒"]) {
149            // 右结合
150            let right = self.parse_implies()?;
151            return Ok(Expr::implies(left, right));
152        }
153        Ok(left)
154    }
155
156    fn parse_or(&mut self) -> Result<Expr, ParseError> {
157        let mut left = self.parse_xor()?;
158        loop {
159            self.skip_ws();
160            if self.eat_op(&["||", "∨", "|"]) {
161                let right = self.parse_xor()?;
162                left = Expr::or(left, right);
163            } else {
164                return Ok(left);
165            }
166        }
167    }
168
169    fn parse_xor(&mut self) -> Result<Expr, ParseError> {
170        let mut left = self.parse_and()?;
171        loop {
172            self.skip_ws();
173            if self.eat_op(&["⊕", "^"]) {
174                let right = self.parse_and()?;
175                left = Expr::xor(left, right);
176            } else {
177                return Ok(left);
178            }
179        }
180    }
181
182    fn parse_and(&mut self) -> Result<Expr, ParseError> {
183        let mut left = self.parse_unary()?;
184        loop {
185            self.skip_ws();
186            if self.eat_op(&["&&", "∧", "&"]) {
187                let right = self.parse_unary()?;
188                left = Expr::and(left, right);
189            } else {
190                return Ok(left);
191            }
192        }
193    }
194
195    fn parse_unary(&mut self) -> Result<Expr, ParseError> {
196        self.skip_ws();
197        if self.eat_op(&["!", "~", "¬"]) {
198            let inner = self.parse_unary()?;
199            return Ok(Expr::logical_not(inner));
200        }
201        self.parse_primary()
202    }
203
204    fn parse_primary(&mut self) -> Result<Expr, ParseError> {
205        self.skip_ws();
206        let Some(ch) = self.peek() else {
207            return Err(ParseError::UnexpectedEof {
208                line: self.line,
209                column: self.column,
210            });
211        };
212
213        if ch == '(' {
214            self.bump();
215            let inner = self.parse_equiv()?;
216            self.skip_ws();
217            if self.peek() != Some(')') {
218                return Err(ParseError::UnclosedParen {
219                    line: self.line,
220                    column: self.column,
221                });
222            }
223            self.bump();
224            return Ok(inner);
225        }
226
227        if ch == ')' {
228            return Err(ParseError::UnexpectedChar {
229                line: self.line,
230                column: self.column,
231                ch,
232            });
233        }
234
235        if is_ident_start(ch) {
236            let start_line = self.line;
237            let start_col = self.column;
238            let mut name = String::new();
239            while let Some(c) = self.peek() {
240                if is_ident_char(c) {
241                    name.push(c);
242                    self.bump();
243                } else {
244                    break;
245                }
246            }
247            if name.is_empty() {
248                return Err(ParseError::InvalidIdentifier {
249                    line: start_line,
250                    column: start_col,
251                    name,
252                });
253            }
254            return Ok(match name.as_str() {
255                "0" | "False" | "false" | "FALSE" => Expr::Const(Ternary::False),
256                "1" | "True" | "true" | "TRUE" => Expr::Const(Ternary::True),
257                "X" | "x" | "?" | "Unknown" | "unknown" => Expr::Const(Ternary::Unknown),
258                _ => Expr::Var(name),
259            });
260        }
261
262        if let Some(v) = Ternary::parse_char(ch) {
263            self.bump();
264            return Ok(Expr::Const(v));
265        }
266
267        Err(ParseError::UnexpectedChar {
268            line: self.line,
269            column: self.column,
270            ch,
271        })
272    }
273}
274
275/// 标识符首字符:字母或下划线。
276fn is_ident_start(c: char) -> bool {
277    c.is_alphabetic() || c == '_'
278}
279
280/// 标识符后续字符:字母、数字或下划线。
281fn is_ident_char(c: char) -> bool {
282    c.is_alphanumeric() || c == '_'
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn parses_variables() {
291        assert_eq!(Parser::parse("a").unwrap(), Expr::Var("a".into()));
292        assert_eq!(Parser::parse("a_1").unwrap(), Expr::Var("a_1".into()));
293    }
294
295    #[test]
296    fn parses_constants() {
297        assert_eq!(Parser::parse("0").unwrap(), Expr::Const(Ternary::False));
298        assert_eq!(Parser::parse("1").unwrap(), Expr::Const(Ternary::True));
299        assert_eq!(Parser::parse("X").unwrap(), Expr::Const(Ternary::Unknown));
300        assert_eq!(Parser::parse("?").unwrap(), Expr::Const(Ternary::Unknown));
301    }
302
303    #[test]
304    fn operator_precedence() {
305        // a ∧ b ∨ c  =>  (a ∧ b) ∨ c
306        let e = Parser::parse("a & b | c").unwrap();
307        assert_eq!(
308            e,
309            Expr::or(
310                Expr::and(Expr::Var("a".into()), Expr::Var("b".into())),
311                Expr::Var("c".into())
312            )
313        );
314    }
315
316    #[test]
317    fn not_binds_tighter_than_and() {
318        let e = Parser::parse("!a & b").unwrap();
319        assert_eq!(
320            e,
321            Expr::and(
322                Expr::logical_not(Expr::Var("a".into())),
323                Expr::Var("b".into())
324            )
325        );
326    }
327
328    #[test]
329    fn implies_is_right_associative() {
330        let e = Parser::parse("a => b => c").unwrap();
331        assert_eq!(
332            e,
333            Expr::implies(
334                Expr::Var("a".into()),
335                Expr::implies(Expr::Var("b".into()), Expr::Var("c".into()))
336            )
337        );
338    }
339
340    #[test]
341    fn parentheses_override_precedence() {
342        let e = Parser::parse("(a | b) & c").unwrap();
343        assert_eq!(
344            e,
345            Expr::and(
346                Expr::or(Expr::Var("a".into()), Expr::Var("b".into())),
347                Expr::Var("c".into())
348            )
349        );
350    }
351
352    #[test]
353    fn unicode_operators() {
354        let e = Parser::parse("a ∧ b ∨ c").unwrap();
355        assert_eq!(
356            e,
357            Expr::or(
358                Expr::and(Expr::Var("a".into()), Expr::Var("b".into())),
359                Expr::Var("c".into())
360            )
361        );
362        let e2 = Parser::parse("a → b").unwrap();
363        assert!(matches!(e2, Expr::Implies(_, _)));
364    }
365
366    #[test]
367    fn reports_unexpected_char() {
368        let err = Parser::parse("a & @").unwrap_err();
369        assert!(matches!(err, ParseError::UnexpectedChar { .. }));
370    }
371
372    #[test]
373    fn reports_unclosed_paren() {
374        let err = Parser::parse("(a & b").unwrap_err();
375        assert!(matches!(err, ParseError::UnclosedParen { .. }));
376    }
377
378    #[test]
379    fn reports_empty() {
380        assert_eq!(Parser::parse("   ").unwrap_err(), ParseError::Empty);
381    }
382}