Add PREC_DYNAMIC rule for resolving runtime ambiguities
This commit is contained in:
parent
cb652239f6
commit
d8e9d04fe7
24 changed files with 316 additions and 83 deletions
|
|
@ -25,25 +25,25 @@ describe("ParseItemSetBuilder", []() {
|
|||
it("adds items at the beginnings of referenced rules", [&]() {
|
||||
SyntaxGrammar grammar{{
|
||||
SyntaxVariable{"rule0", VariableTypeNamed, {
|
||||
Production({
|
||||
Production{{
|
||||
{Symbol::non_terminal(1), 0, AssociativityNone},
|
||||
{Symbol::terminal(11), 0, AssociativityNone},
|
||||
}),
|
||||
}, 0},
|
||||
}},
|
||||
SyntaxVariable{"rule1", VariableTypeNamed, {
|
||||
Production({
|
||||
Production{{
|
||||
{Symbol::terminal(12), 0, AssociativityNone},
|
||||
{Symbol::terminal(13), 0, AssociativityNone},
|
||||
}),
|
||||
Production({
|
||||
}, 0},
|
||||
Production{{
|
||||
{Symbol::non_terminal(2), 0, AssociativityNone},
|
||||
})
|
||||
}, 0}
|
||||
}},
|
||||
SyntaxVariable{"rule2", VariableTypeNamed, {
|
||||
Production({
|
||||
Production{{
|
||||
{Symbol::terminal(14), 0, AssociativityNone},
|
||||
{Symbol::terminal(15), 0, AssociativityNone},
|
||||
})
|
||||
}, 0}
|
||||
}},
|
||||
}, {}, {}, {}};
|
||||
|
||||
|
|
@ -84,17 +84,17 @@ describe("ParseItemSetBuilder", []() {
|
|||
it("handles rules with empty productions", [&]() {
|
||||
SyntaxGrammar grammar{{
|
||||
SyntaxVariable{"rule0", VariableTypeNamed, {
|
||||
Production({
|
||||
Production{{
|
||||
{Symbol::non_terminal(1), 0, AssociativityNone},
|
||||
{Symbol::terminal(11), 0, AssociativityNone},
|
||||
}),
|
||||
}, 0},
|
||||
}},
|
||||
SyntaxVariable{"rule1", VariableTypeNamed, {
|
||||
Production({
|
||||
Production{{
|
||||
{Symbol::terminal(12), 0, AssociativityNone},
|
||||
{Symbol::terminal(13), 0, AssociativityNone},
|
||||
}),
|
||||
Production({})
|
||||
}, 0},
|
||||
Production{{}, 0}
|
||||
}},
|
||||
}, {}, {}, {}};
|
||||
|
||||
|
|
|
|||
|
|
@ -34,21 +34,63 @@ describe("flatten_grammar", []() {
|
|||
AssertThat(result.name, Equals("test"));
|
||||
AssertThat(result.type, Equals(VariableTypeNamed));
|
||||
AssertThat(result.productions, Equals(vector<Production>({
|
||||
Production({
|
||||
Production{{
|
||||
{Symbol::non_terminal(1), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(2), 101, AssociativityLeft},
|
||||
{Symbol::non_terminal(3), 102, AssociativityRight},
|
||||
{Symbol::non_terminal(4), 101, AssociativityLeft},
|
||||
{Symbol::non_terminal(6), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(7), 0, AssociativityNone},
|
||||
}),
|
||||
Production({
|
||||
}, 0},
|
||||
Production{{
|
||||
{Symbol::non_terminal(1), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(2), 101, AssociativityLeft},
|
||||
{Symbol::non_terminal(5), 101, AssociativityLeft},
|
||||
{Symbol::non_terminal(6), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(7), 0, AssociativityNone},
|
||||
}, 0}
|
||||
})));
|
||||
});
|
||||
|
||||
it("stores the maximum dynamic precedence specified in each production", [&]() {
|
||||
SyntaxVariable result = flatten_rule({
|
||||
"test",
|
||||
VariableTypeNamed,
|
||||
Rule::seq({
|
||||
Symbol::non_terminal(1),
|
||||
Metadata::prec_dynamic(101, Rule::seq({
|
||||
Symbol::non_terminal(2),
|
||||
Rule::choice({
|
||||
Metadata::prec_dynamic(102, Rule::seq({
|
||||
Symbol::non_terminal(3),
|
||||
Symbol::non_terminal(4)
|
||||
})),
|
||||
Symbol::non_terminal(5),
|
||||
}),
|
||||
Symbol::non_terminal(6),
|
||||
})),
|
||||
Symbol::non_terminal(7),
|
||||
})
|
||||
});
|
||||
|
||||
AssertThat(result.name, Equals("test"));
|
||||
AssertThat(result.type, Equals(VariableTypeNamed));
|
||||
AssertThat(result.productions, Equals(vector<Production>({
|
||||
Production{{
|
||||
{Symbol::non_terminal(1), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(2), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(3), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(4), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(6), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(7), 0, AssociativityNone},
|
||||
}, 102},
|
||||
Production{{
|
||||
{Symbol::non_terminal(1), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(2), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(5), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(6), 0, AssociativityNone},
|
||||
{Symbol::non_terminal(7), 0, AssociativityNone},
|
||||
}, 101}
|
||||
})));
|
||||
});
|
||||
|
||||
|
|
@ -63,10 +105,10 @@ describe("flatten_grammar", []() {
|
|||
});
|
||||
|
||||
AssertThat(result.productions, Equals(vector<Production>({
|
||||
Production({
|
||||
Production{{
|
||||
{Symbol::non_terminal(1), 101, AssociativityLeft},
|
||||
{Symbol::non_terminal(2), 101, AssociativityLeft},
|
||||
})
|
||||
{Symbol::non_terminal(2), 101, AssociativityLeft},
|
||||
}, 0}
|
||||
})));
|
||||
|
||||
result = flatten_rule({
|
||||
|
|
@ -78,9 +120,9 @@ describe("flatten_grammar", []() {
|
|||
});
|
||||
|
||||
AssertThat(result.productions, Equals(vector<Production>({
|
||||
Production({
|
||||
Production{{
|
||||
{Symbol::non_terminal(1), 101, AssociativityLeft},
|
||||
})
|
||||
}, 0}
|
||||
})));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
25
test/fixtures/test_grammars/dynamic_precedence/corpus.txt
vendored
Normal file
25
test/fixtures/test_grammars/dynamic_precedence/corpus.txt
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
===============================
|
||||
Declarations
|
||||
===============================
|
||||
|
||||
int * x
|
||||
|
||||
---
|
||||
|
||||
(program (declaration
|
||||
(type (identifier))
|
||||
(declarator (identifier))))
|
||||
|
||||
===============================
|
||||
Expressions
|
||||
===============================
|
||||
|
||||
int * x * y
|
||||
|
||||
---
|
||||
|
||||
(program (expression
|
||||
(expression
|
||||
(expression (identifier))
|
||||
(expression (identifier)))
|
||||
(expression (identifier))))
|
||||
73
test/fixtures/test_grammars/dynamic_precedence/grammar.json
vendored
Normal file
73
test/fixtures/test_grammars/dynamic_precedence/grammar.json
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
{
|
||||
"name": "dynamic_precedence",
|
||||
|
||||
"conflicts": [
|
||||
["expression", "type"]
|
||||
],
|
||||
|
||||
"extras": [
|
||||
{"type": "PATTERN", "value": "\\s"}
|
||||
],
|
||||
|
||||
"rules": {
|
||||
"program": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{"type": "SYMBOL", "name": "declaration"},
|
||||
{"type": "SYMBOL", "name": "expression"},
|
||||
]
|
||||
},
|
||||
|
||||
"expression": {
|
||||
"type": "PREC_LEFT",
|
||||
"value": 0,
|
||||
"content": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{"type": "SYMBOL", "name": "expression"},
|
||||
{"type": "STRING", "value": "*"},
|
||||
{"type": "SYMBOL", "name": "expression"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "identifier"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"declaration": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{"type": "SYMBOL", "name": "type"},
|
||||
{"type": "SYMBOL", "name": "declarator"}
|
||||
]
|
||||
},
|
||||
|
||||
"declarator": {
|
||||
"type": "PREC_DYNAMIC",
|
||||
"value": 1,
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{"type": "STRING", "value": "*"},
|
||||
{"type": "SYMBOL", "name": "identifier"}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"type": {
|
||||
"type": "SYMBOL",
|
||||
"name": "identifier"
|
||||
},
|
||||
|
||||
"identifier": {
|
||||
"type": "PATTERN",
|
||||
"value": "[a-zA-Z]+"
|
||||
}
|
||||
}
|
||||
}
|
||||
1
test/fixtures/test_grammars/dynamic_precedence/readme.md
vendored
Normal file
1
test/fixtures/test_grammars/dynamic_precedence/readme.md
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
This grammar contains a conflict that is resolved at runtime. The PREC_DYNAMIC rule is used to indicate that the `declarator` rule should be preferred to the `expression` rule at runtime.
|
||||
|
|
@ -136,9 +136,14 @@ ostream &operator<<(ostream &stream, const Variable &variable) {
|
|||
return stream << "(Variable " << variable.name << " " << variable.rule << ")";
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &stream, const Production &production) {
|
||||
return stream << "(Production " << production.steps << " " <<
|
||||
to_string(production.dynamic_precedence) << ")";
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &stream, const SyntaxVariable &variable) {
|
||||
return stream << "(Variable " << variable.name << " " << variable.productions <<
|
||||
" " << to_string(variable.type) << "}";
|
||||
" " << to_string(variable.type) << ")";
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &stream, const LexicalVariable &variable) {
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ ostream &operator<<(ostream &, const InputGrammar &);
|
|||
ostream &operator<<(ostream &, const CompileError &);
|
||||
ostream &operator<<(ostream &, const ExternalToken &);
|
||||
ostream &operator<<(ostream &, const ProductionStep &);
|
||||
ostream &operator<<(ostream &, const Production &);
|
||||
ostream &operator<<(ostream &, const PrecedenceRange &);
|
||||
ostream &operator<<(ostream &, const Variable &);
|
||||
ostream &operator<<(ostream &, const LexicalVariable &);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ vector<string> test_languages = list_directory(grammars_dir_path);
|
|||
for (auto &language_name : test_languages) {
|
||||
if (language_name == "readme.md") continue;
|
||||
|
||||
describe(("test language: " + language_name).c_str(), [&]() {
|
||||
describe(("test grammar: " + language_name).c_str(), [&]() {
|
||||
string directory_path = grammars_dir_path + "/" + language_name;
|
||||
string grammar_path = directory_path + "/grammar.json";
|
||||
string grammar_json = read_file(grammar_path);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue