cl-parser: Dedicated parsing logic for patterns!

This commit is contained in:
John 2025-05-18 04:00:00 -04:00
parent e6156343c3
commit 3e2063835b
11 changed files with 100 additions and 4 deletions

View File

@ -586,6 +586,7 @@ pub struct MatchArm(pub Pattern, pub Expr);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Pattern {
Name(Sym),
Path(Path),
Literal(Literal),
Rest(Option<Box<Pattern>>),
Ref(Mutability, Box<Pattern>),

View File

@ -468,6 +468,7 @@ impl Display for Pattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Pattern::Name(sym) => sym.fmt(f),
Pattern::Path(path) => path.fmt(f),
Pattern::Literal(literal) => literal.fmt(f),
Pattern::Rest(Some(name)) => write!(f, "..{name}"),
Pattern::Rest(None) => "..".fmt(f),

View File

@ -338,6 +338,7 @@ impl WeightOf for Pattern {
fn weight_of(&self) -> usize {
match self {
Pattern::Name(s) => size_of_val(s),
Pattern::Path(p) => p.weight_of(),
Pattern::Literal(literal) => literal.weight_of(),
Pattern::Rest(Some(pattern)) => pattern.weight_of(),
Pattern::Rest(None) => 0,

View File

@ -253,6 +253,7 @@ pub trait Fold {
fn fold_pattern(&mut self, p: Pattern) -> Pattern {
match p {
Pattern::Name(sym) => Pattern::Name(self.fold_sym(sym)),
Pattern::Path(path) => Pattern::Path(self.fold_path(path)),
Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)),
Pattern::Rest(Some(name)) => Pattern::Rest(Some(self.fold_pattern(*name).into())),
Pattern::Rest(None) => Pattern::Rest(None),

View File

@ -793,6 +793,7 @@ impl Walk for Pattern {
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
Pattern::Name(sym) => sym.visit_in(v),
Pattern::Path(path) => path.visit_in(v),
Pattern::Literal(literal) => literal.visit_in(v),
Pattern::Rest(pattern) => pattern.visit_in(v),
Pattern::Ref(mutability, pattern) => {

View File

@ -14,8 +14,9 @@ use std::collections::{HashMap, VecDeque};
pub fn variables(pat: &Pattern) -> Vec<&Sym> {
fn patvars<'p>(set: &mut Vec<&'p Sym>, pat: &'p Pattern) {
match pat {
Pattern::Name(name) if &**name == "_" => {}
Pattern::Name(name) if name.to_ref() == "_" => {}
Pattern::Name(name) => set.push(name),
Pattern::Path(_) => {}
Pattern::Literal(_) => {}
Pattern::Rest(Some(pattern)) => patvars(set, pattern),
Pattern::Rest(None) => {}

View File

@ -1093,10 +1093,94 @@ impl Parse<'_> for Return {
}
}
fn pathpattern(p: &mut Parser<'_>) -> PResult<Pattern> {
const P: Parsing = Parsing::Pattern;
let name = Path::parse(p)?;
let struct_members = |p: &mut Parser| {
let name = p.parse()?;
let pat = if p.match_type(TokenKind::Colon, P).is_ok() {
Some(p.parse()?)
} else {
None
};
Ok((name, pat))
};
Ok(match p.peek_kind(Parsing::Pattern)? {
TokenKind::LCurly => Pattern::Struct(
name,
delim(
sep(struct_members, TokenKind::Comma, TokenKind::RCurly, P),
CURLIES,
P,
)(p)?,
),
TokenKind::LParen => Pattern::TupleStruct(
name,
delim(
sep(Parse::parse, TokenKind::Comma, TokenKind::RParen, P),
PARENS,
P,
)(p)?,
),
_ => name
.as_sym()
.map(Pattern::Name)
.unwrap_or(Pattern::Path(name)),
})
}
impl Parse<'_> for Pattern {
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
let value = prec::expr(p, prec::Precedence::Pattern.level())?;
Pattern::try_from(value).map_err(|e| p.error(InvalidPattern(e.into()), Parsing::Pattern))
const P: Parsing = Parsing::Pattern;
Ok(match p.peek_kind(P)? {
// Name, Path, Struct, TupleStruct
TokenKind::Identifier => pathpattern(p)?,
// Literal
TokenKind::Literal => Pattern::Literal(p.parse()?),
// Rest
TokenKind::DotDot => {
p.consume_peeked();
if matches!(
p.peek_kind(P),
Ok(TokenKind::Identifier | TokenKind::Literal)
) {
Pattern::Rest(Some(p.parse()?))
} else {
Pattern::Rest(None)
}
}
// Ref
TokenKind::Amp => {
p.consume_peeked();
Pattern::Ref(p.parse()?, p.parse()?)
}
// Ref(Ref)
TokenKind::AmpAmp => {
p.consume_peeked();
Pattern::Ref(
Mutability::Not,
Box::new(Pattern::Ref(p.parse()?, p.parse()?)),
)
}
// Tuple
TokenKind::LParen => Pattern::Tuple(delim(
sep(Parse::parse, TokenKind::Comma, TokenKind::RParen, P),
PARENS,
P,
)(p)?),
// Array
TokenKind::LBrack => Pattern::Array(delim(
sep(Parse::parse, TokenKind::Comma, TokenKind::RBrack, P),
BRACKETS,
P,
)(p)?),
_ => {
let bad_expr = p.parse()?;
Err(p.error(ErrorKind::InvalidPattern(bad_expr), P))?
}
})
}
}

View File

@ -286,7 +286,6 @@ fn structor_body(p: &mut Parser, to: Path) -> PResult<Structor> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Precedence {
Assign,
Pattern, // A pattern can contain a structor
Structor, // A structor is never a valid conditional
Condition, // Anything that syntactically needs a block following it
Logic,

View File

@ -508,6 +508,7 @@ pub mod clangify {
// TODO: Pattern match desugaring!!!
match self {
Pattern::Name(name) => y.p(name),
Pattern::Path(path) => y.p(path),
Pattern::Literal(literal) => y.p(literal),
Pattern::Rest(name) => y.p("..").p(name),
Pattern::Ref(mutability, pattern) => y.p("&").p(mutability).p(pattern),

View File

@ -428,6 +428,7 @@ pub mod yamlify {
fn yaml(&self, y: &mut Yamler) {
match self {
Pattern::Name(name) => y.value(name),
Pattern::Path(path) => y.value(path),
Pattern::Literal(literal) => y.value(literal),
Pattern::Rest(name) => y.pair("Rest", name),
Pattern::Ref(mutability, pattern) => y.yaml(mutability).pair("Pat", pattern),

View File

@ -519,6 +519,11 @@ impl<'a> Inference<'a> for Pattern {
e.at = node;
Ok(node)
}
Pattern::Path(path) => {
// Evaluating a path pattern puts type constraints on the scrutinee
path.evaluate(e.table, e.at)
.map_err(|_| InferenceError::NotFound(path.clone()))
}
Pattern::Literal(literal) => literal.infer(e),
Pattern::Rest(Some(pat)) => pat.infer(e), // <-- glaring soundness holes
Pattern::Rest(_) => todo!("Fix glaring soundness holes in pattern"),