Parser implementation
* Adaptation of the grammar to make more regular w.r.t reference space
names placement.
* Implemented Type check
* Implemented prediction of the result set cardinality. This assumes:
+ A space is not infinite, this is required to compute complementary
sets as we can compute the whole space volume.
+ Density is mostly uniform, this allows us to assume the number of
results is related with a constant factor to the volume in space
of the selection.
+ The prediction is approximated by using the most pessimistic
resulting cardinality for each operator, in order to keep the
operation simple, and fast, at the expense of the precision.
* Implemented execution, which calls into a DB abstraction layer. That
layer is currently mostly empty. Execution is also implemented in a
naive way, and should most likely be optimised.
This commit is contained in:
@@ -17,6 +17,7 @@ path = "src/main.rs"
|
||||
lalrpop-util = "0.17.0"
|
||||
regex = "0.2.1"
|
||||
measure_time = "0.6" # To mesure parsing time, only required by binary
|
||||
lazy_static = "1.3.0" # for space.rs, might be removed once the full data model is provided by the use of the crate
|
||||
|
||||
# Logging macros API
|
||||
log = { version = "0.4", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
|
||||
@@ -134,33 +134,38 @@ hyperrectangle
|
||||
: 'hyperrectangle' '{'
|
||||
position ',' position
|
||||
( ',' position ',' position )*
|
||||
( ',' STRING )?
|
||||
'}'
|
||||
;
|
||||
|
||||
/* A hypersphere is defined by its center and a radius, independantly
|
||||
* of the number of dimensions of the space. */
|
||||
hypersphere
|
||||
: 'hypersphere' '{' position ( ',' positive_number ) '}'
|
||||
: 'hypersphere' '{'
|
||||
position
|
||||
',' positive_number
|
||||
( ',' STRING )?
|
||||
'}'
|
||||
;
|
||||
|
||||
point
|
||||
: 'point' '{' position '}'
|
||||
: 'point' '{' position ( ',' STRING )? '}'
|
||||
;
|
||||
|
||||
/* Define a shape as the non-zero values in a NIfTI object, defined by
|
||||
* nifti{
|
||||
* spaceId: string,
|
||||
* lower_corner: position, // Optional, default to the origin
|
||||
* rotation: [ position+ ], // Optional, no rotation by default
|
||||
* bytes: uri(STRING) // uri to the NIfTI object
|
||||
* bytes: uri(STRING), // uri to the NIfTI object
|
||||
* spaceId: string
|
||||
* }
|
||||
*/
|
||||
nifti
|
||||
: 'nifti' '{'
|
||||
STRING ','
|
||||
(position ',' )?
|
||||
( '[' position ( ',' position )* ']' ',' )?
|
||||
byte_provider
|
||||
byte_provider ','
|
||||
STRING
|
||||
'}'
|
||||
;
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@ projection_operators
|
||||
*
|
||||
* If it is provided, it MUST resolve to a NUMBER. */
|
||||
nifti_operator
|
||||
: 'nifti' '(' ( selector ',' )? bag_expression ')'
|
||||
: 'nifti' '(' ( STRING ',' )? ( selector ',' )? bag_expression ')'
|
||||
;
|
||||
|
||||
json_operator
|
||||
: 'json' '(' jslt ',' bag_expression ')'
|
||||
: 'json' '(' jslt ',' bag_expression ( ',' STRING )? ')'
|
||||
;
|
||||
|
||||
jslt
|
||||
|
||||
113
src/ast.rs
113
src/ast.rs
@@ -1,113 +0,0 @@
|
||||
trait Expression {
|
||||
fn check_type();
|
||||
fn predict();
|
||||
fn execute();
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* FORMATTING DATA */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Projection {
|
||||
Nifti(LiteralSelector, Bag),
|
||||
JSON(JsonValue, Bag),
|
||||
}
|
||||
|
||||
// JSON FORMAT
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum JsonValue {
|
||||
String(String),
|
||||
JsonNumber(LiteralNumber),
|
||||
Bool(bool),
|
||||
Null,
|
||||
Object(Vec<(String, JsonValue)>),
|
||||
Array(Vec<JsonValue>),
|
||||
Selector(LiteralSelector),
|
||||
Aggregation(Aggregation),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Aggregation {
|
||||
Count(bool, LiteralSelector),
|
||||
Sum(LiteralSelector),
|
||||
Min(LiteralSelector),
|
||||
Max(LiteralSelector),
|
||||
}
|
||||
|
||||
// NIFTI
|
||||
#[derive(Clone, Debug)]
|
||||
struct Transform {
|
||||
reference: String,
|
||||
offset: Vec<LiteralNumber>,
|
||||
rotation: Vec<Vec<LiteralNumber>>,
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* SELECTING / FILTERING DATA */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Bag {
|
||||
Distinct(Box<Bag>),
|
||||
Filter(Option<Predicate>, Option<Box<Bag>>),
|
||||
Complement(Box<Bag>),
|
||||
Intersection(Box<Bag>, Box<Bag>),
|
||||
Union(Box<Bag>, Box<Bag>),
|
||||
Bag(Vec<Bag>),
|
||||
Inside(Shape),
|
||||
Outside(Shape),
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* BAG OPERATORS */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Predicate {
|
||||
Less(Position, LiteralPosition),
|
||||
Greater(Position, LiteralPosition),
|
||||
Equal(Position, LiteralPosition),
|
||||
Not(Box<Predicate>),
|
||||
And(Box<Predicate>, Box<Predicate>),
|
||||
Or(Box<Predicate>, Box<Predicate>),
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* SPATIAL OPERATORS */
|
||||
/**********************************************************************/
|
||||
|
||||
/**********************************************************************/
|
||||
/* SHAPES */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Shape {
|
||||
Point(LiteralPosition),
|
||||
HyperRectangle(Vec<LiteralPosition>),
|
||||
HyperSphere(LiteralPosition, LiteralNumber),
|
||||
Nifti(),
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* POSITIONS */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Position {
|
||||
StrCmpICase(LiteralSelector, String),
|
||||
StrCmp(LiteralSelector, String),
|
||||
Selector(LiteralSelector),
|
||||
LiteralPosition(LiteralPosition),
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* Literals / TOKENS */
|
||||
/**********************************************************************/
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Field(pub String, pub Option<usize>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum LiteralNumber {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
}
|
||||
|
||||
pub type LiteralPosition = Vec<LiteralNumber>;
|
||||
pub type LiteralSelector = Vec<Field>;
|
||||
57
src/database/mod.rs
Normal file
57
src/database/mod.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::executors::ResultSet;
|
||||
use crate::symbols::*;
|
||||
|
||||
//use ironsea_index::*;
|
||||
|
||||
pub fn get_all(_space_id: &String) -> ResultSet {
|
||||
//space::get_all(space_id)
|
||||
Err("not yet implemented".to_string())
|
||||
}
|
||||
|
||||
pub fn get_by_bounding_box(_space_id: &String, _bounding_box: &Vec<LiteralPosition>) -> ResultSet {
|
||||
Err("not yet implemented".to_string())
|
||||
}
|
||||
|
||||
pub fn get_by_position(_space_id: &String, _position: &LiteralPosition) -> ResultSet {
|
||||
Err("not yet implemented".to_string())
|
||||
}
|
||||
|
||||
pub fn get(_point: &Object, _fields: &Vec<Field>) -> Result<LiteralPosition, String> {
|
||||
Err("not yet implemented".to_string())
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct Object {
|
||||
id: u32,
|
||||
}
|
||||
|
||||
impl Object {
|
||||
pub fn length(&self) -> f64 {
|
||||
0.0
|
||||
}
|
||||
|
||||
pub fn eval(&self, _predicate: &Predicate) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn eval(&self, point: &Object) -> LiteralPosition {
|
||||
match self {
|
||||
Position::StrCmpICase(_selector, _string) => LiteralPosition(vec![]), //TODO
|
||||
Position::StrCmp(_selector, _string) => LiteralPosition(vec![]), //TODO
|
||||
Position::Selector(selector) => selector.eval(point),
|
||||
Position::Literal(position) => position.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LiteralSelector {
|
||||
pub fn eval(&self, point: &Object) -> LiteralPosition {
|
||||
let LiteralSelector(fields) = self;
|
||||
match get(point, fields) {
|
||||
Err(_) => LiteralPosition(vec![]),
|
||||
Ok(p @ LiteralPosition(_)) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/database/space.rs
Normal file
86
src/database/space.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::types::LiteralTypes;
|
||||
|
||||
//FIXME: Improve, as this should not be static, but coming from DB.
|
||||
lazy_static! {
|
||||
static ref UNIVERSE: String = "Universe".to_string();
|
||||
static ref SPACES: HashMap<&'static str, Space> = {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(
|
||||
"Universe",
|
||||
Space {
|
||||
space_type: LiteralTypes::Vector(vec![
|
||||
LiteralTypes::Float,
|
||||
LiteralTypes::Float,
|
||||
LiteralTypes::Float,
|
||||
]),
|
||||
bounding_box: vec![
|
||||
vec![0, 0, 0],
|
||||
vec![
|
||||
std::u32::MAX as i64,
|
||||
std::u32::MAX as i64,
|
||||
std::u32::MAX as i64,
|
||||
],
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
m
|
||||
};
|
||||
}
|
||||
|
||||
struct Space {
|
||||
space_type: LiteralTypes,
|
||||
bounding_box: Vec<Vec<i64>>,
|
||||
}
|
||||
|
||||
impl Space {
|
||||
pub fn max_volume(&self) -> f64 {
|
||||
let mut volume = 1.0;
|
||||
for max in &self.bounding_box[1] {
|
||||
volume *= *max as f64;
|
||||
}
|
||||
|
||||
volume
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name() -> &'static String {
|
||||
lazy_static! {
|
||||
static ref UNIVERSE: String = "Universe".to_string();
|
||||
};
|
||||
|
||||
&UNIVERSE
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_type(space_id: &String) -> &LiteralTypes {
|
||||
lazy_static! {
|
||||
static ref EMPTY_TYPE: LiteralTypes = LiteralTypes::Vector(Vec::new());
|
||||
};
|
||||
|
||||
match SPACES.get(space_id.as_str()) {
|
||||
None => &EMPTY_TYPE,
|
||||
Some(space) => &space.space_type,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bounding_box(space_id: &String) -> &Vec<Vec<i64>> {
|
||||
lazy_static! {
|
||||
static ref EMPTY_BOX: Vec<Vec<i64>> = Vec::new();
|
||||
};
|
||||
match SPACES.get(space_id.as_str()) {
|
||||
None => &EMPTY_BOX,
|
||||
Some(space) => &space.bounding_box,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_volume(space_id: &String) -> f64 {
|
||||
match SPACES.get(space_id.as_str()) {
|
||||
None => 0.0,
|
||||
Some(space) => space.max_volume(),
|
||||
}
|
||||
}
|
||||
314
src/executors.rs
Normal file
314
src/executors.rs
Normal file
@@ -0,0 +1,314 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::database;
|
||||
use super::expression::*;
|
||||
use super::symbols::*;
|
||||
|
||||
pub type ResultSet = Result<Vec<database::Object>, String>;
|
||||
|
||||
impl Executor for Projection {
|
||||
type ResultSet = self::ResultSet;
|
||||
|
||||
fn execute(&self) -> ResultSet {
|
||||
match self {
|
||||
Projection::Nifti(_, _, _bag) => Err("not yet implemented".to_string()),
|
||||
Projection::JSON(_, _format, bag) => bag.execute(), // FIXME: Add projections here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Executor for Bag {
|
||||
type ResultSet = self::ResultSet;
|
||||
|
||||
fn execute(&self) -> ResultSet {
|
||||
fn get_bounding_box(
|
||||
position: &LiteralPosition,
|
||||
radius: &LiteralNumber,
|
||||
) -> Result<Vec<LiteralPosition>, String> {
|
||||
let LiteralPosition(position) = position;
|
||||
let mut low = vec![];
|
||||
let mut high = vec![];
|
||||
match radius {
|
||||
LiteralNumber::Int(r) => {
|
||||
for x in position {
|
||||
match x {
|
||||
LiteralNumber::Int(x) => {
|
||||
low.push(LiteralNumber::Int(x - r));
|
||||
high.push(LiteralNumber::Int(x + r));
|
||||
}
|
||||
LiteralNumber::Float(x) => {
|
||||
low.push(LiteralNumber::Float(x - (*r as f64)));
|
||||
high.push(LiteralNumber::Float(x + (*r as f64)));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
LiteralNumber::Float(r) => {
|
||||
for x in position {
|
||||
match x {
|
||||
LiteralNumber::Int(_) => {
|
||||
return Err(format!("The radius provided is a floating point value, which is incompatible with integer coordinates components: radius {:?}, coordinates {:?}", radius, position));
|
||||
}
|
||||
LiteralNumber::Float(x) => {
|
||||
low.push(LiteralNumber::Float(x - r));
|
||||
high.push(LiteralNumber::Float(x + r));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vec![LiteralPosition(low), LiteralPosition(high)])
|
||||
};
|
||||
|
||||
match self {
|
||||
Bag::Distinct(bag) => match bag.execute() {
|
||||
e @ Err(_) => e,
|
||||
Ok(mut v) => {
|
||||
let set: HashSet<_> = v.drain(..).collect(); // dedup
|
||||
v.extend(set.into_iter());
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
},
|
||||
Bag::Filter(predicate, bag) => match predicate {
|
||||
None => bag.execute(),
|
||||
Some(predicate) => match bag.execute() {
|
||||
e @ Err(_) => e,
|
||||
Ok(source) => {
|
||||
let mut filtered = Vec::new();
|
||||
|
||||
for point in source {
|
||||
if point.eval(predicate) {
|
||||
filtered.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(filtered)
|
||||
}
|
||||
},
|
||||
},
|
||||
Bag::Complement(bag) => match bag.execute() {
|
||||
// The complement of a set is computed within its definition space.
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => {
|
||||
let mut outside = Vec::new();
|
||||
match database::get_all(bag.space()) {
|
||||
e @ Err(_) => e,
|
||||
Ok(points) => {
|
||||
for point in points {
|
||||
if !inside.contains(&point) {
|
||||
outside.push(point)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(outside)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Bag::Intersection(lh, rh) => {
|
||||
let l = lh.execute();
|
||||
if let Ok(l) = l {
|
||||
let r = rh.execute();
|
||||
if let Ok(r) = r {
|
||||
let mut v = vec![];
|
||||
|
||||
if rh.predict() < lh.predict() {
|
||||
for o in r {
|
||||
if l.contains(&o) {
|
||||
v.push(o);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for o in l {
|
||||
if r.contains(&o) {
|
||||
v.push(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(v)
|
||||
} else {
|
||||
r
|
||||
}
|
||||
} else {
|
||||
l
|
||||
}
|
||||
}
|
||||
Bag::Union(lh, rh) => {
|
||||
let l = lh.execute();
|
||||
if let Ok(mut l) = l {
|
||||
let r = rh.execute();
|
||||
if let Ok(mut r) = r {
|
||||
if rh.predict() < lh.predict() {
|
||||
l.append(&mut r);
|
||||
Ok(l)
|
||||
} else {
|
||||
r.append(&mut l);
|
||||
Ok(r)
|
||||
}
|
||||
} else {
|
||||
r
|
||||
}
|
||||
} else {
|
||||
l
|
||||
}
|
||||
}
|
||||
Bag::Bag(bags) => {
|
||||
let mut v = vec![];
|
||||
for bag in bags {
|
||||
let b = bag.execute();
|
||||
match b {
|
||||
e @ Err(_) => {
|
||||
return e;
|
||||
}
|
||||
Ok(mut b) => {
|
||||
//TODO: SPACE CONVERSIONS IF NOT THE SAME SPACES?
|
||||
v.append(&mut b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
Bag::Inside(shape) => match shape {
|
||||
Shape::Point(space_id, position) => database::get_by_position(space_id, position),
|
||||
Shape::HyperRectangle(space_id, bounding_box) => {
|
||||
database::get_by_bounding_box(space_id, bounding_box)
|
||||
}
|
||||
Shape::HyperSphere(space_id, position, radius) => {
|
||||
let length = match radius {
|
||||
LiteralNumber::Int(x) => *x as f64,
|
||||
LiteralNumber::Float(x) => *x,
|
||||
};
|
||||
|
||||
match get_bounding_box(position, radius) {
|
||||
Err(e) => Err(e),
|
||||
Ok(inside) => match database::get_by_bounding_box(space_id, &inside) {
|
||||
e @ Err(_) => e,
|
||||
Ok(source) => {
|
||||
let mut filtered = vec![];
|
||||
|
||||
for point in source {
|
||||
// Include the surface of the sphere
|
||||
if point.length() <= length {
|
||||
filtered.push(point);
|
||||
}
|
||||
}
|
||||
Ok(filtered)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Shape::Nifti(_space_id) => Err("not yet implemented".to_string()),
|
||||
},
|
||||
Bag::Outside(shape) => {
|
||||
fn outside_set(space_id: &String, inside: Vec<database::Object>) -> ResultSet {
|
||||
let mut outside = Vec::new();
|
||||
match database::get_all(space_id) {
|
||||
e @ Err(_) => e,
|
||||
Ok(points) => {
|
||||
for point in points {
|
||||
if !inside.contains(&point) {
|
||||
outside.push(point)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(outside)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match shape {
|
||||
Shape::Point(space_id, position) => {
|
||||
match database::get_by_position(space_id, position) {
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => outside_set(space_id, inside),
|
||||
}
|
||||
}
|
||||
Shape::HyperRectangle(space_id, bounding_box) => {
|
||||
// We need to adapt the bounding_box to ensure the
|
||||
// surface will not hit as part of the inside set, so we
|
||||
// compute the biggest bounding box contained within the
|
||||
// given box.
|
||||
|
||||
// Smallest increment possible
|
||||
let mut low: Vec<LiteralNumber> = vec![];
|
||||
let LiteralPosition(coordinates) = &bounding_box[0];
|
||||
for coordinate in coordinates {
|
||||
match coordinate {
|
||||
LiteralNumber::Int(x) => low.push(LiteralNumber::Int(x + 1)),
|
||||
LiteralNumber::Float(x) => {
|
||||
low.push(LiteralNumber::Float(x + std::f64::EPSILON))
|
||||
}
|
||||
};
|
||||
}
|
||||
let low = LiteralPosition(low);
|
||||
|
||||
// Smallest decrement possible
|
||||
let mut high: Vec<LiteralNumber> = vec![];
|
||||
let LiteralPosition(coordinates) = &bounding_box[1];
|
||||
for coordinate in coordinates {
|
||||
match coordinate {
|
||||
LiteralNumber::Int(x) => high.push(LiteralNumber::Int(x - 1)),
|
||||
LiteralNumber::Float(x) => {
|
||||
high.push(LiteralNumber::Float(x - std::f64::EPSILON))
|
||||
}
|
||||
};
|
||||
}
|
||||
let high = LiteralPosition(high);
|
||||
|
||||
match database::get_by_bounding_box(space_id, &vec![low, high]) {
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => outside_set(space_id, inside),
|
||||
}
|
||||
}
|
||||
Shape::HyperSphere(space_id, position, radius) => {
|
||||
let length = match radius {
|
||||
LiteralNumber::Int(x) => *x as f64,
|
||||
LiteralNumber::Float(x) => *x,
|
||||
};
|
||||
|
||||
match get_bounding_box(position, radius) {
|
||||
Err(e) => Err(e),
|
||||
Ok(inside) => match database::get_by_bounding_box(space_id, &inside) {
|
||||
Err(e) => Err(e),
|
||||
Ok(source) => {
|
||||
let mut filtered = vec![];
|
||||
|
||||
for point in source {
|
||||
// Exclude the surface of the sphere, so
|
||||
// that it is included in the
|
||||
// complement.
|
||||
if point.length() < length {
|
||||
filtered.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
outside_set(space_id, filtered)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Shape::Nifti(_space_id) => Err("not yet implemented".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate {
|
||||
pub fn eval(&self, point: &database::Object) -> bool {
|
||||
match self {
|
||||
Predicate::Not(predicate) => !predicate.eval(point),
|
||||
Predicate::And(lh, rh) => lh.eval(point) && rh.eval(point),
|
||||
Predicate::Or(lh, rh) => lh.eval(point) || rh.eval(point),
|
||||
// I don't know how to evaluate these at this point, so let the DB object take care of that.
|
||||
// Predicate::Less(selector, literal) => &selector.eval(point) < literal,
|
||||
// Predicate::Greater(selector, literal) => &selector.eval(point) > literal,
|
||||
// Predicate::Equal(selector, literal) => &selector.eval(point) == literal,
|
||||
// Redirect to the DB Objet the evaluation of the remaining predicate operators
|
||||
predicate => point.eval(predicate),
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/expression.rs
Normal file
15
src/expression.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
pub trait Validator {
|
||||
type ValidationResult;
|
||||
|
||||
fn validate(&self) -> Self::ValidationResult;
|
||||
}
|
||||
|
||||
pub trait Predictor {
|
||||
fn predict(&self) -> f64;
|
||||
}
|
||||
|
||||
pub trait Executor {
|
||||
type ResultSet;
|
||||
|
||||
fn execute(&self) -> Self::ResultSet;
|
||||
}
|
||||
19
src/lib.rs
19
src/lib.rs
@@ -1,10 +1,25 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lalrpop_util;
|
||||
|
||||
lalrpop_mod!(pub queries); // synthesized by LALRPOP
|
||||
|
||||
pub mod ast;
|
||||
pub use ast::*;
|
||||
mod database;
|
||||
mod executors;
|
||||
mod expression;
|
||||
mod predictors;
|
||||
mod validators;
|
||||
|
||||
mod symbols;
|
||||
mod types;
|
||||
|
||||
pub use expression::Executor;
|
||||
pub use expression::Predictor;
|
||||
pub use expression::Validator;
|
||||
pub use queries::FiltersParser;
|
||||
pub use queries::QueryParser;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
46
src/main.rs
46
src/main.rs
@@ -3,7 +3,8 @@ extern crate measure_time;
|
||||
|
||||
extern crate parser;
|
||||
|
||||
use parser::queries;
|
||||
use parser::QueryParser;
|
||||
use parser::{Executor, Predictor, Validator};
|
||||
|
||||
use std::io;
|
||||
|
||||
@@ -15,13 +16,13 @@ fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
||||
//let parser = queries::FiltersParser::new();
|
||||
let parser = queries::QueryParser::new();
|
||||
let parser = QueryParser::new();
|
||||
|
||||
loop {
|
||||
println!();
|
||||
info!("Expression to parse (type `quit` to exit): ");
|
||||
|
||||
let mut input = String::new();
|
||||
|
||||
match io::stdin().read_line(&mut input) {
|
||||
Ok(0) => break, // Catch ^D
|
||||
Ok(1) => continue, // Catch \n
|
||||
@@ -34,11 +35,42 @@ fn main() {
|
||||
}
|
||||
|
||||
let input = input.as_str();
|
||||
let mut out;
|
||||
{
|
||||
debug_time!("Parsing");
|
||||
out = parser.parse(input);
|
||||
debug_time!("Interpretation");
|
||||
let mut parse;
|
||||
{
|
||||
trace_time!("Parsing");
|
||||
parse = parser.parse(input);
|
||||
}
|
||||
trace!("Tree: \n{:?}", parse);
|
||||
|
||||
match parse {
|
||||
Ok(Some(t)) => {
|
||||
let validate;
|
||||
{
|
||||
trace_time!("Type check");
|
||||
validate = t.validate();
|
||||
}
|
||||
info!("Type: \n{:?}", validate);
|
||||
|
||||
if let Ok(_) = validate {
|
||||
let predict;
|
||||
{
|
||||
trace_time!("Prediction");
|
||||
predict = t.predict();
|
||||
}
|
||||
info!("Predict: \n{:?}", predict);
|
||||
|
||||
let execute;
|
||||
{
|
||||
trace_time!("Exectution");
|
||||
execute = t.execute();
|
||||
}
|
||||
info!("Execution: \n{:?}", execute);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
info!("Tree: \n{:?}", out);
|
||||
}
|
||||
}
|
||||
|
||||
48
src/predictors.rs
Normal file
48
src/predictors.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use super::expression::Predictor;
|
||||
use super::symbols::*;
|
||||
|
||||
use super::database::space;
|
||||
|
||||
impl Predictor for Projection {
|
||||
fn predict(&self) -> f64 {
|
||||
match self {
|
||||
Projection::Nifti(_, _, bag) => bag.predict(),
|
||||
Projection::JSON(_, _, bag) => bag.predict(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predictor for Bag {
|
||||
fn predict(&self) -> f64 {
|
||||
match self {
|
||||
Bag::Distinct(bag) => bag.predict(),
|
||||
Bag::Filter(_, bag) => bag.predict(),
|
||||
Bag::Complement(bag) => space::max_volume(bag.space()) - bag.predict(),
|
||||
Bag::Intersection(lh, rh) => {
|
||||
let l = lh.predict();
|
||||
let r = rh.predict();
|
||||
if l < r {
|
||||
l
|
||||
} else {
|
||||
r
|
||||
}
|
||||
}
|
||||
Bag::Union(lh, rh) => lh.predict() + rh.predict(),
|
||||
Bag::Bag(bags) => {
|
||||
let mut s = 0.0;
|
||||
for bag in bags {
|
||||
s += bag.predict();
|
||||
}
|
||||
s
|
||||
}
|
||||
Bag::Inside(shape) => shape.predict(),
|
||||
Bag::Outside(shape) => space::max_volume(shape.space()) - shape.predict(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predictor for Shape {
|
||||
fn predict(&self) -> f64 {
|
||||
self.volume()
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::ast;
|
||||
use crate::ast::*;
|
||||
use crate::symbols;
|
||||
use crate::database::space;
|
||||
|
||||
grammar;
|
||||
|
||||
@@ -10,7 +10,7 @@ grammar;
|
||||
//*********************************************************************/
|
||||
pub Query = { Projections? };
|
||||
|
||||
Projections: ast::Projection = {
|
||||
Projections: symbols::Projection = {
|
||||
NiftiOperator,
|
||||
JsonOperator
|
||||
};
|
||||
@@ -19,18 +19,39 @@ Projections: ast::Projection = {
|
||||
// each position where there is a point in bag_expression.
|
||||
//
|
||||
// If it is provided, it MUST resolve to a NUMBER.
|
||||
NiftiOperator: ast::Projection = {
|
||||
"nifti" "(" <s:( Selector "," )?> <b:Bags> ")" =>
|
||||
NiftiOperator: symbols::Projection = {
|
||||
"nifti" "("
|
||||
<s:( Selector "," )?>
|
||||
<b:Bags>
|
||||
<rs:( "," <String> )?>
|
||||
")" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
};
|
||||
|
||||
if let Some((sel, _)) = s {
|
||||
Projection::Nifti(sel, b)
|
||||
symbols::Projection::Nifti(space_id, sel, b)
|
||||
} else {
|
||||
Projection::Nifti(Vec::new(), b)
|
||||
symbols::Projection::Nifti(space_id, symbols::LiteralSelector(Vec::new()), b)
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
JsonOperator: ast::Projection = {
|
||||
"json" "(" <f:JsonValues> "," <b:Bags> ")" =>
|
||||
Projection::JSON(f, b)
|
||||
JsonOperator: symbols::Projection = {
|
||||
"json" "("
|
||||
<f:JsonValues> ","
|
||||
<b:Bags>
|
||||
<rs:( "," <String> )?>
|
||||
")" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
};
|
||||
|
||||
symbols::Projection::JSON(space_id, f, b)
|
||||
}
|
||||
};
|
||||
|
||||
//*********************************************************************/
|
||||
@@ -41,73 +62,73 @@ JsonOperator: ast::Projection = {
|
||||
// https://github.com/antlr/grammars-v4/blob/master/json/JSON.g4
|
||||
//
|
||||
// Some of the parser / lexer rules are in the imported grammar as well.
|
||||
JsonValues: ast::JsonValue = {
|
||||
String => JsonValue::String(<>),
|
||||
JsonValues: symbols::JsonValue = {
|
||||
String => symbols::JsonValue::String(<>),
|
||||
JsonNumber => <>,
|
||||
JsonObj => <>,
|
||||
JsonArray => <>,
|
||||
"true" => JsonValue::Bool(true),
|
||||
"false" => JsonValue::Bool(false),
|
||||
"null" => JsonValue::Null,
|
||||
"true" => symbols::JsonValue::Bool(true),
|
||||
"false" => symbols::JsonValue::Bool(false),
|
||||
"null" => symbols::JsonValue::Null,
|
||||
// Support reference to values from the selected bag.
|
||||
Selector => JsonValue::Selector(<>),
|
||||
Aggregations => JsonValue::Aggregation(<>)
|
||||
Selector => symbols::JsonValue::Selector(<>),
|
||||
Aggregations => symbols::JsonValue::Aggregation(<>)
|
||||
};
|
||||
|
||||
JsonObj: ast::JsonValue = {
|
||||
JsonObj: symbols::JsonValue = {
|
||||
"{" <exp:( JsonPair ( "," JsonPair )* )?> "}" => {
|
||||
if let Some((elem, list)) = exp {
|
||||
let mut values: Vec<(String, JsonValue)> = vec![elem];
|
||||
let mut values = vec![elem];
|
||||
|
||||
for v in list {
|
||||
let (_, pair) = v;
|
||||
values.push(pair.clone());
|
||||
}
|
||||
|
||||
JsonValue::Object(values)
|
||||
symbols::JsonValue::Object(values)
|
||||
} else {
|
||||
JsonValue::Object(Vec::new())
|
||||
symbols::JsonValue::Object(Vec::new())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JsonPair: (String, ast::JsonValue) = {
|
||||
JsonPair: (String, symbols::JsonValue) = {
|
||||
<s:String> ":" <v:JsonValues> => (s, v)
|
||||
};
|
||||
|
||||
JsonArray: ast::JsonValue = {
|
||||
JsonArray: symbols::JsonValue = {
|
||||
"[" <exp:( JsonValues ( "," JsonValues )* )?> "]" => {
|
||||
if let Some((elem, list)) = exp {
|
||||
let mut values: Vec<JsonValue> = vec![elem];
|
||||
let mut values = vec![elem];
|
||||
|
||||
for v in list.iter() {
|
||||
let (_, val) = v;
|
||||
values.push(val.clone());
|
||||
}
|
||||
|
||||
JsonValue::Array(values)
|
||||
symbols::JsonValue::Array(values)
|
||||
} else {
|
||||
JsonValue::Array(Vec::new())
|
||||
symbols::JsonValue::Array(Vec::new())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The bag expression is implicit here, as this is te
|
||||
// second argument to the json operator
|
||||
Aggregations: ast::Aggregation = {
|
||||
Aggregations: symbols::Aggregation = {
|
||||
"count" "(" <d:"distinct"?> <s:Selector> ")" => {
|
||||
if let Some(_) = d {
|
||||
Aggregation::Count(true, s)
|
||||
symbols::Aggregation::Count(true, s)
|
||||
} else {
|
||||
Aggregation::Count(false, s)
|
||||
symbols::Aggregation::Count(false, s)
|
||||
}
|
||||
},
|
||||
"sum" "(" <Selector> ")" =>
|
||||
Aggregation::Sum(<>),
|
||||
symbols::Aggregation::Sum(<>),
|
||||
"min" "(" <Selector> ")" =>
|
||||
Aggregation::Min(<>),
|
||||
symbols::Aggregation::Min(<>),
|
||||
"max" "(" <Selector> ")" =>
|
||||
Aggregation::Max(<>),
|
||||
symbols::Aggregation::Max(<>),
|
||||
};
|
||||
|
||||
//*********************************************************************/
|
||||
@@ -116,7 +137,7 @@ Aggregations: ast::Aggregation = {
|
||||
pub Filters = { Bags };
|
||||
|
||||
// All these expressions generate bags.
|
||||
Bags: ast::Bag = {
|
||||
Bags: symbols::Bag = {
|
||||
// Bag Operators
|
||||
Distinct,
|
||||
Filter,
|
||||
@@ -129,49 +150,65 @@ Bags: ast::Bag = {
|
||||
Outside,
|
||||
// When used directly here, the inside() operation on the shape is
|
||||
// implied.
|
||||
Shapes => Bag::Inside(<>)
|
||||
Shapes => symbols::Bag::Inside(<>)
|
||||
};
|
||||
|
||||
//*********************************************************************/
|
||||
// BAG OPERATORS */
|
||||
//*********************************************************************/
|
||||
Distinct: ast::Bag = {
|
||||
Distinct: symbols::Bag = {
|
||||
"distinct" "(" <Bags> ")" =>
|
||||
Bag::Distinct(Box::new(<>))
|
||||
symbols::Bag::Distinct(Box::new(<>))
|
||||
};
|
||||
|
||||
// Returns all the points which are NOT part of the bag.
|
||||
Complement: ast::Bag = {
|
||||
Complement: symbols::Bag = {
|
||||
"complement" "(" <Bags> ")" =>
|
||||
Bag::Complement(Box::new(<>))
|
||||
symbols::Bag::Complement(Box::new(<>))
|
||||
};
|
||||
|
||||
// Returns points which are part of both left and right sets.
|
||||
Intersection: ast::Bag = {
|
||||
Intersection: symbols::Bag = {
|
||||
"intersection" "(" <lh:Bags> "," <rh:Bags> ")" =>
|
||||
Bag::Intersection(Box::new(lh), Box::new(rh))
|
||||
symbols::Bag::Intersection(Box::new(lh), Box::new(rh))
|
||||
};
|
||||
|
||||
// Returns points which are either part of left or right sets
|
||||
// (or both).
|
||||
Union: ast::Bag = {
|
||||
Union: symbols::Bag = {
|
||||
"union" "(" <lh:Bags> "," <rh:Bags> ")" =>
|
||||
Bag::Union(Box::new(lh), Box::new(rh))
|
||||
symbols::Bag::Union(Box::new(lh), Box::new(rh))
|
||||
};
|
||||
|
||||
// Filters point so that points part of the resulting bag respect
|
||||
// the predicate.
|
||||
Filter: ast::Bag = {
|
||||
Filter: symbols::Bag = {
|
||||
// "filter" "(" <p:Predicates> "," <b:Bags> ")" =>
|
||||
"filter" "(" <b:Bags> ")" =>
|
||||
Bag::Filter(None, Some(Box::new(b))),
|
||||
symbols::Bag::Filter(None, Box::new(b)),
|
||||
"filter" "(" <p:Predicates> <b:("," <Bags> )?> ")" => match b {
|
||||
Some(b) => Bag::Filter(Some(p), Some(Box::new(b))),
|
||||
None => Bag::Filter(Some(p), None),
|
||||
Some(b) => symbols::Bag::Filter(Some(p), Box::new(b)),
|
||||
None => {
|
||||
let v = space::bounding_box(space::name());
|
||||
let mut positions = Vec::new();
|
||||
for point in v {
|
||||
let mut position = Vec::new();
|
||||
for c in point {
|
||||
position.push(symbols::LiteralNumber::Int(*c))
|
||||
}
|
||||
positions.push(symbols::LiteralPosition(position));
|
||||
}
|
||||
let bb = symbols::Shape::HyperRectangle(
|
||||
space::name().clone(),
|
||||
positions
|
||||
);
|
||||
|
||||
symbols::Bag::Filter(Some(p), Box::new(symbols::Bag::Inside(bb)))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Predicates: ast::Predicate = {
|
||||
Predicates: symbols::Predicate = {
|
||||
Less,
|
||||
Greater,
|
||||
Equal,
|
||||
@@ -180,41 +217,41 @@ Predicates: ast::Predicate = {
|
||||
Or
|
||||
};
|
||||
|
||||
Less: ast::Predicate = {
|
||||
Less: symbols::Predicate = {
|
||||
"<" "(" <v:Positions> "," <literal:Position> ")" => {
|
||||
Predicate::Less(v, literal)
|
||||
symbols::Predicate::Less(v, literal)
|
||||
}
|
||||
};
|
||||
|
||||
Greater: ast::Predicate = {
|
||||
Greater: symbols::Predicate = {
|
||||
">" "(" <v:Positions> "," <literal:Position> ")" => {
|
||||
Predicate::Greater(v, literal)
|
||||
symbols::Predicate::Greater(v, literal)
|
||||
}
|
||||
};
|
||||
|
||||
Equal: ast::Predicate = {
|
||||
Equal: symbols::Predicate = {
|
||||
"=" "(" <v:Positions> "," <literal:Position> ")" => {
|
||||
Predicate::Equal(v, literal)
|
||||
symbols::Predicate::Equal(v, literal)
|
||||
}
|
||||
};
|
||||
|
||||
Not: ast::Predicate = {
|
||||
Not: symbols::Predicate = {
|
||||
"!" "(" <p:Predicates> ")" =>
|
||||
Predicate::Not(Box::new(p))
|
||||
symbols::Predicate::Not(Box::new(p))
|
||||
};
|
||||
|
||||
And: ast::Predicate = {
|
||||
And: symbols::Predicate = {
|
||||
"&" "(" <lh:Predicates> "," <rh:Predicates> ")" =>
|
||||
Predicate::And(Box::new(lh), Box::new(rh))
|
||||
symbols::Predicate::And(Box::new(lh), Box::new(rh))
|
||||
};
|
||||
|
||||
Or: ast::Predicate = {
|
||||
Or: symbols::Predicate = {
|
||||
"|" "(" <lh:Predicates> "," <rh:Predicates> ")" =>
|
||||
Predicate::Or(Box::new(lh), Box::new(rh))
|
||||
symbols::Predicate::Or(Box::new(lh), Box::new(rh))
|
||||
};
|
||||
|
||||
// Arbitrary bag of positions.
|
||||
Bag: ast::Bag = {
|
||||
Bag: symbols::Bag = {
|
||||
"bag" "{" <elem:Bags> <list:("," Bags )*> "}" => {
|
||||
let mut bags = vec![elem];
|
||||
|
||||
@@ -222,7 +259,7 @@ Bag: ast::Bag = {
|
||||
bags.push(b);
|
||||
}
|
||||
|
||||
Bag::Bag(bags)
|
||||
symbols::Bag::Bag(bags)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -240,15 +277,15 @@ Bag: ast::Bag = {
|
||||
// of the cube [0,0], [1,1].
|
||||
|
||||
// Returns the set of points outside the shape, (face included)
|
||||
Outside: ast::Bag = {
|
||||
Outside: symbols::Bag = {
|
||||
"outside" "(" <Shapes> ")" =>
|
||||
Bag::Outside(<>)
|
||||
symbols::Bag::Outside(<>)
|
||||
};
|
||||
|
||||
// Returns the set of points inside the shape, (face included)
|
||||
Inside: ast::Bag = {
|
||||
Inside: symbols::Bag = {
|
||||
"inside" "(" <Shapes> ")" =>
|
||||
Bag::Inside(<>)
|
||||
symbols::Bag::Inside(<>)
|
||||
};
|
||||
|
||||
//*********************************************************************/
|
||||
@@ -257,7 +294,7 @@ Inside: ast::Bag = {
|
||||
|
||||
// Shapes are defined in terms of POSITION, a.k.a a LiteralPosition
|
||||
// value, which is not a POSITIONS, which might be a filter for example.
|
||||
Shapes: ast::Shape = {
|
||||
Shapes: symbols::Shape = {
|
||||
Point,
|
||||
HyperRectangle,
|
||||
HyperSphere,
|
||||
@@ -266,30 +303,50 @@ Shapes: ast::Shape = {
|
||||
|
||||
// If the hyperrectangle is aligned with the axes, then two points are
|
||||
// enough, if not we need all the points to be specified.
|
||||
HyperRectangle: ast::Shape = {
|
||||
HyperRectangle: symbols::Shape = {
|
||||
"hyperrectangle" "{"
|
||||
<l:Position> "," <h:Position>
|
||||
<list:( "," Position "," Position )*>
|
||||
<rs:( "," <String> )?>
|
||||
"}" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
};
|
||||
let mut pos = vec![l, h];
|
||||
for (_, lh, _, rh) in list.iter() {
|
||||
pos.push(lh.clone());
|
||||
pos.push(rh.clone());
|
||||
}
|
||||
Shape::HyperRectangle(pos)
|
||||
symbols::Shape::HyperRectangle(space_id, pos)
|
||||
}
|
||||
};
|
||||
|
||||
// A hypersphere is defined by its center and a radius, independantly
|
||||
// of the number of dimensions of the space.
|
||||
HyperSphere: ast::Shape = {
|
||||
"hypersphere" "{" <c:Position> "," <r:PositiveNumber> "}" =>
|
||||
Shape::HyperSphere(c, r)
|
||||
HyperSphere: symbols::Shape = {
|
||||
"hypersphere" "{"
|
||||
<c:Position> "," <r:PositiveNumber>
|
||||
<rs:( "," <String> )?>
|
||||
"}" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
};
|
||||
|
||||
symbols::Shape::HyperSphere(space_id, c, r)
|
||||
}
|
||||
};
|
||||
|
||||
Point: ast::Shape = {
|
||||
"point" "{" <Position> "}" =>
|
||||
Shape::Point(<>)
|
||||
Point: symbols::Shape = {
|
||||
"point" "{" <pos:Position> <rs:( "," <String> )?> "}" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
};
|
||||
|
||||
symbols::Shape::Point(space_id, pos)
|
||||
}
|
||||
};
|
||||
|
||||
// Define a shape as the non-zero values in a NIfTI object, defined by
|
||||
@@ -299,14 +356,18 @@ Point: ast::Shape = {
|
||||
// rotation: [ position+ ], // Optional, no rotation by default
|
||||
// bytes: uri(STRING) // uri to the NIfTI object
|
||||
// }
|
||||
Nifti: ast::Shape = {
|
||||
Nifti: symbols::Shape = {
|
||||
"nifti" "{"
|
||||
String ","
|
||||
( Position "," )?
|
||||
( "[" Position ( "," Position)* "]" "," )?
|
||||
ByteProvider
|
||||
<o:( Position "," )?>
|
||||
<rotation:( "[" Position ( "," Position)* "]" "," )?>
|
||||
<data:ByteProvider>
|
||||
<rs:( "," <String> )?>
|
||||
"}" => {
|
||||
Shape::Nifti()
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
};
|
||||
symbols::Shape::Nifti(space_id)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -321,44 +382,44 @@ ByteProvider = { "uri" "(" String ")" };
|
||||
|
||||
// Always returns a vector of numbers, a.k.a a position (a scalar will
|
||||
// be represented as a vector of one element)
|
||||
Positions: ast::Position = {
|
||||
Positions: symbols::Position = {
|
||||
StrCmpICase,
|
||||
StrCmp,
|
||||
Selector => Position::Selector(<>),
|
||||
Position => Position::LiteralPosition(<>)
|
||||
Selector => symbols::Position::Selector(<>),
|
||||
Position => symbols::Position::Literal(<>)
|
||||
};
|
||||
|
||||
// Compare lexicographically two strings, and returns a `position`:
|
||||
// [-1] : String is lexicographically before,
|
||||
// [ 0] : is equal,
|
||||
// [ 1] : is after.
|
||||
StrCmp: ast::Position = {
|
||||
StrCmp: symbols::Position = {
|
||||
"str_cmp" "(" <s:Selector> "," <v:String> ")" => {
|
||||
Position::StrCmp(s, v)
|
||||
symbols::Position::StrCmp(s, v)
|
||||
}
|
||||
};
|
||||
|
||||
// Same, but case insensitive.
|
||||
StrCmpICase: ast::Position = {
|
||||
StrCmpICase: symbols::Position = {
|
||||
"str_cmp_ignore_case" "(" <s:Selector> "," <v:String> ")" => {
|
||||
Position::StrCmpICase(s, v)
|
||||
symbols::Position::StrCmpICase(s, v)
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: FIELDS are expected to be exisiting in the data model. Root Object is assumed to be the type of the ressource on which the POST call was done.
|
||||
Selector = {
|
||||
( <Field> )+
|
||||
Selector: symbols::LiteralSelector = {
|
||||
( <Field> )+ => symbols::LiteralSelector(<>)
|
||||
};
|
||||
|
||||
Position: ast::LiteralPosition = {
|
||||
Position: symbols::LiteralPosition = {
|
||||
"[" <element:Number> <list:( "," <Number>)*> "]" => {
|
||||
let mut pos: LiteralPosition = vec![element];
|
||||
let mut pos = vec![element];
|
||||
|
||||
for e in list.iter() {
|
||||
pos.push(e.clone());
|
||||
}
|
||||
|
||||
pos
|
||||
symbols::LiteralPosition(pos)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -370,72 +431,75 @@ Position: ast::LiteralPosition = {
|
||||
// 1. start with a dot ('.')
|
||||
// 2. optionnally followed by a field name consisting of a letter or
|
||||
// underscore, followed by letters, numbers or underscore,
|
||||
// 3. optionnally followed by brakets enclosing an natural number
|
||||
// 3. optionnally followed by brakets enclosing a natural number
|
||||
// denoting an offset in a list or array.
|
||||
Field: Field = {
|
||||
Field: symbols::Field = {
|
||||
<n:r"[.]([a-zA-Z_][[a-zA-Z_0-9]]*)?([\[](0|[1-9][0-9]*)[\]])?"> => {
|
||||
if let Some(pos) = n.rfind('[') {
|
||||
let name = &n[1..pos];
|
||||
let index = &n[(pos+1)..(n.len()-1)];
|
||||
let index = usize::from_str(index).unwrap();
|
||||
Field(String::from(name), Some(index))
|
||||
symbols::Field(String::from(name), Some(index))
|
||||
} else {
|
||||
let name = &n[1..];
|
||||
Field(String::from(name), None)
|
||||
symbols::Field(String::from(name), None)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
String: String = {
|
||||
r#"["]([\\](["\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\\u0000-\u001F])*["]"# =>
|
||||
String::from(<>)
|
||||
r#"["]([\\](["\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\\u0000-\u001F])*["]"# => {
|
||||
let s = <>;
|
||||
let l = s.len() - 1;
|
||||
s[1..l].to_string()
|
||||
}
|
||||
};
|
||||
|
||||
//*********************************************************************/
|
||||
// TOKENS - NUMBERS */
|
||||
//*********************************************************************/
|
||||
// We define 3 kinds of number, to avoid ambiguities in the rules.
|
||||
JsonNumber: ast::JsonValue = {
|
||||
JsonNumber: symbols::JsonValue = {
|
||||
<s:"-"?> <v:Num> => match s {
|
||||
None => JsonValue::JsonNumber(v),
|
||||
None => symbols::JsonValue::JsonNumber(v),
|
||||
Some(_) => match v {
|
||||
LiteralNumber::Int(x) => JsonValue::JsonNumber(LiteralNumber::Int(-x)),
|
||||
LiteralNumber::Float(x) => JsonValue::JsonNumber(LiteralNumber::Float(-x))
|
||||
symbols::LiteralNumber::Int(x) => symbols::JsonValue::JsonNumber(symbols::LiteralNumber::Int(-x)),
|
||||
symbols::LiteralNumber::Float(x) => symbols::JsonValue::JsonNumber(symbols::LiteralNumber::Float(-x))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PositiveNumber: ast::LiteralNumber = { "+"? <v:Num> => v };
|
||||
PositiveNumber: symbols::LiteralNumber = { "+"? <v:Num> => v };
|
||||
|
||||
Number: ast::LiteralNumber = {
|
||||
Number: symbols::LiteralNumber = {
|
||||
"+" <v:Num> => v,
|
||||
"-" <v:Num> => match v {
|
||||
LiteralNumber::Int(x) => LiteralNumber::Int(-x),
|
||||
LiteralNumber::Float(x) => LiteralNumber::Float(-x)
|
||||
symbols::LiteralNumber::Int(x) => symbols::LiteralNumber::Int(-x),
|
||||
symbols::LiteralNumber::Float(x) => symbols::LiteralNumber::Float(-x)
|
||||
},
|
||||
<v:Num> => v
|
||||
|
||||
};
|
||||
|
||||
Num: ast::LiteralNumber = {
|
||||
Num: symbols::LiteralNumber = {
|
||||
r"0([.][0-9]+([eE][+\-]?(0|[1-9][0-9]*))?)?"
|
||||
=> {
|
||||
if let Ok(v) = i64::from_str(<>) {
|
||||
LiteralNumber::Int(v)
|
||||
symbols::LiteralNumber::Int(v)
|
||||
} else {
|
||||
// Either parsing as a float succeed or we pass along
|
||||
// the error
|
||||
LiteralNumber::Float(f64::from_str(<>).unwrap())
|
||||
symbols::LiteralNumber::Float(f64::from_str(<>).unwrap())
|
||||
}
|
||||
},
|
||||
r"[1-9][0-9]*([.][0-9]+)?([eE][+\-]?(0|[1-9][0-9]*))?"
|
||||
=> {
|
||||
if let Ok(v) = i64::from_str(<>) {
|
||||
LiteralNumber::Int(v)
|
||||
symbols::LiteralNumber::Int(v)
|
||||
} else {
|
||||
// Either parsing as a float succeed or we pass along
|
||||
// the error
|
||||
LiteralNumber::Float(f64::from_str(<>).unwrap())
|
||||
symbols::LiteralNumber::Float(f64::from_str(<>).unwrap())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
319
src/symbols.rs
Normal file
319
src/symbols.rs
Normal file
@@ -0,0 +1,319 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use super::database::space;
|
||||
pub use super::types::*;
|
||||
|
||||
/**********************************************************************/
|
||||
/* FORMATTING DATA */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Projection {
|
||||
Nifti(String, LiteralSelector, Bag),
|
||||
JSON(String, JsonValue, Bag),
|
||||
}
|
||||
|
||||
impl Projection {
|
||||
pub fn space(&self) -> &String {
|
||||
match self {
|
||||
Projection::Nifti(space, _, _) => &space,
|
||||
Projection::JSON(space, _, _) => &space,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// JSON FORMAT
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum JsonValue {
|
||||
String(String),
|
||||
JsonNumber(LiteralNumber),
|
||||
Bool(bool),
|
||||
Null,
|
||||
Object(Vec<(String, JsonValue)>),
|
||||
Array(Vec<JsonValue>),
|
||||
Selector(LiteralSelector),
|
||||
Aggregation(Aggregation),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Aggregation {
|
||||
Count(bool, LiteralSelector),
|
||||
Sum(LiteralSelector),
|
||||
Min(LiteralSelector),
|
||||
Max(LiteralSelector),
|
||||
}
|
||||
|
||||
// NIFTI
|
||||
#[derive(Clone, Debug)]
|
||||
struct Transform {
|
||||
reference: String,
|
||||
offset: Vec<LiteralNumber>,
|
||||
rotation: Vec<Vec<LiteralNumber>>,
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* SELECTING / FILTERING DATA */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Bag {
|
||||
Distinct(Box<Bag>),
|
||||
Filter(Option<Predicate>, Box<Bag>),
|
||||
Complement(Box<Bag>),
|
||||
Intersection(Box<Bag>, Box<Bag>),
|
||||
Union(Box<Bag>, Box<Bag>),
|
||||
Bag(Vec<Bag>),
|
||||
Inside(Shape),
|
||||
Outside(Shape),
|
||||
}
|
||||
|
||||
impl Bag {
|
||||
pub fn space(&self) -> &String {
|
||||
match self {
|
||||
Bag::Distinct(bag) => bag.space(),
|
||||
Bag::Filter(_, bag) => bag.space(),
|
||||
Bag::Complement(bag) => bag.space(),
|
||||
Bag::Intersection(lh, _) => {
|
||||
// We are assuming lh and rh are in the same space.
|
||||
// Checked as part of the validation.
|
||||
lh.space()
|
||||
}
|
||||
Bag::Union(lh, _) => {
|
||||
// We are assuming lh and rh are in the same space.
|
||||
// Checked as part of the validation.
|
||||
lh.space()
|
||||
}
|
||||
Bag::Bag(_) => {
|
||||
// Bags can be defined in different spaces, thus the output is
|
||||
// always in the universe space.
|
||||
space::name()
|
||||
}
|
||||
Bag::Inside(shape) => shape.space(),
|
||||
Bag::Outside(shape) => shape.space(),
|
||||
}
|
||||
}
|
||||
}
|
||||
/**********************************************************************/
|
||||
/* BAG OPERATORS */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Predicate {
|
||||
Less(Position, LiteralPosition),
|
||||
Greater(Position, LiteralPosition),
|
||||
Equal(Position, LiteralPosition),
|
||||
Not(Box<Predicate>),
|
||||
And(Box<Predicate>, Box<Predicate>),
|
||||
Or(Box<Predicate>, Box<Predicate>),
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* SPATIAL OPERATORS */
|
||||
/**********************************************************************/
|
||||
|
||||
/**********************************************************************/
|
||||
/* SHAPES */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Shape {
|
||||
Point(String, LiteralPosition),
|
||||
HyperRectangle(String, Vec<LiteralPosition>),
|
||||
HyperSphere(String, LiteralPosition, LiteralNumber),
|
||||
Nifti(String),
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
pub fn space(&self) -> &String {
|
||||
match self {
|
||||
Shape::Point(space, _) => space,
|
||||
Shape::HyperRectangle(space, _) => space,
|
||||
Shape::HyperSphere(space, _, _) => space,
|
||||
Shape::Nifti(space) => space,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn volume(&self) -> f64 {
|
||||
match self {
|
||||
Shape::Point(_, _) => 1.0, // This is the smallest non-zero volume possible //TODO DOUBLE CHECK IT IS TRUE
|
||||
Shape::HyperRectangle(_space, pos) => {
|
||||
//TODO: At this time, only aligned to the axes, defined by two points, hyperrectangles are supported.
|
||||
assert_eq!(pos.len(), 2);
|
||||
|
||||
// We assume the first position is the low point, the second is
|
||||
// the high point, this being true for each dimension. As we add
|
||||
// an even number of points per extra dimension, we assume the
|
||||
// last group is the high end, and the last position is the
|
||||
// highest point.
|
||||
let mut volume = 1.0;
|
||||
let LiteralPosition(low) = &pos[0];
|
||||
let LiteralPosition(high) = &pos[pos.len() - 1];
|
||||
|
||||
// For each dimension, multiply by the length in that dimension
|
||||
for i in 0..low.len() {
|
||||
let l = match low[i] {
|
||||
LiteralNumber::Int(x) => x as f64,
|
||||
LiteralNumber::Float(x) => x,
|
||||
};
|
||||
|
||||
let h = match high[i] {
|
||||
LiteralNumber::Int(x) => x as f64,
|
||||
LiteralNumber::Float(x) => x,
|
||||
};
|
||||
|
||||
let length = if h > l { h - l } else { l - h };
|
||||
volume *= length;
|
||||
}
|
||||
|
||||
volume
|
||||
}
|
||||
Shape::HyperSphere(_space, pos, r) => {
|
||||
// Formula from https://en.wikipedia.org/wiki/N-sphere#/media/File:N_SpheresVolumeAndSurfaceArea.png
|
||||
let LiteralPosition(p) = pos;
|
||||
let k = p.len(); // Number of dimensions.
|
||||
|
||||
let r = match *r {
|
||||
LiteralNumber::Int(x) => x as f64,
|
||||
LiteralNumber::Float(x) => x,
|
||||
};
|
||||
|
||||
let pi = std::f64::consts::PI;
|
||||
let factor = 2.0 * pi;
|
||||
|
||||
// Set starting values for the coefficient
|
||||
let mut a = 2.0;
|
||||
let mut i = 1;
|
||||
|
||||
if (k % 2) == 0 {
|
||||
a = pi;
|
||||
i = 2;
|
||||
}
|
||||
|
||||
while i < k {
|
||||
i += 2;
|
||||
a *= factor;
|
||||
a /= i as f64;
|
||||
}
|
||||
|
||||
a * r.powi(i as i32)
|
||||
}
|
||||
Shape::Nifti(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* POSITIONS */
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Position {
|
||||
StrCmpICase(LiteralSelector, String),
|
||||
StrCmp(LiteralSelector, String),
|
||||
Selector(LiteralSelector),
|
||||
Literal(LiteralPosition),
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* Literals / TOKENS */
|
||||
/**********************************************************************/
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Field(pub String, pub Option<usize>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum LiteralNumber {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
}
|
||||
|
||||
impl PartialEq for LiteralNumber {
|
||||
fn eq(&self, other: &LiteralNumber) -> bool {
|
||||
match self {
|
||||
LiteralNumber::Int(l) => match other {
|
||||
LiteralNumber::Int(r) => l == r,
|
||||
LiteralNumber::Float(_) => false,
|
||||
},
|
||||
LiteralNumber::Float(l) => match other {
|
||||
LiteralNumber::Int(r) => l == &(*r as f64),
|
||||
LiteralNumber::Float(r) => l == r,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LiteralPosition(pub Vec<LiteralNumber>);
|
||||
|
||||
impl LiteralPosition {
|
||||
pub fn get_type(&self) -> LiteralTypes {
|
||||
let Self(v) = self;
|
||||
let mut t = Vec::new();
|
||||
|
||||
for n in v {
|
||||
t.push(match n {
|
||||
LiteralNumber::Int(_) => LiteralTypes::Int,
|
||||
LiteralNumber::Float(_) => LiteralTypes::Float,
|
||||
});
|
||||
}
|
||||
|
||||
LiteralTypes::Vector(t)
|
||||
}
|
||||
|
||||
pub fn length(&self) -> f64 {
|
||||
let LiteralPosition(v) = self;
|
||||
let mut a = 0.0;
|
||||
|
||||
for x in v {
|
||||
let x = match x {
|
||||
LiteralNumber::Int(x) => (*x) as f64,
|
||||
LiteralNumber::Float(x) => *x,
|
||||
};
|
||||
|
||||
a += x * x;
|
||||
}
|
||||
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for LiteralPosition {
|
||||
fn partial_cmp(&self, other: &LiteralPosition) -> Option<Ordering> {
|
||||
let LiteralPosition(lh) = self;
|
||||
let LiteralPosition(rh) = other;
|
||||
|
||||
if lh.len() != rh.len() {
|
||||
None
|
||||
} else {
|
||||
// Order is defined by the geometric length of the vector between the Origin and the point.
|
||||
let l = self.length();
|
||||
let r = other.length();
|
||||
|
||||
l.partial_cmp(&r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for LiteralPosition {
|
||||
fn eq(&self, other: &LiteralPosition) -> bool {
|
||||
let LiteralPosition(lh) = self;
|
||||
let LiteralPosition(rh) = other;
|
||||
|
||||
if lh.len() == rh.len() {
|
||||
for i in 0..lh.len() {
|
||||
if lh[i] != rh[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LiteralSelector(pub Vec<Field>);
|
||||
|
||||
impl LiteralSelector {
|
||||
pub fn get_type(&self) -> LiteralTypes {
|
||||
// FIXME: Pretend for now that everything is a number, needs to be actually looked up in data model.
|
||||
LiteralTypes::Int
|
||||
}
|
||||
}
|
||||
1835
src/tests.rs
1835
src/tests.rs
File diff suppressed because it is too large
Load Diff
59
src/types.rs
Normal file
59
src/types.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum LiteralTypes {
|
||||
String,
|
||||
Int,
|
||||
Float,
|
||||
Bag(Vec<LiteralTypes>), // List of types (heterogeneous)
|
||||
Vector(Vec<LiteralTypes>), // List of coordinates types (heterogeneous)
|
||||
Array(usize, Box<LiteralTypes>), // Length, homogeneous type
|
||||
}
|
||||
|
||||
impl PartialEq for LiteralTypes {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match self {
|
||||
LiteralTypes::String => match other {
|
||||
LiteralTypes::String => true,
|
||||
_ => false,
|
||||
},
|
||||
LiteralTypes::Int => match other {
|
||||
LiteralTypes::Int => true,
|
||||
_ => false,
|
||||
},
|
||||
LiteralTypes::Float => match other {
|
||||
LiteralTypes::Float => true,
|
||||
LiteralTypes::Int => true,
|
||||
_ => false,
|
||||
},
|
||||
LiteralTypes::Bag(_) => match other {
|
||||
LiteralTypes::Bag(_) => true,
|
||||
_ => false,
|
||||
},
|
||||
LiteralTypes::Vector(v) => match other {
|
||||
LiteralTypes::Vector(ov) => {
|
||||
let n = v.len();
|
||||
if n != ov.len() {
|
||||
false
|
||||
} else {
|
||||
for i in 0..n {
|
||||
if v[i] != ov[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
LiteralTypes::Array(n, t) => match other {
|
||||
LiteralTypes::Array(on, ot) => {
|
||||
if on == n {
|
||||
t == ot
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
171
src/validators.rs
Normal file
171
src/validators.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use super::database::space;
|
||||
use super::expression::Validator;
|
||||
use super::symbols::*;
|
||||
|
||||
pub type ValidationResult = Result<LiteralTypes, String>;
|
||||
|
||||
impl Validator for Projection {
|
||||
type ValidationResult = self::ValidationResult;
|
||||
|
||||
fn validate(&self) -> ValidationResult {
|
||||
match self {
|
||||
Projection::Nifti(_, _, _) => Err("not yet implemented".to_string()),
|
||||
Projection::JSON(_, _format, bag) => bag.validate(),
|
||||
//FIXME: Add support for projections
|
||||
/* match format.validate() {
|
||||
Ok(_) => bag.validate(),
|
||||
Err(_) => Err(()),
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for Bag {
|
||||
type ValidationResult = self::ValidationResult;
|
||||
|
||||
fn validate(&self) -> ValidationResult {
|
||||
fn compare_bag_types(lh: &Box<Bag>, rh: &Box<Bag>) -> ValidationResult {
|
||||
if lh.space().cmp(rh.space()) != std::cmp::Ordering::Equal {
|
||||
return Err(format!(
|
||||
"left and right sets are defined in different reference spaces: '{}' vs '{}'.",
|
||||
lh.space(),
|
||||
rh.space()
|
||||
));
|
||||
}
|
||||
|
||||
let l = lh.validate();
|
||||
let r = rh.validate();
|
||||
|
||||
match &l {
|
||||
Err(_) => l,
|
||||
Ok(tl) => match r {
|
||||
e @ Err(_) => e,
|
||||
Ok(tr) => {
|
||||
if tl != &tr {
|
||||
Err(format!(
|
||||
"Incoherent types between left and right sets: '{:?}' vs '{:?}'",
|
||||
tl, &tr
|
||||
))
|
||||
} else {
|
||||
l
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
match self {
|
||||
Bag::Distinct(bag) => bag.validate(),
|
||||
Bag::Filter(_, bag) => bag.validate(),
|
||||
Bag::Complement(bag) => bag.validate(),
|
||||
Bag::Intersection(lh, rh) => compare_bag_types(lh, rh),
|
||||
Bag::Union(lh, rh) => compare_bag_types(lh, rh),
|
||||
Bag::Bag(bags) => {
|
||||
for b in bags {
|
||||
let t = b.validate();
|
||||
if t.is_err() {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(space::get_type(space::name()).clone())
|
||||
}
|
||||
Bag::Inside(shape) => shape.validate(),
|
||||
Bag::Outside(shape) => shape.validate(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for Shape {
|
||||
type ValidationResult = self::ValidationResult;
|
||||
|
||||
fn validate(&self) -> ValidationResult {
|
||||
match self {
|
||||
Shape::Point(_, v) => v.validate(),
|
||||
Shape::HyperRectangle(_space, pos) => {
|
||||
let first = pos[0].get_type();
|
||||
match pos.len() {
|
||||
2 => {
|
||||
if first != pos[1].get_type() {
|
||||
Err(format!(
|
||||
"HyperRectangle: Incompatible types in points definitions: '{:?}' vs '{:?}'",
|
||||
first,
|
||||
pos[1].get_type()
|
||||
))
|
||||
} else {
|
||||
Ok(first)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
//FIXME: Implement arbitrary hypercube definition support. For now reject.
|
||||
Err("not yet implemented".to_string())
|
||||
/*
|
||||
fn check_orthogonal(pos: &Vec<LiteralPosition>) -> bool {
|
||||
let k = pos.len();
|
||||
let mut raw_pos = vec![];
|
||||
|
||||
for s in pos {
|
||||
match s {
|
||||
LiteralTypes::Vector(n) => {
|
||||
let mut v = vec![];
|
||||
for c in n {
|
||||
v.push(match c {
|
||||
LiteralNumber::Int(x) => x as f64,
|
||||
LiteralNumber::Float(x) => x,
|
||||
});
|
||||
}
|
||||
raw_pos.push(v)
|
||||
}
|
||||
_ => pass,
|
||||
}
|
||||
}
|
||||
|
||||
for p in raw_pos {}
|
||||
true
|
||||
}
|
||||
match &first {
|
||||
LiteralTypes::Vector(n) => {
|
||||
if 2usize.pow(n.len() as u32) == pos.len() {
|
||||
// Check we have coherent types for the coordinates.
|
||||
for point in pos {
|
||||
if first != point.get_type() {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
// We need to check the points define a shape with orthogonal faces.
|
||||
if check_orthogonal(pos) {
|
||||
Ok(first)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
_ => Err(()),
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
Shape::HyperSphere(_, pos, _) => pos.validate(),
|
||||
Shape::Nifti(_) => Err("not yet implemented".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for LiteralPosition {
|
||||
type ValidationResult = self::ValidationResult;
|
||||
|
||||
fn validate(&self) -> ValidationResult {
|
||||
Ok(self.get_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for LiteralSelector {
|
||||
type ValidationResult = self::ValidationResult;
|
||||
|
||||
fn validate(&self) -> ValidationResult {
|
||||
Ok(self.get_type())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user