use crate::operation::ConcreteData;
use crate::operation::query::ConcreteQueryOutput;
use crate::operation::signature::parameter::{AbstractOperationOutput, OperationOutput};
use crate::prelude::*;
use crate::semantics::*;
use crate::util::log;
use derive_more::From;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::ops::Deref;
pub struct ExampleWithRefSemantics;
pub struct NodeMatcher;
impl AbstractMatcher for NodeMatcher {
type Abstract = NodeType;
fn matches(argument: &Self::Abstract, parameter: &Self::Abstract) -> bool {
if argument == &NodeType::Separate || parameter == &NodeType::Separate {
return argument == parameter;
}
match (argument, parameter) {
(_, NodeType::Object) => true,
_ => argument == parameter,
}
}
}
pub struct EdgeMatcher;
impl AbstractMatcher for EdgeMatcher {
type Abstract = EdgeType;
fn matches(argument: &Self::Abstract, parameter: &Self::Abstract) -> bool {
match (argument, parameter) {
(_, EdgeType::Wildcard) => true,
(EdgeType::Exact(a), EdgeType::Exact(b)) => a == b,
_ => false,
}
}
}
pub struct NodeJoiner;
impl AbstractJoin for NodeJoiner {
type Abstract = NodeType;
fn join(a: &Self::Abstract, b: &Self::Abstract) -> Option<Self::Abstract> {
if a == b {
Some(a.clone())
} else if a != &NodeType::Separate && b != &NodeType::Separate {
Some(NodeType::Object)
} else {
None
}
}
}
pub struct EdgeJoiner;
impl AbstractJoin for EdgeJoiner {
type Abstract = EdgeType;
fn join(a: &Self::Abstract, b: &Self::Abstract) -> Option<Self::Abstract> {
match (a, b) {
(EdgeType::Exact(a), EdgeType::Exact(b)) if a == b => Some(EdgeType::Exact(a.clone())),
_ => Some(EdgeType::Wildcard),
}
}
}
pub struct NodeConcreteToAbstract;
impl ConcreteToAbstract for NodeConcreteToAbstract {
type Concrete = NodeValue;
type Abstract = NodeType;
fn concrete_to_abstract(c: &Self::Concrete) -> Self::Abstract {
match c {
NodeValue::String(_) => NodeType::String,
NodeValue::Integer(_) => NodeType::Integer,
NodeValue::Reference(_, t) => NodeType::Ref(Box::new(t.clone())),
}
}
}
pub struct EdgeConcreteToAbstract;
impl ConcreteToAbstract for EdgeConcreteToAbstract {
type Concrete = String;
type Abstract = EdgeType;
fn concrete_to_abstract(c: &Self::Concrete) -> Self::Abstract {
EdgeType::Exact(c.clone())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum NodeType {
String,
Integer,
#[default]
Object,
Ref(Box<NodeType>),
Separate,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum NodeValue {
String(String),
Integer(i32),
Reference(NodeKey, NodeType),
}
impl NodeValue {
pub fn must_string(&self) -> &str {
match self {
NodeValue::String(s) => s,
_ => {
panic!("type unsoundness: expected a string node value, found integer")
}
}
}
pub fn must_integer(&self) -> i32 {
match self {
NodeValue::Integer(i) => *i,
_ => {
panic!("type unsoundness: expected an integer node value, found string")
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum EdgeType {
Wildcard,
Exact(String),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ExampleOperation {
NoOp,
SetTo {
op_typ: NodeType,
target_typ: NodeType,
value: NodeValue,
},
SetEdgeTo {
node_typ: NodeType,
param_typ: EdgeType,
target_typ: EdgeType,
value: String,
},
AddEdge {
node_typ: NodeType,
param_typ: EdgeType,
target_typ: EdgeType,
value: String,
},
AddNode {
node_type: NodeType,
value: NodeValue,
},
CopyValueFromTo,
SwapValues,
DeleteNode,
DeleteEdge,
AddInteger(i32),
AModBToC,
MakeRef,
ExtractRef {
expected_inner_typ: NodeType,
},
}
impl BuiltinOperation for ExampleOperation {
type S = ExampleWithRefSemantics;
fn parameter(&self) -> OperationParameter<Self::S> {
let mut param_builder = OperationParameterBuilder::new();
match self {
ExampleOperation::NoOp => {
param_builder
.expect_explicit_input_node("input", NodeType::Object)
.unwrap();
}
ExampleOperation::SetTo {
op_typ,
target_typ,
value,
} => {
param_builder
.expect_explicit_input_node("target", op_typ.clone())
.unwrap();
}
ExampleOperation::SetEdgeTo {
node_typ,
param_typ: op_typ,
target_typ,
value,
} => {
param_builder
.expect_explicit_input_node("src", node_typ.clone())
.unwrap();
param_builder
.expect_explicit_input_node("dst", node_typ.clone())
.unwrap();
param_builder
.expect_edge(
SubstMarker::from("src"),
SubstMarker::from("dst"),
op_typ.clone(),
)
.unwrap();
}
ExampleOperation::AddEdge {
node_typ,
param_typ: op_typ,
target_typ,
value,
} => {
param_builder
.expect_explicit_input_node("src", node_typ.clone())
.unwrap();
param_builder
.expect_explicit_input_node("dst", node_typ.clone())
.unwrap();
}
ExampleOperation::AddNode { node_type, value } => {}
ExampleOperation::CopyValueFromTo => {
param_builder
.expect_explicit_input_node("source", NodeType::Object)
.unwrap();
param_builder
.expect_explicit_input_node("destination", NodeType::Object)
.unwrap();
}
ExampleOperation::SwapValues => {
param_builder
.expect_explicit_input_node("source", NodeType::Object)
.unwrap();
param_builder
.expect_explicit_input_node("destination", NodeType::Object)
.unwrap();
}
ExampleOperation::DeleteNode => {
param_builder
.expect_explicit_input_node("target", NodeType::Object)
.unwrap();
}
ExampleOperation::DeleteEdge => {
param_builder
.expect_explicit_input_node("src", NodeType::Object)
.unwrap();
param_builder
.expect_explicit_input_node("dst", NodeType::Object)
.unwrap();
param_builder
.expect_edge(
SubstMarker::from("src"),
SubstMarker::from("dst"),
EdgeType::Wildcard,
)
.unwrap();
}
ExampleOperation::AddInteger(i) => {
param_builder
.expect_explicit_input_node("target", NodeType::Integer)
.unwrap();
}
ExampleOperation::AModBToC => {
param_builder
.expect_explicit_input_node("a", NodeType::Integer)
.unwrap();
param_builder
.expect_explicit_input_node("b", NodeType::Integer)
.unwrap();
param_builder
.expect_explicit_input_node("c", NodeType::Integer)
.unwrap();
}
ExampleOperation::MakeRef => {
param_builder
.expect_explicit_input_node("src", NodeType::Object)
.unwrap();
}
ExampleOperation::ExtractRef { expected_inner_typ } => {
param_builder
.expect_explicit_input_node(
"ref_node",
NodeType::Ref(Box::new(expected_inner_typ.clone())),
)
.unwrap();
param_builder
.expect_explicit_input_node("attach_to", NodeType::Object)
.unwrap();
}
}
param_builder.build().unwrap()
}
fn apply_abstract(
&self,
g: &mut GraphWithSubstitution<AbstractGraph<Self::S>>,
) -> AbstractOperationOutput<Self::S> {
let mut new_node_names = HashMap::new();
match self {
ExampleOperation::NoOp => {
}
ExampleOperation::SetTo {
op_typ,
target_typ,
value,
} => {
g.set_node_value(SubstMarker::from("target"), target_typ.clone())
.unwrap();
}
ExampleOperation::SetEdgeTo {
node_typ,
param_typ: op_typ,
target_typ,
value,
} => {
g.set_edge_value(
SubstMarker::from("src"),
SubstMarker::from("dst"),
target_typ.clone(),
)
.unwrap();
}
ExampleOperation::AddEdge {
node_typ,
param_typ: op_typ,
target_typ,
value,
} => {
g.add_edge(
SubstMarker::from("src"),
SubstMarker::from("dst"),
target_typ.clone(),
);
}
ExampleOperation::AddNode { node_type, value } => {
g.add_node("new", node_type.clone());
new_node_names.insert("new".into(), "new".into());
}
ExampleOperation::CopyValueFromTo => {
let value = g.get_node_value(SubstMarker::from("source")).unwrap();
g.set_node_value(SubstMarker::from("destination"), value.clone())
.unwrap();
}
ExampleOperation::SwapValues => {
let value1 = g.get_node_value(SubstMarker::from("source")).unwrap();
let value2 = g.get_node_value(SubstMarker::from("destination")).unwrap();
let v1 = value1.clone();
let v2 = value2.clone();
g.set_node_value(SubstMarker::from("source"), v2).unwrap();
g.set_node_value(SubstMarker::from("destination"), v1)
.unwrap();
}
ExampleOperation::DeleteNode => {
g.delete_node(SubstMarker::from("target")).unwrap();
}
ExampleOperation::DeleteEdge => {
g.delete_edge(SubstMarker::from("src"), SubstMarker::from("dst"))
.unwrap();
}
ExampleOperation::AddInteger(i) => {
}
ExampleOperation::AModBToC => {
}
ExampleOperation::MakeRef => {
let src_type = g.get_node_value(SubstMarker::from("src")).unwrap().clone();
g.add_node("result", NodeType::Ref(Box::new(src_type)));
new_node_names.insert("result".into(), "result".into());
}
ExampleOperation::ExtractRef { .. } => {
}
}
g.get_abstract_output(new_node_names)
}
fn apply(
&self,
g: &mut GraphWithSubstitution<ConcreteGraph<Self::S>>,
_: &mut ConcreteData,
) -> OperationOutput {
let mut new_node_names = HashMap::new();
match self {
ExampleOperation::NoOp => {
}
ExampleOperation::SetTo {
op_typ,
target_typ,
value,
} => {
g.set_node_value(SubstMarker::from("target"), value.clone())
.unwrap();
}
ExampleOperation::SetEdgeTo {
node_typ,
param_typ: op_typ,
target_typ,
value,
} => {
g.set_edge_value(
SubstMarker::from("src"),
SubstMarker::from("dst"),
value.clone(),
)
.unwrap();
}
ExampleOperation::AddEdge {
node_typ,
param_typ: op_typ,
target_typ,
value,
} => {
g.add_edge(
SubstMarker::from("src"),
SubstMarker::from("dst"),
value.clone(),
);
}
ExampleOperation::AddNode { node_type, value } => {
g.add_node("new", value.clone());
new_node_names.insert("new".into(), "new".into());
}
ExampleOperation::CopyValueFromTo => {
let value = g.get_node_value(SubstMarker::from("source")).unwrap();
g.set_node_value(SubstMarker::from("destination"), value.clone())
.unwrap();
}
ExampleOperation::SwapValues => {
let value1 = g.get_node_value(SubstMarker::from("source")).unwrap();
let value2 = g.get_node_value(SubstMarker::from("destination")).unwrap();
let v1 = value1.clone();
let v2 = value2.clone();
g.set_node_value(SubstMarker::from("source"), v2).unwrap();
g.set_node_value(SubstMarker::from("destination"), v1)
.unwrap();
}
ExampleOperation::DeleteNode => {
g.delete_node(SubstMarker::from("target")).unwrap();
}
ExampleOperation::DeleteEdge => {
g.delete_edge(SubstMarker::from("src"), SubstMarker::from("dst"))
.unwrap();
}
ExampleOperation::AddInteger(i) => {
let NodeValue::Integer(old_value) =
g.get_node_value(SubstMarker::from("target")).unwrap()
else {
panic!(
"expected an integer node value for AddInteger operation - type unsoundness"
);
};
let value = NodeValue::Integer(*i + *old_value);
g.set_node_value(SubstMarker::from("target"), value)
.unwrap();
}
ExampleOperation::AModBToC => {
let a = g.get_node_value(SubstMarker::from("a")).unwrap();
let b = g.get_node_value(SubstMarker::from("b")).unwrap();
let c = g.get_node_value(SubstMarker::from("c")).unwrap();
let NodeValue::Integer(a_val) = a else {
panic!(
"expected an integer node value for AModBToC operation - type unsoundness"
);
};
let NodeValue::Integer(b_val) = b else {
panic!(
"expected an integer node value for AModBToC operation - type unsoundness"
);
};
let NodeValue::Integer(c_val) = c else {
panic!(
"expected an integer node value for AModBToC operation - type unsoundness"
);
};
let result = a_val % b_val;
g.set_node_value(SubstMarker::from("c"), NodeValue::Integer(result))
.unwrap();
}
ExampleOperation::MakeRef => {
let src_key = g.get_node_key(&SubstMarker::from("src").into()).unwrap();
let src_value = g.get_node_value(SubstMarker::from("src")).unwrap();
let new_node_name = "result";
let src_type = NodeConcreteToAbstract::concrete_to_abstract(src_value);
g.add_node(new_node_name, NodeValue::Reference(src_key, src_type));
new_node_names.insert(new_node_name.into(), new_node_name.into());
}
ExampleOperation::ExtractRef { expected_inner_typ } => {
let ref_node_val = g.get_node_value(SubstMarker::from("ref_node")).unwrap();
let NodeValue::Reference(ref_node_key, ref_node_type) = ref_node_val else {
panic!("type unsoundness: expected a reference node value");
};
let attach_to_key = g
.get_node_key(&SubstMarker::from("attach_to").into())
.unwrap();
if g.graph.node_attr_map.contains_key(ref_node_key) {
let val = g.graph.get_node_attr(*ref_node_key).unwrap();
let actual_type = NodeConcreteToAbstract::concrete_to_abstract(val);
if NodeMatcher::matches(&actual_type, expected_inner_typ) {
g.graph
.add_edge(attach_to_key, *ref_node_key, "attached".to_string());
} else {
log::info!(
"ExtractRef operation: reference node key {:?} has type {:?}, expected subtype of {:?}",
ref_node_key,
actual_type,
expected_inner_typ
);
}
} else {
log::info!(
"ExtractRef operation: reference node key {:?} does not exist in the graph, skipping attachment",
ref_node_key
);
}
}
}
g.get_concrete_output(new_node_names)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Copy, From)]
pub struct MyOrdering(std::cmp::Ordering);
impl Deref for MyOrdering {
type Target = std::cmp::Ordering;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ExampleQuery {
ValuesEqual,
ValueEqualTo(NodeValue),
CmpFstSnd(MyOrdering),
}
impl BuiltinQuery for ExampleQuery {
type S = ExampleWithRefSemantics;
fn parameter(&self) -> OperationParameter<Self::S> {
let mut param_builder = OperationParameterBuilder::new();
match self {
ExampleQuery::ValuesEqual => {
param_builder
.expect_explicit_input_node("a", NodeType::Object)
.unwrap();
param_builder
.expect_explicit_input_node("b", NodeType::Object)
.unwrap();
}
ExampleQuery::ValueEqualTo(_) => {
param_builder
.expect_explicit_input_node("a", NodeType::Object)
.unwrap();
}
ExampleQuery::CmpFstSnd(_) => {
param_builder
.expect_explicit_input_node("a", NodeType::Object)
.unwrap();
param_builder
.expect_explicit_input_node("b", NodeType::Object)
.unwrap();
}
}
param_builder.build().unwrap()
}
fn apply_abstract(&self, g: &mut GraphWithSubstitution<AbstractGraph<Self::S>>) {
}
fn query(&self, g: &mut GraphWithSubstitution<ConcreteGraph<Self::S>>) -> ConcreteQueryOutput {
match self {
ExampleQuery::ValuesEqual => {
let value1 = g.get_node_value(SubstMarker::from("a")).unwrap();
let value2 = g.get_node_value(SubstMarker::from("b")).unwrap();
ConcreteQueryOutput {
taken: value1 == value2,
}
}
ExampleQuery::ValueEqualTo(value) => {
let node_value = g.get_node_value(SubstMarker::from("a")).unwrap();
ConcreteQueryOutput {
taken: node_value == value,
}
}
ExampleQuery::CmpFstSnd(ordering) => {
let value1 = g.get_node_value(SubstMarker::from("a")).unwrap();
let value2 = g.get_node_value(SubstMarker::from("b")).unwrap();
let cmp_result = match (value1, value2) {
(NodeValue::Integer(a), NodeValue::Integer(b)) => a.cmp(&b),
_ => {
panic!("type unsoundness: expected integers for comparison");
}
};
ConcreteQueryOutput {
taken: &cmp_result == ordering.deref(),
}
}
}
}
}
impl Semantics for ExampleWithRefSemantics {
type NodeConcrete = NodeValue;
type NodeAbstract = NodeType;
type EdgeConcrete = String;
type EdgeAbstract = EdgeType;
type NodeMatcher = NodeMatcher;
type EdgeMatcher = EdgeMatcher;
type NodeJoin = NodeJoiner;
type EdgeJoin = EdgeJoiner;
type NodeConcreteToAbstract = NodeConcreteToAbstract;
type EdgeConcreteToAbstract = EdgeConcreteToAbstract;
type BuiltinOperation = ExampleOperation;
type BuiltinQuery = ExampleQuery;
fn top_node_abstract() -> Option<Self::NodeAbstract> {
Some(NodeType::Object)
}
}
#[cfg(feature = "serde")]
impl Serialize for MyOrdering {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let value = match self.0 {
std::cmp::Ordering::Less => -1,
std::cmp::Ordering::Equal => 0,
std::cmp::Ordering::Greater => 1,
};
value.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for MyOrdering {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = i32::deserialize(deserializer)?;
let ordering = match value {
-1 => std::cmp::Ordering::Less,
0 => std::cmp::Ordering::Equal,
1 => std::cmp::Ordering::Greater,
_ => return Err(serde::de::Error::custom("invalid ordering value")),
};
Ok(MyOrdering(ordering))
}
}