Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 13 additions & 32 deletions src/typechecker/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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),
],
)
}
Expand Down Expand Up @@ -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),
],
)
}
Expand Down Expand Up @@ -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(),
Expand All @@ -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(),
Expand Down
194 changes: 104 additions & 90 deletions src/typechecker/path_type.rs
Original file line number Diff line number Diff line change
@@ -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<NodeType> for NodePathType {
fn from(n: NodeType) -> Self {
NodePathType::new(n)
}
}

#[derive(PartialEq, Clone, Debug)]
pub struct EdgePathType {
pub p1: Box<PathType>,
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<PathType>,
node: VariableType,
},
Edge(EdgePathType),
/// Union of two path types.
Union(Box<PathType>, Box<PathType>),
/// Bottom type (empty/inconsistent path).
Expand All @@ -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<EdgeType> 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,
}
Expand All @@ -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(),
}
}
Expand All @@ -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);
Expand Down Expand Up @@ -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(),
}
Expand Down
14 changes: 7 additions & 7 deletions src/typechecker/schema.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand All @@ -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<VariableType>,
pub edges: Vec<VariableType>,
pub nodes: Vec<NodeType>,
pub edges: Vec<EdgeType>,
}

impl Schema {
/// Constructs a schema from explicitly provided node and edge types.
pub fn new(nodes: Vec<VariableType>, edges: Vec<VariableType>) -> Self {
pub fn new(nodes: Vec<NodeType>, edges: Vec<EdgeType>) -> Self {
Schema { nodes, edges }
}

Expand All @@ -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(),
Expand Down
Loading
Loading