Skip to main content

parser/
pattern.rs

1use anyhow::{Result, anyhow};
2use dynamic::{Dynamic, Type};
3use smol_str::SmolStr;
4
5use super::{BinaryOp, Parser, Span, expr::Expr, expr::ExprKind};
6
7#[derive(Debug, Clone)]
8pub struct Pattern {
9    pub kind: PatternKind,
10    pub span: Span,
11}
12
13#[derive(Debug, Clone)]
14pub enum PatternKind {
15    Wildcard,
16    Var { idx: u32, ty: Type },
17    Ident { name: SmolStr, ty: Type },
18    Literal(Dynamic),
19    Tuple(Vec<Pattern>),
20    List { elems: Vec<Pattern>, has_rest: bool },
21    Struct { name: SmolStr, fields: Vec<(SmolStr, Option<Pattern>)> },
22    Member(Box<Pattern>, SmolStr),
23    Idx(Box<Pattern>, Expr),
24}
25
26impl Pattern {
27    pub fn new(kind: PatternKind, span: Span) -> Self {
28        Self { kind, span }
29    }
30
31    pub fn var(&self) -> Option<u32> {
32        if let PatternKind::Var { idx, .. } = &self.kind { Some(*idx) } else { None }
33    }
34
35    pub fn expr(&self) -> Result<Expr> {
36        if let PatternKind::Var { idx, .. } = &self.kind { Ok(Expr::new(super::expr::ExprKind::Var(*idx), self.span)) } else { Err(anyhow!("不是变量")) }
37    }
38
39    /// 生成"scrut 是否匹配 pat"的布尔表达式。
40    /// `scrut` 必须是个无副作用、可重复求值的表达式(实际使用时一般是
41    /// 临时变量 `__m_scrut`)。
42    pub fn match_test(&self, scrut: &Expr) -> Expr {
43        let span = self.span;
44        match &self.kind {
45            PatternKind::Wildcard | PatternKind::Var { .. } | PatternKind::Ident { .. } => Expr::new(ExprKind::Value(Dynamic::Bool(true)), span),
46            PatternKind::Literal(v) => bin(scrut.clone(), BinaryOp::Eq, ExprKind::Value(v.clone()).at(span), span),
47            PatternKind::Tuple(items) => {
48                let mut tests = Vec::new();
49                tests.push(bin(method_call(scrut.clone(), "len", Vec::new(), span), BinaryOp::Eq, ExprKind::Value(Dynamic::I32(items.len() as i32)).at(span), span));
50                for (i, p) in items.iter().enumerate() {
51                    let elem = idx_expr(scrut.clone(), i as i32, span);
52                    tests.push(p.match_test(&elem));
53                }
54                and_chain(tests, span)
55            }
56            PatternKind::List { elems, has_rest } => {
57                let prefix_len = if *has_rest { elems.len() - 1 } else { elems.len() };
58                let mut tests = Vec::new();
59                tests.push(method_call(scrut.clone(), "is_list", Vec::new(), span));
60                let len_expr = method_call(scrut.clone(), "len", Vec::new(), span);
61                let len_lit = ExprKind::Value(Dynamic::I32(prefix_len as i32)).at(span);
62                let len_op = if *has_rest { BinaryOp::Ge } else { BinaryOp::Eq };
63                tests.push(bin(len_expr, len_op, len_lit, span));
64                for (i, p) in elems.iter().take(prefix_len).enumerate() {
65                    let elem = idx_expr(scrut.clone(), i as i32, span);
66                    tests.push(p.match_test(&elem));
67                }
68                and_chain(tests, span)
69            }
70            PatternKind::Struct { fields, .. } => {
71                let mut tests = Vec::new();
72                for (name, sub) in fields {
73                    let field = field_expr(scrut.clone(), name.clone(), span);
74                    if let Some(sub) = sub {
75                        tests.push(sub.match_test(&field));
76                    }
77                    // 简写 `field` 永远绑定且永远匹配,跳过 test。
78                }
79                if tests.is_empty() { ExprKind::Value(Dynamic::Bool(true)).at(span) } else { and_chain(tests, span) }
80            }
81            PatternKind::Member(_, _) | PatternKind::Idx(_, _) => ExprKind::Value(Dynamic::Bool(true)).at(span),
82        }
83    }
84}
85
86fn bin(left: Expr, op: BinaryOp, right: Expr, span: Span) -> Expr {
87    Expr::new(ExprKind::Binary { left: Box::new(left), op, right: Box::new(right) }, span)
88}
89
90fn idx_expr(scrut: Expr, idx: i32, span: Span) -> Expr {
91    bin(scrut, BinaryOp::Idx, ExprKind::Value(Dynamic::I32(idx)).at(span), span)
92}
93
94fn field_expr(scrut: Expr, name: SmolStr, span: Span) -> Expr {
95    bin(scrut, BinaryOp::Idx, ExprKind::Value(Dynamic::String(name)).at(span), span)
96}
97
98fn method_call(obj: Expr, name: &str, args: Vec<Expr>, span: Span) -> Expr {
99    let member = field_expr(obj, SmolStr::from(name), span);
100    Expr::new(ExprKind::Call { obj: Box::new(member), params: args }, span)
101}
102
103fn and_chain(mut parts: Vec<Expr>, span: Span) -> Expr {
104    if parts.is_empty() {
105        return ExprKind::Value(Dynamic::Bool(true)).at(span);
106    }
107    let first = parts.remove(0);
108    parts.into_iter().fold(first, |acc, e| bin(acc, BinaryOp::And, e, span))
109}
110
111trait ExprKindExt {
112    fn at(self, span: Span) -> Expr;
113}
114
115impl ExprKindExt for ExprKind {
116    fn at(self, span: Span) -> Expr {
117        Expr::new(self, span)
118    }
119}
120
121impl Parser {
122    pub fn pattern(&mut self) -> Result<Pattern> {
123        self.whitespace()?;
124        let start = self.current_pos();
125        // 数字字面量(支持负号)。
126        if self.get()?.is_ascii_digit() || (self.get()? == b'-' && self.ahead().map(|c| c.is_ascii_digit()).unwrap_or(false)) {
127            let negative = if self.get()? == b'-' {
128                self.pos += 1;
129                true
130            } else {
131                false
132            };
133            let mut value = self.number()?;
134            if negative {
135                value = match value {
136                    Dynamic::I32(n) => Dynamic::I32(-n),
137                    Dynamic::I64(n) => Dynamic::I64(-n),
138                    Dynamic::F32(n) => Dynamic::F32(-n),
139                    Dynamic::F64(n) => Dynamic::F64(-n),
140                    other => return Err(anyhow!("不支持负数模式: {:?}", other)),
141                };
142            }
143            return Ok(Pattern::new(PatternKind::Literal(value), self.span_from(start)));
144        }
145        if self.keyword("_").is_ok() {
146            Ok(Pattern::new(PatternKind::Wildcard, self.span_from(start)))
147        } else if self.keyword("true").is_ok() {
148            Ok(Pattern::new(PatternKind::Literal(Dynamic::Bool(true)), self.span_from(start)))
149        } else if self.keyword("false").is_ok() {
150            Ok(Pattern::new(PatternKind::Literal(Dynamic::Bool(false)), self.span_from(start)))
151        } else if self.keyword("null").is_ok() {
152            Ok(Pattern::new(PatternKind::Literal(Dynamic::Null), self.span_from(start)))
153        } else if self.just("(").is_ok() {
154            Ok(Pattern::new(PatternKind::Tuple(crate::parse_list!(self, Vec::new(), b')', b',', self.pattern()?)), self.span_from(start)))
155        } else if self.just("[").is_ok() {
156            // 手写 list 解析,处理 `..rest`。
157            let mut elems = Vec::new();
158            let mut has_rest = false;
159            self.whitespace()?;
160            if self.take(b']').is_ok() {
161                return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
162            }
163            // 第一个元素可能是 `..name` 形式(整个 list 都是 rest)。
164            if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
165                self.whitespace()?;
166                let (name, ty) = self.ident_typed()?;
167                elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
168                has_rest = true;
169                self.whitespace()?;
170                self.take(b']')?;
171                return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
172            }
173            elems.push(self.pattern()?);
174            self.whitespace()?;
175            if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
176                self.whitespace()?;
177                let (name, ty) = self.ident_typed()?;
178                elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
179                has_rest = true;
180                self.whitespace()?;
181                let _ = self.take(b','); // 容忍尾随逗号
182                self.whitespace()?;
183                self.take(b']')?;
184                return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
185            } else if self.take(b',').is_ok() {
186                loop {
187                    self.whitespace()?;
188                    if self.take(b']').is_ok() {
189                        break;
190                    }
191                    if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
192                        self.whitespace()?;
193                        let (name, ty) = self.ident_typed()?;
194                        elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
195                        has_rest = true;
196                        self.whitespace()?;
197                        self.take(b']')?;
198                        return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
199                    }
200                    elems.push(self.pattern()?);
201                    self.whitespace()?;
202                    if !self.take(b',').is_ok() {
203                        break;
204                    }
205                }
206            }
207            self.whitespace()?;
208            self.take(b']')?;
209            Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)))
210        } else if let Ok(text) = self.string() {
211            Ok(Pattern::new(PatternKind::Literal(Dynamic::String(text)), self.span_from(start)))
212        } else if let Ok((name, ty)) = self.ident_typed() {
213            // 如果紧跟 `{`,且看起来像 struct 字面量,则解析为 Struct 模式。
214            self.whitespace()?;
215            if matches!(self.get(), Ok(b'{')) {
216                self.pos += 1;
217                let mut fields: Vec<(SmolStr, Option<Pattern>)> = Vec::new();
218                self.whitespace()?;
219                if self.take(b'}').is_err() {
220                    loop {
221                        self.whitespace()?;
222                        let field_name = self.ident()?;
223                        self.whitespace()?;
224                        let sub = if self.take(b':').is_ok() { Some(self.pattern()?) } else { None };
225                        fields.push((field_name, sub));
226                        self.whitespace()?;
227                        if !self.take(b',').is_ok() {
228                            break;
229                        }
230                    }
231                    self.whitespace()?;
232                    self.take(b'}')?;
233                }
234                return Ok(Pattern::new(PatternKind::Struct { name, fields }, self.span_from(start)));
235            }
236            Ok(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)))
237        } else {
238            Err(anyhow!("无效的模式 {:?}", String::from_utf8_lossy(&self.buf[self.pos..])))
239        }
240    }
241}