Skip to content

Commit 848532c

Browse files
authored
Introduce expressions and computed value variables in typeql-rust (#294)
## 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.
1 parent 33f0cc6 commit 848532c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2465
-671
lines changed

grammar/TypeQL.g4

+3-4
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,10 @@ type : label | VAR_CONCEPT_
201201

202202
label_any : label_scoped | label ;
203203
label_scoped : LABEL_SCOPED_ ;
204-
label : LABEL_ | schema_native | type_native | unreserved ;
204+
label : LABEL_ | type_native | unreserved ;
205205

206206
// LITERAL INPUT VALUES ========================================================
207207

208-
schema_native : RULE ;
209-
210208
type_native : THING | ENTITY | ATTRIBUTE
211209
| RELATION | ROLE ;
212210

@@ -226,6 +224,7 @@ sign : ADD | SUBTRACT ;
226224
unreserved : VALUE | EXPR_FUNC_NAME
227225
| MIN | MAX | MEDIAN | MEAN | STD | SUM | COUNT
228226
| GET | SORT | LIMIT | OFFSET | GROUP | CONTAINS
227+
| RULE
229228
;
230229

231230
// TYPEQL SYNTAX KEYWORDS =======================================================
@@ -290,7 +289,7 @@ DIVIDE : '/' ; MULTIPLY : '*' ;
290289
POWER : '^' ; MODULO : '%' ;
291290
PAREN_OPEN : '(' ; PAREN_CLOSE : ')' ;
292291

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

296295
// GROUP AND AGGREGATE QUERY KEYWORDS (also used by COMPUTE QUERY)

rust/builder/mod.rs

+83-22
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
*/
2222

2323
use crate::{
24-
common::token::Predicate,
24+
common::token,
2525
pattern::{
26-
Negation, RelationVariableBuilder, RolePlayerConstraint, RuleDeclaration, ThingVariable, TypeVariable,
27-
TypeVariableBuilder, UnboundVariable, Value, ValueConstraint,
26+
Constant, Expression, Function, Negation, PredicateConstraint, RelationVariableBuilder, RolePlayerConstraint,
27+
RuleDeclaration, ThingVariable, TypeVariable, TypeVariableBuilder, UnboundConceptVariable,
28+
UnboundValueVariable, Value,
2829
},
2930
Pattern,
3031
};
@@ -75,6 +76,42 @@ macro_rules! or {
7576
}}
7677
}
7778

79+
#[macro_export]
80+
macro_rules! max {
81+
($($arg:expr),* $(,)?) => {{
82+
let args = [$($arg, )*];
83+
$crate::pattern::Expression::Function($crate::pattern::Function {
84+
function_name: $crate::common::token::Function::Max,
85+
args: args.into_iter().map(|arg| Box::new(arg.into())).collect(),
86+
})
87+
}}
88+
}
89+
90+
#[macro_export]
91+
macro_rules! min {
92+
($($arg:expr),* $(,)?) => {{
93+
let args = [$($arg, )*];
94+
$crate::pattern::Expression::Function($crate::pattern::Function {
95+
function_name: token::Function::Min,
96+
args: args.into_iter().map(|arg| Box::new(arg.into())).collect(),
97+
})
98+
}}
99+
}
100+
101+
#[macro_export]
102+
macro_rules! filter {
103+
($($arg:expr),* $(,)?) => {{
104+
[$(Into::<$crate::pattern::UnboundVariable>::into($arg)),*]
105+
}}
106+
}
107+
108+
#[macro_export]
109+
macro_rules! sort_vars {
110+
($($arg:expr),*) => {{
111+
$crate::query::Sorting::new(vec![$(Into::<$crate::query::sorting::OrderedVariable>::into($arg), )*])
112+
}}
113+
}
114+
78115
pub fn not<T: Into<Pattern>>(pattern: T) -> Negation {
79116
Negation::new(pattern.into())
80117
}
@@ -83,46 +120,70 @@ pub fn rule(name: &str) -> RuleDeclaration {
83120
RuleDeclaration::from(name)
84121
}
85122

86-
pub fn var(var: impl Into<UnboundVariable>) -> UnboundVariable {
123+
pub fn cvar(var: impl Into<UnboundConceptVariable>) -> UnboundConceptVariable {
124+
var.into()
125+
}
126+
127+
pub fn vvar(var: impl Into<UnboundValueVariable>) -> UnboundValueVariable {
87128
var.into()
88129
}
89130

131+
pub fn constant(constant: impl Into<Constant>) -> Constant {
132+
constant.into()
133+
}
134+
90135
pub fn type_(name: impl Into<String>) -> TypeVariable {
91-
UnboundVariable::hidden().type_(name.into())
136+
UnboundConceptVariable::hidden().type_(name.into())
92137
}
93138

94139
pub fn rel<T: Into<RolePlayerConstraint>>(value: T) -> ThingVariable {
95-
UnboundVariable::hidden().rel(value)
140+
UnboundConceptVariable::hidden().rel(value)
141+
}
142+
143+
pub fn eq<T: Into<Value>>(value: T) -> PredicateConstraint {
144+
PredicateConstraint::new(token::Predicate::Eq, value.into())
145+
}
146+
147+
pub fn neq<T: Into<Value>>(value: T) -> PredicateConstraint {
148+
PredicateConstraint::new(token::Predicate::Neq, value.into())
149+
}
150+
151+
pub fn lt<T: Into<Value>>(value: T) -> PredicateConstraint {
152+
PredicateConstraint::new(token::Predicate::Lt, value.into())
153+
}
154+
155+
pub fn lte<T: Into<Value>>(value: T) -> PredicateConstraint {
156+
PredicateConstraint::new(token::Predicate::Lte, value.into())
96157
}
97158

98-
pub fn eq<T: Into<Value>>(value: T) -> ValueConstraint {
99-
ValueConstraint::new(Predicate::Eq, value.into())
159+
pub fn gt<T: Into<Value>>(value: T) -> PredicateConstraint {
160+
PredicateConstraint::new(token::Predicate::Gt, value.into())
100161
}
101162

102-
pub fn neq<T: Into<Value>>(value: T) -> ValueConstraint {
103-
ValueConstraint::new(Predicate::Neq, value.into())
163+
pub fn gte<T: Into<Value>>(value: T) -> PredicateConstraint {
164+
PredicateConstraint::new(token::Predicate::Gte, value.into())
104165
}
105166

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

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

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

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

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

126-
pub fn like<T: Into<String>>(value: T) -> ValueConstraint {
127-
ValueConstraint::new(Predicate::Like, Value::from(value.into()))
187+
pub fn round<T: Into<Expression>>(arg: T) -> Function {
188+
Function { function_name: token::Function::Round, args: vec![Box::from(arg.into())] }
128189
}

rust/common/error/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ error_messages! { TypeQLError
9797
6: "The query has not been provided with any definables.",
9898
MatchHasNoBoundingNamedVariable() =
9999
7: "The match query does not have named variables to bound the nested disjunction/negation pattern(s).",
100+
VariableNameConflict(String) =
101+
8: "The variable names '{}' cannot be used for both concept variables and value variables.",
100102
MatchPatternVariableHasNoNamedVariable(Pattern) =
101103
9: "The pattern '{}' has no named variable.",
102104
MatchHasUnboundedNestedPattern(Pattern) =

rust/common/token.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,16 @@ string_enum! { Filter
8080
Limit = "limit",
8181
}
8282

83-
string_enum! { Operator
83+
string_enum! { LogicOperator
8484
And = "and",
8585
Or = "or",
8686
Not = "not",
8787
}
8888

8989
string_enum! { Predicate
9090
// equality
91-
Eq = "=",
91+
Eq = "==",
92+
EqLegacy = "=", // TODO: Deprecate '=' as equality in 3.0
9293
Neq = "!=",
9394
Gt = ">",
9495
Gte = ">=",
@@ -102,7 +103,7 @@ string_enum! { Predicate
102103
impl Predicate {
103104
pub fn is_equality(&self) -> bool {
104105
use Predicate::*;
105-
matches!(self, Eq | Neq | Gt | Gte | Lt | Lte)
106+
matches!(self, Eq | EqLegacy | Neq | Gt | Gte | Lt | Lte) // TODO: Deprecate '=' as equality in 3.0
106107
}
107108

108109
pub fn is_substring(&self) -> bool {
@@ -120,6 +121,7 @@ string_enum! { Schema
120121
string_enum! { Constraint
121122
Abstract = "abstract",
122123
As = "as",
124+
Assign = "=",
123125
Has = "has",
124126
IID = "iid",
125127
Is = "is",
@@ -162,3 +164,21 @@ string_enum! { Order
162164
Asc = "asc",
163165
Desc = "desc",
164166
}
167+
168+
string_enum! { ArithmeticOperator
169+
Add = "+",
170+
Subtract = "-",
171+
Multiply = "*",
172+
Divide = "/",
173+
Modulo = "%",
174+
Power = "^",
175+
}
176+
177+
string_enum! { Function
178+
Abs = "abs",
179+
Ceil = "ceil",
180+
Floor = "floor",
181+
Max = "max",
182+
Min = "min",
183+
Round = "round",
184+
}

0 commit comments

Comments
 (0)