cl-ast: Add pattern and match nodes, and associated behaviors

This commit is contained in:
John 2025-01-29 04:05:11 -06:00
parent d21683ad61
commit 6e94b702c9
5 changed files with 261 additions and 21 deletions

View File

@ -413,6 +413,27 @@ pub struct Let {
pub init: Option<Box<Expr>>, 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`])\+ /// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Assign { pub struct Assign {

View File

@ -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 { impl Display for Assign {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { parts } = self; let Self { parts } = self;
@ -812,6 +853,50 @@ mod convert {
Self { body: Some(value.into()) } 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 { mod path {
@ -837,10 +922,26 @@ mod path {
self 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 { impl PathPart {
pub fn from_sym(ident: Sym) -> Self { pub fn from_sym(ident: Sym) -> Self {
Self::Ident(ident) Self::Ident(ident)
} }
} }
impl From<Sym> for Path {
fn from(value: Sym) -> Self {
Self { parts: vec![PathPart::Ident(value)], absolute: false }
}
}
} }

View File

@ -229,6 +229,13 @@ pub trait Fold {
fn fold_semi(&mut self, s: Semi) -> Semi { fn fold_semi(&mut self, s: Semi) -> Semi {
s 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 { fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l; let Let { mutable, name, ty, init } = l;
Let { Let {
@ -238,13 +245,47 @@ pub trait Fold {
init: init.map(|e| Box::new(self.fold_expr(*e))), init: init.map(|e| Box::new(self.fold_expr(*e))),
} }
} }
fn fold_expr(&mut self, e: Expr) -> Expr {
let Expr { extents, kind } = e; fn fold_pattern(&mut self, p: Pattern) -> Pattern {
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) } 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())
}
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_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
or_fold_expr_kind(self, kind) 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 { fn fold_assign(&mut self, a: Assign) -> Assign {
let Assign { parts } = a; let Assign { parts } = a;
let (head, tail) = *parts; let (head, tail) = *parts;

View File

@ -192,6 +192,14 @@ pub trait Visit<'a>: Sized {
or_visit_stmt_kind(self, kind) or_visit_stmt_kind(self, kind)
} }
fn visit_semi(&mut self, _s: &'a Semi) {} 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) { fn visit_let(&mut self, l: &'a Let) {
let Let { mutable, name, ty, init } = l; let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable); self.visit_mutability(mutable);
@ -203,14 +211,44 @@ pub trait Visit<'a>: Sized {
self.visit_expr(init) self.visit_expr(init)
} }
} }
fn visit_expr(&mut self, e: &'a Expr) {
let Expr { extents, kind } = e; fn visit_pattern(&mut self, p: &'a Pattern) {
self.visit_span(extents); match p {
self.visit_expr_kind(kind) 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);
}
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_expr_kind(&mut self, e: &'a ExprKind) {
or_visit_expr_kind(self, e) 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) { fn visit_assign(&mut self, a: &'a Assign) {
let Assign { parts } = a; let Assign { parts } = a;
let (head, tail) = parts.as_ref(); let (head, tail) = parts.as_ref();

View File

@ -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 { impl Yamlify for Expr {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self; let Self { extents: _, kind } = self;
@ -423,6 +413,55 @@ pub mod yamlify {
y.key("Quote").value(self); 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 { impl Yamlify for Assign {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { parts } = self; let Self { parts } = self;