From 49c910b8b283eaa5ccaf47667dc6b6520eb195c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Wed, 1 Jan 2025 23:09:50 +0200 Subject: [PATCH] fix memleak; initialization boilerplate --- ast.c | 32 +++++++- ast.h | 32 ++++---- build.zig | 1 - parser_test.zig | 195 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 239 insertions(+), 21 deletions(-) create mode 100644 parser_test.zig diff --git a/ast.c b/ast.c index 767db68..53176ff 100644 --- a/ast.c +++ b/ast.c @@ -28,9 +28,8 @@ Ast astParse(const char* source, const uint32_t len) { exit(1); } TokenizerToken token = tokenizerNext(&tok); - tokens.tags[tokens.len] = token.tag; + tokens.tags[++tokens.len] = token.tag; tokens.starts[tokens.len] = token.loc.start; - tokens.len++; if (token.tag == TOKEN_EOF) break; } @@ -58,10 +57,19 @@ Ast astParse(const char* source, const uint32_t len) { .scratch = SLICE_INIT(AstNodeIndex, N) }; - free(p.scratch.arr); // Parser takes ownership - parseRoot(&p); + p.nodes.cap = p.nodes.len = 0; + free(p.nodes.tags); + free(p.nodes.main_tokens); + free(p.nodes.datas); + + p.extra_data.cap = p.extra_data.len = 0; + free(p.extra_data.arr); + + p.scratch.cap = p.scratch.len = 0; + free(p.scratch.arr); + return (Ast) { .source = source, .source_len = len, @@ -74,3 +82,19 @@ Ast astParse(const char* source, const uint32_t len) { }, }; } + +void astDeinit(Ast* tree) { + tree->tokens.cap = tree->tokens.len = 0; + free(tree->tokens.tags); + free(tree->tokens.starts); + + tree->nodes.cap = 0; + tree->nodes.len = 0; + free(tree->nodes.tags); + free(tree->nodes.main_tokens); + free(tree->nodes.datas); + + tree->extra_data.cap = 0; + tree->extra_data.len = 0; + free(tree->extra_data.arr); +} diff --git a/ast.h b/ast.h index 6ad194b..db9a3ba 100644 --- a/ast.h +++ b/ast.h @@ -33,15 +33,15 @@ typedef enum { AST_NODE_TAG_ALIGNED_VAR_DECL, /// lhs is the identifier token payload if any, /// rhs is the deferred expression. - AST_NODE_TAG_AST_NODE_TAG_ERRDEFER, + AST_NODE_TAG_ERRDEFER, /// lhs is unused. /// rhs is the deferred expression. - AST_NODE_TAG_AST_NODE_TAG_DEFER, + AST_NODE_TAG_DEFER, /// lhs catch rhs /// lhs catch |err| rhs /// main_token is the `catch` keyword. /// payload is determined by looking at the next token after the `catch` keyword. - AST_NODE_TAG_AST_NODE_TAG_CATCH, + AST_NODE_TAG_CATCH, /// `lhs.a`. main_token is the dot. rhs is the identifier token index. AST_NODE_TAG_FIELD_ACCESS, /// `lhs.?`. main_token is the dot. rhs is the `?` token index. @@ -149,7 +149,7 @@ typedef enum { /// `lhs | rhs`. main_token is the `|`. AST_NODE_TAG_BIT_OR, /// `lhs orelse rhs`. main_token is the `orelse`. - AST_NODE_TAG_AST_NODE_TAG_ORELSE, + AST_NODE_TAG_ORELSE, /// `lhs and rhs`. main_token is the `and`. AST_NODE_TAG_BOOL_AND, /// `lhs or rhs`. main_token is the `or`. @@ -165,9 +165,9 @@ typedef enum { /// `op lhs`. rhs unused. main_token is op. AST_NODE_TAG_ADDRESS_OF, /// `op lhs`. rhs unused. main_token is op. - AST_NODE_TAG_AST_NODE_TAG_TRY, + AST_NODE_TAG_TRY, /// `op lhs`. rhs unused. main_token is op. - AST_NODE_TAG_AST_NODE_TAG_AWAIT, + AST_NODE_TAG_AWAIT, /// `?lhs`. rhs unused. main_token is the `?`. AST_NODE_TAG_OPTIONAL_TYPE, /// `[lhs]rhs`. @@ -284,7 +284,7 @@ typedef enum { AST_NODE_TAG_ASYNC_CALL_COMMA, /// `switch(lhs) {}`. `SubRange[rhs]`. /// `main_token` is the identifier of a preceding label, if any; otherwise `switch`. - AST_NODE_TAG_AST_NODE_TAG_SWITCH, + AST_NODE_TAG_SWITCH, /// Same as switch except there is known to be a trailing comma /// before the final rbrace AST_NODE_TAG_SWITCH_COMMA, @@ -310,32 +310,32 @@ typedef enum { /// `while (lhs) |x| : (a) b else c`. `While[rhs]`. /// `while (lhs) |x| : (a) b else |y| c`. `While[rhs]`. /// The cont expression part `: (a)` may be omitted. - AST_NODE_TAG_AST_NODE_TAG_WHILE, + AST_NODE_TAG_WHILE, /// `for (lhs) rhs`. AST_NODE_TAG_FOR_SIMPLE, /// `for (lhs[0..inputs]) lhs[inputs + 1] else lhs[inputs + 2]`. `For[rhs]`. - AST_NODE_TAG_AST_NODE_TAG_AST_NODE_TAG_FOR, + AST_NODE_TAG_FOR, /// `lhs..rhs`. rhs can be omitted. - AST_NODE_TAG_AST_NODE_TAG_FOR_RANGE, + AST_NODE_TAG_FOR_RANGE, /// `if (lhs) rhs`. /// `if (lhs) |a| rhs`. AST_NODE_TAG_IF_SIMPLE, /// `if (lhs) a else b`. `If[rhs]`. /// `if (lhs) |x| a else b`. `If[rhs]`. /// `if (lhs) |x| a else |y| b`. `If[rhs]`. - AST_NODE_TAG_AST_NODE_TAG_AST_NODE_TAG_IF, + AST_NODE_TAG_IF, /// `suspend lhs`. lhs can be omitted. rhs is unused. - AST_NODE_TAG_AST_NODE_TAG_AST_NODE_TAG_SUSPEND, + AST_NODE_TAG_SUSPEND, /// `resume lhs`. rhs is unused. - AST_NODE_TAG_AST_NODE_TAG_AST_NODE_TAG_RESUME, + AST_NODE_TAG_RESUME, /// `continue :lhs rhs` /// both lhs and rhs may be omitted. - AST_NODE_TAG_AST_NODE_TAG_AST_NODE_TAG_CONTINUE, + AST_NODE_TAG_CONTINUE, /// `break :lhs rhs` /// both lhs and rhs may be omitted. - AST_NODE_TAG_AST_NODE_TAG_AST_NODE_TAG_BREAK, + AST_NODE_TAG_BREAK, /// `return lhs`. lhs can be omitted. rhs is unused. - AST_NODE_TAG_AST_NODE_TAG_RETURN, + AST_NODE_TAG_RETURN, /// `fn (a: lhs) rhs`. lhs can be omitted. /// anytype and ... parameters are omitted from the AST tree. /// main_token is the `fn` keyword. diff --git a/build.zig b/build.zig index e8de59a..e88256a 100644 --- a/build.zig +++ b/build.zig @@ -98,7 +98,6 @@ pub fn build(b: *std.Build) !void { const gcc_analyze = b.addSystemCommand(&.{ "gcc", "--analyzer", - "-Wno-analyzer-malloc-leak", // TODO remove when wiring is complete and everything's free()d "-Werror", "-o", "/dev/null", diff --git a/parser_test.zig b/parser_test.zig new file mode 100644 index 0000000..2ffb2ba --- /dev/null +++ b/parser_test.zig @@ -0,0 +1,195 @@ +const std = @import("std"); +const testing = std.testing; + +const Ast = std.zig.Ast; + +const c = @cImport({ + @cInclude("ast.h"); +}); + +fn zigNode(token: c_uint) Ast.Node.Tag { + return switch (token) { + c.AST_NODE_TAG_ROOT => .root, + c.AST_NODE_TAG_USINGNAMESPACE => .@"usingnamespace", + c.AST_NODE_TAG_TEST_DECL => .test_decl, + c.AST_NODE_TAG_GLOBAL_VAR_DECL => .global_var_decl, + c.AST_NODE_TAG_LOCAL_VAR_DECL => .local_var_decl, + c.AST_NODE_TAG_SIMPLE_VAR_DECL => .simple_var_decl, + c.AST_NODE_TAG_ALIGNED_VAR_DECL => .aligned_var_decl, + c.AST_NODE_TAG_ERRDEFER => .@"errdefer", + c.AST_NODE_TAG_DEFER => .@"defer", + c.AST_NODE_TAG_CATCH => .@"catch", + c.AST_NODE_TAG_FIELD_ACCESS => .field_access, + c.AST_NODE_TAG_UNWRAP_OPTIONAL => .unwrap_optional, + c.AST_NODE_TAG_EQUAL_EQUAL => .equal_equal, + c.AST_NODE_TAG_BANG_EQUAL => .bang_equal, + c.AST_NODE_TAG_LESS_THAN => .less_than, + c.AST_NODE_TAG_GREATER_THAN => .greater_than, + c.AST_NODE_TAG_LESS_OR_EQUAL => .less_or_equal, + c.AST_NODE_TAG_GREATER_OR_EQUAL => .greater_or_equal, + c.AST_NODE_TAG_ASSIGN_MUL => .assign_mul, + c.AST_NODE_TAG_ASSIGN_DIV => .assign_div, + c.AST_NODE_TAG_ASSIGN_MOD => .assign_mod, + c.AST_NODE_TAG_ASSIGN_ADD => .assign_add, + c.AST_NODE_TAG_ASSIGN_SUB => .assign_sub, + c.AST_NODE_TAG_ASSIGN_SHL => .assign_shl, + c.AST_NODE_TAG_ASSIGN_SHL_SAT => .assign_shl_sat, + c.AST_NODE_TAG_ASSIGN_SHR => .assign_shr, + c.AST_NODE_TAG_ASSIGN_BIT_AND => .assign_bit_and, + c.AST_NODE_TAG_ASSIGN_BIT_XOR => .assign_bit_xor, + c.AST_NODE_TAG_ASSIGN_BIT_OR => .assign_bit_or, + c.AST_NODE_TAG_ASSIGN_MUL_WRAP => .assign_mul_wrap, + c.AST_NODE_TAG_ASSIGN_ADD_WRAP => .assign_add_wrap, + c.AST_NODE_TAG_ASSIGN_SUB_WRAP => .assign_sub_wrap, + c.AST_NODE_TAG_ASSIGN_MUL_SAT => .assign_mul_sat, + c.AST_NODE_TAG_ASSIGN_ADD_SAT => .assign_add_sat, + c.AST_NODE_TAG_ASSIGN_SUB_SAT => .assign_sub_sat, + c.AST_NODE_TAG_ASSIGN => .assign, + c.AST_NODE_TAG_ASSIGN_DESTRUCTURE => .assign_destructure, + c.AST_NODE_TAG_MERGE_ERROR_SETS => .merge_error_sets, + c.AST_NODE_TAG_MUL => .mul, + c.AST_NODE_TAG_DIV => .div, + c.AST_NODE_TAG_MOD => .mod, + c.AST_NODE_TAG_ARRAY_MULT => .array_mult, + c.AST_NODE_TAG_MUL_WRAP => .mul_wrap, + c.AST_NODE_TAG_MUL_SAT => .mul_sat, + c.AST_NODE_TAG_ADD => .add, + c.AST_NODE_TAG_SUB => .sub, + c.AST_NODE_TAG_ARRAY_CAT => .array_cat, + c.AST_NODE_TAG_ADD_WRAP => .add_wrap, + c.AST_NODE_TAG_SUB_WRAP => .sub_wrap, + c.AST_NODE_TAG_ADD_SAT => .add_sat, + c.AST_NODE_TAG_SUB_SAT => .sub_sat, + c.AST_NODE_TAG_SHL => .shl, + c.AST_NODE_TAG_SHL_SAT => .shl_sat, + c.AST_NODE_TAG_SHR => .shr, + c.AST_NODE_TAG_BIT_AND => .bit_and, + c.AST_NODE_TAG_BIT_XOR => .bit_xor, + c.AST_NODE_TAG_BIT_OR => .bit_or, + c.AST_NODE_TAG_ORELSE => .@"orelse", + c.AST_NODE_TAG_BOOL_AND => .bool_and, + c.AST_NODE_TAG_BOOL_OR => .bool_or, + c.AST_NODE_TAG_BOOL_NOT => .bool_not, + c.AST_NODE_TAG_NEGATION => .negation, + c.AST_NODE_TAG_BIT_NOT => .bit_not, + c.AST_NODE_TAG_NEGATION_WRAP => .negation_wrap, + c.AST_NODE_TAG_ADDRESS_OF => .address_of, + c.AST_NODE_TAG_TRY => .@"try", + c.AST_NODE_TAG_AWAIT => .@"await", + c.AST_NODE_TAG_OPTIONAL_TYPE => .optional_type, + c.AST_NODE_TAG_ARRAY_TYPE => .array_type, + c.AST_NODE_TAG_ARRAY_TYPE_SENTINEL => .array_type_sentinel, + c.AST_NODE_TAG_PTR_TYPE_ALIGNED => .ptr_type_aligned, + c.AST_NODE_TAG_PTR_TYPE_SENTINEL => .ptr_type_sentinel, + c.AST_NODE_TAG_PTR_TYPE => .ptr_type, + c.AST_NODE_TAG_PTR_TYPE_BIT_RANGE => .ptr_type_bit_range, + c.AST_NODE_TAG_SLICE_OPEN => .slice_open, + c.AST_NODE_TAG_SLICE => .slice, + c.AST_NODE_TAG_SLICE_SENTINEL => .slice_sentinel, + c.AST_NODE_TAG_DEREF => .deref, + c.AST_NODE_TAG_ARRAY_ACCESS => .array_access, + c.AST_NODE_TAG_ARRAY_INIT_ONE => .array_init_one, + c.AST_NODE_TAG_ARRAY_INIT_ONE_COMMA => .array_init_one_comma, + c.AST_NODE_TAG_ARRAY_INIT_DOT_TWO => .array_init_dot_two, + c.AST_NODE_TAG_ARRAY_INIT_DOT_TWO_COMMA => .array_init_dot_two_comma, + c.AST_NODE_TAG_ARRAY_INIT_DOT => .array_init_dot, + c.AST_NODE_TAG_ARRAY_INIT_DOT_COMMA => .array_init_dot_comma, + c.AST_NODE_TAG_ARRAY_INIT => .array_init, + c.AST_NODE_TAG_ARRAY_INIT_COMMA => .array_init_comma, + c.AST_NODE_TAG_STRUCT_INIT_ONE => .struct_init_one, + c.AST_NODE_TAG_STRUCT_INIT_ONE_COMMA => .struct_init_one_comma, + c.AST_NODE_TAG_STRUCT_INIT_DOT_TWO => .struct_init_dot_two, + c.AST_NODE_TAG_STRUCT_INIT_DOT_TWO_COMMA => .struct_init_dot_two_comma, + c.AST_NODE_TAG_STRUCT_INIT_DOT => .struct_init_dot, + c.AST_NODE_TAG_STRUCT_INIT_DOT_COMMA => .struct_init_dot_comma, + c.AST_NODE_TAG_STRUCT_INIT => .struct_init, + c.AST_NODE_TAG_STRUCT_INIT_COMMA => .struct_init_comma, + c.AST_NODE_TAG_CALL_ONE => .call_one, + c.AST_NODE_TAG_CALL_ONE_COMMA => .call_one_comma, + c.AST_NODE_TAG_ASYNC_CALL_ONE => .async_call_one, + c.AST_NODE_TAG_ASYNC_CALL_ONE_COMMA => .async_call_one_comma, + c.AST_NODE_TAG_CALL => .call, + c.AST_NODE_TAG_CALL_COMMA => .call_comma, + c.AST_NODE_TAG_ASYNC_CALL => .async_call, + c.AST_NODE_TAG_ASYNC_CALL_COMMA => .async_call_comma, + c.AST_NODE_TAG_SWITCH => .@"switch", + c.AST_NODE_TAG_SWITCH_COMMA => .switch_comma, + c.AST_NODE_TAG_SWITCH_CASE_ONE => .switch_case_one, + c.AST_NODE_TAG_SWITCH_CASE_INLINE_ONE => .switch_case_inline_one, + c.AST_NODE_TAG_SWITCH_CASE => .switch_case, + c.AST_NODE_TAG_SWITCH_CASE_INLINE => .switch_case_inline, + c.AST_NODE_TAG_SWITCH_RANGE => .switch_range, + c.AST_NODE_TAG_WHILE_SIMPLE => .while_simple, + c.AST_NODE_TAG_WHILE_CONT => .while_cont, + c.AST_NODE_TAG_WHILE => .@"while", + c.AST_NODE_TAG_FOR_SIMPLE => .for_simple, + c.AST_NODE_TAG_FOR => .@"for", + c.AST_NODE_TAG_FOR_RANGE => .for_range, + c.AST_NODE_TAG_IF_SIMPLE => .if_simple, + c.AST_NODE_TAG_IF => .@"if", + c.AST_NODE_TAG_SUSPEND => .@"suspend", + c.AST_NODE_TAG_RESUME => .@"resume", + c.AST_NODE_TAG_CONTINUE => .@"continue", + c.AST_NODE_TAG_BREAK => .@"break", + c.AST_NODE_TAG_RETURN => .@"return", + c.AST_NODE_TAG_FN_PROTO_SIMPLE => .fn_proto_simple, + c.AST_NODE_TAG_FN_PROTO_MULTI => .fn_proto_multi, + c.AST_NODE_TAG_FN_PROTO_ONE => .fn_proto_one, + c.AST_NODE_TAG_FN_PROTO => .fn_proto, + c.AST_NODE_TAG_FN_DECL => .fn_decl, + c.AST_NODE_TAG_ANYFRAME_TYPE => .anyframe_type, + c.AST_NODE_TAG_ANYFRAME_LITERAL => .anyframe_literal, + c.AST_NODE_TAG_CHAR_LITERAL => .char_literal, + c.AST_NODE_TAG_NUMBER_LITERAL => .number_literal, + c.AST_NODE_TAG_UNREACHABLE_LITERAL => .unreachable_literal, + c.AST_NODE_TAG_IDENTIFIER => .identifier, + c.AST_NODE_TAG_ENUM_LITERAL => .enum_literal, + c.AST_NODE_TAG_STRING_LITERAL => .string_literal, + c.AST_NODE_TAG_MULTILINE_STRING_LITERAL => .multiline_string_literal, + c.AST_NODE_TAG_GROUPED_EXPRESSION => .grouped_expression, + c.AST_NODE_TAG_BUILTIN_CALL_TWO => .builtin_call_two, + c.AST_NODE_TAG_BUILTIN_CALL_TWO_COMMA => .builtin_call_two_comma, + c.AST_NODE_TAG_BUILTIN_CALL => .builtin_call, + c.AST_NODE_TAG_BUILTIN_CALL_COMMA => .builtin_call_comma, + c.AST_NODE_TAG_ERROR_SET_DECL => .error_set_decl, + c.AST_NODE_TAG_CONTAINER_DECL => .container_decl, + c.AST_NODE_TAG_CONTAINER_DECL_TRAILING => .container_decl_trailing, + c.AST_NODE_TAG_CONTAINER_DECL_TWO => .container_decl_two, + c.AST_NODE_TAG_CONTAINER_DECL_TWO_TRAILING => .container_decl_two_trailing, + c.AST_NODE_TAG_CONTAINER_DECL_ARG => .container_decl_arg, + c.AST_NODE_TAG_CONTAINER_DECL_ARG_TRAILING => .container_decl_arg_trailing, + c.AST_NODE_TAG_TAGGED_UNION => .tagged_union, + c.AST_NODE_TAG_TAGGED_UNION_TRAILING => .tagged_union_trailing, + c.AST_NODE_TAG_TAGGED_UNION_TWO => .tagged_union_two, + c.AST_NODE_TAG_TAGGED_UNION_TWO_TRAILING => .tagged_union_two_trailing, + c.AST_NODE_TAG_TAGGED_UNION_ENUM_TAG => .tagged_union_enum_tag, + c.AST_NODE_TAG_TAGGED_UNION_ENUM_TAG_TRAILING => .tagged_union_enum_tag_trailing, + c.AST_NODE_TAG_CONTAINER_FIELD_INIT => .container_field_init, + c.AST_NODE_TAG_CONTAINER_FIELD_ALIGN => .container_field_align, + c.AST_NODE_TAG_CONTAINER_FIELD => .container_field, + c.AST_NODE_TAG_COMPTIME => .@"comptime", + c.AST_NODE_TAG_NOSUSPEND => .@"nosuspend", + c.AST_NODE_TAG_BLOCK_TWO => .block_two, + c.AST_NODE_TAG_BLOCK_TWO_SEMICOLON => .block_two_semicolon, + c.AST_NODE_TAG_BLOCK => .block, + c.AST_NODE_TAG_BLOCK_SEMICOLON => .block_semicolon, + c.AST_NODE_TAG_ASM_SIMPLE => .asm_simple, + c.AST_NODE_TAG_ASM => .@"asm", + c.AST_NODE_TAG_ASM_OUTPUT => .asm_output, + c.AST_NODE_TAG_ASM_INPUT => .asm_input, + c.AST_NODE_TAG_ERROR_VALUE => .error_value, + c.AST_NODE_TAG_ERROR_UNION => .error_union, + else => undefined, + }; +} + +fn zigAst(c_ast: c.Ast) Ast { + return Ast{ + .source = c_ast.source[0..c_ast.source_len], + //.tokens = + }; +} + +test "Ast header smoke test" { + try std.testing.expectEqual(zigNode(c.AST_NODE_TAG_IF), Ast.Node.Tag.@"if"); +}