loom
181 строка · 7.3 Кб
1/*
2MIT License
3
4Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,
5
6https://bmstu.codes/lsx/simodo/loom
7*/
8
9#include "simodo/variable/Module_interface.h"
10#include "simodo/variable/VariableSetWrapper.h"
11#include "simodo/inout/convert/functions.h"
12
13#include <memory>
14#include <filesystem>
15#include <cassert>
16
17#ifdef CROSS_WIN
18// MinGW related workaround
19#define BOOST_DLL_FORCE_ALIAS_INSTANTIATION
20#endif
21
22#include <boost/dll/alias.hpp>
23
24using namespace simodo;
25using namespace simodo::variable;
26using namespace simodo::inout;
27
28namespace fs = std::filesystem;
29
30namespace
31{
32Value setup(Module_interface * host, const VariableSetWrapper & args);
33Value produceTokens(Module_interface * host, const VariableSetWrapper & args);
34}
35
36class MainTokenizer : public Module_interface
37{
38// ModuleFactory_interface * _factory;
39
40public:
41// MainTokenizer(ModuleFactory_interface * factory) : _factory(factory) {}
42
43Value setup(const std::string & path_to_data, const std::string & language);
44Value produceTokens(const std::u16string & text_to_parse, int position, context_index_t context);
45
46virtual version_t version() const override { return lib_version(); }
47
48virtual Object instantiate(std::shared_ptr<variable::Module_interface> module_object) override
49{
50return Object {{
51// {u"version", u"0.1"},
52{u"specialization", u"Comment"},
53{u"setup", {ValueType::Function, Object {{
54{u"@", ExternalFunction {module_object, ::setup}},
55{{}, ValueType::String},
56{u"path_to_data", ValueType::String},
57{u"language", ValueType::String},
58}}}},
59{u"produceTokens", {ValueType::Function, Object {{
60{u"@", ExternalFunction {module_object, ::produceTokens}},
61{{}, ValueType::Object},
62{u"text_to_parse", ValueType::String},
63{u"position", ValueType::Int},
64{u"context", ValueType::Int},
65}}}},
66}};
67}
68
69// virtual ModuleFactory_interface * factory() override { return _factory; }
70
71// Factory method
72static std::shared_ptr<Module_interface> create() {
73return std::make_shared<MainTokenizer>();
74}
75};
76
77BOOST_DLL_ALIAS(
78MainTokenizer::create, // <-- this function is exported with...
79create_simodo_module // <-- ...this alias name
80)
81
82namespace
83{
84Value setup(Module_interface * host, const VariableSetWrapper & args)
85{
86// Эти условия должны проверяться в вызывающем коде и при необходимости выполняться преобразования
87assert(host != nullptr);
88assert(args.size() == 2);
89assert(args[0].value().type() == ValueType::String);
90assert(args[1].value().type() == ValueType::String);
91
92MainTokenizer * main = static_cast<MainTokenizer *>(host);
93return main->setup(toU8(args[0].value().getString()),
94toU8(args[1].value().getString()));
95}
96
97Value produceTokens(Module_interface * host, const VariableSetWrapper & args)
98{
99// Эти условия должны проверяться в вызывающем коде и при необходимости выполняться преобразования
100assert(host != nullptr);
101assert(args.size() == 3);
102assert(args[0].value().type() == ValueType::String);
103assert(args[1].value().type() == ValueType::Int);
104assert(args[2].value().type() == ValueType::Int);
105
106MainTokenizer * main = static_cast<MainTokenizer *>(host);
107context_index_t context = NO_TOKEN_CONTEXT_INDEX;
108if (args[2].value().getInt() >= 0 )
109context = static_cast<context_index_t>(args[2].value().getInt());
110
111return main->produceTokens(args[0].value().getString(), args[1].value().getInt(), context);
112}
113
114}
115
116Value MainTokenizer::setup(const std::string & /*path_to_data*/, const std::string & language)
117{
118/// @todo Дополнить список поддерживаемых языков
119/// @todo Вынести перечень поддерживаемых языков в JSON-файл.
120using namespace std::literals;
121for(const std::string & ext : {"cpp"s, "java"s})
122if (ext == language)
123return u""; // ОК
124
125return {}; // Не ОК
126}
127
128Value MainTokenizer::produceTokens(const std::u16string & text_to_parse, int position, context_index_t )
129{
130/// @todo Нужно проверять только DOXYGEN-комментарии.
131/// Не забыть учесть разрывы комментариев на несколько строк.
132
133/// @todo Нужно дополнить список ключевых слов DOXYGEN.
134/// @todo Нужно вынести ключевые слова в JSON-файл. Для этого использовать метод setup.
135std::vector<std::u16string> doxy_keys {
136u"brief", u"todo", u"details", u"attention", u"note", u"file", u"author", u"version",
137u"date", u"copyright", u"param", u"return", u"deprecated",
138};
139
140std::u16string::size_type pos;
141for(pos=0; pos < text_to_parse.size(); ++pos)
142if (text_to_parse[pos] == u'\\' || text_to_parse[pos] == u'@') {
143++pos;
144break;
145}
146
147/// @todo Вообще-то, нужно анализировать не только ключевые слова, но и элементы форматирования.
148/// Хотя, вероятно, это будет задача для отдельного токенайзера.
149
150Array tokens;
151while(pos < text_to_parse.size()) {
152for(const std::u16string & key : doxy_keys)
153/// @todo Сейчас проверяется только нижний регистр. Нужно уточнить: является ли
154/// DOXYGEN регистрозависимым.
155if (text_to_parse.substr(pos,key.size()) == key) {
156/// @todo Вообще-то, нужно весь комментарий DOXYGEN выделять, как документ,
157/// а не как обычный комментарий (у простого комментария и документа семантика разная).
158/// Сейчас подсвечиваются только ключевые слова.
159/// Но для этого нужно выделять только DOXYGEN-комментарии (см. todo выше).
160Object token_data {{
161{u"token", text_to_parse.substr(pos-1, key.size()+1)},
162{u"type", u"Doxygen-keyword"},
163{u"position", position + static_cast<int64_t>(pos-1)},
164}};
165tokens.values().emplace_back(token_data);
166pos += key.size();
167break;
168}
169
170/// @todo Дублирование кода (см. выше), нужно ли убирать?
171for(; pos < text_to_parse.size(); ++pos)
172if (text_to_parse[pos] == u'\\' || text_to_parse[pos] == u'@') {
173++pos;
174break;
175}
176}
177
178return Object {{
179{u"tokens", tokens},
180}};
181}
182
183