diff --git a/src/typechecker.rs b/src/typechecker.rs index 7414b42..6a3f945 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -5,7 +5,7 @@ pub mod type_environment; pub mod variable_type; pub use checker::{TypecheckResult, Typechecker}; -pub use path_type::{Direction, PathType}; +pub use path_type::PathType; pub use schema::Schema; pub use type_environment::TypeEnvironment; pub use variable_type::{EdgeKind, EdgeType, NodeType, VariableType}; diff --git a/src/typechecker/checker.rs b/src/typechecker/checker.rs index b70afca..5278ca2 100644 --- a/src/typechecker/checker.rs +++ b/src/typechecker/checker.rs @@ -4,7 +4,7 @@ use crate::ast::{ }; use crate::parser; -use super::path_type::{Direction, PathType}; +use super::path_type::PathType; use super::schema::Schema; use super::type_environment::TypeEnvironment; use super::variable_type::{EdgeKind, EdgeType, NodeType, VariableType}; @@ -103,15 +103,14 @@ impl Typechecker { match node { PathPattern::Node(desc) => { let t = self.refine_pattern_node(desc); - let p = PathType::to_path_type(&t, Direction::Any); + let p = PathType::to_path_type(&t, EdgeDirection::Any); let c = TypeEnvironment::create_context(desc, t); TypecheckResult::new(p, c) } PathPattern::Edge(dir, desc) => { let t = self.refine_pattern_edge(dir, desc); - let path_dir = Self::to_path_direction(dir); - let p = PathType::to_path_type(&t, path_dir); + let p = PathType::to_path_type(&t, *dir); let c = TypeEnvironment::create_context(desc, t); TypecheckResult::new(p, c) } @@ -332,16 +331,6 @@ impl Typechecker { // Helpers // ----------------------------------------------- - /// Converts an AST `EdgeDirection` to a path `Direction`. - fn to_path_direction(dir: &EdgeDirection) -> Direction { - match dir { - EdgeDirection::Right => Direction::Right, - EdgeDirection::Left => Direction::Left, - EdgeDirection::Any => Direction::Any, - EdgeDirection::None => Direction::Undirected, - } - } - /// Extracts the lower bound from a quantifier. fn lower_bound(q: &Quantifier) -> u64 { match q { diff --git a/src/typechecker/path_type.rs b/src/typechecker/path_type.rs index 5e93be9..c8c5990 100644 --- a/src/typechecker/path_type.rs +++ b/src/typechecker/path_type.rs @@ -1,15 +1,8 @@ +use crate::ast::EdgeDirection; + use super::schema::Schema; use super::variable_type::{EdgeKind, EdgeType, VariableType}; -/// Edge direction in a path. -#[derive(PartialEq, Clone, Copy, Debug)] -pub enum Direction { - Right, - Left, - Undirected, - Any, -} - /// Path types representing sequences of nodes and edges. #[derive(PartialEq, Clone, Debug)] pub enum PathType { @@ -32,6 +25,28 @@ impl Default for PathType { } } +impl From<&EdgeType> for PathType { + fn from(edge: &EdgeType) -> Self { + match edge.kind { + EdgeKind::Directed => PathType::Edge { + path: Box::new(PathType::Node(edge.left.clone().into())), + node: edge.right.clone().into(), + }, + EdgeKind::Undirected => { + let forward = edge.to_directed(false); + let reversed = edge.to_directed(true); + PathType::union(PathType::from(&forward), PathType::from(&reversed)) + } + } + } +} + +impl From for PathType { + fn from(edge: EdgeType) -> Self { + PathType::from(&edge) + } +} + impl PathType { /// Creates a node path type. pub fn node(n: VariableType) -> Self { @@ -87,37 +102,22 @@ impl PathType { } /// Converts a VariableType to a PathType given a direction. - pub fn to_path_type(t: &VariableType, direction: Direction) -> PathType { + pub fn to_path_type(t: &VariableType, direction: EdgeDirection) -> PathType { match t { VariableType::Node(_) => PathType::Node(t.clone()), VariableType::Edge(edge) => match edge.kind { - EdgeKind::Directed => match direction { - Direction::Right => PathType::Edge { - path: Box::new(PathType::Node(edge.left.clone().into())), - node: edge.right.clone().into(), - }, - Direction::Left => { - let flipped = VariableType::Edge(EdgeType::directed( - edge.descriptor.clone(), - edge.right.clone(), - edge.left.clone(), - )); - PathType::to_path_type(&flipped, Direction::Right) + EdgeKind::Directed => { + let forward = edge.to_directed(false); + let reversed = edge.to_directed(true); + match direction { + EdgeDirection::Right => PathType::from(&forward), + EdgeDirection::Left => PathType::from(&reversed), + EdgeDirection::Any | EdgeDirection::None => { + PathType::union(PathType::from(&forward), PathType::from(&reversed)) + } } - Direction::Any => PathType::union( - PathType::to_path_type(t, Direction::Right), - PathType::to_path_type(t, Direction::Left), - ), - Direction::Undirected => PathType::to_path_type(t, Direction::Any), - }, - EdgeKind::Undirected => { - let as_directed = VariableType::Edge(EdgeType::directed( - edge.descriptor.clone(), - edge.left.clone(), - edge.right.clone(), - )); - PathType::to_path_type(&as_directed, Direction::Any) } + EdgeKind::Undirected => PathType::from(edge), }, VariableType::Union(t1, t2) => PathType::union( PathType::to_path_type(t1, direction), diff --git a/src/typechecker/variable_type.rs b/src/typechecker/variable_type.rs index d0c846e..f1ee881 100644 --- a/src/typechecker/variable_type.rs +++ b/src/typechecker/variable_type.rs @@ -51,6 +51,18 @@ impl EdgeType { kind: EdgeKind::Undirected, } } + + /// Converts this edge to a directed edge. + /// + /// If `reverse` is true, endpoint order is swapped. + pub fn to_directed(&self, reverse: bool) -> Self { + let (left, right) = if reverse { + (self.right.clone(), self.left.clone()) + } else { + (self.left.clone(), self.right.clone()) + }; + EdgeType::directed(self.descriptor.clone(), left, right) + } } /// Represents the inferred or declared types of variables in a GQL query. @@ -146,21 +158,11 @@ impl VariableType { Ok(VariableType::Edge(EdgeType::directed(desc, left, right))) } (EdgeKind::Undirected, EdgeKind::Undirected) => { - let as_dir1 = VariableType::Edge(EdgeType::directed( - edge1.descriptor.clone(), - edge1.left.clone(), - edge1.right.clone(), - )); - let as_dir2 = VariableType::Edge(EdgeType::directed( - edge2.descriptor.clone(), - edge2.left.clone(), - edge2.right.clone(), - )); - let as_dir2_flipped = VariableType::Edge(EdgeType::directed( - edge2.descriptor.clone(), - edge2.right.clone(), - edge2.left.clone(), - )); + let as_dir1 = VariableType::Edge(edge1.to_directed(false)); + let edge2_a = edge2.to_directed(false); + let edge2_b = edge2.to_directed(true); + let as_dir2 = VariableType::Edge(edge2_a); + let as_dir2_flipped = VariableType::Edge(edge2_b); let n1 = VariableType::meet_edge(&as_dir1, &as_dir2); let n2 = VariableType::meet_edge(&as_dir1, &as_dir2_flipped); @@ -253,21 +255,11 @@ impl VariableType { && DescriptorType::is_subtype(&e1.right.0, &e2.right.0) } (EdgeKind::Undirected, EdgeKind::Undirected) => { - let dir1 = VariableType::Edge(EdgeType::directed( - e1.descriptor.clone(), - e1.left.clone(), - e1.right.clone(), - )); - let dir2_normal = VariableType::Edge(EdgeType::directed( - e2.descriptor.clone(), - e2.left.clone(), - e2.right.clone(), - )); - let dir2_flipped = VariableType::Edge(EdgeType::directed( - e2.descriptor.clone(), - e2.right.clone(), - e2.left.clone(), - )); + let dir1 = VariableType::Edge(e1.to_directed(false)); + let e2_a = e2.to_directed(false); + let e2_b = e2.to_directed(true); + let dir2_normal = VariableType::Edge(e2_a); + let dir2_flipped = VariableType::Edge(e2_b); VariableType::is_subtype(&dir1, &dir2_normal) || VariableType::is_subtype(&dir1, &dir2_flipped) }