forked from syndicate-lang/preserves
Much better: boolean operations on newly-introduced Predicates, rather than selectors generally
This commit is contained in:
parent
137cc63a97
commit
61af114d5f
|
@ -10,7 +10,6 @@ use preserves::value::BinarySource;
|
|||
use preserves::value::BytesBinarySource;
|
||||
use preserves::value::CompoundClass;
|
||||
use preserves::value::IOValue;
|
||||
use preserves::value::Map;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::Reader;
|
||||
use preserves::value::Value;
|
||||
|
@ -26,7 +25,6 @@ use thiserror::Error;
|
|||
|
||||
#[derive(Debug)]
|
||||
enum Binop {
|
||||
Interleave,
|
||||
Union,
|
||||
Intersection,
|
||||
}
|
||||
|
@ -48,6 +46,10 @@ pub enum Path {
|
|||
Step(IOValue, Rc<Path>),
|
||||
}
|
||||
|
||||
pub trait Predicate: std::fmt::Debug {
|
||||
fn test(&mut self, path: Rc<Path>, value: &IOValue) -> bool;
|
||||
}
|
||||
|
||||
pub trait Step: std::fmt::Debug {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue);
|
||||
fn finish(&mut self);
|
||||
|
@ -69,6 +71,12 @@ impl Node {
|
|||
Node(Rc::new(RefCell::new(s)))
|
||||
}
|
||||
|
||||
pub fn test(&self, path: Rc<Path>, value: &IOValue) -> bool {
|
||||
self.accept(path, value);
|
||||
self.finish();
|
||||
!self.reset().is_empty()
|
||||
}
|
||||
|
||||
pub fn accept(&self, path: Rc<Path>, value: &IOValue) {
|
||||
self.0.borrow_mut().accept(path, value)
|
||||
}
|
||||
|
@ -102,6 +110,12 @@ impl Path {
|
|||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -111,19 +125,48 @@ impl<S: StepMaker> StepMaker for Vec<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl StepMaker for path::Expr {
|
||||
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
||||
#[derive(Debug)]
|
||||
enum CompiledPredicate {
|
||||
Selector(Node),
|
||||
Not(Box<CompiledPredicate>),
|
||||
Or(Vec<CompiledPredicate>),
|
||||
And(Vec<CompiledPredicate>),
|
||||
}
|
||||
|
||||
fn compile_predicate(p: &path::Predicate) -> Result<CompiledPredicate, CompilationError> {
|
||||
match p {
|
||||
path::Predicate::Selector(b) =>
|
||||
Ok(CompiledPredicate::Selector((&**b).connect(BoolCollector::new())?)),
|
||||
path::Predicate::Not { pred } =>
|
||||
Ok(CompiledPredicate::Not(Box::new(compile_predicate(&**pred)?))),
|
||||
path::Predicate::Or { preds } =>
|
||||
Ok(CompiledPredicate::Or(preds.iter().map(compile_predicate).collect::<Result<_,_>>()?)),
|
||||
path::Predicate::And { preds } =>
|
||||
Ok(CompiledPredicate::And(preds.iter().map(compile_predicate).collect::<Result<_,_>>()?)),
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate for CompiledPredicate {
|
||||
fn test(&mut self, path: Rc<Path>, value: &IOValue) -> bool {
|
||||
match self {
|
||||
path::Expr::Steps(s) =>
|
||||
s.connect(step),
|
||||
path::Expr::Not { expr } =>
|
||||
expr.connect(Node::new(NotStep { seen_value: false, step, })),
|
||||
path::Expr::Interleave { exprs } =>
|
||||
ForkJoinStep::new(exprs, |e, s| e.connect(s), step),
|
||||
path::Expr::Union { exprs } =>
|
||||
ForkJoinStep::new(exprs, |e, s| e.connect(s), ThresholdStep::new(1, step)?),
|
||||
path::Expr::Intersection { exprs } =>
|
||||
ForkJoinStep::new(exprs, |e, s| e.connect(ThresholdStep::new(1, s)?), ThresholdStep::new(exprs.len(), step)?),
|
||||
CompiledPredicate::Selector(n) => n.test(path, value),
|
||||
CompiledPredicate::Not(p) => !p.test(path, value),
|
||||
CompiledPredicate::Or(ps) => {
|
||||
for p in ps.iter_mut() {
|
||||
if p.test(Rc::clone(&path), value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
CompiledPredicate::And(ps) => {
|
||||
for p in ps.iter_mut() {
|
||||
if !p.test(Rc::clone(&path), value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +176,6 @@ impl StepMaker for path::Step {
|
|||
match self {
|
||||
path::Step::Axis(b) => (&**b).connect(step),
|
||||
path::Step::Filter(b) => (&**b).connect(step),
|
||||
path::Step::Expr(b) => (&**b).connect(step),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,18 +188,13 @@ struct AxisStep {
|
|||
|
||||
impl StepMaker for path::Axis {
|
||||
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
||||
if let path::Axis::Nop = self {
|
||||
Ok(step)
|
||||
} else {
|
||||
Ok(Node::new(AxisStep { step, axis: self.clone() }))
|
||||
}
|
||||
Ok(Node::new(AxisStep { step, axis: self.clone() }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for AxisStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
match &self.axis {
|
||||
path::Axis::Nop => self.step.accept(path, value),
|
||||
path::Axis::Values => {
|
||||
let path = path.step(value);
|
||||
for c in value.value().children() {
|
||||
|
@ -176,17 +213,26 @@ impl Step for AxisStep {
|
|||
}
|
||||
}
|
||||
path::Axis::At { key } => match value.value() {
|
||||
Value::Record(r) => step_index(path.step(value), r.fields(), &key, &mut self.step),
|
||||
Value::Sequence(vs) => step_index(path.step(value), vs, &key, &mut self.step),
|
||||
Value::Dictionary(d) => if let Some(v) = d.get(&key) {
|
||||
self.step.accept(path.step(value), v)
|
||||
},
|
||||
_ => (),
|
||||
Value::String(s) =>
|
||||
step_index(path.step(value), s.chars(), &key, |c| IOValue::new(String::from(c)), &mut self.step),
|
||||
Value::Record(r) =>
|
||||
step_index(path.step(value), r.fields().iter(), &key, |v| v.clone(), &mut self.step),
|
||||
Value::Sequence(vs) =>
|
||||
step_index(path.step(value), vs.iter(), &key, |v| v.clone(), &mut self.step),
|
||||
Value::Dictionary(d) =>
|
||||
if let Some(v) = d.get(&key) {
|
||||
self.step.accept(path.step(value), v)
|
||||
},
|
||||
_ =>
|
||||
(),
|
||||
},
|
||||
path::Axis::Label => if let Some(r) = value.value().as_record(None) {
|
||||
self.step.accept(path.step(value), r.label())
|
||||
},
|
||||
path::Axis::Keys => match value.value() {
|
||||
Value::String(s) => step_keys(path.step(value), s.len(), &mut self.step),
|
||||
Value::ByteString(bs) => step_keys(path.step(value), bs.len(), &mut self.step),
|
||||
Value::Symbol(s) => step_keys(path.step(value), s.len(), &mut self.step),
|
||||
Value::Record(r) => step_keys(path.step(value), r.arity(), &mut self.step),
|
||||
Value::Sequence(vs) => step_keys(path.step(value), vs.len(), &mut self.step),
|
||||
Value::Dictionary(d) => {
|
||||
|
@ -198,6 +244,9 @@ impl Step for AxisStep {
|
|||
_ => (),
|
||||
},
|
||||
path::Axis::Length => match value.value() {
|
||||
Value::String(s) => self.step.accept(path.step(value), &IOValue::new(s.len())),
|
||||
Value::ByteString(bs) => self.step.accept(path.step(value), &IOValue::new(bs.len())),
|
||||
Value::Symbol(s) => self.step.accept(path.step(value), &IOValue::new(s.len())),
|
||||
Value::Record(r) => self.step.accept(path.step(value), &IOValue::new(r.arity())),
|
||||
Value::Sequence(vs) => self.step.accept(path.step(value), &IOValue::new(vs.len())),
|
||||
Value::Dictionary(d) => self.step.accept(path.step(value), &IOValue::new(d.len())),
|
||||
|
@ -218,10 +267,17 @@ impl Step for AxisStep {
|
|||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
fn step_index(p: Rc<Path>, vs: &[IOValue], key: &IOValue, step: &mut Node) {
|
||||
fn step_index<T, Ts: Iterator<Item = T>, F: FnOnce(T) -> IOValue>(
|
||||
p: Rc<Path>,
|
||||
mut vs: Ts,
|
||||
key: &IOValue,
|
||||
f: F,
|
||||
step: &mut Node,
|
||||
) {
|
||||
if let Some(i) = key.value().as_usize() {
|
||||
if i < vs.len() {
|
||||
step.accept(p, &vs[i])
|
||||
match vs.nth(i) {
|
||||
None => (),
|
||||
Some(v) => step.accept(p, &f(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,10 +292,13 @@ impl StepMaker for path::Filter {
|
|||
fn connect(&self, step: Node) -> Result<Node, CompilationError> {
|
||||
match self {
|
||||
path::Filter::Nop => Ok(step),
|
||||
path::Filter::Fail => Ok(Node::new(InertStep)),
|
||||
path::Filter::Eq { literal } => Ok(Node::new(EqStep { literal: literal.clone(), 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 { expr } => Ok(Node::new(TestStep { expr: expr.connect(BoolCollector::new())?, step })),
|
||||
path::Filter::Test { pred } => Ok(Node::new(TestStep { pred: compile_predicate(&**pred)?, step })),
|
||||
path::Filter::Kind { kind } => Ok(Node::new(KindStep {
|
||||
kind: match &**kind {
|
||||
path::ValueKind::Boolean => ValueClass::Atomic(AtomClass::Boolean),
|
||||
|
@ -271,39 +330,22 @@ impl Step for InertStep {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NotStep {
|
||||
seen_value: bool,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
impl Step for NotStep {
|
||||
fn accept(&mut self, _path: Rc<Path>, _value: &IOValue) {
|
||||
self.seen_value = true;
|
||||
}
|
||||
|
||||
fn finish(&mut self) {
|
||||
if !self.seen_value {
|
||||
self.step.accept(Path::root(), &IOValue::new(true));
|
||||
self.seen_value = true; // makes finish() idempotent
|
||||
}
|
||||
self.step.finish()
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Vec<IOValue> {
|
||||
self.seen_value = false;
|
||||
self.step.reset()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EqStep {
|
||||
struct CompareStep {
|
||||
op: path::Comparison,
|
||||
literal: IOValue,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
impl Step for EqStep {
|
||||
impl Step for CompareStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
if value == &self.literal {
|
||||
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(path, value)
|
||||
}
|
||||
}
|
||||
|
@ -331,17 +373,14 @@ impl Step for RegexStep {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct TestStep {
|
||||
expr: Node,
|
||||
pred: CompiledPredicate,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
impl Step for TestStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
self.expr.accept(Rc::clone(&path), value);
|
||||
self.expr.finish();
|
||||
match self.expr.reset().len() {
|
||||
0 => (),
|
||||
_ => self.step.accept(path, value)
|
||||
if self.pred.test(Rc::clone(&path), value) {
|
||||
self.step.accept(path, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,136 +453,57 @@ impl Step for KindStep {
|
|||
delegate_finish_and_reset!(self, self.step);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ForkJoinStep {
|
||||
branches: Vec<Node>,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
impl ForkJoinStep {
|
||||
fn new<F: Fn(&path::Expr, Node) -> Result<Node, CompilationError>>(
|
||||
exprs: &Vec<path::Expr>,
|
||||
f: F,
|
||||
step: Node,
|
||||
) -> Result<Node, CompilationError> {
|
||||
Ok(Node::new(Self {
|
||||
branches: exprs.iter().map(|e| f(e, step.clone())).collect::<Result<Vec<Node>, _>>()?,
|
||||
step,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for ForkJoinStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
for n in self.branches.iter_mut() {
|
||||
n.accept(Rc::clone(&path), value)
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&mut self) {
|
||||
for n in self.branches.iter_mut() {
|
||||
n.finish()
|
||||
}
|
||||
self.step.finish()
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Vec<IOValue> {
|
||||
let result = self.step.reset();
|
||||
for n in self.branches.iter_mut() {
|
||||
n.reset();
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ThresholdStep {
|
||||
threshold: usize,
|
||||
accumulator: Map<IOValue, usize>,
|
||||
step: Node,
|
||||
}
|
||||
|
||||
impl ThresholdStep {
|
||||
fn new(threshold: usize, step: Node) -> Result<Node, CompilationError> {
|
||||
Ok(Node::new(Self {
|
||||
threshold,
|
||||
accumulator: Map::new(),
|
||||
step,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for ThresholdStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
let c = self.accumulator.entry(value.clone()).or_insert(0);
|
||||
*c += 1;
|
||||
if *c == self.threshold {
|
||||
self.step.accept(path, value)
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&mut self) {
|
||||
self.step.finish()
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Vec<IOValue> {
|
||||
self.accumulator.clear();
|
||||
self.step.reset()
|
||||
}
|
||||
}
|
||||
|
||||
fn split_values_by_symbol(tokens: &Vec<IOValue>, separator: &str) -> Vec<Vec<IOValue>> {
|
||||
fn split_values_by_symbol<'a>(tokens: &'a [IOValue], separator: &str) -> Vec<&'a [IOValue]> {
|
||||
tokens
|
||||
.split(|t| matches!(t.value().as_symbol(), Some(s) if s == separator))
|
||||
.map(|ts| ts.to_vec())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn split_binop(tokens: &Vec<IOValue>) -> Result<(Vec<Vec<IOValue>>, Option<Binop>), CompilationError> {
|
||||
let interleave_pieces = split_values_by_symbol(&tokens, "~");
|
||||
fn split_binop(tokens: &[IOValue]) -> Result<(Vec<&[IOValue]>, Option<Binop>), CompilationError> {
|
||||
let union_pieces = split_values_by_symbol(&tokens, "+");
|
||||
let intersection_pieces = split_values_by_symbol(&tokens, "&");
|
||||
match (interleave_pieces.len(), union_pieces.len(), intersection_pieces.len()) {
|
||||
(1, 1, 1) => Ok((interleave_pieces, None)),
|
||||
(m, 1, 1) if m > 1 => Ok((interleave_pieces, Some(Binop::Interleave))),
|
||||
(1, m, 1) if m > 1 => Ok((union_pieces, Some(Binop::Union))),
|
||||
(1, 1, m) if m > 1 => Ok((intersection_pieces, Some(Binop::Intersection))),
|
||||
match (union_pieces.len(), intersection_pieces.len()) {
|
||||
(1, 1) => Ok((union_pieces, None)),
|
||||
(_, 1) => Ok((union_pieces, Some(Binop::Union))),
|
||||
(1, _) => Ok((intersection_pieces, Some(Binop::Intersection))),
|
||||
_ => Err(CompilationError::MixedOperators),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_expr(tokens: &Vec<IOValue>) -> Result<path::Expr, CompilationError> {
|
||||
let (pieces, binop) = split_binop(tokens)?;
|
||||
match binop {
|
||||
None => parse_non_binop(&pieces[0]),
|
||||
Some(o) => {
|
||||
let exprs = pieces.into_iter().map(|ts| parse_non_binop(&ts))
|
||||
.collect::<Result<Vec<path::Expr>, _>>()?;
|
||||
Ok(match o {
|
||||
Binop::Interleave => path::Expr::Interleave { exprs },
|
||||
Binop::Union => path::Expr::Union { exprs },
|
||||
Binop::Intersection => path::Expr::Intersection { exprs },
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_non_binop(tokens: &[IOValue]) -> Result<path::Expr, CompilationError> {
|
||||
if !tokens.is_empty() {
|
||||
let t = tokens[0].value();
|
||||
|
||||
if let Some("!") = t.as_symbol().map(|s| s.as_str()) {
|
||||
return Ok(path::Expr::Not { expr: Box::new(parse_non_binop(&tokens[1..])?) });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_selector(tokens: &[IOValue]) -> Result<path::Selector, CompilationError> {
|
||||
let mut steps = Vec::new();
|
||||
let mut tokens = tokens;
|
||||
while let Some((s, remaining)) = parse_step(tokens)? {
|
||||
steps.push(s);
|
||||
tokens = remaining;
|
||||
}
|
||||
Ok(path::Expr::Steps(steps))
|
||||
Ok(path::Selector(steps))
|
||||
}
|
||||
|
||||
pub fn parse_predicate(tokens: &[IOValue]) -> Result<path::Predicate, CompilationError> {
|
||||
let (pieces, binop) = split_binop(tokens)?;
|
||||
match binop {
|
||||
None => parse_non_binop(&pieces[0]),
|
||||
Some(o) => {
|
||||
let preds = pieces.into_iter().map(|ts| parse_non_binop(&ts)).collect::<Result<_,_>>()?;
|
||||
Ok(match o {
|
||||
Binop::Union => path::Predicate::Or { preds },
|
||||
Binop::Intersection => path::Predicate::And { preds },
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_non_binop(tokens: &[IOValue]) -> Result<path::Predicate, CompilationError> {
|
||||
if !tokens.is_empty() {
|
||||
let t = tokens[0].value();
|
||||
|
||||
if let Some("!") = t.as_symbol().map(|s| s.as_str()) {
|
||||
return Ok(path::Predicate::Not { pred: Box::new(parse_non_binop(&tokens[1..])?) });
|
||||
}
|
||||
}
|
||||
|
||||
Ok(path::Predicate::Selector(Box::new(parse_selector(tokens)?)))
|
||||
}
|
||||
|
||||
fn parse_step(tokens: &[IOValue]) -> Result<Option<(path::Step, &[IOValue])>, CompilationError> {
|
||||
|
@ -554,15 +514,14 @@ fn parse_step(tokens: &[IOValue]) -> Result<Option<(path::Step, &[IOValue])>, Co
|
|||
let remainder = &tokens[1..];
|
||||
|
||||
if tokens[0].value().is_sequence() {
|
||||
return Ok(Some((
|
||||
path::Step::Expr(Box::new(parse_expr(tokens[0].value().as_sequence().unwrap())?)),
|
||||
remainder)));
|
||||
return Ok(Some((path::Step::Filter(Box::new(path::Filter::Test {
|
||||
pred: Box::new(parse_predicate(tokens[0].value().as_sequence().unwrap())?),
|
||||
})), remainder)));
|
||||
}
|
||||
|
||||
match tokens[0].value().as_symbol() {
|
||||
None => return Err(CompilationError::InvalidStep),
|
||||
Some(t) => match t.as_str() {
|
||||
".=" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Nop)), remainder))),
|
||||
"/" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Values)), remainder))),
|
||||
"//" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Descendants)), remainder))),
|
||||
"." => {
|
||||
|
@ -575,30 +534,30 @@ fn parse_step(tokens: &[IOValue]) -> Result<Option<(path::Step, &[IOValue])>, Co
|
|||
".annotations" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Annotations)), remainder))),
|
||||
".embedded" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Embedded)), remainder))),
|
||||
|
||||
"=*" => Ok(Some((path::Step::Filter(Box::new(path::Filter::Nop)), remainder))),
|
||||
"=!" => Ok(Some((path::Step::Filter(Box::new(path::Filter::Fail)), remainder))),
|
||||
"=" => {
|
||||
let (literal, remainder) = pop_step_arg(remainder)?;
|
||||
Ok(Some((path::Step::Filter(Box::new(path::Filter::Eq { literal })), remainder)))
|
||||
}
|
||||
"=r" => {
|
||||
"*" => Ok(Some((path::Step::Filter(Box::new(path::Filter::Nop)), remainder))),
|
||||
"eq" => parse_comparison(remainder, path::Comparison::Eq),
|
||||
"=" => parse_comparison(remainder, path::Comparison::Eq),
|
||||
"ne" => parse_comparison(remainder, path::Comparison::Ne),
|
||||
"lt" => parse_comparison(remainder, path::Comparison::Lt),
|
||||
"gt" => parse_comparison(remainder, path::Comparison::Gt),
|
||||
"le" => parse_comparison(remainder, path::Comparison::Le),
|
||||
"ge" => parse_comparison(remainder, path::Comparison::Ge),
|
||||
"re" | "=r" => {
|
||||
let (regex_val, remainder) = pop_step_arg(remainder)?;
|
||||
let regex = regex_val.value().to_string().map_err(|_| CompilationError::InvalidStep)?.clone();
|
||||
let _ = regex::Regex::new(®ex)?;
|
||||
Ok(Some((path::Step::Filter(Box::new(path::Filter::Regex { regex })), remainder)))
|
||||
}
|
||||
"?" => {
|
||||
let (expr_val, remainder) = pop_step_arg(remainder)?;
|
||||
let expr = Box::new(parse_expr(&vec![expr_val])?);
|
||||
Ok(Some((path::Step::Filter(Box::new(path::Filter::Test { expr })), remainder)))
|
||||
}
|
||||
"^" => {
|
||||
let (literal, remainder) = pop_step_arg(remainder)?;
|
||||
Ok(Some((path::Step::Filter(Box::new(path::Filter::Test {
|
||||
expr: Box::new(path::Expr::Steps(vec![
|
||||
pred: Box::new(path::Predicate::Selector(Box::new(path::Selector(vec![
|
||||
path::Step::Axis(Box::new(path::Axis::Label)),
|
||||
path::Step::Filter(Box::new(path::Filter::Eq { literal })),
|
||||
])),
|
||||
path::Step::Filter(Box::new(path::Filter::Compare {
|
||||
op: Box::new(path::Comparison::Eq),
|
||||
literal,
|
||||
})),
|
||||
])))),
|
||||
})), remainder)))
|
||||
}
|
||||
|
||||
|
@ -635,7 +594,18 @@ fn pop_step_arg(tokens: &[IOValue]) -> Result<(IOValue, &[IOValue]), Compilation
|
|||
Ok((tokens[0].clone(), &tokens[1..]))
|
||||
}
|
||||
|
||||
impl path::Expr {
|
||||
fn parse_comparison(
|
||||
tokens: &[IOValue],
|
||||
op: path::Comparison,
|
||||
) -> Result<Option<(path::Step, &[IOValue])>, CompilationError> {
|
||||
let (literal, remainder) = pop_step_arg(tokens)?;
|
||||
Ok(Some((path::Step::Filter(Box::new(path::Filter::Compare {
|
||||
op: Box::new(op),
|
||||
literal,
|
||||
})), remainder)))
|
||||
}
|
||||
|
||||
impl path::Selector {
|
||||
pub fn compile(&self) -> Result<Node, CompilationError> {
|
||||
self.connect(VecCollector::new())
|
||||
}
|
||||
|
@ -645,20 +615,20 @@ impl path::Expr {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for path::Expr {
|
||||
impl std::str::FromStr for path::Selector {
|
||||
type Err = CompilationError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
parse_expr(&(BytesBinarySource::new(s.as_bytes())
|
||||
.text_iovalues()
|
||||
.configured(false)
|
||||
.collect::<Result<Vec<_>, _>>()?))
|
||||
parse_selector(&(BytesBinarySource::new(s.as_bytes())
|
||||
.text_iovalues()
|
||||
.configured(false)
|
||||
.collect::<Result<Vec<_>, _>>()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Node {
|
||||
type Err = CompilationError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let expr = path::Expr::from_str(s)?;
|
||||
let expr = path::Selector::from_str(s)?;
|
||||
expr.compile()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
´³schema·³version‘³definitions·³Axis´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±values´³rec´³lit³values„´³tupleµ„„„„µ±descendants´³rec´³lit³descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ±annotations´³rec´³lit³annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„„„³Expr´³orµµ±steps´³seqof´³refµ„³Step„„„µ±not´³rec´³lit³not„´³tupleµ´³named³expr´³refµ„³Expr„„„„„„µ±
|
||||
interleave´³rec´³lit³
|
||||
interleave„´³tupleµ´³named³exprs´³seqof´³refµ„³Expr„„„„„„„µ±union´³rec´³lit³union„´³tupleµ´³named³exprs´³seqof´³refµ„³Expr„„„„„„„µ±intersection´³rec´³lit³intersection„´³tupleµ´³named³exprs´³seqof´³refµ„³Expr„„„„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„µ±Expr´³refµ„³Expr„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±fail´³rec´³lit³fail„´³tupleµ„„„„µ±eq´³rec´³lit³eq„´³tupleµ´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³expr´³refµ„³Expr„„„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ±
SignedInteger´³lit³
SignedInteger„„µ±String´³lit³String„„µ±
|
||||
´³schema·³version‘³definitions·³Axis´³orµµ±values´³rec´³lit³values„´³tupleµ„„„„µ±descendants´³rec´³lit³descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ±annotations´³rec´³lit³annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±compare´³rec´³lit³compare„´³tupleµ´³named³op´³refµ„³
|
||||
Comparison„„´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³Selector´³seqof´³refµ„³Step„„³ Predicate´³orµµ±Selector´³refµ„³Selector„„µ±not´³rec´³lit³not„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±or´³rec´³lit³or„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„µ±and´³rec´³lit³and„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ±
SignedInteger´³lit³
SignedInteger„„µ±String´³lit³String„„µ±
|
||||
ByteString´³lit³
|
||||
ByteString„„µ±Symbol´³lit³Symbol„„µ±Record´³lit³Record„„µ±Sequence´³lit³Sequence„„µ±Set´³lit³Set„„µ±
|
||||
Dictionary´³lit³
|
||||
Dictionary„„µ±Embedded´³lit³Embedded„„„„„³embeddedType€„„
|
||||
Dictionary„„µ±Embedded´³lit³Embedded„„„„³
|
||||
Comparison´³orµµ±eq´³lit³eq„„µ±ne´³lit³ne„„µ±lt´³lit³lt„„µ±ge´³lit³ge„„µ±gt´³lit³gt„„µ±le´³lit³le„„„„„³embeddedType€„„
|
|
@ -1,17 +1,17 @@
|
|||
version 1 .
|
||||
|
||||
Expr =
|
||||
/ @steps [Step ...]
|
||||
/ <not @expr Expr>
|
||||
/ <interleave @exprs [Expr ...]>
|
||||
/ <union @exprs [Expr ...]>
|
||||
/ <intersection @exprs [Expr ...]>
|
||||
Selector = [Step ...] .
|
||||
|
||||
Predicate =
|
||||
/ Selector
|
||||
/ <not @pred Predicate>
|
||||
/ <or @preds [Predicate ...]>
|
||||
/ <and @preds [Predicate ...]>
|
||||
.
|
||||
|
||||
Step = Axis / Filter / Expr .
|
||||
Step = Axis / Filter .
|
||||
|
||||
Axis =
|
||||
/ <nop>
|
||||
/ <values>
|
||||
/ <descendants>
|
||||
/ <at @key any>
|
||||
|
@ -24,13 +24,14 @@ Axis =
|
|||
|
||||
Filter =
|
||||
/ <nop>
|
||||
/ <fail>
|
||||
/ <eq @literal any>
|
||||
/ <compare @op Comparison @literal any>
|
||||
/ <regex @regex string>
|
||||
/ <test @expr Expr>
|
||||
/ <test @pred Predicate>
|
||||
/ <kind @kind ValueKind>
|
||||
.
|
||||
|
||||
Comparison = =eq / =ne / =lt / =ge / =gt / =le .
|
||||
|
||||
ValueKind =
|
||||
/ =Boolean / =Float / =Double / =SignedInteger / =String / =ByteString / =Symbol
|
||||
/ =Record / =Sequence / =Set / =Dictionary
|
||||
|
|
|
@ -15,29 +15,35 @@ general-value for dictionaries); into all keys; into all
|
|||
mapped-to-values, i.e. children (n.b. not just for sequences and
|
||||
dicts, but also for sets).
|
||||
|
||||
## Expressions
|
||||
## Selector
|
||||
|
||||
Expressions: compute a sequence or set (or dictionary?) of results
|
||||
from a stream of input values.
|
||||
A sequence of steps, applied one after the other, flatmap-style.
|
||||
|
||||
step ... ;; Applies steps one after the other, flatmap-style
|
||||
|
||||
Each step transforms an input document into zero or more related
|
||||
documents. A step is an axis or a filter.
|
||||
|
||||
## Predicates
|
||||
|
||||
Predicates: interpret selectors as truth-functions over inputs
|
||||
(nonempty output meaning truth), and compose them using and, not, or,
|
||||
etc.
|
||||
|
||||
Precedence groupings from highest to lowest. Within a grouping, no
|
||||
mixed precedence is permitted.
|
||||
|
||||
step ... ;; Applies steps one after the other, flatmap-style
|
||||
selector ;; Applies steps one after the other, flatmap-style
|
||||
|
||||
! expr ;; If no nodes, yields a dummy #t node; if some, yields none
|
||||
! pred ;; "not" of a predicate
|
||||
|
||||
expr ~ expr ~ ... ;; "interleave" of expressions (sequence-valued, duplicates allowed)
|
||||
expr + expr + ... ;; "union" of expressions (set-valued)
|
||||
expr & expr & ... ;; "intersection" of expressions (set-valued)
|
||||
|
||||
A step is an axis, a filter, or `[expr]`, a parenthesis for overriding precedence.
|
||||
pred + pred + ... ;; "or" of predicates
|
||||
pred & pred & ... ;; "and" of predicates
|
||||
|
||||
## Axes
|
||||
|
||||
Axes: move around, applying filters after moving
|
||||
|
||||
.= ;; Doesn't move anywhere
|
||||
/ ;; Moves into immediate children (values / fields)
|
||||
// ;; Flattens children recursively
|
||||
. key ;; Moves into named child
|
||||
|
@ -47,19 +53,30 @@ Axes: move around, applying filters after moving
|
|||
.annotations ;; Moves into any annotations that might be present
|
||||
.embedded ;; Moves into the representation of an embedded value
|
||||
|
||||
Sets have children, but no keys/length; Strings, ByteStrings and
|
||||
Symbols have no children, but have keys/length.
|
||||
|
||||
## Filters
|
||||
|
||||
Filters: narrow down a selection without moving
|
||||
|
||||
=* ;; Accepts all
|
||||
=! ;; Rejects all
|
||||
* ;; Accepts all
|
||||
[!] ;; Rejects all (just a use of `[pred]`)
|
||||
|
||||
= literal ;; Matches values equal to the literal
|
||||
=r regex ;; Matches strings and symbols by regular expression
|
||||
eq literal ;; Matches values (equal to/less than/greater than/etc.) the literal
|
||||
= literal
|
||||
ne literal
|
||||
lt literal
|
||||
gt literal
|
||||
le literal
|
||||
ge literal
|
||||
|
||||
?[expr] ;; Applies the expression to each node; keeps nodes that yield nonempty
|
||||
re regex ;; Matches strings and symbols by regular expression
|
||||
=r regex
|
||||
|
||||
^ literal ;; Matches a record having a the literal as its label -- equivalent to ?[.^ = literal]
|
||||
[pred] ;; Applies predicate to each input; keeps inputs yielding truth
|
||||
|
||||
^ literal ;; Matches a record having a the literal as its label -- equivalent to [.^ = literal]
|
||||
|
||||
bool ;; Type filters
|
||||
float
|
||||
|
|
Loading…
Reference in New Issue