Allow conflicts involving repeat rules to be whitelisted, via their parent rule
This commit is contained in:
parent
119c67dd78
commit
84e4114f79
4 changed files with 119 additions and 9 deletions
|
|
@ -44,6 +44,7 @@ class ParseTableBuilder {
|
|||
const LexicalGrammar lexical_grammar;
|
||||
unordered_map<Symbol, ParseItemSet> recovery_states;
|
||||
unordered_map<ParseItemSet, ParseStateId> parse_state_ids;
|
||||
vector<const ParseItemSet *> item_sets_by_state_id;
|
||||
deque<ParseStateQueueEntry> parse_state_queue;
|
||||
ParseTable parse_table;
|
||||
set<string> conflicts;
|
||||
|
|
@ -168,19 +169,19 @@ class ParseTableBuilder {
|
|||
}
|
||||
|
||||
ParseStateId add_parse_state(SymbolSequence &&preceding_symbols, const ParseItemSet &item_set) {
|
||||
auto pair = parse_state_ids.find(item_set);
|
||||
if (pair == parse_state_ids.end()) {
|
||||
ParseStateId state_id = parse_table.states.size();
|
||||
ParseStateId new_state_id = parse_table.states.size();
|
||||
auto insertion = parse_state_ids.insert({move(item_set), new_state_id});
|
||||
if (insertion.second) {
|
||||
item_sets_by_state_id.push_back(&insertion.first->first);
|
||||
parse_table.states.push_back(ParseState());
|
||||
parse_state_ids[item_set] = state_id;
|
||||
parse_state_queue.push_back({
|
||||
move(preceding_symbols),
|
||||
move(item_set),
|
||||
state_id
|
||||
insertion.first->first,
|
||||
new_state_id
|
||||
});
|
||||
return state_id;
|
||||
return new_state_id;
|
||||
} else {
|
||||
return pair->second;
|
||||
return insertion.first->second;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -603,7 +604,25 @@ class ParseTableBuilder {
|
|||
|
||||
set<Symbol> actual_conflict;
|
||||
for (const ParseItem &item : conflicting_items) {
|
||||
actual_conflict.insert(item.lhs());
|
||||
Symbol symbol = item.lhs();
|
||||
if (grammar.variables[symbol.index].type == VariableTypeAuxiliary) {
|
||||
ParseStateId preceding_state_id = 1;
|
||||
for (auto &preceding_symbol : preceding_symbols) {
|
||||
ParseState &preceding_state = parse_table.states[preceding_state_id];
|
||||
if (preceding_state.nonterminal_entries.count(symbol.index)) break;
|
||||
preceding_state_id = preceding_symbol.is_terminal() ?
|
||||
preceding_state.terminal_entries[preceding_symbol].actions.back().state_index :
|
||||
preceding_state.nonterminal_entries[preceding_symbol.index];
|
||||
}
|
||||
const ParseItemSet &preceding_item_set = *item_sets_by_state_id[preceding_state_id];
|
||||
for (auto &preceding_entry : preceding_item_set.entries) {
|
||||
if (preceding_entry.first.next_symbol() == symbol) {
|
||||
actual_conflict.insert(preceding_entry.first.lhs());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
actual_conflict.insert(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &expected_conflict : grammar.expected_conflicts) {
|
||||
|
|
|
|||
14
test/fixtures/test_grammars/conflict_in_repeat_rule/expected_error.txt
vendored
Normal file
14
test/fixtures/test_grammars/conflict_in_repeat_rule/expected_error.txt
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Unresolved conflict for symbol sequence:
|
||||
|
||||
'[' identifier • ']' …
|
||||
|
||||
Possible interpretations:
|
||||
|
||||
1: '[' (array_repeat1 identifier) • ']' …
|
||||
2: '[' (array_type_repeat1 identifier) • ']' …
|
||||
|
||||
Possible resolutions:
|
||||
|
||||
1: Specify a higher precedence in `array_repeat1` than in the other rules.
|
||||
2: Specify a higher precedence in `array_type_repeat1` than in the other rules.
|
||||
3: Add a conflict for these rules: `array` `array_type`
|
||||
76
test/fixtures/test_grammars/conflict_in_repeat_rule/grammar.json
vendored
Normal file
76
test/fixtures/test_grammars/conflict_in_repeat_rule/grammar.json
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"name": "conflict_in_repeat_rule",
|
||||
|
||||
"rules": {
|
||||
"statement": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{"type": "SYMBOL", "name": "array"},
|
||||
{"type": "STRING", "value": ";"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{"type": "SYMBOL", "name": "array_type"},
|
||||
{"type": "SYMBOL", "name": "identifier"},
|
||||
{"type": "STRING", "value": ";"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"array": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "["
|
||||
},
|
||||
{
|
||||
"type": "REPEAT",
|
||||
"content": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{"type": "SYMBOL", "name": "identifier"},
|
||||
{"type": "STRING", "value": "0"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "]"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"array_type": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "["
|
||||
},
|
||||
{
|
||||
"type": "REPEAT",
|
||||
"content": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{"type": "SYMBOL", "name": "identifier"},
|
||||
{"type": "STRING", "value": "void"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "]"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"identifier": {"type": "PATTERN", "value": "\\a+"}
|
||||
}
|
||||
}
|
||||
1
test/fixtures/test_grammars/conflict_in_repeat_rule/readme.md
vendored
Normal file
1
test/fixtures/test_grammars/conflict_in_repeat_rule/readme.md
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
This grammar has a conflict that involves *repeat rules*: auxiliary rules that are added by the parser generator in order to implement repetition. There is no way of referring to these rules in the grammar DSL, so these conflicts must be resolved by referring to their parent rules.
|
||||
Loading…
Add table
Add a link
Reference in a new issue