diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index 526d9f3..880db11 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -418,12 +418,12 @@ pub struct Let { /// A [Pattern] meta-expression (any [`ExprKind`] that fits pattern rules) #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Pattern { - Path(Path), + Name(Sym), Literal(Literal), Ref(Mutability, Box), Tuple(Vec), Array(Vec), - Struct(Path, Vec<(Path, Option)>), + Struct(Path, Vec<(Sym, Option)>), } /// A `match` expression: `match` `{` ([MatchArm] `,`)* [MatchArm]? `}` @@ -620,7 +620,7 @@ pub struct If { /// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]? #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct For { - pub bind: Sym, // TODO: Patterns? + pub bind: Pattern, pub cond: Box, pub pass: Box, pub fail: Else, diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index 7ea808a..7d048cf 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -468,7 +468,7 @@ 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::Name(sym) => sym.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)), @@ -863,7 +863,10 @@ mod convert { fn try_from(value: ExprKind) -> Result { Ok(match value { ExprKind::Literal(literal) => Pattern::Literal(literal), - ExprKind::Path(path) => Pattern::Path(path), + ExprKind::Path(Path { absolute: false, ref parts }) => match parts.as_slice() { + [PathPart::Ident(name)] => Pattern::Name(*name), + _ => Err(value)?, + }, ExprKind::Empty => Pattern::Tuple(vec![]), ExprKind::Group(Group { expr }) => Pattern::Tuple(vec![Pattern::try_from(*expr)?]), ExprKind::Tuple(Tuple { exprs }) => Pattern::Tuple( @@ -887,10 +890,7 @@ mod convert { let fields = init .into_iter() .map(|Fielder { name, init }| { - Ok(( - name.into(), - init.map(|i| Pattern::try_from(i.kind)).transpose()?, - )) + Ok((name, init.map(|i| Pattern::try_from(i.kind)).transpose()?)) }) .collect::>()?; Pattern::Struct(to, fields) diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index f8eb748..774be2b 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -248,7 +248,7 @@ pub trait Fold { fn fold_pattern(&mut self, p: Pattern) -> Pattern { match p { - Pattern::Path(path) => Pattern::Path(self.fold_path(path)), + Pattern::Name(sym) => Pattern::Name(self.fold_sym(sym)), Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)), Pattern::Ref(mutability, pattern) => Pattern::Ref( self.fold_mutability(mutability), @@ -285,7 +285,7 @@ pub trait Fold { 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; @@ -400,7 +400,7 @@ pub trait Fold { fn fold_for(&mut self, f: For) -> For { let For { bind, cond, pass, fail } = f; For { - bind: self.fold_sym(bind), + bind: self.fold_pattern(bind), cond: Box::new(self.fold_expr(*cond)), pass: Box::new(self.fold_block(*pass)), fail: self.fold_else(fail), diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index e17d8a8..a6ea5bc 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -214,7 +214,7 @@ pub trait Visit<'a>: Sized { fn visit_pattern(&mut self, p: &'a Pattern) { match p { - Pattern::Path(path) => self.visit_path(path), + Pattern::Name(name) => self.visit_sym(name), Pattern::Literal(literal) => self.visit_literal(literal), Pattern::Ref(mutability, pattern) => { self.visit_mutability(mutability); @@ -347,7 +347,7 @@ pub trait Visit<'a>: Sized { } fn visit_for(&mut self, f: &'a For) { let For { bind, cond, pass, fail } = f; - self.visit_sym(bind); + self.visit_pattern(bind); self.visit_expr(cond); self.visit_block(pass); self.visit_else(fail); diff --git a/compiler/cl-interpret/src/function/collect_upvars.rs b/compiler/cl-interpret/src/function/collect_upvars.rs index 10c6836..0af1412 100644 --- a/compiler/cl-interpret/src/function/collect_upvars.rs +++ b/compiler/cl-interpret/src/function/collect_upvars.rs @@ -79,7 +79,7 @@ impl<'a> Visit<'a> for CollectUpvars<'_> { let cl_ast::For { bind, cond, pass, fail } = f; self.visit_expr(cond); self.visit_else(fail); - self.bind_name(bind); // TODO: is bind only bound in the pass block? + self.visit_pattern(bind); self.visit_block(pass); } @@ -105,10 +105,8 @@ impl<'a> Visit<'a> for CollectUpvars<'_> { fn visit_pattern(&mut self, p: &'a cl_ast::Pattern) { match p { - Pattern::Path(path) => { - if let [PathPart::Ident(name)] = path.parts.as_slice() { - self.bind_name(name) - } + Pattern::Name(name) => { + self.bind_name(name); } Pattern::Literal(literal) => self.visit_literal(literal), Pattern::Ref(mutability, pattern) => { diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index 7f5a003..d0edc9a 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -266,19 +266,13 @@ impl Interpret for Let { let Let { mutable: _, name, ty: _, init } = self; match init.as_ref().map(|i| i.interpret(env)).transpose()? { Some(value) => { - for (path, value) in assignment::pattern_substitution(name, value)? { - match path.parts.as_slice() { - [PathPart::Ident(name)] => env.insert(*name, Some(value)), - _ => eprintln!("Bad assignment: {path} = {value}"), - } + for (name, value) in assignment::pattern_substitution(name, value)? { + env.insert(*name, Some(value)); } } None => { - for path in assignment::pattern_variables(name) { - match path.parts.as_slice() { - [PathPart::Ident(name)] => env.insert(*name, None), - _ => eprintln!("Bad assignment: {path}"), - } + for name in assignment::pattern_variables(name) { + env.insert(*name, None); } } } @@ -290,13 +284,10 @@ impl Interpret for Match { fn interpret(&self, env: &mut Environment) -> IResult { let Self { scrutinee, arms } = self; let scrutinee = scrutinee.interpret(env)?; - 'arm: for MatchArm(pat, expr) in arms { + for MatchArm(pat, expr) in arms { if let Ok(substitution) = assignment::pattern_substitution(pat, scrutinee.clone()) { let mut env = env.frame("match"); - for (path, value) in substitution { - let [PathPart::Ident(name)] = path.parts.as_slice() else { - continue 'arm; - }; + for (name, value) in substitution { env.insert(*name, Some(value)); } return expr.interpret(&mut env); @@ -313,11 +304,11 @@ mod assignment { type Namespace = HashMap>; /// Gets the path variables in the given Pattern - pub fn pattern_variables(pat: &Pattern) -> Vec<&Path> { - fn patvars<'p>(set: &mut Vec<&'p Path>, pat: &'p Pattern) { + pub fn pattern_variables(pat: &Pattern) -> Vec<&Sym> { + fn patvars<'p>(set: &mut Vec<&'p Sym>, pat: &'p Pattern) { match pat { - Pattern::Path(path) if path.is_sinkhole() => {} - Pattern::Path(path) => set.push(path), + Pattern::Name(name) if &**name == "_" => {} + Pattern::Name(name) => set.push(name), Pattern::Literal(_) => {} Pattern::Ref(_, pattern) => patvars(set, pattern), Pattern::Tuple(patterns) | Pattern::Array(patterns) => { @@ -338,82 +329,71 @@ mod assignment { /// Appends a substitution to the provided table pub fn append_sub<'pat>( - env: &mut HashMap<&'pat Path, ConValue>, + sub: &mut HashMap<&'pat Sym, ConValue>, pat: &'pat Pattern, value: ConValue, ) -> IResult<()> { - match pat { - Pattern::Path(path) if path.is_sinkhole() => Ok(()), - Pattern::Path(path) => { - env.insert(path, value); + match (pat, value) { + (Pattern::Array(patterns), ConValue::Array(values)) + | (Pattern::Tuple(patterns), ConValue::Tuple(values)) => { + if patterns.len() != values.len() { + Err(Error::ArgNumber { want: patterns.len(), got: values.len() })? + } + for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) { + append_sub(sub, pat, value)?; + } + Ok(()) + } + (Pattern::Tuple(patterns), ConValue::Empty) if patterns.is_empty() => Ok(()), + + (Pattern::Literal(Literal::Bool(a)), ConValue::Bool(b)) => { + (*a == b).then_some(()).ok_or(Error::NotAssignable) + } + (Pattern::Literal(Literal::Char(a)), ConValue::Char(b)) => { + (*a == b).then_some(()).ok_or(Error::NotAssignable) + } + (Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => (f64::from_bits(*a) == b) + .then_some(()) + .ok_or(Error::NotAssignable), + (Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => { + (b == *a as _).then_some(()).ok_or(Error::NotAssignable) + } + (Pattern::Literal(Literal::String(a)), ConValue::String(b)) => { + (*a == *b).then_some(()).ok_or(Error::NotAssignable) + } + (Pattern::Literal(_), _) => Err(Error::NotAssignable), + + (Pattern::Name(name), _) if "_".eq(&**name) => Ok(()), + (Pattern::Name(name), value) => { + sub.insert(name, value); Ok(()) } - Pattern::Literal(literal) => match (literal, value) { - (Literal::Bool(a), ConValue::Bool(b)) => *a == b, - (Literal::Char(a), ConValue::Char(b)) => *a == b, - (Literal::Int(a), ConValue::Int(b)) => *a as isize == b, - (Literal::Float(a), ConValue::Float(b)) => f64::from_bits(*a) == b, - (Literal::String(a), ConValue::String(b)) => *a == *b, - _ => false, + (Pattern::Ref(_, pat), ConValue::Ref(r)) => { + append_sub(sub, pat, Rc::unwrap_or_clone(r)) } - .then_some(()) - .ok_or(Error::NotAssignable), - Pattern::Ref(_, pattern) => match value { - ConValue::Ref(value) => append_sub(env, pattern, Rc::unwrap_or_clone(value)), - _ => Err(Error::NotAssignable), - }, - - Pattern::Tuple(patterns) => match value { - ConValue::Tuple(values) => { - if patterns.len() != values.len() { - return Err(Error::OobIndex(patterns.len(), values.len())); - }; - for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) { - append_sub(env, pat, value)?; - } - Ok(()) - } - _ => Err(Error::NotAssignable), - }, - - Pattern::Array(patterns) => match value { - ConValue::Array(values) => { - if patterns.len() != values.len() { - return Err(Error::OobIndex(patterns.len(), values.len())); - }; - for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) { - append_sub(env, pat, value)?; - } - Ok(()) - } - _ => Err(Error::NotAssignable), - }, - - Pattern::Struct(_path, patterns) => { - let ConValue::Struct(parts) = value else { - return Err(Error::TypeError); - }; - let (_, mut values) = *parts; - if values.len() != patterns.len() { + (Pattern::Struct(_path, patterns), ConValue::Struct(parts)) => { + let (_name, mut values) = *parts; + if patterns.len() != values.len() { return Err(Error::TypeError); } for (name, pat) in patterns { - let [.., PathPart::Ident(index)] = name.parts.as_slice() else { - Err(Error::TypeError)? - }; - let value = values.remove(index).ok_or(Error::TypeError)?; + let value = values.remove(name).ok_or(Error::TypeError)?; match pat { - Some(pat) => append_sub(env, pat, value)?, + Some(pat) => append_sub(sub, pat, value)?, None => { - env.insert(name, value); + sub.insert(name, value); } } } - Ok(()) } + + (pat, value) => { + eprintln!("Could not match pattern `{pat}` with value `{value}`!"); + Err(Error::NotAssignable) + } } } @@ -421,10 +401,10 @@ mod assignment { pub fn pattern_substitution( pat: &Pattern, value: ConValue, - ) -> IResult> { - let mut substitution = HashMap::new(); - append_sub(&mut substitution, pat, value)?; - Ok(substitution) + ) -> IResult> { + let mut sub = HashMap::new(); + append_sub(&mut sub, pat, value)?; + Ok(sub) } pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> { @@ -432,7 +412,7 @@ mod assignment { append_sub(&mut substitution, pat, value) .map_err(|_| Error::PatFailed(pat.clone().into()))?; for (path, value) in substitution { - assign_path(env, path, value)?; + env.insert(*path, Some(value)); } Ok(()) } @@ -449,15 +429,6 @@ mod assignment { Ok(()) } - fn assign_path(env: &mut Environment, path: &Path, value: ConValue) -> IResult<()> { - let Ok(addr) = addrof_path(env, &path.parts) else { - eprintln!("Cannot assign {value} to path {path}"); - return Err(Error::NotAssignable); - }; - *addr = Some(value); - Ok(()) - } - pub(super) fn addrof<'e>( env: &'e mut Environment, pat: &ExprKind, @@ -942,7 +913,10 @@ impl Interpret for For { loop { let mut env = env.frame("loop variable"); if let Some(loop_var) = bounds.next() { - env.insert(*name, Some(loop_var)); + let subs = assignment::pattern_substitution(name, loop_var)?; + for (name, value) in subs { + env.insert(*name, Some(value)); + } match pass.interpret(&mut env) { Err(Error::Break(value)) => return Ok(value), Err(Error::Continue) => continue, diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index 9a608d1..d60eff9 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -1037,7 +1037,7 @@ impl Parse<'_> for For { #[rustfmt::skip] fn parse(p: &mut Parser) -> PResult { p.match_type(TokenKind::For, Parsing::For)?; - let bind = Sym::parse(p)?; + let bind = Pattern::parse(p)?; p.match_type(TokenKind::In, Parsing::For)?; Ok(For { bind, diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index 912861a..9c425a1 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -428,7 +428,7 @@ pub mod yamlify { impl Yamlify for Pattern { fn yaml(&self, y: &mut Yamler) { match self { - Pattern::Path(path) => y.value(path), + Pattern::Name(name) => y.value(name), Pattern::Literal(literal) => y.value(literal), Pattern::Ref(mutability, pattern) => { y.pair("mutability", mutability).pair("subpattern", pattern)