use crate::operation::builtin::LibBuiltinOperation;
use crate::operation::marker::Marker;
use crate::operation::query::{BuiltinQuery, GraphShapeQuery, ShapeNodeIdentifier};
use crate::operation::signature::parameter::{
AbstractOperationOutput, AbstractOutputNodeMarker, GraphWithSubstitution, OperationParameter,
ParameterSubstitution,
};
use crate::operation::signature::parameterbuilder::OperationParameterBuilder;
use crate::operation::signature::{AbstractSignatureNodeId, OperationSignature};
use crate::operation::user_defined::{
AbstractNodeId, AbstractOperationArgument, AbstractOperationResultMarker,
AbstractUserDefinedOperationOutput, NamedMarker, OpLikeInstruction, QueryInstructions,
UserDefinedOperation,
};
use crate::operation::{
BuiltinOperation, Operation, OperationError, OperationResult, get_substitution,
};
use crate::prelude::*;
use crate::semantics::{AbstractGraph, AbstractMatcher};
use crate::util::bimap::BiMap;
use crate::util::log;
use crate::{NodeKey, Semantics, SubstMarker};
use error_stack::{FutureExt, Result, ResultExt, bail, report};
use petgraph::dot;
use petgraph::dot::Dot;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::iter::Peekable;
use std::marker::PhantomData;
use std::mem;
use std::slice::Iter;
use thiserror::Error;
mod programming_by_demonstration;
pub mod stack_based_builder;
enum AbstractOperation<'a, S: Semantics> {
Op(Operation<'a, S>),
Partial(&'a OperationSignature<S>),
}
impl<'a, S: Semantics> AbstractOperation<'a, S> {
fn parameter(&self) -> OperationParameter<S> {
match self {
AbstractOperation::Op(op) => op.parameter(),
AbstractOperation::Partial(sig) => sig.parameter.clone(),
}
}
fn apply_abstract(
&self,
op_ctx: &OperationContext<S>,
g: &mut GraphWithSubstitution<AbstractGraph<S>>,
) -> OperationResult<AbstractOperationOutput<S>> {
match self {
AbstractOperation::Op(op) => op.apply_abstract(op_ctx, g),
AbstractOperation::Partial(sig) => Ok(sig.output.apply_abstract(g)),
}
}
fn from_operation(op: Operation<'a, S>) -> AbstractOperation<'a, S> {
AbstractOperation::Op(op)
}
}
pub enum BuilderOpLike<S: Semantics> {
Builtin(S::BuiltinOperation),
LibBuiltin(LibBuiltinOperation<S>),
FromOperationId(OperationId),
Recurse,
}
impl<S: Semantics> BuilderOpLike<S> {
fn as_abstract_operation<'a>(
&'a self,
op_ctx: &'a OperationContext<S>,
partial_self_signature: &'a OperationSignature<S>,
) -> Result<AbstractOperation<'a, S>, OperationBuilderError> {
let op = match self {
BuilderOpLike::Builtin(op) => AbstractOperation::Op(Operation::Builtin(op)),
BuilderOpLike::LibBuiltin(op) => AbstractOperation::Op(Operation::LibBuiltin(op)),
BuilderOpLike::FromOperationId(id) => {
let op = op_ctx
.get(*id)
.ok_or_else(|| OperationBuilderError::NotFoundOperationId(*id))?;
AbstractOperation::Op(op)
}
BuilderOpLike::Recurse => AbstractOperation::Partial(partial_self_signature),
};
Ok(op)
}
fn to_op_like_instruction(self, self_op_id: OperationId) -> OpLikeInstruction<S> {
match self {
BuilderOpLike::Builtin(op) => OpLikeInstruction::Builtin(op),
BuilderOpLike::LibBuiltin(op) => OpLikeInstruction::LibBuiltin(op),
BuilderOpLike::FromOperationId(id) => OpLikeInstruction::Operation(id),
BuilderOpLike::Recurse => OpLikeInstruction::Operation(self_op_id),
}
}
}
impl<S: Semantics<BuiltinOperation: Clone, BuiltinQuery: Clone>> Clone for BuilderOpLike<S> {
fn clone(&self) -> Self {
match self {
BuilderOpLike::Builtin(op) => BuilderOpLike::Builtin(op.clone()),
BuilderOpLike::LibBuiltin(op) => BuilderOpLike::LibBuiltin(op.clone()),
BuilderOpLike::FromOperationId(id) => BuilderOpLike::FromOperationId(*id),
BuilderOpLike::Recurse => BuilderOpLike::Recurse,
}
}
}
#[derive(derive_more::Debug)]
pub enum BuilderInstruction<S: Semantics> {
#[debug("ExpectParameterNode({_0:?}, ???)")]
ExpectParameterNode(SubstMarker, S::NodeAbstract),
#[debug("ExpectContextNode({_0:?}, ???)")]
ExpectContextNode(SubstMarker, S::NodeAbstract),
#[debug("ExpectParameterEdge({_0:?}, {_1:?}, ???)")]
ExpectParameterEdge(SubstMarker, SubstMarker, S::EdgeAbstract),
#[debug("StartQuery(???, args: {_1:?})")]
StartQuery(S::BuiltinQuery, Vec<AbstractNodeId>),
#[debug("EnterTrueBranch")]
EnterTrueBranch,
#[debug("EnterFalseBranch")]
EnterFalseBranch,
#[debug("StartShapeQuery({_0:?})")]
StartShapeQuery(AbstractOperationResultMarker),
#[debug("EndQuery")]
EndQuery,
#[debug("ExpectShapeNode({_0:?}, ???)")]
ExpectShapeNode(AbstractOutputNodeMarker, S::NodeAbstract),
#[debug("ExpectShapeNodeChange({_0:?}, ???)")]
ExpectShapeNodeChange(AbstractNodeId, S::NodeAbstract),
#[debug("ExpectShapeEdge({_0:?}, {_1:?}, ???)")]
ExpectShapeEdge(AbstractNodeId, AbstractNodeId, S::EdgeAbstract),
#[debug("SkipMarker({_0:?})")]
SkipMarker(Marker),
#[debug("SkipAllMarkers")]
SkipAllMarkers,
#[debug("AddNamedOperation({_0:?}, ???, args: {_2:?})")]
AddNamedOperation(
AbstractOperationResultMarker,
BuilderOpLike<S>,
Vec<AbstractNodeId>,
),
#[debug("AddBangOperation({_0:?}, ???, args: {_2:?})")]
AddBangOperation(NamedMarker, BuilderOpLike<S>, Vec<AbstractNodeId>),
#[debug("AddOperation(???, args: {_1:?})")]
AddOperation(BuilderOpLike<S>, Vec<AbstractNodeId>),
#[debug("ReturnNode({_0:?}, {_1:?}, ???)")]
ReturnNode(AbstractNodeId, AbstractOutputNodeMarker, S::NodeAbstract),
#[debug("ReturnEdge({_0:?}, {_1:?}, ???)")]
ReturnEdge(AbstractNodeId, AbstractNodeId, S::EdgeAbstract),
#[debug("RenameNode({_0:?}, {_1:?})")]
RenameNode(AbstractNodeId, NamedMarker),
Finalize,
#[debug("SelfReturnNode({_0:?}, ???)")]
SelfReturnNode(AbstractOutputNodeMarker, S::NodeAbstract),
#[debug("Diverge({_0})")]
Diverge(String),
}
impl<S: Semantics> BuilderInstruction<S> {
fn can_break_body(&self) -> bool {
use BuilderInstruction::*;
match self {
EnterTrueBranch | EnterFalseBranch | EndQuery | ReturnNode(..) | ReturnEdge(..)
| Finalize => true,
_ => false,
}
}
}
impl<S: Semantics<BuiltinOperation: Clone, BuiltinQuery: Clone>> Clone for BuilderInstruction<S> {
fn clone(&self) -> Self {
use BuilderInstruction::*;
match self {
ExpectParameterNode(marker, node) => ExpectParameterNode(marker.clone(), node.clone()),
ExpectContextNode(marker, node) => ExpectContextNode(marker.clone(), node.clone()),
ExpectParameterEdge(source_marker, target_marker, edge) => {
ExpectParameterEdge(source_marker.clone(), target_marker.clone(), edge.clone())
}
StartQuery(query, args) => StartQuery(query.clone(), args.clone()),
EnterTrueBranch => EnterTrueBranch,
EnterFalseBranch => EnterFalseBranch,
StartShapeQuery(op_marker) => StartShapeQuery(op_marker.clone()),
EndQuery => EndQuery,
ExpectShapeNode(marker, node) => ExpectShapeNode(marker.clone(), node.clone()),
ExpectShapeNodeChange(aid, node) => ExpectShapeNodeChange(aid.clone(), node.clone()),
ExpectShapeEdge(source, target, edge) => {
ExpectShapeEdge(source.clone(), target.clone(), edge.clone())
}
SkipMarker(marker) => SkipMarker(marker.clone()),
SkipAllMarkers => SkipAllMarkers,
AddNamedOperation(name, op, args) => {
AddNamedOperation(name.clone(), op.clone(), args.clone())
}
AddBangOperation(name, op, args) => {
AddBangOperation(name.clone(), op.clone(), args.clone())
}
AddOperation(op, args) => AddOperation(op.clone(), args.clone()),
ReturnNode(aid, output_marker, node) => {
ReturnNode(aid.clone(), output_marker.clone(), node.clone())
}
ReturnEdge(src, dst, edge) => ReturnEdge(src.clone(), dst.clone(), edge.clone()),
RenameNode(old_aid, new_name) => RenameNode(old_aid.clone(), new_name.clone()),
Finalize => Finalize,
SelfReturnNode(marker, node) => SelfReturnNode(marker.clone(), node.clone()),
Diverge(msg) => Diverge(msg.clone()),
}
}
}
#[derive(Error, Debug, Clone)]
pub enum OperationBuilderError {
#[error("Expected a new unique subst marker, found repeat: {0:?}")]
ReusedSubstMarker(SubstMarker),
#[error("Expected an existing subst marker, but {0:?} was not found")]
NotFoundSubstMarker(SubstMarker),
#[error("Expected a new unique subst marker, found repeat: {0:?}")]
ReusedShapeIdent(ShapeNodeIdentifier),
#[error("Cannot call this while in a query context")]
InvalidInQuery,
#[error("Expected an operation or query")]
ExpectedOperationOrQuery,
#[error("Already visited the {0} branch of the active query")]
AlreadyVisitedBranch(bool),
#[error("Could not find abstract node id: {0:?}")]
NotFoundAid(AbstractNodeId),
#[error("AID {0:?} already exists")]
AlreadyExistsAid(AbstractNodeId),
#[error("Could not find operation ID: {0}")]
NotFoundOperationId(OperationId),
#[error("Could not apply operation due to mismatched arguments: {0}")]
SubstitutionError(#[from] crate::operation::SubstitutionError),
#[error("Could not apply operation due to mismatched arguments")]
SubstitutionErrorNew,
#[error("Could not abstractly apply operation {0} due to: {1}")]
AbstractApplyOperationErrorWithId(OperationId, OperationError),
#[error("Could not abstractly apply operation due to: {0}")]
AbstractApplyOperationError(OperationError),
#[error("Could not abstractly apply operation")]
AbstractApplyOperationError2,
#[error("Superfluous instruction {0}")]
SuperfluousInstruction(String),
#[error("Already selected to return node {0:?}")]
AlreadySelectedReturnNode(AbstractNodeId),
#[error("Already selected to return edge {0:?}->{1:?}")]
AlreadySelectedReturnEdge(AbstractNodeId, AbstractNodeId),
#[error("Could not find AID {0:?} for return node")]
NotFoundReturnNode(AbstractNodeId),
#[error("Invalid return node type for AID {0:?}, must be more generic")]
InvalidReturnNodeType(AbstractNodeId),
#[error("Returned {0:?} node may have been created by a shape query, which is not allowed")]
ReturnNodeMayOriginateFromShapeQuery(AbstractNodeId),
#[error("Cannot return a parameter node: {0:?}")]
CannotReturnParameter(AbstractNodeId),
#[error("Could not find AID {0:?} for return edge source")]
NotFoundReturnEdgeSource(AbstractNodeId),
#[error("Could not find AID {0:?} for return edge target")]
NotFoundReturnEdgeTarget(AbstractNodeId),
#[error("Could not statically determine edge {0:?}->{1:?} to be available")]
NotFoundReturnEdge(AbstractNodeId, AbstractNodeId),
#[error("Invalid return edge type for AID {0:?}->{1:?}, must be more generic")]
InvalidReturnEdgeType(AbstractNodeId, AbstractNodeId),
#[error(
"Return edge {0:?}->{1:?} may have been created by a shape query, which is not allowed"
)]
ReturnEdgeMayOriginateFromShapeQuery(AbstractNodeId, AbstractNodeId),
#[error("internal error: {0}")]
InternalError(&'static str),
#[error("Explicitly selected input AID not found")]
SelectedInputsNotFoundAid,
#[error("Shape edge target node not found")]
ShapeEdgeTargetNotFound,
#[error("Shape edge source node not found")]
ShapeEdgeSourceNotFound,
#[error(
"Cannot rename parameter node {0:?}, only new nodes from operation calls can be renamed"
)]
CannotRenameParameterNode(AbstractNodeId),
#[error("Invalid parameter")]
InvalidParameter,
#[error("New builder error")]
NewBuilderError,
}
pub type OperationBuilder<'a, S> = stack_based_builder::OperationBuilder2<'a, S>;
pub struct OperationBuilderInefficient<'a, S: Semantics> {
op_ctx: &'a OperationContext<S>,
self_op_id: OperationId,
instructions: Vec<BuilderInstruction<S>>,
previous_user_defined_operation: RefCell<UserDefinedOperation<S>>,
}
impl<'a, S: Semantics<BuiltinQuery: Clone, BuiltinOperation: Clone>>
OperationBuilderInefficient<'a, S>
{
pub fn new(op_ctx: &'a OperationContext<S>, self_op_id: OperationId) -> Self {
Self {
self_op_id,
instructions: Vec::new(),
op_ctx,
previous_user_defined_operation: RefCell::new(UserDefinedOperation::new_noop()),
}
}
pub fn undo_last_instruction(&mut self) {
if !self.instructions.is_empty() {
self.instructions.pop();
}
self.check_instructions()
.expect("internal error: a prefix slice of instructions should always be valid");
}
pub fn rename_node(
&mut self,
old_aid: AbstractNodeId,
new_name: impl Into<NamedMarker>,
) -> Result<(), OperationBuilderError> {
let new_name = new_name.into();
self.instructions
.push(BuilderInstruction::RenameNode(old_aid, new_name));
self.check_instructions_or_rollback()
}
pub fn expect_parameter_node(
&mut self,
marker: impl Into<SubstMarker>,
node: S::NodeAbstract,
) -> Result<(), OperationBuilderError> {
let marker = marker.into();
self.instructions
.push(BuilderInstruction::ExpectParameterNode(marker, node));
self.check_instructions_or_rollback()
}
pub fn expect_context_node(
&mut self,
marker: impl Into<SubstMarker>,
node: S::NodeAbstract,
) -> Result<(), OperationBuilderError> {
let marker = marker.into();
self.instructions
.push(BuilderInstruction::ExpectContextNode(marker, node));
self.check_instructions_or_rollback()
}
pub fn expect_parameter_edge(
&mut self,
source_marker: impl Into<SubstMarker>,
target_marker: impl Into<SubstMarker>,
edge: S::EdgeAbstract,
) -> Result<(), OperationBuilderError> {
let source_marker = source_marker.into();
let target_marker = target_marker.into();
self.instructions
.push(BuilderInstruction::ExpectParameterEdge(
source_marker,
target_marker,
edge,
));
self.check_instructions_or_rollback()
}
pub fn start_query(
&mut self,
query: S::BuiltinQuery,
args: Vec<AbstractNodeId>,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::StartQuery(query, args));
self.check_instructions_or_rollback()
}
pub fn enter_true_branch(&mut self) -> Result<(), OperationBuilderError> {
self.instructions.push(BuilderInstruction::EnterTrueBranch);
self.check_instructions_or_rollback()
}
pub fn enter_false_branch(&mut self) -> Result<(), OperationBuilderError> {
self.instructions.push(BuilderInstruction::EnterFalseBranch);
self.check_instructions_or_rollback()
}
pub fn start_shape_query(
&mut self,
op_marker: impl Into<AbstractOperationResultMarker>,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::StartShapeQuery(op_marker.into()));
self.check_instructions_or_rollback()
}
pub fn end_query(&mut self) -> Result<(), OperationBuilderError> {
self.instructions.push(BuilderInstruction::EndQuery);
self.check_instructions_or_rollback()
}
pub fn expect_shape_node(
&mut self,
marker: AbstractOutputNodeMarker,
node: S::NodeAbstract,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::ExpectShapeNode(marker, node));
self.check_instructions_or_rollback()
}
pub fn expect_shape_node_change(
&mut self,
aid: AbstractNodeId,
node: S::NodeAbstract,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::ExpectShapeNodeChange(aid, node));
self.check_instructions_or_rollback()
}
pub fn expect_shape_edge(
&mut self,
source: AbstractNodeId,
target: AbstractNodeId,
edge: S::EdgeAbstract,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::ExpectShapeEdge(source, target, edge));
self.check_instructions_or_rollback()
}
pub fn add_named_operation(
&mut self,
name: AbstractOperationResultMarker,
op: BuilderOpLike<S>,
args: Vec<AbstractNodeId>,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::AddNamedOperation(name, op, args));
self.check_instructions_or_rollback()
}
pub fn add_bang_operation(
&mut self,
name: impl Into<NamedMarker>,
op: BuilderOpLike<S>,
args: Vec<AbstractNodeId>,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::AddBangOperation(name.into(), op, args));
self.check_instructions_or_rollback()
}
pub fn add_operation(
&mut self,
op: BuilderOpLike<S>,
args: Vec<AbstractNodeId>,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::AddOperation(op, args));
self.check_instructions_or_rollback()?;
Ok(())
}
pub fn return_node(
&mut self,
aid: AbstractNodeId,
output_marker: AbstractOutputNodeMarker,
node: S::NodeAbstract,
) -> Result<(), OperationBuilderError> {
if let AbstractNodeId::ParameterMarker(..) = &aid {
bail!(OperationBuilderError::CannotReturnParameter(aid));
}
self.instructions
.push(BuilderInstruction::ReturnNode(aid, output_marker, node));
self.check_instructions_or_rollback()
}
pub fn return_edge(
&mut self,
src: AbstractNodeId,
dst: AbstractNodeId,
edge: S::EdgeAbstract,
) -> Result<(), OperationBuilderError> {
self.instructions
.push(BuilderInstruction::ReturnEdge(src, dst, edge));
self.check_instructions_or_rollback()
}
pub fn build(&self) -> Result<UserDefinedOperation<S>, OperationBuilderError> {
let builder_result = IntermediateStateBuilder::run(&self.instructions, self.op_ctx)?;
let param = builder_result.operation_parameter;
let instructions = builder_result.instructions;
let prev_user_ref = self.previous_user_defined_operation.borrow();
let mut interpreter = IntermediateInterpreter::new_for_self_op_id(
self.self_op_id,
param,
self.op_ctx,
&prev_user_ref,
);
let user_def_op = interpreter.create_user_defined_operation(
instructions,
builder_result.return_nodes,
builder_result.return_edges,
)?;
user_def_op
.signature
.parameter
.check_validity()
.change_context(OperationBuilderError::InvalidParameter)?;
Ok(user_def_op)
}
fn check_instructions_or_rollback(&mut self) -> Result<(), OperationBuilderError> {
match self.check_instructions() {
Ok(_) => Ok(()),
Err(e) => {
if !self.instructions.is_empty() {
self.instructions.pop();
}
Err(e)
}
}
}
fn check_instructions(&self) -> Result<(), OperationBuilderError> {
let builder_result = IntermediateStateBuilder::run(&self.instructions, self.op_ctx)?;
let partial_user_def_op = {
let prev_user_ref = self.previous_user_defined_operation.borrow();
let mut interpreter = IntermediateInterpreter::new_for_self_op_id(
0, builder_result.operation_parameter,
self.op_ctx,
&prev_user_ref,
);
interpreter.create_user_defined_operation(
builder_result.instructions,
builder_result.return_nodes,
builder_result.return_edges,
)?
};
*self.previous_user_defined_operation.borrow_mut() = partial_user_def_op;
Ok(())
}
}
impl<
'a,
S: Semantics<
NodeAbstract: Debug,
EdgeAbstract: Debug,
BuiltinOperation: Clone,
BuiltinQuery: Clone,
>,
> OperationBuilderInefficient<'a, S>
{
fn get_intermediate_state(
&self,
) -> Result<(IntermediateState<S>, Vec<IntermediateStatePath>), OperationBuilderError> {
let builder_result = IntermediateStateBuilder::run(&self.instructions, self.op_ctx)?;
let prev_user_ref = self.previous_user_defined_operation.borrow();
let mut interpreter = IntermediateInterpreter::new_for_self_op_id(
0, builder_result.operation_parameter,
self.op_ctx,
&prev_user_ref,
);
let (_, interp_instructions) =
interpreter.interpret_instructions(builder_result.instructions)?;
let path = builder_result.state_path;
let mut path_iter = path.iter().peekable().cloned();
let mut intermediate_state = get_state_for_path(
&interpreter.initial_state,
&interp_instructions,
&mut path_iter,
)
.expect("internal error: Failed to get intermediate state for path");
let query_path = get_query_path_for_path::<S>(&mut path.iter().peekable().cloned());
intermediate_state.query_path = query_path;
Ok((intermediate_state, path))
}
pub fn show_state(&self) -> Result<IntermediateState<S>, OperationBuilderError> {
Ok(self.get_intermediate_state()?.0)
}
pub fn format_state(&self) -> String {
let (state, path) = self.get_intermediate_state().unwrap();
let dot = state.graph.dot();
let mapping = state.node_keys_to_aid.into_inner().0;
let query_path = state.query_path;
format!("\nIntermediate State:\n{dot}\nmapping: {mapping:#?}\nTODO query path")
}
}
struct IntermediateStateBuilder<'a, S: Semantics> {
path: Vec<IntermediateStatePath>,
_phantom_data: PhantomData<&'a S>,
}
use super::user_defined::Instruction as UDInstruction;
#[derive(derive_more::Debug)]
enum IntermediateInstruction<S: Semantics> {
OpLike(IntermediateOpLike<S>),
GraphShapeQuery {
is_finished: bool,
marker: AbstractOperationResultMarker,
graph_instructions: Vec<GraphShapeQueryInstruction<S>>,
query_instructions: IntermediateQueryInstructions<S>,
},
#[debug("BuiltinQuery(???, {_1:#?}, {_2:#?})")]
BuiltinQuery(
S::BuiltinQuery,
Vec<AbstractNodeId>,
IntermediateQueryInstructions<S>,
),
RenameNode {
aid: AbstractNodeId,
new_name: NamedMarker,
},
}
#[derive(derive_more::Debug)]
enum IntermediateOpLike<S: Semantics> {
#[debug("Builtin(???, {_1:#?})")]
Builtin(S::BuiltinOperation, Vec<AbstractNodeId>),
LibBuiltin(LibBuiltinOperation<S>, Vec<AbstractNodeId>),
Operation(OperationId, Vec<AbstractNodeId>),
Recurse(Vec<AbstractNodeId>),
}
#[derive(derive_more::Debug)]
struct IntermediateQueryInstructions<S: Semantics> {
#[debug("[{}]", true_branch.iter().map(|(opt, inst)| format!("({opt:#?}, {:#?})", inst)).collect::<Vec<_>>().join(", "))]
true_branch: Vec<(
Option<AbstractOperationResultMarker>,
IntermediateInstruction<S>,
)>,
#[debug("[{}]", false_branch.iter().map(|(opt, inst)| format!("({opt:#?}, {:#?})", inst)).collect::<Vec<_>>().join(", "))]
false_branch: Vec<(
Option<AbstractOperationResultMarker>,
IntermediateInstruction<S>,
)>,
}
#[derive(derive_more::Debug)]
enum GraphShapeQueryInstruction<S: Semantics> {
#[debug("ExpectShapeNode({_0:#?})")]
ExpectShapeNode(AbstractOutputNodeMarker, S::NodeAbstract),
#[debug("ExpectShapeNodeChange({_0:#?})")]
ExpectShapeNodeChange(AbstractNodeId, S::NodeAbstract),
#[debug("ExpectShapeEdge({_0:#?}, {_1:#?})")]
ExpectShapeEdge(AbstractNodeId, AbstractNodeId, S::EdgeAbstract),
}
struct BuilderResult<S: Semantics> {
operation_parameter: OperationParameter<S>,
instructions: Vec<(
Option<AbstractOperationResultMarker>,
IntermediateInstruction<S>,
)>,
state_path: Vec<IntermediateStatePath>,
return_nodes: HashMap<AbstractNodeId, (AbstractOutputNodeMarker, S::NodeAbstract)>,
return_edges: HashMap<(AbstractNodeId, AbstractNodeId), S::EdgeAbstract>,
}
impl<'a, S: Semantics<BuiltinOperation: Clone, BuiltinQuery: Clone>>
IntermediateStateBuilder<'a, S>
{
fn run(
builder_instructions: &'a [BuilderInstruction<S>],
op_ctx: &'a OperationContext<S>,
) -> Result<BuilderResult<S>, OperationBuilderError> {
let mut iter = builder_instructions.iter().peekable();
let op_parameter = Self::build_operation_parameter(&mut iter)?;
if iter.peek().is_some() {
op_parameter
.check_validity()
.change_context(OperationBuilderError::InvalidParameter)?;
}
let mut builder = Self {
_phantom_data: PhantomData,
path: Vec::new(),
};
let instructions = builder.build_many_instructions(&mut iter)?;
let mut return_nodes = HashMap::new();
let mut return_edges = HashMap::new();
if !builder
.path
.iter()
.any(|i| matches!(i, IntermediateStatePath::StartQuery(..)))
{
(return_nodes, return_edges) = Self::collect_return_instructions(&mut iter)?;
}
if let Some(next_instruction) = iter.peek() {
bail!(OperationBuilderError::SuperfluousInstruction(format!(
"{next_instruction:?}"
)));
}
Ok(BuilderResult {
operation_parameter: op_parameter,
instructions,
state_path: builder.path,
return_nodes,
return_edges,
})
}
fn build_many_instructions(
&mut self,
iter: &mut Peekable<Iter<BuilderInstruction<S>>>,
) -> Result<
Vec<(
Option<AbstractOperationResultMarker>,
IntermediateInstruction<S>,
)>,
OperationBuilderError,
> {
let mut instructions = Vec::new();
while let Some(instr) = iter.peek() {
if instr.can_break_body() {
break;
}
instructions.push(self.build_instruction(iter)?);
}
Ok(instructions)
}
fn build_instruction(
&mut self,
iter: &mut Peekable<Iter<BuilderInstruction<S>>>,
) -> Result<
(
Option<AbstractOperationResultMarker>,
IntermediateInstruction<S>,
),
OperationBuilderError,
> {
let next_instruction = iter
.peek()
.expect("should only be called when there is an instruction");
match next_instruction {
BuilderInstruction::AddNamedOperation(_, oplike, args)
| BuilderInstruction::AddOperation(oplike, args) => {
let name =
if let BuilderInstruction::AddNamedOperation(name, _, _) = next_instruction {
Some(name.clone())
} else {
None
};
iter.next();
let oplike = match oplike {
BuilderOpLike::Builtin(builtin_op) => {
IntermediateOpLike::Builtin(builtin_op.clone(), args.clone())
}
BuilderOpLike::LibBuiltin(lib_builtin_op) => {
IntermediateOpLike::LibBuiltin(lib_builtin_op.clone(), args.clone())
}
BuilderOpLike::FromOperationId(op_id) => {
IntermediateOpLike::Operation(op_id.clone(), args.clone())
}
BuilderOpLike::Recurse => IntermediateOpLike::Recurse(args.clone()),
};
self.path.push(IntermediateStatePath::Advance);
Ok((name, IntermediateInstruction::OpLike(oplike)))
}
BuilderInstruction::StartQuery(query, args) => {
iter.next();
self.path.push(IntermediateStatePath::StartQuery(None));
let query_instructions = self.build_query_instruction(iter)?;
Ok((
None,
IntermediateInstruction::BuiltinQuery(
query.clone(),
args.clone(),
query_instructions,
),
))
}
BuilderInstruction::StartShapeQuery(op_marker) => {
iter.next();
self.path
.push(IntermediateStatePath::StartQuery(Some(format!(
"{op_marker:?}"
))));
let (is_finished, gsq_instructions, branch_instructions) =
self.build_shape_query(iter, op_marker.clone())?;
Ok((
Some(op_marker.clone()), IntermediateInstruction::GraphShapeQuery {
is_finished,
marker: op_marker.clone(),
graph_instructions: gsq_instructions,
query_instructions: branch_instructions,
},
))
}
BuilderInstruction::RenameNode(old_aid, new_name) => {
iter.next();
self.path.push(IntermediateStatePath::Advance);
Ok((
None,
IntermediateInstruction::RenameNode {
aid: *old_aid,
new_name: *new_name,
},
))
}
_ => bail!(OperationBuilderError::ExpectedOperationOrQuery),
}
}
fn build_shape_query(
&mut self,
iter: &mut Peekable<Iter<BuilderInstruction<S>>>,
operation_marker: AbstractOperationResultMarker,
) -> Result<
(
bool,
Vec<GraphShapeQueryInstruction<S>>,
IntermediateQueryInstructions<S>,
),
OperationBuilderError,
> {
let mut gsq_instructions = vec![];
let mut true_branch_instructions = None;
let mut false_branch_instructions = None;
let mut did_query_finish = false;
while let Some(instruction) = iter.peek() {
match instruction {
BuilderInstruction::EnterTrueBranch => {
iter.next();
if true_branch_instructions.is_some() {
bail!(OperationBuilderError::AlreadyVisitedBranch(true));
}
self.remove_until_branch(false);
self.path.push(IntermediateStatePath::EnterTrue);
true_branch_instructions = Some(self.build_many_instructions(iter)?);
did_query_finish = true;
}
BuilderInstruction::EnterFalseBranch => {
iter.next();
if false_branch_instructions.is_some() {
bail!(OperationBuilderError::AlreadyVisitedBranch(false));
}
self.remove_until_branch(true);
self.path.push(IntermediateStatePath::EnterFalse);
false_branch_instructions = Some(self.build_many_instructions(iter)?);
did_query_finish = true;
}
BuilderInstruction::EndQuery => {
iter.next();
self.remove_until_query_start();
self.path.push(IntermediateStatePath::SkipQuery);
did_query_finish = true;
break;
}
BuilderInstruction::ExpectShapeNode(marker, abstract_value) => {
iter.next();
gsq_instructions.push(GraphShapeQueryInstruction::ExpectShapeNode(
marker.clone(),
abstract_value.clone(),
));
}
BuilderInstruction::ExpectShapeNodeChange(aid, new_av) => {
iter.next();
gsq_instructions.push(GraphShapeQueryInstruction::ExpectShapeNodeChange(
aid.clone(),
new_av.clone(),
));
}
BuilderInstruction::ExpectShapeEdge(source, target, abstract_value) => {
iter.next();
gsq_instructions.push(GraphShapeQueryInstruction::ExpectShapeEdge(
source.clone(),
target.clone(),
abstract_value.clone(),
));
}
_ => {
bail!(OperationBuilderError::InvalidInQuery);
}
}
}
Ok((
did_query_finish,
gsq_instructions,
IntermediateQueryInstructions {
true_branch: true_branch_instructions.unwrap_or_default(),
false_branch: false_branch_instructions.unwrap_or_default(),
},
))
}
fn build_query_instruction(
&mut self,
iter: &mut Peekable<Iter<BuilderInstruction<S>>>,
) -> Result<IntermediateQueryInstructions<S>, OperationBuilderError> {
let mut true_branch_instructions = None;
let mut false_branch_instructions = None;
while let Some(instruction) = iter.peek() {
match instruction {
BuilderInstruction::EnterTrueBranch => {
iter.next();
if true_branch_instructions.is_some() {
bail!(OperationBuilderError::AlreadyVisitedBranch(true));
}
self.remove_until_branch(false);
self.path.push(IntermediateStatePath::EnterTrue);
true_branch_instructions = Some(self.build_many_instructions(iter)?);
}
BuilderInstruction::EnterFalseBranch => {
iter.next();
if false_branch_instructions.is_some() {
bail!(OperationBuilderError::AlreadyVisitedBranch(false));
}
self.remove_until_branch(true);
self.path.push(IntermediateStatePath::EnterFalse);
false_branch_instructions = Some(self.build_many_instructions(iter)?);
}
BuilderInstruction::EndQuery => {
iter.next();
self.remove_until_query_start();
self.path.push(IntermediateStatePath::SkipQuery);
break;
}
_ => {
bail!(OperationBuilderError::InvalidInQuery);
}
}
}
let true_branch = true_branch_instructions.unwrap_or_default();
let false_branch = false_branch_instructions.unwrap_or_default();
Ok(IntermediateQueryInstructions {
true_branch,
false_branch,
})
}
fn build_operation_parameter(
iter: &mut Peekable<Iter<BuilderInstruction<S>>>,
) -> Result<OperationParameter<S>, OperationBuilderError> {
let mut builder = OperationParameterBuilder::new();
while let Some(instruction) = iter.peek() {
match instruction {
BuilderInstruction::ExpectParameterNode(marker, node_abstract) => {
iter.next();
builder
.expect_explicit_input_node(*marker, node_abstract.clone())
.change_context(OperationBuilderError::InvalidParameter)?;
}
BuilderInstruction::ExpectContextNode(marker, node_abstract) => {
iter.next();
builder
.expect_context_node(*marker, node_abstract.clone())
.change_context(OperationBuilderError::InvalidParameter)?;
}
BuilderInstruction::ExpectParameterEdge(
source_marker,
target_marker,
edge_abstract,
) => {
iter.next();
builder
.expect_edge(*source_marker, *target_marker, edge_abstract.clone())
.change_context(OperationBuilderError::InvalidParameter)?;
}
_ => {
break;
}
}
}
builder
.build()
.change_context(OperationBuilderError::InvalidParameter)
}
fn collect_return_instructions(
iter: &mut Peekable<Iter<BuilderInstruction<S>>>,
) -> Result<
(
HashMap<AbstractNodeId, (AbstractOutputNodeMarker, S::NodeAbstract)>,
HashMap<(AbstractNodeId, AbstractNodeId), S::EdgeAbstract>,
),
OperationBuilderError,
> {
let mut return_nodes = HashMap::new();
let mut return_edges = HashMap::new();
while let Some(instruction) = iter.peek() {
match instruction {
BuilderInstruction::ReturnNode(aid, output_marker, node) => {
iter.next();
if return_nodes.contains_key(aid) {
bail!(OperationBuilderError::AlreadySelectedReturnNode(
aid.clone(),
));
}
return_nodes.insert(aid.clone(), (output_marker.clone(), node.clone()));
}
BuilderInstruction::ReturnEdge(source, target, edge) => {
iter.next();
if return_edges.contains_key(&(source.clone(), target.clone())) {
bail!(OperationBuilderError::AlreadySelectedReturnEdge(
source.clone(),
target.clone(),
));
}
return_edges.insert((source.clone(), target.clone()), edge.clone());
}
_ => break,
}
}
Ok((return_nodes, return_edges))
}
fn remove_until_branch(&mut self, branch: bool) {
let branch_to_find = if branch {
IntermediateStatePath::EnterTrue
} else {
IntermediateStatePath::EnterFalse
};
let mut found = false;
for last in self.path.iter().rev() {
if last == &branch_to_find {
found = true;
}
if matches!(last, IntermediateStatePath::StartQuery(..)) {
break;
}
}
if !found {
return;
}
while let Some(last) = self.path.pop() {
if (branch && last == IntermediateStatePath::EnterTrue)
|| (!branch && last == IntermediateStatePath::EnterFalse)
{
break;
}
}
}
fn remove_until_query_start(&mut self) {
while let Some(last) = self.path.pop() {
if matches!(last, IntermediateStatePath::StartQuery(..)) {
break;
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum IntermediateStatePath {
Advance,
EnterTrue,
EnterFalse,
SkipQuery,
StartQuery(Option<String>), }
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum QueryPath {
Query(String),
TrueBranch,
FalseBranch,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct IntermediateStateAbstractOutputResult {
new_aids: Vec<AbstractNodeId>,
removed_aids: Vec<AbstractNodeId>,
}
pub struct IntermediateState<S: Semantics> {
pub graph: AbstractGraph<S>,
pub node_keys_to_aid: BiMap<NodeKey, AbstractNodeId>,
pub node_may_originate_from_shape_query: HashSet<AbstractNodeId>,
pub edge_may_originate_from_shape_query: HashSet<(AbstractNodeId, AbstractNodeId)>,
pub node_may_be_written_to: HashMap<AbstractNodeId, S::NodeAbstract>,
pub edge_may_be_written_to: HashMap<(AbstractNodeId, AbstractNodeId), S::EdgeAbstract>,
pub query_path: Vec<QueryPath>,
pub op_marker_counter: u64,
pub has_diverged: bool,
}
impl<S: Semantics> Clone for IntermediateState<S> {
fn clone(&self) -> Self {
IntermediateState {
graph: self.graph.clone(),
node_keys_to_aid: self.node_keys_to_aid.clone(),
node_may_originate_from_shape_query: self.node_may_originate_from_shape_query.clone(),
edge_may_originate_from_shape_query: self.edge_may_originate_from_shape_query.clone(),
node_may_be_written_to: self.node_may_be_written_to.clone(),
edge_may_be_written_to: self.edge_may_be_written_to.clone(),
query_path: self.query_path.clone(),
op_marker_counter: self.op_marker_counter,
has_diverged: self.has_diverged,
}
}
}
impl<S: Semantics> IntermediateState<S> {
fn new() -> Self {
IntermediateState {
graph: AbstractGraph::<S>::new(),
node_keys_to_aid: BiMap::new(),
node_may_originate_from_shape_query: HashSet::new(),
edge_may_originate_from_shape_query: HashSet::new(),
node_may_be_written_to: HashMap::new(),
edge_may_be_written_to: HashMap::new(),
query_path: Vec::new(),
op_marker_counter: 50000,
has_diverged: false,
}
}
fn from_param(param: &OperationParameter<S>) -> Self {
let initial_graph = param.parameter_graph.clone();
let mut initial_mapping = BiMap::new();
for (key, subst) in param.node_keys_to_subst.iter() {
let aid = AbstractNodeId::ParameterMarker(*subst);
initial_mapping.insert(*key, aid);
}
IntermediateState {
graph: initial_graph,
node_keys_to_aid: initial_mapping,
node_may_originate_from_shape_query: HashSet::new(),
edge_may_originate_from_shape_query: HashSet::new(),
node_may_be_written_to: HashMap::new(),
edge_may_be_written_to: HashMap::new(),
query_path: Vec::new(),
op_marker_counter: 50000,
has_diverged: false,
}
}
fn get_next_op_result_marker(&mut self) -> AbstractOperationResultMarker {
let marker = AbstractOperationResultMarker::Implicit(self.op_marker_counter);
self.op_marker_counter += 1;
marker
}
fn add_node(
&mut self,
aid: AbstractNodeId,
node_abstract: S::NodeAbstract,
from_shape_query: bool,
) {
let node_key = self.graph.add_node(node_abstract);
self.node_keys_to_aid.insert(node_key, aid);
if from_shape_query {
self.node_may_originate_from_shape_query.insert(aid);
} else {
}
}
fn add_edge(
&mut self,
source: AbstractNodeId,
target: AbstractNodeId,
edge_abstract: S::EdgeAbstract,
from_shape_query: bool,
) -> Result<(), OperationBuilderError> {
let source_key = self
.node_keys_to_aid
.get_right(&source)
.ok_or(OperationBuilderError::NotFoundAid(source))?;
let target_key = self
.node_keys_to_aid
.get_right(&target)
.ok_or(OperationBuilderError::NotFoundAid(target))?;
self.graph.add_edge(*source_key, *target_key, edge_abstract);
if from_shape_query {
self.edge_may_originate_from_shape_query
.insert((source, target));
} else {
}
Ok(())
}
fn set_node_av(
&mut self,
aid: AbstractNodeId,
node_abstract: S::NodeAbstract,
) -> Result<(), OperationBuilderError> {
let node_key = self
.node_keys_to_aid
.get_right(&aid)
.ok_or(OperationBuilderError::NotFoundAid(aid))?;
self.graph.set_node_attr(*node_key, node_abstract);
Ok(())
}
fn contains_aid(&self, aid: &AbstractNodeId) -> bool {
self.node_keys_to_aid.contains_right(aid)
}
fn contains_edge(&self, source: &AbstractNodeId, target: &AbstractNodeId) -> bool {
let Some(source_key) = self.node_keys_to_aid.get_right(source) else {
return false;
};
let Some(target_key) = self.node_keys_to_aid.get_right(target) else {
return false;
};
self.graph
.get_edge_attr((*source_key, *target_key))
.is_some()
}
pub fn node_av_of_aid(&self, aid: &AbstractNodeId) -> Option<&S::NodeAbstract> {
let node_key = self.node_keys_to_aid.get_right(aid)?;
self.graph.get_node_attr(*node_key)
}
pub fn edge_av_of_aid(
&self,
source: &AbstractNodeId,
target: &AbstractNodeId,
) -> Option<&S::EdgeAbstract> {
let source_key = self.node_keys_to_aid.get_right(source)?;
let target_key = self.node_keys_to_aid.get_right(target)?;
self.graph.get_edge_attr((*source_key, *target_key))
}
fn rename_aid(
&mut self,
old_aid: AbstractNodeId,
new_aid: AbstractNodeId,
) -> Result<(), OperationBuilderError> {
if self.node_keys_to_aid.contains_right(&new_aid) {
bail!(OperationBuilderError::AlreadyExistsAid(new_aid));
}
if let Some(node_key) = self.node_keys_to_aid.remove_right(&old_aid) {
self.node_keys_to_aid.insert(node_key, new_aid);
} else {
bail!(OperationBuilderError::NotFoundAid(old_aid));
}
if self.node_may_originate_from_shape_query.remove(&old_aid) {
self.node_may_originate_from_shape_query.insert(new_aid);
}
self.edge_may_originate_from_shape_query = self
.edge_may_originate_from_shape_query
.iter()
.map(|&(src, dst)| {
let new_src = if src == old_aid { new_aid } else { src };
let new_dst = if dst == old_aid { new_aid } else { dst };
(new_src, new_dst)
})
.collect();
if let Some(node_av) = self.node_may_be_written_to.remove(&old_aid) {
self.node_may_be_written_to.insert(new_aid, node_av);
}
self.edge_may_be_written_to = self
.edge_may_be_written_to
.iter()
.map(|(&(src, dst), edge_av)| {
let new_src = if src == old_aid { new_aid } else { src };
let new_dst = if dst == old_aid { new_aid } else { dst };
((new_src, new_dst), edge_av.clone())
})
.collect();
Ok(())
}
fn diverge(&mut self) {
if !self.has_diverged {
self.has_diverged = true;
}
}
fn interpret_op(
&mut self,
op_ctx: &OperationContext<S>,
marker: Option<AbstractOperationResultMarker>,
op: AbstractOperation<S>,
args: Vec<AbstractNodeId>,
) -> Result<
(
AbstractOperationArgument,
IntermediateStateAbstractOutputResult,
),
OperationBuilderError,
> {
if self.has_diverged {
log::warn!(
"Trying to issue new instruction with name {marker:?} after path has diverged. This may lead to unexpected results regarding available node names."
);
}
let param = op.parameter();
let (subst, abstract_arg) = self.get_substitution(¶m, args)?;
let operation_output = {
let mut gws = GraphWithSubstitution::new(&mut self.graph, &subst);
op.apply_abstract(op_ctx, &mut gws)
.change_context(OperationBuilderError::AbstractApplyOperationError2)?
};
let output = self.handle_abstract_output_changes(marker, operation_output)?;
Ok((abstract_arg, output))
}
fn interpret_builtin_query(
&mut self,
query: &S::BuiltinQuery,
args: Vec<AbstractNodeId>,
) -> Result<AbstractOperationArgument, OperationBuilderError> {
let param = query.parameter();
let (subst, abstract_arg) = self.get_substitution(¶m, args)?;
let mut gws = GraphWithSubstitution::new(&mut self.graph, &subst);
query.apply_abstract(&mut gws);
Ok(abstract_arg)
}
fn handle_abstract_output_changes(
&mut self,
marker: Option<AbstractOperationResultMarker>,
operation_output: AbstractOperationOutput<S>,
) -> Result<IntermediateStateAbstractOutputResult, OperationBuilderError> {
let mut new_aids = Vec::new();
for (node_marker, node_key) in operation_output.new_nodes {
if let Some(op_marker) = marker {
let aid = AbstractNodeId::DynamicOutputMarker(op_marker, node_marker);
self.node_keys_to_aid.insert(node_key, aid);
new_aids.push(aid);
} else {
self.graph.remove_node(node_key);
}
}
let mut removed_aids = Vec::new();
for node_key in &operation_output.removed_nodes {
if let Some(removed_aid) = self.node_keys_to_aid.remove_left(&node_key) {
removed_aids.push(removed_aid);
}
}
for (key, node_abstract) in operation_output.changed_abstract_values_nodes {
let aid = self
.get_aid_from_key(&key)
.expect("internal error: changed node not found in mapping");
self.node_may_be_written_to.insert(aid, node_abstract);
}
for ((source, target), edge_abstract) in operation_output.changed_abstract_values_edges {
let source_aid = self
.get_aid_from_key(&source)
.expect("internal error: changed edge source not found in mapping");
let target_aid = self
.get_aid_from_key(&target)
.expect("internal error: changed edge target not found in mapping");
self.edge_may_be_written_to
.insert((source_aid, target_aid), edge_abstract);
}
Ok(IntermediateStateAbstractOutputResult {
new_aids,
removed_aids,
})
}
fn get_substitution(
&self,
param: &OperationParameter<S>,
args: Vec<AbstractNodeId>,
) -> Result<(ParameterSubstitution, AbstractOperationArgument), OperationBuilderError> {
let selected_inputs = args
.iter()
.map(|aid| self.get_key_from_aid(aid))
.collect::<Result<Vec<_>, _>>()
.change_context(OperationBuilderError::SelectedInputsNotFoundAid)?;
let subst = get_substitution(&self.graph, ¶m, &selected_inputs)
.change_context(OperationBuilderError::SubstitutionErrorNew)?;
let subst_to_aid = subst.mapping.iter().map(|(subst, key)| {
let aid = self.get_aid_from_key(key)
.change_context(OperationBuilderError::InternalError("node key should be in mapping, because all node keys from the abstract graph should be in the mapping"))
.unwrap();
(subst.clone(), aid)
}).collect();
let abstract_arg = AbstractOperationArgument {
selected_input_nodes: args,
subst_to_aid,
};
Ok((subst, abstract_arg))
}
fn get_key_from_aid(&self, aid: &AbstractNodeId) -> Result<NodeKey, OperationBuilderError> {
self.node_keys_to_aid
.get_right(aid)
.cloned()
.ok_or(report!(OperationBuilderError::NotFoundAid(*aid)))
}
fn get_aid_from_key(&self, key: &NodeKey) -> Result<AbstractNodeId, OperationBuilderError> {
self.node_keys_to_aid.get_left(key).cloned().ok_or(report!(
OperationBuilderError::InternalError("could not find node key")
))
}
fn as_param_for_shape_query(&self) -> (OperationParameter<S>, AbstractOperationArgument) {
let param_graph = self.graph.clone();
let mut all_node_keys = param_graph
.node_attr_map
.keys()
.cloned()
.collect::<Vec<_>>();
all_node_keys.sort_unstable();
let mut node_keys_to_subst: BiMap<NodeKey, SubstMarker> = BiMap::new();
let mut explicit_input_nodes = Vec::new();
let mut aid_args = Vec::new();
let mut subst_to_aid = HashMap::new();
for key in all_node_keys {
let subst = SubstMarker::from(format!("{:?}", key));
node_keys_to_subst.insert(key, subst);
explicit_input_nodes.push(subst);
let aid = self.get_aid_from_key(&key).unwrap();
aid_args.push(aid);
subst_to_aid.insert(subst, aid);
}
let abstract_args = AbstractOperationArgument {
selected_input_nodes: aid_args,
subst_to_aid,
};
(
OperationParameter {
explicit_input_nodes,
parameter_graph: param_graph,
node_keys_to_subst,
},
abstract_args,
)
}
}
impl<S: Semantics<NodeAbstract: Debug, EdgeAbstract: Debug>> IntermediateState<S> {
pub fn dot_with_aid(&self) -> String {
struct PrettyAid<'a>(&'a AbstractNodeId);
impl Debug for PrettyAid<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
AbstractNodeId::ParameterMarker(subst) => write!(f, "P({})", subst.0),
AbstractNodeId::DynamicOutputMarker(marker, node_marker) => {
let op_marker = match marker {
AbstractOperationResultMarker::Custom(c) => c,
AbstractOperationResultMarker::Implicit(num) => "<unnamed>",
};
write!(f, "O({}, {})", op_marker, node_marker.0)
}
AbstractNodeId::Named(name) => {
write!(f, "{:?}", name)
}
}
}
}
format!(
"{:?}",
Dot::with_attr_getters(
&self.graph.graph,
&[dot::Config::EdgeNoLabel, dot::Config::NodeNoLabel],
&|g, (src, target, attr)| {
let dbg_attr_format = format!("{:?}", attr.edge_attr);
let dbg_attr_replaced = dbg_attr_format.escape_debug();
format!("label = \"{dbg_attr_replaced}\"")
},
&|g, (node, _)| {
let aid = self
.node_keys_to_aid
.get_left(&node)
.expect("NodeKey not found in node_keys_to_aid");
let aid = PrettyAid(aid);
let aid = format!("{aid:?}");
let aid_replaced = aid.escape_debug();
let av = self
.graph
.get_node_attr(node)
.expect("NodeKey not found in graph");
let dbg_attr_format = format!("{:?}", av);
let dbg_attr_replaced = dbg_attr_format.escape_debug();
format!("shape=Mrecord, label = \"{aid_replaced}|{dbg_attr_replaced}\"")
}
)
)
}
}
enum InterpretedInstruction<S: Semantics> {
OpLike,
Query(InterpretedQueryInstructions<S>),
}
struct InterpretedQueryInstructions<S: Semantics> {
initial_state_true_branch: IntermediateState<S>,
initial_state_false_branch: IntermediateState<S>,
true_branch: InterpretedInstructions<S>,
false_branch: InterpretedInstructions<S>,
}
struct InterpretedInstructionWithState<S: Semantics> {
instruction: InterpretedInstruction<S>,
state_after: IntermediateState<S>,
}
struct IntermediateInterpreter<'a, S: Semantics> {
self_op_id: OperationId,
op_ctx: &'a OperationContext<S>,
op_param: OperationParameter<S>,
initial_state: IntermediateState<S>,
current_state: IntermediateState<S>,
partial_self_user_defined_op: &'a UserDefinedOperation<S>,
counter: u64,
}
type UDInstructionsWithMarker<S> = Vec<(Option<AbstractOperationResultMarker>, UDInstruction<S>)>;
type InterpretedInstructions<S> = Vec<(
Option<AbstractOperationResultMarker>,
InterpretedInstructionWithState<S>,
)>;
impl<'a, S: Semantics> IntermediateInterpreter<'a, S> {
fn new_for_self_op_id(
self_op_id: OperationId,
op_param: OperationParameter<S>,
op_ctx: &'a OperationContext<S>,
partial_self_user_defined_op: &'a UserDefinedOperation<S>,
) -> Self {
let initial_state = IntermediateState::from_param(&op_param);
let current_state = initial_state.clone();
let interpreter = IntermediateInterpreter {
self_op_id,
op_ctx,
op_param,
initial_state,
current_state,
partial_self_user_defined_op,
counter: 0,
};
interpreter
}
fn create_user_defined_operation(
&mut self,
intermediate_instructions: Vec<(
Option<AbstractOperationResultMarker>,
IntermediateInstruction<S>,
)>,
return_nodes: HashMap<AbstractNodeId, (AbstractOutputNodeMarker, S::NodeAbstract)>,
return_edges: HashMap<(AbstractNodeId, AbstractNodeId), S::EdgeAbstract>,
) -> Result<UserDefinedOperation<S>, OperationBuilderError> {
let (ud_instructions, _interp_instructions) =
self.interpret_instructions(intermediate_instructions)?;
let (ud_output, signature) = self.determine_signature(return_nodes, return_edges)?;
Ok(UserDefinedOperation {
instructions: ud_instructions,
output_changes: ud_output,
signature,
})
}
fn determine_signature(
&self,
return_nodes: HashMap<AbstractNodeId, (AbstractOutputNodeMarker, S::NodeAbstract)>,
return_edges: HashMap<(AbstractNodeId, AbstractNodeId), S::EdgeAbstract>,
) -> Result<(AbstractUserDefinedOperationOutput, OperationSignature<S>), OperationBuilderError>
{
let mut ud_output = AbstractUserDefinedOperationOutput::new();
let mut signature = OperationSignature::empty_new("name", self.op_param.clone());
for (aid, (output_marker, node_abstract)) in return_nodes {
let inferred_av = self
.current_state
.node_av_of_aid(&aid)
.ok_or(OperationBuilderError::NotFoundReturnNode(aid.clone()))?;
if !S::NodeMatcher::matches(inferred_av, &node_abstract) {
bail!(OperationBuilderError::InvalidReturnNodeType(aid));
}
if self
.current_state
.node_may_originate_from_shape_query
.contains(&aid)
{
bail!(OperationBuilderError::ReturnNodeMayOriginateFromShapeQuery(
aid,
));
}
ud_output.new_nodes.insert(aid, output_marker);
signature
.output
.new_nodes
.insert(output_marker, node_abstract);
}
let get_param_or_output_sig_id = |aid: &AbstractNodeId| {
match *aid {
AbstractNodeId::ParameterMarker(s) => Ok(AbstractSignatureNodeId::ExistingNode(s)),
AbstractNodeId::DynamicOutputMarker(_, _) | AbstractNodeId::Named(..) => {
let Some(output_marker) = ud_output.new_nodes.get(aid) else {
bail!(OperationBuilderError::NotFoundReturnNode(aid.clone()));
};
Ok(AbstractSignatureNodeId::NewNode(output_marker.clone()))
}
}
};
for ((source_aid, target_aid), edge_abstract) in return_edges {
let Some(source_key) = self.current_state.node_keys_to_aid.get_right(&source_aid)
else {
bail!(OperationBuilderError::NotFoundReturnEdgeSource(source_aid));
};
let Some(target_key) = self.current_state.node_keys_to_aid.get_right(&target_aid)
else {
bail!(OperationBuilderError::NotFoundReturnEdgeTarget(target_aid));
};
let inferred_edge_av = self
.current_state
.edge_av_of_aid(&source_aid, &target_aid)
.ok_or(OperationBuilderError::NotFoundReturnEdge(
source_aid.clone(),
target_aid.clone(),
))?;
if !S::EdgeMatcher::matches(inferred_edge_av, &edge_abstract) {
bail!(OperationBuilderError::InvalidReturnEdgeType(
source_aid, target_aid,
));
}
if self
.current_state
.edge_may_originate_from_shape_query
.contains(&(source_aid, target_aid))
{
bail!(OperationBuilderError::ReturnEdgeMayOriginateFromShapeQuery(
source_aid, target_aid,
));
}
let source_sig_id = get_param_or_output_sig_id(&source_aid)?;
let target_sig_id = get_param_or_output_sig_id(&target_aid)?;
signature
.output
.new_edges
.insert((source_sig_id, target_sig_id), edge_abstract.clone());
}
let initial_subst_nodes = self
.op_param
.node_keys_to_subst
.right_values()
.cloned()
.collect::<HashSet<_>>();
let current_subst_nodes = self
.current_state
.node_keys_to_aid
.right_values()
.filter_map(|aid| {
if let AbstractNodeId::ParameterMarker(subst) = aid {
Some(subst.clone())
} else {
None
}
})
.collect::<HashSet<_>>();
let deleted_nodes: HashSet<_> = initial_subst_nodes
.difference(¤t_subst_nodes)
.cloned()
.collect();
signature.output.maybe_deleted_nodes = deleted_nodes;
let mut initial_edges = HashSet::new();
for (source, target, _) in self.op_param.parameter_graph.graph.all_edges() {
let Some(source_subst) = self.op_param.node_keys_to_subst.get_left(&source) else {
continue; };
let Some(target_subst) = self.op_param.node_keys_to_subst.get_left(&target) else {
continue; };
initial_edges.insert((*source_subst, *target_subst));
}
let mut current_edges = HashSet::new();
for (source, target, _) in self.current_state.graph.graph.all_edges() {
let Some(source_aid) = self.current_state.node_keys_to_aid.get_left(&source) else {
continue; };
let Some(target_aid) = self.current_state.node_keys_to_aid.get_left(&target) else {
continue; };
if let (
AbstractNodeId::ParameterMarker(source_subst),
AbstractNodeId::ParameterMarker(target_subst),
) = (source_aid, target_aid)
{
current_edges.insert((source_subst.clone(), target_subst.clone()));
}
}
let deleted_edges: HashSet<_> = initial_edges.difference(¤t_edges).cloned().collect();
signature.output.maybe_deleted_edges = deleted_edges;
for (aid, node_abstract) in &self.current_state.node_may_be_written_to {
let AbstractNodeId::ParameterMarker(subst) = aid else {
continue;
};
signature
.output
.maybe_changed_nodes
.insert(*subst, node_abstract.clone());
}
for ((source_aid, target_aid), edge_abstract) in &self.current_state.edge_may_be_written_to
{
let AbstractNodeId::ParameterMarker(source_subst) = source_aid else {
continue;
};
let AbstractNodeId::ParameterMarker(target_subst) = target_aid else {
continue;
};
signature
.output
.maybe_changed_edges
.insert((*source_subst, *target_subst), edge_abstract.clone());
}
Ok((ud_output, signature))
}
fn interpret_instructions(
&mut self,
intermediate_instructions: Vec<(
Option<AbstractOperationResultMarker>,
IntermediateInstruction<S>,
)>,
) -> Result<(UDInstructionsWithMarker<S>, InterpretedInstructions<S>), OperationBuilderError>
{
let mut ud_instructions = Vec::new();
let mut interpreted_instructions = Vec::new();
for (marker, instruction) in intermediate_instructions {
let (ud_instruction, interpreted_instruction) =
self.interpret_single_instruction(marker.clone(), instruction)?;
ud_instructions.push((marker.clone(), ud_instruction));
interpreted_instructions.push((
marker,
InterpretedInstructionWithState {
instruction: interpreted_instruction,
state_after: self.current_state.clone(),
},
));
}
Ok((ud_instructions, interpreted_instructions))
}
fn interpret_single_instruction(
&mut self,
marker: Option<AbstractOperationResultMarker>,
instruction: IntermediateInstruction<S>,
) -> Result<(UDInstruction<S>, InterpretedInstruction<S>), OperationBuilderError> {
match instruction {
IntermediateInstruction::OpLike(oplike) => Ok((
self.interpret_op_like(marker, oplike)
.attach_printable_lazy(|| format!("Failed OpLike"))?,
InterpretedInstruction::OpLike,
)),
IntermediateInstruction::BuiltinQuery(query, args, query_instructions) => {
self.interpret_builtin_query(query, args, query_instructions)
}
IntermediateInstruction::GraphShapeQuery {
is_finished,
marker,
graph_instructions,
query_instructions,
} => self.interpret_graph_shape_query(
is_finished,
marker,
graph_instructions,
query_instructions,
),
IntermediateInstruction::RenameNode { aid, new_name } => {
let new_aid = AbstractNodeId::named(new_name);
Ok((
self.rename_node(aid, new_aid)?,
InterpretedInstruction::OpLike,
))
}
}
}
fn rename_node(
&mut self,
old_aid: AbstractNodeId,
new_aid: AbstractNodeId,
) -> Result<UDInstruction<S>, OperationBuilderError> {
if let AbstractNodeId::ParameterMarker(_) = old_aid {
bail!(OperationBuilderError::CannotRenameParameterNode(old_aid));
}
self.current_state.rename_aid(old_aid, new_aid)?;
Ok(UDInstruction::RenameNode {
old: old_aid,
new: new_aid,
})
}
fn interpret_op_like(
&mut self,
marker: Option<AbstractOperationResultMarker>,
oplike: IntermediateOpLike<S>,
) -> Result<UDInstruction<S>, OperationBuilderError> {
match oplike {
IntermediateOpLike::Builtin(builtin_op, args) => {
let op = Operation::Builtin(&builtin_op);
let abstract_arg =
self.interpret_op(marker, op, args)
.attach_printable_lazy(|| {
format!("Failed to interpret builtin operation: {builtin_op:?}")
})?;
Ok(UDInstruction::OpLike(
OpLikeInstruction::Builtin(builtin_op),
abstract_arg,
))
}
IntermediateOpLike::LibBuiltin(lib_builtin_op, args) => {
let op = Operation::LibBuiltin(&lib_builtin_op);
let abstract_arg =
self.interpret_op(marker, op, args)
.attach_printable_lazy(|| {
format!("Failed to interpret lib builtin operation: {lib_builtin_op:?}")
})?;
Ok(UDInstruction::OpLike(
OpLikeInstruction::LibBuiltin(lib_builtin_op),
abstract_arg,
))
}
IntermediateOpLike::Operation(id, args) => {
let op = self
.op_ctx
.get(id)
.ok_or(OperationBuilderError::NotFoundOperationId(id))?;
let abstract_arg = self
.interpret_op(marker, op, args)
.attach_printable_lazy(|| format!("Failed to interpret operation: {id:?}"))?;
Ok(UDInstruction::OpLike(
OpLikeInstruction::Operation(id),
abstract_arg,
))
}
IntermediateOpLike::Recurse(args) => {
let op = Operation::Custom(&self.partial_self_user_defined_op);
let abstract_arg = self
.interpret_op(marker, op, args)
.attach_printable_lazy(|| "Failed to interpret recursive call")?;
Ok(UDInstruction::OpLike(
OpLikeInstruction::Operation(self.self_op_id),
abstract_arg,
))
}
}
}
fn interpret_op(
&mut self,
marker: Option<AbstractOperationResultMarker>,
op: Operation<S>,
args: Vec<AbstractNodeId>,
) -> Result<AbstractOperationArgument, OperationBuilderError> {
self.current_state
.interpret_op(
&self.op_ctx,
marker,
AbstractOperation::from_operation(op),
args,
)
.map(|x| x.0)
}
fn interpret_builtin_query(
&mut self,
query: S::BuiltinQuery,
args: Vec<AbstractNodeId>,
query_instructions: IntermediateQueryInstructions<S>,
) -> Result<(UDInstruction<S>, InterpretedInstruction<S>), OperationBuilderError> {
let param = query.parameter();
let (subst, arg) = self.get_current_substitution(¶m, args)?;
query.apply_abstract(&mut GraphWithSubstitution::new(
&mut self.current_state.graph,
&subst,
));
let state_before = self.current_state.clone();
let false_branch_state = self.current_state.clone();
let initial_true_branch_state = self.current_state.clone();
let initial_false_branch_state = self.current_state.clone();
let (ud_true_branch, interp_true_branch) =
self.interpret_instructions(query_instructions.true_branch)?;
let after_true_branch_state = mem::replace(&mut self.current_state, false_branch_state);
let (ud_false_branch, interp_false_branch) =
self.interpret_instructions(query_instructions.false_branch)?;
let after_false_branch_state = mem::replace(&mut self.current_state, state_before);
let merged_state = merge_states(false, &after_true_branch_state, &after_false_branch_state);
self.current_state = merged_state;
let ud_instr = UDInstruction::BuiltinQuery(
query,
arg,
QueryInstructions {
taken: ud_true_branch,
not_taken: ud_false_branch,
},
);
let interp_instruction = InterpretedInstruction::Query(InterpretedQueryInstructions {
initial_state_true_branch: initial_true_branch_state,
initial_state_false_branch: initial_false_branch_state,
true_branch: interp_true_branch,
false_branch: interp_false_branch,
});
Ok((ud_instr, interp_instruction))
}
fn collect_self_state_to_parameter(
&self,
) -> (OperationParameter<S>, AbstractOperationArgument) {
self.current_state.as_param_for_shape_query()
}
fn interpret_graph_shape_query(
&mut self,
is_finished: bool,
gsq_op_marker: AbstractOperationResultMarker,
gsq_instructions: Vec<GraphShapeQueryInstruction<S>>,
query_instructions: IntermediateQueryInstructions<S>,
) -> Result<(UDInstruction<S>, InterpretedInstruction<S>), OperationBuilderError> {
let state_before = self.current_state.clone();
let false_branch_state = self.current_state.clone();
let initial_false_branch_state = false_branch_state.clone();
let (param, abstract_args) = self.collect_self_state_to_parameter();
let mut expected_graph = param.parameter_graph.clone();
let mut node_keys_to_shape_idents: BiMap<NodeKey, ShapeNodeIdentifier> = BiMap::new();
macro_rules! aid_to_node_key_hack {
($aid:expr) => {
arg_aid_to_node_keys
.get_left(&$aid)
.cloned()
.or_else(|| {
if let AbstractNodeId::DynamicOutputMarker(orm, node_marker) = $aid {
if orm == gsq_op_marker {
let sni: ShapeNodeIdentifier = node_marker.0.into();
node_keys_to_shape_idents.get_right(&sni).copied()
} else {
None
}
} else {
None
}
})
.ok_or(OperationBuilderError::NotFoundAid($aid))
};
}
for instruction in gsq_instructions {
match instruction {
GraphShapeQueryInstruction::ExpectShapeNode(marker, av) => {
let key = expected_graph.add_node(av.clone());
let shape_node_ident = marker.0.clone().into();
node_keys_to_shape_idents.insert(key, shape_node_ident);
let state_key = self.current_state.graph.add_node(av);
let aid =
AbstractNodeId::DynamicOutputMarker(gsq_op_marker.clone(), marker.clone());
self.current_state
.node_keys_to_aid
.insert(state_key, aid.clone());
self.current_state
.node_may_originate_from_shape_query
.insert(aid.clone());
}
GraphShapeQueryInstruction::ExpectShapeNodeChange(aid, av) => {
let key = self.get_current_key_from_aid(aid.clone())?;
expected_graph.set_node_attr(key, av.clone());
let state_key = self
.get_current_key_from_aid(aid)
.change_context(OperationBuilderError::NotFoundAid(aid))?;
self.current_state.graph.set_node_attr(state_key, av);
}
GraphShapeQueryInstruction::ExpectShapeEdge(src, target, av) => {
let src_key = self.get_current_key_from_aid(src.clone())?;
let target_key = self.get_current_key_from_aid(target.clone())?;
expected_graph.add_edge(src_key, target_key, av.clone());
let state_src_key = self
.get_current_key_from_aid(src)
.change_context(OperationBuilderError::ShapeEdgeSourceNotFound)?;
let state_target_key = self
.get_current_key_from_aid(target)
.change_context(OperationBuilderError::ShapeEdgeTargetNotFound)?;
self.current_state
.graph
.add_edge(state_src_key, state_target_key, av);
self.current_state
.edge_may_originate_from_shape_query
.insert((src.clone(), target.clone()));
}
}
}
let gsq = GraphShapeQuery::new(param, expected_graph, node_keys_to_shape_idents);
let initial_true_branch_state = self.current_state.clone();
let (ud_true_branch, interp_true_branch) =
self.interpret_instructions(query_instructions.true_branch)?;
let after_true_branch_state = mem::replace(&mut self.current_state, false_branch_state);
let (ud_false_branch, interp_false_branch) =
self.interpret_instructions(query_instructions.false_branch)?;
let after_false_branch_state = mem::replace(&mut self.current_state, state_before);
let merged_state = merge_states(true, &after_true_branch_state, &after_false_branch_state);
self.current_state = merged_state;
let ud_instruction = UDInstruction::ShapeQuery(
gsq,
abstract_args,
QueryInstructions {
taken: ud_true_branch,
not_taken: ud_false_branch,
},
);
let interp_instruction = InterpretedInstruction::Query(InterpretedQueryInstructions {
initial_state_true_branch: initial_true_branch_state,
initial_state_false_branch: initial_false_branch_state,
true_branch: interp_true_branch,
false_branch: interp_false_branch,
});
Ok((ud_instruction, interp_instruction))
}
fn interpret_graph_shape_query_old(
&mut self,
is_finished: bool,
gsq_op_marker: AbstractOperationResultMarker,
gsq_instructions: Vec<GraphShapeQueryInstruction<S>>,
query_instructions: IntermediateQueryInstructions<S>,
) -> Result<(UDInstruction<S>, InterpretedInstruction<S>), OperationBuilderError> {
let state_before = self.current_state.clone();
let false_branch_state = self.current_state.clone();
let initial_false_branch_state = false_branch_state.clone();
let mut param_builder = OperationParameterBuilder::new();
let mut abstract_args = Vec::new();
let mut arg_aid_to_param_subst: BiMap<AbstractNodeId, SubstMarker> = BiMap::new();
let mut arg_aid_to_node_keys: BiMap<AbstractNodeId, NodeKey> = BiMap::new();
let mut collect_aid = |aid: AbstractNodeId| -> Result<(), OperationBuilderError> {
if arg_aid_to_param_subst.contains_left(&aid) {
return Ok(());
}
let subst_marker = param_builder.next_subst_marker();
let key = self.get_current_key_from_aid(aid)?;
let abstract_value = self
.current_state
.graph
.get_node_attr(key)
.expect(
"internal error: node key should be in state graph since it is in the mapping",
)
.clone();
param_builder
.expect_explicit_input_node(subst_marker, abstract_value)
.change_context(OperationBuilderError::InternalError(
"node should not be in param",
))?;
abstract_args.push(aid.clone());
arg_aid_to_param_subst.insert(aid.clone(), subst_marker.clone());
arg_aid_to_node_keys.insert(aid.clone(), key);
Ok(())
};
let mut collect_non_shape_ident =
|&aid: &AbstractNodeId| -> Result<(), OperationBuilderError> {
match aid {
AbstractNodeId::ParameterMarker(_) => {
collect_aid(aid)?;
}
AbstractNodeId::DynamicOutputMarker(orm, node_marker) => {
if orm != gsq_op_marker {
collect_aid(aid)?;
}
}
AbstractNodeId::Named(..) => {
collect_aid(aid)?;
}
}
Ok(())
};
for instruction in &gsq_instructions {
match instruction {
GraphShapeQueryInstruction::ExpectShapeNode(_, _) => {
}
GraphShapeQueryInstruction::ExpectShapeEdge(src, target, _) => {
collect_non_shape_ident(src)?;
collect_non_shape_ident(target)?;
}
GraphShapeQueryInstruction::ExpectShapeNodeChange(aid, _) => {
collect_non_shape_ident(aid)?;
}
}
}
let param = param_builder
.build()
.change_context(OperationBuilderError::InternalError(
"Failed to build operation parameter for graph shape query",
))?;
let mut expected_graph = param.parameter_graph.clone();
let mut node_keys_to_shape_idents: BiMap<NodeKey, ShapeNodeIdentifier> = BiMap::new();
macro_rules! aid_to_node_key_hack {
($aid:expr) => {
arg_aid_to_node_keys
.get_left(&$aid)
.cloned()
.or_else(|| {
if let AbstractNodeId::DynamicOutputMarker(orm, node_marker) = $aid {
if orm == gsq_op_marker {
let sni: ShapeNodeIdentifier = node_marker.0.into();
node_keys_to_shape_idents.get_right(&sni).copied()
} else {
None
}
} else {
None
}
})
.ok_or(OperationBuilderError::NotFoundAid($aid))
};
}
for instruction in gsq_instructions {
match instruction {
GraphShapeQueryInstruction::ExpectShapeNode(marker, av) => {
let key = expected_graph.add_node(av.clone());
let shape_node_ident = marker.0.clone().into();
node_keys_to_shape_idents.insert(key, shape_node_ident);
let state_key = self.current_state.graph.add_node(av);
let aid =
AbstractNodeId::DynamicOutputMarker(gsq_op_marker.clone(), marker.clone());
self.current_state
.node_keys_to_aid
.insert(state_key, aid.clone());
self.current_state
.node_may_originate_from_shape_query
.insert(aid.clone());
}
GraphShapeQueryInstruction::ExpectShapeNodeChange(aid, av) => {
let key = aid_to_node_key_hack!(aid.clone())?;
expected_graph.set_node_attr(key, av.clone());
let state_key = self
.get_current_key_from_aid(aid)
.change_context(OperationBuilderError::NotFoundAid(aid))?;
self.current_state.graph.set_node_attr(state_key, av);
}
GraphShapeQueryInstruction::ExpectShapeEdge(src, target, av) => {
let src_key = aid_to_node_key_hack!(src.clone())?;
let target_key = aid_to_node_key_hack!(target.clone())?;
expected_graph.add_edge(src_key, target_key, av.clone());
let state_src_key = self
.get_current_key_from_aid(src)
.change_context(OperationBuilderError::ShapeEdgeSourceNotFound)?;
let state_target_key = self
.get_current_key_from_aid(target)
.change_context(OperationBuilderError::ShapeEdgeTargetNotFound)?;
self.current_state
.graph
.add_edge(state_src_key, state_target_key, av);
self.current_state
.edge_may_originate_from_shape_query
.insert((src.clone(), target.clone()));
}
}
}
let gsq = GraphShapeQuery::new(param, expected_graph, node_keys_to_shape_idents);
let initial_true_branch_state = self.current_state.clone();
let (ud_true_branch, interp_true_branch) =
self.interpret_instructions(query_instructions.true_branch)?;
let after_true_branch_state = mem::replace(&mut self.current_state, false_branch_state);
let (ud_false_branch, interp_false_branch) =
self.interpret_instructions(query_instructions.false_branch)?;
let after_false_branch_state = mem::replace(&mut self.current_state, state_before);
let merged_state = merge_states(true, &after_true_branch_state, &after_false_branch_state);
self.current_state = merged_state;
let ud_instruction = UDInstruction::ShapeQuery(
gsq,
AbstractOperationArgument {
selected_input_nodes: abstract_args,
subst_to_aid: arg_aid_to_param_subst.into_right_map(),
},
QueryInstructions {
taken: ud_true_branch,
not_taken: ud_false_branch,
},
);
let interp_instruction = InterpretedInstruction::Query(InterpretedQueryInstructions {
initial_state_true_branch: initial_true_branch_state,
initial_state_false_branch: initial_false_branch_state,
true_branch: interp_true_branch,
false_branch: interp_false_branch,
});
Ok((ud_instruction, interp_instruction))
}
fn get_current_substitution(
&self,
param: &OperationParameter<S>,
args: Vec<AbstractNodeId>,
) -> Result<(ParameterSubstitution, AbstractOperationArgument), OperationBuilderError> {
self.current_state.get_substitution(param, args)
}
fn get_new_unnamed_abstract_operation_marker(&mut self) -> AbstractOperationResultMarker {
let val = self.counter;
self.counter += 1;
AbstractOperationResultMarker::Implicit(val)
}
fn get_current_key_from_aid(
&self,
aid: AbstractNodeId,
) -> Result<NodeKey, OperationBuilderError> {
self.current_state.get_key_from_aid(&aid)
}
fn get_current_aid_from_key(
&self,
key: NodeKey,
) -> Result<AbstractNodeId, OperationBuilderError> {
self.current_state.get_aid_from_key(&key)
}
}
fn get_state_for_path<S: Semantics>(
initial_state: &IntermediateState<S>,
interpreted_instructions: &InterpretedInstructions<S>,
path: &mut impl Iterator<Item = IntermediateStatePath>,
) -> Option<IntermediateState<S>> {
let mut current_state = initial_state;
for (_, instruction) in interpreted_instructions {
match path.next() {
None => {
return Some(current_state.clone());
}
Some(path_element) => {
match path_element {
IntermediateStatePath::Advance | IntermediateStatePath::SkipQuery => {
current_state = &instruction.state_after;
}
IntermediateStatePath::EnterTrue | IntermediateStatePath::EnterFalse => {
panic!(
"internal error: unexpected path element: {:?}",
path_element
);
}
IntermediateStatePath::StartQuery(..) => {
if let InterpretedInstruction::Query(query_instructions) =
&instruction.instruction
{
current_state = &query_instructions.initial_state_true_branch;
match path.next() {
Some(IntermediateStatePath::EnterTrue) => {
return get_state_for_path(
¤t_state,
&query_instructions.true_branch,
path,
);
}
Some(IntermediateStatePath::EnterFalse) => {
current_state = &query_instructions.initial_state_false_branch;
return get_state_for_path(
¤t_state,
&query_instructions.false_branch,
path,
);
}
_ => {
return Some(current_state.clone());
}
}
} else {
return None;
}
}
}
}
}
}
Some(current_state.clone())
}
fn get_query_path_for_path<S: Semantics>(
path: &mut impl Iterator<Item = IntermediateStatePath>,
) -> Vec<QueryPath> {
let mut query_path = Vec::new();
for pe in path {
match pe {
IntermediateStatePath::EnterTrue => query_path.push(QueryPath::TrueBranch),
IntermediateStatePath::EnterFalse => query_path.push(QueryPath::FalseBranch),
IntermediateStatePath::StartQuery(name) => {
query_path.push(QueryPath::Query(
name.unwrap_or("<unnamed query>".to_string()),
));
}
_ => {}
}
}
query_path
}
fn merge_states<S: Semantics>(
is_true_shape: bool,
state_true: &IntermediateState<S>,
state_false: &IntermediateState<S>,
) -> IntermediateState<S> {
merge_states_result(is_true_shape, state_true, state_false).merged_state
}
struct MergeStatesResult<S: Semantics> {
merged_state: IntermediateState<S>,
missing_from_true: HashSet<AbstractNodeId>,
missing_from_false: HashSet<AbstractNodeId>,
}
fn merge_states_result<S: Semantics>(
is_true_shape: bool,
state_true: &IntermediateState<S>,
state_false: &IntermediateState<S>,
) -> MergeStatesResult<S> {
if state_true.has_diverged {
return MergeStatesResult {
merged_state: state_false.clone(),
missing_from_true: state_true
.node_keys_to_aid
.right_values()
.copied()
.collect(),
missing_from_false: HashSet::new(),
};
}
if state_false.has_diverged {
return MergeStatesResult {
merged_state: state_true.clone(),
missing_from_true: HashSet::new(),
missing_from_false: state_false
.node_keys_to_aid
.right_values()
.copied()
.collect(),
};
}
let mut new_state = IntermediateState::new();
let mut common_aids = HashSet::new();
for aid in state_true.node_keys_to_aid.right_values() {
if state_false.node_keys_to_aid.contains_right(aid) {
common_aids.insert(aid.clone());
}
}
for aid in common_aids {
let key_true = *state_true
.node_keys_to_aid
.get_right(&aid)
.expect("internal error: AID should be in mapping");
let key_false = *state_false
.node_keys_to_aid
.get_right(&aid)
.expect("internal error: AID should be in mapping");
let av_true = state_true
.graph
.get_node_attr(key_true)
.expect("internal error: Key should be in graph");
let av_false = state_false
.graph
.get_node_attr(key_false)
.expect("internal error: Key should be in graph");
let Some(merged_av) = S::join_nodes(av_true, av_false) else {
continue;
};
let new_key = new_state.graph.add_node(merged_av);
new_state.node_keys_to_aid.insert(new_key, aid.clone());
if state_true
.node_may_originate_from_shape_query
.contains(&aid)
|| state_false
.node_may_originate_from_shape_query
.contains(&aid)
{
new_state.node_may_originate_from_shape_query.insert(aid);
}
let written_av_true = state_true.node_may_be_written_to.get(&aid).cloned();
let written_av_false = state_false.node_may_be_written_to.get(&aid).cloned();
let merged_written_av = match (written_av_true, written_av_false) {
(Some(av_true), Some(av_false)) => {
Some(S::join_nodes(&av_true, &av_false).expect(
"client semantics error: expected to be able to merge written node attributes",
))
}
(Some(av_true), None) => Some(av_true),
(None, Some(av_false)) => Some(av_false),
(None, None) => None,
};
if let Some(merged_av) = merged_written_av {
new_state.node_may_be_written_to.insert(aid, merged_av);
}
}
for (from_key_true, to_key_true, attr) in state_true.graph.graph.all_edges() {
let from_aid = state_true
.node_keys_to_aid
.get_left(&from_key_true)
.expect("internal error: from key should be in mapping");
let to_aid = state_true
.node_keys_to_aid
.get_left(&to_key_true)
.expect("internal error: to key should be in mapping");
let Some(from_key_merged) = new_state.node_keys_to_aid.get_right(from_aid) else {
continue;
};
let Some(to_key_merged) = new_state.node_keys_to_aid.get_right(to_aid) else {
continue;
};
let av_true = state_true
.graph
.get_edge_attr((from_key_true, to_key_true))
.expect("internal error: edge should be in graph");
let Some(from_key_false) = state_false.node_keys_to_aid.get_right(from_aid) else {
continue;
};
let Some(to_key_false) = state_false.node_keys_to_aid.get_right(to_aid) else {
continue;
};
let Some(av_false) = state_false
.graph
.get_edge_attr((*from_key_false, *to_key_false))
else {
continue;
};
let Some(merged_av) = S::join_edges(av_true, av_false) else {
continue;
};
new_state
.graph
.add_edge(*from_key_merged, *to_key_merged, merged_av);
let edge = (from_aid.clone(), to_aid.clone());
if state_true
.edge_may_originate_from_shape_query
.contains(&edge)
|| state_false
.edge_may_originate_from_shape_query
.contains(&edge)
{
new_state.edge_may_originate_from_shape_query.insert(edge);
}
let written_av_true = state_true
.edge_may_be_written_to
.get(&(from_aid.clone(), to_aid.clone()))
.cloned();
let written_av_false = state_false
.edge_may_be_written_to
.get(&(from_aid.clone(), to_aid.clone()))
.cloned();
let merged_written_av = match (written_av_true, written_av_false) {
(Some(av_true), Some(av_false)) => {
Some(S::join_edges(&av_true, &av_false).expect(
"client semantics error: expected to be able to merge written edge attributes",
))
}
(Some(av_true), None) => Some(av_true),
(None, Some(av_false)) => Some(av_false),
(None, None) => None,
};
if let Some(merged_av) = merged_written_av {
new_state
.edge_may_be_written_to
.insert((from_aid.clone(), to_aid.clone()), merged_av);
}
}
let final_merged_state_aids = new_state
.node_keys_to_aid
.right_values()
.cloned()
.collect::<HashSet<_>>();
MergeStatesResult {
merged_state: new_state,
missing_from_true: state_true
.node_keys_to_aid
.right_values()
.cloned()
.filter(|aid| !final_merged_state_aids.contains(aid))
.collect(),
missing_from_false: state_false
.node_keys_to_aid
.right_values()
.cloned()
.filter(|aid| !final_merged_state_aids.contains(aid))
.collect(),
}
}