载入中...
搜索中...
未找到
Condition.cpp
浏览该文件的文档.
2
3#include <cmath>
4#include <initializer_list>
5#include <utility>
6
7namespace eve::decision {
8namespace {
9
10Value object(std::initializer_list<std::pair<std::string, Value>> fields) {
11 Value::Object result;
12 for (auto&& field : fields) result.emplace(field.first, field.second);
13 return Value(std::move(result));
14}
15
16bool number(const Value& value, double& result) {
17 if (const auto* integer = value.getIf<std::int64_t>()) {
18 result = static_cast<double>(*integer);
19 return true;
20 }
21 if (const auto* real = value.getIf<double>()) {
22 result = *real;
23 return std::isfinite(result);
24 }
25 return false;
26}
27
28bool compareValues(const Value& left, CompareOperator op, const Value& right) {
29 double leftNumber = 0.0;
30 double rightNumber = 0.0;
31 bool numeric = number(left, leftNumber) && number(right, rightNumber);
32 if (numeric) {
33 switch (op) {
34 case CompareOperator::Equal: return leftNumber == rightNumber;
35 case CompareOperator::NotEqual: return leftNumber != rightNumber;
36 case CompareOperator::Less: return leftNumber < rightNumber;
37 case CompareOperator::LessEqual: return leftNumber <= rightNumber;
38 case CompareOperator::Greater: return leftNumber > rightNumber;
39 case CompareOperator::GreaterEqual: return leftNumber >= rightNumber;
40 }
41 }
42 if (op == CompareOperator::Equal) return left == right;
43 if (op == CompareOperator::NotEqual) return !(left == right);
44 if (left.isString() && right.isString()) {
45 const auto& l = *left.getIf<std::string>();
46 const auto& r = *right.getIf<std::string>();
47 switch (op) {
48 case CompareOperator::Less: return l < r;
49 case CompareOperator::LessEqual: return l <= r;
50 case CompareOperator::Greater: return l > r;
51 case CompareOperator::GreaterEqual: return l >= r;
52 default: break;
53 }
54 }
55 return false;
56}
57
58Value childDetails(const std::vector<ConditionResult>& results) {
60 children.reserve(results.size());
61 for (const auto& result : results) {
62 children.emplace_back(object({
63 {"passed", Value(result.passed())},
64 {"reason", Value(conditionReasonCodeName(result.reasonCode()))},
65 {"evidence", result.evidence()},
66 {"details", result.details()},
67 }));
68 }
69 return Value(std::move(children));
70}
71
72ConditionResult unavailable(ConditionReasonCode reason, std::string_view kind, std::string_view key) {
73 return ConditionResult::failed(reason, {},
74 object({
75 {"kind", Value(std::string(kind))},
76 {"key", Value(std::string(key))},
77 }));
78}
79
80} // namespace
81
83 switch (code) {
84 case ConditionReasonCode::Passed: return "passed";
85 case ConditionReasonCode::ChildFailed: return "child_failed";
86 case ConditionReasonCode::NoChildPassed: return "no_child_passed";
87 case ConditionReasonCode::Negated: return "negated";
88 case ConditionReasonCode::MissingValue: return "missing_value";
89 case ConditionReasonCode::ValueMismatch: return "value_mismatch";
90 case ConditionReasonCode::TagMissing: return "tag_missing";
91 case ConditionReasonCode::TagUnavailable: return "tag_unavailable";
92 case ConditionReasonCode::AttributeMissing: return "attribute_missing";
93 case ConditionReasonCode::ResourceMissing: return "resource_missing";
94 case ConditionReasonCode::StateMissing: return "state_missing";
95 case ConditionReasonCode::StateMismatch: return "state_mismatch";
96 case ConditionReasonCode::AuthorityDenied: return "authority_denied";
97 case ConditionReasonCode::AuthorityUnavailable: return "authority_unavailable";
98 case ConditionReasonCode::PolicyRejected: return "policy_rejected";
99 case ConditionReasonCode::PolicyUnavailable: return "policy_unavailable";
100 case ConditionReasonCode::InvalidCondition: return "invalid_condition";
101 }
102 return "unknown";
103}
104
105const char* conditionKindName(ConditionKind kind) noexcept {
106 switch (kind) {
107 case ConditionKind::All: return "all";
108 case ConditionKind::Any: return "any";
109 case ConditionKind::Not: return "not";
110 case ConditionKind::Compare: return "compare";
111 case ConditionKind::HasTag: return "has_tag";
112 case ConditionKind::HasAttribute: return "has_attribute";
113 case ConditionKind::HasResource: return "has_resource";
114 case ConditionKind::StateEquals: return "state_equals";
115 case ConditionKind::AuthorityCheck: return "authority_check";
116 case ConditionKind::PolicyCall: return "policy_call";
117 }
118 return "unknown";
119}
120
121const char* compareOperatorName(CompareOperator op) noexcept {
122 switch (op) {
123 case CompareOperator::Equal: return "eq";
124 case CompareOperator::NotEqual: return "ne";
125 case CompareOperator::Less: return "lt";
126 case CompareOperator::LessEqual: return "le";
127 case CompareOperator::Greater: return "gt";
128 case CompareOperator::GreaterEqual: return "ge";
129 }
130 return "unknown";
131}
132
134 return ConditionResult(true, ConditionReasonCode::Passed, std::move(evidence), std::move(details));
135}
136
138 return ConditionResult(false, reason, std::move(evidence), std::move(details));
139}
140
141Condition Condition::all(std::vector<Condition> children) {
143 result.children_ = std::move(children);
144 return result;
145}
146
147Condition Condition::any(std::vector<Condition> children) {
149 result.children_ = std::move(children);
150 return result;
151}
152
155 result.children_.push_back(std::move(child));
156 return result;
157}
158
161 result.key_ = std::move(key);
162 result.compare_ = op;
163 result.expected_ = std::move(expected);
164 return result;
165}
166
169 result.key_ = std::move(tag);
170 return result;
171}
172
173Condition Condition::hasAttribute(std::string attribute) {
175 result.key_ = std::move(attribute);
176 return result;
177}
178
179Condition Condition::hasResource(std::string resource) {
181 result.key_ = std::move(resource);
182 return result;
183}
184
187 result.key_ = std::move(key);
188 result.expected_ = std::move(expected);
189 return result;
190}
191
194 result.key_ = std::move(scope);
195 return result;
196}
197
199 std::optional<ScriptConditionDeclaration> scriptDeclaration) {
201 result.key_ = std::move(name);
202 result.arguments_ = std::move(arguments);
203 result.scriptDeclaration_ = std::move(scriptDeclaration);
204 return result;
205}
206
207bool Condition::isValid() const noexcept {
208 switch (kind_) {
211 for (const auto& child : children_)
212 if (!child.isValid()) return false;
213 return true;
214 case ConditionKind::Not: return children_.size() == 1 && children_.front().isValid();
216 case ConditionKind::StateEquals: return !key_.empty();
220 case ConditionKind::AuthorityCheck: return !key_.empty();
222 return !key_.empty() &&
223 (!scriptDeclaration_ || (scriptDeclaration_->name.empty() || scriptDeclaration_->name == key_));
224 }
225 return false;
226}
227
230
231 switch (kind_) {
232 case ConditionKind::All: {
233 std::vector<ConditionResult> results;
234 results.reserve(children_.size());
235 for (const auto& child : children_) {
236 auto result = child.evaluate(context);
237 if (!result.passed()) {
238 const auto reason = result.reasonCode();
239 results.push_back(std::move(result));
242 results.back().evidence(),
243 object({{"kind", Value("all")}, {"children", childDetails(results)}}));
244 }
245 results.push_back(std::move(result));
246 }
247 return ConditionResult::success({}, object({{"kind", Value("all")}, {"children", childDetails(results)}}));
248 }
249 case ConditionKind::Any: {
250 std::vector<ConditionResult> results;
251 results.reserve(children_.size());
252 for (const auto& child : children_) {
253 auto result = child.evaluate(context);
254 if (result.passed())
256 result.evidence(), object({{"kind", Value("any")}, {"children", childDetails(results)}}));
257 results.push_back(std::move(result));
258 }
260 object({{"kind", Value("any")}, {"children", childDetails(results)}}));
261 }
262 case ConditionKind::Not: {
263 auto result = children_.front().evaluate(context);
264 if (result.passed())
266 object({{"kind", Value("not")}, {"child", result.details()}}));
267 return ConditionResult::success(result.evidence(),
268 object({{"kind", Value("not")}, {"child", result.details()}}));
269 }
271 auto actual = context.value(key_);
272 if (!actual) return unavailable(ConditionReasonCode::MissingValue, "compare", key_);
273 const bool equal = compareValues(*actual, compare_, expected_);
274 return equal ? ConditionResult::success(*actual, object({{"key", Value(key_)},
275 {"operator", Value(compareOperatorName(compare_))},
276 {"expected", expected_}}))
277 : ConditionResult::failed(ConditionReasonCode::ValueMismatch, *actual,
278 object({{"key", Value(key_)},
279 {"operator", Value(compareOperatorName(compare_))},
280 {"expected", expected_}}));
281 }
283 auto actual = context.hasTag(key_);
284 if (!actual) return unavailable(ConditionReasonCode::TagUnavailable, "has_tag", key_);
285 return *actual ? ConditionResult::success(Value(true), object({{"tag", Value(key_)}}))
286 : ConditionResult::failed(ConditionReasonCode::TagMissing, Value(false),
287 object({{"tag", Value(key_)}}));
288 }
290 auto actual = context.attribute(key_);
291 if (!actual) return unavailable(ConditionReasonCode::AttributeMissing, "has_attribute", key_);
292 return ConditionResult::success(*actual, object({{"attribute", Value(key_)}}));
293 }
295 auto actual = context.resource(key_);
296 if (!actual) return unavailable(ConditionReasonCode::ResourceMissing, "has_resource", key_);
297 return ConditionResult::success(*actual, object({{"resource", Value(key_)}}));
298 }
300 auto actual = context.state(key_);
301 if (!actual) return unavailable(ConditionReasonCode::StateMissing, "state_equals", key_);
302 return compareValues(*actual, CompareOperator::Equal, expected_)
303 ? ConditionResult::success(*actual, object({{"state", Value(key_)}, {"expected", expected_}}))
304 : ConditionResult::failed(ConditionReasonCode::StateMismatch, *actual,
305 object({{"state", Value(key_)}, {"expected", expected_}}));
306 }
308 auto allowed = context.authority(key_);
309 if (!allowed) return unavailable(ConditionReasonCode::AuthorityUnavailable, "authority_check", key_);
310 return *allowed ? ConditionResult::success(Value(true), object({{"scope", Value(key_)}}))
311 : ConditionResult::failed(ConditionReasonCode::AuthorityDenied, Value(false),
312 object({{"scope", Value(key_)}}));
313 }
315 auto result = context.policy(key_, arguments_);
316 if (!result) return unavailable(ConditionReasonCode::PolicyUnavailable, "policy_call", key_);
317 if (result->passed()) return std::move(*result);
318 return ConditionResult::failed(result->reasonCode(), result->evidence(),
319 object({{"policy", Value(key_)}, {"details", result->details()}}));
320 }
321 }
322 return ConditionResult::failed(ConditionReasonCode::InvalidCondition);
323}
324
325} // namespace eve::decision
double value
Pure, explainable condition trees shared by gameplay domains.
DiagnosticCode code
std::uint32_t key
double r
HexVec3 left
HexVec3 right
JobScope scope
TokenKind kind
std::string name
const std::string * tag
std::vector< std::string > fields
Definition PlayHost.cpp:111
double number
std::string resource
Json object
int children
Definition TreeMesh.cpp:295
const VegetationPresetContext & context
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
Explain one condition evaluation in a UI- and log-friendly form.
Definition Condition.h:129
static ConditionResult failed(ConditionReasonCode reason, Value evidence={}, Value details=Value::Object{})
Construct a rejected result with a stable reason and explanation.
static ConditionResult success(Value evidence={}, Value details=Value::Object{})
Construct a passed result with optional evidence and details.
const Value & evidence() const noexcept
Observed value or child evidence owned by this result.
Definition Condition.h:141
const Value & details() const noexcept
Structured details suitable for UI explanation.
Definition Condition.h:143
Immutable condition AST node.
Definition Condition.h:195
const std::optional< ScriptConditionDeclaration > & scriptDeclaration() const noexcept
Return an optional script dependency/determinism declaration.
Definition Condition.h:241
static Condition hasAttribute(std::string attribute)
Require an attribute to exist.
static Condition all(std::vector< Condition > children)
Construct a conjunction of child conditions.
const std::vector< Condition > & children() const noexcept
Return owned children; empty for a leaf node.
Definition Condition.h:231
static Condition not_(Condition child)
Construct a logical negation of one child condition.
ConditionResult evaluate(const EvaluationContext &context) const
Evaluate this tree against a read-only context.
const std::string & key() const noexcept
Return the key/tag/scope/name used by a leaf, if applicable.
Definition Condition.h:233
static Condition hasTag(std::string tag)
Require a tag to be present.
bool isValid() const noexcept
Return whether the factory invariants for this node hold.
static Condition stateEquals(std::string key, Value expected)
Require a named state value to equal the expected value.
static Condition policyCall(std::string name, Value arguments=Value::Object{}, std::optional< ScriptConditionDeclaration > scriptDeclaration=std::nullopt)
Call a named read-only policy and use its ConditionResult.
const Value & expected() const noexcept
Return the expected value owned by Compare/StateEquals.
Definition Condition.h:237
const Value & arguments() const noexcept
Return owned PolicyCall arguments.
Definition Condition.h:239
static Condition compare(std::string key, CompareOperator op, Value expected)
Compare a context value against an expected scalar value.
static Condition authorityCheck(std::string scope)
Require read-only authority for a scope.
static Condition any(std::vector< Condition > children)
Construct a disjunction of child conditions.
static Condition hasResource(std::string resource)
Require a resource to exist.
Read-only state boundary used by every condition evaluation.
Definition Condition.h:166
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
CompareOperator
Scalar comparison used by a Compare node.
Definition Condition.h:36
const char * conditionKindName(ConditionKind kind) noexcept
Return the stable lowercase spelling of a condition node kind. @ownership Borrowed process-static tex...
ConditionKind
Node kind in the side-effect-free condition AST.
Definition Condition.h:22
const char * conditionReasonCodeName(ConditionReasonCode code) noexcept
Return the stable lowercase spelling of a condition reason code. @ownership Borrowed process-static t...
Definition Condition.cpp:82
const char * compareOperatorName(CompareOperator op) noexcept
Return the stable lowercase spelling of a comparison operator. @ownership Borrowed process-static tex...
ConditionReasonCode
Stable machine-readable explanation code for a condition outcome.
Definition Condition.h:74
Result< T > failed(Status status, eve::DiagnosticCode code, RuleId rule, std::string message)
Construct a failed editing result with explicit status and diagnostic categories.