HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
expression.hpp
1// Copyright 2019 Pokitec
2// All rights reserved.
3
4#pragma once
5
6#include "TTauri/Foundation/datum.hpp"
7#include "TTauri/Foundation/parse_location.hpp"
8#include "TTauri/Foundation/exceptions.hpp"
9#include "TTauri/Foundation/tokenizer.hpp"
10#include <vector>
11#include <string>
12#include <string_view>
13#include <memory>
14#include <unordered_map>
15#include <tuple>
16
17
18namespace tt {
19
23
24 ssize_t output_disable_count = 0;
25 std::string output;
26
27 stack local_stack;
28
29 struct loop_info {
30 datum count;
31 datum size;
32 datum first;
33 datum last;
34
35 loop_info(ssize_t count, ssize_t size) :
36 count(), size(), first(), last()
37 {
38 if (count >= 0) {
39 this->count = count;
40 this->first = count == 0;
41 if (size >= 0) {
42 this->size = size;
43 this->last = count == (size - 1);
44 }
45 }
46 }
47 };
48 std::vector<loop_info> loop_stack;
49 scope globals;
50
52
55 void write(std::string_view text) noexcept {
56 if (output_disable_count == 0) {
57 output += text;
58 }
59 }
60
64 ssize_t output_size() const noexcept {
65 return ssize(output);
66 }
67
71 void set_output_size(ssize_t new_size) noexcept {
72 tt_assert(new_size > 0);
73 tt_assert(new_size <= output_size());
74 output.resize(new_size);
75 }
76
77 void enable_output() noexcept {
78 tt_assert(output_disable_count > 0);
79 output_disable_count--;
80 }
81
82 void disable_output() noexcept {
83 output_disable_count++;
84 }
85
86 void loop_push(ssize_t count = -1, ssize_t size = -1) noexcept {
87 loop_stack.emplace_back(count, size);
88 }
89
90 void loop_pop() noexcept {
91 tt_assert(ssize(loop_stack) > 0);
92 loop_stack.pop_back();
93 }
94
95 void push() {
96 local_stack.emplace_back();
97 loop_push();
98 }
99
100 void pop() {
101 tt_assert(local_stack.size() > 0);
102 local_stack.pop_back();
103 loop_pop();
104 }
105
106 [[nodiscard]] bool has_locals() const noexcept {
107 return local_stack.size() > 0;
108 }
109
110 tt_force_inline scope const& locals() const {
111 tt_assume(has_locals());
112 return local_stack.back();
113 }
114
115 tt_force_inline scope& locals() {
116 tt_assume(has_locals());
117 return local_stack.back();
118 }
119
120 [[nodiscard]] datum const &loop_get(std::string_view name) const {
121 tt_assume(name.size() > 0);
122 if (name.back() == '$') {
123 TTAURI_THROW(invalid_operation_error("Invalid loop variable '{}'", name));
124 }
125
126 std::string_view short_name = name.substr(1);
127 auto i = loop_stack.crbegin();
128
129 while (short_name[0] == '$') {
130 if (i == loop_stack.crend() || i->count.is_undefined()) {
131 TTAURI_THROW(invalid_operation_error("Accessing loop variable {} while not in loop", name));
132 }
133
134 short_name = short_name.substr(1);
135 i++;
136 }
137
138 if (short_name == "i" || short_name == "count") {
139 return i->count;
140 } else if (short_name == "first") {
141 return i->first;
142 } else if (short_name == "size" || short_name == "length") {
143 if (i->size.is_undefined()) {
144 TTAURI_THROW(invalid_operation_error("Accessing loop variable {} only available in #for loops", name));
145 }
146 return i->size;
147 } else if (short_name == "last") {
148 if (i->last.is_undefined()) {
149 TTAURI_THROW(invalid_operation_error("Accessing loop variable {} only available in #for loops", name));
150 }
151 return i->last;
152 } else {
153 TTAURI_THROW(invalid_operation_error("Unknown loop variable {}", name));
154 }
155 }
156
157 [[nodiscard]] datum const& get(std::string const &name) const {
158 tt_assert(name.size() > 0);
159
160 if (name[0] == '$') {
161 return loop_get(name);
162 }
163
164 if (has_locals()) {
165 ttlet i = locals().find(name);
166 if (i != locals().end()) {
167 return i->second;
168 }
169 }
170
171 ttlet j = globals.find(name);
172 if (j != globals.end()) {
173 return j->second;
174 }
175
176 TTAURI_THROW(invalid_operation_error("Could not find {} in local or global scope.", name));
177 }
178
179 [[nodiscard]] datum &get(std::string const &name) {
180 tt_assert(name.size() > 0);
181
182 if (has_locals()) {
183 ttlet i = locals().find(name);
184 if (i != locals().end()) {
185 return i->second;
186 }
187 }
188
189 ttlet j = globals.find(name);
190 if (j != globals.end()) {
191 return j->second;
192 }
193
194 TTAURI_THROW(invalid_operation_error("Could not find {} in local or global scope.", name));
195 }
196
197 template<typename T>
198 void set_local(std::string const &name, T &&value) {
199 locals()[name] = std::forward<T>(value);
200 }
201
202 template<typename T>
203 void set_global(std::string const& name, T &&value) {
204 globals[name] = std::forward<T>(value);
205 }
206
207 datum &set(std::string const &name, datum const &value) {
208 if (has_locals()) {
209 return locals()[name] = value;
210 } else {
211 return globals[name] = value;
212 }
213 }
214};
215
217 using filter_type = std::function<std::string(std::string_view)>;
224
225 function_table functions;
226 function_stack super_stack;
227 static function_table global_functions;
228 static method_table global_methods;
229 static filter_table global_filters;
230
231 [[nodiscard]] function_type get_function(std::string const &name) const noexcept {
232 if (name == "super") {
233 if (super_stack.size() > 0) {
234 return super_stack.back();
235 } else {
236 return {};
237 }
238 }
239
240 ttlet i = functions.find(name);
241 if (i != functions.end()) {
242 return i->second;
243 }
244
245 ttlet j = global_functions.find(name);
246 if (j != global_functions.end()) {
247 return j->second;
248 }
249
250 return {};
251 }
252
253 [[nodiscard]] function_type set_function(std::string const &name, function_type func) noexcept {
254 using namespace std;
255
256 swap(functions[name], func);
257 return func;
258 }
259
260 void push_super(function_type func) noexcept {
261 super_stack.push_back(func);
262 }
263
264 void pop_super(void) noexcept {
265 super_stack.pop_back();
266 }
267
268 [[nodiscard]] filter_type get_filter(std::string const &name) const noexcept {
269 ttlet i = global_filters.find(name);
270 if (i != global_filters.end()) {
271 return i->second;
272 }
273
274 return {};
275 }
276
277 [[nodiscard]] method_type get_method(std::string const &name) const noexcept {
278 ttlet i = global_methods.find(name);
279 if (i != global_methods.end()) {
280 return i->second;
281 }
282
283 return {};
284 }
285};
286
288 using const_iterator = typename std::vector<token_t>::const_iterator;
289
290 std::string_view::const_iterator first;
291 std::string_view::const_iterator last;
292
294 const_iterator token_it;
295
296 expression_parse_context(std::string_view::const_iterator first, std::string_view::const_iterator last) :
297 first(first), last(last), tokens(parseTokens(first, last)), token_it(tokens.begin()) {}
298
299 [[nodiscard]] token_t const& operator*() const noexcept {
300 return *token_it;
301 }
302
303 [[nodiscard]] token_t const *operator->() const noexcept {
304 return &(*token_it);
305 }
306
307 expression_parse_context& operator++() noexcept {
308 tt_assume(token_it != tokens.end());
309 tt_assume(*token_it != tokenizer_name_t::End);
310 ++token_it;
311 return *this;
312 }
313
314 expression_parse_context operator++(int) noexcept {
315 auto tmp = *this;
316 ++(*this);
317 return tmp;
318 }
319};
320
323
324 parse_location location;
325
326 expression_node(parse_location location) : location(location) {}
327
328 virtual ~expression_node() {}
329
334
339
342 virtual datum evaluate(expression_evaluation_context& context) const = 0;
343
344 datum evaluate_without_output(expression_evaluation_context& context) const {
345 context.disable_output();
346 auto r = evaluate(context);
347 context.enable_output();
348 return r;
349 }
350
354 TTAURI_THROW(invalid_operation_error("Expression is not a modifiable value.").set_location(location));
355 }
356
357 virtual bool has_evaluate_xvalue() const {
358 return false;
359 }
360
363 virtual datum const &evaluate_xvalue(expression_evaluation_context const& context) const {
364 TTAURI_THROW(invalid_operation_error("Expression is not a xvalue.").set_location(location));
365 }
366
369 virtual datum &assign(expression_evaluation_context& context, datum const &rhs) const {
370 return evaluate_lvalue(context) = rhs;
371 }
372
373 datum &assign_without_output(expression_evaluation_context& context, datum const &rhs) const {
374 context.disable_output();
375 auto &r = assign(context, rhs);
376 context.enable_output();
377 return r;
378 }
379
382 virtual datum call(expression_evaluation_context& context, datum::vector const &arguments) const {
383 TTAURI_THROW(invalid_operation_error("Expression is not callable.").set_location(location));
384 }
385
388 virtual std::string get_name() const {
389 TTAURI_THROW(parse_error("Expect a name got {})", *this).set_location(location));
390 }
391
396 TTAURI_THROW(parse_error("Expect a function definition got {})", *this).set_location(location));
397 }
398
399 virtual std::string string() const noexcept = 0;
400
401 friend std::string to_string(expression_node const& rhs) noexcept {
402 return rhs.string();
403 }
404
405 friend std::ostream& operator<<(std::ostream& lhs, expression_node const& rhs) noexcept {
406 return lhs << to_string(rhs);
407 }
408};
409
413std::unique_ptr<expression_node> parse_expression(expression_parse_context& context);
414
418inline std::unique_ptr<expression_node> parse_expression(std::string_view::const_iterator first, std::string_view::const_iterator last) {
419 auto parse_context = expression_parse_context(first, last);
420 auto e = parse_expression(parse_context);
421
422 auto post_process_context = expression_post_process_context();
423 e->post_process(post_process_context);
424 return e;
425}
426
430inline std::unique_ptr<expression_node> parse_expression(std::string_view text) {
431 return parse_expression(text.cbegin(), text.cend());
432}
433
434
442std::string_view::const_iterator find_end_of_expression(
443 std::string_view::const_iterator first,
444 std::string_view::const_iterator last,
445 std::string_view terminating_string);
446
447}
STL namespace.
Definition exceptions.hpp:154
Definition expression.hpp:20
void write(std::string_view text) noexcept
Write data to the output.
Definition expression.hpp:55
ssize_t output_size() const noexcept
Get the size of the output.
Definition expression.hpp:64
void set_output_size(ssize_t new_size) noexcept
Set the size of the output.
Definition expression.hpp:71
Definition expression.hpp:216
Definition expression.hpp:287
Definition expression.hpp:321
virtual std::string get_name() const
Get the name of a expression_name_node.
Definition expression.hpp:388
virtual std::vector< std::string > get_name_and_argument_names() const
Get name and argument names from a function declaration.
Definition expression.hpp:395
virtual void post_process(expression_post_process_context &context)
Resolve function and method pointers.
Definition expression.hpp:333
virtual datum evaluate(expression_evaluation_context &context) const =0
Evaluate an rvalue.
virtual datum & assign(expression_evaluation_context &context, datum const &rhs) const
Assign to a non-existing or existing lvalue.
Definition expression.hpp:369
virtual datum & evaluate_lvalue(expression_evaluation_context &context) const
Evaluate an existing lvalue.
Definition expression.hpp:353
virtual void resolve_function_pointer(expression_post_process_context &context)
Resolve function and method pointers.
Definition expression.hpp:338
virtual datum const & evaluate_xvalue(expression_evaluation_context const &context) const
Evaluate an existing xvalue.
Definition expression.hpp:363
virtual datum call(expression_evaluation_context &context, datum::vector const &arguments) const
Call a function with a datum::vector as arguments.
Definition expression.hpp:382
Definition parse_location.hpp:15
Definition tokenizer.hpp:62
T back(T... args)
T begin(T... args)
T emplace_back(T... args)
T end(T... args)
T find(T... args)
T pop_back(T... args)
T push_back(T... args)
T crbegin(T... args)
T crend(T... args)
T resize(T... args)
T size(T... args)