Compare commits

...

16 Commits

Author SHA1 Message Date
40786112d0 Updated compiler and dependencies
* Upgraded to rustc 1.80
 * Updated dependencies
 * Fixed most linter warnings
2024-08-09 19:15:37 +02:00
f68ec7af14 Fix compilation with rustc 1.4.2
More adaptation are required to be able to use the most recent version
of Rust, therefore fix the dependencies and the compiler version.
2024-08-08 12:14:21 +02:00
e4cbdf836f Rename library to mercator_parser 2020-04-01 18:14:45 +02:00
242de73053 Adding documentation 2020-04-01 17:06:52 +02:00
98b37e63b4 Silence warning in generated code 2020-03-17 17:18:50 +01:00
e2ea5c9ba4 Introduce Label for search within id
This allows to define a volume using an indexed object.
2020-03-16 13:48:01 +01:00
0dc31c65c6 Remove some .unwrap() calls.
The parser still contains three calls to `unwrap()`, but a this point
I have not yet figured out how to remove them.
2020-01-20 14:53:09 +01:00
e8d931b551 Prevent unsafe blocks for now 2020-01-20 14:53:09 +01:00
b8baee8019 Reduce dependencies. 2020-01-14 18:10:08 +01:00
f95aaa389b Updating test index to new format 2019-11-14 15:30:57 +01:00
a31ce4387f Prevent some allocations 2019-11-13 11:04:29 +01:00
926b879426 Fixes 2019-10-30 17:44:52 +01:00
e1547206e2 Updating to new Index<> API 2019-10-29 15:13:19 +01:00
8dd9250dbb Maybe do not wrap at the top-level 2019-10-18 12:27:30 +02:00
661abbc9f4 Add a new symbol, ViewPort
This is used to wrap the filters within an implicit bounding box,
which corresponds to the view port optionally provided by the users.

The intersection is automatically computed between the query results
and the ViewPort, whenever it is specified.
2019-10-18 12:27:30 +02:00
927feb0297 Actually use the smallest value possible 2019-10-18 12:27:30 +02:00
22 changed files with 395 additions and 184 deletions

BIN
10k.index

Binary file not shown.

View File

@@ -21,26 +21,31 @@ include = ["Cargo.toml", "README.md", "LICENSE", "ACKNOWLEDGEMENTS", "src/**/*.r
build = "build.rs" # LALRPOP preprocessing
[lib]
name = "parser"
name = "mercator_parser"
path = "src/lib.rs"
[[bin]]
name = "parser-driver"
path = "src/main.rs"
required-features = ["bin"]
[features]
bin = ["measure_time", "pretty_env_logger"]
[dependencies]
mercator_db = "^0.1"
mercator_db = "0.1"
lalrpop-util = "^0.17"
regex = "^1.2"
measure_time = "^0.6" # To mesure parsing time, only required by binary
lalrpop-util = "0.20"
regex = "1.10"
# Logging macros API
#log = { version = "^0.4", features = ["max_level_trace", "release_max_level_info"] }
log = { version = "^0.4", features = ["max_level_trace", "release_max_level_trace"] }
pretty_env_logger = "^0.3" # Logger implementation
#log = { version = "0.4", features = ["max_level_trace", "release_max_level_info"] }
log = { version = "0.4", features = ["max_level_trace", "release_max_level_trace"] }
# Used for main.rs
pretty_env_logger = { version = "0.5", optional = true } # Logger implementation
measure_time = { version = "0.8", optional = true } # To mesure parsing time, only required by binary
[build-dependencies]
lalrpop = "^0.17.1"
lalrpop = "0.20"

View File

@@ -19,6 +19,7 @@ bag_expression
// Spatial Operators
| inside
| outside
//| shape
;
/**********************************************************************/
@@ -113,6 +114,11 @@ inside
: 'inside' '(' shapes ')'
;
/* Returns the set of positions inside the shape, (face included) */
shape
: 'shape' '(' shapes ')'
;
/**********************************************************************/
/* SHAPES */
/**********************************************************************/

View File

@@ -18,7 +18,7 @@ projection_operators
*
* If it is provided, it MUST resolve to a NUMBER. */
nifti_operator
: 'nifti' '(' ( STRING ',' )? ( selector ',' )? bag_expression ')'
: 'nifti' '(' ( selector ',' )? bag_expression ( ',' STRING )? ')'
;
json_operator

View File

@@ -22,40 +22,6 @@ This enables the index implementations to be agnostic from the underlying data s
* Rust: https://www.rust-lang.org
## Quick start
## Building from sources
To build this project, you will need to run the following:
```sh
cargo build --release
```
### Installation
To install the software on the system you can use:
```sh
cargo install --release
```
### Usage
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin vehicula pretium
quam sit amet facilisis. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos. Curabitur metus sapien, rhoncus vitae
eleifend nec, convallis vel nunc. Nulla metus mauris, porta eu porta eu,
vulputate et est. Suspendisse lacinia leo vel auctor aliquet. Maecenas non arcu
libero. Nulla ut eleifend dui. Cras bibendum pharetra facilisis. Proin mattis
libero non pharetra tristique. Nam massa nulla, ultrices pharetra quam a,
fermentum placerat dolor. Nullam mollis libero et neque lobortis, id dignissim
lectus dignissim. Maecenas ligula enim, congue in ornare vel, volutpat ut ante.
```sh
cargo run --release
```
## Documentation
For more information, please refer to the [documentation](https://epfl-dias.github.io/mercator_parser/).

1
book/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
book

6
book/book.toml Normal file
View File

@@ -0,0 +1,6 @@
[book]
authors = ["Lionel Sambuc"]
language = "en"
multilingual = false
src = "src"
title = "Mercator Parser"

5
book/src/SUMMARY.md Normal file
View File

@@ -0,0 +1,5 @@
# Summary
[Introduction](./introduction.md)
- [Filter Grammar](./filters.md)
- [Query Grammar](./queries.md)

10
book/src/filters.md Normal file
View File

@@ -0,0 +1,10 @@
# Filter Grammar
You will find below the definition of this SDL, for filtering data
from the index.
## filters.g4
```antlr
{{#include ../../Grammars/filters.g4}}
```

7
book/src/introduction.md Normal file
View File

@@ -0,0 +1,7 @@
# Introduction
To support volumetric queries for Mercator, a new domain-specific language (DSL) was created.
ANTLR was used to write and test the SDL, to check it stays simple
to parse and and fast to execute. The actual [parser](https://epfl-dias.github.io/mercator_parser/) and interpreter is
defined in rust, using [LALRPOP](https://docs.rs/lalrpop/0.18.1/lalrpop/).

9
book/src/queries.md Normal file
View File

@@ -0,0 +1,9 @@
# Query Grammar
You will find below the definition of this SDL, for queries. This builds on top of the [filters](filters.html) grammar.
## queries.g4
```antlr
{{#include ../../Grammars/queries.g4}}
```

2
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "1.80.0"

View File

@@ -1,10 +1,13 @@
use mercator_db::SpaceObject;
use mercator_db::space;
use mercator_db::Properties;
use super::expressions::*;
use super::symbols::*;
impl Evaluator for Predicate {
fn eval(&self, object: &SpaceObject) -> bool {
impl<'e> Evaluator<'e> for Predicate {
type Object = (&'e String, &'e space::Position, &'e Properties);
fn eval(&self, object: Self::Object) -> bool {
match self {
Predicate::Not(predicate) => !predicate.eval(object),
Predicate::And(lh, rh) => lh.eval(object) && rh.eval(object),

View File

@@ -1,9 +1,9 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use mercator_db::space;
use mercator_db::Core;
use mercator_db::CoreQueryParameters;
use mercator_db::SpaceObject;
use mercator_db::Properties;
use super::expressions::*;
use super::symbols::*;
@@ -24,58 +24,132 @@ impl From<&LiteralNumber> for space::Coordinate {
}
}
fn complement_helper(
core: &Core,
parameters: &CoreQueryParameters,
fn complement_helper<'c>(
core: &'c Core,
parameters: &CoreQueryParameters<'c>,
space_id: &str,
inside: Vec<SpaceObject>,
) -> mercator_db::ResultSet {
inside: Vec<(&'c String, Vec<(space::Position, &'c Properties)>)>,
) -> mercator_db::ResultSet<'c> {
let (low, high) = parameters.db.space(space_id)?.bounding_box();
match core.get_by_shape(parameters, &space::Shape::BoundingBox(low, high), space_id) {
e @ Err(_) => e,
Ok(points) => Ok(points
.into_iter()
.filter(|o| !inside.contains(&o))
.collect::<Vec<_>>()),
Ok(points) => {
let hashmap = inside.into_iter().collect::<HashMap<_, _>>();
Ok(points
.into_iter()
.filter_map(|(space, v)|
hashmap.get(space).map(|list|
(space, v.into_iter().filter(|t|
!list.contains(t)).collect())))
.collect::<Vec<_>>())
}
}
}
fn distinct(core_id: &str, parameters: &CoreQueryParameters, bag: &Bag) -> mercator_db::ResultSet {
fn view_port<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
bag: &Bag,
) -> mercator_db::ResultSet<'c> {
if let Some((low, high)) = parameters.view_port {
let vp = Bag::Inside(Shape::HyperRectangle(
bag.space().clone(),
vec![low.into(), high.into()],
));
intersection(core_id, parameters, &vp, bag)
} else {
bag.execute(core_id, parameters)
}
}
fn distinct<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
bag: &Bag,
) -> mercator_db::ResultSet<'c> {
match bag.execute(core_id, parameters) {
e @ Err(_) => e,
Ok(mut v) => {
let set: HashSet<_> = v.drain(..).collect(); // dedup
v.extend(set.into_iter());
v.extend(set);
Ok(v)
}
}
}
fn filter(
core_id: &str,
parameters: &CoreQueryParameters,
predicate: &Option<Predicate>,
fn filter_helper<'c>(
predicate: &Predicate,
bag: &Bag,
) -> mercator_db::ResultSet {
core_id: &str,
parameters: &CoreQueryParameters<'c>,
) -> mercator_db::ResultSet<'c> {
match bag.execute(core_id, parameters) {
e @ Err(_) => e,
Ok(results) => Ok(results
.into_iter()
.filter_map(|(space, positions)| {
let filtered = positions
.into_iter()
.filter(|(position, properties)| predicate.eval((space, position, properties)))
.collect::<Vec<_>>();
if filtered.is_empty() {
None
} else {
Some((space, filtered))
}
})
.collect::<Vec<_>>()),
}
}
fn filter<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
predicate: &Option<Predicate>,
bag: &Option<Box<Bag>>,
) -> mercator_db::ResultSet<'c> {
match predicate {
None => bag.execute(core_id, parameters),
Some(predicate) => match bag.execute(core_id, parameters) {
e @ Err(_) => e,
Ok(results) => Ok(results
.into_iter()
.filter(|o| predicate.eval(&o))
.collect::<Vec<_>>()),
None => {
if let Some(bag) = bag {
bag.execute(core_id, parameters)
} else {
Err("Filter without predicate nor data set.".to_string())
}
}
Some(predicate) => match bag {
None => {
let (low, high) = space::Space::universe().bounding_box();
let low: Vec<_> = low.into();
let high: Vec<_> = high.into();
let shape = Shape::HyperRectangle(
space::Space::universe().name().clone(),
vec![
LiteralPosition(
low.into_iter()
.map(LiteralNumber::Float)
.collect::<Vec<_>>(),
),
LiteralPosition(
high.into_iter()
.map(LiteralNumber::Float)
.collect::<Vec<_>>(),
),
],
);
filter_helper(predicate, &Bag::Inside(shape), core_id, parameters)
}
Some(bag) => filter_helper(predicate, bag.as_ref(), core_id, parameters),
},
}
}
fn complement(
fn complement<'c>(
core_id: &str,
parameters: &CoreQueryParameters,
core: &Core,
parameters: &CoreQueryParameters<'c>,
core: &'c Core,
bag: &Bag,
) -> mercator_db::ResultSet {
) -> mercator_db::ResultSet<'c> {
match bag.execute(core_id, parameters) {
// FIXME: The complement of a set is computed within its definition space.
e @ Err(_) => e,
@@ -88,12 +162,12 @@ fn complement(
}
}
fn intersection(
fn intersection<'c>(
core_id: &str,
parameters: &CoreQueryParameters,
parameters: &CoreQueryParameters<'c>,
rh: &Bag,
lh: &Bag,
) -> mercator_db::ResultSet {
) -> mercator_db::ResultSet<'c> {
let l = lh.execute(core_id, parameters);
if let Ok(l) = l {
let r = rh.execute(core_id, parameters);
@@ -122,12 +196,12 @@ fn intersection(
}
}
fn union(
fn union<'c>(
core_id: &str,
parameters: &CoreQueryParameters,
parameters: &CoreQueryParameters<'c>,
rh: &Bag,
lh: &Bag,
) -> mercator_db::ResultSet {
) -> mercator_db::ResultSet<'c> {
let l = lh.execute(core_id, parameters);
if let Ok(mut l) = l {
let r = rh.execute(core_id, parameters);
@@ -147,7 +221,11 @@ fn union(
}
}
fn bag(core_id: &str, parameters: &CoreQueryParameters, bags: &[Bag]) -> mercator_db::ResultSet {
fn bag<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
bags: &[Bag],
) -> mercator_db::ResultSet<'c> {
let mut v = vec![];
for bag in bags {
let b = bag.execute(core_id, parameters);
@@ -164,7 +242,11 @@ fn bag(core_id: &str, parameters: &CoreQueryParameters, bags: &[Bag]) -> mercato
Ok(v)
}
fn inside(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> mercator_db::ResultSet {
fn inside<'c>(
parameters: &CoreQueryParameters<'c>,
core: &'c Core,
shape: &Shape,
) -> mercator_db::ResultSet<'c> {
let db = parameters.db;
let param = match shape {
Shape::Point(space_id, position) => {
@@ -199,6 +281,10 @@ fn inside(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> merca
//FIXME: RADIUS IS A LENGTH, HOW TO ENCODE IT INTO THE SPACE?
Ok((space_id, space::Shape::HyperSphere(position, radius)))
}
Shape::Label(_, id) => {
// Not a real shape, so short circuit and return.
return core.get_by_label(parameters, id);
}
Shape::Nifti(_space_id) => Err("Inside-Nifti: not yet implemented".to_string()),
};
@@ -208,7 +294,11 @@ fn inside(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> merca
}
}
fn outside(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> mercator_db::ResultSet {
fn outside<'c>(
parameters: &CoreQueryParameters<'c>,
core: &'c Core,
shape: &Shape,
) -> mercator_db::ResultSet<'c> {
match shape {
Shape::Point(space_id, position) => {
let position: Vec<f64> = position.into();
@@ -226,7 +316,7 @@ fn outside(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> merc
// Smallest increment possible
let mut increment = Vec::with_capacity(bounding_box[0].dimensions());
for _ in 0..bounding_box[0].dimensions() {
increment.push(std::f64::EPSILON);
increment.push(f64::EPSILON);
}
// Add it to the lower bound
@@ -245,7 +335,7 @@ fn outside(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> merc
Shape::HyperSphere(space_id, center, radius) => {
// Smallest decrement possible, to exclude the surface
let mut radius: f64 = radius.into();
radius -= std::f64::EPSILON;
radius -= f64::EPSILON;
let center: space::Position = center.into();
match core.get_by_shape(
@@ -257,17 +347,22 @@ fn outside(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> merc
Ok(inside) => complement_helper(core, parameters, space_id, inside),
}
}
Shape::Label(_, _) => Err("Label: not yet implemented".to_string()),
Shape::Nifti(_space_id) => Err("Outside-nifti: not yet implemented".to_string()),
}
}
impl Executor for Projection {
type ResultSet = mercator_db::ResultSet;
impl<'e> Executor<'e> for Projection {
type ResultSet = mercator_db::ResultSet<'e>;
fn execute(&self, core_id: &str, parameters: &CoreQueryParameters) -> Self::ResultSet {
fn execute<'f: 'e>(
&self,
core_id: &str,
parameters: &CoreQueryParameters<'f>,
) -> Self::ResultSet {
match self {
Projection::Nifti(_, _, _bag) => Err("Proj-Nifti: not yet implemented".to_string()),
Projection::JSON(_, _format, bag) => {
Projection::Json(_, _format, bag) => {
bag.execute(core_id, parameters)
// FIXME: Add projections here
}
@@ -275,13 +370,18 @@ impl Executor for Projection {
}
}
impl Executor for Bag {
type ResultSet = mercator_db::ResultSet;
impl<'e> Executor<'e> for Bag {
type ResultSet = mercator_db::ResultSet<'e>;
fn execute(&self, core_id: &str, parameters: &CoreQueryParameters) -> Self::ResultSet {
fn execute<'f: 'e>(
&self,
core_id: &str,
parameters: &CoreQueryParameters<'f>,
) -> Self::ResultSet {
let core = parameters.db.core(core_id)?;
match self {
Bag::ViewPort(bag) => view_port(core_id, parameters, bag),
Bag::Distinct(bag) => distinct(core_id, parameters, bag),
Bag::Filter(predicate, bag) => filter(core_id, parameters, predicate, bag),
Bag::Complement(bag) => complement(core_id, parameters, core, bag),

View File

@@ -1,6 +1,5 @@
use mercator_db::CoreQueryParameters;
use mercator_db::DataBase;
use mercator_db::SpaceObject;
pub trait Validator {
type ValidationResult;
@@ -12,12 +11,18 @@ pub trait Predictor {
fn predict(&self, db: &DataBase) -> Result<f64, String>;
}
pub trait Executor {
pub trait Executor<'e> {
type ResultSet;
fn execute(&self, core_id: &str, parameters: &CoreQueryParameters) -> Self::ResultSet;
fn execute<'f: 'e>(
&self,
core_id: &str,
parameters: &CoreQueryParameters<'f>,
) -> Self::ResultSet;
}
pub trait Evaluator {
fn eval(&self, object: &SpaceObject) -> bool;
pub trait Evaluator<'e> {
type Object;
fn eval(&self, object: Self::Object) -> bool;
}

View File

@@ -1,15 +1,57 @@
#![forbid(unsafe_code)]
//! # Mercator Parser
//!
//! Query parser for Mercator.
//!
//! ## Mercator: Spatial Index
//!
//! **Mercator** is a spatial *volumetric* index for the
//! [Human Brain Project]. It is a component of the [Knowledge Graph]
//! service, which provides the spatial anchoring for the metadata
//! registered as well as processes the volumetric queries.
//!
//! It is build on top of the Iron Sea database toolkit.
//!
//! ## Iron Sea: Database Toolkit
//! **Iron Sea** provides a set of database engine bricks, which can be
//! combined and applied on arbitrary data structures.
//!
//! Unlike a traditional database, it does not assume a specific
//! physical structure for the tables nor the records, but relies on the
//! developer to provide a set of extractor functions which are used by
//! the specific indices provided.
//!
//! This enables the index implementations to be agnostic from the
//! underlying data structure, and re-used.
//!
//! [Human Brain Project]: http://www.humanbrainproject.eu
//! [Knowledge Graph]: http://www.humanbrainproject.eu/en/explore-the-brain/search/
#[macro_use]
extern crate lalrpop_util;
lalrpop_mod!(#[allow(clippy::all)] pub queries); // synthesized by LALRPOP
lalrpop_mod!(#[allow(clippy::all,unused_parens)] pub queries); // synthesized by LALRPOP
// Note: We do not enable for the whole library deny(missing_docs), as
// it requires the automatically generated parser to be documented
// as well.
// Instead we enable it per modules below, except for the tests.
//#[warn(missing_docs)]
mod evaluators;
//#[warn(missing_docs)]
mod executors;
//#[warn(missing_docs)]
mod expressions;
//#[warn(missing_docs)]
mod predictors;
//#[warn(missing_docs)]
mod validators;
//#[warn(missing_docs)]
mod symbols;
//#[warn(missing_docs)]
mod types;
pub use expressions::Executor;

View File

@@ -1,3 +1,5 @@
#![forbid(unsafe_code)]
#[macro_use]
extern crate measure_time;
@@ -5,11 +7,11 @@ use std::io;
use mercator_db::CoreQueryParameters;
use mercator_db::DataBase;
use parser::Executor;
use parser::FiltersParser;
use parser::Predictor;
use parser::QueryParser;
use parser::Validator;
use mercator_parser::Executor;
use mercator_parser::FiltersParser;
use mercator_parser::Predictor;
use mercator_parser::QueryParser;
use mercator_parser::Validator;
fn main() {
// If RUST_LOG is unset, set it to INFO, otherwise keep it as-is.
@@ -24,14 +26,16 @@ fn main() {
let db;
{
info_time!("Loading database index");
db = DataBase::load(&[&format!("{}.index", core)]).unwrap();
db = DataBase::load(&[&format!("{}.index", core)])
.unwrap_or_else(|e| panic!("Unable to load database '{}': {}", core, e));
}
let parameters = CoreQueryParameters {
db: &db,
output_space: None,
threshold_volume: None,
resolution: None,
view_port: &None,
resolution: &Some(vec![0]),
};
let parser = QueryParser::new();
let parser = FiltersParser::new();
@@ -51,7 +55,7 @@ fn main() {
}
info_time!("Interpretation");
let mut parse;
let parse;
{
info_time!("Parsing");
parse = parser.parse(&input);
@@ -90,7 +94,7 @@ fn main() {
}
if let Ok(r) = execute {
//let r = model::to_spatial_objects(&db, r);
//let r = mercator_db::json::model::to_spatial_objects(r);
info!("Execution: \n{:#?}", r);
info!("NB results: {:?}", r.len());
} else {

View File

@@ -1,3 +1,4 @@
use mercator_db::space;
use mercator_db::DataBase;
use super::expressions::Predictor;
@@ -7,7 +8,7 @@ impl Predictor for Projection {
fn predict(&self, db: &DataBase) -> Result<f64, String> {
match self {
Projection::Nifti(_, _, bag) => bag.predict(db),
Projection::JSON(_, _, bag) => bag.predict(db),
Projection::Json(_, _, bag) => bag.predict(db),
}
}
}
@@ -15,8 +16,12 @@ impl Predictor for Projection {
impl Predictor for Bag {
fn predict(&self, db: &DataBase) -> Result<f64, String> {
match self {
Bag::ViewPort(bag) => bag.predict(db),
Bag::Distinct(bag) => bag.predict(db),
Bag::Filter(_, bag) => bag.predict(db),
Bag::Filter(_, bag) => match bag {
None => Ok(db.space(space::Space::universe().name())?.volume()),
Some(b) => b.predict(db),
},
Bag::Complement(bag) => Ok(db.space(bag.space())?.volume() - bag.predict(db)?),
Bag::Intersection(lh, rh) => {
let l = lh.predict(db)?;

View File

@@ -51,7 +51,7 @@ JsonOperator: symbols::Projection = {
None => Space::universe().name().clone(),
};
symbols::Projection::JSON(space_id, f, b)
symbols::Projection::Json(space_id, f, b)
}
};
@@ -135,7 +135,11 @@ Aggregations: symbols::Aggregation = {
//*********************************************************************/
// SELECTING / FILTERING DATA */
//*********************************************************************/
pub Filters = { Bags };
pub Filters: symbols::Bag = {
<Bags>
//<Bags> =>
// symbols::Bag::ViewPort(Box::new(<>))
};
// All these expressions generate bags.
Bags: symbols::Bag = {
@@ -149,6 +153,8 @@ Bags: symbols::Bag = {
// Spatial Operators
Inside,
Outside,
// returns the positions or volume of the shape, instead of the data points in or outside it.
//Shape,
};
//*********************************************************************/
@@ -183,9 +189,13 @@ Union: symbols::Bag = {
Filter: symbols::Bag = {
// "filter" "(" <p:Predicates> "," <b:Bags> ")" =>
"filter" "(" <b:Bags> ")" =>
symbols::Bag::Filter(None, Box::new(b)),
symbols::Bag::Filter(None, Some(Box::new(b))),
"filter" "(" <p:Predicates> <b:("," <Bags> )?> ")" =>
symbols::get_filter(p, b)
match b {
None => symbols::Bag::Filter(Some(p), None),
Some(b) => symbols::Bag::Filter(Some(p), Some(Box::new(b))),
}
};
Predicates: symbols::Predicate = {
@@ -268,6 +278,12 @@ Inside: symbols::Bag = {
symbols::Bag::Inside(<>)
};
//FIXME: ADD A SHAPE VARIANT WHICH JUST RETURNS ALL THE POSITIONS OF THAT SHAPE
//Shape: symbols::Bag = {
// <Shapes> =>
// symbols::Bag::Shape(<>)
//}
//*********************************************************************/
// SHAPES */
//*********************************************************************/
@@ -278,6 +294,7 @@ Shapes: symbols::Shape = {
Point,
HyperRectangle,
HyperSphere,
Label,
Nifti
};
@@ -329,6 +346,21 @@ Point: symbols::Shape = {
}
};
// Filter by Label, a.k.a use an ID to define a volume, and use that volume to
// select data points.
Label: symbols::Shape = {
"label" "{"
<id:String>
<rs:( "," <String> )?>
"}" => {
let space_id = match rs {
Some(id) => id,
None => Space::universe().name().clone(),
};
symbols::Shape::Label(space_id, id)
}
};
// Define a shape as the non-zero values in a NIfTI object, defined by
// nifti{
// spaceId: string,

View File

@@ -1,7 +1,7 @@
use std::cmp::Ordering;
use mercator_db::space;
use mercator_db::SpaceObject;
use mercator_db::Properties;
pub use super::types::*;
@@ -11,14 +11,14 @@ pub use super::types::*;
#[derive(Clone, Debug)]
pub enum Projection {
Nifti(String, LiteralSelector, Bag),
JSON(String, JsonValue, Bag),
Json(String, JsonValue, Bag),
}
impl Projection {
pub fn space(&self) -> &String {
match self {
Projection::Nifti(space, _, _) => &space,
Projection::JSON(space, _, _) => &space,
Projection::Nifti(space, _, _) => space,
Projection::Json(space, _, _) => space,
}
}
}
@@ -57,8 +57,11 @@ struct Transform {
/**********************************************************************/
#[derive(Clone, Debug)]
pub enum Bag {
// This is an implicit operator, inserted by the parser. Never to be used directly.
ViewPort(Box<Bag>),
// Bags
Distinct(Box<Bag>),
Filter(Option<Predicate>, Box<Bag>),
Filter(Option<Predicate>, Option<Box<Bag>>),
Complement(Box<Bag>),
Intersection(Box<Bag>, Box<Bag>),
Union(Box<Bag>, Box<Bag>),
@@ -72,8 +75,12 @@ pub enum Bag {
impl Bag {
pub fn space(&self) -> &String {
match self {
Bag::ViewPort(bag) => bag.space(),
Bag::Distinct(bag) => bag.space(),
Bag::Filter(_, bag) => bag.space(),
Bag::Filter(_, bag) => match bag {
None => space::Space::universe().name(),
Some(b) => b.space(),
},
Bag::Complement(bag) => bag.space(),
Bag::Intersection(lh, _) => {
// We are assuming lh and rh are in the same space.
@@ -120,6 +127,7 @@ pub enum Shape {
Point(String, LiteralPosition),
HyperRectangle(String, Vec<LiteralPosition>),
HyperSphere(String, LiteralPosition, LiteralNumber),
Label(String, String),
Nifti(String),
}
@@ -129,13 +137,14 @@ impl Shape {
Shape::Point(space, _) => space,
Shape::HyperRectangle(space, _) => space,
Shape::HyperSphere(space, _, _) => space,
Shape::Label(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::Point(_, _) => f64::EPSILON, // The smallest non-zero volume possible
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);
@@ -197,9 +206,17 @@ impl Shape {
a * radius.powi(i as i32)
}
Shape::Nifti(_) => unimplemented!(),
Shape::Label(_, _) => {
// FIXME: Needs to find a way to figure out the approximate volume of this specific ID, or return MAX or MIN..
f64::EPSILON
}
Shape::Nifti(_) => unimplemented!("Nifti"),
}
}
pub fn rasterize<'e>(&self) -> mercator_db::ResultSet<'e> {
unimplemented!("rasterize")
}
}
/**********************************************************************/
@@ -213,7 +230,10 @@ pub enum Position {
}
impl Position {
pub fn value(&self, object: &SpaceObject) -> LiteralPosition {
pub fn value<'e>(
&self,
object: (&'e String, &'e space::Position, &'e Properties),
) -> LiteralPosition {
match self {
Position::Literal(literal) => literal.clone(),
Position::Selector(selector) => selector.position(object),
@@ -333,6 +353,23 @@ impl From<&LiteralPosition> for Vec<f64> {
}
}
impl From<&Vec<f64>> for LiteralPosition {
fn from(v: &Vec<f64>) -> Self {
let mut lv = Vec::with_capacity(v.len());
for value in v {
lv.push(LiteralNumber::Float(*value));
}
LiteralPosition(lv)
}
}
impl From<&space::Position> for LiteralPosition {
fn from(position: &space::Position) -> Self {
let lv: Vec<f64> = position.into();
(&lv).into()
}
}
impl PartialOrd for LiteralPosition {
fn partial_cmp(&self, other: &LiteralPosition) -> Option<Ordering> {
let LiteralPosition(lh) = self;
@@ -379,59 +416,30 @@ impl LiteralSelector {
}
// FIXME: THIS IS SOOO WRONG
pub fn position(&self, object: &SpaceObject) -> LiteralPosition {
pub fn position<'e>(
&self,
object: (&'e String, &'e space::Position, &'e Properties),
) -> LiteralPosition {
println!("LiteralSelector.position(): {:?}", self);
let v: Vec<f64> = object.position.clone().into();
LiteralPosition(v.into_iter().map(LiteralNumber::Float).collect::<Vec<_>>())
object.1.into()
}
// FIXME: THIS IS SOOO WRONG
pub fn str(&self, object: &SpaceObject) -> String {
pub fn str<'e>(&self, object: (&'e String, &'e space::Position, &'e Properties)) -> &'e str {
let LiteralSelector(v) = self;
let last = v.last();
if let Some(Field(name, _)) = last {
if name == "id" {
return object.value.id().into();
return object.2.id();
} else if name == "type" {
return object.value.type_name().into();
return object.2.type_name();
} else if name == "reference_space" {
return object.space_id.clone();
return object.0;
}
}
println!("LiteralSelector.str(): {:?}", self);
unimplemented!();
}
}
// The logic was getting a bit too complex to be embedded directly into the
// grammar definition.
pub fn get_filter(p: Predicate, b: Option<Bag>) -> Bag {
match b {
Some(b) => Bag::Filter(Some(p), Box::new(b)),
None => {
let (low, high) = space::Space::universe().bounding_box();
let low: Vec<_> = low.into();
let high: Vec<_> = high.into();
let bb = Shape::HyperRectangle(
space::Space::universe().name().clone(),
vec![
LiteralPosition(
low.into_iter()
.map(LiteralNumber::Float)
.collect::<Vec<_>>(),
),
LiteralPosition(
high.into_iter()
.map(LiteralNumber::Float)
.collect::<Vec<_>>(),
),
],
);
Bag::Filter(Some(p), Box::new(Bag::Inside(bb)))
}
unimplemented!("Unknown Field");
}
}

View File

@@ -11,23 +11,10 @@ pub enum LiteralTypes {
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::String => matches!(other, LiteralTypes::String),
LiteralTypes::Int => matches!(other, LiteralTypes::Int),
LiteralTypes::Float => matches!(other, LiteralTypes::Float | LiteralTypes::Int),
LiteralTypes::Bag(_) => matches!(other, LiteralTypes::Bag(_)),
LiteralTypes::Vector(v) => match other {
LiteralTypes::Vector(ov) => {
let n = v.len();

View File

@@ -9,7 +9,7 @@ impl Validator for Projection {
fn validate(&self) -> ValidationResult {
match self {
Projection::Nifti(_, _, _) => Err("not yet implemented".to_string()),
Projection::JSON(_, _format, bag) => bag.validate(),
Projection::Json(_, _format, bag) => bag.validate(),
//FIXME: Add support for projections
/* match format.validate() {
Ok(_) => bag.validate(),
@@ -54,8 +54,12 @@ impl Validator for Bag {
}
match self {
Bag::ViewPort(bag) => bag.validate(),
Bag::Distinct(bag) => bag.validate(),
Bag::Filter(_, bag) => bag.validate(),
Bag::Filter(_, bag) => match bag {
None => Ok(LiteralPosition(vec![]).get_type()),
Some(b) => b.validate(),
},
Bag::Complement(bag) => bag.validate(),
Bag::Intersection(lh, rh) => compare_bag_types(lh, rh),
Bag::Union(lh, rh) => compare_bag_types(lh, rh),
@@ -148,6 +152,10 @@ impl Validator for Shape {
}
}
Shape::HyperSphere(_, pos, _) => pos.validate(),
Shape::Label(_, _) => {
// FIXME: Quick Hack, we need to fix this and return the effective type of the object Id.
Ok(LiteralPosition(vec![]).get_type())
}
Shape::Nifti(_) => Err("not yet implemented".to_string()),
}
}