diff --git a/src/typechecker/checker.rs b/src/typechecker/checker.rs index 5278ca2..2902027 100644 --- a/src/typechecker/checker.rs +++ b/src/typechecker/checker.rs @@ -103,14 +103,14 @@ impl Typechecker { match node { PathPattern::Node(desc) => { let t = self.refine_pattern_node(desc); - let p = PathType::to_path_type(&t, EdgeDirection::Any); + let p = PathType::from((&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 p = PathType::to_path_type(&t, *dir); + let p = PathType::from((&t, *dir)); let c = TypeEnvironment::create_context(desc, t); TypecheckResult::new(p, c) } @@ -434,21 +434,10 @@ mod tests { ); Schema::new( + vec![account_node.clone(), dummy_person_node.clone()], vec![ - VariableType::Node(account_node.clone()), - VariableType::Node(dummy_person_node.clone()), - ], - vec![ - VariableType::Edge(EdgeType::directed( - transfer_desc, - account_node.clone(), - account_node.clone(), - )), - VariableType::Edge(EdgeType::directed( - foo_desc, - account_node, - dummy_person_node, - )), + EdgeType::directed(transfer_desc, account_node.clone(), account_node.clone()), + EdgeType::directed(foo_desc, account_node, dummy_person_node), ], ) } @@ -511,25 +500,17 @@ mod tests { Schema::new( vec![ - VariableType::Node(teacher_node.clone()), - VariableType::Node(student_node.clone()), - VariableType::Node(comment_node.clone()), + teacher_node.clone(), + student_node.clone(), + comment_node.clone(), ], vec![ // Knows {since: int} — directed for refinement purposes - VariableType::Edge(EdgeType::directed( - knows_desc, - teacher_node.clone(), - student_node.clone(), - )), + EdgeType::directed(knows_desc, teacher_node.clone(), student_node.clone()), // Likes is directed (->) - VariableType::Edge(EdgeType::directed( - likes_desc, - teacher_node, - comment_node.clone(), - )), + EdgeType::directed(likes_desc, teacher_node, comment_node.clone()), // Author is directed (->) - VariableType::Edge(EdgeType::directed(author_desc, comment_node, student_node)), + EdgeType::directed(author_desc, comment_node, student_node), ], ) } @@ -863,7 +844,7 @@ mod tests { // Part 2: schema with closed {status: bool} — "stauts" doesn't exist → empty let schema = Schema::new( - vec![VariableType::node_with(DescriptorType::new( + vec![NodeType(DescriptorType::new( LabelType::Star, PropertyType::Closed(HashMap::from([( "status".into(), @@ -879,7 +860,7 @@ mod tests { #[test] fn test_example24() { let schema = Schema::new( - vec![VariableType::node_with(DescriptorType::new( + vec![NodeType(DescriptorType::new( LabelType::Star, PropertyType::Closed(HashMap::from([( "status".into(), diff --git a/src/typechecker/path_type.rs b/src/typechecker/path_type.rs index c8c5990..6af349b 100644 --- a/src/typechecker/path_type.rs +++ b/src/typechecker/path_type.rs @@ -1,18 +1,38 @@ -use crate::ast::EdgeDirection; +use crate::ast::{DescriptorType, EdgeDirection}; use super::schema::Schema; -use super::variable_type::{EdgeKind, EdgeType, VariableType}; +use super::variable_type::{EdgeKind, EdgeType, NodeType, VariableType}; + +#[derive(PartialEq, Clone, Debug)] +pub struct NodePathType { + pub n: NodeType, +} + +impl NodePathType { + pub fn new(n: NodeType) -> Self { + NodePathType { n } + } +} + +impl From for NodePathType { + fn from(n: NodeType) -> Self { + NodePathType::new(n) + } +} + +#[derive(PartialEq, Clone, Debug)] +pub struct EdgePathType { + pub p1: Box, + pub n2: NodePathType, +} /// Path types representing sequences of nodes and edges. #[derive(PartialEq, Clone, Debug)] pub enum PathType { /// A single node in the path. - Node(VariableType), + Node(NodePathType), /// An edge connecting a path to a node: path - node. - Edge { - path: Box, - node: VariableType, - }, + Edge(EdgePathType), /// Union of two path types. Union(Box, Box), /// Bottom type (empty/inconsistent path). @@ -21,51 +41,69 @@ pub enum PathType { impl Default for PathType { fn default() -> Self { - PathType::Node(VariableType::node()) + PathType::Node(NodeType::default().into()) } } -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(), +impl From<(&VariableType, EdgeDirection)> for PathType { + fn from((t, direction): (&VariableType, EdgeDirection)) -> Self { + match t { + VariableType::Node(n) => PathType::Node(n.clone().into()), + VariableType::Edge(edge) => match edge.kind { + EdgeKind::Directed => PathType::directed_edge_to_path(edge, direction), + EdgeKind::Undirected => { + let directed = edge.to_directed(false); + PathType::directed_edge_to_path(&directed, EdgeDirection::Any) + } }, - EdgeKind::Undirected => { - let forward = edge.to_directed(false); - let reversed = edge.to_directed(true); - PathType::union(PathType::from(&forward), PathType::from(&reversed)) - } + VariableType::Union(t1, t2) => PathType::union( + PathType::from((&**t1, direction)), + PathType::from((&**t2, direction)), + ), + VariableType::Zero => PathType::Zero, + VariableType::List(_) => PathType::Zero, } } } -impl From for PathType { - fn from(edge: EdgeType) -> Self { - PathType::from(&edge) +impl PathType { + fn directed_edge_to_path(edge: &EdgeType, direction: EdgeDirection) -> PathType { + let make_step = |e: &EdgeType| { + PathType::Edge(EdgePathType { + p1: Box::new(PathType::Node(e.left.clone().into())), + n2: e.right.clone().into(), + }) + }; + + let forward = make_step(edge); + let reversed_edge = edge.to_directed(true); + let reversed = make_step(&reversed_edge); + + match direction { + EdgeDirection::Right => forward, + EdgeDirection::Left => reversed, + EdgeDirection::Any | EdgeDirection::None => PathType::union(forward, reversed), + } } -} -impl PathType { /// Creates a node path type. - pub fn node(n: VariableType) -> Self { - PathType::Node(n) + pub fn node(n: NodeType) -> Self { + PathType::Node(n.into()) } /// Creates an edge path type. - pub fn edge(path: PathType, node: VariableType) -> Self { - PathType::Edge { - path: Box::new(path), - node, - } + pub fn edge(path: PathType, node: NodeType) -> Self { + PathType::Edge(EdgePathType { + p1: Box::new(path), + n2: node.into(), + }) } /// Returns the length of the path (number of edges). pub fn len(&self) -> usize { match self { PathType::Node(_) => 0, - PathType::Edge { path, .. } => path.len() + 1, + PathType::Edge(edge) => edge.p1.len() + 1, PathType::Union(p1, p2) => p1.len().min(p2.len()), PathType::Zero => 0, } @@ -75,7 +113,7 @@ impl PathType { pub fn is_empty(&self) -> bool { match self { PathType::Node(_) | PathType::Zero => true, - PathType::Edge { .. } => false, + PathType::Edge(_) => false, PathType::Union(p1, p2) => p1.is_empty() || p2.is_empty(), } } @@ -101,67 +139,43 @@ impl PathType { .unwrap_or(PathType::Zero) } - /// Converts a VariableType to a PathType given a direction. - 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 => { - 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)) - } - } - } - EdgeKind::Undirected => PathType::from(edge), - }, - VariableType::Union(t1, t2) => PathType::union( - PathType::to_path_type(t1, direction), - PathType::to_path_type(t2, direction), - ), - VariableType::Zero => PathType::Zero, - VariableType::List(_) => PathType::Zero, - } - } - /// Computes the meet (greatest lower bound) of two path types. pub fn meet(schema: &Schema, p1: &PathType, p2: &PathType) -> PathType { match (p1, p2) { (PathType::Zero, _) | (_, PathType::Zero) => PathType::Zero, - (PathType::Node(n1), PathType::Node(n2)) => match VariableType::meet(n1, n2) { - Ok(met) => PathType::Node(VariableType::refine(schema, &met)), - Err(_) => PathType::Zero, - }, + (PathType::Node(n1), PathType::Node(n2)) => { + let met = VariableType::Node(NodeType(DescriptorType::meet(&n1.n.0, &n2.n.0))); + let nodes = VariableType::refine_to_nodes(schema, &met); + PathType::union_from_list( + nodes + .into_iter() + .map(|n| PathType::Node(n.into())) + .collect(), + ) + } - ( - PathType::Edge { - path: p1_path, - node: p1_node, - }, - PathType::Node(n2), - ) => match VariableType::meet(p1_node, n2) { - Ok(met) => PathType::Edge { - path: p1_path.clone(), - node: VariableType::refine(schema, &met), - }, - Err(_) => PathType::Zero, - }, + (PathType::Edge(p1_edge), PathType::Node(n2)) => { + let met = + VariableType::Node(NodeType(DescriptorType::meet(&p1_edge.n2.n.0, &n2.n.0))); + let nodes = VariableType::refine_to_nodes(schema, &met); + PathType::union_from_list( + nodes + .into_iter() + .map(|n| { + PathType::Edge(EdgePathType { + p1: Box::new((*p1_edge.p1).clone()), + n2: n.into(), + }) + }) + .collect(), + ) + } - ( - _, - PathType::Edge { - path: p2_path, - node: p2_node, - }, - ) => PathType::Edge { - path: Box::new(PathType::meet(schema, p1, p2_path)), - node: p2_node.clone(), - }, + (_, PathType::Edge(p2_edge)) => PathType::Edge(EdgePathType { + p1: Box::new(PathType::meet(schema, p1, &p2_edge.p1)), + n2: p2_edge.n2.clone(), + }), (_, PathType::Union(u1, u2)) => { let m1 = PathType::meet(schema, p1, u1); @@ -193,9 +207,9 @@ impl PathType { pub fn is_unsatisfiable(&self) -> bool { match self { PathType::Zero => true, - PathType::Node(n) => VariableType::is_empty(n), - PathType::Edge { path, node } => { - path.is_unsatisfiable() || VariableType::is_empty(node) + PathType::Node(n) => DescriptorType::is_empty(&n.n.0), + PathType::Edge(edge) => { + edge.p1.is_unsatisfiable() || DescriptorType::is_empty(&edge.n2.n.0) } PathType::Union(p1, p2) => p1.is_unsatisfiable() && p2.is_unsatisfiable(), } diff --git a/src/typechecker/schema.rs b/src/typechecker/schema.rs index fc51e9b..3657c03 100644 --- a/src/typechecker/schema.rs +++ b/src/typechecker/schema.rs @@ -1,6 +1,6 @@ use crate::ast::DescriptorType; -use super::variable_type::{NodeType, VariableType}; +use super::variable_type::{EdgeType, NodeType}; /// Represents a user-defined schema for a GQL graph. /// @@ -11,13 +11,13 @@ use super::variable_type::{NodeType, VariableType}; /// Used for type checking and type refinement of query patterns. #[derive(Clone, Debug)] pub struct Schema { - pub nodes: Vec, - pub edges: Vec, + pub nodes: Vec, + pub edges: Vec, } impl Schema { /// Constructs a schema from explicitly provided node and edge types. - pub fn new(nodes: Vec, edges: Vec) -> Self { + pub fn new(nodes: Vec, edges: Vec) -> Self { Schema { nodes, edges } } @@ -28,14 +28,14 @@ impl Schema { /// - one directional edge and one undirected edge, both with star descriptors pub fn star() -> Self { Schema { - nodes: vec![VariableType::node()], + nodes: vec![NodeType::default()], edges: vec![ - VariableType::edge_directional( + EdgeType::directed( DescriptorType::star(), NodeType::default(), NodeType::default(), ), - VariableType::edge_non_directional( + EdgeType::undirected( DescriptorType::star(), NodeType::default(), NodeType::default(), diff --git a/src/typechecker/variable_type.rs b/src/typechecker/variable_type.rs index f1ee881..9bab96c 100644 --- a/src/typechecker/variable_type.rs +++ b/src/typechecker/variable_type.rs @@ -93,6 +93,24 @@ impl From for VariableType { } impl VariableType { + pub fn refine_to_nodes(schema: &Schema, t: &VariableType) -> Vec { + let mut out = Vec::new(); + let mut stack = vec![VariableType::refine(schema, t)]; + + while let Some(curr) = stack.pop() { + match curr { + VariableType::Node(n) => out.push(n), + VariableType::Union(t1, t2) => { + stack.push(*t2); + stack.push(*t1); + } + _ => {} + } + } + + out + } + /// Creates a node variable type with a star descriptor. pub fn node() -> Self { VariableType::Node(NodeType::default()) @@ -285,8 +303,9 @@ impl VariableType { let refined: Vec = schema .nodes .iter() + .map(|n| VariableType::Node(n.clone())) .filter(|n| VariableType::is_subtype(n, node)) - .filter_map(|n| VariableType::meet(n, node).ok()) + .filter_map(|n| VariableType::meet(&n, node).ok()) .collect(); VariableType::join_from_list(refined) } @@ -294,8 +313,9 @@ impl VariableType { let refined: Vec = schema .edges .iter() + .map(|e| VariableType::Edge(e.clone())) .filter(|e| VariableType::is_subtype(e, node)) - .filter_map(|e| VariableType::meet(e, node).ok()) + .filter_map(|e| VariableType::meet(&e, node).ok()) .collect(); VariableType::join_from_list(refined) }