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