Skip to main content

trit_core/
parser.rs

1//! 表达式解析器:递归下降,把文本解析为 [`Expr`]。
2//!
3//! 解析错误带行列位置,便于 CLI 给出可读诊断。
4
5use crate::ast::Expr;
6use crate::limits::Limits;
7use crate::value::Ternary;
8
9/// 解析错误类型。
10#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
11pub enum ParseError {
12    /// 表达式意外结束。
13    #[error("表达式在第 {line} 行第 {column} 列意外结束")]
14    UnexpectedEof {
15        /// 行号(1 起)。
16        line: usize,
17        /// 列号(1 起)。
18        column: usize,
19    },
20    /// 遇到无法识别的字符。
21    #[error("第 {line} 行第 {column} 列出现无法识别的字符 {ch:?}")]
22    UnexpectedChar {
23        /// 行号(1 起)。
24        line: usize,
25        /// 列号(1 起)。
26        column: usize,
27        /// 实际字符。
28        ch: char,
29    },
30    /// 括号不匹配。
31    #[error("第 {line} 行第 {column} 列缺少右括号 ')'")]
32    UnclosedParen {
33        /// 行号(1 起)。
34        line: usize,
35        /// 列号(1 起)。
36        column: usize,
37    },
38    /// 标识符非法(空或含非法字符)。
39    #[error("第 {line} 行第 {column} 列的标识符不合法:{name:?}")]
40    InvalidIdentifier {
41        /// 行号(1 起)。
42        line: usize,
43        /// 列号(1 起)。
44        column: usize,
45        /// 标识符原文。
46        name: String,
47    },
48    /// 表达式为空。
49    #[error("表达式为空")]
50    Empty,
51    /// 嵌套过深。
52    #[error("表达式嵌套超过 {limit} 层")]
53    TooDeep {
54        /// 允许的最大嵌套层数。
55        limit: usize,
56    },
57    /// 输入文本过大。
58    #[error("表达式 {bytes} 字节,超过上限 {limit}")]
59    InputTooLarge {
60        /// 实际字节数。
61        bytes: usize,
62        /// 上限。
63        limit: usize,
64    },
65    /// 节点数超限。
66    #[error("表达式节点数超过 {limit}")]
67    TooManyNodes {
68        /// 上限。
69        limit: usize,
70    },
71}
72
73/// 兼容常量:默认深度上限,见 [`Limits::default`]。
74pub const MAX_DEPTH: usize = 256;
75
76/// 输入字节上限:在转成 `Vec<char>` 之前拒绝超长文本。
77pub const MAX_INPUT_BYTES: usize = 1 << 20;
78
79/// 解析得到的表达式与其实际树深度(根为 1)。
80type Parsed = (Expr, usize);
81
82/// 递归下降解析器。
83pub struct Parser {
84    input: Vec<char>,
85    pos: usize,
86    line: usize,
87    column: usize,
88    depth: usize,
89    nodes: usize,
90    limits: Limits,
91}
92
93impl Parser {
94    /// 从字符串创建解析器(默认预算)。
95    pub fn new(input: &str) -> Self {
96        Self::with_limits(input, Limits::default())
97    }
98
99    /// 从字符串创建解析器并指定预算。
100    pub fn with_limits(input: &str, limits: Limits) -> Self {
101        Self {
102            input: input.chars().collect(),
103            pos: 0,
104            line: 1,
105            column: 1,
106            depth: 0,
107            nodes: 0,
108            limits,
109        }
110    }
111
112    /// 解析完整表达式,要求输入被完全消费(默认预算)。
113    pub fn parse(input: &str) -> Result<Expr, ParseError> {
114        Self::parse_with_limits(input, Limits::default())
115    }
116
117    /// 解析完整表达式,要求输入被完全消费并满足给定预算。
118    ///
119    /// 预算在构造 AST 的过程中生效:右结合蕴含链与扁平的二元链同样受限,
120    /// 不会先建成深树再检查。
121    pub fn parse_with_limits(input: &str, limits: Limits) -> Result<Expr, ParseError> {
122        if input.len() > MAX_INPUT_BYTES {
123            return Err(ParseError::InputTooLarge {
124                bytes: input.len(),
125                limit: MAX_INPUT_BYTES,
126            });
127        }
128        let mut p = Parser::with_limits(input, limits);
129        p.skip_ws();
130        if p.peek().is_none() {
131            return Err(ParseError::Empty);
132        }
133        let (expr, _) = p.parse_equiv()?;
134        if let Some(ch) = p.peek() {
135            return Err(ParseError::UnexpectedChar {
136                line: p.line,
137                column: p.column,
138                ch,
139            });
140        }
141        Ok(expr)
142    }
143
144    fn take_node(&mut self) -> Result<(), ParseError> {
145        self.nodes += 1;
146        if self.nodes > self.limits.max_nodes {
147            return Err(ParseError::TooManyNodes {
148                limit: self.limits.max_nodes,
149            });
150        }
151        Ok(())
152    }
153
154    fn check_depth(&self, depth: usize) -> Result<(), ParseError> {
155        if depth > self.limits.max_depth {
156            return Err(ParseError::TooDeep {
157                limit: self.limits.max_depth,
158            });
159        }
160        Ok(())
161    }
162
163    fn leaf(&mut self, expr: Expr) -> Result<Parsed, ParseError> {
164        self.take_node()?;
165        Ok((expr, 1))
166    }
167
168    fn join(
169        &mut self,
170        left: Parsed,
171        right: Parsed,
172        f: impl FnOnce(Expr, Expr) -> Expr,
173    ) -> Result<Parsed, ParseError> {
174        let depth = 1 + left.1.max(right.1);
175        self.check_depth(depth)?;
176        self.take_node()?;
177        Ok((f(left.0, right.0), depth))
178    }
179
180    fn not(&mut self, inner: Parsed) -> Result<Parsed, ParseError> {
181        let depth = 1 + inner.1;
182        self.check_depth(depth)?;
183        self.take_node()?;
184        Ok((Expr::logical_not(inner.0), depth))
185    }
186
187    fn peek(&self) -> Option<char> {
188        self.input.get(self.pos).copied()
189    }
190
191    fn bump(&mut self) -> Option<char> {
192        let ch = self.input.get(self.pos).copied()?;
193        self.pos += 1;
194        if ch == '\n' {
195            self.line += 1;
196            self.column = 1;
197        } else {
198            self.column += 1;
199        }
200        Some(ch)
201    }
202
203    fn skip_ws(&mut self) {
204        while let Some(ch) = self.peek() {
205            if ch.is_whitespace() {
206                self.bump();
207            } else {
208                break;
209            }
210        }
211    }
212
213    /// 尝试匹配一个运算符关键字(不区分 ASCII 空格要求)。
214    fn eat_op(&mut self, ops: &[&str]) -> bool {
215        self.skip_ws();
216        for op in ops {
217            let chars: Vec<char> = op.chars().collect();
218            if self.input.len() >= self.pos + chars.len()
219                && self.input[self.pos..self.pos + chars.len()] == chars[..]
220            {
221                for _ in 0..chars.len() {
222                    self.bump();
223                }
224                return true;
225            }
226        }
227        false
228    }
229
230    fn parse_equiv(&mut self) -> Result<Parsed, ParseError> {
231        let mut left = self.parse_implies()?;
232        loop {
233            self.skip_ws();
234            if self.eat_op(&["<=>", "≡", "=="]) {
235                let right = self.parse_implies()?;
236                left = self.join(left, right, Expr::equiv)?;
237            } else {
238                return Ok(left);
239            }
240        }
241    }
242
243    fn parse_implies(&mut self) -> Result<Parsed, ParseError> {
244        // 右结合:先迭代收集操作数,再从右向左组装,避免右递归绕过深度/节点预算。
245        let mut operands = vec![self.parse_or()?];
246        loop {
247            self.skip_ws();
248            if self.eat_op(&["=>", "→", "⇒"]) {
249                operands.push(self.parse_or()?);
250            } else {
251                break;
252            }
253        }
254        let mut acc = operands.pop().expect("至少有一个操作数");
255        while let Some(left) = operands.pop() {
256            acc = self.join(left, acc, Expr::implies)?;
257        }
258        Ok(acc)
259    }
260
261    fn parse_or(&mut self) -> Result<Parsed, ParseError> {
262        let mut left = self.parse_xor()?;
263        loop {
264            self.skip_ws();
265            if self.eat_op(&["||", "∨", "|"]) {
266                let right = self.parse_xor()?;
267                left = self.join(left, right, Expr::or)?;
268            } else {
269                return Ok(left);
270            }
271        }
272    }
273
274    fn parse_xor(&mut self) -> Result<Parsed, ParseError> {
275        let mut left = self.parse_and()?;
276        loop {
277            self.skip_ws();
278            if self.eat_op(&["⊕", "^"]) {
279                let right = self.parse_and()?;
280                left = self.join(left, right, Expr::xor)?;
281            } else {
282                return Ok(left);
283            }
284        }
285    }
286
287    fn parse_and(&mut self) -> Result<Parsed, ParseError> {
288        let mut left = self.parse_unary()?;
289        loop {
290            self.skip_ws();
291            if self.eat_op(&["&&", "∧", "&"]) {
292                let right = self.parse_unary()?;
293                left = self.join(left, right, Expr::and)?;
294            } else {
295                return Ok(left);
296            }
297        }
298    }
299
300    fn parse_unary(&mut self) -> Result<Parsed, ParseError> {
301        // 该计数保护递归调用本身(`!!!!...` / 深括号),AST 深度另由 join/not 校验。
302        self.depth += 1;
303        if self.depth > self.limits.max_depth {
304            self.depth -= 1;
305            return Err(ParseError::TooDeep {
306                limit: self.limits.max_depth,
307            });
308        }
309        let result = self.parse_unary_inner();
310        self.depth -= 1;
311        result
312    }
313
314    fn parse_unary_inner(&mut self) -> Result<Parsed, ParseError> {
315        self.skip_ws();
316        if self.eat_op(&["!", "~", "¬"]) {
317            let inner = self.parse_unary()?;
318            return self.not(inner);
319        }
320        self.parse_primary()
321    }
322
323    fn parse_primary(&mut self) -> Result<Parsed, ParseError> {
324        self.skip_ws();
325        let Some(ch) = self.peek() else {
326            return Err(ParseError::UnexpectedEof {
327                line: self.line,
328                column: self.column,
329            });
330        };
331
332        if ch == '(' {
333            self.bump();
334            let inner = self.parse_equiv()?;
335            self.skip_ws();
336            if self.peek() != Some(')') {
337                return Err(ParseError::UnclosedParen {
338                    line: self.line,
339                    column: self.column,
340                });
341            }
342            self.bump();
343            return Ok(inner);
344        }
345
346        if ch == ')' {
347            return Err(ParseError::UnexpectedChar {
348                line: self.line,
349                column: self.column,
350                ch,
351            });
352        }
353
354        if is_ident_start(ch) {
355            let start_line = self.line;
356            let start_col = self.column;
357            let mut name = String::new();
358            while let Some(c) = self.peek() {
359                if is_ident_char(c) {
360                    name.push(c);
361                    self.bump();
362                } else {
363                    break;
364                }
365            }
366            if name.is_empty() {
367                return Err(ParseError::InvalidIdentifier {
368                    line: start_line,
369                    column: start_col,
370                    name,
371                });
372            }
373            return self.leaf(match name.as_str() {
374                "0" | "False" | "false" | "FALSE" => Expr::Const(Ternary::False),
375                "1" | "True" | "true" | "TRUE" => Expr::Const(Ternary::True),
376                "X" | "x" | "?" | "Unknown" | "unknown" => Expr::Const(Ternary::Unknown),
377                _ => Expr::Var(name),
378            });
379        }
380
381        if let Some(v) = Ternary::parse_char(ch) {
382            self.bump();
383            return self.leaf(Expr::Const(v));
384        }
385
386        Err(ParseError::UnexpectedChar {
387            line: self.line,
388            column: self.column,
389            ch,
390        })
391    }
392}
393
394/// 标识符首字符:字母或下划线。
395fn is_ident_start(c: char) -> bool {
396    c.is_alphabetic() || c == '_'
397}
398
399/// 标识符后续字符:字母、数字或下划线。
400fn is_ident_char(c: char) -> bool {
401    c.is_alphanumeric() || c == '_'
402}
403
404#[cfg(test)]
405mod tests {
406    use super::*;
407
408    #[test]
409    fn parses_variables() {
410        assert_eq!(Parser::parse("a").unwrap(), Expr::Var("a".into()));
411        assert_eq!(Parser::parse("a_1").unwrap(), Expr::Var("a_1".into()));
412    }
413
414    #[test]
415    fn parses_constants() {
416        assert_eq!(Parser::parse("0").unwrap(), Expr::Const(Ternary::False));
417        assert_eq!(Parser::parse("1").unwrap(), Expr::Const(Ternary::True));
418        assert_eq!(Parser::parse("X").unwrap(), Expr::Const(Ternary::Unknown));
419        assert_eq!(Parser::parse("?").unwrap(), Expr::Const(Ternary::Unknown));
420    }
421
422    #[test]
423    fn operator_precedence() {
424        // a ∧ b ∨ c  =>  (a ∧ b) ∨ c
425        let e = Parser::parse("a & b | c").unwrap();
426        assert_eq!(
427            e,
428            Expr::or(
429                Expr::and(Expr::Var("a".into()), Expr::Var("b".into())),
430                Expr::Var("c".into())
431            )
432        );
433    }
434
435    #[test]
436    fn not_binds_tighter_than_and() {
437        let e = Parser::parse("!a & b").unwrap();
438        assert_eq!(
439            e,
440            Expr::and(
441                Expr::logical_not(Expr::Var("a".into())),
442                Expr::Var("b".into())
443            )
444        );
445    }
446
447    #[test]
448    fn implies_is_right_associative() {
449        let e = Parser::parse("a => b => c").unwrap();
450        assert_eq!(
451            e,
452            Expr::implies(
453                Expr::Var("a".into()),
454                Expr::implies(Expr::Var("b".into()), Expr::Var("c".into()))
455            )
456        );
457    }
458
459    #[test]
460    fn parentheses_override_precedence() {
461        let e = Parser::parse("(a | b) & c").unwrap();
462        assert_eq!(
463            e,
464            Expr::and(
465                Expr::or(Expr::Var("a".into()), Expr::Var("b".into())),
466                Expr::Var("c".into())
467            )
468        );
469    }
470
471    #[test]
472    fn unicode_operators() {
473        let e = Parser::parse("a ∧ b ∨ c").unwrap();
474        assert_eq!(
475            e,
476            Expr::or(
477                Expr::and(Expr::Var("a".into()), Expr::Var("b".into())),
478                Expr::Var("c".into())
479            )
480        );
481        let e2 = Parser::parse("a → b").unwrap();
482        assert!(matches!(e2, Expr::Implies(_, _)));
483    }
484
485    #[test]
486    fn reports_unexpected_char() {
487        let err = Parser::parse("a & @").unwrap_err();
488        assert!(matches!(err, ParseError::UnexpectedChar { .. }));
489    }
490
491    #[test]
492    fn reports_unclosed_paren() {
493        let err = Parser::parse("(a & b").unwrap_err();
494        assert!(matches!(err, ParseError::UnclosedParen { .. }));
495    }
496
497    #[test]
498    fn reports_empty() {
499        assert_eq!(Parser::parse("   ").unwrap_err(), ParseError::Empty);
500    }
501
502    #[test]
503    fn rejects_excessive_nesting() {
504        let deep = format!("{}a", "!".repeat(MAX_DEPTH + 1));
505        assert!(matches!(
506            Parser::parse(&deep).unwrap_err(),
507            ParseError::TooDeep { .. }
508        ));
509        let ok = format!("{}a", "!".repeat(MAX_DEPTH - 1));
510        assert!(Parser::parse(&ok).is_ok());
511    }
512
513    #[test]
514    fn rejects_long_flat_binary_chain() {
515        let src = std::iter::repeat_n("a", MAX_DEPTH + 2)
516            .collect::<Vec<_>>()
517            .join(" & ");
518        assert!(matches!(
519            Parser::parse(&src).unwrap_err(),
520            ParseError::TooDeep { .. }
521        ));
522    }
523
524    #[test]
525    fn rejects_long_right_associative_implication_chain() {
526        let src = std::iter::repeat_n("a", MAX_DEPTH + 2)
527            .collect::<Vec<_>>()
528            .join(" => ");
529        assert!(matches!(
530            Parser::parse(&src).unwrap_err(),
531            ParseError::TooDeep { .. }
532        ));
533    }
534
535    #[test]
536    fn node_budget_is_enforced_while_parsing() {
537        let limits = Limits {
538            max_nodes: 4,
539            ..Limits::default()
540        };
541        let err = Parser::parse_with_limits("a & b & c & d", limits).unwrap_err();
542        assert_eq!(err, ParseError::TooManyNodes { limit: 4 });
543    }
544
545    #[test]
546    fn compat_depth_constant_matches_default_limits() {
547        assert_eq!(MAX_DEPTH, Limits::default().max_depth);
548    }
549
550    #[test]
551    fn oversized_input_is_rejected_before_collecting_chars() {
552        let src = "a".repeat(MAX_INPUT_BYTES + 1);
553        assert!(matches!(
554            Parser::parse(&src).unwrap_err(),
555            ParseError::InputTooLarge { .. }
556        ));
557    }
558
559    #[test]
560    fn deep_parentheses_are_bounded_by_unary_depth() {
561        let src = format!(
562            "{}a{}",
563            "(".repeat(MAX_DEPTH + 1),
564            ")".repeat(MAX_DEPTH + 1)
565        );
566        assert!(matches!(
567            Parser::parse(&src).unwrap_err(),
568            ParseError::TooDeep { .. }
569        ));
570    }
571}