From ac20a89b5910e3ecceeed54f432a5de75e38f2d1 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:28:06 +0100 Subject: [PATCH 1/5] add try to insert and delete --- rust/parser/pipeline.rs | 36 ++++++++++++++++++++++++++--- rust/parser/typeql.pest | 6 +++-- rust/query/pipeline/stage/delete.rs | 28 +++++++++++++++++++++- rust/query/pipeline/stage/insert.rs | 14 +++++------ 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/rust/parser/pipeline.rs b/rust/parser/pipeline.rs index 24446d3d..9e606624 100644 --- a/rust/parser/pipeline.rs +++ b/rust/parser/pipeline.rs @@ -175,8 +175,9 @@ fn visit_clause_insert(node: Node<'_>) -> Insert { .into_children() .skip_expected(Rule::INSERT) .map(|child| match child.as_rule() { - Rule::statement_thing => visit_statement_thing(child), - Rule::statement_assignment => Statement::Assignment(visit_statement_assignment(child)), + Rule::statement_thing => Pattern::Statement(visit_statement_thing(child)), + Rule::statement_assignment => Pattern::Statement(Statement::Assignment(visit_statement_assignment(child))), + Rule::pattern_try => Pattern::Optional(visit_pattern_try(child)), _ => unreachable!( "Unrecognised statement inside insert clause: {:?}", TypeQLError::IllegalGrammar { input: child.as_str().to_owned() } @@ -203,7 +204,18 @@ fn visit_clause_update(node: Node<'_>) -> Update { fn visit_clause_delete(node: Node<'_>) -> Delete { debug_assert_eq!(node.as_rule(), Rule::clause_delete); let span = node.span(); - let deletables = node.into_children().skip_expected(Rule::DELETE).map(visit_statement_deletable).collect(); + let deletables = node + .into_children() + .skip_expected(Rule::DELETE) + .map(|child| match child.as_rule() { + Rule::statement_deletable => visit_statement_deletable(child), + Rule::pattern_try_deletable => visit_pattern_try_deletable(child), + _ => unreachable!( + "Unrecognised statement inside delete clause: {:?}", + TypeQLError::IllegalGrammar { input: child.as_str().to_owned() } + ), + }) + .collect(); Delete::new(span, deletables) } @@ -235,6 +247,24 @@ fn visit_statement_deletable(node: Node<'_>) -> Deletable { Deletable::new(span, kind) } +fn visit_pattern_try_deletable(node: Node<'_>) -> Deletable { + debug_assert_eq!(node.as_rule(), Rule::pattern_try_deletable); + let span = node.span(); + let deletables = node + .into_children() + .skip_expected(Rule::TRY) + .map(|child| match child.as_rule() { + Rule::statement_deletable => visit_statement_deletable(child), + Rule::pattern_try_deletable => visit_pattern_try_deletable(child), + _ => unreachable!( + "Unrecognised statement inside delete clause: {:?}", + TypeQLError::IllegalGrammar { input: child.as_str().to_owned() } + ), + }) + .collect(); + Deletable::new(span, DeletableKind::Try { deletables }) +} + fn visit_clause_fetch(node: Node<'_>) -> Fetch { debug_assert_eq!(node.as_rule(), Rule::clause_fetch); let span = node.span(); diff --git a/rust/parser/typeql.pest b/rust/parser/typeql.pest index 4bbd7c66..8ce90b37 100644 --- a/rust/parser/typeql.pest +++ b/rust/parser/typeql.pest @@ -29,11 +29,11 @@ query_stage_terminal = { clause_fetch ~ SEMICOLON } clause_match = { MATCH ~ patterns } -clause_insert = { INSERT ~ ( statement_thing ~ SEMICOLON | statement_assignment ~ SEMICOLON )+ } +clause_insert = { INSERT ~ ( ( statement_thing | statement_assignment | pattern_try ) ~ SEMICOLON )+ } clause_put = { PUT ~ ( statement_thing ~ SEMICOLON )+ } clause_update = { UPDATE ~ ( statement_thing ~ SEMICOLON )+ } -clause_delete = { DELETE ~ ( statement_deletable ~ SEMICOLON )+ } +clause_delete = { DELETE ~ ( ( statement_deletable | pattern_try_deletable ) ~ SEMICOLON )+ } // STREAM OPERATORS =========================================================== @@ -123,6 +123,8 @@ statement_deletable = { HAS? ~ var ~ OF ~ var | var } +pattern_try_deletable = { TRY ~ CURLY_OPEN ~ ( statement_deletable ~ SEMICOLON )+ ~ CURLY_CLOSE } + // SINGLE STATEMENTS =========================================================== statement_single = { statement_is | statement_comparison | statement_assignment | statement_in } diff --git a/rust/query/pipeline/stage/delete.rs b/rust/query/pipeline/stage/delete.rs index 9c064b53..e03b9834 100644 --- a/rust/query/pipeline/stage/delete.rs +++ b/rust/query/pipeline/stage/delete.rs @@ -93,9 +93,27 @@ pub enum DeletableKind { Has { attribute: Variable, owner: Variable }, Links { players: Relation, relation: Variable }, Concept { variable: Variable }, + Try { deletables: Vec }, } -impl Pretty for DeletableKind {} +impl Pretty for DeletableKind { + fn fmt(&self, indent_level: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Try { deletables } => { + writeln!(f, "{} {{", token::Keyword::Try)?; + for deletable in deletables { + indent(indent_level + 1, f)?; + Pretty::fmt(deletable, indent_level + 1, f)?; + writeln!(f, ";")?; + indent(indent_level, f)?; + } + f.write_char('}')?; + Ok(()) + } + _ => write!(f, "{}", self), + } + } +} impl fmt::Display for DeletableKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -107,6 +125,14 @@ impl fmt::Display for DeletableKind { write!(f, "{} {} {} {}", token::Keyword::Links, players, token::Keyword::Of, relation) } Self::Concept { variable } => write!(f, "{}", variable), + Self::Try { deletables } => { + write!(f, "{} {{ ", token::Keyword::Try)?; + for deletable in deletables { + write!(f, "{}; ", deletable)?; + } + f.write_char('}')?; + Ok(()) + } } } } diff --git a/rust/query/pipeline/stage/insert.rs b/rust/query/pipeline/stage/insert.rs index e07f9549..ab0bc883 100644 --- a/rust/query/pipeline/stage/insert.rs +++ b/rust/query/pipeline/stage/insert.rs @@ -9,18 +9,18 @@ use std::fmt::{self, Write}; use crate::{ common::{token, Span, Spanned}, pretty::{indent, Pretty}, - statement::Statement, + pattern::Pattern, }; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Insert { pub span: Option, - pub statements: Vec, + pub patterns: Vec, } impl Insert { - pub(crate) fn new(span: Option, statements: Vec) -> Self { - Self { span, statements } + pub(crate) fn new(span: Option, patterns: Vec) -> Self { + Self { span, patterns } } } @@ -33,10 +33,10 @@ impl Spanned for Insert { impl Pretty for Insert { fn fmt(&self, indent_level: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", token::Clause::Insert)?; - for statement in &self.statements { + for pattern in &self.patterns { writeln!(f)?; indent(indent_level, f)?; - Pretty::fmt(statement, indent_level, f)?; + Pretty::fmt(pattern, indent_level, f)?; f.write_char(';')?; } Ok(()) @@ -49,7 +49,7 @@ impl fmt::Display for Insert { Pretty::fmt(self, 0, f) } else { write!(f, "{}", token::Clause::Insert)?; - for statement in &self.statements { + for statement in &self.patterns { write!(f, " {statement};")?; } Ok(()) From a14d5d8bd9f0c8b2909ca69f96aba9a5795ad141 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Fri, 26 Sep 2025 19:35:59 +0100 Subject: [PATCH 2/5] Try => Optional --- rust/parser/pipeline.rs | 2 +- rust/query/pipeline/stage/delete.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/parser/pipeline.rs b/rust/parser/pipeline.rs index 9e606624..2f76d55d 100644 --- a/rust/parser/pipeline.rs +++ b/rust/parser/pipeline.rs @@ -262,7 +262,7 @@ fn visit_pattern_try_deletable(node: Node<'_>) -> Deletable { ), }) .collect(); - Deletable::new(span, DeletableKind::Try { deletables }) + Deletable::new(span, DeletableKind::Optional { deletables }) } fn visit_clause_fetch(node: Node<'_>) -> Fetch { diff --git a/rust/query/pipeline/stage/delete.rs b/rust/query/pipeline/stage/delete.rs index e03b9834..6339f143 100644 --- a/rust/query/pipeline/stage/delete.rs +++ b/rust/query/pipeline/stage/delete.rs @@ -93,13 +93,13 @@ pub enum DeletableKind { Has { attribute: Variable, owner: Variable }, Links { players: Relation, relation: Variable }, Concept { variable: Variable }, - Try { deletables: Vec }, + Optional { deletables: Vec }, } impl Pretty for DeletableKind { fn fmt(&self, indent_level: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Try { deletables } => { + Self::Optional { deletables } => { writeln!(f, "{} {{", token::Keyword::Try)?; for deletable in deletables { indent(indent_level + 1, f)?; @@ -125,7 +125,7 @@ impl fmt::Display for DeletableKind { write!(f, "{} {} {} {}", token::Keyword::Links, players, token::Keyword::Of, relation) } Self::Concept { variable } => write!(f, "{}", variable), - Self::Try { deletables } => { + Self::Optional { deletables } => { write!(f, "{} {{ ", token::Keyword::Try)?; for deletable in deletables { write!(f, "{}; ", deletable)?; From b604303a2b3547089d88ae89d4127f398a1fb8d9 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Sat, 4 Oct 2025 11:26:05 +0100 Subject: [PATCH 3/5] fmt --- rust/query/pipeline/stage/insert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/query/pipeline/stage/insert.rs b/rust/query/pipeline/stage/insert.rs index ab0bc883..a041199e 100644 --- a/rust/query/pipeline/stage/insert.rs +++ b/rust/query/pipeline/stage/insert.rs @@ -8,8 +8,8 @@ use std::fmt::{self, Write}; use crate::{ common::{token, Span, Spanned}, - pretty::{indent, Pretty}, pattern::Pattern, + pretty::{indent, Pretty}, }; #[derive(Debug, Clone, Eq, PartialEq)] From b220659baa0a3ec70b5298bd71a7fe66fec9348d Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:41:54 +0100 Subject: [PATCH 4/5] try in update & put --- rust/parser/pipeline.rs | 38 +++++++++++------------------ rust/parser/typeql.pest | 6 ++--- rust/query/pipeline/stage/put.rs | 16 ++++++------ rust/query/pipeline/stage/update.rs | 16 ++++++------ 4 files changed, 33 insertions(+), 43 deletions(-) diff --git a/rust/parser/pipeline.rs b/rust/parser/pipeline.rs index 2f76d55d..c1cc6a16 100644 --- a/rust/parser/pipeline.rs +++ b/rust/parser/pipeline.rs @@ -10,10 +10,7 @@ use super::{ define::function::visit_definition_function, expression::{visit_expression, visit_expression_function}, literal::{visit_integer_literal, visit_quoted_string_literal}, - statement::{ - thing::{visit_relation, visit_statement_thing}, - visit_statement, - }, + statement::{thing::visit_relation, visit_statement}, type_::{visit_label, visit_label_list}, visit_reduce_assignment_var, visit_var, visit_var_named, visit_vars, IntoChildNodes, Node, Rule, RuleMatcher, }; @@ -23,7 +20,7 @@ use crate::{ token::{Order, ReduceOperator}, Spanned, }, - parser::{define::function::visit_function_block, statement::single::visit_statement_assignment}, + parser::define::function::visit_function_block, pattern::{Conjunction, Disjunction, Negation, Optional, Pattern}, query::{ pipeline::{ @@ -44,7 +41,6 @@ use crate::{ }, Pipeline, }, - statement::Statement, value::StringLiteral, TypeRef, TypeRefAny, }; @@ -171,34 +167,28 @@ fn visit_pattern_try(node: Node<'_>) -> Optional { fn visit_clause_insert(node: Node<'_>) -> Insert { debug_assert_eq!(node.as_rule(), Rule::clause_insert); let span = node.span(); - let statements = node - .into_children() - .skip_expected(Rule::INSERT) - .map(|child| match child.as_rule() { - Rule::statement_thing => Pattern::Statement(visit_statement_thing(child)), - Rule::statement_assignment => Pattern::Statement(Statement::Assignment(visit_statement_assignment(child))), - Rule::pattern_try => Pattern::Optional(visit_pattern_try(child)), - _ => unreachable!( - "Unrecognised statement inside insert clause: {:?}", - TypeQLError::IllegalGrammar { input: child.as_str().to_owned() } - ), - }) - .collect(); - Insert::new(span, statements) + let mut children = node.into_children(); + let patterns = visit_patterns(children.skip_expected(Rule::INSERT).consume_expected(Rule::patterns)); + debug_assert_eq!(children.try_consume_any(), None); + Insert::new(span, patterns) } fn visit_clause_put(node: Node<'_>) -> Put { debug_assert_eq!(node.as_rule(), Rule::clause_put); let span = node.span(); - let statement_things = node.into_children().skip_expected(Rule::PUT).map(visit_statement_thing).collect(); - Put::new(span, statement_things) + let mut children = node.into_children(); + let patterns = visit_patterns(children.skip_expected(Rule::PUT).consume_expected(Rule::patterns)); + debug_assert_eq!(children.try_consume_any(), None); + Put::new(span, patterns) } fn visit_clause_update(node: Node<'_>) -> Update { debug_assert_eq!(node.as_rule(), Rule::clause_update); let span = node.span(); - let statement_things = node.into_children().skip_expected(Rule::UPDATE).map(visit_statement_thing).collect(); - Update::new(span, statement_things) + let mut children = node.into_children(); + let patterns = visit_patterns(children.skip_expected(Rule::UPDATE).consume_expected(Rule::patterns)); + debug_assert_eq!(children.try_consume_any(), None); + Update::new(span, patterns) } fn visit_clause_delete(node: Node<'_>) -> Delete { diff --git a/rust/parser/typeql.pest b/rust/parser/typeql.pest index 8ce90b37..3fc04455 100644 --- a/rust/parser/typeql.pest +++ b/rust/parser/typeql.pest @@ -29,9 +29,9 @@ query_stage_terminal = { clause_fetch ~ SEMICOLON } clause_match = { MATCH ~ patterns } -clause_insert = { INSERT ~ ( ( statement_thing | statement_assignment | pattern_try ) ~ SEMICOLON )+ } -clause_put = { PUT ~ ( statement_thing ~ SEMICOLON )+ } -clause_update = { UPDATE ~ ( statement_thing ~ SEMICOLON )+ } +clause_insert = { INSERT ~ patterns } +clause_put = { PUT ~ patterns } +clause_update = { UPDATE ~ patterns } clause_delete = { DELETE ~ ( ( statement_deletable | pattern_try_deletable ) ~ SEMICOLON )+ } diff --git a/rust/query/pipeline/stage/put.rs b/rust/query/pipeline/stage/put.rs index ca384316..e06aeff0 100644 --- a/rust/query/pipeline/stage/put.rs +++ b/rust/query/pipeline/stage/put.rs @@ -9,18 +9,18 @@ use std::fmt::{self, Write}; use crate::{ common::{token, Span, Spanned}, pretty::{indent, Pretty}, - statement::Statement, + pattern::Pattern }; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Put { pub span: Option, - pub statements: Vec, + pub patterns: Vec, } impl Put { - pub(crate) fn new(span: Option, statements: Vec) -> Self { - Self { span, statements } + pub(crate) fn new(span: Option, patterns: Vec) -> Self { + Self { span, patterns } } } @@ -33,10 +33,10 @@ impl Spanned for Put { impl Pretty for Put { fn fmt(&self, indent_level: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", token::Clause::Put)?; - for statement in &self.statements { + for pattern in &self.patterns { writeln!(f)?; indent(indent_level, f)?; - Pretty::fmt(statement, indent_level, f)?; + Pretty::fmt(pattern, indent_level, f)?; f.write_char(';')?; } Ok(()) @@ -49,8 +49,8 @@ impl fmt::Display for Put { Pretty::fmt(self, 0, f) } else { write!(f, "{}", token::Clause::Put)?; - for statement in &self.statements { - write!(f, " {statement};")?; + for pattern in &self.patterns { + write!(f, " {pattern};")?; } Ok(()) } diff --git a/rust/query/pipeline/stage/update.rs b/rust/query/pipeline/stage/update.rs index faf119fe..43fdc613 100644 --- a/rust/query/pipeline/stage/update.rs +++ b/rust/query/pipeline/stage/update.rs @@ -8,19 +8,19 @@ use std::fmt::{self, Write}; use crate::{ common::{token, Span, Spanned}, + pattern::Pattern, pretty::{indent, Pretty}, - statement::Statement, }; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Update { pub span: Option, - pub statements: Vec, + pub patterns: Vec, } impl Update { - pub(crate) fn new(span: Option, statements: Vec) -> Self { - Self { span, statements } + pub(crate) fn new(span: Option, patterns: Vec) -> Self { + Self { span, patterns } } } @@ -33,10 +33,10 @@ impl Spanned for Update { impl Pretty for Update { fn fmt(&self, indent_level: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", token::Clause::Update)?; - for statement in &self.statements { + for pattern in &self.patterns { writeln!(f)?; indent(indent_level, f)?; - Pretty::fmt(statement, indent_level, f)?; + Pretty::fmt(pattern, indent_level, f)?; f.write_char(';')?; } Ok(()) @@ -49,8 +49,8 @@ impl fmt::Display for Update { Pretty::fmt(self, 0, f) } else { write!(f, "{}", token::Clause::Update)?; - for statement in &self.statements { - write!(f, " {statement};")?; + for pattern in &self.patterns { + write!(f, " {pattern};")?; } Ok(()) } From fb5031e1cb8388879ef800040c76f1355b5530dd Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:54:22 +0100 Subject: [PATCH 5/5] fmt --- rust/query/pipeline/stage/put.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/query/pipeline/stage/put.rs b/rust/query/pipeline/stage/put.rs index e06aeff0..dfd0de70 100644 --- a/rust/query/pipeline/stage/put.rs +++ b/rust/query/pipeline/stage/put.rs @@ -8,8 +8,8 @@ use std::fmt::{self, Write}; use crate::{ common::{token, Span, Spanned}, + pattern::Pattern, pretty::{indent, Pretty}, - pattern::Pattern }; #[derive(Debug, Clone, Eq, PartialEq)]