Rework tree generation mode and API

Store tree nodes in congruent, compact arena array.
Define handle types to refer to tree nodes rather than pointers to
structure instances.
Free tree with context.
This commit is contained in:
josh committed 2026-07-27 20:57:44 -04:00
1 parent dddb1b5089
commit 5fc712c6ee
39 files changed
+1349 -811

No files matched your search

+30 -43
View File
@@ -3,21 +3,8 @@
#include <string.h>
#include "testutils.h"
/* Grammar (tree generation mode; parentheses handled by the lex function):
* tree;
* lex_fn mylexfn;
* token lparen /\(/; token rparen /\)/; token plus /\+/; token num /\d+/;
* Start -> Expr;
* Expr -> num;
* Expr -> Expr plus num;
*
* The same lexer-driven nested parse as test_parse_inner_nested, but in tree
* generation mode. Each "( ... )" group is parsed by a reentrant
* p_parse_inner_Start() call from the lex function; the resulting subtree is
* discarded and a single synthesized num token is handed to the outer parse.
* The synthesized token's position is set to span the whole group ('(' start
* through ')' end), so this verifies that positions survive the nested-parse
* boundary and land correctly in the outer tree. */
/* Grammar: see the D variant / spec. Tree generation mode; parentheses handled
* by the lex function. Tree nodes live in the context arena. */
size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
{
@@ -36,8 +23,8 @@ size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
{
return inner_result;
}
Start * inner = p_result_Start(context);
assert_not_null(inner);
Start inner = p_result_Start(context);
assert(p_node_valid(inner));
/* p_parse_inner rewound the input so that ')' was not consumed; consume
* it now. */
p_token_info_t rparen_info;
@@ -45,10 +32,11 @@ size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
assert(rparen_result == P_SUCCESS);
assert(rparen_info.token == TOKEN_rparen);
/* The subtree covers the region strictly between the parentheses. */
assert_eq((size_t)(start_position.col + 1u), (size_t)inner->position.col);
assert_eq((size_t)(rparen_info.position.col - 1u), (size_t)inner->end_position.col);
p_tree_delete_Start(inner);
/* Synthesize a num token spanning the entire "( ... )" group. */
assert_eq((size_t)(start_position.col + 1u), (size_t)p_node_position(inner).col);
assert_eq((size_t)(rparen_info.position.col - 1u), (size_t)p_node_end_position(inner).col);
/* The inner subtree is discarded (the lexer synthesizes a num token in
* its place), but its nodes remain in the shared context arena and are
* freed with the context. */
out_token_info->token = TOKEN_num;
out_token_info->position = start_position;
out_token_info->end_position = rparen_info.end_position;
@@ -64,40 +52,39 @@ int main()
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(context) == P_SUCCESS);
Start * tree = p_result(context);
assert_not_null(tree);
Start tree = p_result(context);
assert(p_node_valid(tree));
/* Start -> Expr, where the top Expr is "Expr plus num". */
Expr * top = tree->pExpr;
assert_not_null(top);
assert_not_null(top->pExpr);
assert_not_null(top->pToken2);
assert_not_null(top->pToken3);
Expr top = p_Start_pExpr(tree);
assert(p_node_valid(top));
assert(p_node_valid(p_Expr_pExpr(top)));
assert(p_node_valid(p_Expr_pToken2(top)));
assert(p_node_valid(p_Expr_pToken3(top)));
/* The '+' joining the two groups is at column 9. */
assert_eq(1u, (size_t)top->pToken2->position.row);
assert_eq(9u, (size_t)top->pToken2->position.col);
assert_eq(1u, (size_t)p_node_position(p_Expr_pToken2(top)).row);
assert_eq(9u, (size_t)p_node_position(p_Expr_pToken2(top)).col);
/* Right operand: synthesized num for "(5 + 6)", spanning columns 11..17. */
assert_eq(1u, (size_t)top->pToken3->position.row);
assert_eq(11u, (size_t)top->pToken3->position.col);
assert_eq(1u, (size_t)top->pToken3->end_position.row);
assert_eq(17u, (size_t)top->pToken3->end_position.col);
assert_eq(1u, (size_t)p_node_position(p_Expr_pToken3(top)).row);
assert_eq(11u, (size_t)p_node_position(p_Expr_pToken3(top)).col);
assert_eq(1u, (size_t)p_node_end_position(p_Expr_pToken3(top)).row);
assert_eq(17u, (size_t)p_node_end_position(p_Expr_pToken3(top)).col);
/* Left operand: Expr -> num, the synthesized num for "(3 + 4)", spanning
* columns 1..7. */
Expr * left = top->pExpr;
assert_not_null(left->pToken1);
assert_eq(1u, (size_t)left->pToken1->position.row);
assert_eq(1u, (size_t)left->pToken1->position.col);
assert_eq(1u, (size_t)left->pToken1->end_position.row);
assert_eq(7u, (size_t)left->pToken1->end_position.col);
Expr left = p_Expr_pExpr(top);
assert(p_node_valid(p_Expr_pToken1(left)));
assert_eq(1u, (size_t)p_node_position(p_Expr_pToken1(left)).row);
assert_eq(1u, (size_t)p_node_position(p_Expr_pToken1(left)).col);
assert_eq(1u, (size_t)p_node_end_position(p_Expr_pToken1(left)).row);
assert_eq(7u, (size_t)p_node_end_position(p_Expr_pToken1(left)).col);
/* The whole tree spans columns 1..17. */
assert_eq(1u, (size_t)tree->position.col);
assert_eq(17u, (size_t)tree->end_position.col);
assert_eq(1u, (size_t)p_node_position(tree).col);
assert_eq(17u, (size_t)p_node_end_position(tree).col);
p_tree_delete(tree);
p_context_delete(context);
return 0;