use anyhow::{Result, anyhow};
use dynamic::{Dynamic, Type};
use smol_str::SmolStr;
use super::{BinaryOp, Parser, Span, expr::Expr, expr::ExprKind};
#[derive(Debug, Clone)]
pub struct Pattern {
pub kind: PatternKind,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum PatternKind {
Wildcard,
Var { idx: u32, ty: Type },
Ident { name: SmolStr, ty: Type },
Literal(Dynamic),
Tuple(Vec<Pattern>),
List { elems: Vec<Pattern>, has_rest: bool },
Struct { name: SmolStr, fields: Vec<(SmolStr, Option<Pattern>)> },
Member(Box<Pattern>, SmolStr),
Idx(Box<Pattern>, Expr),
}
impl Pattern {
pub fn new(kind: PatternKind, span: Span) -> Self {
Self { kind, span }
}
pub fn var(&self) -> Option<u32> {
if let PatternKind::Var { idx, .. } = &self.kind { Some(*idx) } else { None }
}
pub fn expr(&self) -> Result<Expr> {
if let PatternKind::Var { idx, .. } = &self.kind { Ok(Expr::new(super::expr::ExprKind::Var(*idx), self.span)) } else { Err(anyhow!("不是变量")) }
}
pub fn match_test(&self, scrut: &Expr) -> Expr {
let span = self.span;
match &self.kind {
PatternKind::Wildcard | PatternKind::Var { .. } | PatternKind::Ident { .. } => Expr::new(ExprKind::Value(Dynamic::Bool(true)), span),
PatternKind::Literal(v) => bin(scrut.clone(), BinaryOp::Eq, ExprKind::Value(v.clone()).at(span), span),
PatternKind::Tuple(items) => {
let mut tests = Vec::new();
tests.push(bin(method_call(scrut.clone(), "len", Vec::new(), span), BinaryOp::Eq, ExprKind::Value(Dynamic::I32(items.len() as i32)).at(span), span));
for (i, p) in items.iter().enumerate() {
let elem = idx_expr(scrut.clone(), i as i32, span);
tests.push(p.match_test(&elem));
}
and_chain(tests, span)
}
PatternKind::List { elems, has_rest } => {
let prefix_len = if *has_rest { elems.len() - 1 } else { elems.len() };
let mut tests = Vec::new();
tests.push(method_call(scrut.clone(), "is_list", Vec::new(), span));
let len_expr = method_call(scrut.clone(), "len", Vec::new(), span);
let len_lit = ExprKind::Value(Dynamic::I32(prefix_len as i32)).at(span);
let len_op = if *has_rest { BinaryOp::Ge } else { BinaryOp::Eq };
tests.push(bin(len_expr, len_op, len_lit, span));
for (i, p) in elems.iter().take(prefix_len).enumerate() {
let elem = idx_expr(scrut.clone(), i as i32, span);
tests.push(p.match_test(&elem));
}
and_chain(tests, span)
}
PatternKind::Struct { fields, .. } => {
let mut tests = Vec::new();
for (name, sub) in fields {
let field = field_expr(scrut.clone(), name.clone(), span);
if let Some(sub) = sub {
tests.push(sub.match_test(&field));
}
}
if tests.is_empty() { ExprKind::Value(Dynamic::Bool(true)).at(span) } else { and_chain(tests, span) }
}
PatternKind::Member(_, _) | PatternKind::Idx(_, _) => ExprKind::Value(Dynamic::Bool(true)).at(span),
}
}
}
fn bin(left: Expr, op: BinaryOp, right: Expr, span: Span) -> Expr {
Expr::new(ExprKind::Binary { left: Box::new(left), op, right: Box::new(right) }, span)
}
fn idx_expr(scrut: Expr, idx: i32, span: Span) -> Expr {
bin(scrut, BinaryOp::Idx, ExprKind::Value(Dynamic::I32(idx)).at(span), span)
}
fn field_expr(scrut: Expr, name: SmolStr, span: Span) -> Expr {
bin(scrut, BinaryOp::Idx, ExprKind::Value(Dynamic::String(name)).at(span), span)
}
fn method_call(obj: Expr, name: &str, args: Vec<Expr>, span: Span) -> Expr {
let member = field_expr(obj, SmolStr::from(name), span);
Expr::new(ExprKind::Call { obj: Box::new(member), params: args }, span)
}
fn and_chain(mut parts: Vec<Expr>, span: Span) -> Expr {
if parts.is_empty() {
return ExprKind::Value(Dynamic::Bool(true)).at(span);
}
let first = parts.remove(0);
parts.into_iter().fold(first, |acc, e| bin(acc, BinaryOp::And, e, span))
}
trait ExprKindExt {
fn at(self, span: Span) -> Expr;
}
impl ExprKindExt for ExprKind {
fn at(self, span: Span) -> Expr {
Expr::new(self, span)
}
}
impl Parser {
pub fn pattern(&mut self) -> Result<Pattern> {
self.whitespace()?;
let start = self.current_pos();
if self.get()?.is_ascii_digit() || (self.get()? == b'-' && self.ahead().map(|c| c.is_ascii_digit()).unwrap_or(false)) {
let negative = if self.get()? == b'-' {
self.pos += 1;
true
} else {
false
};
let mut value = self.number()?;
if negative {
value = match value {
Dynamic::I32(n) => Dynamic::I32(-n),
Dynamic::I64(n) => Dynamic::I64(-n),
Dynamic::F32(n) => Dynamic::F32(-n),
Dynamic::F64(n) => Dynamic::F64(-n),
other => return Err(anyhow!("不支持负数模式: {:?}", other)),
};
}
return Ok(Pattern::new(PatternKind::Literal(value), self.span_from(start)));
}
if self.keyword("_").is_ok() {
Ok(Pattern::new(PatternKind::Wildcard, self.span_from(start)))
} else if self.keyword("true").is_ok() {
Ok(Pattern::new(PatternKind::Literal(Dynamic::Bool(true)), self.span_from(start)))
} else if self.keyword("false").is_ok() {
Ok(Pattern::new(PatternKind::Literal(Dynamic::Bool(false)), self.span_from(start)))
} else if self.keyword("null").is_ok() {
Ok(Pattern::new(PatternKind::Literal(Dynamic::Null), self.span_from(start)))
} else if self.just("(").is_ok() {
Ok(Pattern::new(PatternKind::Tuple(crate::parse_list!(self, Vec::new(), b')', b',', self.pattern()?)), self.span_from(start)))
} else if self.just("[").is_ok() {
let mut elems = Vec::new();
let mut has_rest = false;
self.whitespace()?;
if self.take(b']').is_ok() {
return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
}
if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
self.whitespace()?;
let (name, ty) = self.ident_typed()?;
elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
has_rest = true;
self.whitespace()?;
self.take(b']')?;
return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
}
elems.push(self.pattern()?);
self.whitespace()?;
if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
self.whitespace()?;
let (name, ty) = self.ident_typed()?;
elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
has_rest = true;
self.whitespace()?;
let _ = self.take(b','); self.whitespace()?;
self.take(b']')?;
return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
} else if self.take(b',').is_ok() {
loop {
self.whitespace()?;
if self.take(b']').is_ok() {
break;
}
if self.take(b'.').is_ok() && self.take(b'.').is_ok() {
self.whitespace()?;
let (name, ty) = self.ident_typed()?;
elems.push(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)));
has_rest = true;
self.whitespace()?;
self.take(b']')?;
return Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)));
}
elems.push(self.pattern()?);
self.whitespace()?;
if !self.take(b',').is_ok() {
break;
}
}
}
self.whitespace()?;
self.take(b']')?;
Ok(Pattern::new(PatternKind::List { elems, has_rest }, self.span_from(start)))
} else if let Ok(text) = self.string() {
Ok(Pattern::new(PatternKind::Literal(Dynamic::String(text)), self.span_from(start)))
} else if let Ok((name, ty)) = self.ident_typed() {
self.whitespace()?;
if matches!(self.get(), Ok(b'{')) {
self.pos += 1;
let mut fields: Vec<(SmolStr, Option<Pattern>)> = Vec::new();
self.whitespace()?;
if self.take(b'}').is_err() {
loop {
self.whitespace()?;
let field_name = self.ident()?;
self.whitespace()?;
let sub = if self.take(b':').is_ok() { Some(self.pattern()?) } else { None };
fields.push((field_name, sub));
self.whitespace()?;
if !self.take(b',').is_ok() {
break;
}
}
self.whitespace()?;
self.take(b'}')?;
}
return Ok(Pattern::new(PatternKind::Struct { name, fields }, self.span_from(start)));
}
Ok(Pattern::new(PatternKind::Ident { name, ty }, self.span_from(start)))
} else {
Err(anyhow!("无效的模式 {:?}", String::from_utf8_lossy(&self.buf[self.pos..])))
}
}
}