diff --git a/examples/grammars/helpers.cc b/examples/grammars/helpers.cc index 50a6f420..258feb63 100644 --- a/examples/grammars/helpers.cc +++ b/examples/grammars/helpers.cc @@ -39,7 +39,7 @@ namespace tree_sitter_examples { str(op), sym(rule_name) })); } - + rule_ptr delimited(std::string delimiter) { return seq({ str(delimiter), diff --git a/include/tree_sitter/compiler.h b/include/tree_sitter/compiler.h index cd47a11c..cf680086 100644 --- a/include/tree_sitter/compiler.h +++ b/include/tree_sitter/compiler.h @@ -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> compile(const Grammar &grammar, std::string name); + std::tuple, const GrammarError *> + compile(const Grammar &grammar, std::string name); } #endif // TREE_SITTER_COMPILER_H_ diff --git a/spec/compiler/build_tables/conflict_manager_spec.cc b/spec/compiler/build_tables/conflict_manager_spec.cc index db2b89f5..775584dd 100644 --- a/spec/compiler/build_tables/conflict_manager_spec.cc +++ b/spec/compiler/build_tables/conflict_manager_spec.cc @@ -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()); }); diff --git a/spec/compiler/compile_examples.cc b/spec/compiler/compile_examples.cc index 38c0d782..8b19fcaa 100644 --- a/spec/compiler/compile_examples.cc +++ b/spec/compiler/compile_examples.cc @@ -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 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(); }); }; diff --git a/spec/compiler/prepare_grammar/intern_symbols_spec.cc b/spec/compiler/prepare_grammar/intern_symbols_spec.cc index 9361f3b3..4ba281e6 100644 --- a/spec/compiler/prepare_grammar/intern_symbols_spec.cc +++ b/spec/compiler/prepare_grammar/intern_symbols_spec.cc @@ -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'")); }); }); }); diff --git a/src/compiler/build_tables/build_tables.cc b/src/compiler/build_tables/build_tables.cc index f1d25e32..5998b2b5 100644 --- a/src/compiler/build_tables/build_tables.cc +++ b/src/compiler/build_tables/build_tables.cc @@ -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, vector> + tuple> 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() }; } } } diff --git a/src/compiler/build_tables/build_tables.h b/src/compiler/build_tables/build_tables.h index 36856d8e..fbe0c705 100644 --- a/src/compiler/build_tables/build_tables.h +++ b/src/compiler/build_tables/build_tables.h @@ -13,7 +13,7 @@ namespace tree_sitter { class PreparedGrammar; namespace build_tables { - std::pair, std::vector> + std::tuple> build_tables(const PreparedGrammar &grammar, const PreparedGrammar &lex_grammar); } diff --git a/src/compiler/build_tables/item.cc b/src/compiler/build_tables/item.cc index 410e6167..9c0fda72 100644 --- a/src/compiler/build_tables/item.cc +++ b/src/compiler/build_tables/item.cc @@ -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); } diff --git a/src/compiler/compile.cc b/src/compiler/compile.cc index 6194e8bf..c9b54824 100644 --- a/src/compiler/compile.cc +++ b/src/compiler/compile.cc @@ -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> 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, 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(), 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 &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 }; } } diff --git a/src/compiler/grammar.cc b/src/compiler/grammar.cc index d571fb34..e0678b8f 100644 --- a/src/compiler/grammar.cc +++ b/src/compiler/grammar.cc @@ -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("#message + "'>"); + else + return stream << string("#"); + } } diff --git a/src/compiler/prepare_grammar/intern_symbols.cc b/src/compiler/prepare_grammar/intern_symbols.cc index 2d67e21c..7f6b2f16 100644 --- a/src/compiler/prepare_grammar/intern_symbols.cc +++ b/src/compiler/prepare_grammar/intern_symbols.cc @@ -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 }); } diff --git a/src/compiler/prepare_grammar/intern_symbols.h b/src/compiler/prepare_grammar/intern_symbols.h index 0c2cc3ca..11e791c7 100644 --- a/src/compiler/prepare_grammar/intern_symbols.h +++ b/src/compiler/prepare_grammar/intern_symbols.h @@ -3,18 +3,12 @@ #include #include +#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 intern_symbols(const Grammar &); } diff --git a/src/compiler/prepare_grammar/prepare_grammar.cc b/src/compiler/prepare_grammar/prepare_grammar.cc index 02468b30..ef584a53 100644 --- a/src/compiler/prepare_grammar/prepare_grammar.cc +++ b/src/compiler/prepare_grammar/prepare_grammar.cc @@ -5,18 +5,22 @@ #include "compiler/prepare_grammar/intern_symbols.h" namespace tree_sitter { - using std::pair; + using std::tuple; namespace prepare_grammar { - pair 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 + 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 }; } } diff --git a/src/compiler/prepare_grammar/prepare_grammar.h b/src/compiler/prepare_grammar/prepare_grammar.h index bc30bd73..d21317e8 100644 --- a/src/compiler/prepare_grammar/prepare_grammar.h +++ b/src/compiler/prepare_grammar/prepare_grammar.h @@ -5,10 +5,12 @@ namespace tree_sitter { class Grammar; + class GrammarError; class PreparedGrammar; namespace prepare_grammar { - std::pair prepare_grammar(const Grammar &); + std::tuple + prepare_grammar(const Grammar &); } } diff --git a/src/compiler/rules/rules.cc b/src/compiler/rules/rules.cc index 28b1dda5..d9ea3537 100644 --- a/src/compiler/rules/rules.cc +++ b/src/compiler/rules/rules.cc @@ -58,7 +58,7 @@ namespace tree_sitter { rule_ptr keyword(const string &value) { return metadata(make_shared(value), { { PRECEDENCE, 100}, { IS_TOKEN, 1 } }); } - + rule_ptr err(const rule_ptr &rule) { return choice({ rule, ERROR().copy() }); }