From 6e00f260a8f291af5fcba0365179afb00d3edd2c Mon Sep 17 00:00:00 2001 From: mehbark Date: Tue, 13 Jun 2023 22:51:51 -0400 Subject: [PATCH] i did actually pattern matching holy crap --- src/main.rs | 98 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4ef5371..f544fc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use core::fmt; use std::{ - collections::HashSet, + collections::{HashMap, HashSet}, env, io::{self, Read}, iter, @@ -436,6 +436,23 @@ impl Rule { rhs: to.clone(), } } + + #[must_use] + fn concrete_with_matches(&self, matches: &Matches<'_>) -> Rule { + match self { + Rule::Forall { vars, .. } => { + let mut out = self.clone(); + + for var in vars { + let expr = matches.get(var).copied().cloned().unwrap_or_else(Sexp::nil); + out = out.concrify(&expr); + } + + out + } + Rule::Concrete { .. } => self.clone(), + } + } } impl fmt::Display for Rule { @@ -473,45 +490,76 @@ fn could_match(vars: Option<&[Symbol]>, lhs: &Sexp, expr: &Sexp) -> bool { } } -// type Matches<'src> = HashMap>; +type Matches<'src> = HashMap; -// TODO: there. can. be. at most. one. match. +// DONE?: there. can. be. at most. one. match. // i'm happy that this is faster, but it might be worth going back to the per-variable thing -fn matches<'src>(vars: &HashSet, lhs: &Sexp, expr: &'src Sexp) -> HashSet<&'src Sexp> { +fn matches<'src>(vars: &[Symbol], lhs: &Sexp, expr: &'src Sexp) -> Option> { match (lhs, expr) { (Sexp::Atom(a), Sexp::Atom(b)) => { - if a == b || vars.contains(a) { - let mut out = HashSet::with_capacity(1); - out.insert(expr); - out + if a == b { + Some(HashMap::with_capacity(0)) + } else if vars.contains(a) { + let mut out = HashMap::with_capacity(1); + out.insert(*a, expr); + Some(out) } else { - HashSet::with_capacity(0) + None } } (Sexp::Atom(a), Sexp::List(_)) => { if vars.contains(a) { - let mut out = HashSet::with_capacity(1); - out.insert(expr); - out + let mut out = HashMap::with_capacity(1); + out.insert(*a, expr); + Some(out) } else { - HashSet::with_capacity(0) + None } } - (Sexp::List(_), Sexp::Atom(_)) => HashSet::with_capacity(0), + (Sexp::List(_), Sexp::Atom(_)) => None, (Sexp::List(xs), Sexp::List(ys)) => { if xs.len() == ys.len() { xs.iter() .zip(ys) .map(|(lhs, expr)| matches(vars, lhs, expr)) - .reduce(|a, b| a.union(&b).copied().collect()) - .unwrap_or(HashSet::with_capacity(0)) + .reduce(|a, b| match (a, b) { + (None, _) => None, + (_, None) => None, + (Some(a), Some(b)) => merge_matches(vars, a, b), + }) + .unwrap_or(None) } else { - HashSet::with_capacity(0) + None } } } } +fn merge_matches<'src>( + vars: &[Symbol], + a: Matches<'src>, + b: Matches<'src>, +) -> Option> { + let mut out = HashMap::with_capacity(vars.len()); + + for var in vars { + match (a.get(var), b.get(var)) { + (None, None) => {} + (Some(m), None) | (None, Some(m)) => { + out.insert(*var, *m); + } + (Some(a), Some(b)) => { + if a != b { + return None; + } + out.insert(*var, *a); + } + } + } + + Some(out) +} + // fn absorb_matches<'src>(from: Matches<'src>, into: &mut Matches<'src>) { // for (var, matches) in from { // let insert_into = into.entry(var).or_default(); @@ -527,19 +575,9 @@ fn rw(rule: &Rule, sexp: &Sexp) -> Sexp { } match rule { - rule @ Rule::Forall { vars, lhs, .. } => { - if could_match(Some(vars), lhs, sexp) { - // let targets = sexp.concretion_targets(); - let targets = matches(&vars.iter().copied().collect(), lhs, sexp); - let mut out = sexp.clone(); - for rule in rule.concretions(&targets) { - let rwed = rw(&rule, &out); - if rwed != out { - out = rwed; - break; - } - } - out + Rule::Forall { vars, lhs, .. } => { + if let Some(matc) = matches(vars, lhs, sexp) { + rule.concrete_with_matches(&matc).rw(sexp) } else { match sexp { Sexp::Atom(_) => sexp.clone(),