Skip to content

Commit

Permalink
Introduce expressions and computed value variables in typeql-rust (#294)
Browse files Browse the repository at this point in the history
## What is the goal of this PR?

We introduce in `typeql-rust` the ability to perform arithmetic
computation and store the results in a "value variable" - denoted by a
preceding `?`, the same as introduced in `typeql-java` by
#260.

All redundant parenthesis from original query will not persist in the
string representation of the parsed expression anymore. If we get this
query:
```
match
  $p isa person, has salary $s;
  ?net = (($s - 12500) * 0.8 + 12500);
```
it will be transformed into
```
match
  $p isa person, has salary $s;
  ?net = ($s - 12500) * 0.8 + 12500;
```

## What are the changes implemented in this PR?

In addition to changes implemented in
#260:
- We removed `Parenthesis` class and do not store parenthesis in the
expression tree anymore. When we build a string representation of the
expression, we add all necessary parenthesis. Nevertheless, if we get
something like `a + (b + c)`, these parenthesis will persist in the
resulting string representation.
  • Loading branch information
dmikhalin authored Aug 3, 2023
1 parent 33f0cc6 commit 848532c
Show file tree
Hide file tree
Showing 48 changed files with 2,465 additions and 671 deletions.
7 changes: 3 additions & 4 deletions grammar/TypeQL.g4
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,10 @@ type : label | VAR_CONCEPT_

label_any : label_scoped | label ;
label_scoped : LABEL_SCOPED_ ;
label : LABEL_ | schema_native | type_native | unreserved ;
label : LABEL_ | type_native | unreserved ;

// LITERAL INPUT VALUES ========================================================

schema_native : RULE ;

type_native : THING | ENTITY | ATTRIBUTE
| RELATION | ROLE ;

Expand All @@ -226,6 +224,7 @@ sign : ADD | SUBTRACT ;
unreserved : VALUE | EXPR_FUNC_NAME
| MIN | MAX | MEDIAN | MEAN | STD | SUM | COUNT
| GET | SORT | LIMIT | OFFSET | GROUP | CONTAINS
| RULE
;

// TYPEQL SYNTAX KEYWORDS =======================================================
Expand Down Expand Up @@ -290,7 +289,7 @@ DIVIDE : '/' ; MULTIPLY : '*' ;
POWER : '^' ; MODULO : '%' ;
PAREN_OPEN : '(' ; PAREN_CLOSE : ')' ;

// Incomplete list of function names usable in expressions. The 'func_name' rule references all function names.
// Incomplete list of function names usable in expressions. The 'expression_function_name' rule references all function names.
EXPR_FUNC_NAME : 'floor' | 'ceil' | 'round' | 'abs' ;

// GROUP AND AGGREGATE QUERY KEYWORDS (also used by COMPUTE QUERY)
Expand Down
105 changes: 83 additions & 22 deletions rust/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@
*/

use crate::{
common::token::Predicate,
common::token,
pattern::{
Negation, RelationVariableBuilder, RolePlayerConstraint, RuleDeclaration, ThingVariable, TypeVariable,
TypeVariableBuilder, UnboundVariable, Value, ValueConstraint,
Constant, Expression, Function, Negation, PredicateConstraint, RelationVariableBuilder, RolePlayerConstraint,
RuleDeclaration, ThingVariable, TypeVariable, TypeVariableBuilder, UnboundConceptVariable,
UnboundValueVariable, Value,
},
Pattern,
};
Expand Down Expand Up @@ -75,6 +76,42 @@ macro_rules! or {
}}
}

#[macro_export]
macro_rules! max {
($($arg:expr),* $(,)?) => {{
let args = [$($arg, )*];
$crate::pattern::Expression::Function($crate::pattern::Function {
function_name: $crate::common::token::Function::Max,
args: args.into_iter().map(|arg| Box::new(arg.into())).collect(),
})
}}
}

#[macro_export]
macro_rules! min {
($($arg:expr),* $(,)?) => {{
let args = [$($arg, )*];
$crate::pattern::Expression::Function($crate::pattern::Function {
function_name: token::Function::Min,
args: args.into_iter().map(|arg| Box::new(arg.into())).collect(),
})
}}
}

#[macro_export]
macro_rules! filter {
($($arg:expr),* $(,)?) => {{
[$(Into::<$crate::pattern::UnboundVariable>::into($arg)),*]
}}
}

#[macro_export]
macro_rules! sort_vars {
($($arg:expr),*) => {{
$crate::query::Sorting::new(vec![$(Into::<$crate::query::sorting::OrderedVariable>::into($arg), )*])
}}
}

pub fn not<T: Into<Pattern>>(pattern: T) -> Negation {
Negation::new(pattern.into())
}
Expand All @@ -83,46 +120,70 @@ pub fn rule(name: &str) -> RuleDeclaration {
RuleDeclaration::from(name)
}

pub fn var(var: impl Into<UnboundVariable>) -> UnboundVariable {
pub fn cvar(var: impl Into<UnboundConceptVariable>) -> UnboundConceptVariable {
var.into()
}

pub fn vvar(var: impl Into<UnboundValueVariable>) -> UnboundValueVariable {
var.into()
}

pub fn constant(constant: impl Into<Constant>) -> Constant {
constant.into()
}

pub fn type_(name: impl Into<String>) -> TypeVariable {
UnboundVariable::hidden().type_(name.into())
UnboundConceptVariable::hidden().type_(name.into())
}

pub fn rel<T: Into<RolePlayerConstraint>>(value: T) -> ThingVariable {
UnboundVariable::hidden().rel(value)
UnboundConceptVariable::hidden().rel(value)
}

pub fn eq<T: Into<Value>>(value: T) -> PredicateConstraint {
PredicateConstraint::new(token::Predicate::Eq, value.into())
}

pub fn neq<T: Into<Value>>(value: T) -> PredicateConstraint {
PredicateConstraint::new(token::Predicate::Neq, value.into())
}

pub fn lt<T: Into<Value>>(value: T) -> PredicateConstraint {
PredicateConstraint::new(token::Predicate::Lt, value.into())
}

pub fn lte<T: Into<Value>>(value: T) -> PredicateConstraint {
PredicateConstraint::new(token::Predicate::Lte, value.into())
}

pub fn eq<T: Into<Value>>(value: T) -> ValueConstraint {
ValueConstraint::new(Predicate::Eq, value.into())
pub fn gt<T: Into<Value>>(value: T) -> PredicateConstraint {
PredicateConstraint::new(token::Predicate::Gt, value.into())
}

pub fn neq<T: Into<Value>>(value: T) -> ValueConstraint {
ValueConstraint::new(Predicate::Neq, value.into())
pub fn gte<T: Into<Value>>(value: T) -> PredicateConstraint {
PredicateConstraint::new(token::Predicate::Gte, value.into())
}

pub fn lt<T: Into<Value>>(value: T) -> ValueConstraint {
ValueConstraint::new(Predicate::Lt, value.into())
pub fn contains<T: Into<String>>(value: T) -> PredicateConstraint {
PredicateConstraint::new(token::Predicate::Contains, Value::from(value.into()))
}

pub fn lte<T: Into<Value>>(value: T) -> ValueConstraint {
ValueConstraint::new(Predicate::Lte, value.into())
pub fn like<T: Into<String>>(value: T) -> PredicateConstraint {
PredicateConstraint::new(token::Predicate::Like, Value::from(value.into()))
}

pub fn gt<T: Into<Value>>(value: T) -> ValueConstraint {
ValueConstraint::new(Predicate::Gt, value.into())
pub fn abs<T: Into<Expression>>(arg: T) -> Function {
Function { function_name: token::Function::Abs, args: vec![Box::from(arg.into())] }
}

pub fn gte<T: Into<Value>>(value: T) -> ValueConstraint {
ValueConstraint::new(Predicate::Gte, value.into())
pub fn ceil<T: Into<Expression>>(arg: T) -> Function {
Function { function_name: token::Function::Ceil, args: vec![Box::from(arg.into())] }
}

pub fn contains<T: Into<String>>(value: T) -> ValueConstraint {
ValueConstraint::new(Predicate::Contains, Value::from(value.into()))
pub fn floor<T: Into<Expression>>(arg: T) -> Function {
Function { function_name: token::Function::Floor, args: vec![Box::from(arg.into())] }
}

pub fn like<T: Into<String>>(value: T) -> ValueConstraint {
ValueConstraint::new(Predicate::Like, Value::from(value.into()))
pub fn round<T: Into<Expression>>(arg: T) -> Function {
Function { function_name: token::Function::Round, args: vec![Box::from(arg.into())] }
}
2 changes: 2 additions & 0 deletions rust/common/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ error_messages! { TypeQLError
6: "The query has not been provided with any definables.",
MatchHasNoBoundingNamedVariable() =
7: "The match query does not have named variables to bound the nested disjunction/negation pattern(s).",
VariableNameConflict(String) =
8: "The variable names '{}' cannot be used for both concept variables and value variables.",
MatchPatternVariableHasNoNamedVariable(Pattern) =
9: "The pattern '{}' has no named variable.",
MatchHasUnboundedNestedPattern(Pattern) =
Expand Down
26 changes: 23 additions & 3 deletions rust/common/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,16 @@ string_enum! { Filter
Limit = "limit",
}

string_enum! { Operator
string_enum! { LogicOperator
And = "and",
Or = "or",
Not = "not",
}

string_enum! { Predicate
// equality
Eq = "=",
Eq = "==",
EqLegacy = "=", // TODO: Deprecate '=' as equality in 3.0
Neq = "!=",
Gt = ">",
Gte = ">=",
Expand All @@ -102,7 +103,7 @@ string_enum! { Predicate
impl Predicate {
pub fn is_equality(&self) -> bool {
use Predicate::*;
matches!(self, Eq | Neq | Gt | Gte | Lt | Lte)
matches!(self, Eq | EqLegacy | Neq | Gt | Gte | Lt | Lte) // TODO: Deprecate '=' as equality in 3.0
}

pub fn is_substring(&self) -> bool {
Expand All @@ -120,6 +121,7 @@ string_enum! { Schema
string_enum! { Constraint
Abstract = "abstract",
As = "as",
Assign = "=",
Has = "has",
IID = "iid",
Is = "is",
Expand Down Expand Up @@ -162,3 +164,21 @@ string_enum! { Order
Asc = "asc",
Desc = "desc",
}

string_enum! { ArithmeticOperator
Add = "+",
Subtract = "-",
Multiply = "*",
Divide = "/",
Modulo = "%",
Power = "^",
}

string_enum! { Function
Abs = "abs",
Ceil = "ceil",
Floor = "floor",
Max = "max",
Min = "min",
Round = "round",
}
Loading

0 comments on commit 848532c

Please sign in to comment.