fix memleak; initialization boilerplate

This commit is contained in:
2025-01-01 23:09:50 +02:00
parent 85dfbe9d09
commit 49c910b8b2
4 changed files with 239 additions and 21 deletions

32
ast.c
View File

@@ -28,9 +28,8 @@ Ast astParse(const char* source, const uint32_t len) {
exit(1); exit(1);
} }
TokenizerToken token = tokenizerNext(&tok); TokenizerToken token = tokenizerNext(&tok);
tokens.tags[tokens.len] = token.tag; tokens.tags[++tokens.len] = token.tag;
tokens.starts[tokens.len] = token.loc.start; tokens.starts[tokens.len] = token.loc.start;
tokens.len++;
if (token.tag == TOKEN_EOF) if (token.tag == TOKEN_EOF)
break; break;
} }
@@ -58,10 +57,19 @@ Ast astParse(const char* source, const uint32_t len) {
.scratch = SLICE_INIT(AstNodeIndex, N) .scratch = SLICE_INIT(AstNodeIndex, N)
}; };
free(p.scratch.arr); // Parser takes ownership
parseRoot(&p); 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) { return (Ast) {
.source = source, .source = source,
.source_len = len, .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);
}

32
ast.h
View File

@@ -33,15 +33,15 @@ typedef enum {
AST_NODE_TAG_ALIGNED_VAR_DECL, AST_NODE_TAG_ALIGNED_VAR_DECL,
/// lhs is the identifier token payload if any, /// lhs is the identifier token payload if any,
/// rhs is the deferred expression. /// rhs is the deferred expression.
AST_NODE_TAG_AST_NODE_TAG_ERRDEFER, AST_NODE_TAG_ERRDEFER,
/// lhs is unused. /// lhs is unused.
/// rhs is the deferred expression. /// rhs is the deferred expression.
AST_NODE_TAG_AST_NODE_TAG_DEFER, AST_NODE_TAG_DEFER,
/// lhs catch rhs /// lhs catch rhs
/// lhs catch |err| rhs /// lhs catch |err| rhs
/// main_token is the `catch` keyword. /// main_token is the `catch` keyword.
/// payload is determined by looking at the next token after 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. /// `lhs.a`. main_token is the dot. rhs is the identifier token index.
AST_NODE_TAG_FIELD_ACCESS, AST_NODE_TAG_FIELD_ACCESS,
/// `lhs.?`. main_token is the dot. rhs is the `?` token index. /// `lhs.?`. main_token is the dot. rhs is the `?` token index.
@@ -149,7 +149,7 @@ typedef enum {
/// `lhs | rhs`. main_token is the `|`. /// `lhs | rhs`. main_token is the `|`.
AST_NODE_TAG_BIT_OR, AST_NODE_TAG_BIT_OR,
/// `lhs orelse rhs`. main_token is the `orelse`. /// `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`. /// `lhs and rhs`. main_token is the `and`.
AST_NODE_TAG_BOOL_AND, AST_NODE_TAG_BOOL_AND,
/// `lhs or rhs`. main_token is the `or`. /// `lhs or rhs`. main_token is the `or`.
@@ -165,9 +165,9 @@ typedef enum {
/// `op lhs`. rhs unused. main_token is op. /// `op lhs`. rhs unused. main_token is op.
AST_NODE_TAG_ADDRESS_OF, AST_NODE_TAG_ADDRESS_OF,
/// `op lhs`. rhs unused. main_token is op. /// `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. /// `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 `?`. /// `?lhs`. rhs unused. main_token is the `?`.
AST_NODE_TAG_OPTIONAL_TYPE, AST_NODE_TAG_OPTIONAL_TYPE,
/// `[lhs]rhs`. /// `[lhs]rhs`.
@@ -284,7 +284,7 @@ typedef enum {
AST_NODE_TAG_ASYNC_CALL_COMMA, AST_NODE_TAG_ASYNC_CALL_COMMA,
/// `switch(lhs) {}`. `SubRange[rhs]`. /// `switch(lhs) {}`. `SubRange[rhs]`.
/// `main_token` is the identifier of a preceding label, if any; otherwise `switch`. /// `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 /// Same as switch except there is known to be a trailing comma
/// before the final rbrace /// before the final rbrace
AST_NODE_TAG_SWITCH_COMMA, 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 c`. `While[rhs]`.
/// `while (lhs) |x| : (a) b else |y| c`. `While[rhs]`. /// `while (lhs) |x| : (a) b else |y| c`. `While[rhs]`.
/// The cont expression part `: (a)` may be omitted. /// The cont expression part `: (a)` may be omitted.
AST_NODE_TAG_AST_NODE_TAG_WHILE, AST_NODE_TAG_WHILE,
/// `for (lhs) rhs`. /// `for (lhs) rhs`.
AST_NODE_TAG_FOR_SIMPLE, AST_NODE_TAG_FOR_SIMPLE,
/// `for (lhs[0..inputs]) lhs[inputs + 1] else lhs[inputs + 2]`. `For[rhs]`. /// `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. /// `lhs..rhs`. rhs can be omitted.
AST_NODE_TAG_AST_NODE_TAG_FOR_RANGE, AST_NODE_TAG_FOR_RANGE,
/// `if (lhs) rhs`. /// `if (lhs) rhs`.
/// `if (lhs) |a| rhs`. /// `if (lhs) |a| rhs`.
AST_NODE_TAG_IF_SIMPLE, AST_NODE_TAG_IF_SIMPLE,
/// `if (lhs) a else b`. `If[rhs]`. /// `if (lhs) a else b`. `If[rhs]`.
/// `if (lhs) |x| a else b`. `If[rhs]`. /// `if (lhs) |x| a else b`. `If[rhs]`.
/// `if (lhs) |x| a else |y| 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. /// `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. /// `resume lhs`. rhs is unused.
AST_NODE_TAG_AST_NODE_TAG_AST_NODE_TAG_RESUME, AST_NODE_TAG_RESUME,
/// `continue :lhs rhs` /// `continue :lhs rhs`
/// both lhs and rhs may be omitted. /// both lhs and rhs may be omitted.
AST_NODE_TAG_AST_NODE_TAG_AST_NODE_TAG_CONTINUE, AST_NODE_TAG_CONTINUE,
/// `break :lhs rhs` /// `break :lhs rhs`
/// both lhs and rhs may be omitted. /// 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. /// `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. /// `fn (a: lhs) rhs`. lhs can be omitted.
/// anytype and ... parameters are omitted from the AST tree. /// anytype and ... parameters are omitted from the AST tree.
/// main_token is the `fn` keyword. /// main_token is the `fn` keyword.

View File

@@ -98,7 +98,6 @@ pub fn build(b: *std.Build) !void {
const gcc_analyze = b.addSystemCommand(&.{ const gcc_analyze = b.addSystemCommand(&.{
"gcc", "gcc",
"--analyzer", "--analyzer",
"-Wno-analyzer-malloc-leak", // TODO remove when wiring is complete and everything's free()d
"-Werror", "-Werror",
"-o", "-o",
"/dev/null", "/dev/null",

195
parser_test.zig Normal file
View File

@@ -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");
}