2016-11-18 16:14:05 -08:00
|
|
|
#include "compiler/build_tables/build_parse_table.h"
|
2015-07-08 19:18:59 -07:00
|
|
|
#include <algorithm>
|
2014-05-04 22:07:52 -07:00
|
|
|
#include <map>
|
|
|
|
|
#include <set>
|
2014-11-05 18:39:50 -08:00
|
|
|
#include <string>
|
2014-05-04 22:07:52 -07:00
|
|
|
#include <unordered_map>
|
2014-10-12 12:56:04 -07:00
|
|
|
#include <utility>
|
2014-11-05 18:39:50 -08:00
|
|
|
#include "compiler/parse_table.h"
|
2014-10-12 12:56:04 -07:00
|
|
|
#include "compiler/build_tables/parse_item.h"
|
2016-11-10 10:25:32 -08:00
|
|
|
#include "compiler/build_tables/parse_item_set_builder.h"
|
2015-01-12 23:01:52 -08:00
|
|
|
#include "compiler/lexical_grammar.h"
|
|
|
|
|
#include "compiler/syntax_grammar.h"
|
2017-03-17 12:52:01 -07:00
|
|
|
#include "compiler/rule.h"
|
2017-03-06 09:47:00 -08:00
|
|
|
#include "compiler/build_tables/lex_table_builder.h"
|
2014-07-01 20:47:35 -07:00
|
|
|
|
2014-05-04 22:07:52 -07:00
|
|
|
namespace tree_sitter {
|
2014-07-20 21:43:27 -07:00
|
|
|
namespace build_tables {
|
|
|
|
|
|
2015-07-08 19:18:59 -07:00
|
|
|
using std::find;
|
2014-07-20 21:43:27 -07:00
|
|
|
using std::pair;
|
|
|
|
|
using std::vector;
|
|
|
|
|
using std::set;
|
|
|
|
|
using std::map;
|
2014-11-05 18:39:50 -08:00
|
|
|
using std::string;
|
2015-06-28 16:22:31 -05:00
|
|
|
using std::to_string;
|
2014-07-20 21:43:27 -07:00
|
|
|
using std::unordered_map;
|
2016-11-17 17:29:10 -08:00
|
|
|
using rules::Associativity;
|
2014-07-20 21:43:27 -07:00
|
|
|
using rules::Symbol;
|
2016-11-09 20:47:47 -08:00
|
|
|
using rules::END_OF_INPUT;
|
2014-07-20 21:43:27 -07:00
|
|
|
|
|
|
|
|
class ParseTableBuilder {
|
|
|
|
|
const SyntaxGrammar grammar;
|
2015-06-28 16:22:31 -05:00
|
|
|
const LexicalGrammar lexical_grammar;
|
2016-05-09 14:31:44 -07:00
|
|
|
unordered_map<Symbol, ParseItemSet> recovery_states;
|
2016-11-16 11:46:22 -08:00
|
|
|
unordered_map<ParseItemSet, ParseStateId> parse_state_ids;
|
2014-10-12 11:50:36 -07:00
|
|
|
vector<pair<ParseItemSet, ParseStateId>> item_sets_to_process;
|
2014-07-20 21:43:27 -07:00
|
|
|
ParseTable parse_table;
|
2016-11-09 20:47:47 -08:00
|
|
|
set<string> conflicts;
|
2016-11-10 10:25:32 -08:00
|
|
|
ParseItemSetBuilder item_set_builder;
|
2016-11-09 20:47:47 -08:00
|
|
|
set<const Production *> fragile_productions;
|
2017-03-17 16:31:29 -07:00
|
|
|
vector<set<Symbol>> incompatible_tokens_by_index;
|
2017-07-07 13:59:57 -07:00
|
|
|
bool processing_recovery_states;
|
2014-07-20 21:43:27 -07:00
|
|
|
|
2014-10-12 12:56:04 -07:00
|
|
|
public:
|
2017-07-07 13:59:57 -07:00
|
|
|
ParseTableBuilder(const SyntaxGrammar &grammar, const LexicalGrammar &lex_grammar)
|
|
|
|
|
: grammar(grammar),
|
|
|
|
|
lexical_grammar(lex_grammar),
|
|
|
|
|
item_set_builder(grammar, lex_grammar),
|
|
|
|
|
processing_recovery_states(false) {}
|
2014-10-12 12:56:04 -07:00
|
|
|
|
2016-01-10 20:04:41 -08:00
|
|
|
pair<ParseTable, CompileError> build() {
|
2016-11-30 09:34:47 -08:00
|
|
|
Symbol start_symbol = grammar.variables.empty() ?
|
2017-03-17 12:52:01 -07:00
|
|
|
Symbol::terminal(0) :
|
|
|
|
|
Symbol::non_terminal(0);
|
2017-07-07 13:59:57 -07:00
|
|
|
Production start_production{{{start_symbol, 0, rules::AssociativityNone}}, 0};
|
2016-11-30 09:34:47 -08:00
|
|
|
|
2017-07-07 13:59:57 -07:00
|
|
|
ParseStateId error_state_id = add_parse_state(ParseItemSet());
|
2015-10-03 22:36:04 -07:00
|
|
|
add_parse_state(ParseItemSet({
|
2015-10-04 21:33:54 -07:00
|
|
|
{
|
2015-10-17 22:54:56 -07:00
|
|
|
ParseItem(rules::START(), start_production, 0),
|
2017-07-07 13:59:57 -07:00
|
|
|
LookaheadSet({END_OF_INPUT()}),
|
2015-10-04 21:33:54 -07:00
|
|
|
},
|
2015-10-03 22:36:04 -07:00
|
|
|
}));
|
2014-10-12 12:56:04 -07:00
|
|
|
|
2016-02-12 23:44:05 -08:00
|
|
|
CompileError error = process_part_state_queue();
|
2017-07-07 13:59:57 -07:00
|
|
|
if (error.type != TSCompileErrorTypeNone) return {parse_table, error};
|
2014-10-12 12:56:04 -07:00
|
|
|
|
2017-03-06 09:47:00 -08:00
|
|
|
compute_unmergable_token_pairs();
|
2016-10-18 15:18:33 -07:00
|
|
|
|
2017-07-07 13:59:57 -07:00
|
|
|
processing_recovery_states = true;
|
|
|
|
|
build_error_parse_state(error_state_id);
|
2016-02-12 23:44:05 -08:00
|
|
|
process_part_state_queue();
|
|
|
|
|
|
2015-12-17 12:48:55 -08:00
|
|
|
mark_fragile_actions();
|
2015-12-20 15:26:35 -08:00
|
|
|
remove_duplicate_parse_states();
|
2017-07-07 13:59:57 -07:00
|
|
|
return {parse_table, CompileError::none()};
|
2014-10-12 12:56:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2016-02-12 23:44:05 -08:00
|
|
|
CompileError process_part_state_queue() {
|
|
|
|
|
while (!item_sets_to_process.empty()) {
|
|
|
|
|
auto pair = item_sets_to_process.back();
|
2016-11-10 10:25:32 -08:00
|
|
|
ParseItemSet &item_set = pair.first;
|
2016-02-12 23:44:05 -08:00
|
|
|
ParseStateId state_id = pair.second;
|
|
|
|
|
item_sets_to_process.pop_back();
|
|
|
|
|
|
2016-11-10 10:25:32 -08:00
|
|
|
item_set_builder.apply_transitive_closure(&item_set);
|
2016-11-17 17:29:10 -08:00
|
|
|
string conflict = add_actions(item_set, state_id);
|
2016-02-12 23:44:05 -08:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
if (!conflict.empty()) {
|
|
|
|
|
return CompileError(TSCompileErrorTypeParseConflict, conflict);
|
2016-02-12 23:44:05 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CompileError::none();
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-07 13:59:57 -07:00
|
|
|
void build_error_parse_state(ParseStateId state_id) {
|
2016-06-27 14:07:47 -07:00
|
|
|
ParseState error_state;
|
|
|
|
|
|
2017-03-17 16:31:29 -07:00
|
|
|
for (unsigned i = 0; i < lexical_grammar.variables.size(); i++) {
|
|
|
|
|
Symbol token = Symbol::terminal(i);
|
2017-03-06 09:47:00 -08:00
|
|
|
bool has_non_reciprocal_conflict = false;
|
2017-03-17 16:31:29 -07:00
|
|
|
|
|
|
|
|
for (Symbol incompatible_token : incompatible_tokens_by_index[i]) {
|
|
|
|
|
if (incompatible_token.is_terminal() &&
|
|
|
|
|
!incompatible_tokens_by_index[incompatible_token.index].count(token)) {
|
2017-03-06 09:47:00 -08:00
|
|
|
has_non_reciprocal_conflict = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!has_non_reciprocal_conflict) {
|
2017-03-17 12:52:01 -07:00
|
|
|
add_out_of_context_parse_state(&error_state, Symbol::terminal(i));
|
2017-03-06 09:47:00 -08:00
|
|
|
}
|
2016-11-09 20:47:47 -08:00
|
|
|
}
|
2016-03-02 20:45:13 -08:00
|
|
|
|
2016-11-09 20:47:47 -08:00
|
|
|
for (const Symbol &symbol : grammar.extra_tokens) {
|
2016-11-30 09:34:47 -08:00
|
|
|
if (!error_state.terminal_entries.count(symbol)) {
|
|
|
|
|
error_state.terminal_entries[symbol].actions.push_back(ParseAction::ShiftExtra());
|
2016-11-09 20:47:47 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-06-18 20:35:33 -07:00
|
|
|
|
2016-12-05 11:50:24 -08:00
|
|
|
for (size_t i = 0; i < grammar.external_tokens.size(); i++) {
|
2017-03-17 12:52:01 -07:00
|
|
|
add_out_of_context_parse_state(&error_state, Symbol::external(i));
|
2016-12-05 11:50:24 -08:00
|
|
|
}
|
|
|
|
|
|
2016-03-02 20:45:13 -08:00
|
|
|
for (size_t i = 0; i < grammar.variables.size(); i++) {
|
2017-03-17 12:52:01 -07:00
|
|
|
add_out_of_context_parse_state(&error_state, Symbol::non_terminal(i));
|
2016-03-02 20:45:13 -08:00
|
|
|
}
|
2016-04-25 21:59:40 -07:00
|
|
|
|
2016-11-30 09:34:47 -08:00
|
|
|
error_state.terminal_entries[END_OF_INPUT()].actions.push_back(ParseAction::Recover(0));
|
2017-07-07 13:59:57 -07:00
|
|
|
parse_table.states[state_id] = error_state;
|
2016-03-02 20:45:13 -08:00
|
|
|
}
|
|
|
|
|
|
2016-06-27 14:07:47 -07:00
|
|
|
void add_out_of_context_parse_state(ParseState *error_state,
|
|
|
|
|
const rules::Symbol &symbol) {
|
2016-05-09 14:31:44 -07:00
|
|
|
const ParseItemSet &item_set = recovery_states[symbol];
|
2016-04-25 21:59:40 -07:00
|
|
|
if (!item_set.entries.empty()) {
|
|
|
|
|
ParseStateId state = add_parse_state(item_set);
|
2016-11-30 09:34:47 -08:00
|
|
|
if (symbol.is_non_terminal()) {
|
2016-11-14 08:36:06 -08:00
|
|
|
error_state->nonterminal_entries[symbol.index] = state;
|
2016-11-30 09:34:47 -08:00
|
|
|
} else {
|
|
|
|
|
error_state->terminal_entries[symbol].actions.assign({ ParseAction::Recover(state) });
|
2016-11-14 08:36:06 -08:00
|
|
|
}
|
2016-04-25 21:59:40 -07:00
|
|
|
}
|
2016-02-12 23:44:05 -08:00
|
|
|
}
|
|
|
|
|
|
2014-07-20 21:43:27 -07:00
|
|
|
ParseStateId add_parse_state(const ParseItemSet &item_set) {
|
|
|
|
|
auto pair = parse_state_ids.find(item_set);
|
|
|
|
|
if (pair == parse_state_ids.end()) {
|
2017-03-01 22:15:26 -08:00
|
|
|
ParseStateId state_id = parse_table.states.size();
|
|
|
|
|
parse_table.states.push_back(ParseState());
|
2014-07-20 21:43:27 -07:00
|
|
|
parse_state_ids[item_set] = state_id;
|
2016-11-16 10:21:30 -08:00
|
|
|
parse_table.states[state_id].shift_actions_signature = item_set.unfinished_item_signature();
|
2016-11-10 10:25:32 -08:00
|
|
|
item_sets_to_process.push_back({ std::move(item_set), state_id });
|
2014-07-20 21:43:27 -07:00
|
|
|
return state_id;
|
|
|
|
|
} else {
|
|
|
|
|
return pair->second;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
string add_actions(const ParseItemSet &item_set, ParseStateId state_id) {
|
2016-11-30 09:34:47 -08:00
|
|
|
map<Symbol, ParseItemSet> terminal_successors;
|
2016-11-17 17:29:10 -08:00
|
|
|
map<Symbol::Index, ParseItemSet> nonterminal_successors;
|
2016-11-30 09:34:47 -08:00
|
|
|
set<Symbol> lookaheads_with_conflicts;
|
2014-10-12 12:56:04 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
for (const auto &pair : item_set.entries) {
|
|
|
|
|
const ParseItem &item = pair.first;
|
|
|
|
|
const LookaheadSet &lookahead_symbols = pair.second;
|
2016-05-09 14:31:44 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
// If the item is finished, immediately add a Reduce or Accept action to
|
|
|
|
|
// the parse table for each of its lookahead terminals.
|
|
|
|
|
if (item.is_done()) {
|
|
|
|
|
ParseAction action = (item.lhs() == rules::START()) ?
|
|
|
|
|
ParseAction::Accept() :
|
|
|
|
|
ParseAction::Reduce(item.lhs(), item.step_index, *item.production);
|
|
|
|
|
|
|
|
|
|
int precedence = item.precedence();
|
2016-11-30 09:34:47 -08:00
|
|
|
for (Symbol lookahead : *lookahead_symbols.entries) {
|
2016-11-17 17:29:10 -08:00
|
|
|
ParseTableEntry &entry = parse_table.states[state_id].terminal_entries[lookahead];
|
|
|
|
|
|
2016-11-18 16:14:05 -08:00
|
|
|
// Only add the highest-precedence Reduce actions to the parse table.
|
|
|
|
|
// If other lower-precedence actions are possible, ignore them.
|
2016-11-17 17:29:10 -08:00
|
|
|
if (entry.actions.empty()) {
|
|
|
|
|
parse_table.add_terminal_action(state_id, lookahead, action);
|
|
|
|
|
} else {
|
|
|
|
|
ParseAction &existing_action = entry.actions[0];
|
2017-07-07 13:59:57 -07:00
|
|
|
if (existing_action.type == ParseActionTypeAccept || processing_recovery_states) {
|
2016-11-17 17:29:10 -08:00
|
|
|
entry.actions.push_back(action);
|
|
|
|
|
} else {
|
|
|
|
|
int existing_precedence = existing_action.precedence();
|
|
|
|
|
if (precedence > existing_precedence) {
|
|
|
|
|
for (const ParseAction &old_action : entry.actions)
|
|
|
|
|
fragile_productions.insert(old_action.production);
|
|
|
|
|
entry.actions.clear();
|
|
|
|
|
entry.actions.push_back(action);
|
2016-11-18 16:14:05 -08:00
|
|
|
lookaheads_with_conflicts.erase(lookahead);
|
2016-11-17 17:29:10 -08:00
|
|
|
} else if (precedence == existing_precedence) {
|
|
|
|
|
entry.actions.push_back(action);
|
2016-11-18 16:14:05 -08:00
|
|
|
lookaheads_with_conflicts.insert(lookahead);
|
2016-11-17 17:29:10 -08:00
|
|
|
} else {
|
|
|
|
|
fragile_productions.insert(item.production);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-14 08:36:06 -08:00
|
|
|
}
|
2014-10-12 12:56:04 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
// If the item is unfinished, create a new item by advancing one symbol.
|
|
|
|
|
// Add that new item to a successor item set.
|
|
|
|
|
} else {
|
|
|
|
|
Symbol symbol = item.production->at(item.step_index).symbol;
|
|
|
|
|
ParseItem new_item(item.lhs(), *item.production, item.step_index + 1);
|
2014-07-20 21:43:27 -07:00
|
|
|
|
2016-11-30 09:34:47 -08:00
|
|
|
if (symbol.is_non_terminal()) {
|
2016-11-17 17:29:10 -08:00
|
|
|
nonterminal_successors[symbol.index].entries[new_item] = lookahead_symbols;
|
2016-11-30 09:34:47 -08:00
|
|
|
} else {
|
|
|
|
|
terminal_successors[symbol].entries[new_item] = lookahead_symbols;
|
2016-05-10 13:29:34 -07:00
|
|
|
}
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
|
|
|
|
}
|
2014-10-12 12:56:04 -07:00
|
|
|
|
2016-11-18 16:14:05 -08:00
|
|
|
// Add a Shift action for each possible successor state. Shift actions for
|
|
|
|
|
// terminal lookaheads can conflict with Reduce actions added previously.
|
2016-11-17 17:29:10 -08:00
|
|
|
for (auto &pair : terminal_successors) {
|
2016-11-30 09:34:47 -08:00
|
|
|
Symbol lookahead = pair.first;
|
2016-11-17 17:29:10 -08:00
|
|
|
ParseItemSet &next_item_set = pair.second;
|
|
|
|
|
ParseStateId next_state_id = add_parse_state(next_item_set);
|
2016-11-18 16:14:05 -08:00
|
|
|
ParseState &state = parse_table.states[state_id];
|
|
|
|
|
bool had_existing_action = !state.terminal_entries[lookahead].actions.empty();
|
2016-11-17 17:29:10 -08:00
|
|
|
parse_table.add_terminal_action(state_id, lookahead, ParseAction::Shift(next_state_id));
|
2017-07-07 13:59:57 -07:00
|
|
|
if (!processing_recovery_states) {
|
|
|
|
|
if (had_existing_action) {
|
2016-11-17 17:29:10 -08:00
|
|
|
lookaheads_with_conflicts.insert(lookahead);
|
2017-07-07 13:59:57 -07:00
|
|
|
}
|
2016-11-30 09:34:47 -08:00
|
|
|
recovery_states[lookahead].add(next_item_set);
|
2014-07-20 21:43:27 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
// Add a Shift action for each non-terminal transition.
|
|
|
|
|
for (auto &pair : nonterminal_successors) {
|
|
|
|
|
Symbol::Index lookahead = pair.first;
|
|
|
|
|
ParseItemSet &next_item_set = pair.second;
|
|
|
|
|
ParseStateId next_state = add_parse_state(next_item_set);
|
|
|
|
|
parse_table.set_nonterminal_action(state_id, lookahead, next_state);
|
2017-07-07 13:59:57 -07:00
|
|
|
if (!processing_recovery_states) {
|
2017-03-17 12:52:01 -07:00
|
|
|
recovery_states[Symbol::non_terminal(lookahead)].add(next_item_set);
|
2017-07-07 13:59:57 -07:00
|
|
|
}
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
|
|
|
|
|
2016-11-30 09:34:47 -08:00
|
|
|
for (Symbol lookahead : lookaheads_with_conflicts) {
|
2016-11-17 17:29:10 -08:00
|
|
|
string conflict = handle_conflict(item_set, state_id, lookahead);
|
|
|
|
|
if (!conflict.empty()) return conflict;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ParseAction shift_extra = ParseAction::ShiftExtra();
|
2016-07-17 07:25:57 -07:00
|
|
|
ParseState &state = parse_table.states[state_id];
|
2016-11-17 17:29:10 -08:00
|
|
|
for (const Symbol &extra_symbol : grammar.extra_tokens) {
|
2016-11-30 09:34:47 -08:00
|
|
|
if (!state.terminal_entries.count(extra_symbol) ||
|
2017-07-07 13:59:57 -07:00
|
|
|
state.has_shift_action() || processing_recovery_states) {
|
2016-11-30 09:34:47 -08:00
|
|
|
parse_table.add_terminal_action(state_id, extra_symbol, shift_extra);
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "";
|
2014-07-20 21:43:27 -07:00
|
|
|
}
|
|
|
|
|
|
2015-12-17 12:48:55 -08:00
|
|
|
void mark_fragile_actions() {
|
2015-12-09 14:23:19 -08:00
|
|
|
for (ParseState &state : parse_table.states) {
|
2016-11-14 08:36:06 -08:00
|
|
|
for (auto &entry : state.terminal_entries) {
|
2016-06-21 07:28:04 -07:00
|
|
|
auto &actions = entry.second.actions;
|
2015-12-17 12:48:55 -08:00
|
|
|
|
2016-06-21 07:28:04 -07:00
|
|
|
for (ParseAction &action : actions) {
|
2016-11-14 08:36:06 -08:00
|
|
|
if (action.type == ParseActionTypeReduce) {
|
2017-07-06 15:20:11 -07:00
|
|
|
if (has_fragile_production(action.production)) {
|
2015-12-17 12:48:55 -08:00
|
|
|
action.fragile = true;
|
2017-07-06 15:20:11 -07:00
|
|
|
}
|
|
|
|
|
action.production = nullptr;
|
2015-12-17 12:48:55 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-03-02 20:52:18 -08:00
|
|
|
|
2016-06-21 07:28:04 -07:00
|
|
|
for (auto i = actions.begin(); i != actions.end();) {
|
2016-03-02 20:52:18 -08:00
|
|
|
bool erased = false;
|
2016-06-21 07:28:04 -07:00
|
|
|
for (auto j = actions.begin(); j != i; j++) {
|
2016-03-02 20:52:18 -08:00
|
|
|
if (*j == *i) {
|
2016-06-21 07:28:04 -07:00
|
|
|
actions.erase(i);
|
2016-03-02 20:52:18 -08:00
|
|
|
erased = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-14 08:36:06 -08:00
|
|
|
if (!erased) {
|
2016-03-02 20:52:18 -08:00
|
|
|
++i;
|
2015-12-09 14:23:19 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-06 09:47:00 -08:00
|
|
|
void compute_unmergable_token_pairs() {
|
2017-03-17 16:31:29 -07:00
|
|
|
incompatible_tokens_by_index.resize(lexical_grammar.variables.size());
|
2017-03-06 09:47:00 -08:00
|
|
|
|
2017-03-17 16:31:29 -07:00
|
|
|
auto lex_table_builder = LexTableBuilder::create(lexical_grammar);
|
|
|
|
|
for (unsigned i = 0, n = lexical_grammar.variables.size(); i < n; i++) {
|
|
|
|
|
Symbol token = Symbol::terminal(i);
|
|
|
|
|
auto &incompatible_indices = incompatible_tokens_by_index[i];
|
|
|
|
|
|
|
|
|
|
for (unsigned j = 0; j < n; j++) {
|
|
|
|
|
if (i == j) continue;
|
|
|
|
|
if (lex_table_builder->detect_conflict(i, j)) {
|
|
|
|
|
incompatible_indices.insert(Symbol::terminal(j));
|
|
|
|
|
}
|
2017-03-06 09:47:00 -08:00
|
|
|
}
|
|
|
|
|
|
2017-03-17 16:31:29 -07:00
|
|
|
for (const ExternalToken &external_token : grammar.external_tokens) {
|
|
|
|
|
if (external_token.corresponding_internal_token == token) {
|
|
|
|
|
for (unsigned j = 0; j < grammar.external_tokens.size(); j++) {
|
|
|
|
|
incompatible_indices.insert(Symbol::external(j));
|
|
|
|
|
}
|
2017-02-27 22:54:38 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-20 15:26:35 -08:00
|
|
|
void remove_duplicate_parse_states() {
|
2016-11-15 17:51:52 -08:00
|
|
|
map<size_t, set<ParseStateId>> state_indices_by_signature;
|
|
|
|
|
|
|
|
|
|
for (ParseStateId i = 0, n = parse_table.states.size(); i < n; i++) {
|
|
|
|
|
ParseState &state = parse_table.states[i];
|
|
|
|
|
state_indices_by_signature[state.shift_actions_signature].insert(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set<ParseStateId> deleted_states;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
2017-02-26 12:23:35 -08:00
|
|
|
map<ParseStateId, ParseStateId> state_replacements;
|
2016-11-15 17:51:52 -08:00
|
|
|
|
|
|
|
|
for (auto &pair : state_indices_by_signature) {
|
|
|
|
|
auto &state_group = pair.second;
|
|
|
|
|
|
|
|
|
|
for (ParseStateId i : state_group) {
|
|
|
|
|
for (ParseStateId j : state_group) {
|
|
|
|
|
if (j == i) break;
|
2017-02-26 12:23:35 -08:00
|
|
|
if (!state_replacements.count(j) && merge_parse_state(j, i)) {
|
2016-11-15 17:51:52 -08:00
|
|
|
state_replacements.insert({ i, j });
|
|
|
|
|
deleted_states.insert(i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state_replacements.empty()) break;
|
|
|
|
|
|
|
|
|
|
for (ParseStateId i = 0, n = parse_table.states.size(); i < n; i++) {
|
|
|
|
|
ParseState &state = parse_table.states[i];
|
|
|
|
|
|
|
|
|
|
if (state_replacements.count(i)) {
|
2016-11-16 10:21:30 -08:00
|
|
|
state_indices_by_signature[state.shift_actions_signature].erase(i);
|
2016-11-15 17:51:52 -08:00
|
|
|
} else {
|
2016-11-19 20:45:32 -08:00
|
|
|
state.each_referenced_state([&state_replacements](ParseStateId *state_index) {
|
2016-11-16 10:21:30 -08:00
|
|
|
auto replacement = state_replacements.find(*state_index);
|
|
|
|
|
if (replacement != state_replacements.end()) {
|
|
|
|
|
*state_index = replacement->second;
|
2016-11-15 17:51:52 -08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<ParseStateId> new_state_ids(parse_table.states.size());
|
|
|
|
|
size_t deleted_state_count = 0;
|
|
|
|
|
auto deleted_state_iter = deleted_states.begin();
|
2016-11-18 16:14:05 -08:00
|
|
|
for (ParseStateId i = 0; i < new_state_ids.size(); i++) {
|
2016-11-15 17:51:52 -08:00
|
|
|
while (deleted_state_iter != deleted_states.end() && *deleted_state_iter < i) {
|
|
|
|
|
deleted_state_count++;
|
|
|
|
|
deleted_state_iter++;
|
|
|
|
|
}
|
|
|
|
|
new_state_ids[i] = i - deleted_state_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ParseStateId original_state_index = 0;
|
|
|
|
|
auto iter = parse_table.states.begin();
|
|
|
|
|
while (iter != parse_table.states.end()) {
|
|
|
|
|
if (deleted_states.count(original_state_index)) {
|
|
|
|
|
iter = parse_table.states.erase(iter);
|
|
|
|
|
} else {
|
|
|
|
|
ParseState &state = *iter;
|
2016-11-19 20:45:32 -08:00
|
|
|
state.each_referenced_state([&new_state_ids](ParseStateId *state_index) {
|
2016-11-15 17:51:52 -08:00
|
|
|
*state_index = new_state_ids[*state_index];
|
|
|
|
|
});
|
|
|
|
|
++iter;
|
|
|
|
|
}
|
|
|
|
|
original_state_index++;
|
|
|
|
|
}
|
2015-12-09 14:23:19 -08:00
|
|
|
}
|
|
|
|
|
|
2017-02-26 12:23:35 -08:00
|
|
|
static bool has_entry(const ParseState &state, const ParseTableEntry &entry) {
|
|
|
|
|
for (const auto &pair : state.terminal_entries)
|
|
|
|
|
if (pair.second == entry)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool merge_parse_state(size_t i, size_t j) {
|
|
|
|
|
ParseState &state = parse_table.states[i];
|
|
|
|
|
ParseState &other = parse_table.states[j];
|
|
|
|
|
|
2017-06-22 15:33:07 -07:00
|
|
|
if (state.nonterminal_entries != other.nonterminal_entries) return false;
|
2017-02-26 12:23:35 -08:00
|
|
|
|
|
|
|
|
for (auto &entry : state.terminal_entries) {
|
|
|
|
|
Symbol lookahead = entry.first;
|
|
|
|
|
const auto &other_entry = other.terminal_entries.find(lookahead);
|
2017-06-22 15:33:07 -07:00
|
|
|
|
2017-02-26 12:23:35 -08:00
|
|
|
if (other_entry == other.terminal_entries.end()) {
|
2017-06-22 15:33:07 -07:00
|
|
|
if (entry.second.actions.back().type != ParseActionTypeReduce) return false;
|
|
|
|
|
if (!has_entry(other, entry.second)) return false;
|
|
|
|
|
|
2017-03-06 09:47:00 -08:00
|
|
|
if (lookahead.is_external()) return false;
|
2017-02-27 22:54:38 -08:00
|
|
|
if (!lookahead.is_built_in()) {
|
2017-06-22 15:33:07 -07:00
|
|
|
for (const Symbol &incompatible_token : incompatible_tokens_by_index[lookahead.index]) {
|
2017-03-17 16:31:29 -07:00
|
|
|
if (other.terminal_entries.count(incompatible_token)) return false;
|
2017-02-27 22:54:38 -08:00
|
|
|
}
|
|
|
|
|
}
|
2017-02-26 12:23:35 -08:00
|
|
|
} else if (entry.second != other_entry->second) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set<Symbol> symbols_to_merge;
|
|
|
|
|
|
|
|
|
|
for (auto &entry : other.terminal_entries) {
|
|
|
|
|
Symbol lookahead = entry.first;
|
|
|
|
|
|
|
|
|
|
if (!state.terminal_entries.count(lookahead)) {
|
2017-06-22 15:33:07 -07:00
|
|
|
if (entry.second.actions.back().type != ParseActionTypeReduce) return false;
|
|
|
|
|
if (!has_entry(state, entry.second)) return false;
|
|
|
|
|
|
2017-03-06 09:47:00 -08:00
|
|
|
if (lookahead.is_external()) return false;
|
2017-02-27 22:54:38 -08:00
|
|
|
if (!lookahead.is_built_in()) {
|
2017-06-22 15:33:07 -07:00
|
|
|
for (const Symbol &incompatible_token : incompatible_tokens_by_index[lookahead.index]) {
|
2017-03-17 16:31:29 -07:00
|
|
|
if (state.terminal_entries.count(incompatible_token)) return false;
|
2017-02-27 22:54:38 -08:00
|
|
|
}
|
|
|
|
|
}
|
2017-06-22 15:33:07 -07:00
|
|
|
|
2017-02-26 12:23:35 -08:00
|
|
|
symbols_to_merge.insert(lookahead);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-22 15:33:07 -07:00
|
|
|
for (const Symbol &lookahead : symbols_to_merge) {
|
2017-02-26 12:23:35 -08:00
|
|
|
state.terminal_entries[lookahead] = other.terminal_entries.find(lookahead)->second;
|
2017-06-22 15:33:07 -07:00
|
|
|
}
|
2017-02-26 12:23:35 -08:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
string handle_conflict(const ParseItemSet &item_set, ParseStateId state_id,
|
2016-11-30 09:34:47 -08:00
|
|
|
Symbol lookahead) {
|
2016-11-17 17:29:10 -08:00
|
|
|
ParseTableEntry &entry = parse_table.states[state_id].terminal_entries[lookahead];
|
|
|
|
|
int reduction_precedence = entry.actions.front().precedence();
|
|
|
|
|
set<ParseItem> shift_items;
|
2016-11-18 17:05:16 -08:00
|
|
|
bool considered_associativity = false;
|
2016-11-17 17:29:10 -08:00
|
|
|
|
|
|
|
|
for (const ParseAction &action : entry.actions)
|
|
|
|
|
if (action.type == ParseActionTypeReduce)
|
|
|
|
|
fragile_productions.insert(action.production);
|
|
|
|
|
|
|
|
|
|
if (entry.actions.back().type == ParseActionTypeShift) {
|
|
|
|
|
PrecedenceRange shift_precedence;
|
|
|
|
|
for (const auto &item_set_entry : item_set.entries) {
|
|
|
|
|
const ParseItem &item = item_set_entry.first;
|
|
|
|
|
if (item.step_index > 0 && !item.is_done()) {
|
|
|
|
|
LookaheadSet first_set = item_set_builder.get_first_set(item.next_symbol());
|
|
|
|
|
if (first_set.contains(lookahead)) {
|
|
|
|
|
shift_items.insert(item);
|
2016-12-01 10:24:06 -08:00
|
|
|
shift_precedence.add(item.production->at(item.step_index - 1).precedence);
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
2015-12-06 14:09:24 -08:00
|
|
|
}
|
2015-10-01 17:10:39 -07:00
|
|
|
}
|
2015-06-28 16:22:31 -05:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
// If the shift action has higher precedence, prefer it over any of the
|
|
|
|
|
// reduce actions.
|
|
|
|
|
if (shift_precedence.min > reduction_precedence ||
|
|
|
|
|
(shift_precedence.min == reduction_precedence &&
|
|
|
|
|
shift_precedence.max > reduction_precedence)) {
|
|
|
|
|
for (const ParseAction &action : entry.actions) {
|
|
|
|
|
if (action.type == ParseActionTypeShift) break;
|
|
|
|
|
fragile_productions.insert(action.production);
|
2015-12-09 14:23:19 -08:00
|
|
|
}
|
2016-11-17 17:29:10 -08:00
|
|
|
entry.actions.assign({ entry.actions.back() });
|
2015-06-28 16:22:31 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
// If the shift action has lower precedence, prefer the reduce actions.
|
|
|
|
|
else if (shift_precedence.max < reduction_precedence ||
|
|
|
|
|
(shift_precedence.max == reduction_precedence &&
|
|
|
|
|
shift_precedence.min < reduction_precedence)) {
|
|
|
|
|
entry.actions.pop_back();
|
|
|
|
|
}
|
2015-10-14 17:35:47 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
// If the shift action has the same precedence as the reduce actions,
|
|
|
|
|
// consider the reduce actions' associativity. If they are all left
|
|
|
|
|
// associative, prefer the reduce actions. If they are all right
|
|
|
|
|
// associative, prefer the shift.
|
|
|
|
|
else if (shift_precedence.min == reduction_precedence &&
|
|
|
|
|
shift_precedence.max == reduction_precedence) {
|
2016-11-18 17:05:16 -08:00
|
|
|
considered_associativity = true;
|
2016-11-17 17:29:10 -08:00
|
|
|
bool has_non_associative_reductions = false;
|
|
|
|
|
bool has_left_associative_reductions = false;
|
|
|
|
|
bool has_right_associative_reductions = false;
|
|
|
|
|
for (const ParseAction &action : entry.actions) {
|
|
|
|
|
if (action.type != ParseActionTypeReduce) break;
|
|
|
|
|
switch (action.associativity()) {
|
|
|
|
|
case rules::AssociativityLeft:
|
|
|
|
|
has_left_associative_reductions = true;
|
|
|
|
|
break;
|
|
|
|
|
case rules::AssociativityRight:
|
|
|
|
|
has_right_associative_reductions = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
has_non_associative_reductions = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-10-14 17:35:47 -07:00
|
|
|
}
|
2016-11-17 17:29:10 -08:00
|
|
|
|
|
|
|
|
if (!has_non_associative_reductions) {
|
|
|
|
|
if (has_right_associative_reductions && !has_left_associative_reductions) {
|
|
|
|
|
for (const ParseAction &action : entry.actions) {
|
|
|
|
|
if (action.type == ParseActionTypeShift) break;
|
|
|
|
|
fragile_productions.insert(action.production);
|
|
|
|
|
}
|
|
|
|
|
entry.actions.assign({ entry.actions.back() });
|
|
|
|
|
} else if (has_left_associative_reductions && !has_right_associative_reductions) {
|
|
|
|
|
entry.actions.pop_back();
|
2015-10-14 17:35:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-04 22:07:52 -07:00
|
|
|
}
|
2015-10-14 17:35:47 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
if (entry.actions.size() == 1) return "";
|
2015-10-14 17:35:47 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
set<Symbol> actual_conflict;
|
|
|
|
|
for (const ParseItem &item : shift_items)
|
|
|
|
|
actual_conflict.insert(item.lhs());
|
|
|
|
|
for (const ParseAction &action : entry.actions)
|
|
|
|
|
if (action.type == ParseActionTypeReduce)
|
|
|
|
|
actual_conflict.insert(action.symbol);
|
2015-10-14 17:35:47 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
for (const auto &expected_conflict : grammar.expected_conflicts)
|
|
|
|
|
if (expected_conflict == actual_conflict)
|
|
|
|
|
return "";
|
2015-10-14 17:35:47 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
ParseItem earliest_starting_item;
|
|
|
|
|
for (const ParseAction &action : entry.actions)
|
|
|
|
|
if (action.type == ParseActionTypeReduce)
|
|
|
|
|
if (action.consumed_symbol_count > earliest_starting_item.step_index)
|
|
|
|
|
earliest_starting_item = ParseItem(action.symbol, *action.production, action.consumed_symbol_count);
|
2015-10-14 17:35:47 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
for (const ParseItem &shift_item : shift_items)
|
|
|
|
|
if (shift_item.step_index > earliest_starting_item.step_index)
|
|
|
|
|
earliest_starting_item = shift_item;
|
|
|
|
|
|
|
|
|
|
string description = "Unresolved conflict for symbol sequence:\n\n";
|
|
|
|
|
for (size_t i = 0; i < earliest_starting_item.step_index; i++) {
|
|
|
|
|
description += " " + symbol_name(earliest_starting_item.production->at(i).symbol);
|
2015-10-14 17:35:47 -07:00
|
|
|
}
|
|
|
|
|
|
2016-11-30 09:34:47 -08:00
|
|
|
description += " \u2022 " + symbol_name(lookahead) + " \u2026";
|
2016-11-17 17:29:10 -08:00
|
|
|
description += "\n\n";
|
|
|
|
|
|
|
|
|
|
description += "Possible interpretations:\n\n";
|
|
|
|
|
|
2016-11-18 17:05:16 -08:00
|
|
|
size_t interpretation_count = 1;
|
2016-11-17 17:29:10 -08:00
|
|
|
for (const ParseAction &action : entry.actions) {
|
|
|
|
|
if (action.type == ParseActionTypeReduce) {
|
2016-11-18 17:05:16 -08:00
|
|
|
description += " " + to_string(interpretation_count++) + ":";
|
|
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
for (size_t i = 0; i < earliest_starting_item.step_index - action.consumed_symbol_count; i++) {
|
|
|
|
|
description += " " + symbol_name(earliest_starting_item.production->at(i).symbol);
|
|
|
|
|
}
|
2015-10-14 17:35:47 -07:00
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
description += " (" + symbol_name(action.symbol);
|
2017-07-06 15:20:11 -07:00
|
|
|
for (const ProductionStep &step : action.production->steps) {
|
2016-11-17 17:29:10 -08:00
|
|
|
description += " " + symbol_name(step.symbol);
|
|
|
|
|
}
|
|
|
|
|
description += ")";
|
2016-11-30 09:34:47 -08:00
|
|
|
description += " \u2022 " + symbol_name(lookahead) + " \u2026";
|
2016-11-18 17:05:16 -08:00
|
|
|
description += "\n";
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
2015-10-14 17:35:47 -07:00
|
|
|
}
|
2016-11-17 17:29:10 -08:00
|
|
|
|
|
|
|
|
for (const ParseItem &shift_item : shift_items) {
|
2016-11-18 17:05:16 -08:00
|
|
|
description += " " + to_string(interpretation_count++) + ":";
|
|
|
|
|
|
2016-11-17 17:29:10 -08:00
|
|
|
for (size_t i = 0; i < earliest_starting_item.step_index - shift_item.step_index; i++) {
|
|
|
|
|
description += " " + symbol_name(earliest_starting_item.production->at(i).symbol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
description += " (" + symbol_name(shift_item.lhs());
|
|
|
|
|
for (size_t i = 0; i < shift_item.production->size(); i++) {
|
|
|
|
|
if (i == shift_item.step_index)
|
|
|
|
|
description += " \u2022";
|
|
|
|
|
description += " " + symbol_name(shift_item.production->at(i).symbol);
|
|
|
|
|
}
|
|
|
|
|
description += ")";
|
2016-11-18 17:05:16 -08:00
|
|
|
description += "\n";
|
2015-10-24 12:49:04 -07:00
|
|
|
}
|
|
|
|
|
|
2016-11-18 17:05:16 -08:00
|
|
|
description += "\nPossible resolutions:\n\n";
|
2015-06-28 16:22:31 -05:00
|
|
|
|
2016-11-18 17:05:16 -08:00
|
|
|
size_t resolution_count = 1;
|
2016-11-17 17:29:10 -08:00
|
|
|
if (actual_conflict.size() > 1) {
|
2016-11-18 17:05:16 -08:00
|
|
|
if (!shift_items.empty()) {
|
|
|
|
|
description += " " + to_string(resolution_count++) + ": ";
|
|
|
|
|
description += "Specify a higher precedence in";
|
|
|
|
|
bool is_first = true;
|
|
|
|
|
for (const ParseItem &shift_item : shift_items) {
|
|
|
|
|
if (!is_first) description += " and";
|
|
|
|
|
description += " `" + symbol_name(shift_item.lhs()) + "`";
|
|
|
|
|
is_first = false;
|
|
|
|
|
}
|
|
|
|
|
description += " than in the other rules.\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const ParseAction &action : entry.actions) {
|
|
|
|
|
if (action.type == ParseActionTypeReduce) {
|
|
|
|
|
description += " " + to_string(resolution_count++) + ": ";
|
|
|
|
|
description += "Specify a higher precedence in `";
|
|
|
|
|
description += symbol_name(action.symbol);
|
|
|
|
|
description += "` than in the other rules.\n";
|
|
|
|
|
}
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
2015-06-28 16:22:31 -05:00
|
|
|
}
|
2015-10-14 17:35:47 -07:00
|
|
|
|
2016-11-18 17:05:16 -08:00
|
|
|
if (considered_associativity) {
|
|
|
|
|
description += " " + to_string(resolution_count++) + ": ";
|
|
|
|
|
description += "Specify a left or right associativity in";
|
2017-06-14 09:52:55 -04:00
|
|
|
bool is_first = true;
|
2016-11-17 17:29:10 -08:00
|
|
|
for (const ParseAction &action : entry.actions) {
|
|
|
|
|
if (action.type == ParseActionTypeReduce) {
|
2016-11-18 17:05:16 -08:00
|
|
|
if (!is_first) description += " and";
|
|
|
|
|
description += " `" + symbol_name(action.symbol) + "`";
|
|
|
|
|
is_first = false;
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-11-18 17:05:16 -08:00
|
|
|
description += "\n";
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
|
|
|
|
|
2016-11-18 17:05:16 -08:00
|
|
|
description += " " + to_string(resolution_count++) + ": ";
|
|
|
|
|
description += "Add a conflict for these rules:";
|
2016-11-17 17:29:10 -08:00
|
|
|
for (const Symbol &conflict_symbol : actual_conflict) {
|
2016-11-18 17:05:16 -08:00
|
|
|
description += " `" + symbol_name(conflict_symbol) + "`";
|
2016-11-17 17:29:10 -08:00
|
|
|
}
|
|
|
|
|
description += "\n";
|
|
|
|
|
return description;
|
2015-06-28 16:22:31 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string symbol_name(const rules::Symbol &symbol) const {
|
|
|
|
|
if (symbol.is_built_in()) {
|
2016-11-09 20:47:47 -08:00
|
|
|
if (symbol == END_OF_INPUT())
|
2015-06-28 16:22:31 -05:00
|
|
|
return "END_OF_INPUT";
|
|
|
|
|
else
|
|
|
|
|
return "";
|
2016-11-30 09:34:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (symbol.type) {
|
|
|
|
|
case Symbol::Terminal: {
|
2017-02-27 22:54:38 -08:00
|
|
|
const LexicalVariable &variable = lexical_grammar.variables[symbol.index];
|
2016-11-30 09:34:47 -08:00
|
|
|
if (variable.type == VariableTypeNamed)
|
|
|
|
|
return variable.name;
|
|
|
|
|
else
|
|
|
|
|
return "'" + variable.name + "'";
|
|
|
|
|
}
|
|
|
|
|
case Symbol::NonTerminal: {
|
|
|
|
|
return grammar.variables[symbol.index].name;
|
|
|
|
|
}
|
2017-01-31 11:46:28 -08:00
|
|
|
case Symbol::External:
|
|
|
|
|
default: {
|
2016-12-05 17:26:11 -08:00
|
|
|
return grammar.external_tokens[symbol.index].name;
|
2016-11-30 09:34:47 -08:00
|
|
|
}
|
2015-09-19 13:19:49 -07:00
|
|
|
}
|
2015-06-28 16:22:31 -05:00
|
|
|
}
|
2015-12-09 14:23:19 -08:00
|
|
|
|
|
|
|
|
bool has_fragile_production(const Production *production) {
|
2016-11-17 17:29:10 -08:00
|
|
|
return fragile_productions.find(production) != fragile_productions.end();
|
2015-12-09 14:23:19 -08:00
|
|
|
}
|
2014-07-20 21:43:27 -07:00
|
|
|
};
|
|
|
|
|
|
2016-01-10 20:04:41 -08:00
|
|
|
pair<ParseTable, CompileError> build_parse_table(
|
2015-07-27 18:29:48 -07:00
|
|
|
const SyntaxGrammar &grammar, const LexicalGrammar &lex_grammar) {
|
2014-07-20 21:43:27 -07:00
|
|
|
return ParseTableBuilder(grammar, lex_grammar).build();
|
2014-05-04 22:07:52 -07:00
|
|
|
}
|
2014-07-20 21:43:27 -07:00
|
|
|
|
|
|
|
|
} // namespace build_tables
|
|
|
|
|
} // namespace tree_sitter
|