cl-ast: Add pattern and match nodes, and associated behaviors
This commit is contained in:
parent
d21683ad61
commit
6e94b702c9
@ -413,6 +413,27 @@ pub struct Let {
|
||||
pub init: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
/// A [Pattern] meta-expression (any [`ExprKind`] that fits pattern rules)
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Pattern {
|
||||
Path(Path),
|
||||
Literal(Literal),
|
||||
Ref(Mutability, Box<Pattern>),
|
||||
Tuple(Vec<Pattern>),
|
||||
Array(Vec<Pattern>),
|
||||
Struct(Path, Vec<(Path, Option<Pattern>)>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Match {
|
||||
pub scrutinee: Box<Expr>,
|
||||
pub arms: Vec<MatchArm>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct MatchArm(pub Pattern, pub Expr);
|
||||
|
||||
|
||||
/// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Assign {
|
||||
|
@ -464,6 +464,47 @@ mod display {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Pattern {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Pattern::Path(path) => path.fmt(f),
|
||||
Pattern::Literal(literal) => literal.fmt(f),
|
||||
Pattern::Ref(mutability, pattern) => write!(f, "&{mutability}{pattern}"),
|
||||
Pattern::Tuple(patterns) => separate(patterns, ", ")(f.delimit(INLINE_PARENS)),
|
||||
Pattern::Array(patterns) => separate(patterns, ", ")(f.delimit(INLINE_SQUARE)),
|
||||
Pattern::Struct(path, items) => {
|
||||
write!(f, "{path}: ")?;
|
||||
let f = &mut f.delimit(BRACES);
|
||||
for (idx, (name, item)) in items.iter().enumerate() {
|
||||
if idx != 0 {
|
||||
f.write_str(",\n")?;
|
||||
}
|
||||
write!(f, "{name}")?;
|
||||
if let Some(pattern) = item {
|
||||
write!(f, ": {pattern}")?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Match {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { scrutinee, arms } = self;
|
||||
write!(f, "match {scrutinee} ")?;
|
||||
separate(arms, ",\n")(f.delimit(BRACES))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MatchArm {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self(pat, expr) = self;
|
||||
write!(f, "{pat} => {expr}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Assign {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { parts } = self;
|
||||
@ -812,6 +853,50 @@ mod convert {
|
||||
Self { body: Some(value.into()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ExprKind> for Pattern {
|
||||
type Error = ExprKind;
|
||||
|
||||
/// Performs the conversion. On failure, returns the *first* non-pattern subexpression.
|
||||
fn try_from(value: ExprKind) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
ExprKind::Literal(literal) => Pattern::Literal(literal),
|
||||
ExprKind::Path(path) => Pattern::Path(path),
|
||||
ExprKind::Empty => Pattern::Tuple(vec![]),
|
||||
ExprKind::Group(Group { expr }) => Pattern::Tuple(vec![Pattern::try_from(*expr)?]),
|
||||
ExprKind::Tuple(Tuple { exprs }) => Pattern::Tuple(
|
||||
exprs
|
||||
.into_iter()
|
||||
.map(|e| Pattern::try_from(e.kind))
|
||||
.collect::<Result<_, _>>()?,
|
||||
),
|
||||
ExprKind::AddrOf(AddrOf { mutable, expr }) => {
|
||||
Pattern::Ref(mutable, Box::new(Pattern::try_from(*expr)?))
|
||||
}
|
||||
ExprKind::Array(Array { values }) => Pattern::Array(
|
||||
values
|
||||
.into_iter()
|
||||
.map(|e| Pattern::try_from(e.kind))
|
||||
.collect::<Result<_, _>>()?,
|
||||
),
|
||||
// ExprKind::Index(index) => todo!(),
|
||||
// ExprKind::Member(member) => todo!(),
|
||||
ExprKind::Structor(Structor { to, init }) => {
|
||||
let fields = init
|
||||
.into_iter()
|
||||
.map(|Fielder { name, init }| {
|
||||
Ok((
|
||||
name.into(),
|
||||
init.map(|i| Pattern::try_from(i.kind)).transpose()?,
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, Self::Error>>()?;
|
||||
Pattern::Struct(to, fields)
|
||||
}
|
||||
err => Err(err)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod path {
|
||||
@ -837,10 +922,26 @@ mod path {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether this path refers to the sinkhole identifier, `_`
|
||||
pub fn is_sinkhole(&self) -> bool {
|
||||
if let [PathPart::Ident(id)] = self.parts.as_slice() {
|
||||
if let "_" = Sym::to_ref(id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
impl PathPart {
|
||||
pub fn from_sym(ident: Sym) -> Self {
|
||||
Self::Ident(ident)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Sym> for Path {
|
||||
fn from(value: Sym) -> Self {
|
||||
Self { parts: vec![PathPart::Ident(value)], absolute: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -229,6 +229,13 @@ pub trait Fold {
|
||||
fn fold_semi(&mut self, s: Semi) -> Semi {
|
||||
s
|
||||
}
|
||||
fn fold_expr(&mut self, e: Expr) -> Expr {
|
||||
let Expr { extents, kind } = e;
|
||||
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) }
|
||||
}
|
||||
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
|
||||
or_fold_expr_kind(self, kind)
|
||||
}
|
||||
fn fold_let(&mut self, l: Let) -> Let {
|
||||
let Let { mutable, name, ty, init } = l;
|
||||
Let {
|
||||
@ -238,13 +245,47 @@ pub trait Fold {
|
||||
init: init.map(|e| Box::new(self.fold_expr(*e))),
|
||||
}
|
||||
}
|
||||
fn fold_expr(&mut self, e: Expr) -> Expr {
|
||||
let Expr { extents, kind } = e;
|
||||
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) }
|
||||
|
||||
fn fold_pattern(&mut self, p: Pattern) -> Pattern {
|
||||
match p {
|
||||
Pattern::Path(path) => Pattern::Path(self.fold_path(path)),
|
||||
Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)),
|
||||
Pattern::Ref(mutability, pattern) => Pattern::Ref(
|
||||
self.fold_mutability(mutability),
|
||||
Box::new(self.fold_pattern(*pattern)),
|
||||
),
|
||||
Pattern::Tuple(patterns) => {
|
||||
Pattern::Tuple(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
|
||||
}
|
||||
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
|
||||
or_fold_expr_kind(self, kind)
|
||||
Pattern::Array(patterns) => {
|
||||
Pattern::Array(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
|
||||
}
|
||||
Pattern::Struct(path, items) => Pattern::Struct(
|
||||
self.fold_path(path),
|
||||
items
|
||||
.into_iter()
|
||||
.map(|(name, bind)| (name, bind.map(|p| self.fold_pattern(p))))
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_match(&mut self, m: Match) -> Match {
|
||||
let Match { scrutinee, arms } = m;
|
||||
Match {
|
||||
scrutinee: self.fold_expr(*scrutinee).into(),
|
||||
arms: arms
|
||||
.into_iter()
|
||||
.map(|arm| self.fold_match_arm(arm))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_match_arm(&mut self, a: MatchArm) -> MatchArm {
|
||||
let MatchArm(pat, expr) = a;
|
||||
MatchArm(self.fold_pattern(pat), self.fold_expr(expr))
|
||||
}
|
||||
|
||||
fn fold_assign(&mut self, a: Assign) -> Assign {
|
||||
let Assign { parts } = a;
|
||||
let (head, tail) = *parts;
|
||||
|
@ -192,6 +192,14 @@ pub trait Visit<'a>: Sized {
|
||||
or_visit_stmt_kind(self, kind)
|
||||
}
|
||||
fn visit_semi(&mut self, _s: &'a Semi) {}
|
||||
fn visit_expr(&mut self, e: &'a Expr) {
|
||||
let Expr { extents, kind } = e;
|
||||
self.visit_span(extents);
|
||||
self.visit_expr_kind(kind)
|
||||
}
|
||||
fn visit_expr_kind(&mut self, e: &'a ExprKind) {
|
||||
or_visit_expr_kind(self, e)
|
||||
}
|
||||
fn visit_let(&mut self, l: &'a Let) {
|
||||
let Let { mutable, name, ty, init } = l;
|
||||
self.visit_mutability(mutable);
|
||||
@ -203,14 +211,44 @@ pub trait Visit<'a>: Sized {
|
||||
self.visit_expr(init)
|
||||
}
|
||||
}
|
||||
fn visit_expr(&mut self, e: &'a Expr) {
|
||||
let Expr { extents, kind } = e;
|
||||
self.visit_span(extents);
|
||||
self.visit_expr_kind(kind)
|
||||
|
||||
fn visit_pattern(&mut self, p: &'a Pattern) {
|
||||
match p {
|
||||
Pattern::Path(path) => self.visit_path(path),
|
||||
Pattern::Literal(literal) => self.visit_literal(literal),
|
||||
Pattern::Ref(mutability, pattern) => {
|
||||
self.visit_mutability(mutability);
|
||||
self.visit_pattern(pattern);
|
||||
}
|
||||
fn visit_expr_kind(&mut self, e: &'a ExprKind) {
|
||||
or_visit_expr_kind(self, e)
|
||||
Pattern::Tuple(patterns) => {
|
||||
patterns.iter().for_each(|p| self.visit_pattern(p));
|
||||
}
|
||||
Pattern::Array(patterns) => {
|
||||
patterns.iter().for_each(|p| self.visit_pattern(p));
|
||||
}
|
||||
Pattern::Struct(path, items) => {
|
||||
self.visit_path(path);
|
||||
items.iter().for_each(|(_name, bind)| {
|
||||
bind.as_ref().inspect(|bind| {
|
||||
self.visit_pattern(bind);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_match(&mut self, m: &'a Match) {
|
||||
let Match { scrutinee, arms } = m;
|
||||
self.visit_expr(scrutinee);
|
||||
arms.iter().for_each(|arm| self.visit_match_arm(arm));
|
||||
}
|
||||
|
||||
fn visit_match_arm(&mut self, a: &'a MatchArm) {
|
||||
let MatchArm(pat, expr) = a;
|
||||
self.visit_pattern(pat);
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
|
||||
fn visit_assign(&mut self, a: &'a Assign) {
|
||||
let Assign { parts } = a;
|
||||
let (head, tail) = parts.as_ref();
|
||||
|
@ -369,16 +369,6 @@ pub mod yamlify {
|
||||
};
|
||||
}
|
||||
}
|
||||
impl Yamlify for Let {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
let Self { mutable, name, ty, init } = self;
|
||||
y.key("Let")
|
||||
.pair("name", name)
|
||||
.yaml(mutable)
|
||||
.pair("ty", ty)
|
||||
.pair("init", init);
|
||||
}
|
||||
}
|
||||
impl Yamlify for Expr {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
let Self { extents: _, kind } = self;
|
||||
@ -423,6 +413,55 @@ pub mod yamlify {
|
||||
y.key("Quote").value(self);
|
||||
}
|
||||
}
|
||||
impl Yamlify for Let {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
let Self { mutable, name, ty, init } = self;
|
||||
y.key("Let")
|
||||
.pair("name", name)
|
||||
.yaml(mutable)
|
||||
.pair("ty", ty)
|
||||
.pair("init", init);
|
||||
}
|
||||
}
|
||||
|
||||
impl Yamlify for Pattern {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
match self {
|
||||
Pattern::Path(path) => y.value(path),
|
||||
Pattern::Literal(literal) => y.value(literal),
|
||||
Pattern::Ref(mutability, pattern) => {
|
||||
y.pair("mutability", mutability).pair("subpattern", pattern)
|
||||
}
|
||||
Pattern::Tuple(patterns) => y.key("Tuple").yaml(patterns),
|
||||
Pattern::Array(patterns) => y.key("Array").yaml(patterns),
|
||||
Pattern::Struct(path, items) => {
|
||||
{
|
||||
let mut y = y.key("Struct");
|
||||
y.pair("name", path);
|
||||
for (name, item) in items {
|
||||
y.pair(name, item);
|
||||
}
|
||||
}
|
||||
y
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
impl Yamlify for Match {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
let Self { scrutinee, arms } = self;
|
||||
y.key("Match")
|
||||
.pair("scrutinee", scrutinee)
|
||||
.pair("arms", arms);
|
||||
}
|
||||
}
|
||||
|
||||
impl Yamlify for MatchArm {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
let Self(pat, expr) = self;
|
||||
y.pair("pat", pat).pair("expr", expr);
|
||||
}
|
||||
}
|
||||
impl Yamlify for Assign {
|
||||
fn yaml(&self, y: &mut Yamler) {
|
||||
let Self { parts } = self;
|
||||
|
Loading…
x
Reference in New Issue
Block a user