1
#include "FuzeDocumentOperation.h"
3
#include "ReporterWithReducibleSeverity.h"
4
#include "InputSupplier.h"
5
#include "simodo/inout/convert/functions.h"
6
#include "simodo/interpret/builtins/hosts/base/BaseAnalyzer.h"
7
#include "simodo/interpret/builtins/hosts/fuze/FuzeAnalyzer.h"
8
#include "simodo/interpret/builtins/modules/LexicalParametersModule.h"
9
#include "simodo/interpret/builtins/modules/AstFormationModule.h"
10
#include "simodo/inout/token/RefBufferStream.h"
12
#include "simodo/lsp-client/SimodoCommandResult.h"
13
#include "simodo/lsp-client/CompletionItemKind.h"
14
#include "simodo/lsp-client/LspEnums.h"
15
#include "simodo/utility/grammatize.h"
16
#include "simodo/inout/reporter/StreamReporter.h"
18
#include "simodo/interpret/SemanticOperationsEnumsLoader.h"
20
using namespace simodo;
22
FuzeDocumentOperation::FuzeDocumentOperation(lsp::DocumentContext & doc, const DocumentOperationFactory & factory, const std::string & languageId)
23
: ScriptDocumentOperation(doc, factory, languageId)
25
, _grammar_dir(factory.grammar_dir())
27
_hosts_and_operations = interpret::loadSemanticOperationsEnums(_grammar_dir);
30
bool FuzeDocumentOperation::analyze(const std::u16string & text, inout::Reporter_abstract & reporter)
32
ReporterWithReducibleSeverity m(reporter);
33
ast::FormationFlow flow;
34
InputSupplier input_supplier(_doc.server(), _doc.file_name(), text);
35
SemanticDataCollector data_collector;
36
parser::FuzeRdp fuze(m, _doc.file_name(), flow, input_supplier, data_collector);
37
inout::RefBufferStream buffer_stream(text.data());
39
bool ok = fuze.parse(buffer_stream);
41
_files = flow.tree().files();
44
interpret::builtins::FuzeAnalyzer * analyzer = nullptr;
46
_grammar = parser::Grammar();
49
interpret::Interpret inter(interpret::InterpretType::Analyzer, m, _loom, flow.tree().files(), flow.tree().root(),
51
analyzer = new interpret::builtins::FuzeAnalyzer(
54
fs::path(_doc.file_name()).stem().string(),
56
parser::TableBuildMethod::LR1
60
_loom.stretch(&inter);
63
analyzer->swapProductions(_productions);
64
analyzer->swapDirections(_directions);
69
for(const auto & [name, node] : _grammar.handlers)
71
interpret::Interpret * inter = new interpret::Interpret(interpret::InterpretType::Analyzer, m, _loom, flow.tree().files(), node);
72
interpret::builtins::BaseAnalyzer * lexAnalyzer = new interpret::builtins::BaseAnalyzer(inter, data_collector);
73
std::shared_ptr<interpret::builtins::LexicalParametersModule>
74
lex = std::make_shared<interpret::builtins::LexicalParametersModule>(_grammar.lexical);
76
lexAnalyzer->importNamespace(u"lex", lex->instantiate(lex), inout::null_token_location);
77
inter->instantiateSemantics({lexAnalyzer});
78
_loom.dock(inter, true);
80
/// \note Убрал параллельную обработку из-за нежелания контролировать гонку за data_collector
84
for(const parser::GrammarRule & r : _grammar.rules)
86
interpret::Interpret * inter = new interpret::Interpret(interpret::InterpretType::Analyzer, m, _loom, flow.tree().files(), r.reduce_action);
87
interpret::builtins::BaseAnalyzer * astAnalyzer = new interpret::builtins::BaseAnalyzer(inter, data_collector);
88
std::shared_ptr<interpret::builtins::AstFormationModule>
89
ast = std::make_shared<interpret::builtins::AstFormationModule>(_hosts_and_operations);
91
astAnalyzer->importNamespace(u"ast", ast->instantiate(ast), inout::null_token_location);
92
inter->instantiateSemantics({astAnalyzer});
93
_loom.dock(inter, true);
95
/// \note Убрал параллельную обработку из-за нежелания контролировать гонку за data_collector
99
/// \note Убрал параллельную обработку из-за нежелания контролировать гонку за data_collector
102
data_collector.swap(_semantic_data);
108
bool FuzeDocumentOperation::checkDependency(const std::string & uri) const
110
auto it = std::find(_files.begin(), _files.end(), uri);
111
if (it != _files.end())
114
return ScriptDocumentOperation::checkDependency(uri);
117
variable::Value FuzeDocumentOperation::produceSimodoCommandResponse(const std::u16string & command_name, std::u16string text) const
119
if (command_name.empty())
122
parser::TableBuildMethod method = parser::TableBuildMethod::none;
124
if (command_name == SLR_method_report)
125
method = parser::TableBuildMethod::SLR;
126
else if (command_name == LR1_method_report)
127
method = parser::TableBuildMethod::LR1;
129
return ScriptDocumentOperation::produceSimodoCommandResponse(command_name, text);
131
inout::RefBufferStream buffer_stream(text.data());
132
std::ostringstream out;
133
inout::StreamReporter m(out);
134
parser::Grammar grammar;
136
utility::grammatize( _doc.file_name(),
140
"", //json_file_name,
141
"", //st_dot_file_name,
142
method, // grammar_builder_method,
144
true, //need_state_transitions_info,
145
true, //need_rules_info,
146
false,//need_st_info,
147
false,//need_time_intervals,
148
true, //need_silence,
149
true, //need_build_grammar,
150
false,//need_load_grammar
151
true, //need_analyze_handles
152
false,//need_analyze_inserts
156
return variable::Object {{
157
{u"uri", inout::toU16(_doc.file_name())},
158
{u"commandResult", variable::Object {{
159
{u"id", u"grammar-report"},
160
{u"title", u"'" + fs::path(_doc.file_name()).stem().u16string() + u"' " + command_name},
161
{u"type", static_cast<int64_t>(lsp::SimodoCommandReportType::plainText)},
162
{u"text", inout::encodeSpecialChars(inout::toU16(out.str()))},
167
variable::Value FuzeDocumentOperation::produceHoverResponse(const lsp::Position & pos) const
169
for(const auto & [p_key, p_pair] : _productions) {
170
const auto & [prod, pattern] = p_pair;
171
if (prod.location().range().start().line() == pos.line()
172
&& prod.location().uri_index() == 0) {
173
if (prod.location().range().start().character() <= pos.character()
174
&& prod.location().range().end().character() > pos.character()) {
175
return variable::Object {{
176
{u"contents", variable::Object {{
177
{u"kind", u"plaintext"},
178
{u"value", inout::encodeSpecialChars(makeHover(prod))},
180
{u"range", _doc.makeRange(prod.location().range())},
184
for(const inout::Token & t : pattern)
185
if (t.location().range().start().line() == pos.line()
186
&& t.location().uri_index() == 0) {
187
if (t.location().range().start().character() <= pos.character()
188
&& t.location().range().end().character() > pos.character()) {
189
return variable::Object {{
190
{u"contents", variable::Object {{
191
{u"kind", u"plaintext"},
192
{u"value", inout::encodeSpecialChars(makeHover(t))},
194
{u"range", _doc.makeRange(t.location().range())},
200
for(const inout::Token & dt : _directions)
201
if (dt.location().range().start().line() == pos.line()
202
&& dt.location().uri_index() == 0
203
&& dt.location().range().start().character() <= pos.character()
204
&& dt.location().range().end().character() > pos.character())
205
return variable::Object {{
206
{u"contents", variable::Object {{
207
{u"kind", u"plaintext"},
208
{u"value", inout::encodeSpecialChars(dt.lexeme() + u" - управляющий символ предотвращения несогласованности грамматики типа R|S")},
210
{u"range", _doc.makeRange(dt.location().range())},
213
// for(const auto & [token, ref] : _refs)
214
// if (token.location().range().start().line() == pos.line()
215
// && token.location().uri_index() == 0
216
// && token.location().range().start().character() <= pos.character()
217
// && token.location().range().end().character() > pos.character())
218
// return variable::Object {{
219
// {u"contents", variable::Object {{
220
// {u"kind", u"plaintext"},
221
// {u"value", inout::encodeSpecialChars(ref)},
223
// {u"range", _doc.makeRange(token.location().range())},
226
return ScriptDocumentOperation::produceHoverResponse(pos);
229
variable::Value FuzeDocumentOperation::produceGotoDeclarationResponse(const lsp::Position & pos) const
231
return produceGotoDefinitionResponse(pos);
234
variable::Value FuzeDocumentOperation::produceGotoDefinitionResponse(const lsp::Position & pos) const
236
for(const auto & [p_key, p_pair] : _productions) {
237
const auto & [prod, pattern] = p_pair;
238
if (prod.location().range().start().line() == pos.line()
239
&& prod.location().uri_index() == 0) {
240
if (prod.location().range().start().character() <= pos.character()
241
&& prod.location().range().end().character() >= pos.character()) {
242
auto it = _productions.find(prod.lexeme());
243
if (it == _productions.end())
245
const auto & [t_prod, t_pattern] = it->second;
246
std::u16string uri = t_prod.location().uri_index() < _files.size()
247
? inout::toU16(_files[t_prod.location().uri_index()])
248
: inout::toU16(_doc.file_name());
249
return variable::Object {{
251
{u"range", _doc.makeRange(t_prod.location().range())},
255
for(const inout::Token & t : pattern)
256
if (t.location().range().start().line() == pos.line()
257
&& t.location().uri_index() == 0) {
258
if (t.location().range().start().character() <= pos.character()
259
&& t.location().range().end().character() >= pos.character()) {
260
auto it = _productions.find(t.lexeme());
261
if (it == _productions.end())
263
const auto & [t_prod, t_pattern] = it->second;
264
std::u16string uri = t_prod.location().uri_index() < _files.size()
265
? inout::toU16(_files[t_prod.location().uri_index()])
266
: inout::toU16(_doc.file_name());
267
return variable::Object {{
269
{u"range", _doc.makeRange(t_prod.location().range())},
275
// for(const auto & [t, ref] : _refs)
276
// if (t.location().range().start().line() == pos.line()
277
// && t.location().uri_index() == 0) {
278
// if (t.location().range().start().character() <= pos.character()
279
// && t.location().range().end().character() >= pos.character()) {
280
// return variable::Object {{
282
// {u"range", _doc.makeRange({0,0},{0,0})},
287
return ScriptDocumentOperation::produceGotoDefinitionResponse(pos);
290
variable::Value FuzeDocumentOperation::produceCompletionResponse(const lsp::CompletionParams & completionParams) const
294
_doc.server().log().debug("FuzeDocumentOperation::produceCompletionResponse: completionParams.position = ["
295
+ std::to_string(completionParams.position.line())
296
+ ", " + std::to_string(completionParams.position.character()) + "]");
298
for(const auto & [oped_scope, close_scope] : _semantic_data.scoped())
299
if (oped_scope.uri_index() == 0) {
300
_doc.server().log().debug("FuzeDocumentOperation::produceCompletionResponse: scope = ["
301
+ std::to_string(oped_scope.range().start().line())
302
+ ", " + std::to_string(oped_scope.range().start().character()) + "] - ["
303
+ std::to_string(close_scope.range().end().line())
304
+ ", " + std::to_string(close_scope.range().end().character()) + "]");
306
if (completionParams.position >= oped_scope.range().start()
307
&& completionParams.position <= close_scope.range().end()) {
308
_doc.server().log().debug("FuzeDocumentOperation::produceCompletionResponse: found!");
315
return ScriptDocumentOperation::produceCompletionResponse(completionParams);
317
std::vector<variable::Value> completion_items;
319
std::u16string last_production;
320
for(const auto & [key, value] : _productions)
321
if (last_production != key) {
322
completion_items.push_back(makeCompletionItem(key, u"production", u"", int64_t(lsp::CompletionItemKind::Variable)));
323
last_production = key;
326
std::vector<std::u16string> keywords {
327
u"main", u"include", u"remove",
329
for(const std::u16string & w : keywords)
330
completion_items.push_back(makeCompletionItem(w, u"keyword", u"", int64_t(lsp::CompletionItemKind::Keyword)));
332
return completion_items;
335
variable::Value FuzeDocumentOperation::produceSemanticTokensResponse() const
337
std::function<int64_t(inout::LexemeType)> makeType = [](inout::LexemeType type){
340
case inout::LexemeType::Compound:
342
case inout::LexemeType::Punctuation:
343
case inout::LexemeType::Annotation:
344
case inout::LexemeType::Id:
345
case inout::LexemeType::Number:
352
std::function<int64_t(inout::LexemeType)> makeModifiers = [](inout::LexemeType type){
353
int64_t modifiers = 0;
354
if (type == inout::LexemeType::Compound)
356
else if (type != inout::LexemeType::Punctuation)
361
struct TokenInfo { int64_t length, type, modifiers; };
363
bool operator() (const std::pair<int64_t,int64_t> & x1, const std::pair<int64_t,int64_t> & x2) const {
364
return x1.first < x2.first || (x1.first == x2.first && x1.second < x2.second);
368
std::map<std::pair<int64_t,int64_t>, TokenInfo, TokenComp> tokens;
370
for(const auto & [p_key, p_pair] : _productions) {
371
const auto & [prod, pattern] = p_pair;
372
if (prod.location().uri_index() == 0) {
373
int64_t line = static_cast<int64_t>(prod.location().range().start().line());
374
int64_t character = static_cast<int64_t>(prod.location().range().start().character());
375
int64_t length = static_cast<int64_t>(prod.location().range().end().character()-character);
376
int64_t modifiers = static_cast<int64_t>(3);
377
tokens.insert({ {line, character}, {length, 0, modifiers}});
379
for(const inout::Token & t : pattern) {
380
int64_t line = static_cast<int64_t>(t.location().range().start().line());
381
int64_t character = static_cast<int64_t>(t.location().range().start().character());
382
int64_t length = static_cast<int64_t>(t.location().range().end().character()-character);
383
int64_t modifiers = makeModifiers(t.type());
384
tokens.insert({ {line, character}, {length, makeType(t.type()), modifiers}});
389
for(const inout::Token & t : _directions)
390
if (t.location().uri_index() == 0) {
391
int64_t line = static_cast<int64_t>(t.location().range().start().line());
392
int64_t character = static_cast<int64_t>(t.location().range().start().character());
393
int64_t length = static_cast<int64_t>(t.location().range().end().character()-character);
394
int64_t modifiers = static_cast<int64_t>(2);
395
tokens.insert({ {line, character}, {length, 2, modifiers}});
398
// for(const auto & [t, ref] : _refs)
399
// if (t.location().uri_index() == 0) {
400
// int64_t line = static_cast<int64_t>(t.location().range().start().line());
401
// int64_t character = static_cast<int64_t>(t.location().range().start().character());
402
// int64_t length = static_cast<int64_t>(t.location().range().end().character()-character);
403
// int64_t modifiers = static_cast<int64_t>(1);
404
// tokens.insert({ {line, character}, {length, 7, modifiers}});
407
std::vector<variable::Value> sem_tokens;
409
for(const auto & [p,t] : tokens) {
410
sem_tokens.push_back(p.first);
411
sem_tokens.push_back(p.second);
412
sem_tokens.push_back(t.length);
413
sem_tokens.push_back(t.type);
414
sem_tokens.push_back(t.modifiers);
417
const variable::Value script_response_value = ScriptDocumentOperation::produceSemanticTokensResponse();
418
if (script_response_value.type() == variable::ValueType::Object) {
419
const variable::Value & script_response_data = script_response_value.getObject()->find(u"data");
420
if (script_response_data.type() == variable::ValueType::Array) {
421
const std::vector<variable::Value> & script_response_array = script_response_data.getArray()->values();
422
sem_tokens.insert(sem_tokens.end(), script_response_array.begin(), script_response_array.end());
426
return variable::Object {{{u"data", sem_tokens}}};
429
variable::Value FuzeDocumentOperation::produceDocumentSymbolsResponse() const
431
std::vector<variable::Value> doc_symbols;
432
std::multimap<uint64_t,std::pair<inout::Token, std::vector<inout::Token>>> prods_by_pos;
434
if (_productions.empty())
437
for(const auto & [p_key, p_pair] : _productions)
438
if (p_pair.first.location().uri_index() == 0)
439
prods_by_pos.insert({p_pair.first.location().range().start().line()*uint64_t(1000),p_pair});
441
inout::Token current_prod(inout::LexemeType::Empty, u"", inout::null_token_location);
442
inout::Position current_prod_end;
443
std::u16string current_pattern_str;
445
for(const inout::Token & t : prods_by_pos.begin()->second.second)
446
current_pattern_str += u" " + t.token();
448
for(const auto & [p_line, p_pair] : prods_by_pos) {
449
const auto & [prod, pattern] = p_pair;
451
if (prod.token() != current_prod.token()) {
452
if (current_prod.type() != inout::LexemeType::Empty) {
453
uint32_t end_line = prod.location().range().start().line();
454
uint32_t end_char = prod.location().range().start().character();
456
end_line = (end_char == 0) ? end_line - 1 : end_line;
457
end_char = (end_char == 0) ? 1000 : end_char - 1;
459
doc_symbols.push_back(variable::Object {{
460
{u"name", current_prod.token()},
461
{u"detail", current_prod.token() + u" =" + current_pattern_str},
462
{u"kind", int64_t(lsp::SymbolKind::Variable)},
463
{u"range", _doc.makeRange({current_prod.location().range().start(), inout::Position(end_line,end_char)})},
464
{u"selectionRange", _doc.makeRange(current_prod.location().range())},
468
current_pattern_str.clear();
471
if (!current_pattern_str.empty()) current_pattern_str += u" |";
472
for(const inout::Token & t : pattern) current_pattern_str += u" " + t.token();
474
current_prod_end = pattern.back().location().range().end();
477
doc_symbols.push_back(variable::Object {{
478
{u"name", current_prod.token()},
479
{u"detail", current_prod.token() + u" =" + current_pattern_str},
480
{u"kind", int64_t(lsp::SymbolKind::Variable)},
481
{u"range", _doc.makeRange({current_prod.location().range().start(), inout::Position(current_prod_end.line()+1000,0)})},
482
{u"selectionRange", _doc.makeRange(current_prod.location().range())},
485
// \todo Использование ScriptDocumentOperation::produceDocumentSymbolsResponse кидает ассерт, так как тот код не предназначен для семантики fuze:
486
// при обработке `lex { ... }` getFunctionBodyRange, кажется, иногда не может найти закрывающую скобку,
487
// но в целом мне вообще не понятно, для чего использовать DocumentSymbols из ScriptDocumentOperation для fuze
488
// variable::Value script_response_value = ScriptDocumentOperation::produceDocumentSymbolsResponse();
489
// if (script_response_value.type() == variable::ValueType::Array) {
490
// const std::vector<variable::Value> & array = script_response_value.getArray()->values();
491
// doc_symbols.insert(doc_symbols.end(), array.begin(), array.end());
497
std::u16string FuzeDocumentOperation::makeHover(const inout::Token & t) const
499
std::u16string text = t.lexeme();
500
auto range = _productions.equal_range(text);
502
if (range.first == range.second)
503
text += u" - терминальный символ грамматики";
505
for(auto it = range.first; it != range.second; ++it) {
507
for(const inout::Token & t : it->second.second)
508
text += u" " + t.token();