diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index 97c96f7..e6ea251 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -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>), Ref(Mutability, Box), diff --git a/compiler/cl-ast/src/ast_impl/display.rs b/compiler/cl-ast/src/ast_impl/display.rs index 1f24d3c..f251bca 100644 --- a/compiler/cl-ast/src/ast_impl/display.rs +++ b/compiler/cl-ast/src/ast_impl/display.rs @@ -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), diff --git a/compiler/cl-ast/src/ast_impl/weight_of.rs b/compiler/cl-ast/src/ast_impl/weight_of.rs index 46ec5d1..3d15a43 100644 --- a/compiler/cl-ast/src/ast_impl/weight_of.rs +++ b/compiler/cl-ast/src/ast_impl/weight_of.rs @@ -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, diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index 5dc77ea..19f414e 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -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), diff --git a/compiler/cl-ast/src/ast_visitor/walk.rs b/compiler/cl-ast/src/ast_visitor/walk.rs index bd3a487..0383329 100644 --- a/compiler/cl-ast/src/ast_visitor/walk.rs +++ b/compiler/cl-ast/src/ast_visitor/walk.rs @@ -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) => { diff --git a/compiler/cl-interpret/src/pattern.rs b/compiler/cl-interpret/src/pattern.rs index 7a271d0..bcf52f4 100644 --- a/compiler/cl-interpret/src/pattern.rs +++ b/compiler/cl-interpret/src/pattern.rs @@ -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) => {} diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index 097ef18..ee5a353 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -1093,10 +1093,94 @@ impl Parse<'_> for Return { } } +fn pathpattern(p: &mut Parser<'_>) -> PResult { + 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 { - 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))? + } + }) } } diff --git a/compiler/cl-parser/src/parser/prec.rs b/compiler/cl-parser/src/parser/prec.rs index fed343e..4ee9e6b 100644 --- a/compiler/cl-parser/src/parser/prec.rs +++ b/compiler/cl-parser/src/parser/prec.rs @@ -286,7 +286,6 @@ fn structor_body(p: &mut Parser, to: Path) -> PResult { #[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, diff --git a/compiler/cl-repl/examples/to_c.rs b/compiler/cl-repl/examples/to_c.rs index 26ab218..a69923f 100644 --- a/compiler/cl-repl/examples/to_c.rs +++ b/compiler/cl-repl/examples/to_c.rs @@ -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), diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index 12e31b7..4cfefd5 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -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), diff --git a/compiler/cl-typeck/src/stage/infer/inference.rs b/compiler/cl-typeck/src/stage/infer/inference.rs index ab76adc..50566c1 100644 --- a/compiler/cl-typeck/src/stage/infer/inference.rs +++ b/compiler/cl-typeck/src/stage/infer/inference.rs @@ -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"),