HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
formula_evaluation_context.hpp
1// Copyright Take Vos 2020-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../utility/module.hpp"
8#include "../datum.hpp"
9#include <unordered_map>
10#include <vector>
11#include <string_view>
12
13namespace hi::inline v1 {
14
18
19 ssize_t output_disable_count = 0;
20 std::string output;
21
22 stack local_stack;
23
24 struct loop_info {
25 datum count;
26 datum size;
27 datum first;
28 datum last;
29
30 loop_info(ssize_t count, ssize_t size) : count(), size(), first(), last()
31 {
32 if (count >= 0) {
33 this->count = count;
34 this->first = count == 0;
35 if (size >= 0) {
36 this->size = size;
37 this->last = count == (size - 1);
38 }
39 }
40 }
41 };
42 std::vector<loop_info> loop_stack;
43 scope globals;
44
45 formula_evaluation_context() noexcept = default;
46
49 void write(std::string_view text) noexcept
50 {
51 if (output_disable_count == 0) {
52 output += text;
53 }
54 }
55
59 ssize_t output_size() const noexcept
60 {
61 return ssize(output);
62 }
63
67 void set_output_size(ssize_t new_size) noexcept
68 {
69 hi_assert(new_size > 0);
70 hi_assert(new_size <= output_size());
71 output.resize(new_size);
72 }
73
74 void enable_output() noexcept
75 {
76 hi_assert(output_disable_count > 0);
77 output_disable_count--;
78 }
79
80 void disable_output() noexcept
81 {
82 output_disable_count++;
83 }
84
85 void loop_push(ssize_t count = -1, ssize_t size = -1) noexcept
86 {
87 loop_stack.emplace_back(count, size);
88 }
89
90 void loop_pop() noexcept
91 {
92 hi_assert(ssize(loop_stack) > 0);
93 loop_stack.pop_back();
94 }
95
96 void push()
97 {
98 local_stack.emplace_back();
99 loop_push();
100 }
101
102 void pop()
103 {
104 hi_assert(local_stack.size() > 0);
105 local_stack.pop_back();
106 loop_pop();
107 }
108
109 [[nodiscard]] bool has_locals() const noexcept
110 {
111 return local_stack.size() > 0;
112 }
113
114 scope const &locals() const
115 {
116 hi_assert(has_locals());
117 return local_stack.back();
118 }
119
120 scope &locals()
121 {
122 hi_assert(has_locals());
123 return local_stack.back();
124 }
125
126 [[nodiscard]] datum const &loop_get(std::string_view name) const
127 {
128 hi_assert(name.size() > 0);
129 if (name.back() == '$') {
130 throw operation_error(std::format("Invalid loop variable '{}'", name));
131 }
132
133 std::string_view short_name = name.substr(1);
134 auto i = loop_stack.crbegin();
135
136 while (short_name[0] == '$') {
137 if (i == loop_stack.crend() || holds_alternative<std::monostate>(i->count)) {
138 throw operation_error(std::format("Accessing loop variable {} while not in loop", name));
139 }
140
141 short_name = short_name.substr(1);
142 i++;
143 }
144
145 if (short_name == "i" || short_name == "count") {
146 return i->count;
147 } else if (short_name == "first") {
148 return i->first;
149 } else if (short_name == "size" || short_name == "length") {
150 if (holds_alternative<std::monostate>(i->size)) {
151 throw operation_error(std::format("Accessing loop variable {} only available in #for loops", name));
152 }
153 return i->size;
154 } else if (short_name == "last") {
155 if (holds_alternative<std::monostate>(i->last)) {
156 throw operation_error(std::format("Accessing loop variable {} only available in #for loops", name));
157 }
158 return i->last;
159 } else {
160 throw operation_error(std::format("Unknown loop variable {}", name));
161 }
162 }
163
164 [[nodiscard]] datum const &get(std::string const &name) const
165 {
166 hi_assert(name.size() > 0);
167
168 if (name[0] == '$') {
169 return loop_get(name);
170 }
171
172 if (has_locals()) {
173 hilet i = locals().find(name);
174 if (i != locals().end()) {
175 return i->second;
176 }
177 }
178
179 hilet j = globals.find(name);
180 if (j != globals.end()) {
181 return j->second;
182 }
183
184 throw operation_error(std::format("Could not find {} in local or global scope.", name));
185 }
186
187 [[nodiscard]] datum &get(std::string const &name)
188 {
189 hi_assert(name.size() > 0);
190
191 if (has_locals()) {
192 hilet i = locals().find(name);
193 if (i != locals().end()) {
194 return i->second;
195 }
196 }
197
198 hilet j = globals.find(name);
199 if (j != globals.end()) {
200 return j->second;
201 }
202
203 throw operation_error(std::format("Could not find {} in local or global scope.", name));
204 }
205
206 template<typename T>
207 void set_local(std::string const &name, T &&value)
208 {
209 locals()[name] = std::forward<T>(value);
210 }
211
212 template<typename T>
213 void set_global(std::string const &name, T &&value)
214 {
215 globals[name] = std::forward<T>(value);
216 }
217
218 datum &set(std::string const &name, datum const &value)
219 {
220 if (has_locals()) {
221 return locals()[name] = value;
222 } else {
223 return globals[name] = value;
224 }
225 }
226};
227
228} // namespace hi::inline v1
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:199
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:13
A dynamic data type.
Definition datum.hpp:223
Definition formula_evaluation_context.hpp:15
void set_output_size(ssize_t new_size) noexcept
Set the size of the output.
Definition formula_evaluation_context.hpp:67
ssize_t output_size() const noexcept
Get the size of the output.
Definition formula_evaluation_context.hpp:59
Definition formula_evaluation_context.hpp:24
T back(T... args)
T emplace_back(T... args)
T end(T... args)
T find(T... args)
T pop_back(T... args)
T crbegin(T... args)
T crend(T... args)
T size(T... args)