4
Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,
6
https://bmstu.codes/lsx/simodo/loom
9
#include "simodo/interpret/builtins/hosts/fuze/ParsingTableBuilder_abstract.h"
11
#include "simodo/inout/convert/functions.h"
12
#include "simodo/inout/format/fmt.h"
17
namespace simodo::interpret::builtins
19
bool ParsingTableBuilder_abstract::build(const std::vector<parser::GrammarRuleTokens> &rules, const inout::Token &main_rule)
23
_m.reportError( inout::null_location,
24
inout::fmt("Не заданы правила грамматики '%1'")
29
inout::Token main_production = (main_rule.token().empty() ? rules[0].production : main_rule);
31
// Добавляем обобщённое правило
32
std::vector<inout::Token> pattern {
33
{ inout::LexemeType::Compound, main_production.token(), main_production.location() },
34
{ inout::LexemeType::Empty, u"", main_production.location() },
36
_rules.push_back({ inout::Token { inout::LexemeType::Compound, main_production.token()+u"'", main_production.location() },
39
parser::RuleReduceDirection::Undefined });
41
// Вытягиваем из заданных правил грамматики только те, что относятся к основному правилу
42
if(!extractRules(rules, main_production.token()))
45
if (_rules.size() <= 1)
47
_m.reportError( main_production.makeLocation(_files),
48
inout::fmt("Не удалось извлечь правила грамматики '%1'")
53
// Заполняем индекс продукций для правил грамматики
54
for(size_t i=0; i < _rules.size(); ++i)
55
_production_index.emplace(_rules[i].production.token(),i);
57
// Заполняем индекс символов из образца для правил грамматики
58
for(size_t i=0; i < _rules.size(); ++i)
59
for(const inout::Lexeme & s : _rules[i].pattern)
60
if (s.type() == inout::LexemeType::Compound)
62
// Повторы пар (s, индекс на правило) нам не нужны - проверяем на наличие
63
auto range = _pattern_index.equal_range(s.lexeme());
64
auto & it = range.first;
65
for(; it != range.second; ++it)
69
if (it == range.second)
70
_pattern_index.emplace(s.lexeme(),i);
73
// Проверяем какие правила не были собраны (не используются)
74
if (_rules.size() <= rules.size())
75
for(const parser::GrammarRuleTokens & r : rules)
76
if (_production_index.end() == _production_index.find(r.production.token()))
77
_m.reportWarning(r.production.makeLocation(_files),
78
inout::fmt("Нетерминал '%1' не используется в грамматике '%2'")
79
.arg(r.production.token())
82
// Проверяем, что все нетерминалы имеют продукции
84
for(const auto & [p,i] : _pattern_index)
89
if (_production_index.find(p) == _production_index.end())
91
assert(i < _rules.size());
93
for(; j < _rules[i].pattern.size(); ++j)
94
if (p == _rules[i].pattern[j].token())
97
inout::Token t = (j < _rules[i].pattern.size()) ? _rules[i].pattern[j] : _rules[i].production;
99
_m.reportError(t.makeLocation(_files),
100
inout::fmt("Нетерминал '%1' не имеет продукции для грамматики '%2'")
102
.arg(_grammar_name));
111
// Копируем _rules в _g.rules для работы наследуемого кода (всё равно нужно копировать)
112
for(const parser::GrammarRuleTokens & r : _rules)
114
std::vector<inout::Lexeme> p;
116
for(const inout::Token & t : r.pattern)
119
_g.rules.emplace_back(r.production.token(), p, r.reduce_action, r.reduce_direction);
122
// Заполняем перечень состояний автомата разбора
125
// Вычисляем границу для значений таблицы разбора
126
assert(std::max(_g.rules.size(),_g.states.size()) < UINT32_MAX/5);
127
_g.value_bound = static_cast<parser::Fsm_value_t>(std::max(_g.rules.size(),_g.states.size())) + 1;
129
// Заполняем значения колонок
132
// Определяем "вычисляемые" параметры лексики
133
fillLexemeParameters(_g);
135
// Вычисляем границу для ключа таблицы разбора
136
assert(_g.columns.size()*_g.states.size() < UINT32_MAX/2);
137
_g.key_bound = static_cast<parser::Fsm_key_t>(_g.columns.size()) + 1;
139
// Формируем таблицу разбора
140
if (!fillParseTable())
143
// Проверяем полноту наполнения
144
return checkCompleteness();
147
bool ParsingTableBuilder_abstract::extractRules(const std::vector<parser::GrammarRuleTokens> &rules, std::u16string production)
152
_rules.reserve(rules.size());
154
while(!production.empty())
156
// Вытягиваем все связанные с заданной продукцией правила
157
for(const parser::GrammarRuleTokens & r : rules)
158
if (r.production.token() == production)
160
// Убеждаемся, что это правило не дубль
162
for(; i < _rules.size(); ++i)
164
if (_rules[i].production.token() != production)
167
if (_rules[i].pattern.size() != r.pattern.size())
171
for(; j < _rules[i].pattern.size(); ++j)
172
if (_rules[i].pattern[j] != r.pattern[j])
175
if (j == _rules[i].pattern.size())
178
if (i != _rules.size())
181
// Добавляем правило в грамматику
187
// Специально избавляемся от рекурсии, чтобы упорядочить перечень правил по продукциям
192
if (patt_no == _rules[rule_no].pattern.size())
196
if (rule_no == _rules.size())
202
assert(!_rules[rule_no].pattern.empty());
204
if (_rules[rule_no].pattern[patt_no].type() == inout::LexemeType::Compound)
206
// Сначала убеждаемся, что этого нетерминала нет в нашем списке (оптимизируем)
207
const std::u16string & att_prod = _rules[rule_no].pattern[patt_no].lexeme();
210
for(; i < _rules.size(); ++i)
211
if (_rules[i].production.token() == att_prod)
214
// Не нашли, значит обрабатываем
215
if (i == _rules.size())
216
production = att_prod;
219
while(production.empty());
225
void ParsingTableBuilder_abstract::fillColumns() const
227
// Сначала заполняются терминальные символы грамматики
228
for(const parser::GrammarRule & r : _g.rules)
229
for(const inout::Lexeme & s : r.pattern)
230
if (s.type() != inout::LexemeType::Compound)
231
if (find(_g.columns.begin(),_g.columns.end(),s) == _g.columns.end())
232
_g.columns.push_back(s);
234
// Затем идут нетерминалы
235
_g.first_compound_index = _g.columns.size();
237
for(const parser::GrammarRule &r : _g.rules)
238
for(const inout::Lexeme & s : r.pattern)
239
if (s.type() == inout::LexemeType::Compound)
240
if (find(_g.columns.begin(),_g.columns.end(),s) == _g.columns.end())
241
_g.columns.push_back(s);
244
bool ParsingTableBuilder_abstract::checkCompleteness()
248
// Проверяем, что все приведения попали в таблицу
249
// (правило 0 не должно участвовать)
250
for(size_t i=1; i < _g.rules.size(); ++i)
253
for(const auto & [key,value] : _g.parse_table)
255
parser::FsmActionType action = _g.unpackFsmAction(value);
256
size_t location = _g.unpackFsmLocation(value);
258
if (action == parser::FsmActionType::Reduce)
267
assert(i < _rules.size());
268
_m.reportFatal( _rules[i].production.makeLocation(_files),
269
inout::fmt("Правило %1 грамматики '%2' не участвует в разборе.")
271
.arg(_grammar_name));
276
return ok || !_strict_rule_consistency;