Expose all grammar compilation errors

This commit is contained in:
Max Brunsfeld 2014-05-01 23:28:40 -07:00
parent e36d586817
commit 3a50171249
15 changed files with 79 additions and 49 deletions

View file

@ -39,7 +39,7 @@ namespace tree_sitter_examples {
str(op),
sym(rule_name) }));
}
rule_ptr delimited(std::string delimiter) {
return seq({
str(delimiter),

View file

@ -41,10 +41,24 @@ namespace tree_sitter {
bool operator<(const Conflict &other) const;
};
enum GrammarErrorType {
GrammarErrorTypeRegex,
GrammarErrorTypeUndefinedSymbol
};
class GrammarError {
public:
GrammarError(GrammarErrorType type, std::string message);
GrammarErrorType type;
std::string message;
};
std::ostream& operator<<(std::ostream &stream, const Grammar &grammar);
std::ostream& operator<<(std::ostream &stream, const Conflict &conflict);
std::ostream& operator<<(std::ostream &stream, const GrammarError *error);
std::pair<std::string, std::vector<Conflict>> compile(const Grammar &grammar, std::string name);
std::tuple<std::string, std::vector<Conflict>, const GrammarError *>
compile(const Grammar &grammar, std::string name);
}
#endif // TREE_SITTER_COMPILER_H_

View file

@ -47,17 +47,17 @@ describe("resolving parse conflicts", []() {
it("prefers the token with the higher precedence", [&]() {
should_update = manager->resolve_lex_action(LexAction::Accept(sym3, 2), LexAction::Accept(sym2, 0));
AssertThat(should_update, IsFalse());
should_update = manager->resolve_lex_action(LexAction::Accept(sym2, 0), LexAction::Accept(sym3, 2));
AssertThat(should_update, IsTrue());
});
});
describe("when both tokens have the same precedence", [&]() {
it("prefers the token listed earlier in the grammar", [&]() {
should_update = manager->resolve_lex_action(LexAction::Accept(sym1, 0), LexAction::Accept(sym2, 0));
AssertThat(should_update, IsFalse());
should_update = manager->resolve_lex_action(LexAction::Accept(sym2, 0), LexAction::Accept(sym1, 0));
AssertThat(should_update, IsTrue());
});

View file

@ -22,11 +22,16 @@ describe("compiling the example grammars", []() {
auto compile_grammar = [&](const Grammar &grammar, string language) {
it(("compiles the " + language + " grammar").c_str(), [&]() {
ofstream file(example_parser_dir + language + ".c");
auto result = compile(grammar, language);
string code = get<0>(result);
vector<Conflict> conflicts = get<1>(result);
const GrammarError *error = get<2>(result);
// cout << "\n\nconflicts for " << language << ":\n" << result.second;
AssertThat(error, Equals((GrammarError *)nullptr));
// cout << "\n\nconflicts for " << language << ":\n" << get<1>(result);
file << result.first;
file << get<0>(result);
file.close();
});
};

View file

@ -35,7 +35,7 @@ describe("interning symbols in a grammar", []() {
auto result = intern_symbols(grammar);
AssertThat(result.second->message(), Equals("Undefined rule 'y'"));
AssertThat(result.second->message, Equals("Undefined rule 'y'"));
});
});
});

View file

@ -18,7 +18,7 @@
#include "compiler/build_tables/first_set.h"
namespace tree_sitter {
using std::pair;
using std::tuple;
using std::string;
using std::map;
using std::vector;
@ -199,12 +199,12 @@ namespace tree_sitter {
LexTable lex_table;
};
pair<pair<ParseTable, LexTable>, vector<Conflict>>
tuple<ParseTable, LexTable, vector<Conflict>>
build_tables(const PreparedGrammar &grammar,
const PreparedGrammar &lex_grammar) {
TableBuilder builder(grammar, lex_grammar);
builder.build();
return { { builder.parse_table, builder.lex_table }, builder.conflicts() };
return { builder.parse_table, builder.lex_table, builder.conflicts() };
}
}
}

View file

@ -13,7 +13,7 @@ namespace tree_sitter {
class PreparedGrammar;
namespace build_tables {
std::pair<std::pair<ParseTable, LexTable>, std::vector<Conflict>>
std::tuple<ParseTable, LexTable, std::vector<Conflict>>
build_tables(const PreparedGrammar &grammar,
const PreparedGrammar &lex_grammar);
}

View file

@ -13,7 +13,7 @@ namespace tree_sitter {
bool Item::is_done() const {
return rule_can_be_blank(rule);
}
int Item::precedence() const {
return get_metadata(rule, rules::PRECEDENCE);
}

View file

@ -5,22 +5,27 @@
#include "compiler/prepared_grammar.h"
namespace tree_sitter {
using std::pair;
using std::tuple;
using std::string;
using std::vector;
using std::get;
pair<string, vector<Conflict>> compile(const Grammar &grammar, std::string name) {
auto grammars = prepare_grammar::prepare_grammar(grammar);
PreparedGrammar &syntax_grammar = grammars.first;
PreparedGrammar &lexical_grammar = grammars.second;
tuple<string, vector<Conflict>, const GrammarError *>
compile(const Grammar &grammar, std::string name) {
auto prepare_grammar_result = prepare_grammar::prepare_grammar(grammar);
const PreparedGrammar &syntax_grammar = get<0>(prepare_grammar_result);
const PreparedGrammar &lexical_grammar = get<1>(prepare_grammar_result);
const GrammarError *error = get<2>(prepare_grammar_result);
if (error) return { "", vector<Conflict>(), error };
auto table_build_result = build_tables::build_tables(syntax_grammar, lexical_grammar);
auto tables = table_build_result.first;
auto conflicts = table_build_result.second;
const ParseTable &parse_table = get<0>(table_build_result);
const LexTable &lex_table = get<1>(table_build_result);
const vector<Conflict> &conflicts = get<2>(table_build_result);
ParseTable &parse_table = tables.first;
LexTable &lex_table = tables.second;
string code = generate_code::c_code(name, parse_table, lex_table, syntax_grammar, lexical_grammar);
return { generate_code::c_code(name, parse_table, lex_table, syntax_grammar, lexical_grammar), conflicts };
return { code, conflicts, nullptr };
}
}

View file

@ -39,4 +39,15 @@ namespace tree_sitter {
}
return stream << string("}>");
}
GrammarError::GrammarError(GrammarErrorType type, std::string message) :
type(type),
message(message) {}
ostream& operator<<(ostream &stream, const GrammarError *error) {
if (error)
return stream << (string("#<grammar-error '") + error->message + "'>");
else
return stream << string("#<null>");
}
}

View file

@ -14,12 +14,6 @@ namespace tree_sitter {
using std::pair;
using std::make_shared;
GrammarError::GrammarError(string rule_name) : rule_name(rule_name) {}
string GrammarError::message() const {
return "Undefined rule '" + rule_name + "'";
}
namespace prepare_grammar {
class InternSymbols : public rules::IdentityRuleFn {
const Grammar grammar;
@ -54,7 +48,8 @@ namespace tree_sitter {
if (!interner.missing_rule_name.empty())
return {
PreparedGrammar({}, {}),
new GrammarError(interner.missing_rule_name)
new GrammarError(GrammarErrorTypeUndefinedSymbol,
"Undefined rule '" + interner.missing_rule_name + "'")
};
rules.push_back({ pair.first, new_rule });
}

View file

@ -3,18 +3,12 @@
#include <utility>
#include <string>
#include "tree_sitter/compiler.h"
namespace tree_sitter {
class Grammar;
class PreparedGrammar;
class GrammarError {
std::string rule_name;
public:
explicit GrammarError(std::string rule_name);
std::string message() const;
};
namespace prepare_grammar {
std::pair<PreparedGrammar, const GrammarError *> intern_symbols(const Grammar &);
}

View file

@ -5,18 +5,22 @@
#include "compiler/prepare_grammar/intern_symbols.h"
namespace tree_sitter {
using std::pair;
using std::tuple;
namespace prepare_grammar {
pair<PreparedGrammar, PreparedGrammar> prepare_grammar(const Grammar &input_grammar) {
auto interned = intern_symbols(input_grammar);
if (interned.second) {
printf("Error! %s", interned.second->message().c_str());
exit(1);
}
auto grammars = extract_tokens(interned.first);
const auto &rule_grammar = expand_repeats(grammars.first);
const auto &lex_grammar = grammars.second;
tuple<PreparedGrammar, PreparedGrammar, const GrammarError *>
prepare_grammar(const Grammar &input_grammar) {
auto result = intern_symbols(input_grammar);
const PreparedGrammar &grammar = result.first;
const GrammarError *error = result.second;
if (error)
return { PreparedGrammar({}, {}), PreparedGrammar({}, {}), error };
auto grammars = extract_tokens(grammar);
const PreparedGrammar &rule_grammar = expand_repeats(grammars.first);
const PreparedGrammar &lex_grammar = grammars.second;
return { rule_grammar, lex_grammar };
}
}

View file

@ -5,10 +5,12 @@
namespace tree_sitter {
class Grammar;
class GrammarError;
class PreparedGrammar;
namespace prepare_grammar {
std::pair<PreparedGrammar, PreparedGrammar> prepare_grammar(const Grammar &);
std::tuple<PreparedGrammar, PreparedGrammar, const GrammarError *>
prepare_grammar(const Grammar &);
}
}

View file

@ -58,7 +58,7 @@ namespace tree_sitter {
rule_ptr keyword(const string &value) {
return metadata(make_shared<String>(value), { { PRECEDENCE, 100}, { IS_TOKEN, 1 } });
}
rule_ptr err(const rule_ptr &rule) {
return choice({ rule, ERROR().copy() });
}