2021-07-03 07:03:52 +00:00
|
|
|
use crate::schemas::dataspace_patterns::*;
|
|
|
|
|
2021-12-13 14:43:24 +00:00
|
|
|
use super::language;
|
2021-07-03 07:03:52 +00:00
|
|
|
|
2021-12-13 14:43:24 +00:00
|
|
|
use preserves::value::Map;
|
|
|
|
use preserves::value::NestedValue;
|
|
|
|
use preserves::value::Record;
|
|
|
|
use preserves::value::Value;
|
|
|
|
use preserves_schema::Codec;
|
2021-07-03 07:03:52 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
|
|
|
|
pub enum PathStep {
|
|
|
|
Index(usize),
|
2021-07-15 07:13:31 +00:00
|
|
|
Key(_Any),
|
2021-07-03 07:03:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub type Path = Vec<PathStep>;
|
|
|
|
pub type Paths = Vec<Path>;
|
|
|
|
|
|
|
|
struct Analyzer {
|
|
|
|
pub const_paths: Paths,
|
|
|
|
pub const_values: Vec<_Any>,
|
|
|
|
pub capture_paths: Paths,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PatternAnalysis {
|
|
|
|
pub const_paths: Paths,
|
|
|
|
pub const_values: _Any,
|
|
|
|
pub capture_paths: Paths,
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:43:24 +00:00
|
|
|
struct PatternMatcher {
|
|
|
|
captures: Vec<_Any>,
|
2021-10-07 15:00:04 +00:00
|
|
|
}
|
|
|
|
|
2021-07-03 07:03:52 +00:00
|
|
|
impl PatternAnalysis {
|
|
|
|
pub fn new(p: &Pattern) -> Self {
|
|
|
|
let mut analyzer = Analyzer {
|
|
|
|
const_paths: Vec::new(),
|
|
|
|
const_values: Vec::new(),
|
|
|
|
capture_paths: Vec::new(),
|
|
|
|
};
|
|
|
|
analyzer.walk(&mut Vec::new(), p);
|
|
|
|
PatternAnalysis {
|
|
|
|
const_paths: analyzer.const_paths,
|
|
|
|
const_values: _Any::new(analyzer.const_values),
|
|
|
|
capture_paths: analyzer.capture_paths,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Analyzer {
|
|
|
|
fn walk_step(&mut self, path: &mut Path, s: PathStep, p: &Pattern) {
|
|
|
|
path.push(s);
|
|
|
|
self.walk(path, p);
|
|
|
|
path.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk(&mut self, path: &mut Path, p: &Pattern) {
|
|
|
|
match p {
|
|
|
|
Pattern::DCompound(b) => match &**b {
|
2021-12-13 14:43:24 +00:00
|
|
|
DCompound::Rec { fields, .. } => {
|
|
|
|
for (i, p) in fields.iter().enumerate() {
|
|
|
|
self.walk_step(path, PathStep::Index(i), p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DCompound::Arr { items, .. } => {
|
|
|
|
for (i, p) in items.iter().enumerate() {
|
|
|
|
self.walk_step(path, PathStep::Index(i), p);
|
2021-07-03 07:03:52 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-13 14:43:24 +00:00
|
|
|
DCompound::Dict { entries, .. } => {
|
|
|
|
for (k, p) in entries {
|
2021-07-03 07:03:52 +00:00
|
|
|
self.walk_step(path, PathStep::Key(k.clone()), p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Pattern::DBind(b) => {
|
|
|
|
let DBind { pattern, .. } = &**b;
|
|
|
|
self.capture_paths.push(path.clone());
|
|
|
|
self.walk(path, pattern)
|
|
|
|
}
|
|
|
|
Pattern::DDiscard(_) =>
|
|
|
|
(),
|
|
|
|
Pattern::DLit(b) => {
|
|
|
|
let DLit { value } = &**b;
|
|
|
|
self.const_paths.push(path.clone());
|
2021-12-13 14:43:24 +00:00
|
|
|
self.const_values.push(language().unparse(value));
|
2021-07-03 07:03:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-07 15:00:04 +00:00
|
|
|
|
2021-12-13 14:43:24 +00:00
|
|
|
impl Pattern<_Any> {
|
|
|
|
pub fn match_value(&self, value: &_Any) -> Option<Vec<_Any>> {
|
2021-10-07 15:00:04 +00:00
|
|
|
let mut matcher = PatternMatcher::new();
|
|
|
|
if matcher.run(self, value) {
|
|
|
|
Some(matcher.captures)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:43:24 +00:00
|
|
|
impl PatternMatcher {
|
2021-10-07 15:00:04 +00:00
|
|
|
fn new() -> Self {
|
|
|
|
PatternMatcher {
|
|
|
|
captures: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:43:24 +00:00
|
|
|
fn run(&mut self, pattern: &Pattern<_Any>, value: &_Any) -> bool {
|
2021-10-07 15:00:04 +00:00
|
|
|
match pattern {
|
|
|
|
Pattern::DDiscard(_) => true,
|
|
|
|
Pattern::DBind(b) => {
|
|
|
|
self.captures.push(value.clone());
|
|
|
|
self.run(&b.pattern, value)
|
|
|
|
}
|
2021-12-13 14:43:24 +00:00
|
|
|
Pattern::DLit(b) => value == &language().unparse(&b.value),
|
2021-10-07 15:00:04 +00:00
|
|
|
Pattern::DCompound(b) => match &**b {
|
2021-12-13 14:43:24 +00:00
|
|
|
DCompound::Rec { label, fields } => {
|
|
|
|
match value.value().as_record(Some(fields.len())) {
|
2021-10-07 15:00:04 +00:00
|
|
|
None => false,
|
|
|
|
Some(r) => {
|
2021-12-13 14:43:24 +00:00
|
|
|
if r.label() != label {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (i, p) in fields.iter().enumerate() {
|
2021-10-07 15:00:04 +00:00
|
|
|
if !self.run(p, &r.fields()[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-13 14:43:24 +00:00
|
|
|
DCompound::Arr { items } => {
|
2021-10-07 15:00:04 +00:00
|
|
|
match value.value().as_sequence() {
|
|
|
|
None => false,
|
|
|
|
Some(vs) => {
|
2021-12-13 14:43:24 +00:00
|
|
|
if vs.len() != items.len() {
|
2021-10-07 15:00:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-12-13 14:43:24 +00:00
|
|
|
for (i, p) in items.iter().enumerate() {
|
2021-10-07 15:00:04 +00:00
|
|
|
if !self.run(p, &vs[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-13 14:43:24 +00:00
|
|
|
DCompound::Dict { entries: expected_entries } => {
|
2021-10-07 15:00:04 +00:00
|
|
|
match value.value().as_dictionary() {
|
|
|
|
None => false,
|
2021-12-13 14:43:24 +00:00
|
|
|
Some(actual_entries) => {
|
|
|
|
for (k, p) in expected_entries.iter() {
|
|
|
|
if !actual_entries.get(k).map(|v| self.run(p, v)).unwrap_or(false) {
|
2021-10-07 15:00:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-13 14:43:24 +00:00
|
|
|
|
|
|
|
pub fn lift_literal(v: &_Any) -> Pattern {
|
|
|
|
match v.value() {
|
|
|
|
Value::Record(r) => Pattern::DCompound(Box::new(DCompound::Rec {
|
|
|
|
label: r.label().clone(),
|
|
|
|
fields: r.fields().iter().map(lift_literal).collect(),
|
|
|
|
})),
|
|
|
|
Value::Sequence(items) => Pattern::DCompound(Box::new(DCompound::Arr {
|
|
|
|
items: items.iter().map(lift_literal).collect(),
|
|
|
|
})),
|
|
|
|
Value::Set(_members) => panic!("Cannot express literal set in pattern"),
|
|
|
|
Value::Dictionary(entries) => Pattern::DCompound(Box::new(DCompound::Dict {
|
|
|
|
entries: entries.iter().map(|(k, v)| (k.clone(), lift_literal(v))).collect(),
|
|
|
|
})),
|
|
|
|
_other => Pattern::DLit(Box::new(DLit {
|
|
|
|
value: language().parse(v).expect("Non-compound datum can be converted to AnyAtom"),
|
|
|
|
})),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn drop_literal(p: &Pattern) -> Option<_Any> {
|
|
|
|
match p {
|
|
|
|
Pattern::DCompound(b) => match &**b {
|
|
|
|
DCompound::Rec { label, fields } => {
|
|
|
|
let mut r = vec![label.clone()];
|
|
|
|
for f in fields.iter() {
|
|
|
|
r.push(drop_literal(f)?);
|
|
|
|
}
|
|
|
|
Some(Value::Record(Record(r)).wrap())
|
|
|
|
}
|
|
|
|
DCompound::Arr { items } =>
|
|
|
|
Some(Value::Sequence(items.iter().map(drop_literal)
|
|
|
|
.collect::<Option<Vec<_Any>>>()?).wrap()),
|
|
|
|
DCompound::Dict { entries } =>
|
|
|
|
Some(Value::Dictionary(entries.iter()
|
|
|
|
.map(|(k, p)| Some((k.clone(), drop_literal(p)?)))
|
|
|
|
.collect::<Option<Map<_Any, _Any>>>()?).wrap()),
|
|
|
|
},
|
|
|
|
Pattern::DLit(b) => Some(language().unparse(&b.value)),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|