Experimental schema interpreter and preserves-path support
This commit is contained in:
parent
b38d180956
commit
ed63d46348
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "preserves-path"
|
||||
version = "2.2.0"
|
||||
version = "3.0.0"
|
||||
authors = ["Tony Garnock-Jones <tonyg@leastfixedpoint.com>"]
|
||||
edition = "2018"
|
||||
description = "Implementation of preserves-path, a query language for Preserves documents."
|
||||
|
@ -9,11 +9,11 @@ repository = "https://gitlab.com/preserves/preserves"
|
|||
license = "Apache-2.0"
|
||||
|
||||
[build-dependencies]
|
||||
preserves-schema = { path = "../preserves-schema", version = "^2.2.0"}
|
||||
preserves-schema = { path = "../preserves-schema", version = "^2.3.0"}
|
||||
|
||||
[dependencies]
|
||||
preserves = { path = "../preserves", version = "^2.2.0"}
|
||||
preserves-schema = { path = "../preserves-schema", version = "^2.2.0"}
|
||||
preserves-schema = { path = "../preserves-schema", version = "^2.3.0"}
|
||||
|
||||
num = "0.4"
|
||||
regex = "1.5"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
´³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µ„³
|
||||
´³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µ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³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„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±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„„µ±
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
use preserves::value::IOValue;
|
||||
|
||||
use preserves_schema::compiler::load_schema_or_bundle;
|
||||
use preserves_schema::gen::schema::Definition;
|
||||
|
||||
use std::io;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Env(pub preserves_schema::support::interpret::Env<IOValue>);
|
||||
|
||||
pub struct Context<'a> {
|
||||
pub env: &'a Env,
|
||||
path: Vec<IOValue>,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(env: &'a Env) -> Self {
|
||||
Context {
|
||||
env,
|
||||
path: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_path_step<R, F: FnOnce(&mut Self) -> R>(&mut self, v: &IOValue, f: F) -> R {
|
||||
self.path.push(v.clone());
|
||||
let result = f(self);
|
||||
self.path.pop();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn load_bundle(&mut self, filename: &std::path::PathBuf) -> io::Result<()> {
|
||||
load_schema_or_bundle(&mut self.0, filename)
|
||||
}
|
||||
|
||||
pub fn lookup_definition(&self, module: &Vec<String>, name: &str) -> Option<&Definition<IOValue>> {
|
||||
self.0.get(module).and_then(|s| s.definitions.0.get(name))
|
||||
}
|
||||
|
||||
pub fn to_context(&self) -> Context {
|
||||
Context::new(self)
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ pub enum CompilationError {
|
|||
MixedOperators,
|
||||
#[error("Invalid step")]
|
||||
InvalidStep,
|
||||
#[error("Undefined schema definition name: {0}")]
|
||||
UndefinedSchemaDefinitionName(String),
|
||||
#[error(transparent)]
|
||||
RegexError(#[from] regex::Error),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
pub mod context;
|
||||
pub mod error;
|
||||
pub mod parse;
|
||||
pub mod path;
|
||||
pub mod predicate;
|
||||
|
||||
pub mod schemas {
|
||||
|
@ -9,6 +9,9 @@ pub mod schemas {
|
|||
|
||||
pub mod step;
|
||||
|
||||
pub use context::Context;
|
||||
pub use context::Env;
|
||||
|
||||
pub use error::CompilationError;
|
||||
|
||||
pub use parse::parse_selector;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::CompilationError;
|
||||
use crate::context::Env;
|
||||
use crate::schemas::path;
|
||||
use crate::step::Node;
|
||||
|
||||
|
@ -33,22 +34,22 @@ fn split_binop(tokens: &[IOValue]) -> Result<(Vec<&[IOValue]>, Option<Binop>), C
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_selector(tokens: &[IOValue]) -> Result<path::Selector, CompilationError> {
|
||||
pub fn parse_selector(env: &Env, tokens: &[IOValue]) -> Result<path::Selector, CompilationError> {
|
||||
let mut steps = Vec::new();
|
||||
let mut tokens = tokens;
|
||||
while let Some((s, remaining)) = parse_step(tokens)? {
|
||||
while let Some((s, remaining)) = parse_step(env, tokens)? {
|
||||
steps.push(s);
|
||||
tokens = remaining;
|
||||
}
|
||||
Ok(path::Selector(steps))
|
||||
}
|
||||
|
||||
pub fn parse_predicate(tokens: &[IOValue]) -> Result<path::Predicate, CompilationError> {
|
||||
pub fn parse_predicate(env: &Env, tokens: &[IOValue]) -> Result<path::Predicate, CompilationError> {
|
||||
let (pieces, binop) = split_binop(tokens)?;
|
||||
match binop {
|
||||
None => parse_non_binop(&pieces[0]),
|
||||
None => parse_non_binop(env, &pieces[0]),
|
||||
Some(o) => {
|
||||
let preds = pieces.into_iter().map(|ts| parse_non_binop(&ts)).collect::<Result<_,_>>()?;
|
||||
let preds = pieces.into_iter().map(|ts| parse_non_binop(env, &ts)).collect::<Result<_,_>>()?;
|
||||
Ok(match o {
|
||||
Binop::Union => path::Predicate::Or { preds },
|
||||
Binop::Intersection => path::Predicate::And { preds },
|
||||
|
@ -57,19 +58,29 @@ pub fn parse_predicate(tokens: &[IOValue]) -> Result<path::Predicate, Compilatio
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_non_binop(tokens: &[IOValue]) -> Result<path::Predicate, CompilationError> {
|
||||
fn parse_non_binop(env: &Env, 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..])?) });
|
||||
return Ok(path::Predicate::Not { pred: Box::new(parse_non_binop(env, &tokens[1..])?) });
|
||||
}
|
||||
}
|
||||
|
||||
Ok(path::Predicate::Selector(Box::new(parse_selector(tokens)?)))
|
||||
Ok(path::Predicate::Selector(Box::new(parse_selector(env, tokens)?)))
|
||||
}
|
||||
|
||||
fn parse_step(tokens: &[IOValue]) -> Result<Option<(path::Step, &[IOValue])>, CompilationError> {
|
||||
fn parse_schema_definition_name(env: &Env, token: &IOValue) -> Result<(Vec<String>, String), CompilationError> {
|
||||
let defpath = token.value().to_symbol().map_err(|_| CompilationError::InvalidStep)?;
|
||||
let mut module: Vec<String> = defpath.split('.').map(|s| s.to_string()).collect();
|
||||
let name = module.pop().expect("at least one element in the Schema name");
|
||||
match env.lookup_definition(&module, &name) {
|
||||
Some(_) => Ok((module, name)),
|
||||
None => Err(CompilationError::UndefinedSchemaDefinitionName(format!("{:?}", token))),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_step<'a>(env: &Env, tokens: &'a [IOValue]) -> Result<Option<(path::Step, &'a [IOValue])>, CompilationError> {
|
||||
if tokens.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
@ -78,7 +89,7 @@ fn parse_step(tokens: &[IOValue]) -> Result<Option<(path::Step, &[IOValue])>, Co
|
|||
|
||||
if tokens[0].value().is_sequence() {
|
||||
return Ok(Some((path::Step::Filter(Box::new(path::Filter::Test {
|
||||
pred: Box::new(parse_predicate(tokens[0].value().as_sequence().unwrap())?),
|
||||
pred: Box::new(parse_predicate(env, tokens[0].value().as_sequence().unwrap())?),
|
||||
})), remainder)));
|
||||
}
|
||||
|
||||
|
@ -96,7 +107,16 @@ fn parse_step(tokens: &[IOValue]) -> Result<Option<(path::Step, &[IOValue])>, Co
|
|||
".length" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Length)), remainder))),
|
||||
".annotations" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Annotations)), remainder))),
|
||||
".embedded" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Embedded)), remainder))),
|
||||
|
||||
"%" => {
|
||||
let (defpath, remainder) = pop_step_arg(remainder)?;
|
||||
let (module, name) = parse_schema_definition_name(env, &defpath)?;
|
||||
Ok(Some((path::Step::Axis(Box::new(path::Axis::Parse { module, name })), remainder)))
|
||||
}
|
||||
"%-" => {
|
||||
let (defpath, remainder) = pop_step_arg(remainder)?;
|
||||
let (module, name) = parse_schema_definition_name(env, &defpath)?;
|
||||
Ok(Some((path::Step::Axis(Box::new(path::Axis::Unparse { module, name })), remainder)))
|
||||
}
|
||||
"*" => Ok(Some((path::Step::Filter(Box::new(path::Filter::Nop)), remainder))),
|
||||
"eq" | "=" => parse_comparison(remainder, path::Comparison::Eq),
|
||||
"ne" | "!=" => parse_comparison(remainder, path::Comparison::Ne),
|
||||
|
@ -170,20 +190,18 @@ fn parse_comparison(
|
|||
})), remainder)))
|
||||
}
|
||||
|
||||
impl std::str::FromStr for path::Selector {
|
||||
type Err = CompilationError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
parse_selector(&(BytesBinarySource::new(s.as_bytes())
|
||||
.text_iovalues()
|
||||
.configured(false)
|
||||
.collect::<Result<Vec<_>, _>>()?))
|
||||
impl path::Selector {
|
||||
pub fn from_str(env: &Env, s: &str) -> Result<Self, CompilationError> {
|
||||
parse_selector(env, &(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::Selector::from_str(s)?;
|
||||
impl Node {
|
||||
pub fn from_str(env: &Env, s: &str) -> Result<Self, CompilationError> {
|
||||
let expr = path::Selector::from_str(env, s)?;
|
||||
expr.compile()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
use preserves::value::IOValue;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum Path {
|
||||
Root,
|
||||
Step(IOValue, Rc<Path>),
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn root() -> Rc<Self> {
|
||||
Rc::new(Path::Root)
|
||||
}
|
||||
|
||||
pub fn step(self: &Rc<Self>, v: &IOValue) -> Rc<Self> {
|
||||
Rc::new(Path::Step(v.clone(), Rc::clone(self)))
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use crate::CompilationError;
|
||||
use crate::path::Path;
|
||||
use crate::context::Context;
|
||||
use crate::schemas::path;
|
||||
use crate::step::BoolCollector;
|
||||
use crate::step::Node;
|
||||
|
@ -7,10 +7,8 @@ use crate::step::StepMaker;
|
|||
|
||||
use preserves::value::IOValue;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
pub trait Predicate: std::fmt::Debug {
|
||||
fn test(&mut self, path: Rc<Path>, value: &IOValue) -> bool;
|
||||
fn test(&mut self, ctxt: &mut Context, value: &IOValue) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -35,19 +33,19 @@ impl path::Predicate {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn exec(&self, value: &IOValue) -> Result<bool, CompilationError> {
|
||||
Ok(self.compile()?.test(Path::root(), value))
|
||||
pub fn exec(&self, ctxt: &mut Context, value: &IOValue) -> Result<bool, CompilationError> {
|
||||
Ok(self.compile()?.test(ctxt, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate for CompiledPredicate {
|
||||
fn test(&mut self, path: Rc<Path>, value: &IOValue) -> bool {
|
||||
fn test(&mut self, ctxt: &mut Context, value: &IOValue) -> bool {
|
||||
match self {
|
||||
CompiledPredicate::Selector(n) => n.test(path, value),
|
||||
CompiledPredicate::Not(p) => !p.test(path, value),
|
||||
CompiledPredicate::Selector(n) => n.test(ctxt, value),
|
||||
CompiledPredicate::Not(p) => !p.test(ctxt, value),
|
||||
CompiledPredicate::Or(ps) => {
|
||||
for p in ps.iter_mut() {
|
||||
if p.test(Rc::clone(&path), value) {
|
||||
if p.test(ctxt, value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +53,7 @@ impl Predicate for CompiledPredicate {
|
|||
},
|
||||
CompiledPredicate::And(ps) => {
|
||||
for p in ps.iter_mut() {
|
||||
if !p.test(Rc::clone(&path), value) {
|
||||
if !p.test(ctxt, value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// If we could make Schemas produce generics...
|
||||
|
||||
use crate::CompilationError;
|
||||
use crate::path::Path;
|
||||
use crate::context::Context;
|
||||
use crate::predicate::CompiledPredicate;
|
||||
use crate::predicate::Predicate;
|
||||
use crate::schemas::path;
|
||||
|
@ -18,8 +18,9 @@ use preserves::value::NestedValue;
|
|||
use preserves::value::Value;
|
||||
use preserves::value::ValueClass;
|
||||
|
||||
use preserves_schema::support::interpret;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::iter::Iterator;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
@ -28,7 +29,7 @@ pub trait StepMaker {
|
|||
}
|
||||
|
||||
pub trait Step: std::fmt::Debug {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue);
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue);
|
||||
fn finish(&mut self);
|
||||
fn reset(&mut self) -> Vec<IOValue>;
|
||||
}
|
||||
|
@ -99,14 +100,14 @@ impl Node {
|
|||
Node(Rc::new(RefCell::new(s)))
|
||||
}
|
||||
|
||||
pub fn test(&self, path: Rc<Path>, value: &IOValue) -> bool {
|
||||
self.accept(path, value);
|
||||
pub fn test(&self, ctxt: &mut Context, value: &IOValue) -> bool {
|
||||
self.accept(ctxt, value);
|
||||
self.finish();
|
||||
!self.reset().is_empty()
|
||||
}
|
||||
|
||||
pub fn accept(&self, path: Rc<Path>, value: &IOValue) {
|
||||
self.0.borrow_mut().accept(path, value)
|
||||
pub fn accept(&self, ctxt: &mut Context, value: &IOValue) {
|
||||
self.0.borrow_mut().accept(ctxt, value)
|
||||
}
|
||||
|
||||
pub fn finish(&self) {
|
||||
|
@ -117,8 +118,8 @@ impl Node {
|
|||
self.0.borrow_mut().reset()
|
||||
}
|
||||
|
||||
pub fn exec(&self, value: &IOValue) -> Vec<IOValue> {
|
||||
self.accept(Path::root(), value);
|
||||
pub fn exec(&self, ctxt: &mut Context, value: &IOValue) -> Vec<IOValue> {
|
||||
self.accept(ctxt, value);
|
||||
self.finish();
|
||||
self.reset()
|
||||
}
|
||||
|
@ -154,83 +155,85 @@ impl StepMaker for path::Axis {
|
|||
}
|
||||
}
|
||||
|
||||
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, path: Rc<Path>, value: &IOValue) {
|
||||
match &self.axis {
|
||||
path::Axis::Values => {
|
||||
let path = path.step(value);
|
||||
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(Rc::clone(&path), &c)
|
||||
}
|
||||
}
|
||||
path::Axis::Descendants => {
|
||||
let mut q = VecDeque::new();
|
||||
q.push_back((path, value.clone()));
|
||||
while let Some((p, c)) = q.pop_front() {
|
||||
let p = p.step(&c);
|
||||
for cc in c.value().children() {
|
||||
q.push_back((Rc::clone(&p), cc.clone()));
|
||||
}
|
||||
self.step.accept(p, &c)
|
||||
}
|
||||
}
|
||||
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(path.step(value), s.chars(), &key, |c| IOValue::new(String::from(c)), &mut self.step),
|
||||
step_index(ctxt, 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),
|
||||
step_index(ctxt, 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),
|
||||
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(path.step(value), v)
|
||||
self.step.accept(ctxt, v)
|
||||
},
|
||||
_ =>
|
||||
(),
|
||||
},
|
||||
path::Axis::Label => if let Some(r) = value.value().as_record(None) {
|
||||
self.step.accept(path.step(value), r.label())
|
||||
self.step.accept(ctxt, r.label())
|
||||
},
|
||||
path::Axis::Keys => match value.value() {
|
||||
Value::String(s) | Value::Symbol(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::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) => {
|
||||
let path = path.step(value);
|
||||
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(Rc::clone(&path), k)
|
||||
}
|
||||
},
|
||||
self.step.accept(ctxt, k)
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
path::Axis::Length => match value.value() {
|
||||
Value::String(s) | Value::Symbol(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::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())),
|
||||
_ => self.step.accept(path.step(value), &IOValue::new(0)),
|
||||
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 => {
|
||||
let path = path.step(value);
|
||||
path::Axis::Annotations =>
|
||||
for c in value.annotations().slice() {
|
||||
self.step.accept(Rc::clone(&path), &c)
|
||||
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::Embedded => if let Some(d) = value.value().as_embedded() {
|
||||
self.step.accept(path.step(value), d)
|
||||
},
|
||||
}
|
||||
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>(
|
||||
p: Rc<Path>,
|
||||
ctxt: &mut Context,
|
||||
mut vs: Ts,
|
||||
key: &IOValue,
|
||||
f: F,
|
||||
|
@ -239,14 +242,14 @@ fn step_index<T, Ts: Iterator<Item = T>, F: FnOnce(T) -> IOValue>(
|
|||
if let Some(i) = key.value().as_usize() {
|
||||
match vs.nth(i) {
|
||||
None => (),
|
||||
Some(v) => step.accept(p, &f(v)),
|
||||
Some(v) => step.accept(ctxt, &f(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn step_keys(p: Rc<Path>, count: usize, step: &mut Node) {
|
||||
fn step_keys(ctxt: &mut Context, count: usize, step: &mut Node) {
|
||||
for i in 0 .. count {
|
||||
step.accept(Rc::clone(&p), &IOValue::new(i))
|
||||
step.accept(ctxt, &IOValue::new(i))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,7 +288,7 @@ impl StepMaker for path::Filter {
|
|||
}
|
||||
|
||||
impl Step for CompareStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
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,
|
||||
|
@ -294,7 +297,7 @@ impl Step for CompareStep {
|
|||
path::Comparison::Gt => value > &self.literal,
|
||||
path::Comparison::Le => value <= &self.literal,
|
||||
} {
|
||||
self.step.accept(path, value)
|
||||
self.step.accept(ctxt, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,10 +305,10 @@ impl Step for CompareStep {
|
|||
}
|
||||
|
||||
impl Step for RegexStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
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(path, value) },
|
||||
if self.regex.is_match(s) { self.step.accept(ctxt, value) },
|
||||
_ =>
|
||||
(),
|
||||
}
|
||||
|
@ -315,9 +318,9 @@ impl Step for RegexStep {
|
|||
}
|
||||
|
||||
impl Step for TestStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
if self.pred.test(Rc::clone(&path), value) {
|
||||
self.step.accept(path, value)
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
if self.pred.test(ctxt, value) {
|
||||
self.step.accept(ctxt, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,13 +328,13 @@ impl Step for TestStep {
|
|||
}
|
||||
|
||||
impl Step for RealStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
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(path, &IOValue::new(r))
|
||||
self.step.accept(ctxt, &IOValue::new(r))
|
||||
},
|
||||
Value::Float(f) => self.step.accept(path, &IOValue::new(f32::from(*f) as f64)),
|
||||
Value::Double(_) => self.step.accept(path, value),
|
||||
Value::Float(f) => self.step.accept(ctxt, &IOValue::new(f32::from(*f) as f64)),
|
||||
Value::Double(_) => self.step.accept(ctxt, value),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -340,14 +343,14 @@ impl Step for RealStep {
|
|||
}
|
||||
|
||||
impl Step for IntStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
match value.value() {
|
||||
Value::SignedInteger(_) => self.step.accept(path, value),
|
||||
Value::SignedInteger(_) => self.step.accept(ctxt, value),
|
||||
Value::Float(f) => if let Some(i) = BigInt::from_f32(f32::from(*f)) {
|
||||
self.step.accept(path, &IOValue::new(i))
|
||||
self.step.accept(ctxt, &IOValue::new(i))
|
||||
},
|
||||
Value::Double(d) => if let Some(i) = BigInt::from_f64(f64::from(*d)) {
|
||||
self.step.accept(path, &IOValue::new(i))
|
||||
self.step.accept(ctxt, &IOValue::new(i))
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -363,7 +366,7 @@ impl VecCollector {
|
|||
}
|
||||
|
||||
impl Step for VecCollector {
|
||||
fn accept(&mut self, _path: Rc<Path>, value: &IOValue) {
|
||||
fn accept(&mut self, _ctxt: &mut Context, value: &IOValue) {
|
||||
self.accumulator.push(value.clone())
|
||||
}
|
||||
|
||||
|
@ -382,7 +385,7 @@ impl BoolCollector {
|
|||
}
|
||||
|
||||
impl Step for BoolCollector {
|
||||
fn accept(&mut self, _path: Rc<Path>, _value: &IOValue) {
|
||||
fn accept(&mut self, _ctxt: &mut Context, _value: &IOValue) {
|
||||
self.seen_value = true
|
||||
}
|
||||
|
||||
|
@ -397,9 +400,9 @@ impl Step for BoolCollector {
|
|||
}
|
||||
|
||||
impl Step for KindStep {
|
||||
fn accept(&mut self, path: Rc<Path>, value: &IOValue) {
|
||||
fn accept(&mut self, ctxt: &mut Context, value: &IOValue) {
|
||||
if value.value_class() == self.kind {
|
||||
self.step.accept(path, value)
|
||||
self.step.accept(ctxt, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,7 +414,7 @@ impl path::Selector {
|
|||
self.connect(VecCollector::new())
|
||||
}
|
||||
|
||||
pub fn exec(&self, value: &IOValue) -> Result<Vec<IOValue>, CompilationError> {
|
||||
Ok(self.compile()?.exec(value))
|
||||
pub fn exec(&self, ctxt: &mut Context, value: &IOValue) -> Result<Vec<IOValue>, CompilationError> {
|
||||
Ok(self.compile()?.exec(ctxt, value))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "preserves-schema"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
authors = ["Tony Garnock-Jones <tonyg@leastfixedpoint.com>"]
|
||||
edition = "2018"
|
||||
description = "Implementation of Preserves Schema code generation and support for Rust."
|
||||
|
|
|
@ -105,6 +105,33 @@ pub struct CompilerConfig {
|
|||
pub plugins: Vec<Box<dyn Plugin>>,
|
||||
}
|
||||
|
||||
pub fn load_schema_or_bundle(bundle: &mut Map<ModulePath, Schema>, i: &PathBuf) -> io::Result<()> {
|
||||
let mut f = File::open(&i)?;
|
||||
let mut src = IOBinarySource::new(&mut f);
|
||||
let mut reader = src.packed_iovalues();
|
||||
let blob = reader.demand_next(false)?;
|
||||
let language = Language::default();
|
||||
|
||||
if let Ok(s) = language.parse(&blob) {
|
||||
let prefix = i.file_stem().ok_or_else(
|
||||
|| io::Error::new(io::ErrorKind::InvalidData,
|
||||
format!("Bad schema file stem: {:?}", i)))?
|
||||
.to_str().ok_or_else(
|
||||
|| io::Error::new(io::ErrorKind::InvalidData,
|
||||
format!("Invalid UTF-8 in schema file name: {:?}", i)))?;
|
||||
bundle.insert(vec![prefix.to_owned()], s);
|
||||
} else if let Ok(Bundle { modules }) = language.parse(&blob) {
|
||||
for (ModulePath(k), v) in modules.0 {
|
||||
bundle.insert(k, v);
|
||||
}
|
||||
} else {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData,
|
||||
format!("Invalid schema binary blob {:?}", i)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl CompilerConfig {
|
||||
pub fn new(
|
||||
output_dir: PathBuf,
|
||||
|
@ -134,32 +161,7 @@ impl CompilerConfig {
|
|||
|
||||
pub fn load_schemas_and_bundles(&mut self, inputs: &Vec<PathBuf>) -> io::Result<()> {
|
||||
for i in inputs {
|
||||
let mut f = File::open(&i)?;
|
||||
let mut src = IOBinarySource::new(&mut f);
|
||||
let mut reader = src.packed_iovalues();
|
||||
let blob = reader.demand_next(false)?;
|
||||
let language = Language::default();
|
||||
|
||||
if let Ok(s) = language.parse(&blob) {
|
||||
let prefix = i.file_stem().ok_or_else(
|
||||
|| io::Error::new(io::ErrorKind::InvalidData,
|
||||
format!("Bad schema file stem: {:?}", i)))?
|
||||
.to_str().ok_or_else(
|
||||
|| io::Error::new(io::ErrorKind::InvalidData,
|
||||
format!("Invalid UTF-8 in schema file name: {:?}", i)))?;
|
||||
self.bundle.insert(vec![prefix.to_owned()], s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(Bundle { modules }) = language.parse(&blob) {
|
||||
for (ModulePath(k), v) in modules.0 {
|
||||
self.bundle.insert(k, v);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData,
|
||||
format!("Invalid schema binary blob {:?}", i)));
|
||||
load_schema_or_bundle(&mut self.bundle, i)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
use crate::gen::schema::*;
|
||||
|
||||
use preserves::value::Map;
|
||||
use preserves::value::NestedValue;
|
||||
use preserves::value::Value;
|
||||
use preserves::value::merge::merge2;
|
||||
|
||||
pub type Env<V> = Map<Vec<String>, Schema<V>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context<'a, V: NestedValue> {
|
||||
pub env: &'a Env<V>,
|
||||
module: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DynField<V: NestedValue> {
|
||||
Simple(V),
|
||||
Compound(Map<V, V>),
|
||||
}
|
||||
|
||||
impl<'a, V: NestedValue> Context<'a, V> {
|
||||
pub fn new(env: &'a Env<V>) -> Self {
|
||||
Context {
|
||||
env,
|
||||
module: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dynamic_parse(&mut self, module: &Vec<String>, name: &str, v: &V) -> Option<V> {
|
||||
let old_module = (module.len() > 0).then(|| std::mem::replace(&mut self.module, module.clone()));
|
||||
let schema = self.env.get(&self.module);
|
||||
let definition = schema.and_then(|s| s.definitions.0.get(name));
|
||||
let result = definition.and_then(|d| d.dynamic_parse(self, v));
|
||||
if let Some(m) = old_module {
|
||||
self.module = m;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn dynamic_unparse(&mut self, module: &Vec<String>, name: &str, w: &V) -> Option<V> {
|
||||
panic!("Not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> Definition<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<V> {
|
||||
match self {
|
||||
Definition::Or { pattern_0, pattern_1, pattern_n } =>
|
||||
pattern_0.dynamic_parse(ctxt, v)
|
||||
.or_else(|| pattern_1.dynamic_parse(ctxt, v))
|
||||
.or_else(|| pattern_n.iter().find_map(|p| p.dynamic_parse(ctxt, v))),
|
||||
Definition::And { pattern_0, pattern_1, pattern_n } =>
|
||||
pattern_0.dynamic_parse(ctxt, v)
|
||||
.and_then(|w0| pattern_1.dynamic_parse(ctxt, v)
|
||||
.and_then(|w1| pattern_n.iter().fold(merge(w0, w1), |w, p| {
|
||||
w.and_then(|w| p.dynamic_parse(ctxt, v).and_then(
|
||||
|wn| merge(w, wn)))
|
||||
})))
|
||||
.map(|w| DynField::Compound(w).finish()),
|
||||
Definition::Pattern(p) => p.dynamic_parse(ctxt, v).map(|w| w.finish()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> NamedAlternative<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<V> {
|
||||
self.pattern.dynamic_parse(ctxt, v).map(|w| {
|
||||
let mut r = Value::simple_record(&self.variant_label, 1);
|
||||
match w {
|
||||
DynField::Simple(field) => r.fields_vec_mut().push(field),
|
||||
DynField::Compound(fields) => if fields.len() > 0 {
|
||||
r.fields_vec_mut().push(V::new(fields))
|
||||
}
|
||||
}
|
||||
r.finish().wrap()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> NamedPattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<Map<V, V>> {
|
||||
match self {
|
||||
NamedPattern::Named(b) => {
|
||||
let binding = &**b;
|
||||
binding.pattern.dynamic_parse(ctxt, v).map(|w| w.to_map(Some(&binding.name)))
|
||||
}
|
||||
NamedPattern::Anonymous(b) => b.dynamic_parse(ctxt, v).map(|w| w.to_map(None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> NamedSimplePattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
|
||||
match self {
|
||||
NamedSimplePattern::Named(b) => {
|
||||
let binding = &**b;
|
||||
binding.pattern.dynamic_parse(ctxt, v).map(
|
||||
|w| DynField::Compound(w.to_map(Some(&binding.name))))
|
||||
}
|
||||
NamedSimplePattern::Anonymous(b) =>
|
||||
b.dynamic_parse(ctxt, v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> SimplePattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
|
||||
match self {
|
||||
SimplePattern::Any => Some(DynField::Simple(v.clone())),
|
||||
SimplePattern::Atom { atom_kind } => match &**atom_kind {
|
||||
AtomKind::Boolean => v.value().is_boolean().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::Float => v.value().is_float().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::Double => v.value().is_double().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::SignedInteger => v.value().is_signedinteger().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::String => v.value().is_string().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::ByteString => v.value().is_bytestring().then(|| DynField::Simple(v.clone())),
|
||||
AtomKind::Symbol => v.value().is_symbol().then(|| DynField::Simple(v.clone())),
|
||||
},
|
||||
SimplePattern::Embedded { .. } => v.value().is_embedded().then(|| DynField::Simple(v.clone())),
|
||||
SimplePattern::Lit { value } => (v == value).then(|| DynField::Compound(Map::new())),
|
||||
SimplePattern::Seqof { pattern } =>
|
||||
v.value().as_sequence()
|
||||
.and_then(|vs| vs.iter().map(|v| (**pattern).dynamic_parse(ctxt, v).map(|w| w.finish()))
|
||||
.collect::<Option<Vec<V>>>())
|
||||
.map(|ws| DynField::Simple(V::new(ws))),
|
||||
SimplePattern::Setof { pattern } =>
|
||||
v.value().as_set()
|
||||
.and_then(|vs| vs.iter().map(|v| (**pattern).dynamic_parse(ctxt, v).map(|w| w.finish()))
|
||||
.collect::<Option<Vec<V>>>())
|
||||
.map(|ws| DynField::Simple(V::new(ws))),
|
||||
SimplePattern::Dictof { key, value } =>
|
||||
v.value().as_dictionary()
|
||||
.and_then(|d| {
|
||||
d.iter().map(|(k, v)| {
|
||||
(**key).dynamic_parse(ctxt, k)
|
||||
.and_then(|kw| (**value).dynamic_parse(ctxt, v)
|
||||
.map(|vw| (kw.finish(), vw.finish())))
|
||||
}).collect::<Option<Map<V, V>>>()
|
||||
})
|
||||
.map(|d| DynField::Simple(V::new(d))),
|
||||
SimplePattern::Ref(r) =>
|
||||
ctxt.dynamic_parse(&r.module.0, &r.name, v).map(DynField::Simple),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> CompoundPattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<Map<V, V>> {
|
||||
match self {
|
||||
CompoundPattern::Rec { label, fields } =>
|
||||
v.value().as_record(None).and_then(
|
||||
|r| (**label).dynamic_parse(ctxt, r.label()).and_then(
|
||||
|lw| (**fields).dynamic_parse(ctxt, &V::new(r.fields().to_vec())).and_then(
|
||||
|fsw| merge(lw, fsw)))),
|
||||
CompoundPattern::Tuple { patterns } =>
|
||||
v.value().as_sequence().and_then(
|
||||
|vs| if vs.len() == patterns.len() {
|
||||
patterns.iter().zip(vs)
|
||||
.fold(Some(Map::new()), |acc, (p, v)| {
|
||||
acc.and_then(|acc| p.dynamic_parse(ctxt, v).and_then(
|
||||
|w| merge(acc, w)))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
CompoundPattern::TuplePrefix { fixed, variable } =>
|
||||
v.value().as_sequence().and_then(
|
||||
|vs| if vs.len() >= fixed.len() {
|
||||
fixed.iter().zip(vs)
|
||||
.fold(Some(Map::new()), |acc, (p, v)| {
|
||||
acc.and_then(|acc| p.dynamic_parse(ctxt, v).and_then(
|
||||
|w| merge(acc, w)))
|
||||
}).and_then(|fixed_ws| {
|
||||
let remainder = V::new(vs[fixed.len()..].to_vec());
|
||||
(**variable).dynamic_parse(ctxt, &remainder).and_then(
|
||||
|variable_ws| merge(fixed_ws, variable_ws.unwrap_compound()))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
CompoundPattern::Dict { entries } =>
|
||||
v.value().as_dictionary().and_then(
|
||||
|d| (**entries).0.iter().fold(Some(Map::new()), |acc, (k, p)| {
|
||||
acc.and_then(|acc| d.get(k).and_then(|v| p.dynamic_parse(ctxt, v).and_then(
|
||||
|w| merge(acc, w.unwrap_compound()))))
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> Pattern<V> {
|
||||
fn dynamic_parse(&self, ctxt: &mut Context<V>, v: &V) -> Option<DynField<V>> {
|
||||
match self {
|
||||
Pattern::SimplePattern(b) => (**b).dynamic_parse(ctxt, v),
|
||||
Pattern::CompoundPattern(b) => (**b).dynamic_parse(ctxt, v).map(DynField::Compound),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NestedValue> DynField<V> {
|
||||
fn finish(self) -> V {
|
||||
match self {
|
||||
DynField::Simple(v) => v,
|
||||
DynField::Compound(v) => V::new(v),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_map(self, key: Option<&str>) -> Map<V, V> {
|
||||
match self {
|
||||
DynField::Simple(v) => {
|
||||
let mut d = Map::new();
|
||||
if let Some(k) = key {
|
||||
d.insert(V::symbol(k), v);
|
||||
}
|
||||
d
|
||||
}
|
||||
DynField::Compound(d) => d,
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_compound(self) -> Map<V, V> {
|
||||
match self {
|
||||
DynField::Simple(_) => panic!("Cannot unwrap DynField::Simple to compound fields"),
|
||||
DynField::Compound(d) => d,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn merge<V: NestedValue>(a: Map<V, V>, b: Map<V, V>) -> Option<Map<V, V>> {
|
||||
merge2(V::new(a), V::new(b))
|
||||
.map(|d| d.value_owned().into_dictionary().expect("merge to yield Dictionary"))
|
||||
}
|
|
@ -4,6 +4,8 @@ pub use preserves;
|
|||
pub use preserves::value::Reader;
|
||||
pub use preserves::value::boundary as B;
|
||||
|
||||
pub mod interpret;
|
||||
|
||||
use preserves::value::ArcValue;
|
||||
use preserves::value::Domain;
|
||||
use preserves::value::IOValue;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "preserves-tools"
|
||||
version = "2.2.1"
|
||||
version = "2.3.0"
|
||||
authors = ["Tony Garnock-Jones <tonyg@leastfixedpoint.com>"]
|
||||
edition = "2018"
|
||||
description = "Command-line utilities for working with Preserves documents."
|
||||
|
@ -10,7 +10,8 @@ license = "Apache-2.0"
|
|||
|
||||
[dependencies]
|
||||
preserves = { path = "../preserves", version = "^2.2.0"}
|
||||
preserves-path = { path = "../preserves-path", version = "^2.2.0"}
|
||||
preserves-path = { path = "../preserves-path", version = "^3.0.0"}
|
||||
preserves-schema = { path = "../preserves-schema", version = "^2.3.0"}
|
||||
|
||||
bytes = "1.0"
|
||||
clap = "=3.0.0-beta.2"
|
||||
|
|
|
@ -98,8 +98,8 @@ struct Convert {
|
|||
#[clap(long, arg_enum, value_name = "on/off", default_value = "on")]
|
||||
indent: Boolish,
|
||||
|
||||
#[clap(long, default_value="*")]
|
||||
select: preserves_path::Node,
|
||||
#[clap(long="select", default_value="*")]
|
||||
select_expr: String,
|
||||
|
||||
#[clap(long, arg_enum, default_value="sequence")]
|
||||
select_output: SelectOutput,
|
||||
|
@ -112,6 +112,12 @@ struct Convert {
|
|||
|
||||
#[clap(long, arg_enum, value_name = "on/off", default_value = "on")]
|
||||
write_annotations: Boolish,
|
||||
|
||||
#[clap(long, value_name = "filename")]
|
||||
bundle: Vec<std::path::PathBuf>,
|
||||
|
||||
#[clap(long)]
|
||||
schema: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Debug)]
|
||||
|
@ -133,7 +139,6 @@ struct StringQuotation {
|
|||
escape_spaces: bool,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clap, Clone, Debug)]
|
||||
enum QuotationOutput {
|
||||
String(StringQuotation),
|
||||
|
@ -152,11 +157,11 @@ struct Quote {
|
|||
|
||||
#[derive(Clap, Clone, Debug)]
|
||||
enum Subcommand {
|
||||
Convert(Convert),
|
||||
Completions {
|
||||
#[clap(arg_enum, value_name = "dialect")]
|
||||
dialect: CompletionDialect,
|
||||
},
|
||||
Convert(Convert),
|
||||
Quote(Quote),
|
||||
}
|
||||
|
||||
|
@ -169,7 +174,7 @@ struct CommandLine {
|
|||
|
||||
fn main() -> io::Result<()> {
|
||||
let args = CommandLine::parse();
|
||||
match args.command {
|
||||
Ok(match args.command {
|
||||
Subcommand::Completions { dialect } => {
|
||||
let mut app = CommandLine::into_app();
|
||||
match dialect {
|
||||
|
@ -182,8 +187,7 @@ fn main() -> io::Result<()> {
|
|||
},
|
||||
Subcommand::Convert(c) => convert(c)?,
|
||||
Subcommand::Quote(q) => quote(q)?,
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
struct RollingBuffer<R: io::Read> {
|
||||
|
@ -365,6 +369,14 @@ fn print_unquoted(v: &IOValue) {
|
|||
}
|
||||
|
||||
fn convert(c: Convert) -> io::Result<()> {
|
||||
let mut env = preserves_path::Env::new();
|
||||
for f in c.bundle.iter() {
|
||||
env.load_bundle(f)?;
|
||||
}
|
||||
let select = preserves_path::Node::from_str(&env, &c.select_expr)
|
||||
.map_err(|e| io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Invalid select expression: {}: {:?}", e, c.select_expr)))?;
|
||||
let mut vs = ValueStream::new(c.input_format, c.read_annotations.into(), io::stdin());
|
||||
let write_ann: bool = c.write_annotations.into();
|
||||
let mut w: Box<dyn FnMut(&IOValue) -> io::Result<()>> = match c.output_format {
|
||||
|
@ -385,18 +397,24 @@ fn convert(c: Convert) -> io::Result<()> {
|
|||
}
|
||||
OutputFormat::Binary => {
|
||||
let mut p = PackedWriter::new(io::stdout());
|
||||
Box::new(move |v| if write_ann {
|
||||
p.write(&mut IOValueDomainCodec, v)
|
||||
} else {
|
||||
p.write(&mut IOValueDomainCodec, &v.strip_annotations::<IOValue>())
|
||||
Box::new(move |v| {
|
||||
if write_ann {
|
||||
p.write(&mut IOValueDomainCodec, v)?;
|
||||
} else {
|
||||
p.write(&mut IOValueDomainCodec, &v.strip_annotations::<IOValue>())?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
OutputFormat::Unquoted =>
|
||||
Box::new(|v| Ok(print_unquoted(v))),
|
||||
Box::new(|v| {
|
||||
print_unquoted(v);
|
||||
Ok(())
|
||||
}),
|
||||
};
|
||||
while let Some(value) = vs.next() {
|
||||
let value = value?;
|
||||
let matches = c.select.exec(&value);
|
||||
let matches = select.exec(&mut env.to_context(), &value);
|
||||
if c.collect {
|
||||
match c.select_output {
|
||||
SelectOutput::Sequence => w(&IOValue::new(matches))?,
|
||||
|
@ -410,7 +428,7 @@ fn convert(c: Convert) -> io::Result<()> {
|
|||
}
|
||||
if let Some(limit) = c.limit {
|
||||
if vs.count >= limit {
|
||||
return Ok(());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
´³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µ„³
|
||||
´³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µ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³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„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±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„„µ±
|
||||
|
|
|
@ -20,6 +20,8 @@ Axis =
|
|||
/ <length>
|
||||
/ <annotations>
|
||||
/ <embedded>
|
||||
/ <parse @module [symbol ...] @name symbol>
|
||||
/ <unparse @module [symbol ...] @name symbol>
|
||||
.
|
||||
|
||||
Filter =
|
||||
|
|
|
@ -52,6 +52,8 @@ Axes: move around, applying filters after moving
|
|||
.length ;; Moves into the number of keys
|
||||
.annotations ;; Moves into any annotations that might be present
|
||||
.embedded ;; Moves into the representation of an embedded value
|
||||
% name ;; Moves into successful Preserves Schema parse of definition `name`
|
||||
%- name ;; Moves into successful Preserves Schema unparse of definition `name`
|
||||
|
||||
Sets have children, but no keys/length; Strings, ByteStrings and
|
||||
Symbols have no children, but have keys/length.
|
||||
|
|
Loading…
Reference in New Issue