diff --git a/helixdb/src/grammar.pest b/helixdb/src/grammar.pest index b42113a2..3ab089ef 100644 --- a/helixdb/src/grammar.pest +++ b/helixdb/src/grammar.pest @@ -1,7 +1,7 @@ // --------------------------------------------------------------------- // Main rules // --------------------- -source = { SOI ~ (node_def | edge_def | vector_def | query_def)* ~ EOI } +source = { SOI ~ (node_def | edge_def | vector_def | enum_def | query_def)* ~ EOI } // --------------------------------------------------------------------- @@ -10,6 +10,7 @@ source = { SOI ~ (node_def | edge_def | vector_def | query_def)* ~ EOI } vector_def = { "V::" ~ identifier_upper ~ node_body? } node_def = { "N::" ~ identifier_upper ~ node_body? } edge_def = { "E::" ~ identifier_upper ~ edge_body } +enum_def = { "Enum::" ~ identifier_upper ~ enum_body } node_body = { "{" ~ field_defs ~ "}" } edge_body = { "{" ~ "From:" ~ identifier_upper ~ "," ~ ("To:" ~ identifier_upper ~ "," ~ properties ~ "}" | "To:" ~ identifier_upper ~ ","? ~ "}") } @@ -18,6 +19,9 @@ field_def = { index? ~ identifier ~ ":" ~ param_type ~ (default)? } index= { "INDEX" } default = { "DEFAULT" ~ (now | float | integer | boolean | string_literal | none) } // optional = { "OPTIONAL" } +enum_body = { "{" ~ enum_fields ~ "}" } +enum_fields = { (enum_field ~ ",")* ~ (enum_field ~ ","?)? } +enum_field = { identifier_upper ~ ("(" ~ identifier_upper ~ ")")? } properties = { "Properties" ~ ":" ~ "{" ~ field_defs? ~ "}" } @@ -39,7 +43,7 @@ get_stmt = { identifier ~ "<-" ~ evaluates_to_anything } traversal = { (start_node | start_edge | start_vector ) ~ step* ~ last_step? } id_traversal = { identifier ~ ((step+ ~ last_step?) | last_step) } anonymous_traversal = { "_" ~ ((step+ ~ last_step?) | last_step)? } -step = { "::" ~ (graph_step | where_step | closure_step | object_step | exclude_field | count | ID | range_step | AddE) } +step = { "::" ~ (graph_step | where_step | match_step | closure_step | object_step | exclude_field | count | ID | range_step | AddE) } last_step = { "::" ~ (bool_operations | update) } // change this for loop to be able to take traversals etc in the future. for_loop = { "FOR" ~ for_argument ~ "IN" ~ identifier ~ "{" ~ query_body ~ "}" } @@ -115,10 +119,11 @@ AddV = { "AddV" ~ ("<" ~ identifier_upper ~ ">") ~ ("(" ~ vector_data ~ // --------------------------------------------------------------------- // Source steps // --------------------------------------------------------------------- -start_node = { "N" ~ ("<" ~ type_args ~ ">")? ~ ("(" ~ (id_args | by_index) ~ ")")? } -start_edge = { "E" ~ ("<" ~ type_args ~ ">")? ~ ("(" ~ (id_args | by_index) ~ ")")? } -start_vector = { "V" ~ ("<" ~ type_args ~ ">")? ~ ("(" ~ (id_args | by_index) ~ ")")? } +start_node = { "N" ~ ("<" ~ type_args ~ ">")? ~ ("(" ~ (id_args | by_index) ~ ")")? ~ variable_declaration? } +start_edge = { "E" ~ ("<" ~ type_args ~ ">")? ~ ("(" ~ (id_args | by_index) ~ ")")? ~ variable_declaration? } +start_vector = { "V" ~ ("<" ~ type_args ~ ">")? ~ ("(" ~ (id_args | by_index) ~ ")")? ~ variable_declaration? } by_index = { "{" ~ id_arg ~ ":" ~ evaluates_to_anything ~ "}" } + // --------------------------------------------------------------------- // Traversal steps // --------------------------------------------------------------------- @@ -131,13 +136,13 @@ graph_step = { | in_nodes | shortest_path } -out_e ={ "OutE" ~ ("<" ~ type_args ~ ">")?} -in_e ={ "InE" ~ ("<" ~ type_args ~ ">")?} -from_n ={ "FromN"} -to_n ={ "ToN"} -out ={ "Out" ~ ("<" ~ type_args ~ ">")?} -in_nodes ={ "In" ~ ("<" ~ type_args ~ ">")?} -shortest_path ={ "ShortestPath" ~ ("<" ~ type_args ~ ">")? ~ to_from} +out_e ={ "OutE" ~ ("<" ~ type_args ~ ">")? ~ variable_declaration?} +in_e ={ "InE" ~ ("<" ~ type_args ~ ">")? ~ variable_declaration?} +from_n ={ "FromN" ~ variable_declaration?} +to_n ={ "ToN" ~ variable_declaration?} +out ={ "Out" ~ ("<" ~ type_args ~ ">")? ~ variable_declaration?} +in_nodes ={ "In" ~ ("<" ~ type_args ~ ">")? ~ variable_declaration?} +shortest_path ={ "ShortestPath" ~ ("<" ~ type_args ~ ">")? ~ to_from ~ variable_declaration?} // --------------------------------------------------------------------- @@ -153,6 +158,17 @@ update_field = { identifier ~ ":" ~ (evaluates_to_anything | anonymous_traversal update = { "UPDATE" ~ "(" ~ "{" ~ update_field ~ ("," ~ update_field)* ~ "}" ~ ")" } drop = { "DROP" ~ (traversal | id_traversal | identifier)? } +// --------------------------------------------------------------------- +// Match steps +// --------------------------------------------------------------------- +match_step = { "MATCH" ~ match_variable? ~ "{" ~ match_statements ~ "}" } +match_variable = { "|" ~ (identifier | traversal | "_") ~ "|" } +match_statements = { (match_statement ~ ",")* ~ match_statement ~ ","? } +match_statement = { match_type ~ "=>" ~ match_value } +match_type = { identifier_upper ~ "::" ~ identifier_upper ~ ("(" ~ identifier ~ ")")?} +match_value = { "{" ~ query_body ~ "}" | traversal | id_traversal | anonymous_traversal | none | identifier } + + // --------------------------------------------------------------------- // Vector steps // --------------------------------------------------------------------- @@ -183,7 +199,7 @@ exclude_field = { "!" ~ "{" ~ identifier ~ ("," ~ identifier)* ~ ("," ~ spread_o closure_step = { "|" ~ identifier ~ "|" ~ object_step } spread_object = { ".." ~ ","?} mapping_field = { (identifier ~ (":" ~ (anonymous_traversal | evaluates_to_anything | object_step))) | identifier } - +variable_declaration = { "|" ~ identifier ~ "|" } // --------------------------------------------------------------------- // Types diff --git a/helixdb/src/helixc/analyzer/analyzer.rs b/helixdb/src/helixc/analyzer/analyzer.rs index ed8ca8cc..b70ac074 100644 --- a/helixdb/src/helixc/analyzer/analyzer.rs +++ b/helixdb/src/helixc/analyzer/analyzer.rs @@ -1332,7 +1332,11 @@ impl<'a> Ctx<'a> { ) -> Type { let mut previous_step = None; let mut cur_ty = match &tr.start { - StartNode::Node { node_type, ids } => { + StartNode::Node { + node_type, + ids, + variable, + } => { if !self.node_set.contains(node_type.as_str()) { self.push_query_err( q, @@ -1464,7 +1468,11 @@ impl<'a> Ctx<'a> { gen_traversal.traversal_type = TraversalType::Ref; Type::Nodes(Some(node_type.to_string())) } - StartNode::Edge { edge_type, ids } => { + StartNode::Edge { + edge_type, + ids, + variable, + } => { if !self.edge_map.contains_key(edge_type.as_str()) { self.push_query_err( q, @@ -1508,35 +1516,37 @@ impl<'a> Ctx<'a> { Type::Edges(Some(edge_type.to_string())) } - StartNode::Identifier(identifier) => { - match self.is_valid_identifier(q, tr.loc.clone(), identifier.as_str()) { - true => scope.get(identifier.as_str()).cloned().map_or_else( - || { - self.push_query_err( - q, - tr.loc.clone(), - format!("variable named `{}` is not in scope", identifier), - format!( - "declare {} in the current scope or fix the typo", - identifier - ), - ); - Type::Unknown - }, - |var_type| { - gen_traversal.traversal_type = - TraversalType::FromVar(GenRef::Std(identifier.clone())); - gen_traversal.source_step = Separator::Empty(SourceStep::Identifier( - GenRef::Std(identifier.clone()), - )); - var_type.clone() - }, - ), - false => Type::Unknown, - } - } + StartNode::Identifier { + name: identifier, + loc, + variable, + } => match self.is_valid_identifier(q, tr.loc.clone(), identifier.as_str()) { + true => scope.get(identifier.as_str()).cloned().map_or_else( + || { + self.push_query_err( + q, + tr.loc.clone(), + format!("variable named `{}` is not in scope", identifier), + format!( + "declare {} in the current scope or fix the typo", + identifier + ), + ); + Type::Unknown + }, + |var_type| { + gen_traversal.traversal_type = + TraversalType::FromVar(GenRef::Std(identifier.clone())); + gen_traversal.source_step = Separator::Empty(SourceStep::Identifier( + GenRef::Std(identifier.clone()), + )); + var_type.clone() + }, + ), + false => Type::Unknown, + }, // anonymous will be the traversal type rather than the start type - StartNode::Anonymous => { + StartNode::Anonymous { loc, variable } => { let parent = parent_ty.unwrap(); gen_traversal.traversal_type = TraversalType::Nested(GenRef::Std("val".to_string())); // TODO: ensure this default is stable @@ -2463,31 +2473,38 @@ impl<'a> Ctx<'a> { use GraphStepType::*; match (&gs.step, cur_ty.base()) { // Node‑to‑Edge - (OutE(label), Type::Nodes(Some(node_label)) | Type::Vector(Some(node_label))) => { + ( + OutE { + edge_type, + variable, + loc, + }, + Type::Nodes(Some(node_label)) | Type::Vector(Some(node_label)), + ) => { traversal .steps .push(Separator::Period(GeneratedStep::OutE(GeneratedOutE { - label: GenRef::Literal(label.clone()), + label: GenRef::Literal(edge_type.clone()), }))); - let edge = self.edge_map.get(label.as_str()); + let edge = self.edge_map.get(edge_type.as_str()); if edge.is_none() { self.push_query_err( q, gs.loc.clone(), - format!("Edge of type `{}` does not exist", label), + format!("Edge of type `{}` does not exist", edge_type), "check the schema for valid edge types", ); return None; } match edge.unwrap().from.1 == node_label.clone() { - true => Some(Type::Edges(Some(label.to_string()))), + true => Some(Type::Edges(Some(edge_type.to_string()))), false => { self.push_query_err( q, gs.loc.clone(), format!( "Edge of type `{}` exists but it is not a valid outgoing edge type for node of type `{}`", - label, node_label + edge_type, node_label ), "check the schema for valid edge types", ); @@ -2495,30 +2512,37 @@ impl<'a> Ctx<'a> { } } } - (InE(label), Type::Nodes(Some(node_label)) | Type::Vector(Some(node_label))) => { + ( + InE { + edge_type, + variable, + loc, + }, + Type::Nodes(Some(node_label)) | Type::Vector(Some(node_label)), + ) => { traversal .steps .push(Separator::Period(GeneratedStep::InE(GeneratedInE { - label: GenRef::Literal(label.clone()), + label: GenRef::Literal(edge_type.clone()), }))); - let edge = self.edge_map.get(label.as_str()); + let edge = self.edge_map.get(edge_type.as_str()); if edge.is_none() { self.push_query_err( q, gs.loc.clone(), - format!("Edge of type `{}` does not exist", label), + format!("Edge of type `{}` does not exist", edge_type), "check the schema for valid edge types", ); return None; } match edge.unwrap().to.1 == node_label.clone() { - true => Some(Type::Edges(Some(label.to_string()))), + true => Some(Type::Edges(Some(edge_type.to_string()))), false => { self.push_query_err( q, gs.loc.clone(), - format!("Edge of type `{}` does not exist", label), + format!("Edge of type `{}` does not exist", edge_type), "check the schema for valid edge types", ); None @@ -2527,15 +2551,22 @@ impl<'a> Ctx<'a> { } // Node‑to‑Node - (Out(label), Type::Nodes(Some(node_label)) | Type::Vector(Some(node_label))) => { - let edge_type = match self.edge_map.get(label.as_str()) { - Some(ref edge) => { + ( + Out { + edge_type, + variable, + loc, + }, + Type::Nodes(Some(node_label)) | Type::Vector(Some(node_label)), + ) => { + let schema_edge_type = match self.edge_map.get(edge_type.as_str()) { + Some(edge) => { if self.node_set.contains(edge.to.1.as_str()) { EdgeType::Node } else if self.vector_set.contains(edge.to.1.as_str()) { EdgeType::Vec } else { - panic!("Edge of type `{}` does not exist", label); + panic!("Edge of type `{}` does not exist", edge_type); } } None => { @@ -2545,16 +2576,16 @@ impl<'a> Ctx<'a> { traversal .steps .push(Separator::Period(GeneratedStep::Out(GeneratedOut { - edge_type: GenRef::Ref(edge_type.to_string()), - label: GenRef::Literal(label.clone()), + edge_type: GenRef::Ref(schema_edge_type.to_string()), + label: GenRef::Literal(edge_type.clone()), }))); - let edge = self.edge_map.get(label.as_str()); + let edge = self.edge_map.get(edge_type.as_str()); // assert!(edge.is_some()); // make sure is caught if edge.is_none() { self.push_query_err( q, gs.loc.clone(), - format!("Edge of type `{}` does not exist", label), + format!("Edge of type `{}` does not exist", edge_type), "check the schema for valid edge types", ); return None; @@ -2567,7 +2598,7 @@ impl<'a> Ctx<'a> { gs.loc.clone(), format!( "Edge of type `{}` exists but it is not a valid outgoing edge type for node of type `{}`", - label, node_label + edge_type, node_label ), "check the schema for valid edge types", ); @@ -2576,15 +2607,22 @@ impl<'a> Ctx<'a> { } } - (In(label), Type::Nodes(Some(node_label)) | Type::Vector(Some(node_label))) => { - let edge_type = match self.edge_map.get(label.as_str()) { - Some(ref edge) => { + ( + In { + edge_type, + variable, + loc, + }, + Type::Nodes(Some(node_label)) | Type::Vector(Some(node_label)), + ) => { + let schema_edge_type = match self.edge_map.get(edge_type.as_str()) { + Some(edge) => { if self.node_set.contains(edge.from.1.as_str()) { EdgeType::Node } else if self.vector_set.contains(edge.from.1.as_str()) { EdgeType::Vec } else { - panic!("Edge of type `{}` does not exist", label); + panic!("Edge of type `{}` does not exist", edge_type); } } None => { @@ -2595,16 +2633,16 @@ impl<'a> Ctx<'a> { traversal .steps .push(Separator::Period(GeneratedStep::In(GeneratedIn { - edge_type: GenRef::Ref(edge_type.to_string()), - label: GenRef::Literal(label.clone()), + edge_type: GenRef::Ref(schema_edge_type.to_string()), + label: GenRef::Literal(edge_type.clone()), }))); - let edge = self.edge_map.get(label.as_str()); + let edge = self.edge_map.get(edge_type.as_str()); // assert!(edge.is_some()); if edge.is_none() { self.push_query_err( q, gs.loc.clone(), - format!("Edge of type `{}` does not exist", label), + format!("Edge of type `{}` does not exist", edge_type), "check the schema for valid edge types", ); return None; @@ -2618,7 +2656,7 @@ impl<'a> Ctx<'a> { gs.loc.clone(), format!( "Edge of type `{}` exists but it is not a valid incoming edge type for node of type `{}`", - label, node_label + edge_type, node_label ), "check the schema for valid edge types", ); @@ -2628,7 +2666,7 @@ impl<'a> Ctx<'a> { } // Edge‑to‑Node - (FromN, Type::Edges(_)) => { + (FromN { variable, loc }, Type::Edges(_)) => { traversal .steps .push(Separator::Period(GeneratedStep::FromN)); @@ -2639,7 +2677,7 @@ impl<'a> Ctx<'a> { .to_string(), ))) } - (ToN, Type::Edges(_)) => { + (ToN { variable, loc }, Type::Edges(_)) => { traversal.steps.push(Separator::Period(GeneratedStep::ToN)); Some(Type::Nodes(Some( gs.loc @@ -2707,7 +2745,7 @@ impl<'a> Ctx<'a> { match (current_step, next_step) { ( Type::Nodes(Some(span)) | Type::Vector(Some(span)), - GraphStepType::ToN | GraphStepType::FromN, + GraphStepType::ToN { variable, loc } | GraphStepType::FromN { variable, loc }, ) => { format!( "\n{}\n{}", @@ -2721,10 +2759,34 @@ impl<'a> Ctx<'a> { ), ) } - (Type::Edges(Some(span)), GraphStepType::OutE(_) | GraphStepType::InE(_)) => { + ( + Type::Edges(Some(span)), + GraphStepType::OutE { + edge_type, + variable, + loc, + } + | GraphStepType::InE { + edge_type, + variable, + loc, + }, + ) => { format!("use `FromN` or `ToN` to traverse nodes from `{}`", span) } - (Type::Edges(Some(span)), GraphStepType::Out(_) | GraphStepType::In(_)) => { + ( + Type::Edges(Some(span)), + GraphStepType::Out { + edge_type, + variable, + loc, + } + | GraphStepType::In { + edge_type, + variable, + loc, + }, + ) => { format!("use `FromN` or `ToN` to traverse nodes from `{}`", span) } @@ -2812,7 +2874,7 @@ impl<'a> Ctx<'a> { None, ); match &traversal.start { - StartNode::Identifier(name) => { + StartNode::Identifier { name, variable, loc } => { if name.to_string() == var_name { inner_traversal.traversal_type = TraversalType::NestedFrom( GenRef::Std(var_name.to_string()), @@ -2853,7 +2915,7 @@ impl<'a> Ctx<'a> { None, ); match &traversal.start { - StartNode::Identifier(name) => { + StartNode::Identifier { name, variable, loc } => { if name.to_string() == var_name { inner_traversal.traversal_type = TraversalType::NestedFrom(GenRef::Std( diff --git a/helixdb/src/helixc/parser/helix_parser.rs b/helixdb/src/helixc/parser/helix_parser.rs index 39ac13ba..f2f90aac 100644 --- a/helixdb/src/helixc/parser/helix_parser.rs +++ b/helixdb/src/helixc/parser/helix_parser.rs @@ -267,7 +267,7 @@ impl PartialEq for FieldType { l => { println!("l: {:?}", l); false - }, + } } } } @@ -384,13 +384,22 @@ pub enum StartNode { Node { node_type: String, ids: Option>, + variable: Option, }, Edge { edge_type: String, ids: Option>, + variable: Option, + }, + Identifier { + name: String, + loc: Loc, + variable: Option, + }, + Anonymous { + loc: Loc, + variable: Option, }, - Identifier(String), - Anonymous, } #[derive(Debug, Clone)] @@ -464,24 +473,62 @@ pub struct GraphStep { #[derive(Debug, Clone)] pub enum GraphStepType { - Out(String), - In(String), + Out { + edge_type: String, + variable: Option, + loc: Loc, + }, + In { + edge_type: String, + variable: Option, + loc: Loc, + }, - FromN, - ToN, + FromN { + variable: Option, + loc: Loc, + }, + ToN { + variable: Option, + loc: Loc, + }, - OutE(String), - InE(String), + OutE { + edge_type: String, + variable: Option, + loc: Loc, + }, + InE { + edge_type: String, + variable: Option, + loc: Loc, + }, ShortestPath(ShortestPath), } impl GraphStep { pub fn get_item_type(&self) -> Option { match &self.step { - GraphStepType::Out(s) => Some(s.clone()), - GraphStepType::In(s) => Some(s.clone()), - GraphStepType::OutE(s) => Some(s.clone()), - GraphStepType::InE(s) => Some(s.clone()), + GraphStepType::Out { + edge_type, + variable: _, + loc: _, + } => Some(edge_type.clone()), + GraphStepType::In { + edge_type, + variable: _, + loc: _, + } => Some(edge_type.clone()), + GraphStepType::OutE { + edge_type, + variable: _, + loc: _, + } => Some(edge_type.clone()), + GraphStepType::InE { + edge_type, + variable: _, + loc: _, + } => Some(edge_type.clone()), _ => None, } } @@ -710,6 +757,58 @@ pub struct Closure { pub loc: Loc, } +#[derive(Debug, Clone)] +pub struct VariableDeclaration { + pub loc: Loc, + pub identifier: String, +} + +#[derive(Debug, Clone)] +pub struct MatchStep { + pub loc: Loc, + pub match_variable: Option, + pub match_statements: Vec, +} + +#[derive(Debug, Clone)] +pub struct MatchStatement { + pub loc: Loc, + pub match_type: MatchType, + pub match_value: Statement, +} + +#[derive(Debug, Clone)] +pub enum MatchType { + Enum(EnumAccess), + Identifier(String), + Literal(String), +} + +#[derive(Debug, Clone)] +pub struct EnumAccess { + pub enum_name: EnumName, + pub enum_values: EnumValue, + pub loc: Loc, +} + +#[derive(Debug, Clone)] +pub struct Enum { + pub enum_name: EnumName, + pub enum_values: Vec, +} + +#[derive(Debug, Clone)] +pub struct EnumName { + pub enum_name: String, + pub loc: Loc, +} + +#[derive(Debug, Clone)] +pub struct EnumValue { + pub enum_value: String, + pub loc: Loc, +} + impl HelixParser { pub fn parse_source(input: &Content) -> Result { let mut source = Source { @@ -960,9 +1059,7 @@ impl HelixParser { _ => unreachable!(), // throw error } } - Rule::now => { - DefaultValue::Now - } + Rule::now => DefaultValue::Now, Rule::boolean => { DefaultValue::Boolean(pair.as_str().parse::().unwrap()) } @@ -1720,7 +1817,10 @@ impl HelixParser { fn parse_anon_traversal(&self, pair: Pair) -> Result { let pairs = pair.clone().into_inner(); - let start = StartNode::Anonymous; + let start = StartNode::Anonymous { + loc: pair.loc(), + variable: None, + }; let steps = pairs .map(|p| self.parse_step(p)) .collect::, _>>()?; @@ -1738,12 +1838,19 @@ impl HelixParser { let pairs = pair.into_inner(); let mut node_type = String::new(); let mut ids = None; + let mut variable = None; for p in pairs { match p.as_rule() { Rule::type_args => { node_type = p.into_inner().next().unwrap().as_str().to_string(); // WATCH } + Rule::variable_declaration => { + variable = Some(VariableDeclaration { + loc: p.loc(), + identifier: p.into_inner().next().unwrap().as_str().to_string(), + }); + } Rule::id_args => { ids = Some( p.into_inner() @@ -1833,17 +1940,28 @@ impl HelixParser { _ => unreachable!(), } } - Ok(StartNode::Node { node_type, ids }) + Ok(StartNode::Node { + node_type, + ids, + variable, + }) } Rule::start_edge => { let pairs = pair.into_inner(); let mut edge_type = String::new(); let mut ids = None; + let mut variable = None; for p in pairs { match p.as_rule() { Rule::type_args => { edge_type = p.into_inner().next().unwrap().as_str().to_string(); } + Rule::variable_declaration => { + variable = Some(VariableDeclaration { + loc: p.loc(), + identifier: p.into_inner().next().unwrap().as_str().to_string(), + }); + } Rule::id_args => { ids = Some( p.into_inner() @@ -1870,10 +1988,39 @@ impl HelixParser { _ => unreachable!(), } } - Ok(StartNode::Edge { edge_type, ids }) + Ok(StartNode::Edge { + edge_type, + ids, + variable, + }) + } + Rule::identifier => { + let mut variable = None; + if let Some(p) = pair.clone().into_inner().next() { + variable = Some(VariableDeclaration { + loc: p.loc(), + identifier: p.as_str().to_string(), + }); + } + Ok(StartNode::Identifier { + loc: pair.loc(), + name: pair.as_str().to_string(), + variable, + }) + } + _ => { + let mut variable = None; + if let Some(p) = pair.clone().into_inner().next() { + variable = Some(VariableDeclaration { + loc: p.loc(), + identifier: p.as_str().to_string(), + }); + } + Ok(StartNode::Anonymous { + loc: pair.loc(), + variable, + }) } - Rule::identifier => Ok(StartNode::Identifier(pair.as_str().to_string())), - _ => Ok(StartNode::Anonymous), } } @@ -1964,9 +2111,19 @@ impl HelixParser { .into_inner() .next() .map(|p| p.as_str().to_string()) - .ok_or_else(|| ParserError::from("Expected type".to_string())) + .ok_or_else(|| ParserError::from("Expected variable".to_string())) .unwrap() }; // TODO: change to error + + let variable = |pair: &Pair| { + pair.clone() + .into_inner() + .nth(1) // skips type_args + .map(|p| VariableDeclaration { + loc: p.loc(), + identifier: p.as_str().to_string(), + }) + }; let pair = pair.into_inner().next().unwrap(); // TODO: change to error match pair.as_rule() { // s if s.starts_with("OutE") => GraphStep { @@ -1999,38 +2156,70 @@ impl HelixParser { // } Rule::out_e => { let types = types(&pair); + let variable = variable(&pair); GraphStep { loc: pair.loc(), - step: GraphStepType::OutE(types), + step: GraphStepType::OutE { + edge_type: types, + variable, + loc: pair.loc(), + }, } } Rule::in_e => { let types = types(&pair); + let variable = variable(&pair); GraphStep { loc: pair.loc(), - step: GraphStepType::InE(types), + step: GraphStepType::InE { + edge_type: types, + variable, + loc: pair.loc(), + }, + } + } + Rule::from_n => { + let variable = variable(&pair); + GraphStep { + loc: pair.loc(), + step: GraphStepType::FromN { + variable, + loc: pair.loc(), + }, + } + } + Rule::to_n => { + let variable = variable(&pair); + GraphStep { + loc: pair.loc(), + step: GraphStepType::ToN { + variable, + loc: pair.loc(), + }, } } - Rule::from_n => GraphStep { - loc: pair.loc(), - step: GraphStepType::FromN, - }, - Rule::to_n => GraphStep { - loc: pair.loc(), - step: GraphStepType::ToN, - }, Rule::out => { let types = types(&pair); + let variable = variable(&pair); GraphStep { loc: pair.loc(), - step: GraphStepType::Out(types), + step: GraphStepType::Out { + edge_type: types, + variable, + loc: pair.loc(), + }, } } Rule::in_nodes => { let types = types(&pair); + let variable = variable(&pair); GraphStep { loc: pair.loc(), - step: GraphStepType::In(types), + step: GraphStepType::In { + edge_type: types, + variable, + loc: pair.loc(), + }, } } Rule::shortest_path => {