Cogs.Core
Expressions.cpp
1#include "Expressions.h"
2
3#include "Parsing.h"
4
5#include "Foundation/HashFunctions.h"
6#include "Foundation/StringViewFormat.h"
7
8#include "Foundation/Logging/Logger.h"
9
10#include <glm/glm.hpp>
11
12namespace
13{
14 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("Expressions");
15}
16
17Cogs::Core::ExpressionContext::ExpressionContext()
18{
19 clear();
20 reserve();
21}
22
23void Cogs::Core::ExpressionContext::reserve()
24{
25 values.reserve(kMaxVariables);
26 variables.reserve(kMaxVariables);
27}
28
29void Cogs::Core::ExpressionContext::inherit(ExpressionContext * other)
30{
31 assert(parent == nullptr);
32 parent = other;
33
34 assert(scope.parent == nullptr);
35 scope.parent = &other->scope;
36
37 scope.variables = variables.data();
38 scope.var_count = static_cast<int>(variables.size());
39}
40
41double Cogs::Core::ExpressionContext::eval(std::string_view expression, double defaultValue)
42{
43 double result = defaultValue;
44
45 auto expr = compile(expression, "");
46
47 if (expr && expr->expression) {
48 result = te_eval(expr->expression);
49 }
50
51 free(expr);
52
53 return result;
54}
55
56Cogs::Core::Expression * Cogs::Core::ExpressionContext::compile(std::string_view expression, std::string_view variable)
57{
58 size_t code = Cogs::hash(expression, Cogs::hash(variable));
59 ExpressionVariable* resultVariable = nullptr;
60
61 if (variable.size()) {
62 for (auto & v : values) {
63 if (variable == v.name) {
64 resultVariable = &v;
65 }
66 }
67 }
68 auto fIt = expressions.find(code);
69
70 if (fIt != expressions.end()) {
71 return &fIt->second;
72 }
73
74 int error = 0;
75 auto expr = te_compile(expression.data(), expression.size(), &scope, &error);
76
77 if (!expr) {
78 LOG_ERROR(logger, "Could not compile expression '%.*s' <error> '%.*s'.",
79 StringViewFormat(expression.substr(0, error)),
80 StringViewFormat(expression.substr(error)));
81 return nullptr;
82 }
83
84 expressions[code] = Expression{ std::string(expression), resultVariable, expr, code };
85
86 return &expressions[code];
87}
88
89void Cogs::Core::ExpressionContext::free(Expression * /*expression*/)
90{
91 // No freeing for now.
92}
93
94Cogs::Core::ExpressionVariable& Cogs::Core::ExpressionContext::add(std::string_view name, double value)
95{
96 // If value already exists, we just update and return.
97 for (auto & v : values) {
98 if (name == v.name) {
99 v.value = value;
100 return v;
101 }
102 }
103
104 values.emplace_back();
105 variables.emplace_back();
106
107 auto & v = values.back();
108
109 v.name = std::string(name);
110 v.value = value;
111
112 auto & var = variables.back();
113
114 var.address = &values.back().value;
115 var.name = v.name.c_str();
116
117 scope.variables = variables.data();
118 scope.var_count = static_cast<int>(variables.size());
119
120 return v;
121}
122
123double Cogs::Core::ExpressionContext::update(const Expression * expression, double defaultValue)
124{
125 if (!expression || !expression->expression) return defaultValue;
126
127 auto result = te_eval(expression->expression);
128
129 if (expression->resultVariable) {
130 expression->resultVariable->value = result;
131 }
132
133 return result;
134}
135
136float Cogs::Core::ExpressionContext::update(const Expression * expression, float defaultValue)
137{
138 return static_cast<float>(update(expression, static_cast<double>(defaultValue)));
139}
140
141int Cogs::Core::ExpressionContext::update(const Expression * expression, int defaultValue)
142{
143 return static_cast<int>(update(expression, static_cast<double>(defaultValue)));
144}
145
146size_t Cogs::Core::ExpressionContext::update(const Expression * expression, size_t defaultValue)
147{
148 double value = update(expression, static_cast<double>(defaultValue));
149
150 if (glm::fract(value) > std::numeric_limits<double>::epsilon()) {
151 LOG_TRACE(logger, "Truncating evaluated expression %f", value);
152 }
153
154 return static_cast<size_t>(value);
155}
156
157void Cogs::Core::ExpressionContext::link(ExpressionContext * source, std::string_view sourceName, std::string_view destName)
158{
159
160 while (source != nullptr) {
161 int numSourceVars = source->scope.var_count;
162 for (int i = 0; i < numSourceVars; ++i) {
163 if (sourceName == source->variables[i].name) {
164 auto& sourceVariable = source->variables[i];
165
166 values.emplace_back();
167 variables.emplace_back();
168
169 auto& v = values.back();
170
171 v.name = std::string(destName);
172 v.value = source->values[i].value;
173
174 auto& var = variables.back();
175
176 // Use the source variable value instead of the "var.address = &values.back().value;"
177 // to get updated values from the links
178 var.address = &source->values[i].value;
179 var.name = v.name.c_str();
180 var.link = &sourceVariable;
181
182 scope.variables = variables.data();
183 scope.var_count = static_cast<int>(variables.size());
184
185 return;
186 }
187 }
188 source = source->parent;
189 }
190
191 LOG_WARNING(logger, "Could not link variable. \"%s\" to \"%s\".", sourceName.data(), destName.data());
192}
193
194void Cogs::Core::ExpressionContext::clear()
195{
196 for (auto & e : expressions) {
197 te_free(e.second.expression);
198 }
199 expressions.clear();
200
201 parent = nullptr;
202 scope.parent = nullptr;
203 scope.variables = nullptr;
204 scope.var_count = 0;
205}
206
207std::span<const Cogs::Core::ExpressionVariable> Cogs::Core::ExpressionContext::getValues() const
208{
209 return std::span(values.data(), values.data() + values.size());
210}
211
212std::span<Cogs::Core::ExpressionVariable> Cogs::Core::ExpressionContext::getValues()
213{
214 return std::span(values.data(), values.data() + values.size());
215}
216
217void Cogs::Core::readSize_t(ExpressionValue<size_t> & value, ExpressionContext * ev, const std::string & prefix, const ParsedValue & parsedValue)
218{
219 if (parsedValue.type == ParsedDataType::Int) {
220 value = parsedValue.intValue;
221 } else if (parsedValue.type == ParsedDataType::Float) {
222 value = static_cast<size_t>(parsedValue.floatValue);
223 } else {
224 value = ExpressionValue<size_t>(ev, prefix + "." + parsedValue.key, parsedValue.value, 0);
225 }
226}
Log implementation class.
Definition: LogManager.h:140
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
Defines a single named expression variable.
Definition: Expressions.h:19
Defines a string expression, to be parsed and evaluated by an expression context.
Definition: Expressions.h:35