载入中...
搜索中...
未找到
SequenceAsset.cpp
浏览该文件的文档.
2
3#include <string>
4#include <unordered_set>
5#include <utility>
6
7namespace eve::dnut {
8
9namespace {
10
11} // namespace
12
13bool isCoreSequenceNodeType(const std::string& type) noexcept {
14 return type == "branch" || type == "choice" || type == "call" || type == "command" || type == "wait" ||
15 type == "end";
16}
17
19 switch (type) {
20 case SequenceParameterType::Any: return "any";
21 case SequenceParameterType::String: return "string";
22 case SequenceParameterType::Integer: return "integer";
23 case SequenceParameterType::Number: return "number";
24 case SequenceParameterType::Boolean: return "boolean";
25 }
26 return "unknown";
27}
28
29const SequenceNode* SequenceAsset::findNode(const std::string& nodeId) const noexcept {
30 for (const auto& node : nodes) {
31 if (node.id == nodeId) return &node;
32 }
33 return nullptr;
34}
35
37 if (id.empty())
39 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "sequence asset has no id", "id", {}, "dnut.sequence"));
40 if (entry.empty())
42 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "sequence asset has no entry node", "entry", {}, "dnut.sequence"));
43 if (version < 1)
45 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "sequence asset version must be positive", "version", {}, "dnut.sequence"));
46
47 std::unordered_set<std::string> parameterNames;
48 for (const auto& parameter : parameters) {
49 if (parameter.name.empty())
51 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "sequence parameter has an empty name", "parameters", {}, "dnut.sequence"));
52 if (!parameterNames.insert(parameter.name).second)
54 eve::Diagnostic::error(eve::DiagnosticCode::AlreadyExists, "duplicate sequence parameter '" + parameter.name + "'", "parameters." + parameter.name, {}, "dnut.sequence"));
55 if (parameter.required && !parameter.defaultValue.isNull())
57 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "required sequence parameter '" + parameter.name + "' cannot have a default", "parameters." + parameter.name, {}, "dnut.sequence"));
58 }
59
60 std::unordered_set<std::string> ids;
61 for (const auto& node : nodes) {
62 if (node.id.empty())
64 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "sequence node has an empty id", "nodes", {}, "dnut.sequence"));
65 if (!ids.insert(node.id).second)
67 eve::Diagnostic::error(eve::DiagnosticCode::AlreadyExists, "duplicate sequence node id '" + node.id + "'", "nodes." + node.id, {}, "dnut.sequence"));
68 if (node.type.empty())
70 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "sequence node '" + node.id + "' has no type", "nodes." + node.id + ".type", {}, "dnut.sequence"));
71 if (!node.payload.isObject())
73 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "sequence node '" + node.id + "' payload must be an object", "nodes." + node.id + ".payload", {}, "dnut.sequence"));
74 for (const auto& route : node.routes) {
75 if (!route.payload.isObject())
77 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "sequence route payload must be an object", "nodes." + node.id + ".routes", {}, "dnut.sequence"));
78 }
79 }
80
81 if (!findNode(entry))
83 eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "entry node '" + entry + "' does not exist", "entry", {}, "dnut.sequence"));
84
85 const auto checkReference = [&](const std::string& owner, const std::string& field,
86 const std::string& reference) -> eve::Result<void> {
89 eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "sequence node '" + owner + "' references missing node '" + reference + "'", "nodes." + owner + "." + field, {}, "dnut.sequence"));
90 };
91
92 for (const auto& node : nodes) {
93 auto nextResult = checkReference(node.id, "next", node.next);
94 if (!nextResult.ok()) return nextResult;
95
96 if (node.type == "call") {
97 const eve::Value* target = node.payload.find("target");
98 if (!target || !target->isString() || target->asString().empty())
100 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "call node '" + node.id + "' requires a target asset id", "nodes." + node.id + ".payload.target", {}, "dnut.sequence"));
101 const eve::Value* returnNode = node.payload.find("return");
102 if (returnNode) {
103 if (!returnNode->isString())
105 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "call node '" + node.id + "' return must be a node id", "nodes." + node.id + ".payload.return", {}, "dnut.sequence"));
106 auto returnResult = checkReference(node.id, "payload.return", returnNode->asString());
107 if (!returnResult.ok()) return returnResult;
108 }
109 }
110 if (node.type == "choice") {
111 if (node.routes.empty())
113 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "choice node '" + node.id + "' requires at least one route", "nodes." + node.id + ".routes", {}, "dnut.sequence"));
114 std::unordered_set<std::string> labels;
115 for (const auto& route : node.routes) {
116 if (route.label.empty())
118 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "choice node '" + node.id + "' has a route without a label", "nodes." + node.id + ".routes", {}, "dnut.sequence"));
119 if (!labels.insert(route.label).second)
121 eve::Diagnostic::error(eve::DiagnosticCode::AlreadyExists, "choice node '" + node.id + "' has duplicate route label '" + route.label + "'", "nodes." + node.id + ".routes", {}, "dnut.sequence"));
122 }
123 }
124 if (node.type == "branch") {
125 if (node.routes.empty())
127 eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "branch node '" + node.id + "' requires at least one route", "nodes." + node.id + ".routes", {}, "dnut.sequence"));
128 }
129 for (const auto& route : node.routes) {
130 auto routeResult = checkReference(node.id, "routes", route.target);
131 if (!routeResult.ok()) return routeResult;
132 }
133 }
135}
136
137} // namespace eve::dnut
LogicalId target
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
std::string nodeId
glm::uvec4 ids
std::vector< BvhNode > nodes
const RoadNode * node
Domain-neutral compiled sequence graph for `.dnut` sources.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
const std::string & asString() const
Return the string payload; the caller must have checked the kind.
Definition Value.cpp:87
bool isString() const noexcept
Return true when this value is a string.
Definition Value.h:95
Value * find(const std::string &key) noexcept
Return an object member, or nullptr when absent.
Definition Value.cpp:124
bool isCoreSequenceNodeType(const std::string &type) noexcept
Return whether type is a control-flow type interpreted by the runtime.
const char * sequenceParameterTypeName(SequenceParameterType type) noexcept
Return the stable lowercase spelling of a parameter type.
SequenceParameterType
Declared value kind for one sequence invocation parameter.
eve::Result< void > validate() const
Validate stable identifiers, the entry point and every graph reference.
const SequenceNode * findNode(const std::string &nodeId) const noexcept
Find one node by its stable identifier.
std::vector< SequenceNode > nodes
std::vector< SequenceParameter > parameters
One immutable step of a compiled sequence.