libslide/parser/
expression_pattern_parser.rs

1use super::{extra_tokens_diag, Parser};
2use crate::common::Span;
3use crate::diagnostics::Diagnostic;
4use crate::grammar::*;
5use crate::scanner::types::Token;
6use crate::utils::{hash, PeekIter};
7
8use std::collections::HashMap;
9use std::rc::Rc;
10
11pub fn parse(input: Vec<Token>) -> (Rc<ExprPat>, Vec<Diagnostic>) {
12    let mut parser = ExpressionPatternParser::new(input);
13    (parser.parse(), parser.diagnostics)
14}
15
16pub struct ExpressionPatternParser {
17    _input: PeekIter<Token>,
18    diagnostics: Vec<Diagnostic>,
19    // We use an untyped hash here because we don't want to clone an Expr into the map in case it's
20    // already there when using an entry API.
21    // TODO: replace with Expr when raw_entry API is stabilized (see rust#56167)
22    seen: HashMap<u64, Rc<ExprPat>>,
23}
24
25impl Parser<Rc<ExprPat>> for ExpressionPatternParser {
26    type Expr = ExprPat;
27
28    fn new(input: Vec<Token>) -> Self {
29        Self {
30            _input: PeekIter::new(input.into_iter()),
31            diagnostics: vec![],
32            seen: HashMap::new(),
33        }
34    }
35
36    fn input(&mut self) -> &mut PeekIter<Token> {
37        &mut self._input
38    }
39
40    fn push_diag(&mut self, diagnostic: Diagnostic) {
41        self.diagnostics.push(diagnostic);
42    }
43
44    fn parse(&mut self) -> Rc<ExprPat> {
45        let parsed = self.expr();
46        if !self.done() {
47            let extra_tokens_diag = extra_tokens_diag(self.input());
48            self.push_diag(extra_tokens_diag);
49        }
50        parsed
51    }
52
53    fn parse_float(&mut self, f: f64, _span: Span) -> Self::Expr {
54        Self::Expr::Const(f)
55    }
56
57    fn parse_variable(&mut self, name: String, span: Span) -> Self::Expr {
58        self.push_diag(
59            Diagnostic::span_err(
60                span,
61                "Variables cannot be used in an expression pattern",
62                Some("unexpected variable".into()),
63            )
64            .with_help(format!(
65                r##"consider using "${name}", "#{name}", or "_{name}" as a pattern"##,
66                name = name,
67            )),
68        );
69        Self::Expr::VarPat(name)
70    }
71
72    fn parse_var_pattern(&mut self, name: String, _span: Span) -> Self::Expr {
73        Self::Expr::VarPat(name)
74    }
75
76    fn parse_const_pattern(&mut self, name: String, _span: Span) -> Self::Expr {
77        Self::Expr::ConstPat(name)
78    }
79
80    fn parse_any_pattern(&mut self, name: String, _span: Span) -> Self::Expr {
81        Self::Expr::AnyPat(name)
82    }
83
84    fn finish_expr(&mut self, expr: Self::Expr) -> Rc<Self::Expr> {
85        let p = self
86            .seen
87            .entry(hash(&expr))
88            .or_insert_with(|| Rc::new(expr));
89        Rc::clone(p)
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::scan;
97
98    parser_tests! {
99        parse_expression_pattern
100
101        pattern:                 "$a"
102        pattern_in_op_left:      "$a + 1"
103        pattern_in_op_right:     "1 + $a"
104    }
105
106    #[test]
107    fn common_subexpression_elimination() {
108        let program = "$v * #c + $v * #c";
109        let tokens = scan(program).tokens;
110        let (parsed, _) = parse(tokens);
111        let (l, r) = match (*parsed).clone() {
112            ExprPat::BinaryExpr(BinaryExpr { lhs, rhs, .. }) => (lhs, rhs),
113            _ => unreachable!(),
114        };
115        assert!(std::ptr::eq(l.as_ref(), r.as_ref())); // $v * #c
116
117        let (ll, lr, rl, rr) = match (l.as_ref(), r.as_ref()) {
118            (
119                ExprPat::BinaryExpr(BinaryExpr {
120                    lhs: ll, rhs: lr, ..
121                }),
122                ExprPat::BinaryExpr(BinaryExpr {
123                    lhs: rl, rhs: rr, ..
124                }),
125            ) => (ll, lr, rl, rr),
126            _ => unreachable!(),
127        };
128        assert!(std::ptr::eq(ll.as_ref(), rl.as_ref())); // 1
129        assert!(std::ptr::eq(lr.as_ref(), rr.as_ref())); // 2
130    }
131}