HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
skeleton.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 "skeleton_node.hpp"
8#include "skeleton_parse_context.hpp"
9#include "skeleton_node.hpp"
10#include "skeleton_block_node.hpp"
11#include "skeleton_break_node.hpp"
12#include "skeleton_continue_node.hpp"
13#include "skeleton_do_node.hpp"
14#include "skeleton_expression_node.hpp"
15#include "skeleton_for_node.hpp"
16#include "skeleton_function_node.hpp"
17#include "skeleton_if_node.hpp"
18#include "skeleton_placeholder_node.hpp"
19#include "skeleton_return_node.hpp"
20#include "skeleton_string_node.hpp"
21#include "skeleton_top_node.hpp"
22#include "skeleton_while_node.hpp"
23#include "../formula/formula.hpp"
24#include "../algorithm/module.hpp"
25#include "../file/file.hpp"
26#include "../macros.hpp"
27
28namespace hi::inline v1 {
29
30constexpr void parse_skeleton_hash(skeleton_parse_context &context)
31{
32 hilet &location = context.location;
33
34 if (context.starts_with("end")) {
35 context.advance_over("\n");
36
37 if (!context.pop()) {
38 throw parse_error(std::format("{}: Unexpected #end statement.", location));
39 }
40
41 context.start_of_text_segment();
42
43 } else if (context.starts_with_and_advance_over("if ")) {
44 context.push<skeleton_if_node>(location, context.parse_expression_and_advance_over("\n"));
45
46 context.start_of_text_segment();
47
48 } else if (context.starts_with_and_advance_over("elif ")) {
49 if (!context.found_elif(location, context.parse_expression_and_advance_over("\n"))) {
50 throw parse_error(std::format("{}: Unexpected #elif statement.", location));
51 }
52
53 context.start_of_text_segment();
54
55 } else if (context.starts_with_and_advance_over("else")) {
56 context.advance_over("\n");
57
58 if (!context.found_else(location)) {
59 throw parse_error(std::format("{}: Unexpected #else statement.", location));
60 }
61
62 context.start_of_text_segment();
63
64 } else if (context.starts_with_and_advance_over("for ")) {
65 auto name_expression = context.parse_expression_and_advance_over(":");
66 auto list_expression = context.parse_expression_and_advance_over("\n");
67
68 context.push<skeleton_for_node>(location, std::move(name_expression), std::move(list_expression));
69
70 context.start_of_text_segment();
71
72 } else if (context.starts_with_and_advance_over("while ")) {
73 auto expression = context.parse_expression_and_advance_over("\n");
74
75 if (context.top_statement_is_do()) {
76 if (!context.found_while(location, std::move(expression))) {
77 throw parse_error(std::format("{}: Unexpected #while statement; missing #do.", location));
78 }
79
80 hi_assert(context.pop());
81 } else {
82 context.push<skeleton_while_node>(location, std::move(expression));
83 }
84
85 context.start_of_text_segment();
86
87 } else if (context.starts_with_and_advance_over("do")) {
88 context.advance_over("\n");
89
90 context.push<skeleton_do_node>(location);
91
92 context.start_of_text_segment();
93
94 } else if (context.starts_with_and_advance_over("function ")) {
95 context.push<skeleton_function_node>(
96 location, context.post_process_context, *context.parse_expression_and_advance_over("\n"));
97
98 context.start_of_text_segment();
99
100 } else if (context.starts_with_and_advance_over("block ")) {
101 context.push<skeleton_block_node>(
102 location, context.post_process_context, *context.parse_expression_and_advance_over("\n"));
103
104 context.start_of_text_segment();
105
106 } else if (context.starts_with_and_advance_over("break")) {
107 context.advance_over("\n");
108
109 if (!context.append<skeleton_break_node>(location)) {
110 throw parse_error(std::format("{}: Unexpected #break statement", location));
111 }
112
113 context.start_of_text_segment();
114
115 } else if (context.starts_with_and_advance_over("continue")) {
116 context.advance_over("\n");
117
118 if (!context.append<skeleton_continue_node>(location)) {
119 throw parse_error(std::format("{}: Unexpected #continue statement", location));
120 }
121
122 context.start_of_text_segment();
123
124 } else if (context.starts_with_and_advance_over("return ")) {
125 if (!context.append<skeleton_return_node>(location, context.parse_expression_and_advance_over("\n"))) {
126 throw parse_error(std::format("{}: Unexpected #return statement", location));
127 }
128
129 context.start_of_text_segment();
130
131 } else if (context.starts_with_and_advance_over("include ")) {
132 context.include(location, *context.parse_expression_and_advance_over("\n"));
133 context.start_of_text_segment();
134
135 } else { // Add '#' and the current character to text.
136 if (!context.append<skeleton_expression_node>(location, context.parse_expression_and_advance_over("\n"))) {
137 throw parse_error(std::format("{}: Unexpected # (expression) statement.", location));
138 }
139
140 context.start_of_text_segment();
141 }
142}
143
144constexpr void parse_skeleton_dollar(skeleton_parse_context &context)
145{
146 hilet &location = context.location;
147
148 if (*context == '{') {
149 ++context;
150 if (!context.append<skeleton_placeholder_node>(location, context.parse_expression_and_advance_over("}"))) {
151 throw parse_error("Unexpected placeholder.");
152 }
153
154 context.start_of_text_segment();
155
156 } else {
157 ++context;
158 context.start_of_text_segment(-2);
159 }
160}
161
162constexpr void parse_skeleton_escape(skeleton_parse_context &context)
163{
164 for (; !context.atEOF(); ++context) {
165 hilet c = *context;
166
167 switch (c) {
168 case '\n': // Skip over line-feed
169 ++context;
170 context.start_of_text_segment();
171 return;
172
173 case '\r': // Skip over carriage return and potential line-feed.
174 ++context;
175 break;
176
177 default: // Add character to text.
178 ++context;
179 context.start_of_text_segment(-2);
180 return;
181 }
182 }
183 throw parse_error(std::format("{}: Unexpected end-of-file after escape '\' character.", context.location));
184}
185
186[[nodiscard]] constexpr std::unique_ptr<skeleton_node> parse_skeleton(skeleton_parse_context &context)
187{
188 context.start_of_text_segment();
189
190 while (!context.atEOF()) {
191 switch (*context) {
192 case '#':
193 context.end_of_text_segment();
194 ++context;
195 parse_skeleton_hash(context);
196 break;
197
198 case '$':
199 context.end_of_text_segment();
200 ++context;
201 parse_skeleton_dollar(context);
202 break;
203
204 case '\\': // Skip the backslash.
205 context.end_of_text_segment();
206 ++context;
207 parse_skeleton_escape(context);
208 break;
209
210 default: ++context;
211 }
212 }
213 context.end_of_text_segment();
214
215 if (context.statement_stack.size() < 1) {
216 throw parse_error(std::format("{}: Found to many #end statements.", context.location));
217 } else if (context.statement_stack.size() > 1) {
218 throw parse_error(std::format("{}: Missing #end statement.", context.location));
219 }
220
221 auto top = std::move(context.statement_stack.back());
222 context.statement_stack.pop_back();
223
224 top->post_process(context.post_process_context);
225 return top;
226}
227
229parse_skeleton(std::filesystem::path path, std::string_view::const_iterator first, std::string_view::const_iterator last)
230{
231 auto context = skeleton_parse_context(std::move(path), first, last);
232 auto e = parse_skeleton(context);
233 return e;
234}
235
236[[nodiscard]] inline std::unique_ptr<skeleton_node> parse_skeleton(std::filesystem::path path, std::string_view text)
237{
238 return parse_skeleton(std::move(path), text.cbegin(), text.cend());
239}
240
241[[nodiscard]] inline std::unique_ptr<skeleton_node> parse_skeleton(std::filesystem::path path)
242{
243 hilet fv = file_view(path);
244 hilet sv = as_string_view(fv);
245
246 return parse_skeleton(std::move(path), sv.cbegin(), sv.cend());
247}
248
249} // namespace hi::inline v1
Defines the file class.
@ top
Align to the top.
DOXYGEN BUG.
Definition algorithm.hpp:16
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
T move(T... args)