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
41float Cogs::Core::ExpressionContext::eval(std::string_view expression, float defaultValue)
42{
43 return static_cast<float>(eval(expression, static_cast<double>(defaultValue)));
44}
45
46double Cogs::Core::ExpressionContext::eval(std::string_view expression, double defaultValue)
47{
48 double result = defaultValue;
49
50 auto expr = compile(expression, "");
51
52 if (expr && expr->expression) {
53 result = static_cast<float>(te_eval(expr->expression));
54 }
55
56 free(expr);
57
58 return result;
59}
60
61int Cogs::Core::ExpressionContext::eval(std::string_view expression, int defaultValue)
62{
63 return static_cast<int>(eval(expression, static_cast<double>(defaultValue)));
64}
65
66Cogs::Core::Expression * Cogs::Core::ExpressionContext::compile(std::string_view expression, std::string_view variable)
67{
68 size_t code = Cogs::hash(expression, Cogs::hash(variable));
69 ExpressionVariable* resultVariable = nullptr;
70
71 if (variable.size()) {
72 for (auto & v : values) {
73 if (variable == v.name) {
74 resultVariable = &v;
75 }
76 }
77 }
78 auto fIt = expressions.find(code);
79
80 if (fIt != expressions.end()) {
81 return &fIt->second;
82 }
83
84 int error = 0;
85 auto expr = te_compile(expression.data(), expression.size(), &scope, &error);
86
87 if (!expr) {
88 LOG_ERROR(logger, "Could not compile expression '%.*s' <error> '%.*s'.",
89 StringViewFormat(expression.substr(0, error)),
90 StringViewFormat(expression.substr(error)));
91 return nullptr;
92 }
93
94 expressions[code] = Expression{ std::string(expression), resultVariable, expr, code };
95
96 return &expressions[code];
97}
98
99void Cogs::Core::ExpressionContext::free(Expression * /*expression*/)
100{
101 // No freeing for now.
102}
103
104Cogs::Core::ExpressionVariable& Cogs::Core::ExpressionContext::add(std::string_view name, double value)
105{
106 // If value already exists, we just update and return.
107 for (auto & v : values) {
108 if (name == v.name) {
109 v.value = value;
110 return v;
111 }
112 }
113
114 values.emplace_back();
115 variables.emplace_back();
116
117 auto & v = values.back();
118
119 v.name = std::string(name);
120 v.value = value;
121
122 auto & var = variables.back();
123
124 var.address = &values.back().value;
125 var.name = v.name.c_str();
126
127 scope.variables = variables.data();
128 scope.var_count = static_cast<int>(variables.size());
129
130 return v;
131}
132
133double Cogs::Core::ExpressionContext::update(const Expression * expression, double defaultValue)
134{
135 if (!expression || !expression->expression) return defaultValue;
136
137 auto result = te_eval(expression->expression);
138
139 if (expression->resultVariable) {
140 expression->resultVariable->value = result;
141 }
142
143 return result;
144}
145
146float Cogs::Core::ExpressionContext::update(const Expression * expression, float defaultValue)
147{
148 return static_cast<float>(update(expression, static_cast<double>(defaultValue)));
149}
150
151int Cogs::Core::ExpressionContext::update(const Expression * expression, int defaultValue)
152{
153 return static_cast<int>(update(expression, static_cast<double>(defaultValue)));
154}
155
156size_t Cogs::Core::ExpressionContext::update(const Expression * expression, size_t defaultValue)
157{
158 double value = update(expression, static_cast<double>(defaultValue));
159
160 if (glm::fract(value) > std::numeric_limits<double>::epsilon()) {
161 LOG_TRACE(logger, "Truncating evaluated expression %f", value);
162 }
163
164 return static_cast<size_t>(value);
165}
166
167void Cogs::Core::ExpressionContext::link(ExpressionContext * source, std::string_view sourceName, std::string_view destName)
168{
169
170 while (source != nullptr) {
171 int numSourceVars = source->scope.var_count;
172 for (int i = 0; i < numSourceVars; ++i) {
173 if (sourceName == source->variables[i].name) {
174 auto& sourceVariable = source->variables[i];
175
176 values.emplace_back();
177 variables.emplace_back();
178
179 auto& v = values.back();
180
181 v.name = std::string(destName);
182 v.value = source->values[i].value;
183
184 auto& var = variables.back();
185
186 var.address = &values.back().value;
187 var.name = v.name.c_str();
188 var.link = &sourceVariable;
189
190 scope.variables = variables.data();
191 scope.var_count = static_cast<int>(variables.size());
192
193 return;
194 }
195 }
196 source = source->parent;
197 }
198
199 LOG_WARNING(logger, "Could not link variable.");
200}
201
202void Cogs::Core::ExpressionContext::clear()
203{
204 for (auto & e : expressions) {
205 te_free(e.second.expression);
206 }
207 expressions.clear();
208
209 parent = nullptr;
210 scope.parent = nullptr;
211 scope.variables = nullptr;
212 scope.var_count = 0;
213}
214
215std::span<const Cogs::Core::ExpressionVariable> Cogs::Core::ExpressionContext::getValues() const
216{
217 return std::span(values.data(), values.data() + values.size());
218}
219
220std::span<Cogs::Core::ExpressionVariable> Cogs::Core::ExpressionContext::getValues()
221{
222 return std::span(values.data(), values.data() + values.size());
223}
224
225void Cogs::Core::readSize_t(ExpressionValue<size_t> & value, ExpressionContext * ev, const std::string & prefix, const ParsedValue & parsedValue)
226{
227 if (parsedValue.type == ParsedDataType::Int) {
228 value = parsedValue.intValue;
229 } else if (parsedValue.type == ParsedDataType::Float) {
230 value = static_cast<size_t>(parsedValue.floatValue);
231 } else {
232 value = { ev, prefix + "." + parsedValue.key, parsedValue.value };
233 }
234}
Log implementation class.
Definition: LogManager.h:139
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
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