-
Notifications
You must be signed in to change notification settings - Fork 2
/
grammar.cpp
334 lines (277 loc) · 11.3 KB
/
grammar.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
//#define BOOST_SPIRIT_X3_DEBUG
#include "grammar.h"
#include "grammar_actions.hpp"
#include "runtime.h"
#include <boost/fusion/adapted/std_tuple.hpp>
using namespace runtime;
using namespace actions;
namespace main_pass
{
using x3::int_;
constexpr x3::real_parser<float, x3::strict_real_policies<float>> strict_float = {};
using x3::char_;
using x3::lit;
using x3::no_case;
using x3::attr;
using x3::eoi;
using x3::eps;
using x3::omit;
using x3::lexeme;
constexpr auto line_num = x3::ulong_long;
using str_view = boost::iterator_range<std::string_view::const_iterator>;
expression_type const expression( "expression" );
sequence_separator_type const sequence_separator( "sequence_separator" );
statement_type const statement( "statement" );
else_statement_type const else_statement( "else_statement" );
x3::rule<class mult_div, value_t> const mult_div( "mult_div" );
x3::rule<class exponent, value_t> const exponent( "exponent" );
x3::rule<class term, value_t> const term( "term" );
x3::rule<class add_sub, value_t> const add_sub( "add_sub" );
x3::rule<class relational, value_t> const relational( "relational" );
x3::rule<class log_and, value_t> const log_and( "log_and" );
x3::rule<class log_or, value_t> const log_or( "log_or" );
x3::rule<class double_args, std::tuple<value_t, value_t>> const double_args( "double_args" );
x3::rule<class triple_args, std::tuple<value_t, value_t, value_t>> const triple_args( "triple_args" );
x3::rule<class identifier, std::string> const identifier( "identifier" );
x3::rule<class var_name, std::string> const var_name( "var_name" );
x3::rule<class string_lit, std::string> const string_lit( "string_lit" );
x3::rule<class next_stmt, std::string> const next_stmt( "next_stmt" );
x3::rule<class expression_int, int_t> const expression_int( "expression_int" );
const auto expression_def =
log_or;
const auto expression_int_def =
expression[force_int_op];
const auto quote = char_( '"' ); //MSVC has problems if we inline it.
const auto string_lit_def =
lexeme['"' >> *~quote >> '"'];
const auto statement_end =
':' | no_case["else"] | eoi;
const auto sequence_separator_def =
+lit( ':' );
const auto single_arg =
'(' >> expression >> ')';
const auto double_args_def =
'(' >> expression >> ',' >> expression >> ')';
const auto triple_args_def =
'(' >> expression >> ',' >> expression >> ',' >> expression >> ')';
const auto print_comma =
',' >> attr( value_t{ "\t" } )[print_op];
const auto print_arg =
+(
+print_comma |
no_case["tab"] >> single_arg[print_tab_op] |
expression[print_op]
);
const auto print_stmt =
(no_case["print"] >> print_arg >> *(';' >> print_arg) >> (
';' | (&statement_end >> attr( value_t{ "\n" } )[print_op])
)) |
no_case["print"] >> attr( value_t{ "\n" } )[print_op];
const auto input_stmt =
no_case["input"] >>
(
(string_lit >> ';' >> var_name)[input_op] |
(attr( std::string{ "?" } ) >> var_name)[input_op]
) >>
*((',' >> attr( std::string{ "??" } ) >> var_name)[input_op]);
const auto next_stmt_def =
no_case["next"] >> (var_name % ',')[next_stmt_op] |
no_case["next"] >> attr( std::vector<std::string>{} )[next_stmt_op];
const auto for_stmt =
(no_case["for"] >> var_name >> '=' >> expression >> no_case["to"] >> expression >>
-(no_case["step"] >> expression)
)[for_stmt_op];
//A straightforward approach here will have problems with reseting boost::optional<linenum_t>
//during backtracking:
// https://github.com/boostorg/spirit/issues/378
// https://stackoverflow.com/a/49309385/3415353
const auto if_stmt =
(no_case["if"] >> expression >> (no_case["then"] | no_case["goto"]) >> line_num)[if_stmt_op] |
(no_case["if"] >> expression >> -no_case["then"] >> attr(MaxLineNum))[if_stmt_op]
;
const auto else_statement_def =
no_case["else"] >> -line_num[else_stmt_op];
const auto on_stmt =
(no_case["on"] >> expression >> no_case["goto"] >> line_num % ',')[on_goto_stmt_op] |
(no_case["on"] >> expression >> no_case["gosub"] >> line_num % ',')[on_gosub_stmt_op]
;
const auto var_name_dim =
(identifier >> '(' >> expression_int % ',' >> ')')[dim_stmt_op] |
(identifier >> attr( std::vector<int_t>{} ))[dim_stmt_op]
;
const auto restore_stmt =
no_case["restore"] >> line_num [restore_stmt_op] |
no_case["restore"] >> attr( MaxLineNum )[restore_stmt_op]
;
const auto statement_def =
no_case["text"] |
no_case["home"] |
no_case["cls"] |
no_case["stop"][stop_stmt_op] |
print_stmt |
input_stmt |
if_stmt |
on_stmt |
no_case["goto"] >> line_num[goto_stmt_op] |
no_case["gosub"] >> line_num[gosub_stmt_op] |
no_case["return"][return_stmt_op] |
for_stmt |
next_stmt |
no_case["end"][end_stmt_op] |
no_case["dim"] >> var_name_dim % ',' |
restore_stmt |
no_case["read"] >> var_name[read_stmt_op] % ',' |
no_case["randomize"] >> expression[randomize_stmt_op] |
no_case["rem"] >> omit[lexeme[*char_]] |
no_case["def"] >> no_case["fn"] >> (identifier >> '(' >> identifier >> ')' >> '=' >> lexeme[+~char_(':')])[def_stmt_op] |
(-no_case["let"] >> var_name >> '=' >> expression)[assing_var_op]
;
// Technically, we should exclude all keywords here, but only ELSE is necessary due to
// the crazy PRINT syntax that allows multiple statements not divided by any separator
const auto identifier_def = !no_case["else"] >> x3::raw[lexeme[(x3::alpha | '_') >> *(x3::alnum | '_') >> -(lit( '%' ) | '$')]];
const auto var_name_def =
identifier[cpy_op] >> char_( '(' )[append_op] >> expression[append_idx_op] % char_( ',' )[append_op] >> char_( ')' )[append_op] |
identifier[cpy_op]
;
const auto term_def =
strict_float[cpy_op] |
int_[cpy_int_op] |
string_lit[cpy_op] |
'(' >> expression[cpy_op] >> ')' |
'-' >> term[neg_op] |
'+' >> term[cpy_op] |
(no_case["not"] >> term[not_op]) |
no_case["sqr"] >> single_arg[sqr_op] |
no_case["int"] >> single_arg[int_op] |
no_case["abs"] >> single_arg[abs_op] |
no_case["left$"] >> double_args[left_op] |
no_case["right$"] >> double_args[right_op] |
no_case["mid$"] >> triple_args[mid_op] |
no_case["mid$"] >> double_args[mid2_op] |
no_case["str$"] >> single_arg[str_op] |
no_case["val"] >> single_arg[val_op] |
no_case["len"] >> single_arg[len_op] |
no_case["asc"] >> single_arg[asc_op] |
no_case["chr$"] >> single_arg[chr_op] |
no_case["rnd"] >> single_arg[rnd_op] |
no_case["inkey$"][inkey_op] |
no_case["fn"] >> (identifier >> single_arg) [call_fn_op] |
var_name[load_var_op]
;
const auto exponent_def =
term[cpy_op] >> *(
('^' >> term[exp_op])
);
const auto mult_div_def =
exponent[cpy_op] >> *(
('*' >> exponent[mul_op]) |
('/' >> exponent[div_op])
);
const auto add_sub_def =
mult_div[cpy_op] >> *(
('+' >> mult_div[add_op]) |
('-' >> mult_div[sub_op])
);
const auto relational_def =
add_sub[cpy_op] >> *(
((lit( "==" ) | '=') >> add_sub[eq_op]) |
('<' >> lit( '>' ) >> add_sub[not_eq_op]) |
('<' >> add_sub[less_op]) |
('>' >> add_sub[greater_op]) |
('<' >> lit( '=' ) >> add_sub[less_eq_op]) |
('>' >> lit( '=' ) >> add_sub[greater_eq_op])
);
const auto log_and_def =
relational[cpy_op] >> *(
no_case["and"] >> relational[and_op]
);
const auto log_or_def =
log_and[cpy_op] >> *(
no_case["or"] >> log_and[or_op]
);
BOOST_SPIRIT_DEFINE( expression, expression_int, exponent, mult_div, term, add_sub, relational, log_and, log_or,
double_args, triple_args, identifier, var_name, string_lit, statement, else_statement, sequence_separator, next_stmt
);
expression_type expression_rule()
{
return expression;
}
statement_type statement_rule()
{
return statement;
}
else_statement_type else_statement_rule()
{
return else_statement;
}
sequence_separator_type sequence_separator_rule()
{
return sequence_separator;
}
}
namespace preparse
{
using x3::char_;
using x3::int_;
using x3::eoi;
using x3::attr;
using x3::no_case;
using x3::omit;
using x3::lexeme;
using main_pass::line_num;
using main_pass::strict_float;
using main_pass::string_lit;
line_type const line( "line" );
x3::rule<class statement> const statement( "statement" );
const auto data_stmt =
no_case["data"] >> (
strict_float[data_op] |
int_[data_op] |
string_lit[data_op]
) % ',';
const auto statement_def =
no_case["rem"] >> omit[lexeme[*char_]] |
data_stmt;
const auto num_line =
(line_num[update_cur_line_op] >> statement % ':' >> attr(std::string{}))[add_num_line_op] |
(line_num >> lexeme[+char_])[add_num_line_op];
const auto line_def =
num_line |
':' >> lexeme[+char_][append_line_op] |
eoi;
BOOST_SPIRIT_DEFINE( line, statement );
line_type line_rule()
{
return line;
}
}
namespace x3 = boost::spirit::x3;
using iterator_type = std::string_view::const_iterator;
using simple_context_type = x3::phrase_parse_context<x3::ascii::space_type>::type;
template<class RuntimeT>
using context_type = x3::context<
parse_mode_tag, std::reference_wrapper<runtime::ParseMode>,
x3::context <
line_begin_tag, iterator_type,
x3::context<
runtime_tag, std::reference_wrapper<RuntimeT>,
simple_context_type
>
>
>;
namespace main_pass
{
BOOST_SPIRIT_INSTANTIATE( expression_type, iterator_type, context_type<runtime::TestRuntime> );
BOOST_SPIRIT_INSTANTIATE( expression_type, iterator_type, context_type<runtime::FunctionRuntime> );
BOOST_SPIRIT_INSTANTIATE( statement_type, iterator_type, context_type<runtime::Runtime> );
BOOST_SPIRIT_INSTANTIATE( statement_type, iterator_type, context_type<runtime::TestRuntime> );
BOOST_SPIRIT_INSTANTIATE( statement_type, iterator_type, context_type<runtime::SkipStatementRuntime> );
BOOST_SPIRIT_INSTANTIATE( else_statement_type, iterator_type, context_type<runtime::Runtime> );
BOOST_SPIRIT_INSTANTIATE( else_statement_type, iterator_type, context_type<runtime::TestRuntime> );
BOOST_SPIRIT_INSTANTIATE( else_statement_type, iterator_type, context_type<runtime::SkipStatementRuntime> );
BOOST_SPIRIT_INSTANTIATE( sequence_separator_type, iterator_type, simple_context_type );
}
namespace preparse
{
BOOST_SPIRIT_INSTANTIATE( line_type, iterator_type, context_type<runtime::Runtime> );
}