Test p_parse_inner with tree generation mode active
This commit is contained in:
parent
2f9a29f7c5
commit
fb4288d5b9
@ -1639,6 +1639,25 @@ EOF
|
|||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "parse_inner APIs work in tree generation mode with follow tokens" do
|
||||||
|
write_grammar <<EOF
|
||||||
|
tree;
|
||||||
|
token a;
|
||||||
|
token b;
|
||||||
|
start Start;
|
||||||
|
start R1;
|
||||||
|
Start -> R1 a;
|
||||||
|
Start -> R2 b;
|
||||||
|
R1 -> a b;
|
||||||
|
R2 -> a b;
|
||||||
|
EOF
|
||||||
|
run_propane(language: language)
|
||||||
|
compile("spec/test_parse_inner_tree.#{language}", language: language)
|
||||||
|
results = run_test(language: language)
|
||||||
|
expect(results.stderr).to eq ""
|
||||||
|
expect(results.status).to eq 0
|
||||||
|
end
|
||||||
|
|
||||||
it "allows multiple starting rules in tree mode" do
|
it "allows multiple starting rules in tree mode" do
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
tree;
|
tree;
|
||||||
|
|||||||
89
spec/test_parse_inner_tree.c
Normal file
89
spec/test_parse_inner_tree.c
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "testparser.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
/* Grammar (tree generation mode, shared reduce state):
|
||||||
|
* tree;
|
||||||
|
* token a; token b;
|
||||||
|
* start Start;
|
||||||
|
* start R1;
|
||||||
|
* Start -> R1 a;
|
||||||
|
* Start -> R2 b;
|
||||||
|
* R1 -> a b;
|
||||||
|
* R2 -> a b;
|
||||||
|
*
|
||||||
|
* Exercises p_parse_inner_R1() with a non-EOF follow token in tree
|
||||||
|
* generation mode. Verifies:
|
||||||
|
* * The reduced tree for R1 is well-formed after a follow-token
|
||||||
|
* completion.
|
||||||
|
* * The follow token is not consumed and remains available for a
|
||||||
|
* subsequent p_lex() call.
|
||||||
|
* * p_tree_delete_R1() cleans up the returned tree without leaks
|
||||||
|
* (verified in CI via valgrind). */
|
||||||
|
|
||||||
|
/* Baseline: p_parse_R1 works on "ab" and the returned tree is
|
||||||
|
* well-formed. */
|
||||||
|
{
|
||||||
|
char const * input = "ab";
|
||||||
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
assert(p_parse_R1(context) == P_SUCCESS);
|
||||||
|
R1 * tree = p_result_R1(context);
|
||||||
|
assert_not_null(tree);
|
||||||
|
assert_not_null(tree->pToken1);
|
||||||
|
assert_eq((size_t)TOKEN_a, (size_t)tree->pToken1->token);
|
||||||
|
assert_not_null(tree->pToken2);
|
||||||
|
assert_eq((size_t)TOKEN_b, (size_t)tree->pToken2->token);
|
||||||
|
p_tree_delete_R1(tree);
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primary case: p_parse_inner_R1 with a non-EOF follow token completes
|
||||||
|
* the parse, returns a well-formed tree, and leaves the follow token
|
||||||
|
* unconsumed. */
|
||||||
|
{
|
||||||
|
char const * input = "abb";
|
||||||
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
p_token_t follow_tokens[] = { TOKEN_b };
|
||||||
|
assert(p_parse_inner_R1(context, follow_tokens, 1u) == P_SUCCESS);
|
||||||
|
|
||||||
|
/* Tree is well-formed. */
|
||||||
|
R1 * tree = p_result_R1(context);
|
||||||
|
assert_not_null(tree);
|
||||||
|
assert_not_null(tree->pToken1);
|
||||||
|
assert_eq((size_t)TOKEN_a, (size_t)tree->pToken1->token);
|
||||||
|
assert_eq(1u, (size_t)tree->pToken1->position.row);
|
||||||
|
assert_eq(1u, (size_t)tree->pToken1->position.col);
|
||||||
|
assert_not_null(tree->pToken2);
|
||||||
|
assert_eq((size_t)TOKEN_b, (size_t)tree->pToken2->token);
|
||||||
|
assert_eq(1u, (size_t)tree->pToken2->position.row);
|
||||||
|
assert_eq(2u, (size_t)tree->pToken2->position.col);
|
||||||
|
|
||||||
|
/* The R1 tree covers positions 1..2 — the third `b` at column 3 is
|
||||||
|
* the follow token and is not part of the tree. */
|
||||||
|
assert_eq(1u, (size_t)tree->position.row);
|
||||||
|
assert_eq(1u, (size_t)tree->position.col);
|
||||||
|
assert_eq(1u, (size_t)tree->end_position.row);
|
||||||
|
assert_eq(2u, (size_t)tree->end_position.col);
|
||||||
|
|
||||||
|
/* Follow token remains in the input. */
|
||||||
|
p_position_t pos = p_position(context);
|
||||||
|
assert_eq(1u, (size_t)pos.row);
|
||||||
|
assert_eq(3u, (size_t)pos.col);
|
||||||
|
p_token_info_t token_info;
|
||||||
|
assert(p_lex(context, &token_info) == P_SUCCESS);
|
||||||
|
assert_eq((size_t)TOKEN_b, (size_t)token_info.token);
|
||||||
|
assert_eq(1u, (size_t)token_info.position.row);
|
||||||
|
assert_eq(3u, (size_t)token_info.position.col);
|
||||||
|
|
||||||
|
/* p_tree_delete_R1 must free every node reachable from the tree
|
||||||
|
* without leaking anything. valgrind (invoked by the spec runner on
|
||||||
|
* Linux) will detect any missed frees. */
|
||||||
|
p_tree_delete_R1(tree);
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
68
spec/test_parse_inner_tree.d
Normal file
68
spec/test_parse_inner_tree.d
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import testparser;
|
||||||
|
import std.stdio;
|
||||||
|
import testutils;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
/* See test_parse_inner_tree.c for details on the grammar and cases. */
|
||||||
|
|
||||||
|
/* Baseline: p_parse_R1 works on "ab". */
|
||||||
|
{
|
||||||
|
string input = "ab";
|
||||||
|
p_context_t * context = p_context_new(input);
|
||||||
|
assert(p_parse_R1(context) == P_SUCCESS);
|
||||||
|
R1 * tree = p_result_R1(context);
|
||||||
|
assert(tree !is null);
|
||||||
|
assert(tree.pToken1 !is null);
|
||||||
|
assert(tree.pToken1.token == TOKEN_a);
|
||||||
|
assert(tree.pToken2 !is null);
|
||||||
|
assert(tree.pToken2.token == TOKEN_b);
|
||||||
|
p_tree_delete_R1(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primary case: p_parse_inner_R1 with a non-EOF follow token completes
|
||||||
|
* the parse, returns a well-formed tree, and leaves the follow token
|
||||||
|
* unconsumed. */
|
||||||
|
{
|
||||||
|
string input = "abb";
|
||||||
|
p_context_t * context = p_context_new(input);
|
||||||
|
p_token_t[] follow_tokens = [TOKEN_b];
|
||||||
|
assert(p_parse_inner_R1(context, follow_tokens) == P_SUCCESS);
|
||||||
|
|
||||||
|
/* Tree is well-formed. */
|
||||||
|
R1 * tree = p_result_R1(context);
|
||||||
|
assert(tree !is null);
|
||||||
|
assert(tree.pToken1 !is null);
|
||||||
|
assert(tree.pToken1.token == TOKEN_a);
|
||||||
|
assert(tree.pToken1.position.row == 1);
|
||||||
|
assert(tree.pToken1.position.col == 1);
|
||||||
|
assert(tree.pToken2 !is null);
|
||||||
|
assert(tree.pToken2.token == TOKEN_b);
|
||||||
|
assert(tree.pToken2.position.row == 1);
|
||||||
|
assert(tree.pToken2.position.col == 2);
|
||||||
|
|
||||||
|
/* The R1 tree covers positions 1..2. The third `b` at column 3 is
|
||||||
|
* the follow token and is not part of the tree. */
|
||||||
|
assert(tree.position.row == 1);
|
||||||
|
assert(tree.position.col == 1);
|
||||||
|
assert(tree.end_position.row == 1);
|
||||||
|
assert(tree.end_position.col == 2);
|
||||||
|
|
||||||
|
/* Follow token remains in the input. */
|
||||||
|
p_position_t pos = p_position(context);
|
||||||
|
assert(pos.row == 1);
|
||||||
|
assert(pos.col == 3);
|
||||||
|
p_token_info_t token_info;
|
||||||
|
assert(p_lex(context, &token_info) == P_SUCCESS);
|
||||||
|
assert(token_info.token == TOKEN_b);
|
||||||
|
assert(token_info.position.row == 1);
|
||||||
|
assert(token_info.position.col == 3);
|
||||||
|
|
||||||
|
p_tree_delete_R1(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user