| chigraph master Systems programming language written for beginners in LLVM

NodeInstance.cpp
Go to the documentation of this file.
1
3 #include "chi/NodeInstance.hpp"
4 #include "chi/DataType.hpp"
5 #include "chi/FunctionValidator.hpp"
6 #include "chi/GraphFunction.hpp"
7 #include "chi/GraphModule.hpp"
8 #include "chi/NodeType.hpp"
9 #include "chi/Support/Result.hpp"
10
11 #include <cassert>
12
13 namespace chi {
14 NodeInstance::NodeInstance(GraphFunction* func, std::unique_ptr<NodeType> nodeType, float posX,
15 float posY, boost::uuids::uuid nodeID)
16 : mType{std::move(nodeType)},
17 mX{posX},
18 mY{posY},
19 mId{nodeID},
20 mContext{&mType->context()},
21 mFunction{func},
22 mGraphModule{&func->module()} {
23 assert(mType != nullptr && mFunction != nullptr);
24
25 mType->mNodeInstance = this;
26
27 inputDataConnections.resize(type().dataInputs().size(), {nullptr, ~0ull});
28 outputDataConnections.resize(type().dataOutputs().size(), {});
29
30 if (!type().pure()) {
31 inputExecConnections.resize(type().execInputs().size(), {});
32 outputExecConnections.resize(type().execOutputs().size(), {nullptr, ~0ull});
33 }
34 }
35
36 NodeInstance::NodeInstance(const NodeInstance& other, boost::uuids::uuid id)
37 : mType(other.type().clone()),
38 mX{other.x()},
39 mY{other.y()},
40 mId{id},
41 mContext{&other.context()},
42 mFunction{&other.function()} {
43 assert(mType != nullptr && mFunction != nullptr);
44
45 mType->mNodeInstance = this;
46
47 inputDataConnections.resize(type().dataInputs().size(), {nullptr, ~0ull});
48 outputDataConnections.resize(type().dataOutputs().size(), {});
49
50 inputExecConnections.resize(type().execInputs().size(), {});
51 outputExecConnections.resize(type().execOutputs().size(), {nullptr, ~0ull});
52 }
53
54 NodeInstance::~NodeInstance() = default;
55
56 void NodeInstance::setType(std::unique_ptr<NodeType> newType) {
57 module().updateLastEditTime();
58
59 // delete exec connections that are out of range
60 // start at one past the end
61 for (size_t id = newType->execInputs().size(); id < inputExecConnections.size(); ++id) {
62 while (!inputExecConnections[id].empty()) {
63 assert(inputExecConnections[id][0].first); // should never fail...
64 auto res = disconnectExec(*inputExecConnections[id][0].first,
65 inputExecConnections[id][0].second);
66
67 assert(res.mSuccess);
68 }
69 }
70 inputExecConnections.resize(newType->execInputs().size());
71
72 for (size_t id = newType->execOutputs().size(); id < outputExecConnections.size(); ++id) {
73 auto& conn = outputExecConnections[id];
74 if (conn.first != nullptr) { disconnectExec(*this, id); }
75 }
76 outputExecConnections.resize(newType->execOutputs().size(), std::make_pair(nullptr, ~0ull));
77
78 auto id = 0ull;
79 for (const auto& conn : inputDataConnections) {
80 // if there is no connection, then skip
81 if (conn.first == nullptr) {
82 ++id;
83 continue;
84 }
85
86 // if we get here, then we have to deal with the connection.
87
88 // see if we can keep it
89 if (newType->dataInputs().size() > id &&
90 type().dataInputs()[id].type == newType->dataInputs()[id].type) {
91 ++id;
92 continue;
93 }
94
95 // disconnect if there's a connection and we can't keep it
96 auto res = disconnectData(*conn.first, conn.second, *this);
97
98 assert(res.mSuccess);
99
100 ++id;
101 }
102 inputDataConnections.resize(newType->dataInputs().size(), std::make_pair(nullptr, ~0ull));
103
104 id = 0ull;
105 for (const auto& connSlot : outputDataConnections) {
106 // keep the connections if they're still good
107 if (newType->dataOutputs().size() > id &&
108 type().dataOutputs()[id].type == newType->dataOutputs()[id].type) {
109 ++id;
110 continue;
111 }
112
113 while (!connSlot.empty()) {
114 assert(connSlot[0].first);
115
116 auto res = disconnectData(*this, id, *connSlot[0].first);
117
118 assert(res.mSuccess);
119 }
120 ++id;
121 }
122 outputDataConnections.resize(newType->dataOutputs().size());
123
124 mType = std::move(newType);
125 mType->mNodeInstance = this;
126 }
127
128 Result connectData(NodeInstance& lhs, size_t lhsConnID, NodeInstance& rhs, size_t rhsConnID) {
129 Result res = {};
130
131 assert(&lhs.function() == &rhs.function());
132
133 rhs.module().updateLastEditTime();
134
135 // make sure the connection exists
136 // the input to the connection is the output to the node
137 if (lhsConnID >= lhs.outputDataConnections.size()) {
138 auto dataOutputs = nlohmann::json::array();
139 for (auto& output : lhs.type().dataOutputs()) {
140 dataOutputs.push_back({{output.name, output.type.qualifiedName()}});
141 }
142
143 res.addEntry("E22", "Output Data connection doesn't exist in node",
144 {{"Requested ID", lhsConnID},
145 {"Node Type", lhs.type().qualifiedName()},
146 {"Node JSON", rhs.type().toJSON()},
147 {"Node Output Data Connections", dataOutputs}});
148 }
149 if (rhsConnID >= rhs.inputDataConnections.size()) {
150 auto dataInputs = nlohmann::json::array();
151 for (auto& output : rhs.type().dataInputs()) {
152 dataInputs.push_back({{output.name, output.type.qualifiedName()}});
153 }
154
155 res.addEntry("E23", "Input Data connection doesn't exist in node",
156 {{"Requested ID", rhsConnID},
157 {"Node Type", rhs.type().qualifiedName()},
158 {"Node JSON", rhs.type().toJSON()},
159 {"Node Input Data Connections", dataInputs}});
160 }
161
162 // if there are errors, back out
163 if (!res) { return res; }
164 // make sure the connection is of the right type
165 if (lhs.type().dataOutputs()[lhsConnID].type != rhs.type().dataInputs()[rhsConnID].type) {
166 res.addEntry("E24", "Connecting data nodes with different types is invalid",
167 {{"Left Hand Type", lhs.type().dataOutputs()[lhsConnID].type.qualifiedName()},
168 {"Right Hand Type", rhs.type().dataInputs()[rhsConnID].type.qualifiedName()},
169 {"Left Node JSON", rhs.type().toJSON()},
170 {"Right Node JSON", rhs.type().toJSON()}});
171 return res;
172 }
173
174 // if we are replacing a connection, disconnect it
175 if (rhs.inputDataConnections[rhsConnID].first != nullptr) {
176 res += disconnectData(lhs, lhsConnID, rhs);
177 if (!res) { return res; }
178 }
179
180 lhs.outputDataConnections[lhsConnID].emplace_back(&rhs, rhsConnID);
181 rhs.inputDataConnections[rhsConnID] = {&lhs, lhsConnID};
182
183 return res;
184 }
185
186 Result connectExec(NodeInstance& lhs, size_t lhsConnID, NodeInstance& rhs, size_t rhsConnID) {
187 Result res = {};
188
189 assert(&lhs.function() == &rhs.function());
190
191 lhs.module().updateLastEditTime();
192
193 // make sure the connection exists
194 if (lhsConnID >= lhs.outputExecConnections.size()) {
195 auto execOutputs = nlohmann::json::array();
196 for (auto& output : lhs.type().execOutputs()) { execOutputs.push_back(output); }
197
198 res.addEntry("E22", "Output exec connection doesn't exist in node",
199 {{"Requested ID", lhsConnID},
200 {"Node Type", lhs.type().qualifiedName()},
201 {"Node Output Exec Connections", execOutputs}});
202 }
203 if (rhsConnID >= rhs.inputExecConnections.size()) {
204 auto execInputs = nlohmann::json::array();
205 for (auto& output : rhs.type().execInputs()) { execInputs.push_back(output); }
206
207 res.addEntry("E23", "Input exec connection doesn't exist in node",
208 {{"Requested ID", lhsConnID},
209 {"Node Type", rhs.type().qualifiedName()},
210 {"Node Input Exec Connections", execInputs}
211
212 });
213 }
214
215 if (!res) { return res; }
216 // if we are replacing a connection, disconnect it
217 if (lhs.outputExecConnections[lhsConnID].first != nullptr) {
218 res += disconnectExec(lhs, lhsConnID);
219 if (!res) { return res; }
220 }
221
222 // connect it!
223 lhs.outputExecConnections[lhsConnID] = {&rhs, rhsConnID};
224 rhs.inputExecConnections[rhsConnID].emplace_back(&lhs, lhsConnID);
225
226 return res;
227 }
228
229 Result disconnectData(NodeInstance& lhs, size_t lhsConnID, NodeInstance& rhs) {
230 assert(&lhs.function() == &rhs.function());
231
232 lhs.module().updateLastEditTime();
233
234 Result res = {};
235
236 if (lhsConnID >= lhs.outputDataConnections.size()) {
237 auto dataOutputs = nlohmann::json::array();
238 for (auto& output : lhs.type().dataOutputs()) {
239 dataOutputs.push_back({{output.name, output.type.qualifiedName()}});
240 }
241
242 res.addEntry("E22", "Output data connection in node doesn't exist",
243 {{"Requested ID", lhsConnID},
244 {"Node Type", lhs.type().qualifiedName()},
245 {"Node JSON", rhs.type().toJSON()},
246 {"Node Output Data Connections", dataOutputs}});
247
248 return res;
249 }
250
251 // find the connection
252 auto iter = std::find_if(lhs.outputDataConnections[lhsConnID].begin(),
253 lhs.outputDataConnections[lhsConnID].end(),
254 [&](auto& pair) { return pair.first == &rhs; });
255
256 if (iter == lhs.outputDataConnections[lhsConnID].end()) {
257 res.addEntry("EUKN", "Cannot disconnect from connection that doesn't exist",
258 {{"Left node ID", lhs.stringId()},
259 {"Right node ID", rhs.stringId()},
260 {"Left dock ID", lhsConnID}});
261
262 return res;
263 }
264
265 if (rhs.inputDataConnections.size() <= iter->second) {
266 auto dataInputs = nlohmann::json::array();
267 for (auto& output : rhs.type().dataInputs()) {
268 dataInputs.push_back({{output.name, output.type.qualifiedName()}});
269 }
270
271 res.addEntry("E23", "Input Data connection doesn't exist in node",
272 {{"Requested ID", iter->second},
273 {"Node Type", rhs.type().qualifiedName()},
274 {"Node JSON", rhs.type().toJSON()},
275 {"Node Input Data Connections", dataInputs}});
276
277 return res;
278 }
279
280 if (rhs.inputDataConnections[iter->second] != std::make_pair(&lhs, lhsConnID)) {
281 res.addEntry("EUKN", "Cannot disconnect from connection that doesn't exist",
282 {{"Left node ID", lhs.stringId()}, {"Right node ID", rhs.stringId()}});
283
284 return res;
285 }
286
287 // finally actually disconnect it
288 rhs.inputDataConnections[iter->second] = {nullptr, ~0ull};
289 lhs.outputDataConnections[lhsConnID].erase(iter);
290
291 return res;
292 }
293
294 Result disconnectExec(NodeInstance& lhs, size_t lhsConnID) {
295 Result res = {};
296
297 lhs.module().updateLastEditTime();
298
299 if (lhsConnID >= lhs.outputExecConnections.size()) {
300 auto execOutputs = nlohmann::json::array();
301 for (auto& output : lhs.type().execOutputs()) { execOutputs.push_back(output); }
302
303 res.addEntry("E22", "Output exec connection doesn't exist in node",
304 {{"Requested ID", lhsConnID},
305 {"Node Type", lhs.type().qualifiedName()},
306 {"Node Output Exec Connections", execOutputs}});
307 }
308
309 auto& lhsconn = lhs.outputExecConnections[lhsConnID];
310 auto& rhsconns = lhsconn.first->inputExecConnections[lhsconn.second];
311
312 auto iter = std::find_if(rhsconns.begin(), rhsconns.end(),
313 [&](auto pair) { return pair == std::make_pair(&lhs, lhsConnID); });
314
315 if (iter == rhsconns.end()) {
316 res.addEntry("EUKN", "Cannot disconnect an exec connection that doesn't connect back",
317 {{"Left node ID", lhs.stringId()}, {"Left node dock id", lhsConnID}});
318 return res;
319 }
320
321 rhsconns.erase(iter);
322 lhsconn = {nullptr, ~0ull};
323
324 return res;
325 }
326
327 } // namespace chi
chi::NodeType::execInputs
const std::vector< std::string > & execInputs() const
Get the execution inputs for the node.
Definition: NodeType.hpp:79
chi::NodeInstance::inputExecConnections
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > inputExecConnections
The connections that lead to this node, exec.
Definition: NodeInstance.hpp:74
FunctionValidator.hpp
Defines functions for validating GraphFunction objects.
chi::GraphFunction
this is an AST-like representation of a function in a graph It is used for IDE-like behavior...
Definition: GraphFunction.hpp:23
chi::Result::addEntry
void addEntry(const char *ec, const char *overview, nlohmann::json data)
Add a entry to the result, either a warning or an error.
Definition: Result.cpp:52
chi::NodeInstance::setType
void setType(std::unique_ptr< NodeType > newType)
Set the type of the node instance.
Definition: NodeInstance.cpp:56
chi::NodeInstance::stringId
std::string stringId() const
Get the ID as a string.
Definition: NodeInstance.hpp:68
Result.hpp
Defines the Result class and related functions.
chi::NodeInstance::outputDataConnections
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > outputDataConnections
The connections that lead out of this node, data.
Definition: NodeInstance.hpp:83
chi::NodeType::dataOutputs
const std::vector< NamedDataType > & dataOutputs() const
Get the data outputs for the node.
Definition: NodeType.hpp:76
chi::NodeInstance::type
NodeType & type()
Get the type of the instance.
Definition: NodeInstance.hpp:46
chi::NodeInstance
An instance of a node.
Definition: NodeInstance.hpp:19
chi::NodeInstance::inputDataConnections
std::vector< std::pair< NodeInstance *, size_t > > inputDataConnections
The connections that go into this node, data.
Definition: NodeInstance.hpp:77
chi::NodeInstance::module
GraphModule & module() const
Get the containing GraphModule.
Definition: NodeInstance.hpp:95
chi::NodeInstance::NodeInstance
NodeInstance(GraphFunction *func, std::unique_ptr< NodeType > nodeType, float posX, float posY, boost::uuids::uuid nodeID=boost::uuids::random_generator()())
Construct a nodeinstace with a type location and an ID, usually called from GraphFunction::insertNode...
Definition: NodeInstance.cpp:14
chi::NodeType::toJSON
virtual nlohmann::json toJSON() const
Create the JSON necessary to store the object.
Definition: NodeType.hpp:54
chi::NodeType::execOutputs
const std::vector< std::string > & execOutputs() const
Get the execution outputs for the node.
Definition: NodeType.hpp:82
chi::NodeInstance::~NodeInstance
~NodeInstance()
Destructor.
chi::disconnectData
Result disconnectData(NodeInstance &lhs, size_t lhsConnID, NodeInstance &rhs)
Disconnect a data connection.
Definition: NodeInstance.cpp:229
chi::connectData
Result connectData(NodeInstance &lhs, size_t lhsConnID, NodeInstance &rhs, size_t rhsConnID)
Connects two nodes&#39; data connections.
Definition: NodeInstance.cpp:128
chi::connectExec
Result connectExec(NodeInstance &lhs, size_t lhsConnID, NodeInstance &rhs, size_t rhsConnID)
Connects two nodes&#39; exec connections.
Definition: NodeInstance.cpp:186
chi::NodeInstance::function
GraphFunction & function() const
Get the containing GraphFunction.
Definition: NodeInstance.hpp:91
NodeType.hpp
Defines the NodeType class.
DataType.hpp
Defines the DataType class.
chi::ChiModule::updateLastEditTime
void updateLastEditTime(std::time_t newLastEditTime=std::time(nullptr))
Update the last edit time, signifying that it&#39;s been edited.
Definition: ChiModule.hpp:105
chi::NodeInstance::outputExecConnections
std::vector< std::pair< NodeInstance *, size_t > > outputExecConnections
The connections that go out of this node, exec.
Definition: NodeInstance.hpp:80
chi::NodeType::dataInputs
const std::vector< NamedDataType > & dataInputs() const
Get the data inputs for the node.
Definition: NodeType.hpp:73
chi::disconnectExec
Result disconnectExec(NodeInstance &lhs, size_t lhsConnID)
Disconnect a exec connection.
Definition: NodeInstance.cpp:294
GraphModule.hpp
Defines the GraphModule class.
chi::NodeInstance::x
float x() const
Get the X location of the instance.
Definition: NodeInstance.hpp:52
chi
The namespace where chigraph lives.
Definition: BitcodeParser.hpp:12
chi::NodeType::qualifiedName
std::string qualifiedName() const
Get the qualified name of the node type, like module.name():name()
Definition: NodeType.cpp:17
chi::NodeInstance::id
boost::uuids::uuid id() const
Get the ID of the instance, unique to the graph.
Definition: NodeInstance.hpp:64
chi::Result
The result object, used for identifiying errors with good diagnostics.
Definition: Result.hpp:72
GraphFunction.hpp
Declares the GraphFunction class.
NodeInstance.hpp
Defines the NodeInstance class and related functions.