Fix precedence calculations when building parse table
* Recurse into choice rules * Compute reduction precedence differently than shift precedence
This commit is contained in:
parent
14d7ebb7da
commit
97bb7a26cf
15 changed files with 240 additions and 71 deletions
|
|
@ -6,6 +6,8 @@
|
|||
#include <vector>
|
||||
#include "compiler/build_tables/lex_conflict_manager.h"
|
||||
#include "compiler/build_tables/item_set_transitions.h"
|
||||
#include "compiler/build_tables/get_completion_status.h"
|
||||
#include "compiler/build_tables/get_metadata.h"
|
||||
#include "compiler/build_tables/lex_item.h"
|
||||
#include "compiler/parse_table.h"
|
||||
#include "compiler/lexical_grammar.h"
|
||||
|
|
@ -106,13 +108,16 @@ class LexTableBuilder {
|
|||
}
|
||||
|
||||
void add_accept_token_actions(const LexItemSet &item_set, LexStateId state_id) {
|
||||
for (const LexItem &item : item_set)
|
||||
if (item.is_done()) {
|
||||
for (const LexItem &item : item_set) {
|
||||
CompletionStatus completion_status = get_completion_status(item.rule);
|
||||
if (completion_status.is_done) {
|
||||
auto current_action = lex_table.state(state_id).default_action;
|
||||
auto new_action = LexAction::Accept(item.lhs, item.precedence());
|
||||
auto new_action =
|
||||
LexAction::Accept(item.lhs, completion_status.precedence);
|
||||
if (conflict_manager.resolve(new_action, current_action))
|
||||
lex_table.state(state_id).default_action = new_action;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_token_start(const LexItemSet &item_set, LexStateId state_id) {
|
||||
|
|
@ -140,8 +145,11 @@ class LexTableBuilder {
|
|||
|
||||
set<int> precedence_values_for_item_set(const LexItemSet &item_set) const {
|
||||
set<int> result;
|
||||
for (const auto &item : item_set)
|
||||
result.insert(item.precedence());
|
||||
for (const auto &item : item_set) {
|
||||
auto precedence_range = get_metadata(item.rule, rules::PRECEDENCE);
|
||||
result.insert(precedence_range.min);
|
||||
result.insert(precedence_range.max);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#include "compiler/build_tables/item_set_transitions.h"
|
||||
#include "compiler/build_tables/parse_conflict_manager.h"
|
||||
#include "compiler/build_tables/parse_item.h"
|
||||
#include "compiler/build_tables/get_completion_status.h"
|
||||
#include "compiler/build_tables/get_metadata.h"
|
||||
#include "compiler/lexical_grammar.h"
|
||||
#include "compiler/syntax_grammar.h"
|
||||
#include "compiler/rules/symbol.h"
|
||||
|
|
@ -109,12 +111,14 @@ class ParseTableBuilder {
|
|||
const ParseItem &item = pair.first;
|
||||
const set<Symbol> &lookahead_symbols = pair.second;
|
||||
|
||||
if (item.is_done()) {
|
||||
CompletionStatus completion_status = get_completion_status(item.rule);
|
||||
if (completion_status.is_done) {
|
||||
ParseAction action =
|
||||
(item.lhs == rules::START())
|
||||
? ParseAction::Accept()
|
||||
: ParseAction::Reduce(item.lhs, item.consumed_symbols.size(),
|
||||
item.precedence(), item.associativity(),
|
||||
completion_status.precedence,
|
||||
completion_status.associativity,
|
||||
get_production_id(item.consumed_symbols));
|
||||
|
||||
for (const auto &lookahead_sym : lookahead_symbols)
|
||||
|
|
@ -206,8 +210,11 @@ class ParseTableBuilder {
|
|||
set<int> result;
|
||||
for (const auto &pair : item_set) {
|
||||
const ParseItem &item = pair.first;
|
||||
if (!item.consumed_symbols.empty())
|
||||
result.insert(item.precedence());
|
||||
if (!item.consumed_symbols.empty()) {
|
||||
auto precedence_range = get_metadata(item.rule, rules::PRECEDENCE);
|
||||
result.insert(precedence_range.min);
|
||||
result.insert(precedence_range.max);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
53
src/compiler/build_tables/get_completion_status.cc
Normal file
53
src/compiler/build_tables/get_completion_status.cc
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#include "compiler/build_tables/get_completion_status.h"
|
||||
#include "compiler/rules/visitor.h"
|
||||
#include "compiler/rules/choice.h"
|
||||
#include "compiler/rules/seq.h"
|
||||
#include "compiler/rules/metadata.h"
|
||||
|
||||
namespace tree_sitter {
|
||||
namespace build_tables {
|
||||
|
||||
class GetCompletionStatus : public rules::RuleFn<CompletionStatus> {
|
||||
protected:
|
||||
CompletionStatus apply_to(const rules::Choice *rule) {
|
||||
for (const auto &element : rule->elements) {
|
||||
CompletionStatus status = apply(element);
|
||||
if (status.is_done)
|
||||
return status;
|
||||
}
|
||||
return { false, 0, rules::AssociativityNone };
|
||||
}
|
||||
|
||||
CompletionStatus apply_to(const rules::Metadata *rule) {
|
||||
CompletionStatus result = apply(rule->rule);
|
||||
if (result.is_done && !result.associativity) {
|
||||
result.precedence = rule->value_for(rules::PRECEDENCE);
|
||||
result.associativity =
|
||||
(rules::Associativity)(rule->value_for(rules::ASSOCIATIVITY));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CompletionStatus apply_to(const rules::Repeat *rule) {
|
||||
return { true, 0, rules::AssociativityNone };
|
||||
}
|
||||
|
||||
CompletionStatus apply_to(const rules::Blank *rule) {
|
||||
return { true, 0, rules::AssociativityNone };
|
||||
}
|
||||
|
||||
CompletionStatus apply_to(const rules::Seq *rule) {
|
||||
CompletionStatus left_status = apply(rule->left);
|
||||
if (left_status.is_done)
|
||||
return apply(rule->right);
|
||||
else
|
||||
return { false, 0, rules::AssociativityNone };
|
||||
}
|
||||
};
|
||||
|
||||
CompletionStatus get_completion_status(const rules::rule_ptr &rule) {
|
||||
return GetCompletionStatus().apply(rule);
|
||||
}
|
||||
|
||||
} // namespace build_tables
|
||||
} // namespace tree_sitter
|
||||
20
src/compiler/build_tables/get_completion_status.h
Normal file
20
src/compiler/build_tables/get_completion_status.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef COMPILER_BUILD_TABLES_GET_COMPLETION_STATUS_H_
|
||||
#define COMPILER_BUILD_TABLES_GET_COMPLETION_STATUS_H_
|
||||
|
||||
#include "tree_sitter/compiler.h"
|
||||
|
||||
namespace tree_sitter {
|
||||
namespace build_tables {
|
||||
|
||||
struct CompletionStatus {
|
||||
bool is_done;
|
||||
int precedence;
|
||||
rules::Associativity associativity;
|
||||
};
|
||||
|
||||
CompletionStatus get_completion_status(const rules::rule_ptr &);
|
||||
|
||||
} // namespace build_tables
|
||||
} // namespace tree_sitter
|
||||
|
||||
#endif // COMPILER_BUILD_TABLES_GET_COMPLETION_STATUS_H_
|
||||
|
|
@ -1,32 +1,74 @@
|
|||
#include "compiler/build_tables/get_metadata.h"
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include "compiler/rules/visitor.h"
|
||||
#include "compiler/rules/seq.h"
|
||||
#include "compiler/rules/repeat.h"
|
||||
#include "compiler/rules/choice.h"
|
||||
#include "compiler/build_tables/rule_can_be_blank.h"
|
||||
|
||||
namespace tree_sitter {
|
||||
namespace build_tables {
|
||||
|
||||
int get_metadata(const rules::rule_ptr &rule, rules::MetadataKey key) {
|
||||
class GetMetadata : public rules::RuleFn<int> {
|
||||
using std::pair;
|
||||
using std::string;
|
||||
using std::to_string;
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const MetadataRange &range) {
|
||||
return stream << string("{") << to_string(range.min) << string(", ")
|
||||
<< to_string(range.max) << string("}");
|
||||
}
|
||||
|
||||
MetadataRange get_metadata(const rules::rule_ptr &rule, rules::MetadataKey key) {
|
||||
class GetMetadata : public rules::RuleFn<pair<MetadataRange, bool>> {
|
||||
rules::MetadataKey metadata_key;
|
||||
|
||||
public:
|
||||
explicit GetMetadata(rules::MetadataKey key) : metadata_key(key) {}
|
||||
|
||||
protected:
|
||||
int apply_to(const rules::Metadata *rule) {
|
||||
int result = rule->value_for(metadata_key);
|
||||
return (result != 0) ? result : apply(rule->rule);
|
||||
pair<MetadataRange, bool> apply_to(const rules::Metadata *rule) {
|
||||
pair<MetadataRange, bool> result = apply(rule->rule);
|
||||
if (result.second) {
|
||||
return result;
|
||||
} else {
|
||||
int value = rule->value_for(metadata_key);
|
||||
return { { value, value }, value != 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// TODO -
|
||||
// Remove this. It is currently needed to make the rule generated
|
||||
// by `LexTableBuilder::after_separators` have the right precedence.
|
||||
int apply_to(const rules::Seq *rule) {
|
||||
return apply(rule->left);
|
||||
pair<MetadataRange, bool> apply_to(const rules::Choice *rule) {
|
||||
pair<MetadataRange, bool> result(MetadataRange(0, 0), false);
|
||||
for (const auto &element : rule->elements)
|
||||
merge_result(&result, apply(element));
|
||||
return result;
|
||||
}
|
||||
|
||||
pair<MetadataRange, bool> apply_to(const rules::Seq *rule) {
|
||||
pair<MetadataRange, bool> result = apply(rule->left);
|
||||
if (rule_can_be_blank(rule->left))
|
||||
merge_result(&result, apply(rule->right));
|
||||
return result;
|
||||
}
|
||||
|
||||
pair<MetadataRange, bool> apply_to(const rules::Repeat *rule) {
|
||||
return apply(rule->content);
|
||||
}
|
||||
|
||||
private:
|
||||
void merge_result(pair<MetadataRange, bool> *left,
|
||||
const pair<MetadataRange, bool> &right) {
|
||||
if (right.second) {
|
||||
if (!left->second || right.first.min < left->first.min)
|
||||
left->first.min = right.first.min;
|
||||
if (!left->second || right.first.max > left->first.max)
|
||||
left->first.max = right.first.max;
|
||||
left->second = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return GetMetadata(key).apply(rule);
|
||||
return GetMetadata(key).apply(rule).first;
|
||||
}
|
||||
|
||||
} // namespace build_tables
|
||||
|
|
|
|||
|
|
@ -7,7 +7,21 @@
|
|||
namespace tree_sitter {
|
||||
namespace build_tables {
|
||||
|
||||
int get_metadata(const rules::rule_ptr &, rules::MetadataKey);
|
||||
struct MetadataRange {
|
||||
MetadataRange() : MetadataRange(0, 0) {}
|
||||
MetadataRange(int min, int max) : min(min), max(max) {}
|
||||
|
||||
bool operator==(const MetadataRange &other) const {
|
||||
return min == other.min && max == other.max;
|
||||
}
|
||||
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const MetadataRange &range);
|
||||
|
||||
MetadataRange get_metadata(const rules::rule_ptr &, rules::MetadataKey);
|
||||
|
||||
} // namespace build_tables
|
||||
} // namespace tree_sitter
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
#include "compiler/build_tables/get_metadata.h"
|
||||
#include "compiler/build_tables/item.h"
|
||||
#include "compiler/build_tables/rule_can_be_blank.h"
|
||||
#include "compiler/rules/metadata.h"
|
||||
#include "tree_sitter/compiler.h"
|
||||
|
||||
namespace tree_sitter {
|
||||
|
|
@ -10,13 +7,5 @@ namespace build_tables {
|
|||
Item::Item(const rules::Symbol &lhs, const rules::rule_ptr rule)
|
||||
: lhs(lhs), rule(rule) {}
|
||||
|
||||
bool Item::is_done() const {
|
||||
return rule_can_be_blank(rule);
|
||||
}
|
||||
|
||||
int Item::precedence() const {
|
||||
return get_metadata(rule, rules::PRECEDENCE);
|
||||
}
|
||||
|
||||
} // namespace build_tables
|
||||
} // namespace tree_sitter
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ namespace build_tables {
|
|||
class Item {
|
||||
public:
|
||||
Item(const rules::Symbol &lhs, rules::rule_ptr rule);
|
||||
bool is_done() const;
|
||||
int precedence() const;
|
||||
|
||||
rules::Symbol lhs;
|
||||
rules::rule_ptr rule;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include "compiler/build_tables/lex_item.h"
|
||||
#include "compiler/build_tables/rule_can_be_blank.h"
|
||||
#include "compiler/build_tables/get_metadata.h"
|
||||
#include "compiler/rules/symbol.h"
|
||||
#include "compiler/rules/metadata.h"
|
||||
#include "compiler/rules/seq.h"
|
||||
|
|
@ -19,22 +19,7 @@ bool LexItem::operator==(const LexItem &other) const {
|
|||
}
|
||||
|
||||
bool LexItem::is_token_start() const {
|
||||
class IsTokenStart : public rules::RuleFn<bool> {
|
||||
bool apply_to(const rules::Seq *rule) {
|
||||
if (apply(rule->left))
|
||||
return true;
|
||||
else if (rule_can_be_blank(rule->left))
|
||||
return apply(rule->right);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool apply_to(const rules::Metadata *rule) {
|
||||
return rule->value_for(rules::START_TOKEN);
|
||||
}
|
||||
};
|
||||
|
||||
return IsTokenStart().apply(rule);
|
||||
return get_metadata(rule, rules::START_TOKEN).max > 0;
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &stream, const LexItem &item) {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,12 @@ pair<bool, ConflictType> ParseConflictManager::resolve(
|
|||
int min_precedence = *old_action.precedence_values.begin();
|
||||
int max_precedence = *old_action.precedence_values.rbegin();
|
||||
int new_precedence = *new_action.precedence_values.rbegin();
|
||||
if (new_precedence < min_precedence)
|
||||
if (new_precedence < min_precedence ||
|
||||
(new_precedence == min_precedence && min_precedence < max_precedence))
|
||||
return { false, ConflictTypeResolved };
|
||||
else if (new_precedence > max_precedence)
|
||||
else if (new_precedence > max_precedence ||
|
||||
(new_precedence == max_precedence &&
|
||||
min_precedence < max_precedence))
|
||||
return { true, ConflictTypeResolved };
|
||||
else if (min_precedence == max_precedence) {
|
||||
switch (new_action.associativity) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
#include "compiler/build_tables/parse_item.h"
|
||||
#include "compiler/build_tables/get_metadata.h"
|
||||
#include "compiler/rules/metadata.h"
|
||||
#include "tree_sitter/compiler.h"
|
||||
|
||||
namespace tree_sitter {
|
||||
|
|
@ -32,10 +30,6 @@ bool ParseItem::operator<(const ParseItem &other) const {
|
|||
return rule < other.rule;
|
||||
}
|
||||
|
||||
rules::Associativity ParseItem::associativity() const {
|
||||
return rules::Associativity(get_metadata(rule, rules::ASSOCIATIVITY));
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &stream, const ParseItem &item) {
|
||||
return stream << string("(item ") << item.lhs << string(" ") << *item.rule
|
||||
<< string(")");
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <vector>
|
||||
#include "compiler/build_tables/item.h"
|
||||
#include "compiler/rules/symbol.h"
|
||||
#include "compiler/rules/metadata.h"
|
||||
|
||||
namespace tree_sitter {
|
||||
namespace build_tables {
|
||||
|
|
@ -17,7 +16,6 @@ class ParseItem : public Item {
|
|||
const std::vector<rules::Symbol> &consumed_symbols);
|
||||
bool operator==(const ParseItem &other) const;
|
||||
bool operator<(const ParseItem &other) const;
|
||||
rules::Associativity associativity() const;
|
||||
std::vector<rules::Symbol> consumed_symbols;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue