228 lines
7.0 KiB
Rust
228 lines
7.0 KiB
Rust
use std::sync::Arc;
|
|
|
|
use crate::schemas::dataspace_patterns::*;
|
|
|
|
use super::language;
|
|
|
|
use preserves::value::Map;
|
|
use preserves::value::NestedValue;
|
|
use preserves::value::Record;
|
|
use preserves::value::Value;
|
|
use preserves_schema::Codec;
|
|
|
|
pub type PathStep = _Any;
|
|
pub type Path = Vec<PathStep>;
|
|
pub type Paths = Vec<Path>;
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct ConstantPositions {
|
|
pub with_values: Paths,
|
|
pub required_to_exist: Paths,
|
|
}
|
|
|
|
struct Analyzer {
|
|
pub const_paths: Paths,
|
|
pub const_values: Vec<_Any>,
|
|
pub checked_paths: Paths,
|
|
pub capture_paths: Paths,
|
|
}
|
|
|
|
pub struct PatternAnalysis {
|
|
pub const_positions: Arc<ConstantPositions>,
|
|
pub const_values: _Any,
|
|
pub capture_paths: Paths,
|
|
}
|
|
|
|
struct PatternMatcher {
|
|
captures: Vec<_Any>,
|
|
}
|
|
|
|
impl PatternAnalysis {
|
|
pub fn new(p: &Pattern) -> Self {
|
|
let mut analyzer = Analyzer {
|
|
const_paths: Vec::new(),
|
|
const_values: Vec::new(),
|
|
checked_paths: Vec::new(),
|
|
capture_paths: Vec::new(),
|
|
};
|
|
analyzer.walk(&mut Vec::new(), p);
|
|
PatternAnalysis {
|
|
const_positions: Arc::new(ConstantPositions {
|
|
with_values: analyzer.const_paths,
|
|
required_to_exist: analyzer.checked_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::Group { entries, .. } => {
|
|
for (k, p) in entries {
|
|
self.walk_step(path, k.clone(), p)
|
|
}
|
|
}
|
|
Pattern::Bind { pattern } => {
|
|
self.capture_paths.push(path.clone());
|
|
self.walk(path, &**pattern);
|
|
}
|
|
Pattern::Discard => {
|
|
self.checked_paths.push(path.clone());
|
|
}
|
|
Pattern::Lit { value } => {
|
|
self.const_paths.push(path.clone());
|
|
self.const_values.push(language().unparse(&**value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Pattern<_Any> {
|
|
pub fn match_value(&self, value: &_Any) -> Option<Vec<_Any>> {
|
|
let mut matcher = PatternMatcher::new();
|
|
if matcher.run(self, value) {
|
|
Some(matcher.captures)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PatternMatcher {
|
|
fn new() -> Self {
|
|
PatternMatcher {
|
|
captures: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn run_seq<'a, F: 'a + Fn(usize) -> &'a _Any>(&mut self, entries: &Map<_Any, Pattern<_Any>>, values: F) -> bool {
|
|
for (k, p) in entries {
|
|
match k.value().as_usize() {
|
|
None => return false,
|
|
Some(i) => if !self.run(p, values(i)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
fn run(&mut self, pattern: &Pattern<_Any>, value: &_Any) -> bool {
|
|
match pattern {
|
|
Pattern::Discard => true,
|
|
Pattern::Bind { pattern } => {
|
|
self.captures.push(value.clone());
|
|
self.run(&**pattern, value)
|
|
}
|
|
Pattern::Lit { value: expected } => value == &language().unparse(&**expected),
|
|
Pattern::Group { type_, entries } => match &**type_ {
|
|
GroupType::Rec { label } => {
|
|
match value.value().as_record(None) {
|
|
None => false,
|
|
Some(r) =>
|
|
r.label() == label &&
|
|
self.run_seq(entries, |i| &r.fields()[i])
|
|
}
|
|
}
|
|
GroupType::Arr => {
|
|
match value.value().as_sequence() {
|
|
None => false,
|
|
Some(vs) =>
|
|
self.run_seq(entries, |i| &vs[i])
|
|
}
|
|
}
|
|
GroupType::Dict => {
|
|
match value.value().as_dictionary() {
|
|
None => false,
|
|
Some(actual_entries) => {
|
|
for (k, p) in entries {
|
|
if !actual_entries.get(k).map(|v| self.run(p, v)).unwrap_or(false) {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn lift_literal(v: &_Any) -> Pattern {
|
|
match v.value() {
|
|
Value::Record(r) => Pattern::Group {
|
|
type_: Box::new(GroupType::Rec { label: r.label().clone() }),
|
|
entries: r.fields().iter().enumerate()
|
|
.map(|(i, v)| (_Any::new(i), lift_literal(v)))
|
|
.collect(),
|
|
},
|
|
Value::Sequence(items) => Pattern::Group {
|
|
type_: Box::new(GroupType::Arr),
|
|
entries: items.iter().enumerate()
|
|
.map(|(i, v)| (_Any::new(i), lift_literal(v)))
|
|
.collect(),
|
|
},
|
|
Value::Set(_members) => panic!("Cannot express literal set in pattern"),
|
|
Value::Dictionary(entries) => Pattern::Group {
|
|
type_: Box::new(GroupType::Dict),
|
|
entries: entries.iter()
|
|
.map(|(k, v)| (k.clone(), lift_literal(v)))
|
|
.collect(),
|
|
},
|
|
_other => Pattern::Lit {
|
|
value: Box::new(language().parse(v).expect("Non-compound datum can be converted to AnyAtom")),
|
|
},
|
|
}
|
|
}
|
|
|
|
const DISCARD: Pattern = Pattern::Discard;
|
|
|
|
pub fn pattern_seq_from_dictionary(entries: &Map<_Any, Pattern>) -> Option<Vec<&Pattern>> {
|
|
let mut max_k: Option<usize> = None;
|
|
for k in entries.keys() {
|
|
max_k = max_k.max(Some(k.value().as_usize()?));
|
|
}
|
|
let mut seq = vec![];
|
|
if let Some(max_k) = max_k {
|
|
seq.reserve(max_k + 1);
|
|
for i in 0..=max_k {
|
|
seq.push(entries.get(&_Any::new(i)).unwrap_or(&DISCARD));
|
|
}
|
|
}
|
|
return Some(seq);
|
|
}
|
|
|
|
fn drop_literal_entries_seq(mut seq: Vec<_Any>, entries: &Map<_Any, Pattern>) -> Option<Vec<_Any>> {
|
|
for p in pattern_seq_from_dictionary(entries)?.into_iter() {
|
|
seq.push(drop_literal(p)?);
|
|
}
|
|
Some(seq)
|
|
}
|
|
|
|
pub fn drop_literal(p: &Pattern) -> Option<_Any> {
|
|
match p {
|
|
Pattern::Group { type_, entries } => match &**type_ {
|
|
GroupType::Rec { label } =>
|
|
Some(Value::Record(Record(drop_literal_entries_seq(vec![label.clone()], entries)?)).wrap()),
|
|
GroupType::Arr =>
|
|
Some(Value::Sequence(drop_literal_entries_seq(vec![], entries)?).wrap()),
|
|
GroupType::Dict =>
|
|
Some(Value::Dictionary(entries.iter()
|
|
.map(|(k, p)| Some((k.clone(), drop_literal(p)?)))
|
|
.collect::<Option<Map<_Any, _Any>>>()?).wrap()),
|
|
},
|
|
Pattern::Lit { value } => Some(language().unparse(&**value)),
|
|
_ => None,
|
|
}
|
|
}
|