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                // 支持所有有符号整数与浮点类型的负数字面量模式。
136                // 无符号类型(U8 等)取负无意义,落到 other 报错。
137                value = match value {
138                    Dynamic::I8(n) => Dynamic::I8(n.wrapping_neg()),
139                    Dynamic::I16(n) => Dynamic::I16(n.wrapping_neg()),
140                    Dynamic::I32(n) => Dynamic::I32(-n),
141                    Dynamic::I64(n) => Dynamic::I64(-n),
142                    Dynamic::F16(n) => Dynamic::F16(dynamic::f64_to_f16(-dynamic::f16_to_f64(n))),
143                    Dynamic::F32(n) => Dynamic::F32(-n),
144                    Dynamic::F64(n) => Dynamic::F64(-n),
145                    other => return Err(anyhow!("不支持负数模式: {:?}", other)),
146                };
147            }
148            return Ok(Pattern::new(PatternKind::Literal(value), self.span_from(start)));
149        }
150        if self.keyword("_").is_ok() {
151            Ok(Pattern::new(PatternKind::Wildcard, self.span_from(start)))
152        } else if self.keyword("true").is_ok() {
153            Ok(Pattern::new(PatternKind::Literal(Dynamic::Bool(true)), self.span_from(start)))
154        } else if self.keyword("false").is_ok() {
155            Ok(Pattern::new(PatternKind::Literal(Dynamic::Bool(false)), self.span_from(start)))
156        } else if self.keyword("null").is_ok() {
157            Ok(Pattern::new(PatternKind::Literal(Dynamic::Null), self.span_from(start)))
158        } else if self.just("(").is_ok() {
159            self.whitespace()?;
160            if self.take(b')').is_ok() {
161                return Ok(Pattern::new(PatternKind::Tuple(Vec::new()), self.span_from(start)));
162            }
163            let first = self.pattern()?;
164            self.whitespace()?;
165            if self.take(b',').is_ok() {
166                let mut items = vec![first];
167                loop {
168                    self.whitespace()?;
169                    if self.take(b')').is_ok() {
170                        break;
171                    }
172                    items.push(self.pattern()?);
173                    self.whitespace()?;
174                    if self.take(b',').is_err() {
175                        self.take(b')')?;
176                        break;
177                    }
178                }
179                Ok(Pattern::new(PatternKind::Tuple(items), self.span_from(start)))
180            } else {
181                self.take(b')')?;
182                Ok(first)
183            }
184        } else if self.just("[").is_ok() {
185            // 手写 list 解析,处理 `..rest`。
186            let mut elems = Vec::new();
187            let mut has_rest = false;
188            self.whitespace()?;
189            if self.take(b']').is_ok() {
190                return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
191            }
192            // 第一个元素可能是 `..name` 形式(整个 list 都是 rest)。
193            if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
194                self.whitespace()?;
195                let (name, ty) = self.ident_typed()?;
196                elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
197                has_rest = true;
198                self.whitespace()?;
199                self.take(b']')?;
200                return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
201            }
202            elems.push(self.pattern()?);
203            self.whitespace()?;
204            if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
205                self.whitespace()?;
206                let (name, ty) = self.ident_typed()?;
207                elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
208                has_rest = true;
209                self.whitespace()?;
210                let _ = self.take(b','); // 容忍尾随逗号
211                self.whitespace()?;
212                self.take(b']')?;
213                return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
214            } else if self.take(b',').is_ok() {
215                loop {
216                    self.whitespace()?;
217                    if self.take(b']').is_ok() {
218                        break;
219                    }
220                    if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
221                        self.whitespace()?;
222                        let (name, ty) = self.ident_typed()?;
223                        elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
224                        has_rest = true;
225                        self.whitespace()?;
226                        self.take(b']')?;
227                        return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
228                    }
229                    elems.push(self.pattern()?);
230                    self.whitespace()?;
231                    if !self.take(b',').is_ok() {
232                        break;
233                    }
234                }
235            }
236            self.whitespace()?;
237            self.take(b']')?;
238            Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)))
239        } else if let Ok(text) = self.string() {
240            Ok(Pattern::new(PatternKind::Literal(Dynamic::String(text)), self.span_from(start)))
241        } else if let Ok((name, ty)) = self.ident_typed() {
242            // 如果紧跟 `{`,且看起来像 struct 字面量,则解析为 Struct 模式。
243            self.whitespace()?;
244            if matches!(self.get(), Ok(b'{')) {
245                self.pos += 1;
246                let mut fields: Vec<(SmolStr, Option<Pattern>)> = Vec::new();
247                self.whitespace()?;
248                if self.take(b'}').is_err() {
249                    loop {
250                        self.whitespace()?;
251                        let field_name = self.ident()?;
252                        self.whitespace()?;
253                        let sub = if self.take(b':').is_ok() { Some(self.pattern()?) } else { None };
254                        fields.push((field_name, sub));
255                        self.whitespace()?;
256                        if !self.take(b',').is_ok() {
257                            break;
258                        }
259                    }
260                    self.whitespace()?;
261                    self.take(b'}')?;
262                }
263                return Ok(Pattern::new(PatternKind::Struct { name, fields }, self.span_from(start)));
264            }
265            Ok(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)))
266        } else {
267            Err(anyhow!("无效的模式 {:?}", String::from_utf8_lossy(&self.buf[self.pos..])))
268        }
269    }
270}