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>>,
|
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 {
|
||||||
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user