421 lines
13 KiB
Rust
421 lines
13 KiB
Rust
// Selectors operate on IOValues because the AST includes keys of IOValue type.
|
|
// If we could make Schemas produce generics...
|
|
|
|
use crate::CompilationError;
|
|
use crate::context::Context;
|
|
use crate::predicate::CompiledPredicate;
|
|
use crate::predicate::Predicate;
|
|
use crate::schemas::path;
|
|
|
|
use num::bigint::BigInt;
|
|
use num::traits::cast::ToPrimitive;
|
|
use num::traits::cast::FromPrimitive;
|
|
|
|
use preserves::value::AtomClass;
|
|
use preserves::value::CompoundClass;
|
|
use preserves::value::IOValue;
|
|
use preserves::value::NestedValue;
|
|
use preserves::value::Value;
|
|
use preserves::value::ValueClass;
|
|
|
|
use preserves_schema::support::interpret;
|
|
|
|
use std::cell::RefCell;
|
|
use std::iter::Iterator;
|
|
use std::rc::Rc;
|
|
|
|
pub trait StepMaker {
|
|
fn connect(&self, step: Node) -> Result<Node, CompilationError>;
|
|
}
|
|
|
|
pub trait Step: std::fmt::Debug {
|
|
fn accept(&mut self, ctxt: &mut Context, value: &IOValue);
|
|
fn finish(&mut self);
|
|
fn reset(&mut self) -> Vec<IOValue>;
|
|
}
|
|
|
|
macro_rules! delegate_finish_and_reset {
|
|
($self:ident, $target:expr) => {
|
|
fn finish(&mut $self) { $target.finish() }
|
|
fn reset(&mut $self) -> Vec<IOValue> { $target.reset() }
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Node(pub Rc<RefCell<dyn Step>>);
|
|
|
|
#[derive(Debug)]
|
|
struct AxisStep {
|
|
step: Node,
|
|
axis: path::Axis,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct CompareStep {
|
|
op: path::Comparison,
|
|
literal: IOValue,
|
|
step: Node,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct RegexStep {
|
|
regex: regex::Regex,
|
|
step: Node,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct TestStep {
|
|
pred: CompiledPredicate,
|
|
step: Node,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct RealStep {
|
|
step: Node,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct IntStep {
|
|
step: Node,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct VecCollector {
|
|
accumulator: Vec<IOValue>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct BoolCollector {
|
|
seen_value: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct KindStep {
|
|
kind: ValueClass,
|
|
step: Node,
|
|
}
|
|
|
|
impl Node {
|
|
fn new<S: Step + 'static>(s: S) -> Self {
|
|
Node(Rc::new(RefCell::new(s)))
|
|
}
|
|
|
|
pub fn test(&self, ctxt: &mut Context, value: &IOValue) -> bool {
|
|
self.accept(ctxt, value);
|
|
self.finish();
|
|
!self.reset().is_empty()
|
|
}
|
|
|
|
pub fn accept(&self, ctxt: &mut Context, value: &IOValue) {
|
|
self.0.borrow_mut().accept(ctxt, value)
|
|
}
|
|
|
|
pub fn finish(&self) {
|
|
self.0.borrow_mut().finish()
|
|
}
|
|
|
|
pub fn reset(&self) -> Vec<IOValue> {
|
|
self.0.borrow_mut().reset()
|
|
}
|
|
|
|
pub fn exec(&self, ctxt: &mut Context, value: &IOValue) -> Vec<IOValue> {
|
|
self.accept(ctxt, value);
|
|
self.finish();
|
|
self.reset()
|
|
}
|
|
}
|
|
|
|
impl StepMaker for path::Selector {
|
|
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
|
self.0.connect(step)
|
|
}
|
|
}
|
|
|
|
impl<S: StepMaker> StepMaker for Vec<S> {
|
|
fn connect(&self, mut step: Node) -> Result<Node, CompilationError> {
|
|
for s in self.iter().rev() {
|
|
step = s.connect(step)?;
|
|
}
|
|
Ok(step)
|
|
}
|
|
}
|
|
|
|
impl StepMaker for path::Step {
|
|
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
|
match self {
|
|
path::Step::Axis(b) => (&**b).connect(step),
|
|
path::Step::Filter(b) => (&**b).connect(step),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl StepMaker for path::Axis {
|
|
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
|
Ok(Node::new(AxisStep { step, axis: self.clone() }))
|
|
}
|
|
}
|
|
|
|
fn descendants(ctxt: &mut Context, step: &mut Node, v: &IOValue) {
|
|
step.accept(ctxt, v);
|
|
for c in v.value().children() {
|
|
ctxt.with_path_step(&c, |ctxt| descendants(ctxt, step, &c));
|
|
}
|
|
}
|
|
|
|
impl Step for AxisStep {
|
|
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
|
ctxt.with_path_step(value, |ctxt| match &self.axis {
|
|
path::Axis::Values =>
|
|
for c in value.value().children() {
|
|
self.step.accept(ctxt, &c)
|
|
},
|
|
path::Axis::Descendants =>
|
|
descendants(ctxt, &mut self.step, value),
|
|
path::Axis::At { key } => match value.value() {
|
|
Value::String(s) | Value::Symbol(s) =>
|
|
step_index(ctxt, s.chars(), &key, |c| IOValue::new(String::from(c)), &mut self.step),
|
|
Value::Record(r) =>
|
|
step_index(ctxt, r.fields().iter(), &key, |v| v.clone(), &mut self.step),
|
|
Value::Sequence(vs) =>
|
|
step_index(ctxt, vs.iter(), &key, |v| v.clone(), &mut self.step),
|
|
Value::Dictionary(d) =>
|
|
if let Some(v) = d.get(&key) {
|
|
self.step.accept(ctxt, v)
|
|
},
|
|
_ =>
|
|
(),
|
|
},
|
|
path::Axis::Label => if let Some(r) = value.value().as_record(None) {
|
|
self.step.accept(ctxt, r.label())
|
|
},
|
|
path::Axis::Keys => match value.value() {
|
|
Value::String(s) | Value::Symbol(s) =>
|
|
step_keys(ctxt, s.len(), &mut self.step),
|
|
Value::ByteString(bs) => step_keys(ctxt, bs.len(), &mut self.step),
|
|
Value::Record(r) => step_keys(ctxt, r.arity(), &mut self.step),
|
|
Value::Sequence(vs) => step_keys(ctxt, vs.len(), &mut self.step),
|
|
Value::Dictionary(d) =>
|
|
for k in d.keys() {
|
|
self.step.accept(ctxt, k)
|
|
},
|
|
_ => (),
|
|
},
|
|
path::Axis::Length => match value.value() {
|
|
Value::String(s) | Value::Symbol(s) =>
|
|
self.step.accept(ctxt, &IOValue::new(s.len())),
|
|
Value::ByteString(bs) => self.step.accept(ctxt, &IOValue::new(bs.len())),
|
|
Value::Record(r) => self.step.accept(ctxt, &IOValue::new(r.arity())),
|
|
Value::Sequence(vs) => self.step.accept(ctxt, &IOValue::new(vs.len())),
|
|
Value::Dictionary(d) => self.step.accept(ctxt, &IOValue::new(d.len())),
|
|
_ => self.step.accept(ctxt, &IOValue::new(0)),
|
|
},
|
|
path::Axis::Annotations =>
|
|
for c in value.annotations().slice() {
|
|
self.step.accept(ctxt, &c)
|
|
},
|
|
path::Axis::Embedded => if let Some(d) = value.value().as_embedded() {
|
|
self.step.accept(ctxt, d)
|
|
},
|
|
path::Axis::Parse { module, name } => {
|
|
if let Some(p) = interpret::Context::new(&ctxt.env.0).dynamic_parse(module, name, value) {
|
|
self.step.accept(ctxt, &p)
|
|
}
|
|
}
|
|
path::Axis::Unparse { module, name } => {
|
|
if let Some(p) = interpret::Context::new(&ctxt.env.0).dynamic_unparse(module, name, value) {
|
|
self.step.accept(ctxt, &p)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
delegate_finish_and_reset!(self, self.step);
|
|
}
|
|
|
|
fn step_index<T, Ts: Iterator<Item = T>, F: FnOnce(T) -> IOValue>(
|
|
ctxt: &mut Context,
|
|
mut vs: Ts,
|
|
key: &IOValue,
|
|
f: F,
|
|
step: &mut Node,
|
|
) {
|
|
if let Some(i) = key.value().as_usize() {
|
|
match vs.nth(i) {
|
|
None => (),
|
|
Some(v) => step.accept(ctxt, &f(v)),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn step_keys(ctxt: &mut Context, count: usize, step: &mut Node) {
|
|
for i in 0 .. count {
|
|
step.accept(ctxt, &IOValue::new(i))
|
|
}
|
|
}
|
|
|
|
impl StepMaker for path::Filter {
|
|
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
|
match self {
|
|
path::Filter::Nop => Ok(step),
|
|
path::Filter::Compare { op, literal } => Ok(Node::new(CompareStep {
|
|
op: (**op).clone(),
|
|
literal: literal.clone(),
|
|
step,
|
|
})),
|
|
path::Filter::Regex { regex } => Ok(Node::new(RegexStep { regex: regex::Regex::new(regex)?, step })),
|
|
path::Filter::Test { pred } => Ok(Node::new(TestStep { pred: (&**pred).compile()?, step })),
|
|
path::Filter::Real => Ok(Node::new(RealStep { step })),
|
|
path::Filter::Int => Ok(Node::new(IntStep { step })),
|
|
path::Filter::Kind { kind } => Ok(Node::new(KindStep {
|
|
kind: match &**kind {
|
|
path::ValueKind::Boolean => ValueClass::Atomic(AtomClass::Boolean),
|
|
path::ValueKind::Float => ValueClass::Atomic(AtomClass::Float),
|
|
path::ValueKind::Double => ValueClass::Atomic(AtomClass::Double),
|
|
path::ValueKind::SignedInteger => ValueClass::Atomic(AtomClass::SignedInteger),
|
|
path::ValueKind::String => ValueClass::Atomic(AtomClass::String),
|
|
path::ValueKind::ByteString => ValueClass::Atomic(AtomClass::ByteString),
|
|
path::ValueKind::Symbol => ValueClass::Atomic(AtomClass::Symbol),
|
|
path::ValueKind::Record => ValueClass::Compound(CompoundClass::Record),
|
|
path::ValueKind::Sequence => ValueClass::Compound(CompoundClass::Sequence),
|
|
path::ValueKind::Set => ValueClass::Compound(CompoundClass::Set),
|
|
path::ValueKind::Dictionary => ValueClass::Compound(CompoundClass::Dictionary),
|
|
path::ValueKind::Embedded => ValueClass::Embedded,
|
|
},
|
|
step,
|
|
})),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Step for CompareStep {
|
|
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
|
if match self.op {
|
|
path::Comparison::Eq => value == &self.literal,
|
|
path::Comparison::Ne => value != &self.literal,
|
|
path::Comparison::Lt => value < &self.literal,
|
|
path::Comparison::Ge => value >= &self.literal,
|
|
path::Comparison::Gt => value > &self.literal,
|
|
path::Comparison::Le => value <= &self.literal,
|
|
} {
|
|
self.step.accept(ctxt, value)
|
|
}
|
|
}
|
|
|
|
delegate_finish_and_reset!(self, self.step);
|
|
}
|
|
|
|
impl Step for RegexStep {
|
|
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
|
match value.value() {
|
|
Value::String(s) | Value::Symbol(s) =>
|
|
if self.regex.is_match(s) { self.step.accept(ctxt, value) },
|
|
_ =>
|
|
(),
|
|
}
|
|
}
|
|
|
|
delegate_finish_and_reset!(self, self.step);
|
|
}
|
|
|
|
impl Step for TestStep {
|
|
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
|
if self.pred.test(ctxt, value) {
|
|
self.step.accept(ctxt, value)
|
|
}
|
|
}
|
|
|
|
delegate_finish_and_reset!(self, self.step);
|
|
}
|
|
|
|
impl Step for RealStep {
|
|
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
|
match value.value() {
|
|
Value::SignedInteger(i) => if let Some(r) = BigInt::from(i).to_f64() {
|
|
self.step.accept(ctxt, &IOValue::new(r))
|
|
},
|
|
Value::Float(f) => self.step.accept(ctxt, &IOValue::new(f32::from(*f) as f64)),
|
|
Value::Double(_) => self.step.accept(ctxt, value),
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
delegate_finish_and_reset!(self, self.step);
|
|
}
|
|
|
|
impl Step for IntStep {
|
|
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
|
match value.value() {
|
|
Value::SignedInteger(_) => self.step.accept(ctxt, value),
|
|
Value::Float(f) => if let Some(i) = BigInt::from_f32(f32::from(*f)) {
|
|
self.step.accept(ctxt, &IOValue::new(i))
|
|
},
|
|
Value::Double(d) => if let Some(i) = BigInt::from_f64(f64::from(*d)) {
|
|
self.step.accept(ctxt, &IOValue::new(i))
|
|
},
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
delegate_finish_and_reset!(self, self.step);
|
|
}
|
|
|
|
impl VecCollector {
|
|
fn new() -> Node {
|
|
Node::new(VecCollector { accumulator: Vec::new() })
|
|
}
|
|
}
|
|
|
|
impl Step for VecCollector {
|
|
fn accept(&mut self, _ctxt: &mut Context, value: &IOValue) {
|
|
self.accumulator.push(value.clone())
|
|
}
|
|
|
|
fn finish(&mut self) {
|
|
}
|
|
|
|
fn reset(&mut self) -> Vec<IOValue> {
|
|
std::mem::take(&mut self.accumulator)
|
|
}
|
|
}
|
|
|
|
impl BoolCollector {
|
|
pub fn new() -> Node {
|
|
Node::new(BoolCollector { seen_value: false })
|
|
}
|
|
}
|
|
|
|
impl Step for BoolCollector {
|
|
fn accept(&mut self, _ctxt: &mut Context, _value: &IOValue) {
|
|
self.seen_value = true
|
|
}
|
|
|
|
fn finish(&mut self) {
|
|
}
|
|
|
|
fn reset(&mut self) -> Vec<IOValue> {
|
|
let result = if self.seen_value { vec![IOValue::new(true)] } else { vec![] };
|
|
self.seen_value = false;
|
|
result
|
|
}
|
|
}
|
|
|
|
impl Step for KindStep {
|
|
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
|
if value.value_class() == self.kind {
|
|
self.step.accept(ctxt, value)
|
|
}
|
|
}
|
|
|
|
delegate_finish_and_reset!(self, self.step);
|
|
}
|
|
|
|
impl path::Selector {
|
|
pub fn compile(&self) -> Result<Node, CompilationError> {
|
|
self.connect(VecCollector::new())
|
|
}
|
|
|
|
pub fn exec(&self, ctxt: &mut Context, value: &IOValue) -> Result<Vec<IOValue>, CompilationError> {
|
|
Ok(self.compile()?.exec(ctxt, value))
|
|
}
|
|
}
|