conlang: PATTERN MATCHING AND DESTRUCTURED BINDINGS WOOOOO
- Integrate the Match and Pattern nodes into the AST
  - TODO: `let x: T` is ambiguous with `let x: {}`. Currently the latter takes precedence in the parser.
- Implement pattern matching through unification in the interpreter.
  - It's not fast, but it works!
- Refactor destructuring assignments to use the new pattern functionality
			
			
This commit is contained in:
		| @@ -351,6 +351,8 @@ pub enum ExprKind { | ||||
|     Quote(Quote), | ||||
|     /// A local bind instruction, `let` [`Sym`] `=` [`Expr`] | ||||
|     Let(Let), | ||||
|     /// A [Match] expression, `match` [Expr] `{` ([MatchArm] `,`)* [MatchArm]? `}` | ||||
|     Match(Match), | ||||
|     /// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+ | ||||
|     Assign(Assign), | ||||
|     /// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ | ||||
| @@ -408,7 +410,7 @@ pub struct Quote { | ||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||
| pub struct Let { | ||||
|     pub mutable: Mutability, | ||||
|     pub name: Sym, | ||||
|     pub name: Pattern, | ||||
|     pub ty: Option<Box<Ty>>, | ||||
|     pub init: Option<Box<Expr>>, | ||||
| } | ||||
|   | ||||
| @@ -417,6 +417,7 @@ mod display { | ||||
|                 ExprKind::Empty => "()".fmt(f), | ||||
|                 ExprKind::Quote(v) => v.fmt(f), | ||||
|                 ExprKind::Let(v) => v.fmt(f), | ||||
|                 ExprKind::Match(v) => v.fmt(f), | ||||
|                 ExprKind::Assign(v) => v.fmt(f), | ||||
|                 ExprKind::Modify(v) => v.fmt(f), | ||||
|                 ExprKind::Binary(v) => v.fmt(f), | ||||
| @@ -814,6 +815,7 @@ mod convert { | ||||
|         impl From for ExprKind { | ||||
|             Let => ExprKind::Let, | ||||
|             Quote => ExprKind::Quote, | ||||
|             Match => ExprKind::Match, | ||||
|             Assign => ExprKind::Assign, | ||||
|             Modify => ExprKind::Modify, | ||||
|             Binary => ExprKind::Binary, | ||||
|   | ||||
| @@ -240,7 +240,7 @@ pub trait Fold { | ||||
|         let Let { mutable, name, ty, init } = l; | ||||
|         Let { | ||||
|             mutable: self.fold_mutability(mutable), | ||||
|             name: self.fold_sym(name), | ||||
|             name: self.fold_pattern(name), | ||||
|             ty: ty.map(|t| Box::new(self.fold_ty(*t))), | ||||
|             init: init.map(|e| Box::new(self.fold_expr(*e))), | ||||
|         } | ||||
| @@ -572,6 +572,7 @@ pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> Ex | ||||
|         ExprKind::Empty => ExprKind::Empty, | ||||
|         ExprKind::Quote(q) => ExprKind::Quote(q), // quoted expressions are left unmodified | ||||
|         ExprKind::Let(l) => ExprKind::Let(folder.fold_let(l)), | ||||
|         ExprKind::Match(m) => ExprKind::Match(folder.fold_match(m)), | ||||
|         ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)), | ||||
|         ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)), | ||||
|         ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)), | ||||
|   | ||||
| @@ -203,7 +203,7 @@ pub trait Visit<'a>: Sized { | ||||
|     fn visit_let(&mut self, l: &'a Let) { | ||||
|         let Let { mutable, name, ty, init } = l; | ||||
|         self.visit_mutability(mutable); | ||||
|         self.visit_sym(name); | ||||
|         self.visit_pattern(name); | ||||
|         if let Some(ty) = ty { | ||||
|             self.visit_ty(ty); | ||||
|         } | ||||
| @@ -492,6 +492,7 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { | ||||
|         ExprKind::Empty => {} | ||||
|         ExprKind::Quote(_q) => {} // Quoted expressions are left unvisited | ||||
|         ExprKind::Let(l) => visitor.visit_let(l), | ||||
|         ExprKind::Match(m) => visitor.visit_match(m), | ||||
|         ExprKind::Assign(a) => visitor.visit_assign(a), | ||||
|         ExprKind::Modify(m) => visitor.visit_modify(m), | ||||
|         ExprKind::Binary(b) => visitor.visit_binary(b), | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| //! The [Error] type represents any error thrown by the [Environment](super::Environment) | ||||
|  | ||||
| use cl_ast::Sym; | ||||
| use cl_ast::{Pattern, Sym}; | ||||
|  | ||||
| use super::convalue::ConValue; | ||||
|  | ||||
| @@ -39,11 +39,11 @@ pub enum Error { | ||||
|     /// A value was called, but is not callable | ||||
|     NotCallable(ConValue), | ||||
|     /// A function was called with the wrong number of arguments | ||||
|     ArgNumber { | ||||
|         want: usize, | ||||
|         got: usize, | ||||
|     }, | ||||
|     Outlined(Sym), | ||||
|     ArgNumber { want: usize, got: usize }, | ||||
|     /// A pattern failed to match | ||||
|     PatFailed(Pattern), | ||||
|     /// Fell through a non-exhaustive match | ||||
|     MatchNonexhaustive, | ||||
| } | ||||
|  | ||||
| impl std::error::Error for Error {} | ||||
| @@ -83,8 +83,11 @@ impl std::fmt::Display for Error { | ||||
|                     if *want == 1 { "" } else { "s" } | ||||
|                 ) | ||||
|             } | ||||
|             Error::Outlined(name) => { | ||||
|                 write!(f, "Module {name} specified, but not imported.") | ||||
|             Error::PatFailed(pattern) => { | ||||
|                 write!(f, "Failed to match pattern {pattern}") | ||||
|             } | ||||
|             Error::MatchNonexhaustive => { | ||||
|                 write!(f, "Fell through a non-exhaustive match expression!") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| //! Collects the "Upvars" of a function at the point of its creation, allowing variable capture | ||||
| use crate::{convalue::ConValue, env::Environment}; | ||||
| use cl_ast::{ast_visitor::visit::*, Function, Let, Param, Path, PathPart, Sym}; | ||||
| use cl_ast::{ast_visitor::visit::*, Function, Let, Param, Path, PathPart, Pattern, Sym}; | ||||
| use std::collections::{HashMap, HashSet}; | ||||
|  | ||||
| pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars { | ||||
| @@ -61,7 +61,7 @@ impl<'a> Visit<'a> for CollectUpvars<'_> { | ||||
|             self.visit_expr(init) | ||||
|         } | ||||
|         // a bound name can never be an upvar | ||||
|         self.bind_name(name); | ||||
|         self.visit_pattern(name); | ||||
|     } | ||||
|  | ||||
|     fn visit_function(&mut self, f: &'a cl_ast::Function) { | ||||
| @@ -102,4 +102,33 @@ impl<'a> Visit<'a> for CollectUpvars<'_> { | ||||
|             self.add_upvar(name); // fielder without init grabs from env | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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::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); | ||||
|                     }); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -75,11 +75,11 @@ impl Interpret for Module { | ||||
|         let out = match kind { | ||||
|             ModuleKind::Inline(file) => file.interpret(env), | ||||
|             ModuleKind::Outline => { | ||||
|                 eprintln!("{}", Error::Outlined(*name)); | ||||
|                 eprintln!("Module {name} specified, but not imported."); | ||||
|                 Ok(ConValue::Empty) | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|  | ||||
|         let frame = env | ||||
|             .pop_frame() | ||||
|             .expect("Environment frames must be balanced"); | ||||
| @@ -128,14 +128,7 @@ impl Interpret for Stmt { | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| impl Interpret for Let { | ||||
|     fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { | ||||
|         let Let { mutable: _, name, ty: _, init } = self; | ||||
|         let init = init.as_ref().map(|i| i.interpret(env)).transpose()?; | ||||
|         env.insert(*name, init); | ||||
|         Ok(ConValue::Empty) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Interpret for Expr { | ||||
|     #[inline] | ||||
|     fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { | ||||
| @@ -143,12 +136,14 @@ impl Interpret for Expr { | ||||
|         kind.interpret(env) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Interpret for ExprKind { | ||||
|     fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { | ||||
|         match self { | ||||
|             ExprKind::Empty => Ok(ConValue::Empty), | ||||
|             ExprKind::Quote(q) => q.interpret(env), | ||||
|             ExprKind::Let(v) => v.interpret(env), | ||||
|             ExprKind::Match(v) => v.interpret(env), | ||||
|             ExprKind::Assign(v) => v.interpret(env), | ||||
|             ExprKind::Modify(v) => v.interpret(env), | ||||
|             ExprKind::Binary(v) => v.interpret(env), | ||||
| @@ -182,38 +177,190 @@ impl Interpret for Quote { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Interpret for Let { | ||||
|     fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { | ||||
|         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}"), | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             None => { | ||||
|                 for path in assignment::pattern_variables(name) { | ||||
|                     match path.parts.as_slice() { | ||||
|                         [PathPart::Ident(name)] => env.insert(*name, None), | ||||
|                         _ => eprintln!("Bad assignment: {path}"), | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Ok(ConValue::Empty) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Interpret for Match { | ||||
|     fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { | ||||
|         let Self { scrutinee, arms } = self; | ||||
|         let scrutinee = scrutinee.interpret(env)?; | ||||
|         'arm: 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; | ||||
|                     }; | ||||
|                     env.insert(*name, Some(value)); | ||||
|                 } | ||||
|                 return expr.interpret(&mut env); | ||||
|             } | ||||
|         } | ||||
|         Err(Error::MatchNonexhaustive) | ||||
|     } | ||||
| } | ||||
|  | ||||
| mod assignment { | ||||
|     /// Pattern matching engine for assignment | ||||
|     use super::*; | ||||
|     use std::collections::HashMap; | ||||
|     type Namespace = HashMap<Sym, Option<ConValue>>; | ||||
|  | ||||
|     pub(super) fn assign(env: &mut Environment, pat: &ExprKind, value: ConValue) -> IResult<()> { | ||||
|         match (pat, value) { | ||||
|             (ExprKind::Empty, ConValue::Empty) => Ok(()), | ||||
|             (ExprKind::Path(path), value) => assign_path(env, path, value), | ||||
|             (ExprKind::Group(Group { expr }), value) => assign(env, expr, value), | ||||
|             (ExprKind::Tuple(tuple), value) => assign_destructure_tuple(env, tuple, value), | ||||
|             (ExprKind::Array(array), value) => assign_destructure_array(env, array, value), | ||||
|             (ExprKind::Index(index), value) => assign_index(env, index, value), | ||||
|             (ExprKind::Member(member), value) => assign_member(env, member, value), | ||||
|             (ExprKind::Structor(structor), value) => { | ||||
|                 assign_destructure_struct(env, structor, value) | ||||
|     /// 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) { | ||||
|             match pat { | ||||
|                 Pattern::Path(path) if path.is_sinkhole() => {} | ||||
|                 Pattern::Path(path) => set.push(path), | ||||
|                 Pattern::Literal(_) => {} | ||||
|                 Pattern::Ref(_, pattern) => patvars(set, pattern), | ||||
|                 Pattern::Tuple(patterns) | Pattern::Array(patterns) => { | ||||
|                     patterns.iter().for_each(|pat| patvars(set, pat)) | ||||
|                 } | ||||
|                 Pattern::Struct(_path, items) => { | ||||
|                     items.iter().for_each(|(name, pat)| match pat { | ||||
|                         Some(pat) => patvars(set, pat), | ||||
|                         None => set.push(name), | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|             _ => { | ||||
|                 eprintln!("{pat} is not a valid pattern expression"); | ||||
|                 Err(Error::NotAssignable) | ||||
|         } | ||||
|         let mut set = Vec::new(); | ||||
|         patvars(&mut set, pat); | ||||
|         set | ||||
|     } | ||||
|  | ||||
|     /// Appends a substitution to the provided table | ||||
|     pub fn append_sub<'pat>( | ||||
|         env: &mut HashMap<&'pat Path, ConValue>, | ||||
|         pat: &'pat Pattern, | ||||
|         value: ConValue, | ||||
|     ) -> IResult<()> { | ||||
|         match pat { | ||||
|             Pattern::Path(path) if path.is_sinkhole() => Ok(()), | ||||
|             Pattern::Path(path) => { | ||||
|                 env.insert(path, 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, | ||||
|             } | ||||
|             .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() { | ||||
|                     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)?; | ||||
|                     match pat { | ||||
|                         Some(pat) => append_sub(env, pat, value)?, | ||||
|                         None => { | ||||
|                             env.insert(name, value); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 Ok(()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn assign_member(env: &mut Environment, member: &Member, value: ConValue) -> IResult<()> { | ||||
|         *addrof_member(env, member)? = value; | ||||
|     /// Constructs a substitution from a pattern and a value | ||||
|     pub fn pattern_substitution( | ||||
|         pat: &Pattern, | ||||
|         value: ConValue, | ||||
|     ) -> IResult<HashMap<&Path, ConValue>> { | ||||
|         let mut substitution = HashMap::new(); | ||||
|         append_sub(&mut substitution, pat, value)?; | ||||
|         Ok(substitution) | ||||
|     } | ||||
|  | ||||
|     pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> { | ||||
|         let mut substitution = HashMap::new(); | ||||
|         append_sub(&mut substitution, pat, value).map_err(|_| Error::PatFailed(pat.clone()))?; | ||||
|         for (path, value) in substitution { | ||||
|             assign_path(env, path, value)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn assign_index(env: &mut Environment, index: &Index, value: ConValue) -> IResult<()> { | ||||
|         *addrof_index(env, index)? = value; | ||||
|     pub(super) fn assign(env: &mut Environment, pat: &ExprKind, value: ConValue) -> IResult<()> { | ||||
|         if let Ok(pat) = Pattern::try_from(pat.clone()) { | ||||
|             return pat_assign(env, &pat, value); | ||||
|         } | ||||
|         match pat { | ||||
|             ExprKind::Member(member) => *addrof_member(env, member)? = value, | ||||
|             ExprKind::Index(index) => *addrof_index(env, index)? = value, | ||||
|             _ => Err(Error::NotAssignable)?, | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| @@ -226,66 +373,6 @@ mod assignment { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn assign_destructure_array( | ||||
|         env: &mut Environment, | ||||
|         array: &Array, | ||||
|         value: ConValue, | ||||
|     ) -> IResult<()> { | ||||
|         let Array { values } = array; | ||||
|         let ConValue::Array(inits) = &value else { | ||||
|             eprintln!("{value} does not match pattern {array}"); | ||||
|             return Err(Error::TypeError); | ||||
|         }; | ||||
|         if values.len() != inits.len() { | ||||
|             return Err(Error::TypeError); | ||||
|         } | ||||
|  | ||||
|         for (init, expr) in inits.iter().zip(values) { | ||||
|             assign(env, &expr.kind, init.clone())?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn assign_destructure_tuple( | ||||
|         env: &mut Environment, | ||||
|         tuple: &Tuple, | ||||
|         value: ConValue, | ||||
|     ) -> IResult<()> { | ||||
|         let Tuple { exprs } = tuple; | ||||
|         let ConValue::Tuple(inits) = &value else { | ||||
|             eprintln!("{value} does not match pattern {tuple}"); | ||||
|             return Err(Error::TypeError); | ||||
|         }; | ||||
|         if exprs.len() != inits.len() { | ||||
|             return Err(Error::TypeError); | ||||
|         } | ||||
|  | ||||
|         for (init, expr) in inits.iter().zip(exprs) { | ||||
|             assign(env, &expr.kind, init.clone())?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn assign_destructure_struct( | ||||
|         env: &mut Environment, | ||||
|         pat: &Structor, | ||||
|         value: ConValue, | ||||
|     ) -> IResult<()> { | ||||
|         let Structor { to: _, init: pat } = pat; | ||||
|         let ConValue::Struct(parts) = value else { | ||||
|             return Err(Error::TypeError); | ||||
|         }; | ||||
|         let (_, members) = *parts; | ||||
|         for Fielder { name, init: pat } in pat { | ||||
|             let value = members.get(name).ok_or(Error::NotDefined(*name))?; | ||||
|             match pat { | ||||
|                 Some(pat) => assign(env, &pat.kind, value.clone())?, | ||||
|                 None => *env.get_mut(*name)? = Some(value.clone()), | ||||
|             } | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub(super) fn addrof<'e>( | ||||
|         env: &'e mut Environment, | ||||
|         pat: &ExprKind, | ||||
| @@ -297,6 +384,7 @@ mod assignment { | ||||
|             ExprKind::Member(member) => addrof_member(env, member), | ||||
|             ExprKind::Index(index) => addrof_index(env, index), | ||||
|             ExprKind::Group(Group { expr }) => addrof(env, expr), | ||||
|             ExprKind::AddrOf(AddrOf { mutable: Mutability::Mut, expr }) => addrof(env, expr), | ||||
|             _ => Err(Error::TypeError), | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -119,6 +119,10 @@ pub enum Parsing { | ||||
|     Break, | ||||
|     Return, | ||||
|     Continue, | ||||
|  | ||||
|     Pattern, | ||||
|     Match, | ||||
|     MatchArm, | ||||
| } | ||||
|  | ||||
| impl Display for Error { | ||||
| @@ -225,6 +229,10 @@ impl Display for Parsing { | ||||
|             Parsing::Break => "a break expression", | ||||
|             Parsing::Return => "a return expression", | ||||
|             Parsing::Continue => "a continue expression", | ||||
|  | ||||
|             Parsing::Pattern => "a pattern", | ||||
|             Parsing::Match => "a match expression", | ||||
|             Parsing::MatchArm => "a match arm", | ||||
|         } | ||||
|         .fmt(f) | ||||
|     } | ||||
|   | ||||
| @@ -917,7 +917,7 @@ impl Parse<'_> for Let { | ||||
|         p.consume_peeked(); | ||||
|         Ok(Let { | ||||
|             mutable: Mutability::parse(p)?, | ||||
|             name: Sym::parse(p)?, | ||||
|             name: Pattern::parse(p)?, | ||||
|             ty: if p.match_type(TokenKind::Colon, Parsing::Let).is_ok() { | ||||
|                 Some(Ty::parse(p)?.into()) | ||||
|             } else { | ||||
| @@ -1075,6 +1075,38 @@ impl Parse<'_> for Return { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Parse<'_> for Pattern { | ||||
|     fn parse(p: &mut Parser<'_>) -> PResult<Self> { | ||||
|         let value = prec::exprkind(p, prec::Precedence::Highest.level())?; | ||||
|         Pattern::try_from(value) | ||||
|             .map_err(|_| p.error(ExpectedParsing { want: Parsing::Pattern }, Parsing::Pattern)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Parse<'_> for Match { | ||||
|     /// [Match] = `match` [Expr] `{` [MatchArm],* `}` | ||||
|     fn parse(p: &mut Parser<'_>) -> PResult<Self> { | ||||
|         p.match_type(TokenKind::Match, Parsing::Match)?; | ||||
|         let scrutinee = Expr::parse(p)?.into(); | ||||
|         let arms = delim( | ||||
|             sep(MatchArm::parse, TokenKind::Comma, CURLIES.1, Parsing::Match), | ||||
|             CURLIES, | ||||
|             Parsing::Match, | ||||
|         )(p)?; | ||||
|         Ok(Match { scrutinee, arms }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Parse<'_> for MatchArm { | ||||
|     /// [MatchArm] = [Pattern] `=>` [Expr] | ||||
|     fn parse(p: &mut Parser<'_>) -> PResult<Self> { | ||||
|         let pat = Pattern::parse(p)?; | ||||
|         p.match_type(TokenKind::FatArrow, Parsing::MatchArm)?; | ||||
|         let expr = Expr::parse(p)?; | ||||
|         Ok(MatchArm(pat, expr)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// ret_body = (*unconsumed* `;` | [Expr]) | ||||
| fn ret_body(p: &mut Parser, while_parsing: Parsing) -> PResult<Option<Box<Expr>>> { | ||||
|     Ok(match p.peek_kind(while_parsing)? { | ||||
|   | ||||
| @@ -21,6 +21,7 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> { | ||||
|         TokenKind::LBrack => exprkind_arraylike(p)?, | ||||
|         TokenKind::LParen => exprkind_tuplelike(p)?, | ||||
|         TokenKind::Let => Let::parse(p)?.into(), | ||||
|         TokenKind::Match => Match::parse(p)?.into(), | ||||
|         TokenKind::While => ExprKind::While(While::parse(p)?), | ||||
|         TokenKind::If => ExprKind::If(If::parse(p)?), | ||||
|         TokenKind::For => ExprKind::For(For::parse(p)?), | ||||
| @@ -32,8 +33,7 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> { | ||||
|         } | ||||
|  | ||||
|         op => { | ||||
|             let (kind, prec) = | ||||
|                 from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?; | ||||
|             let (kind, prec) = from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?; | ||||
|             let ((), after) = prec.prefix().expect("should have a precedence"); | ||||
|             p.consume_peeked(); | ||||
|             Unary { kind, tail: exprkind(p, after)?.into() }.into() | ||||
| @@ -65,14 +65,10 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> { | ||||
|                     ExprKind::Index(Index { head: head.into(), indices }) | ||||
|                 } | ||||
|                 TokenKind::LParen => { | ||||
|                     let exprs = | ||||
|                         sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?; | ||||
|                     let exprs = sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?; | ||||
|                     p.match_type(TokenKind::RParen, parsing)?; | ||||
|                     Binary { | ||||
|                         kind: BinaryKind::Call, | ||||
|                         parts: (head, Tuple { exprs }.into()).into(), | ||||
|                     } | ||||
|                     .into() | ||||
|                     Binary { kind: BinaryKind::Call, parts: (head, Tuple { exprs }.into()).into() } | ||||
|                         .into() | ||||
|                 } | ||||
|                 TokenKind::Dot => { | ||||
|                     let kind = MemberKind::parse(p)?; | ||||
| @@ -260,6 +256,7 @@ pub enum Precedence { | ||||
|     Cast, | ||||
|     Member, // left-associative | ||||
|     Call, | ||||
|     Highest, | ||||
| } | ||||
|  | ||||
| impl Precedence { | ||||
| @@ -310,9 +307,7 @@ impl From<BinaryKind> for Precedence { | ||||
|             Op::BitAnd | Op::BitOr | Op::BitXor => Precedence::Bitwise, | ||||
|             Op::LogAnd | Op::LogOr | Op::LogXor => Precedence::Logic, | ||||
|             Op::RangeExc | Op::RangeInc => Precedence::Range, | ||||
|             Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => { | ||||
|                 Precedence::Compare | ||||
|             } | ||||
|             Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => Precedence::Compare, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -380,6 +380,7 @@ pub mod yamlify { | ||||
|             match self { | ||||
|                 ExprKind::Quote(k) => k.yaml(y), | ||||
|                 ExprKind::Let(k) => k.yaml(y), | ||||
|                 ExprKind::Match(k) => k.yaml(y), | ||||
|                 ExprKind::Assign(k) => k.yaml(y), | ||||
|                 ExprKind::Modify(k) => k.yaml(y), | ||||
|                 ExprKind::Binary(k) => k.yaml(y), | ||||
|   | ||||
| @@ -28,6 +28,7 @@ pub enum TokenKind { | ||||
|     In,       // "in" | ||||
|     Let,      // "let" | ||||
|     Loop,     // "loop" | ||||
|     Match,    // "match" | ||||
|     Mod,      // "mod" | ||||
|     Mut,      // "mut" | ||||
|     Pub,      // "pub" | ||||
| @@ -121,6 +122,7 @@ impl Display for TokenKind { | ||||
|             TokenKind::In => "in".fmt(f), | ||||
|             TokenKind::Let => "let".fmt(f), | ||||
|             TokenKind::Loop => "loop".fmt(f), | ||||
|             TokenKind::Match => "match".fmt(f), | ||||
|             TokenKind::Mod => "mod".fmt(f), | ||||
|             TokenKind::Mut => "mut".fmt(f), | ||||
|             TokenKind::Pub => "pub".fmt(f), | ||||
| @@ -213,6 +215,7 @@ impl FromStr for TokenKind { | ||||
|             "in" => Self::In, | ||||
|             "let" => Self::Let, | ||||
|             "loop" => Self::Loop, | ||||
|             "match" => Self::Match, | ||||
|             "mod" => Self::Mod, | ||||
|             "mut" => Self::Mut, | ||||
|             "pub" => Self::Pub, | ||||
|   | ||||
| @@ -32,7 +32,7 @@ impl Source<'_> { | ||||
|             Source::Const(v) => Some(v.name), | ||||
|             Source::Static(v) => Some(v.name), | ||||
|             Source::Function(v) => Some(v.name), | ||||
|             Source::Local(l) => Some(l.name), | ||||
|             Source::Local(_) => None, | ||||
|             Source::Impl(_) | Source::Use(_) | Source::Ty(_) => None, | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -146,11 +146,11 @@ impl<'a> Visit<'a> for Populator<'_, 'a> { | ||||
|     } | ||||
|  | ||||
|     fn visit_let(&mut self, l: &'a cl_ast::Let) { | ||||
|         let cl_ast::Let { mutable, name, ty, init } = l; | ||||
|         let cl_ast::Let { mutable, name: _, ty, init } = l; | ||||
|         let mut entry = self.new_entry(NodeKind::Local); | ||||
|  | ||||
|         entry.inner.set_source(Source::Local(l)); | ||||
|         entry.set_name(*name); | ||||
|         // entry.set_name(*name); | ||||
|  | ||||
|         entry.visit_mutability(mutable); | ||||
|         if let Some(ty) = ty { | ||||
| @@ -160,7 +160,8 @@ impl<'a> Visit<'a> for Populator<'_, 'a> { | ||||
|             entry.visit_expr(init) | ||||
|         } | ||||
|  | ||||
|         let child = entry.inner.id(); | ||||
|         self.inner.add_child(*name, child); | ||||
|         // let child = entry.inner.id(); | ||||
|         // self.inner.add_child(*name, child); | ||||
|         todo!("Pattern destructuring in cl-typeck") | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user