Require that grammars' start rules be visible
This commit is contained in:
parent
1dca3a0b58
commit
94dc703bfc
12 changed files with 64 additions and 56 deletions
|
|
@ -16,6 +16,7 @@ typedef enum {
|
||||||
TSCompileErrorTypeParseConflict,
|
TSCompileErrorTypeParseConflict,
|
||||||
TSCompileErrorTypeEpsilonRule,
|
TSCompileErrorTypeEpsilonRule,
|
||||||
TSCompileErrorTypeInvalidTokenContents,
|
TSCompileErrorTypeInvalidTokenContents,
|
||||||
|
TSCompileErrorTypeInvalidRuleName,
|
||||||
} TSCompileErrorType;
|
} TSCompileErrorType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,9 @@ pair<InternedGrammar, CompileError> intern_symbols(const InputGrammar &grammar)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (grammar.variables[0].name[0] == '_') {
|
||||||
|
return {result, CompileError(TSCompileErrorTypeInvalidRuleName, "A grammar's start rule must be visible.")};
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &variable : grammar.variables) {
|
for (auto &variable : grammar.variables) {
|
||||||
auto new_rule = interner.apply(variable.rule);
|
auto new_rule = interner.apply(variable.rule);
|
||||||
|
|
|
||||||
|
|
@ -159,10 +159,7 @@ void ts_document_invalidate(TSDocument *self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TSNode ts_document_root_node(const TSDocument *self) {
|
TSNode ts_document_root_node(const TSDocument *self) {
|
||||||
TSNode result = ts_node_make(self->tree, 0, 0, 0);
|
return ts_node_make(self->tree, 0, 0, 0);
|
||||||
while (result.data && !((Tree *)result.data)->visible)
|
|
||||||
result = ts_node_named_child(result, 0);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ts_document_parse_count(const TSDocument *self) {
|
uint32_t ts_document_parse_count(const TSDocument *self) {
|
||||||
|
|
|
||||||
|
|
@ -148,20 +148,11 @@ static Iterator iterator_new(TreePath *path, Tree *tree, const TSLanguage *langu
|
||||||
.child_index = 0,
|
.child_index = 0,
|
||||||
.structural_child_index = 0,
|
.structural_child_index = 0,
|
||||||
}));
|
}));
|
||||||
|
return (Iterator) {
|
||||||
Iterator self = {
|
|
||||||
.path = *path,
|
.path = *path,
|
||||||
.language = language,
|
.language = language,
|
||||||
.visible_depth = 0,
|
.visible_depth = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tree->visible) {
|
|
||||||
self.visible_depth++;
|
|
||||||
} else {
|
|
||||||
iterator_descend(&self, length_zero());
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Length iterator_start_position(Iterator *self) {
|
Length iterator_start_position(Iterator *self) {
|
||||||
|
|
|
||||||
13
test/fixtures/error_corpus/json_errors.txt
vendored
13
test/fixtures/error_corpus/json_errors.txt
vendored
|
|
@ -25,10 +25,11 @@ errors inside arrays
|
||||||
[1, , 2]
|
[1, , 2]
|
||||||
|
|
||||||
---
|
---
|
||||||
(array
|
|
||||||
|
(value (array
|
||||||
(number)
|
(number)
|
||||||
(ERROR)
|
(ERROR)
|
||||||
(number))
|
(number)))
|
||||||
|
|
||||||
==========================================
|
==========================================
|
||||||
errors inside objects
|
errors inside objects
|
||||||
|
|
@ -38,7 +39,9 @@ errors inside objects
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
(object (pair (string) (number)) (ERROR (UNEXPECTED 'o')))
|
(value (object
|
||||||
|
(pair (string) (number))
|
||||||
|
(ERROR (UNEXPECTED 'o'))))
|
||||||
|
|
||||||
==========================================
|
==========================================
|
||||||
errors inside nested objects
|
errors inside nested objects
|
||||||
|
|
@ -48,9 +51,9 @@ errors inside nested objects
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
(object
|
(value (object
|
||||||
(pair (string) (object
|
(pair (string) (object
|
||||||
(pair (string) (number))
|
(pair (string) (number))
|
||||||
(ERROR (number))))
|
(ERROR (number))))
|
||||||
(ERROR)
|
(ERROR)
|
||||||
(pair (string) (number)))
|
(pair (string) (number))))
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
"name": "statement"
|
"name": "statement"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"statement": {
|
"statement": {
|
||||||
"type": "SEQ",
|
"type": "SEQ",
|
||||||
"members": [
|
"members": [
|
||||||
|
|
|
||||||
1
test/fixtures/test_grammars/invisible_start_rule/expected_error.txt
vendored
Normal file
1
test/fixtures/test_grammars/invisible_start_rule/expected_error.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
A grammar's start rule must be visible.
|
||||||
23
test/fixtures/test_grammars/invisible_start_rule/grammar.json
vendored
Normal file
23
test/fixtures/test_grammars/invisible_start_rule/grammar.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "invisible_start_rule",
|
||||||
|
|
||||||
|
"rules": {
|
||||||
|
"_value": {
|
||||||
|
"type": "CHOICE",
|
||||||
|
"members": [
|
||||||
|
{"type": "SYMBOL", "name": "a"},
|
||||||
|
{"type": "SYMBOL", "name": "b"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"a": {
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "a"
|
||||||
|
},
|
||||||
|
|
||||||
|
"b": {
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,19 +14,7 @@
|
||||||
|
|
||||||
static void assert_correct_tree_size(TSDocument *document, string content) {
|
static void assert_correct_tree_size(TSDocument *document, string content) {
|
||||||
TSNode root_node = ts_document_root_node(document);
|
TSNode root_node = ts_document_root_node(document);
|
||||||
size_t expected_size = content.size();
|
AssertThat(ts_node_end_byte(root_node), Equals(content.size()));
|
||||||
|
|
||||||
// In the JSON grammar, the start rule (`_value`) is hidden, so the node
|
|
||||||
// returned from `ts_document_root_node` (e.g. an `object` node), does not
|
|
||||||
// actually point to the root of the tree. In this weird case, trailing
|
|
||||||
// whitespace is not included in the root node's size.
|
|
||||||
//
|
|
||||||
// TODO: Fix this inconsistency. Maybe disallow the start rule being hidden?
|
|
||||||
if (ts_document_language(document) == load_real_language("json") &&
|
|
||||||
string(ts_node_type(root_node, document)) != "ERROR")
|
|
||||||
expected_size = content.find_last_not_of("\n ") + 1;
|
|
||||||
|
|
||||||
AssertThat(ts_node_end_byte(root_node), Equals(expected_size));
|
|
||||||
assert_consistent_tree_sizes(root_node);
|
assert_consistent_tree_sizes(root_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ describe("Document", [&]() {
|
||||||
root = ts_document_root_node(document);
|
root = ts_document_root_node(document);
|
||||||
assert_node_string_equals(
|
assert_node_string_equals(
|
||||||
root,
|
root,
|
||||||
"(object (pair (string) (array (number) (number))))");
|
"(value (object (pair (string) (array (number) (number)))))");
|
||||||
});
|
});
|
||||||
|
|
||||||
after_each([&]() {
|
after_each([&]() {
|
||||||
|
|
@ -71,7 +71,7 @@ describe("Document", [&]() {
|
||||||
root = ts_document_root_node(document);
|
root = ts_document_root_node(document);
|
||||||
assert_node_string_equals(
|
assert_node_string_equals(
|
||||||
root,
|
root,
|
||||||
"(array (true) (false))");
|
"(value (array (true) (false)))");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles truncated UTF16 data", [&]() {
|
it("handles truncated UTF16 data", [&]() {
|
||||||
|
|
@ -96,14 +96,14 @@ describe("Document", [&]() {
|
||||||
ts_document_parse(document);
|
ts_document_parse(document);
|
||||||
|
|
||||||
TSNode root = ts_document_root_node(document);
|
TSNode root = ts_document_root_node(document);
|
||||||
AssertThat(ts_node_end_point(root), Equals<TSPoint>({0, 13}));
|
AssertThat(ts_node_end_point(root), Equals<TSPoint>({0, 14}));
|
||||||
|
|
||||||
input.measure_columns_in_bytes = true;
|
input.measure_columns_in_bytes = true;
|
||||||
ts_document_set_input(document, input);
|
ts_document_set_input(document, input);
|
||||||
ts_document_invalidate(document);
|
ts_document_invalidate(document);
|
||||||
ts_document_parse(document);
|
ts_document_parse(document);
|
||||||
root = ts_document_root_node(document);
|
root = ts_document_root_node(document);
|
||||||
AssertThat(ts_node_end_point(root), Equals<TSPoint>({0, 26}));
|
AssertThat(ts_node_end_point(root), Equals<TSPoint>({0, 28}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows the input to be retrieved later", [&]() {
|
it("allows the input to be retrieved later", [&]() {
|
||||||
|
|
@ -135,7 +135,7 @@ describe("Document", [&]() {
|
||||||
TSNode new_root = ts_document_root_node(document);
|
TSNode new_root = ts_document_root_node(document);
|
||||||
assert_node_string_equals(
|
assert_node_string_equals(
|
||||||
new_root,
|
new_root,
|
||||||
"(object (pair (string) (array (null) (number))))");
|
"(value (object (pair (string) (array (null) (number)))))");
|
||||||
AssertThat(spy_input->strings_read(), Equals(vector<string>({" [null, 2" })));
|
AssertThat(spy_input->strings_read(), Equals(vector<string>({" [null, 2" })));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -147,7 +147,7 @@ describe("Document", [&]() {
|
||||||
AssertThat(ts_node_end_char(new_root), Equals<size_t>(1));
|
AssertThat(ts_node_end_char(new_root), Equals<size_t>(1));
|
||||||
assert_node_string_equals(
|
assert_node_string_equals(
|
||||||
new_root,
|
new_root,
|
||||||
"(number)");
|
"(value (number))");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reads from the new input correctly when the old input was blank", [&]() {
|
it("reads from the new input correctly when the old input was blank", [&]() {
|
||||||
|
|
@ -165,7 +165,7 @@ describe("Document", [&]() {
|
||||||
AssertThat(ts_node_end_char(new_root), Equals<size_t>(1));
|
AssertThat(ts_node_end_char(new_root), Equals<size_t>(1));
|
||||||
assert_node_string_equals(
|
assert_node_string_equals(
|
||||||
new_root,
|
new_root,
|
||||||
"(number)");
|
"(value (number))");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -181,7 +181,7 @@ describe("Document", [&]() {
|
||||||
root = ts_document_root_node(document);
|
root = ts_document_root_node(document);
|
||||||
assert_node_string_equals(
|
assert_node_string_equals(
|
||||||
root,
|
root,
|
||||||
"(object (pair (string) (array (number) (number))))");
|
"(value (object (pair (string) (array (number) (number)))))");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("clears out any previous tree", [&]() {
|
it("clears out any previous tree", [&]() {
|
||||||
|
|
@ -405,7 +405,7 @@ describe("Document", [&]() {
|
||||||
root = ts_document_root_node(document);
|
root = ts_document_root_node(document);
|
||||||
assert_node_string_equals(
|
assert_node_string_equals(
|
||||||
root,
|
root,
|
||||||
"(array (number) (null) (ERROR (UNEXPECTED 'e')) (number))");
|
"(value (array (number) (null) (ERROR (UNEXPECTED 'e')) (number)))");
|
||||||
|
|
||||||
ts_document_invalidate(document);
|
ts_document_invalidate(document);
|
||||||
|
|
||||||
|
|
@ -432,7 +432,7 @@ describe("Document", [&]() {
|
||||||
root = ts_document_root_node(document);
|
root = ts_document_root_node(document);
|
||||||
assert_node_string_equals(
|
assert_node_string_equals(
|
||||||
root,
|
root,
|
||||||
"(array (number) (null) (number))");
|
"(value (array (number) (null) (number)))");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ describe("Node", [&]() {
|
||||||
ts_document_set_language(document, load_real_language("json"));
|
ts_document_set_language(document, load_real_language("json"));
|
||||||
ts_document_set_input_string(document, json_string.c_str());
|
ts_document_set_input_string(document, json_string.c_str());
|
||||||
ts_document_parse(document);
|
ts_document_parse(document);
|
||||||
root_node = ts_document_root_node(document);
|
root_node = ts_node_child(ts_document_root_node(document), 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
after_each([&]() {
|
after_each([&]() {
|
||||||
|
|
@ -161,7 +161,7 @@ describe("Node", [&]() {
|
||||||
AssertThat(ts_node_parent(number_node), Equals(root_node));
|
AssertThat(ts_node_parent(number_node), Equals(root_node));
|
||||||
AssertThat(ts_node_parent(false_node), Equals(root_node));
|
AssertThat(ts_node_parent(false_node), Equals(root_node));
|
||||||
AssertThat(ts_node_parent(object_node), Equals(root_node));
|
AssertThat(ts_node_parent(object_node), Equals(root_node));
|
||||||
AssertThat(ts_node_parent(root_node).data, Equals<void *>(nullptr));
|
AssertThat(ts_node_parent(ts_document_root_node(document)).data, Equals<void *>(nullptr));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("works correctly when the node contains aliased children and extras", [&]() {
|
it("works correctly when the node contains aliased children and extras", [&]() {
|
||||||
|
|
@ -293,7 +293,7 @@ describe("Node", [&]() {
|
||||||
AssertThat(ts_node_parent(child5), Equals(root_node));
|
AssertThat(ts_node_parent(child5), Equals(root_node));
|
||||||
AssertThat(ts_node_parent(child6), Equals(root_node));
|
AssertThat(ts_node_parent(child6), Equals(root_node));
|
||||||
AssertThat(ts_node_parent(child7), Equals(root_node));
|
AssertThat(ts_node_parent(child7), Equals(root_node));
|
||||||
AssertThat(ts_node_parent(root_node).data, Equals<void *>(nullptr));
|
AssertThat(ts_node_parent(ts_document_root_node(document)).data, Equals<void *>(nullptr));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,9 @@ describe("Parser", [&]() {
|
||||||
set_text(" [123, @@@@@, true]");
|
set_text(" [123, @@@@@, true]");
|
||||||
|
|
||||||
assert_root_node(
|
assert_root_node(
|
||||||
"(array (number) (ERROR (UNEXPECTED '@')) (true))");
|
"(value (array (number) (ERROR (UNEXPECTED '@')) (true)))");
|
||||||
|
|
||||||
TSNode error = ts_node_named_child(root, 1);
|
TSNode error = ts_node_named_child(ts_node_child(root, 0), 1);
|
||||||
AssertThat(ts_node_type(error, document), Equals("ERROR"));
|
AssertThat(ts_node_type(error, document), Equals("ERROR"));
|
||||||
AssertThat(get_node_text(error), Equals(", @@@@@"));
|
AssertThat(get_node_text(error), Equals(", @@@@@"));
|
||||||
AssertThat(ts_node_child_count(error), Equals<size_t>(2));
|
AssertThat(ts_node_child_count(error), Equals<size_t>(2));
|
||||||
|
|
@ -100,7 +100,7 @@ describe("Parser", [&]() {
|
||||||
TSNode garbage = ts_node_child(error, 1);
|
TSNode garbage = ts_node_child(error, 1);
|
||||||
AssertThat(get_node_text(garbage), Equals("@@@@@"));
|
AssertThat(get_node_text(garbage), Equals("@@@@@"));
|
||||||
|
|
||||||
TSNode node_after_error = ts_node_named_child(root, 2);
|
TSNode node_after_error = ts_node_next_named_sibling(error);
|
||||||
AssertThat(ts_node_type(node_after_error, document), Equals("true"));
|
AssertThat(ts_node_type(node_after_error, document), Equals("true"));
|
||||||
AssertThat(get_node_text(node_after_error), Equals("true"));
|
AssertThat(get_node_text(node_after_error), Equals("true"));
|
||||||
});
|
});
|
||||||
|
|
@ -112,9 +112,9 @@ describe("Parser", [&]() {
|
||||||
set_text(" [123, faaaaalse, true]");
|
set_text(" [123, faaaaalse, true]");
|
||||||
|
|
||||||
assert_root_node(
|
assert_root_node(
|
||||||
"(array (number) (ERROR (UNEXPECTED 'a')) (true))");
|
"(value (array (number) (ERROR (UNEXPECTED 'a')) (true)))");
|
||||||
|
|
||||||
TSNode error = ts_node_named_child(root, 1);
|
TSNode error = ts_node_named_child(ts_node_child(root, 0), 1);
|
||||||
AssertThat(ts_node_type(error, document), Equals("ERROR"));
|
AssertThat(ts_node_type(error, document), Equals("ERROR"));
|
||||||
AssertThat(ts_node_child_count(error), Equals<size_t>(2));
|
AssertThat(ts_node_child_count(error), Equals<size_t>(2));
|
||||||
|
|
||||||
|
|
@ -126,7 +126,7 @@ describe("Parser", [&]() {
|
||||||
AssertThat(ts_node_type(garbage, document), Equals("ERROR"));
|
AssertThat(ts_node_type(garbage, document), Equals("ERROR"));
|
||||||
AssertThat(get_node_text(garbage), Equals("faaaaalse"));
|
AssertThat(get_node_text(garbage), Equals("faaaaalse"));
|
||||||
|
|
||||||
TSNode last = ts_node_named_child(root, 2);
|
TSNode last = ts_node_next_named_sibling(error);
|
||||||
AssertThat(ts_node_type(last, document), Equals("true"));
|
AssertThat(ts_node_type(last, document), Equals("true"));
|
||||||
AssertThat(ts_node_start_byte(last), Equals(strlen(" [123, faaaaalse, ")));
|
AssertThat(ts_node_start_byte(last), Equals(strlen(" [123, faaaaalse, ")));
|
||||||
});
|
});
|
||||||
|
|
@ -138,14 +138,14 @@ describe("Parser", [&]() {
|
||||||
set_text(" [123, true false, true]");
|
set_text(" [123, true false, true]");
|
||||||
|
|
||||||
assert_root_node(
|
assert_root_node(
|
||||||
"(array (number) (true) (ERROR (false)) (true))");
|
"(value (array (number) (true) (ERROR (false)) (true)))");
|
||||||
|
|
||||||
TSNode error = ts_node_named_child(root, 2);
|
TSNode error = ts_node_named_child(ts_node_child(root, 0), 2);
|
||||||
AssertThat(ts_node_type(error, document), Equals("ERROR"));
|
AssertThat(ts_node_type(error, document), Equals("ERROR"));
|
||||||
AssertThat(get_node_text(error), Equals("false"));
|
AssertThat(get_node_text(error), Equals("false"));
|
||||||
AssertThat(ts_node_child_count(error), Equals<size_t>(1));
|
AssertThat(ts_node_child_count(error), Equals<size_t>(1));
|
||||||
|
|
||||||
TSNode last = ts_node_named_child(root, 1);
|
TSNode last = ts_node_next_named_sibling(error);
|
||||||
AssertThat(ts_node_type(last, document), Equals("true"));
|
AssertThat(ts_node_type(last, document), Equals("true"));
|
||||||
AssertThat(get_node_text(last), Equals("true"));
|
AssertThat(get_node_text(last), Equals("true"));
|
||||||
});
|
});
|
||||||
|
|
@ -157,7 +157,7 @@ describe("Parser", [&]() {
|
||||||
set_text(" [123, \"hi\n, true]");
|
set_text(" [123, \"hi\n, true]");
|
||||||
|
|
||||||
assert_root_node(
|
assert_root_node(
|
||||||
"(array (number) (ERROR (UNEXPECTED '\\n')) (true))");
|
"(value (array (number) (ERROR (UNEXPECTED '\\n')) (true)))");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue