Add PREC_DYNAMIC rule for resolving runtime ambiguities

This commit is contained in:
Max Brunsfeld 2017-07-06 15:20:11 -07:00
parent cb652239f6
commit d8e9d04fe7
24 changed files with 316 additions and 83 deletions

View file

@ -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}
}},
}, {}, {}, {}};

View file

@ -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}
})));
});
});

View 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))))

View 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]+"
}
}
}

View 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.

View file

@ -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) {

View file

@ -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 &);

View file

@ -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);