4
Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,
6
https://bmstu.codes/lsx/simodo
9
#include "simodo/engine/utility/grammatize.h"
10
#include "simodo/inout/convert/functions.h"
11
#include "simodo/inout/format/fmt.h"
12
#include "simodo/parser/fuze/FuzeRdp.h"
13
#include "simodo/ast/generator/FormationFlow.h"
14
#include "simodo/interpret/builtins/hosts/fuze/FuzeAnalyzer.h"
15
#include "simodo/interpret/builtins/hosts/base/BaseAnalyzer.h"
16
#include "simodo/interpret/builtins/hosts/base/BaseRunning.h"
17
#include "simodo/interpret/builtins/modules/LexicalParametersModule.h"
18
#include "simodo/interpret/builtins/modules/AstFormationModule.h"
19
#include "simodo/interpret/SemanticOperationsEnumsLoader.h"
20
#include "simodo/interpret/Interpret.h"
21
#include "simodo/engine/utility/common_functions.h"
22
#include "simodo/engine/utility/generateDotFile.h"
23
#include "simodo/loom/Loom.h"
24
#include "simodo/variable/convert/Ast.h"
25
#include "simodo/variable/json/Serialization.h"
32
#if __cplusplus >= __cpp_2017
34
namespace fs = std::filesystem;
36
#include <experimental/filesystem>
37
namespace fs = std::filesystem::experimental;
40
namespace simodo::engine
45
std::chrono::time_point<std::chrono::steady_clock> _start;
48
LocalClock() = delete;
49
LocalClock(double & seconds) : _seconds(seconds)
51
_start = std::chrono::steady_clock::now();
56
_seconds = std::chrono::duration<double>(std::chrono::steady_clock::now() - _start).count();
60
void printRules(const parser::Grammar & g, const inout::uri_set_t & files, bool need_st_info, std::ostream & out)
62
out << inout::fmt("Правила грамматики:").str() << std::endl;
63
for(size_t i=0; i < g.rules.size(); ++i) {
64
char action = (g.rules[i].reduce_action.branches().empty()) ? ' ' : '*';
65
char direction = (g.rules[i].reduce_direction == parser::RuleReduceDirection::Undefined)
66
? ' ' : ((g.rules[i].reduce_direction == parser::RuleReduceDirection::LeftAssociative)
68
out << i << ": " << action << direction << "\t" << inout::toU8(g.rules[i].production) << "\t→ ";
70
for(const inout::Lexeme & lex : g.rules[i].pattern)
71
out << engine::getMnemonic(lex) << " ";
75
if (need_st_info && !g.rules[i].reduce_action.branches().empty())
76
engine::printSemanticTree(g.rules[i].reduce_action, files, 0, 2);
80
void printStateTransitions(const parser::Grammar & g, std::ostream & out)
82
out << inout::fmt("Состояния автомата разбора:").str() << std::endl;
83
for(size_t i=0; i < g.states.size(); ++i) {
84
out << "State [" << i << "]:" << std::endl;
86
const parser::FsmState_t & state = g.states[i];
88
for(const parser::FsmStatePosition & p : state) {
89
out << (p.is_main ? " M:" : " ") << "\t"
90
<< inout::toU8(g.rules[p.rule_no].production) << "\t→ ";
92
for(size_t j=0; j < g.rules[p.rule_no].pattern.size(); ++j) {
95
out << engine::getMnemonic(g.rules[p.rule_no].pattern[j]) << " ";
97
if (p.position == g.rules[p.rule_no].pattern.size())
100
if (!p.lookahead.empty()) {
103
for(const inout::Lexeme & lex : p.lookahead)
104
out << " " << engine::getMnemonic(lex);
107
out << "\t\t---- " << ((p.next_state_no > 0) ? "S" : "R")
108
<< ((p.next_state_no > 0) ? p.next_state_no : p.rule_no)
113
out << inout::fmt("Символы грамматики:").str() << std::endl;
114
for(size_t i=0; i < g.columns.size(); ++i)
115
out << i << ": " << engine::getMnemonic(g.columns[i]) << std::endl;
117
out << inout::fmt("Таблица разбора:").str();
119
size_t table_size = 0;
120
for(auto [fsm_key,fsm_value] : g.parse_table) {
121
size_t line = g.unpackFsmState(fsm_key);
122
size_t column = g.unpackFsmColumn(fsm_key);
123
parser::FsmActionType action = g.unpackFsmAction(fsm_value);
124
size_t location = g.unpackFsmLocation(fsm_value);
127
out << std::endl << "[" << line << "]:\t";
129
out << engine::getMnemonic(g.columns[column])
130
<< "(" << inout::toU8(parser::getFsmActionChar(action))
136
out << std::endl << inout::fmt("Количество элементов = ").str() << table_size << std::endl;
139
void createStateTransitionsGraph(const parser::Grammar & g,
140
const std::string & dot_file,
141
const std::string & grammar_file,
145
std::ofstream dot(dot_file);
149
// dot << "digraph \"" << grammar_file << "\" { rankdir=\"LR\";" << endl;
150
dot << "digraph \"" << grammar_file << "\" {" << std::endl;
151
for(size_t i=0; i < g.states.size(); ++i)
153
dot << "\tS" << i << " [shape=none,style=filled,margin=0,fontsize=12,fontname=Helvetica,labelfloat=false,labelloc=t,labeljust=l,label=<" << std::endl
154
<< "\t\t<TABLE BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"2\">" << std::endl
155
<< "\t\t<TR><TD COLSPAN=\"2\"><b>" << i << "</b></TD></TR>" << std::endl;
157
const parser::FsmState_t & state = g.states[i];
159
for(const parser::FsmStatePosition & p : state)
162
dot << "\t\t\t<TR><TD align='right'>" << (p.is_main ? "* " : "")
163
<< engine::toHtml(inout::toU8(g.rules[p.rule_no].production))
164
<< " →</TD><TD align='left'>";
166
for(size_t j=0; j < g.rules[p.rule_no].pattern.size(); ++j)
170
dot << engine::toHtml(engine::getMnemonic(g.rules[p.rule_no].pattern[j])) << " ";
172
if (p.position == g.rules[p.rule_no].pattern.size())
175
if (!p.lookahead.empty())
179
for(const inout::Lexeme & lex : p.lookahead)
180
dot << " " << engine::toHtml(engine::getMnemonic(lex));
183
dot << "</TD></TR>" << std::endl;
185
dot << "\t\t</TABLE>>];" << std::endl;
187
for(size_t i=0; i < g.states.size(); ++i)
189
const parser::FsmState_t & state = g.states[i];
191
for(size_t ip=0; ip < state.size(); ++ip)
193
const parser::FsmStatePosition & p = state[ip];
195
if (p.next_state_no > 0)
199
if (state[j].position < g.rules[state[j].rule_no].pattern.size() &&
200
p.position < g.rules[p.rule_no].pattern.size() &&
201
g.rules[state[j].rule_no].pattern[state[j].position] == g.rules[p.rule_no].pattern[p.position])
208
if (p.position < g.rules[p.rule_no].pattern.size())
209
label = engine::toHtml(engine::getMnemonic(g.rules[p.rule_no].pattern[p.position]));
210
dot << "\t\tS" << i << " -> " << "S" << p.next_state_no
211
<< " [fontsize=12,fontname=Helvetica,label=<" << label << ">];" << std::endl;
215
dot << "}" << std::endl;
219
out << inout::fmt("Создан DOT-файл '").str() << dot_file << "'" << std::endl;
222
out << inout::fmt("Не удалось записать в файл '").str() << dot_file << "'" << std::endl;
225
out << inout::fmt("Не удалось записать в файл '").str() << dot_file << "'" << std::endl;
228
bool grammatize(const std::string & grammar_file,
229
inout::InputStream_interface & in,
231
inout::Reporter_abstract & m,
232
const std::string & json_file_name,
233
const std::string & st_dot_file_name,
234
parser::TableBuildMethod grammar_builder_method,
235
const std::string & dot_file_name,
236
bool need_state_transitions_info,
237
bool need_rules_info,
239
bool need_time_intervals,
241
bool need_build_grammar,
242
bool need_load_grammar,
243
bool need_analyze_handles,
244
bool need_analyze_inserts,
245
parser::Grammar & grammar)
249
if (need_load_grammar) {
250
if (!parser::loadGrammarDump(grammar_file,grammar))
251
out << inout::fmt("Ошибка при загрузке дампа грамматики '").str() << grammar_file << "'" << std::endl;
252
else if (!need_silence) {
253
out << inout::fmt("Дамп грамматики успешно загружен").str() << std::endl;
254
out << inout::fmt("Грамматика '%1' построена методом %2")
256
.arg(getGrammarBuilderMethodName(grammar.build_method)).str()
261
double total_work_time = 0,
264
script_analyze_time = 0;
267
LocalClock total_work_clock(total_work_time);
269
ast::FormationFlow flow;
270
parser::FuzeRdp fuze(m, grammar_file, flow);
273
LocalClock parse_clock(parse_time);
281
LocalClock built_clock(built_time);
283
/// \todo Заменить interpret::Interpret на interpret::Interpret_interface!
284
interpret::Interpret inter(interpret::InterpretType::Analyzer, m, loom, flow.tree().files(), flow.tree().root(),
286
new interpret::builtins::FuzeAnalyzer(
288
fs::path(grammar_file).stem().string(),
290
grammar_builder_method),
293
loom.stretch(&inter);
296
grammar.files = flow.tree().files();
297
ok = !inter.errors();
302
out << inout::fmt("Грамматика '%1' построена методом %2")
304
.arg(getGrammarBuilderMethodName(grammar.build_method)).str()
308
LocalClock script_analyze_clock(script_analyze_time);
310
if (need_build_grammar || need_analyze_handles)
311
for(const auto & [name, node] : grammar.handlers)
312
if (name == u"lex") {
313
interpret::Interpret inter(interpret::InterpretType::Preview, m, loom, flow.tree().files(), node);
314
interpret::builtins::BaseInterpret_abstract *
315
/// \note Используется BaseRunning вместо BaseAnalyzer, чтобы заполнить grammar.lexical
316
runner = new interpret::builtins::BaseRunning(&inter);
317
std::shared_ptr<interpret::builtins::LexicalParametersModule>
318
lex = std::make_shared<interpret::builtins::LexicalParametersModule>
321
runner->importNamespace(u"lex", lex->instantiate(lex), inout::null_token_location);
322
inter.instantiateSemantics({runner});
323
loom.stretch(&inter);
327
if (need_analyze_inserts) {
329
variable::VariableSet_t
330
hosts_and_operations = interpret::loadSemanticOperationsEnums(fs::path(grammar_file).parent_path().string());
332
for(const auto & [name, node] : grammar.handlers)
333
if (name != u"lex") {
334
interpret::Interpret *
335
inter = new interpret::Interpret(interpret::InterpretType::Analyzer, m, loom, flow.tree().files(), node);
336
interpret::builtins::BaseAnalyzer *
337
astAnalyzer = new interpret::builtins::BaseAnalyzer(inter);
338
std::shared_ptr<interpret::builtins::AstFormationModule>
339
ast = std::make_shared<interpret::builtins::AstFormationModule>(hosts_and_operations);
341
astAnalyzer->importNamespace(u"ast", ast->instantiate(ast), inout::null_token_location);
342
inter->instantiateSemantics({astAnalyzer});
343
loom.dock(inter, true);
347
for(const parser::GrammarRule & r : grammar.rules) {
348
interpret::Interpret * inter = new interpret::Interpret(interpret::InterpretType::Analyzer, m, loom, flow.tree().files(), r.reduce_action);
349
interpret::builtins::BaseAnalyzer *
350
astAnalyzer = new interpret::builtins::BaseAnalyzer(inter);
351
std::shared_ptr<interpret::builtins::AstFormationModule>
352
ast = std::make_shared<interpret::builtins::AstFormationModule>(hosts_and_operations);
354
astAnalyzer->importNamespace(u"ast", ast->instantiate(ast), inout::null_token_location);
355
inter->instantiateSemantics({astAnalyzer});
356
loom.dock(inter, true);
364
if (need_build_grammar) {
365
if (!parser::saveGrammarDump(grammar_file,grammar))
366
out << inout::fmt("Ошибка при сохранении дампа грамматики '%1'").arg(grammar_file).str()
368
else if (!need_silence)
369
out << inout::fmt("Дамп грамматики успешно сохранён").str()
373
if (!st_dot_file_name.empty()) {
375
out << inout::fmt("Построение DOT-файла...").str()
377
engine::generateDotFile(st_dot_file_name, flow.tree().root(), {});
380
if (!json_file_name.empty()) {
382
out << inout::fmt("Сохранение JSON-файла...").str()
385
ok = variable::saveJson(variable::toValue(flow.tree()), json_file_name, false);
388
out << inout::fmt("Ошибка сохранения в '%1'").arg(json_file_name).str()
393
out << inout::fmt("При построении грамматики '%1' методом %2 возникли ошибки")
395
.arg(getGrammarBuilderMethodName(grammar_builder_method)).str()
398
else if (!need_silence)
399
out << inout::fmt("Синтаксический анализ описания грамматики '%1' выявил ошибки")
400
.arg(grammar_file).str()
404
if (need_time_intervals) {
405
out << inout::fmt("Время разбора грамматики:\t%1 ms").arg(parse_time*1000).str()
407
out << inout::fmt("Время построения таблиц:\t%1 ms").arg(built_time*1000).str()
409
out << inout::fmt("Время анализа скриптов:\t\t%1 ms").arg(script_analyze_time*1000).str()
411
out << inout::fmt("Общее время работы с грамматикой:\t%1 ms").arg(total_work_time*1000).str()
417
out << inout::fmt("Обработчики:").str() << std::endl;
418
for(const auto & [handler_name,handler_ast] : grammar.handlers) {
419
out << '\t' << inout::toU8(handler_name) << ":" << std::endl;
420
engine::printSemanticTree(handler_ast, grammar.files, 0, 2);
425
printRules(grammar, grammar.files, need_st_info, out);
427
if (need_state_transitions_info)
428
printStateTransitions(grammar, out);
430
if (!dot_file_name.empty())
431
createStateTransitionsGraph(grammar, dot_file_name, grammar_file, need_silence, out);