diff --git a/assets/parser.c.erb b/assets/parser.c.erb index bcbd269..96c184b 100644 --- a/assets/parser.c.erb +++ b/assets/parser.c.erb @@ -695,11 +695,13 @@ typedef struct } state_value_t; /** Common AST node structure. */ -typedef struct +typedef struct ASTNode_s { <%= @grammar.prefix %>position_t position; <%= @grammar.prefix %>position_t end_position; - void * fields[]; + uint16_t n_fields; + uint8_t is_token; + struct ASTNode_s * fields[]; } ASTNode; /** Parser shift table. */ @@ -992,6 +994,8 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context) <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> * token_ast_node = (<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> *)malloc(sizeof(<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>)); token_ast_node->position = token_info.position; token_ast_node->end_position = token_info.end_position; + token_ast_node->n_fields = 0u; + token_ast_node->is_token = 1u; token_ast_node->token = token; token_ast_node->pvalue = token_info.pvalue; state_values_stack_index(&statevalues, -1)->ast_node = token_ast_node; @@ -1028,31 +1032,30 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context) else if (parser_reduce_table[reduce_index].n_states > 0) { size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size; - ASTNode * node = (ASTNode *)malloc(sizeof(ASTNode) + n_fields * sizeof(void *)); + size_t bytes = sizeof(ASTNode) + n_fields * sizeof(void *); + ASTNode * node = (ASTNode *)malloc(bytes); + memset(node, 0, bytes); node->position = INVALID_POSITION; node->end_position = INVALID_POSITION; - for (size_t i = 0; i < n_fields; i++) - { - node->fields[i] = NULL; - } + node->n_fields = n_fields; if (parser_reduce_table[reduce_index].rule_set_node_field_index_map == NULL) { for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++) { - node->fields[i] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node; + node->fields[i] = (ASTNode *)state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node; } } else { for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++) { - node->fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node; + node->fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = (ASTNode *)state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node; } } bool position_found = false; for (size_t i = 0; i < n_fields; i++) { - ASTNode * child = (ASTNode *)node->fields[i]; + ASTNode * child = node->fields[i]; if ((child != NULL) && <%= @grammar.prefix %>position_valid(child->position)) { if (!position_found) @@ -1074,6 +1077,7 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context) memset(&reduced_parser_value2, 0, sizeof(reduced_parser_value2)); if (parser_user_code(&reduced_parser_value2, parser_reduce_table[reduce_index].rule, &statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED) { + state_values_stack_free(&statevalues); return P_USER_TERMINATED; } reduced_parser_value = reduced_parser_value2; @@ -1153,3 +1157,32 @@ size_t <%= @grammar.prefix %>user_terminate_code(<%= @grammar.prefix %>context_t { return context->token; } +<% if @grammar.ast %> + +static void free_ast_node(ASTNode * node) +{ + if (node->is_token) + { + /* TODO: free value_t */ + } + else if (node->n_fields > 0u) + { + for (size_t i = 0u; i < node->n_fields; i++) + { + if (node->fields[i] != NULL) + { + free_ast_node(node->fields[i]); + } + } + } + free(node); +} + +/** + * Free all AST node memory. + */ +void <%= @grammar.prefix %>free_ast(<%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> * ast) +{ + free_ast_node((ASTNode *)ast); +} +<% end %> diff --git a/assets/parser.h.erb b/assets/parser.h.erb index 058371c..86a8b1e 100644 --- a/assets/parser.h.erb +++ b/assets/parser.h.erb @@ -75,9 +75,11 @@ typedef union /** AST node types. @{ */ typedef struct <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> { - /* ASTNode fields must be present in the same order here. */ + <% # ASTNode fields must be present in the same order here. # %> <%= @grammar.prefix %>position_t position; <%= @grammar.prefix %>position_t end_position; + uint16_t n_fields; + uint8_t is_token; <%= @grammar.prefix %>token_t token; <%= @grammar.prefix %>value_t pvalue; } <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>; @@ -93,8 +95,11 @@ struct <%= name %>; <% next if rule_set.optional? %> typedef struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %> { + <% # ASTNode fields must be present in the same order here. # %> <%= @grammar.prefix %>position_t position; <%= @grammar.prefix %>position_t end_position; + uint16_t n_fields; + uint8_t is_token; <% rule_set.ast_fields.each do |fields| %> union { @@ -187,6 +192,8 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context); <% if @grammar.ast %> <%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context); + +void <%= @grammar.prefix %>free_ast(<%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> * ast); <% else %> <%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context); <% end %> diff --git a/spec/propane_spec.rb b/spec/propane_spec.rb index f9ef20e..f606381 100644 --- a/spec/propane_spec.rb +++ b/spec/propane_spec.rb @@ -78,9 +78,9 @@ EOF end case options[:language] when "c" - command = [*%w[gcc -Wall -o spec/run/testparser -Ispec -Ispec/run], *parsers, *test_files, "spec/testutils.c", "-lm"] + command = [*%w[gcc -g -Wall -o spec/run/testparser -Ispec -Ispec/run], *parsers, *test_files, "spec/testutils.c", "-lm"] when "cpp" - command = [*%w[g++ -x c++ -Wall -o spec/run/testparser -Ispec -Ispec/run], *parsers, *test_files, "spec/testutils.c", "-lm"] + command = [*%w[g++ -g -x c++ -Wall -o spec/run/testparser -Ispec -Ispec/run], *parsers, *test_files, "spec/testutils.c", "-lm"] when "d" command = [*%w[ldc2 -g --unittest -of spec/run/testparser -Ispec], *parsers, *test_files, "spec/testutils.d"] end @@ -88,12 +88,13 @@ EOF expect(result).to be_truthy end - def run_test + def run_test(options) stdout, stderr, status = Open3.capture3("spec/run/testparser") File.binwrite("spec/run/.stderr", stderr) File.binwrite("spec/run/.stdout", stdout) stderr.sub!(/^.*modules passed unittests\n/, "") - Results.new(stdout, stderr, status) + results = Results.new(stdout, stderr, status) + results end def lines(str) @@ -286,7 +287,7 @@ Foo -> plus <<>> EOF run_propane(language: language) compile("spec/test_lexer.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -324,7 +325,7 @@ EOF end run_propane(language: language) compile("spec/test_lexer_unknown_character.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -418,7 +419,7 @@ EOF end run_propane(language: language) compile("spec/test_basic_math_grammar.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -444,7 +445,7 @@ R2 -> a b; EOF run_propane(language: language) compile("spec/test_parser_identical_rules_lookahead.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 end @@ -459,7 +460,7 @@ R1 -> b; EOF run_propane(language: language) compile("spec/test_parser_rule_from_multiple_states.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 end @@ -494,7 +495,7 @@ EOF end run_propane(language: language) compile("spec/test_user_code.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 verify_lines(results.stdout, [ "abc!", @@ -530,7 +531,7 @@ EOF end run_propane(language: language) compile("spec/test_pattern.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 verify_lines(results.stdout, [ "def!", @@ -572,7 +573,7 @@ EOF end run_propane(language: language) compile("spec/test_return_token_from_pattern.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 verify_lines(results.stdout, [ "def!", @@ -630,7 +631,7 @@ EOF end run_propane(language: language) compile("spec/test_lexer_modes.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 verify_lines(results.stdout, [ "begin string mode", @@ -688,7 +689,7 @@ EOF end run_propane(language: language) compile("spec/test_lexer_multiple_modes.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 verify_lines(results.stdout, [ "ident: d", @@ -725,7 +726,7 @@ EOF end run_propane(language: language) compile("spec/test_parser_rule_user_code.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 verify_lines(results.stdout, [ "A!", @@ -744,7 +745,7 @@ As -> As a << $$ = $1 + 1u; >> EOF run_propane(language: language) compile("spec/test_parsing_lists.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 expect(results.stderr).to eq "" end @@ -798,7 +799,7 @@ EOF end run_propane(language: language) compile("spec/test_lexer_match_text.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.status).to eq 0 verify_lines(results.stdout, [ "Matched token is identifier_123", @@ -831,7 +832,7 @@ EOF end run_propane(language: language) compile("spec/test_lexer_result_value.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -846,7 +847,7 @@ Start -> a num; EOF run_propane(language: language) compile("spec/test_error_positions.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -875,7 +876,7 @@ Start -> b c b; EOF run_propane(name: "myp2", language: language) compile("spec/test_multiple_parsers.#{language}", parsers: %w[myp1 myp2], language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -894,7 +895,7 @@ Any -> c; EOF run_propane(language: language) compile("spec/test_user_terminate_lexer.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -912,7 +913,7 @@ Any -> ; EOF run_propane(language: language) compile("spec/test_user_terminate.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -956,7 +957,7 @@ EOF end run_propane(language: language) compile("spec/test_match_backslashes.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 verify_lines(results.stdout, [ @@ -1021,7 +1022,7 @@ Two -> two; EOF run_propane(language: language) compile("spec/test_ast.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -1065,7 +1066,7 @@ Two -> two; EOF run_propane(language: language) compile("spec/test_ast_ps.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -1139,7 +1140,7 @@ EOF end run_propane(language: language) compile("spec/test_optional_rule_component.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 verify_lines(results.stdout, [ @@ -1191,7 +1192,7 @@ EOF end run_propane(language: language) compile("spec/test_optional_rule_component_ast.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -1232,7 +1233,7 @@ EOF end run_propane(language: language) compile("spec/test_named_optional_rule_component_ast.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -1252,7 +1253,7 @@ T -> c; EOF run_propane(language: language) compile("spec/test_ast_token_positions.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -1272,7 +1273,7 @@ A -> bb? c?; EOF run_propane(language: language) compile("spec/test_ast_invalid_positions.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -1292,7 +1293,7 @@ T -> c; EOF run_propane(language: language) compile("spec/test_ast_field_aliases.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -1315,7 +1316,7 @@ T -> c; EOF run_propane(language: language) compile("spec/test_ast_field_aliases.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end @@ -1358,7 +1359,7 @@ EOF end run_propane(language: language) compile("spec/test_field_aliases.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 expect(results.stdout).to match /first is foo1.*second is bar2/m @@ -1408,7 +1409,7 @@ EOF end run_propane(language: language) compile("spec/test_field_aliases.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 expect(results.stdout).to match /first is foo1.*second is bar2/m @@ -1419,7 +1420,7 @@ EOF write_grammar(File.read("spec/ast_node_memory_remains.#{ext}.propane")) run_propane(language: language) compile("spec/test_ast_node_memory_remains.#{language}", language: language) - results = run_test + results = run_test(language: language) expect(results.stderr).to eq "" expect(results.status).to eq 0 end diff --git a/spec/test_ast.c b/spec/test_ast.c index 73e60cc..dd055bd 100644 --- a/spec/test_ast.c +++ b/spec/test_ast.c @@ -33,12 +33,16 @@ int main() assert_eq(22, itemsmore->pItem->pToken1->pvalue); assert(itemsmore->pItemsMore == NULL); + p_free_ast(start); + input = ""; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert_eq(P_SUCCESS, p_parse(&context)); start = p_result(&context); assert(start->pItems == NULL); + p_free_ast(start); + input = "2 1"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert_eq(P_SUCCESS, p_parse(&context)); @@ -51,5 +55,7 @@ int main() assert(start->pItems->pItem->pDual->pTwo2 == NULL); assert(start->pItems->pItem->pDual->pOne1 == NULL); + p_free_ast(start); + return 0; } diff --git a/spec/test_ast_field_aliases.c b/spec/test_ast_field_aliases.c index 5b3716a..fec19cc 100644 --- a/spec/test_ast_field_aliases.c +++ b/spec/test_ast_field_aliases.c @@ -15,5 +15,7 @@ int main() assert_eq(TOKEN_b, start->second->pToken->token); assert_eq(TOKEN_c, start->third->pToken->token); + p_free_ast(start); + return 0; } diff --git a/spec/test_ast_invalid_positions.c b/spec/test_ast_invalid_positions.c index eaac60f..28beb7b 100644 --- a/spec/test_ast_invalid_positions.c +++ b/spec/test_ast_invalid_positions.c @@ -30,6 +30,8 @@ int main() assert_eq(3, start->end_position.row); assert_eq(8, start->end_position.col); + p_free_ast(start); + input = "a\nbb"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert(p_parse(&context) == P_SUCCESS); @@ -54,6 +56,8 @@ int main() assert_eq(2, start->end_position.row); assert_eq(2, start->end_position.col); + p_free_ast(start); + input = "a\nc\nc"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert(p_parse(&context) == P_SUCCESS); @@ -78,6 +82,8 @@ int main() assert_eq(3, start->end_position.row); assert_eq(1, start->end_position.col); + p_free_ast(start); + input = "a"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert(p_parse(&context) == P_SUCCESS); @@ -98,5 +104,7 @@ int main() assert_eq(1, start->end_position.row); assert_eq(1, start->end_position.col); + p_free_ast(start); + return 0; } diff --git a/spec/test_ast_node_memory_remains.c b/spec/test_ast_node_memory_remains.c index d29284d..9c6cecb 100644 --- a/spec/test_ast_node_memory_remains.c +++ b/spec/test_ast_node_memory_remains.c @@ -411,5 +411,7 @@ int main(int argc, char * argv[]) } } + p_free_ast(pmod); + return 0; } diff --git a/spec/test_ast_ps.c b/spec/test_ast_ps.c index 69ebcae..516d164 100644 --- a/spec/test_ast_ps.c +++ b/spec/test_ast_ps.c @@ -33,12 +33,16 @@ int main() assert_eq(22, itemsmore->pItem->pToken1->pvalue); assert(itemsmore->pItemsMore == NULL); + p_free_ast(start); + input = ""; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert_eq(P_SUCCESS, p_parse(&context)); start = p_result(&context); assert(start->pItems == NULL); + p_free_ast(start); + input = "2 1"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert_eq(P_SUCCESS, p_parse(&context)); @@ -51,5 +55,7 @@ int main() assert(start->pItems->pItem->pDual->pTwo2 == NULL); assert(start->pItems->pItem->pDual->pOne1 == NULL); + p_free_ast(start); + return 0; } diff --git a/spec/test_ast_token_positions.c b/spec/test_ast_token_positions.c index 91c0437..6354996 100644 --- a/spec/test_ast_token_positions.c +++ b/spec/test_ast_token_positions.c @@ -43,6 +43,8 @@ int main() assert_eq(1, start->end_position.row); assert_eq(6, start->end_position.col); + p_free_ast(start); + input = "\n\n bb\nc\ncc\n\n a"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert(p_parse(&context) == P_SUCCESS); @@ -80,5 +82,7 @@ int main() assert_eq(7, start->end_position.row); assert_eq(6, start->end_position.col); + p_free_ast(start); + return 0; } diff --git a/spec/test_named_optional_rule_component_ast.c b/spec/test_named_optional_rule_component_ast.c index 5bbb3fd..4244cf3 100644 --- a/spec/test_named_optional_rule_component_ast.c +++ b/spec/test_named_optional_rule_component_ast.c @@ -17,6 +17,8 @@ int main() assert(start->pR == NULL); assert(start->r == NULL); + p_free_ast(start); + input = "abcd"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert(p_parse(&context) == P_SUCCESS); @@ -31,6 +33,8 @@ int main() assert(start->pR == start->r); assert_eq(TOKEN_c, start->pR->pToken1->token); + p_free_ast(start); + input = "bdc"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert(p_parse(&context) == P_SUCCESS); @@ -40,6 +44,8 @@ int main() assert(start->r != NULL); assert_eq(TOKEN_d, start->pR->pToken1->token); + p_free_ast(start); + return 0; } diff --git a/spec/test_optional_rule_component_ast.c b/spec/test_optional_rule_component_ast.c index 1885983..d88ea46 100644 --- a/spec/test_optional_rule_component_ast.c +++ b/spec/test_optional_rule_component_ast.c @@ -16,6 +16,8 @@ int main() assert(start->pR3 == NULL); assert(start->pR == NULL); + p_free_ast(start); + input = "abcd"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert(p_parse(&context) == P_SUCCESS); @@ -28,6 +30,8 @@ int main() assert(start->pR == start->pR3); assert_eq(TOKEN_c, start->pR->pToken1->token); + p_free_ast(start); + input = "bdc"; p_context_init(&context, (uint8_t const *)input, strlen(input)); assert(p_parse(&context) == P_SUCCESS); @@ -37,6 +41,8 @@ int main() assert(start->pR != NULL); assert_eq(TOKEN_d, start->pR->pToken1->token); + p_free_ast(start); + return 0; } diff --git a/spec/test_start_rule_ast.c b/spec/test_start_rule_ast.c index 3d90812..d262143 100644 --- a/spec/test_start_rule_ast.c +++ b/spec/test_start_rule_ast.c @@ -13,5 +13,7 @@ int main() assert(top->pToken != NULL); assert_eq(TOKEN_hi, top->pToken->token); + p_free_ast(top); + return 0; }