syndicate-js/packages/core/src/runtime/pattern.ts

167 lines
5.3 KiB
TypeScript

/// SPDX-License-Identifier: GPL-3.0-or-later
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
use crate::schemas::dataspace_patterns::*;
use preserves::value::NestedValue;
use std::convert::TryFrom;
use std::convert::TryInto;
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub enum PathStep {
Index(usize),
Key(_Any),
}
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,
}
struct PatternMatcher<N: NestedValue> {
captures: Vec<N>,
}
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 {
DCompound::Rec { members, .. } |
DCompound::Arr { members, .. } => {
for (i, p) in members {
self.walk_step(path, PathStep::Index(usize::try_from(i).unwrap_or(0)), p);
}
}
DCompound::Dict { members, .. } => {
for (k, p) in members {
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());
self.const_values.push(value.clone());
}
}
}
}
impl<N: NestedValue> Pattern<N> {
pub fn match_value(&self, value: &N) -> Option<Vec<N>> {
let mut matcher = PatternMatcher::new();
if matcher.run(self, value) {
Some(matcher.captures)
} else {
None
}
}
}
impl<N: NestedValue> PatternMatcher<N> {
fn new() -> Self {
PatternMatcher {
captures: Vec::new(),
}
}
fn run(&mut self, pattern: &Pattern<N>, value: &N) -> bool {
match pattern {
Pattern::DDiscard(_) => true,
Pattern::DBind(b) => {
self.captures.push(value.clone());
self.run(&b.pattern, value)
}
Pattern::DLit(b) => value == &b.value,
Pattern::DCompound(b) => match &**b {
DCompound::Rec { ctor, members } => {
let arity = (&ctor.arity).try_into().expect("reasonable arity");
match value.value().as_record(Some(arity)) {
None => false,
Some(r) => {
for (i, p) in members.iter() {
let i: usize = i.try_into().expect("reasonable index");
if !self.run(p, &r.fields()[i]) {
return false;
}
}
true
}
}
}
DCompound::Arr { ctor, members } => {
let arity: usize = (&ctor.arity).try_into().expect("reasonable arity");
match value.value().as_sequence() {
None => false,
Some(vs) => {
if vs.len() != arity {
return false;
}
for (i, p) in members.iter() {
let i: usize = i.try_into().expect("reasonable index");
if !self.run(p, &vs[i]) {
return false;
}
}
true
}
}
}
DCompound::Dict { ctor: _, members } => {
match value.value().as_dictionary() {
None => false,
Some(entries) => {
for (k, p) in members.iter() {
if !entries.get(k).map(|v| self.run(p, v)).unwrap_or(false) {
return false;
}
}
true
}
}
}
}
}
}
}