-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathebe.cpp
381 lines (346 loc) · 12.9 KB
/
ebe.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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/**
* @file ebe.cpp
* @author Marek Sedlacek
* @date July 2021
* @copyright Copyright 2021 Marek Sedlacek. All rights reserved.
*
* @brief Entry point of the ebe compiler
*
* Ebe (Edit By Example) compiler can create text editing programs
* based on example (original program and wanted output on small scale).
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>
#include <filesystem>
#include "ebe.hpp"
#include "preprocessor.hpp"
#include "scanner_text.hpp"
#include "scanner_ebel.hpp"
#include "ir.hpp"
#include "interpreter.hpp"
#include "engine_jenn.hpp"
#include "engine_miRANDa.hpp"
#include "engine_taylor.hpp"
#include "rng.hpp"
#include "logging.hpp"
#include "arg_parser.hpp"
/**
* Initializer and handler for compilation
*/
std::pair<IR::EbelNode *, float> compile_core(IR::Node *ir_in, IR::Node *ir_out, std::ostream &out, Engine *engine) {
// Evolution
float precision = -0.01f;
float best_precision = -0.01f;
// Get the engine ID
EngineUtils::EngineID engine_id = EngineUtils::EngineID::JENN;
if(Args::arg_opts.engine != nullptr){
engine_id = EngineUtils::get_engine_id(Args::arg_opts.engine);
if(engine_id == EngineUtils::EngineID::UNKNOWN) {
// This should not happen, it should be tested in the parse_args
Error::error(Error::ErrorCode::ARGUMENTS, "Incorrect engine name");
}
// In case MiRANDa is used generate a warning to let the user know
if(engine_id == EngineUtils::EngineID::MIRANDA){
Error::warning("Using MiRANDa engine, which uses only pure randomness and is NOT meant for real use");
}
}
IR::EbelNode *best_program = nullptr;
size_t evolutions = (Args::arg_opts.evolutions > 0) ? Args::arg_opts.evolutions : 3;
for(size_t e = 1; e <= evolutions || Args::arg_opts.precision != 0 || Args::arg_opts.timeout != 0; ++e){
if(best_program != nullptr) {
// Before deletion, best program has to be copied to not be lost
auto best_program_copy = new IR::EbelNode(*best_program);
best_program = best_program_copy;
/*if(engine != nullptr) {
delete engine;
}*/
//engine = nullptr;
}
switch(engine_id){
case EngineUtils::EngineID::MIRANDA:
engine = new EngineMiRANDa(ir_in, ir_out);
break;
case EngineUtils::EngineID::JENN:
engine = new EngineJenn(ir_in, ir_out);
break;
case EngineUtils::EngineID::TAYLOR:
engine = new EngineTaylor(ir_in, ir_out);
break;
default:
Error::error(Error::ErrorCode::INTERNAL, "Attempt to use engine unknown by the compile process");
}
LOGMAX("Started " << e << ". compilation with engine " << engine->engine_name);
auto program = engine->generate(&precision);
if(precision >= 1.0f){
// Found perfect program, end now
if(!Args::arg_opts.no_info_print) {
out << "Perfectly fitting program found. ";
}
best_program = program;
best_precision = precision;
LOG3("Perfectly fitting program found, compilation ended");
break;
}
else if(Utils::is_precise(precision)) {
if(!Args::arg_opts.no_info_print) {
out << "Minimum precision program found. ";
}
best_program = program;
best_precision = precision;
LOG3("Minimum precision program found, compilation ended");
break;
}
else if(Utils::is_timeout()) {
if(!Args::arg_opts.no_info_print) {
out << "Timeout. ";
}
best_program = program;
best_precision = precision;
LOG3("Timeout, compilation ended");
break;
}
LOG4(e << ". evolution finished. Best program (with " << (precision*100) << "% precision):\n" << *program);
// Save program if it is the best one so far
if(!best_program || best_precision < precision){
best_program = program;
best_precision = precision;
}
}
// No need to make a deep copy, since engine is passed as well. One copy is avoided and engine can ba
// free after printing the program.
IR::EbelNode *ebel = best_program;
return std::make_pair(ebel, best_precision);
}
void compile(const char *f_in, const char *f_out) {
LOGMAX("Compilation started");
// Preprocessing
auto preproc = new Preprocessor();
LOGMAX("Text preprocessor started");
auto in_text = preproc->process(f_in);
auto out_text = preproc->process(f_out);
LOGMAX("Text preprocessor finished");
// Syntactical check
auto scanner = new TextFile::ScannerText();
LOGMAX("Text scanner started");
auto ir_in = scanner->process(in_text, f_in);
LOG1("Text IN IR:\n" << *ir_in);
auto ir_out = scanner->process(out_text, f_out);
LOG1("Text OUT IR:\n" << *ir_out);
LOGMAX("Text scanner finished");
Engine *engine = nullptr;
auto compiled = compile_core(ir_in, ir_out, std::cout, engine);
IR::EbelNode *ebel = compiled.first;
float precision = compiled.second;
if(ebel) {
// Print the best program
std::chrono::duration<float> diff = std::chrono::steady_clock::now()-Args::arg_opts.start_time;
std::string ebel_out;
if(Args::arg_opts.ebel_out) {
ebel_out = std::string(Args::arg_opts.ebel_out);
}
else {
// Use input file name with .ebel as the output
auto in_f = std::string(Args::arg_opts.file_in);
ebel_out = (in_f.substr(0, in_f.rfind("."))+".ebel");
}
// Folder existence is checked in arg_parser
std::ofstream o_file(ebel_out);
o_file << *ebel;
o_file.close();
if(!Args::arg_opts.no_info_print) {
std::cout << "Ebel saved to '" << ebel_out << "'." << std::endl;
std::cout << std::endl << "Best compiled program has " << (precision*100) << "% precision ("
<< std::fixed << std::setprecision(1) << diff.count()
<< " s)." << std::endl;
}
LOG1("Best compiled program with " << (precision*100) << "% precision:\n" << *ebel);
}
// Cleanup
delete ir_in;
delete ir_out;
if(in_text != &std::cin) {
delete in_text;
}
if(out_text != &std::cin) {
delete out_text;
}
delete engine;
delete scanner;
delete preproc;
LOGMAX("Compilation done");
}
void interpret_core(IR::EbelNode *ebel, std::vector<const char *> input_files) {
// Interpret initialization
auto interpreter = new Interpreter(ebel);
bool use_stdin = false;
// If no input files are specified, use stdin
if(input_files.empty()) {
input_files.push_back("stdin");
use_stdin = true;
}
for(auto input_f: input_files){
// Preprocessing input file
auto text_preproc = new Preprocessor();
LOGMAX("Text preprocessor for " << input_f << " started");
auto text_stream = text_preproc->process(use_stdin ? nullptr : input_f);
LOGMAX("Text preprocessor finished");
// Syntactical check/parse of input file
auto text_scanner = new TextFile::ScannerText();
LOGMAX("Text scanner started");
auto text_ir = text_scanner->process(text_stream, input_f);
LOG1("Text IR:\n" << *text_ir);
LOGMAX("Text scanner finished");
LOGMAX("Interpreter started");
interpreter->parse(text_ir);
LOGMAX("Interpreter finished");
LOG1("Interpreted text IR:\n" << *text_ir);
if(Args::arg_opts.interpret_out == nullptr) {
if(input_files.size() > 1) {
std::cout << "# Interpreted " << input_f << ": " << std::endl;
}
std::cout << text_ir->output();
}
else {
// Folder existence is checked in arg_parser
auto f_out_name = std::string(Args::arg_opts.interpret_out);
if(input_files.size() > 1) {
auto file_name = std::filesystem::path(input_f);
// Create file name if multiple files are interpreted
f_out_name = std::string(Args::arg_opts.interpret_out) + "/edited-" + std::string(file_name.filename());
}
std::ofstream o_file(f_out_name);
o_file << text_ir->output();
o_file.close();
}
delete text_ir;
delete text_scanner;
if(!use_stdin) {
delete text_stream;
}
delete text_preproc;
}
if(use_stdin) {
input_files.pop_back();
}
delete interpreter;
}
void interpret(const char *ebel_f, std::vector<const char *> input_files){
LOGMAX("Interpetation started");
// Preprocessing
auto ebel_preproc = new Preprocessor();
LOGMAX("Ebel preprocessor started");
auto ebel_text = ebel_preproc->process(ebel_f);
LOGMAX("Ebel preprocessor finished");
// Syntactical check
auto ebel_scanner = new EbelFile::ScannerEbel();
LOGMAX("Ebel scanner started");
auto ebel_ir = ebel_scanner->process(ebel_text, ebel_f);
LOG1("Ebel IR:\n" << *ebel_ir);
LOGMAX("Ebel scanner finished");
interpret_core(ebel_ir, input_files);
// Cleanup
delete ebel_ir;
delete ebel_scanner;
delete ebel_text;
delete ebel_preproc;
LOGMAX("Interpretation done");
}
void compile_and_interpret(const char *f_in, const char *f_out, std::vector<const char *> input_files) {
LOGMAX("Compilation and interpretation started");
// Preprocessing
auto preproc = new Preprocessor();
LOGMAX("Text preprocessor started");
auto in_text = preproc->process(f_in);
auto out_text = preproc->process(f_out);
LOGMAX("Text preprocessor finished");
// Syntactical check
auto scanner = new TextFile::ScannerText();
LOGMAX("Text scanner started");
auto ir_in = scanner->process(in_text, f_in);
LOG1("Text IN IR:\n" << *ir_in);
auto ir_out = scanner->process(out_text, f_out);
LOG1("Text OUT IR:\n" << *ir_out);
LOGMAX("Text scanner finished");
std::stringstream ss;
Engine *engine = nullptr;
auto compiled = compile_core(ir_in, ir_out, ss, engine);
if(!Args::arg_opts.no_info_print) {
std::cerr << ss.str();
}
IR::EbelNode *ebel = compiled.first;
float precision = compiled.second;
if(!ebel) {
return;
}
// Saving the ebel if requested
std::chrono::duration<float> diff = std::chrono::steady_clock::now()-Args::arg_opts.start_time;
if(Args::arg_opts.ebel_out) {
std::string ebel_out = std::string(Args::arg_opts.ebel_out);
// Folder existence is checked in arg_parser
std::ofstream o_file(ebel_out);
o_file << *ebel;
o_file.close();
// Don't print if output will go to stdcout
if(!Args::arg_opts.no_info_print) {
std::cerr << "Ebel saved to '" << ebel_out << "'.";
}
}
else {
if(!Args::arg_opts.no_info_print) {
std::cerr << std::endl;
}
}
if(!Args::arg_opts.no_info_print) {
std::cerr << std::endl << "Best compiled program has " << (precision*100) << "% precision ("
<< std::fixed << std::setprecision(1) << diff.count()
<< " s)." << std::endl;
if(Args::arg_opts.interpret_out == nullptr) {
std::cerr << "#--- INTERPRETER OUTPUT: ---#" << std::endl;
}
}
LOG1("Best compiled program with " << (precision*100) << "% precision:\n" << *ebel);
// Interpreting
LOGMAX("Interpetation started");
// Preprocessing
interpret_core(ebel, input_files);
LOGMAX("Interpretation done");
// Cleanup
delete ir_in;
delete ir_out;
if(in_text != &std::cin) {
delete in_text;
}
if(out_text != &std::cin) {
delete out_text;
}
delete engine;
delete scanner;
delete preproc;
LOGMAX("Compilation and interpretation done");
}
// Main
int main(int argc, char *argv[]){
// Parse arguments (no need to make sure there are args because help is printed if argc is low)
Args::arg_opts.parse(argc-1, &argv[1]);
Args::arg_opts.start_timer();
// Inits
// Set logging level
Logger::get().set_logging_level(Args::arg_opts.logging_level);
Logger::get().set_flags(std::ios_base::boolalpha);
LOG1("Argument params: \n" << Args::arg_opts);
Analytics::get().set_flags(std::ios_base::boolalpha);
RNG::init(Args::arg_opts.seed);
// Start compilation of example input files
if(Args::arg_opts.execute_mode) {
compile_and_interpret(Args::arg_opts.file_in, Args::arg_opts.file_out, Args::arg_opts.int_files);
}
else if(Args::arg_opts.interpret_mode){
interpret(Args::arg_opts.ebel_in, Args::arg_opts.int_files);
}
else{
compile(Args::arg_opts.file_in, Args::arg_opts.file_out);
}
return 0;
}