Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeQL syntax updates #383

Merged
merged 11 commits into from
Dec 18, 2024
2 changes: 1 addition & 1 deletion rust/common/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ string_enum! { ValueType
Decimal = "decimal",
Double = "double",
Duration = "duration",
Long = "long",
Integer = "integer",
String = "string",
}

Expand Down
9 changes: 3 additions & 6 deletions rust/parser/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use self::{
single::visit_statement_single,
thing::{visit_statement_relation_anonymous, visit_statement_thing_var},
};
use self::single::visit_statement_single;
use super::{
expression::visit_expression_value, statement::type_::visit_statement_type, IntoChildNodes, Node, Rule, RuleMatcher,
};
use crate::{
common::{error::TypeQLError, token::Comparator, Spanned},
parser::statement::thing::visit_statement_thing,
statement::{comparison::Comparison, Statement},
};

Expand All @@ -26,8 +24,7 @@ pub(super) fn visit_statement(node: Node<'_>) -> Statement {
match child.as_rule() {
Rule::statement_single => visit_statement_single(child),
Rule::statement_type => visit_statement_type(child),
Rule::statement_thing_var => visit_statement_thing_var(child),
Rule::statement_relation_anonymous => visit_statement_relation_anonymous(child),
Rule::statement_thing => visit_statement_thing(child),
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.to_string() }),
}
}
Expand Down
2 changes: 2 additions & 0 deletions rust/parser/statement/single.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub fn visit_statement_assignment(node: Node<'_>) -> Assignment {
debug_assert_eq!(node.as_rule(), Rule::statement_assignment);
let span = node.span();
let mut children = node.into_children();
children.skip_expected(Rule::LET);
let lhs = visit_assignment_left(children.consume_expected(Rule::assignment_left));
children.skip_expected(Rule::ASSIGN);
let rhs = visit_expression(children.consume_expected(Rule::expression));
Expand Down Expand Up @@ -108,6 +109,7 @@ pub fn visit_statement_in(node: Node<'_>) -> InIterable {
debug_assert_eq!(node.as_rule(), Rule::statement_in);
let span = node.span();
let mut children = node.into_children();
children.skip_expected(Rule::LET);
let lhs = visit_vars_assignment(children.consume_expected(Rule::vars_assignment));
children.skip_expected(Rule::IN);
let child = children.consume_any();
Expand Down
84 changes: 41 additions & 43 deletions rust/parser/statement/thing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,71 +8,62 @@ use crate::{
common::{error::TypeQLError, Spanned},
expression::Expression,
parser::{
expression::{visit_expression_list, visit_expression_struct, visit_expression_value},
expression::{visit_expression, visit_expression_list, visit_expression_struct, visit_expression_value},
literal::visit_value_literal,
statement::visit_comparison,
type_::{visit_type_ref, visit_type_ref_list},
visit_var, IntoChildNodes, Node, Rule, RuleMatcher,
},
statement::{
thing::{
isa::{Isa, IsaKind},
AttributeComparisonStatement, AttributeValueStatement, Constraint, Has, HasValue, Head, Iid, Links,
Relation, RolePlayer, Thing,
isa::{Isa, IsaInstanceConstraint, IsaKind},
Constraint, Has, HasValue, Head, Iid, Links, Relation, RolePlayer, Thing,
},
Statement,
},
type_::TypeRefAny,
TypeRef,
};

pub(in crate::parser) fn visit_statement_thing(node: Node<'_>) -> Statement {
debug_assert_eq!(node.as_rule(), Rule::statement_thing);
let child = node.into_child();
match child.as_rule() {
Rule::statement_thing_var => visit_statement_thing_var(child),
Rule::statement_relation_anonymous => visit_statement_relation_anonymous(child),
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.to_string() }),
}
}

pub(super) fn visit_statement_thing_var(node: Node<'_>) -> Statement {
debug_assert_eq!(node.as_rule(), Rule::statement_thing_var);
let span = node.span();
let mut children = node.into_children();
let var = visit_var(children.consume_expected(Rule::var));
match children.peek_rule().unwrap() {
Rule::thing_constraint => {
Statement::Thing(Thing::new(span, Head::Variable(var), children.map(visit_thing_constraint).collect()))
}
Rule::value_literal => {
let value = visit_value_literal(children.consume_expected(Rule::value_literal));
let isa = visit_isa_constraint(children.consume_expected(Rule::isa_constraint));
debug_assert_eq!(children.try_consume_any(), None);
Statement::AttributeValue(AttributeValueStatement::new(span, var, value, isa))
}
Rule::expression_struct => {
let value = visit_expression_struct(children.consume_expected(Rule::expression_struct));
let isa = visit_isa_constraint(children.consume_expected(Rule::isa_constraint));
debug_assert_eq!(children.try_consume_any(), None);
Statement::AttributeValue(AttributeValueStatement::new(span, var, value, isa))
let child = children.consume_any();
match child.as_rule() {
Rule::var => {
let var = visit_var(child);
let constraints = visit_thing_constraint_list(children.consume_expected(Rule::thing_constraint_list));
Statement::Thing(Thing::new(span, Head::Variable(var), constraints))
}
Rule::comparison => {
let comparison = visit_comparison(children.consume_expected(Rule::comparison));
let isa = visit_isa_constraint(children.consume_expected(Rule::isa_constraint));
debug_assert_eq!(children.try_consume_any(), None);
Statement::AttributeComparison(AttributeComparisonStatement::new(span, var, comparison, isa))
Rule::thing_relation_anonymous => {
let (type_ref_opt, relation) = visit_thing_relation_anonymous(child);
let constraints = if let Some(constraint_list) = children.try_consume_expected(Rule::thing_constraint_list)
{
visit_thing_constraint_list(constraint_list)
} else {
debug_assert_eq!(children.try_consume_any(), None);
vec![]
};
Statement::Thing(Thing::new(span, Head::Relation(type_ref_opt, relation), constraints))
}
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: children.to_string() }),
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.to_string() }),
}
}

pub(super) fn visit_statement_relation_anonymous(node: Node<'_>) -> Statement {
debug_assert_eq!(node.as_rule(), Rule::statement_relation_anonymous);
pub(super) fn visit_thing_relation_anonymous(node: Node<'_>) -> (Option<TypeRef>, Relation) {
debug_assert_eq!(node.as_rule(), Rule::thing_relation_anonymous);
let span = node.span();
let mut children = node.into_children();
let head = Head::Relation(visit_relation(children.consume_expected(Rule::relation)));
let constraints = children.map(visit_thing_constraint).collect();
Statement::Thing(Thing::new(span, head, constraints))
let type_ = children.try_consume_expected(Rule::type_ref).map(visit_type_ref);
let relation = visit_relation(children.consume_expected(Rule::relation));
(type_, relation)
}

fn visit_thing_constraint_list(node: Node<'_>) -> Vec<Constraint> {
debug_assert_eq!(node.as_rule(), Rule::thing_constraint_list);
let children = node.into_children();
children.map(visit_thing_constraint).collect()
}

fn visit_thing_constraint(node: Node<'_>) -> Constraint {
Expand All @@ -93,8 +84,15 @@ fn visit_isa_constraint(node: Node<'_>) -> Isa {
let mut children = node.into_children();
let kind = visit_isa_token(children.consume_expected(Rule::ISA_));
let type_ = visit_type_ref(children.consume_expected(Rule::type_ref));
debug_assert_eq!(children.try_consume_any(), None);
Isa::new(span, kind, type_)
let instance_constraint = children.try_consume_any().map(|child| match child.as_rule() {
Rule::relation => IsaInstanceConstraint::Relation(visit_relation(child)),
Rule::expression => IsaInstanceConstraint::Expression(visit_expression(child)),
Rule::expression_struct => IsaInstanceConstraint::Struct(visit_expression_struct(child)),
Rule::value_literal => IsaInstanceConstraint::Value(visit_value_literal(child)),
Rule::comparison => IsaInstanceConstraint::Comparison(visit_comparison(child)),
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.to_string() }),
});
Isa::new(span, kind, type_, instance_constraint)
}

fn visit_isa_token(node: Node<'_>) -> IsaKind {
Expand Down
34 changes: 17 additions & 17 deletions rust/parser/test/builtin_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ fn test_function_min() {
let query = r#"match
$x isa commodity,
has price $p;
(commodity: $x, qty: $q) isa order;
$net = $p * $q;
$gross = min($net * 1.21, $net + 100.0);"#;
$oder isa order (commodity: $x, qty: $q);
let $net = $p * $q;
let $gross = min($net * 1.21, $net + 100.0);"#;
let parsed = parse_query(query).unwrap();
// let expected = typeql_match!(
// var("x").isa("commodity").has(("price", cvar("p"))),
Expand All @@ -30,9 +30,9 @@ fn test_function_max() {
let query = r#"match
$x isa commodity,
has price $p;
(commodity: $x, qty: $q) isa order;
$net = $p * $q;
$gross = max($net * 1.21, $net + 100.0);"#;
$order isa order (commodity: $x, qty: $q);
let $net = $p * $q;
let $gross = max($net * 1.21, $net + 100.0);"#;
let parsed = parse_query(query).unwrap();
// let expected = typeql_match!(
// var("x").isa("commodity").has(("price", cvar("p"))),
Expand All @@ -48,9 +48,9 @@ fn test_function_abs() {
let query = r#"match
$x isa commodity,
has price $p;
(commodity: $x, qty: $q) isa order;
$net = $p * $q;
$value = abs($net * 1.21);"#;
$order isa order (commodity: $x, qty: $q);
let $net = $p * $q;
let $value = abs($net * 1.21);"#;
let parsed = parse_query(query).unwrap();
// let expected = typeql_match!(
// var("x").isa("commodity").has(("price", cvar("p"))),
Expand All @@ -67,8 +67,8 @@ fn test_function_ceil() {
$x isa commodity,
has price $p;
(commodity: $x, qty: $q) isa order;
$net = $p * $q;
$value = ceil($net * 1.21);"#;
let $net = $p * $q;
let $value = ceil($net * 1.21);"#;
let parsed = parse_query(query).unwrap();
// let expected = typeql_match!(
// var("x").isa("commodity").has(("price", cvar("p"))),
Expand All @@ -84,9 +84,9 @@ fn test_function_floor() {
let query = r#"match
$x isa commodity,
has price $p;
(commodity: $x, qty: $q) isa order;
$net = $p * $q;
$value = floor($net * 1.21);"#;
$order isa order (commodity: $x, qty: $q);
let $net = $p * $q;
let $value = floor($net * 1.21);"#;
let parsed = parse_query(query).unwrap();
// let expected = typeql_match!(
// var("x").isa("commodity").has(("price", cvar("p"))),
Expand All @@ -102,9 +102,9 @@ fn test_function_round() {
let query = r#"match
$x isa commodity,
has price $p;
(commodity: $x, qty: $q) isa order;
$net = $p * $q;
$value = round($net * 1.21);"#;
$order isa order (commodity: $x, qty: $q);
let $net = $p * $q;
let $value = round($net * 1.21);"#;
let parsed = parse_query(query).unwrap();
// let expected = typeql_match!(
// var("x").isa("commodity").has(("price", cvar("p"))),
Expand Down
2 changes: 1 addition & 1 deletion rust/parser/test/disjunctions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn test_or_query() {
let query = r#"match
$x isa movie;
{
$y == "drama" isa genre;
$y isa genre == "drama";
($x, $y);
} or {
$x == "The Muppets";
Expand Down
24 changes: 17 additions & 7 deletions rust/parser/test/match_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ $x isa person,
#[test]
fn test_relation_query() {
let query = r#"match
$brando "Marl B" isa name;
$brando isa name "Marl B";
(actor: $brando, $char, production-with-cast: $prod);
select $char, $prod;"#;

Expand All @@ -56,6 +56,16 @@ select $char, $prod;"#;
assert_valid_eq_repr!(expected, parsed, query);
}

#[test]
fn test_labelled_relation() {
let query = r#"match
$brando isa name "Marl B";
casting (actor: $brando, $char, production-with-cast: $prod);
select $char, $prod;"#;
let parsed = parse_query(query).unwrap();
assert_valid_eq_repr!(expected, parsed, query);
}

#[test]
fn test_role_type_scoped_globally() {
let query = r#"match
Expand Down Expand Up @@ -155,7 +165,7 @@ fn test_predicate_query_4() {
let query = r#"match
$x has age $y;
$y >= $z;
$z 18 isa age;"#;
$z isa age 18;"#;

let parsed = parse_query(query).unwrap();
// let expected = ();
Expand Down Expand Up @@ -353,7 +363,7 @@ $x has release-date 1000-11-12T13:14:15.0001234567;"#;
}

#[test]
fn test_parsing_long_predicate_query() {
fn test_parsing_integer_predicate_query() {
let query = r#"match
$x isa movie,
has tmdb-vote-count <= 400;"#;
Expand All @@ -367,8 +377,8 @@ $x isa movie,
#[test]
fn test_parsing_attribute_query_by_value_variable() {
let query = r#"match
$x = 5;
$a == $x isa age;"#;
let $x = 5;
$a isa age == $x;"#;

let parsed = parse_query(query).unwrap();
// let expected = match_!(var("x").assign(5), var("a").equals(var("x")).isa("age"));
Expand All @@ -379,7 +389,7 @@ $a == $x isa age;"#;
#[test]
fn test_parsing_precedence_operators() {
let query = r#"match
$res = $a / $b * $c + $d ^ $e ^ $f / $g;"#;
let $res = $a / $b * $c + $d ^ $e ^ $f / $g;"#;

let parsed = parse_query(query).unwrap();
// let expected = match_!(var("res").assign(
Expand All @@ -391,7 +401,7 @@ $res = $a / $b * $c + $d ^ $e ^ $f / $g;"#;
#[test]
fn test_parsing_precedence_function_and_parentheses() {
let query = r#"match
$res = $a + (round($b + $c) + $d) * $e;"#;
let $res = $a + (round($b + $c) + $d) * $e;"#;

let parsed = parse_query(query).unwrap();
// let expected =
Expand Down
9 changes: 5 additions & 4 deletions rust/parser/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod modifiers;
mod nonquery;
mod regex;
mod schema_queries;
mod sugar;
mod write_queries;

macro_rules! assert_valid_eq_repr {
Expand Down Expand Up @@ -65,12 +66,12 @@ fn tmp() {
// limit 10;
// return { $name, $age, $dob };

// fun test_single_1($x: person) -> long:
// fun test_single_1($x: person) -> integer:
// match
// $x isa person;
// return count($x);

// fun test_single_many($x: person) -> long, long:
// fun test_single_many($x: person) -> integer, integer:
// match
// $x isa person, has age $a;
// return count($x), sum($a);
Expand All @@ -82,7 +83,7 @@ fn tmp() {
// $y in get_all_dob($x);
// return { $x, $age, $y };

// fun test_single_optional($x: person) -> name?, long, double?:
// fun test_single_optional($x: person) -> name?, integer, double?:
// match
// $x isa person, has age $age;
// try { $one_name = get_a_name($x); };
Expand All @@ -104,7 +105,7 @@ fn tmp() {
// $x = 10 + 11;

// person sub attribute @abstract,
// value long @values(1,2,3);
// value integer @values(1,2,3);

// $person sub attribute @abstract;
// $person sub $parent, value string @regex("abc");
Expand Down
4 changes: 2 additions & 2 deletions rust/parser/test/modifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn test_get_sort_on_value_variable() {
let query = r#"match
$x isa movie,
has rating $r;
$l = 100 - $r;
let $l = 100 - $r;
sort $l desc;"#;
let parsed = parse_query(query).unwrap();
// let expected = typeql_match!(
Expand All @@ -41,7 +41,7 @@ fn test_get_sort_multiple() {
$x isa movie,
has title $t,
has rating $r;
$rate = $r * 100;
let $rate = $r * 100;
sort $rate desc, $t;"#;
let parsed = parse_query(query).unwrap();
// let expected = typeql_match!(
Expand Down
Loading