11 #include <unordered_map> 39 for (
const auto& node : func.
nodes()) {
42 for (
const auto& conn : node.second->inputDataConnections) {
43 if (conn.first ==
nullptr) {
44 res.
addEntry(
"EUKN",
"Node is missing an input data connection",
45 {{
"Node ID", node.second->stringId()},
46 {
"nodetype", node.second->type().qualifiedName()},
47 {
"requested id",
id}});
53 bool connectsBack =
false;
54 for (
const auto& remoteConn : conn.first->outputDataConnections[conn.second]) {
55 if (remoteConn.first == node.second.get() && remoteConn.second == id) {
61 res.
addEntry(
"EUKN",
"Data connection doesn't connect back",
62 {{
"Left Node", conn.first->stringId()},
63 {
"Right Node", node.second->stringId()},
64 {
"Right input ID",
id}});
71 for (
const auto& outputDataSlot : node.second->outputDataConnections) {
73 for (
const auto& connection : outputDataSlot) {
74 assert(connection.first !=
nullptr);
76 if (connection.first->inputDataConnections.size() <= connection.second) {
77 res.
addEntry(
"EUKN",
"Input data port not found in node",
78 {{
"Node ID", connection.first->stringId()},
79 {
"requested id", connection.second}});
83 auto& remoteConn = connection.first->inputDataConnections[connection.second];
84 if (remoteConn.first != node.second.get() || remoteConn.second != id) {
85 res.
addEntry(
"EUKN",
"Data connection doesn't connect back",
86 {{
"Left Node", node.second->stringId()},
87 {
"Right Node", connection.first->stringId()},
88 {
"Right input ID", connection.second}});
96 for (
const auto& inputExecSlot : node.second->inputExecConnections) {
98 for (
const auto& connection : inputExecSlot) {
99 auto& remoteConn = connection.first->outputExecConnections[connection.second];
100 if (remoteConn.first != node.second.get() || remoteConn.second != id) {
101 res.
addEntry(
"EUKN",
"Exec connection doesn't connect back",
102 {{
"Left Node", connection.first->stringId()},
103 {
"Left Node Type", connection.first->type().qualifiedName()},
104 {
"Right Node", node.second->stringId()},
105 {
"Right Node Type", node.second->type().qualifiedName()},
106 {
"Left output ID", connection.second}});
114 for (
const auto& connection : node.second->outputExecConnections) {
115 bool connectsBack =
false;
117 if (connection.first ==
nullptr) {
121 for (
const auto& remoteConnection :
122 connection.first->inputExecConnections[connection.second]) {
123 if (remoteConnection.second ==
id && remoteConnection.first == node.second.get()) {
130 res.
addEntry(
"EUKN",
"Exec connection doesn't connect back",
131 {{
"Left Node", node.second->stringId()},
132 {
"Left Node Type", node.second->type().qualifiedName()},
133 {
"Right Node", connection.first->stringId()},
134 {
"Right Node Type", connection.first->type().qualifiedName()},
135 {
"Left output ID",
id}});
154 std::unordered_map<
const NodeInstance*, std::vector<int> > alreadyCalled) {
159 auto iter = alreadyCalled.find(&inst);
160 if (iter != alreadyCalled.end() &&
161 std::find(iter->second.begin(), iter->second.end(), inExecId) != iter->second.end()) {
169 if (conn.first ==
nullptr) {
170 res.
addEntry(
"EUKN",
"Node is missing an input data connection",
178 if (!conn.first->type().pure() && alreadyCalled.find(conn.first) == alreadyCalled.end()) {
179 res.
addEntry(
"EUKN",
"Node that accepts data from another node is called first",
180 {{
"Node ID", inst.
stringId()}, {
"othernodeid", conn.first->stringId()}});
186 alreadyCalled[&inst].push_back(inExecId);
190 if (conn.first ==
nullptr) {
continue; }
191 res += validatePath(*conn.first, conn.second, alreadyCalled);
207 if (entry ==
nullptr) {
return res; }
209 std::unordered_map<const NodeInstance*, std::vector<int>> alreadyCalled;
210 alreadyCalled.emplace(entry, std::vector<int>{});
214 for (
const auto& conn : entry->outputExecConnections) {
215 if (conn.first ==
nullptr) {
continue; }
216 res += validatePath(*conn.first, conn.second, alreadyCalled);
232 for (
const auto& nodepair : func.
nodes()) {
233 auto node = nodepair.second.get();
236 for (
const auto& conn : node->outputExecConnections) {
237 if (conn.second == ~0ull || conn.first ==
nullptr) {
238 res.
addEntry(
"EUKN",
"Node is missing an output exec connection",
239 {{
"Node ID", node->stringId()}, {
"Missing ID",
id}});
254 res.
addEntry(
"EUKN",
"Function must have a valid entry node to validate the entry type",
261 entry->type().dataOutputs().begin())) {
262 nlohmann::json inFunc = nlohmann::json::array();
264 inFunc.push_back({{in.name, in.type.qualifiedName()}});
267 nlohmann::json inEntry = nlohmann::json::array();
269 entry->type().dataOutputs()) {
270 inEntry.push_back({{in.name, in.type.qualifiedName()}});
273 res.
addEntry(
"EUKN",
"Inputs to function doesn't match function inputs",
274 {{
"Function Inputs", inFunc}, {
"Entry Inputs", inEntry}});
287 exitNode->type().dataInputs().begin())) {
288 nlohmann::json outFunc = nlohmann::json::array();
290 outFunc.push_back({{out.name, out.type.qualifiedName()}});
293 nlohmann::json outExit = nlohmann::json::array();
294 for (
auto& out : exitNode->type().dataOutputs()) {
296 outExit.push_back({{out.name, out.type.qualifiedName()}});
299 res.
addEntry(
"EUKN",
"Outputs to function doesn't match function exit",
300 {{
"Function Outputs", outFunc},
301 {
"Exit Outputs", outExit},
302 {
"Node ID", exitNode->stringId()}});
314 res.
addEntry(
"EUKN",
"A main function must have exactly one exec in",
318 res.
addEntry(
"EUKN",
"A main function must have exactly one exec out",
323 auto dataJson = nlohmann::json::array();
325 dataJson.push_back({{param.name, param.type.qualifiedName()}});
328 res.
addEntry(
"EUKN",
"A main function must have no data inputs",
329 {{
"Data Inputs", dataJson}});
332 func.
dataOutputs()[0].type.qualifiedName() !=
"lang:i32") {
333 auto dataJson = nlohmann::json::array();
335 dataJson.push_back({{param.name, param.type.qualifiedName()}});
338 res.
addEntry(
"EUKN",
"A main function must have exactly one data output that's a lang:i32",
339 {{
"Data Outputs", dataJson}});
const std::vector< NamedDataType > & dataOutputs() const
Get the function data outputs in the format {type, docstring}.
Defines functions for validating GraphFunction objects.
this is an AST-like representation of a function in a graph It is used for IDE-like behavior...
void addEntry(const char *ec, const char *overview, nlohmann::json data)
Add a entry to the result, either a warning or an error.
Result validateFunctionExecOutputs(const GraphFunction &func)
Make sure nodes have an output connection.
const std::vector< std::string > & execInputs() const
Get the function exec inputs.
std::string stringId() const
Get the ID as a string.
const std::vector< std::string > & execOutputs() const
Get the function exec outputs.
Result validateFunctionEntryType(const GraphFunction &func)
Make sure the function entry type aligns with the function type.
Defines the Result class and related functions.
Result validateFunctionConnectionsAreTwoWay(const GraphFunction &func)
Make sure that connections connect back and that they have the same types.
Result validateFunction(const GraphFunction &func)
Validate that a function is compilable.
NodeType & type()
Get the type of the instance.
std::vector< std::pair< NodeInstance *, size_t > > inputDataConnections
The connections that go into this node, data.
GraphModule & module() const
Get the GraphModule that contains this GraphFunction.
std::string name() const
Get the name of the function.
Result validateFunctionNodeInputs(const GraphFunction &func)
Make sure that nodes are called before their outputs are used.
std::string fullName() const
Get the full name of the module.
ScopedContext addScopedContext(const nlohmann::json &data)
Add a context with a scope Example usage: chi::Result res; res.contextJson(); // returns {} { aut...
NodeInstance * entryNode() const noexcept
Gets the node with type lang:entry returns nullptr on failure Also returns nullptr if there are two e...
std::unordered_map< boost::uuids::uuid, std::unique_ptr< NodeInstance > > & nodes()
Get the nodes in the function Usually called by connectData or connectExec or GraphFunction.
Defines the NodeType class.
Result validateFunctionMainSignature(const GraphFunction &func)
Make sure it's a valid signautre for a main function.
Result validateFunctionExitTypes(const GraphFunction &func)
Make sure the function exit types align with the function type.
Defines the DataType class.
std::vector< NodeInstance * > nodesWithType(const boost::filesystem::path &module, boost::string_view name) const noexcept
Gets the nodes with a given type.
std::vector< std::pair< NodeInstance *, size_t > > outputExecConnections
The connections that go out of this node, exec.
Defines the GraphModule class.
The namespace where chigraph lives.
std::string qualifiedName() const
Get the qualified name of the node type, like module.name():name()
const std::vector< NamedDataType > & dataInputs() const
Get the function data inputs in the format {type, docstring}.
std::string shortName() const
Get the short name of the module (the last bit)
The result object, used for identifiying errors with good diagnostics.
Declares the GraphFunction class.
Defines the NodeInstance class and related functions.