const std = @import("std"); const Ast = std.zig.Ast; const Allocator = std.mem.Allocator; const Token = std.zig.Token; pub const enabled = true; pub const c = @cImport({ @cInclude("ast.h"); @cInclude("tokenizer.h"); }); pub fn zigToken(token: c_uint) Token.Tag { return switch (token) { c.TOKEN_INVALID => .invalid, c.TOKEN_INVALID_PERIODASTERISKS => .invalid_periodasterisks, c.TOKEN_IDENTIFIER => .identifier, c.TOKEN_STRING_LITERAL => .string_literal, c.TOKEN_MULTILINE_STRING_LITERAL_LINE => .multiline_string_literal_line, c.TOKEN_CHAR_LITERAL => .char_literal, c.TOKEN_EOF => .eof, c.TOKEN_BUILTIN => .builtin, c.TOKEN_BANG => .bang, c.TOKEN_PIPE => .pipe, c.TOKEN_PIPE_PIPE => .pipe_pipe, c.TOKEN_PIPE_EQUAL => .pipe_equal, c.TOKEN_EQUAL => .equal, c.TOKEN_EQUAL_EQUAL => .equal_equal, c.TOKEN_EQUAL_ANGLE_BRACKET_RIGHT => .equal_angle_bracket_right, c.TOKEN_BANG_EQUAL => .bang_equal, c.TOKEN_L_PAREN => .l_paren, c.TOKEN_R_PAREN => .r_paren, c.TOKEN_SEMICOLON => .semicolon, c.TOKEN_PERCENT => .percent, c.TOKEN_PERCENT_EQUAL => .percent_equal, c.TOKEN_L_BRACE => .l_brace, c.TOKEN_R_BRACE => .r_brace, c.TOKEN_L_BRACKET => .l_bracket, c.TOKEN_R_BRACKET => .r_bracket, c.TOKEN_PERIOD => .period, c.TOKEN_PERIOD_ASTERISK => .period_asterisk, c.TOKEN_ELLIPSIS2 => .ellipsis2, c.TOKEN_ELLIPSIS3 => .ellipsis3, c.TOKEN_CARET => .caret, c.TOKEN_CARET_EQUAL => .caret_equal, c.TOKEN_PLUS => .plus, c.TOKEN_PLUS_PLUS => .plus_plus, c.TOKEN_PLUS_EQUAL => .plus_equal, c.TOKEN_PLUS_PERCENT => .plus_percent, c.TOKEN_PLUS_PERCENT_EQUAL => .plus_percent_equal, c.TOKEN_PLUS_PIPE => .plus_pipe, c.TOKEN_PLUS_PIPE_EQUAL => .plus_pipe_equal, c.TOKEN_MINUS => .minus, c.TOKEN_MINUS_EQUAL => .minus_equal, c.TOKEN_MINUS_PERCENT => .minus_percent, c.TOKEN_MINUS_PERCENT_EQUAL => .minus_percent_equal, c.TOKEN_MINUS_PIPE => .minus_pipe, c.TOKEN_MINUS_PIPE_EQUAL => .minus_pipe_equal, c.TOKEN_ASTERISK => .asterisk, c.TOKEN_ASTERISK_EQUAL => .asterisk_equal, c.TOKEN_ASTERISK_ASTERISK => .asterisk_asterisk, c.TOKEN_ASTERISK_PERCENT => .asterisk_percent, c.TOKEN_ASTERISK_PERCENT_EQUAL => .asterisk_percent_equal, c.TOKEN_ASTERISK_PIPE => .asterisk_pipe, c.TOKEN_ASTERISK_PIPE_EQUAL => .asterisk_pipe_equal, c.TOKEN_ARROW => .arrow, c.TOKEN_COLON => .colon, c.TOKEN_SLASH => .slash, c.TOKEN_SLASH_EQUAL => .slash_equal, c.TOKEN_COMMA => .comma, c.TOKEN_AMPERSAND => .ampersand, c.TOKEN_AMPERSAND_EQUAL => .ampersand_equal, c.TOKEN_QUESTION_MARK => .question_mark, c.TOKEN_ANGLE_BRACKET_LEFT => .angle_bracket_left, c.TOKEN_ANGLE_BRACKET_LEFT_EQUAL => .angle_bracket_left_equal, c.TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_LEFT => .angle_bracket_angle_bracket_left, c.TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_LEFT_EQUAL => .angle_bracket_angle_bracket_left_equal, c.TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_LEFT_PIPE => .angle_bracket_angle_bracket_left_pipe, c.TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_LEFT_PIPE_EQUAL => .angle_bracket_angle_bracket_left_pipe_equal, c.TOKEN_ANGLE_BRACKET_RIGHT => .angle_bracket_right, c.TOKEN_ANGLE_BRACKET_RIGHT_EQUAL => .angle_bracket_right_equal, c.TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_RIGHT => .angle_bracket_angle_bracket_right, c.TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_RIGHT_EQUAL => .angle_bracket_angle_bracket_right_equal, c.TOKEN_TILDE => .tilde, c.TOKEN_NUMBER_LITERAL => .number_literal, c.TOKEN_DOC_COMMENT => .doc_comment, c.TOKEN_CONTAINER_DOC_COMMENT => .container_doc_comment, c.TOKEN_KEYWORD_ADDRSPACE => .keyword_addrspace, c.TOKEN_KEYWORD_ALIGN => .keyword_align, c.TOKEN_KEYWORD_ALLOWZERO => .keyword_allowzero, c.TOKEN_KEYWORD_AND => .keyword_and, c.TOKEN_KEYWORD_ANYFRAME => .keyword_anyframe, c.TOKEN_KEYWORD_ANYTYPE => .keyword_anytype, c.TOKEN_KEYWORD_ASM => .keyword_asm, c.TOKEN_KEYWORD_BREAK => .keyword_break, c.TOKEN_KEYWORD_CALLCONV => .keyword_callconv, c.TOKEN_KEYWORD_CATCH => .keyword_catch, c.TOKEN_KEYWORD_COMPTIME => .keyword_comptime, c.TOKEN_KEYWORD_CONST => .keyword_const, c.TOKEN_KEYWORD_CONTINUE => .keyword_continue, c.TOKEN_KEYWORD_DEFER => .keyword_defer, c.TOKEN_KEYWORD_ELSE => .keyword_else, c.TOKEN_KEYWORD_ENUM => .keyword_enum, c.TOKEN_KEYWORD_ERRDEFER => .keyword_errdefer, c.TOKEN_KEYWORD_ERROR => .keyword_error, c.TOKEN_KEYWORD_EXPORT => .keyword_export, c.TOKEN_KEYWORD_EXTERN => .keyword_extern, c.TOKEN_KEYWORD_FN => .keyword_fn, c.TOKEN_KEYWORD_FOR => .keyword_for, c.TOKEN_KEYWORD_IF => .keyword_if, c.TOKEN_KEYWORD_INLINE => .keyword_inline, c.TOKEN_KEYWORD_NOALIAS => .keyword_noalias, c.TOKEN_KEYWORD_NOINLINE => .keyword_noinline, c.TOKEN_KEYWORD_NOSUSPEND => .keyword_nosuspend, c.TOKEN_KEYWORD_OPAQUE => .keyword_opaque, c.TOKEN_KEYWORD_OR => .keyword_or, c.TOKEN_KEYWORD_ORELSE => .keyword_orelse, c.TOKEN_KEYWORD_PACKED => .keyword_packed, c.TOKEN_KEYWORD_PUB => .keyword_pub, c.TOKEN_KEYWORD_RESUME => .keyword_resume, c.TOKEN_KEYWORD_RETURN => .keyword_return, c.TOKEN_KEYWORD_LINKSECTION => .keyword_linksection, c.TOKEN_KEYWORD_STRUCT => .keyword_struct, c.TOKEN_KEYWORD_SUSPEND => .keyword_suspend, c.TOKEN_KEYWORD_SWITCH => .keyword_switch, c.TOKEN_KEYWORD_TEST => .keyword_test, c.TOKEN_KEYWORD_THREADLOCAL => .keyword_threadlocal, c.TOKEN_KEYWORD_TRY => .keyword_try, c.TOKEN_KEYWORD_UNION => .keyword_union, c.TOKEN_KEYWORD_UNREACHABLE => .keyword_unreachable, c.TOKEN_KEYWORD_VAR => .keyword_var, c.TOKEN_KEYWORD_VOLATILE => .keyword_volatile, c.TOKEN_KEYWORD_WHILE => .keyword_while, else => invalid_sentinel, }; } const invalid_sentinel = @as(Token.Tag, @enumFromInt(std.math.maxInt(@typeInfo(Token.Tag).@"enum".tag_type))); pub fn zigNode(token: c_uint) Ast.Node.Tag { return switch (token) { c.AST_NODE_ROOT => .root, c.AST_NODE_TEST_DECL => .test_decl, c.AST_NODE_GLOBAL_VAR_DECL => .global_var_decl, c.AST_NODE_LOCAL_VAR_DECL => .local_var_decl, c.AST_NODE_SIMPLE_VAR_DECL => .simple_var_decl, c.AST_NODE_ALIGNED_VAR_DECL => .aligned_var_decl, c.AST_NODE_ERRDEFER => .@"errdefer", c.AST_NODE_DEFER => .@"defer", c.AST_NODE_CATCH => .@"catch", c.AST_NODE_FIELD_ACCESS => .field_access, c.AST_NODE_UNWRAP_OPTIONAL => .unwrap_optional, c.AST_NODE_EQUAL_EQUAL => .equal_equal, c.AST_NODE_BANG_EQUAL => .bang_equal, c.AST_NODE_LESS_THAN => .less_than, c.AST_NODE_GREATER_THAN => .greater_than, c.AST_NODE_LESS_OR_EQUAL => .less_or_equal, c.AST_NODE_GREATER_OR_EQUAL => .greater_or_equal, c.AST_NODE_ASSIGN_MUL => .assign_mul, c.AST_NODE_ASSIGN_DIV => .assign_div, c.AST_NODE_ASSIGN_MOD => .assign_mod, c.AST_NODE_ASSIGN_ADD => .assign_add, c.AST_NODE_ASSIGN_SUB => .assign_sub, c.AST_NODE_ASSIGN_SHL => .assign_shl, c.AST_NODE_ASSIGN_SHL_SAT => .assign_shl_sat, c.AST_NODE_ASSIGN_SHR => .assign_shr, c.AST_NODE_ASSIGN_BIT_AND => .assign_bit_and, c.AST_NODE_ASSIGN_BIT_XOR => .assign_bit_xor, c.AST_NODE_ASSIGN_BIT_OR => .assign_bit_or, c.AST_NODE_ASSIGN_MUL_WRAP => .assign_mul_wrap, c.AST_NODE_ASSIGN_ADD_WRAP => .assign_add_wrap, c.AST_NODE_ASSIGN_SUB_WRAP => .assign_sub_wrap, c.AST_NODE_ASSIGN_MUL_SAT => .assign_mul_sat, c.AST_NODE_ASSIGN_ADD_SAT => .assign_add_sat, c.AST_NODE_ASSIGN_SUB_SAT => .assign_sub_sat, c.AST_NODE_ASSIGN => .assign, c.AST_NODE_ASSIGN_DESTRUCTURE => .assign_destructure, c.AST_NODE_MERGE_ERROR_SETS => .merge_error_sets, c.AST_NODE_MUL => .mul, c.AST_NODE_DIV => .div, c.AST_NODE_MOD => .mod, c.AST_NODE_ARRAY_MULT => .array_mult, c.AST_NODE_MUL_WRAP => .mul_wrap, c.AST_NODE_MUL_SAT => .mul_sat, c.AST_NODE_ADD => .add, c.AST_NODE_SUB => .sub, c.AST_NODE_ARRAY_CAT => .array_cat, c.AST_NODE_ADD_WRAP => .add_wrap, c.AST_NODE_SUB_WRAP => .sub_wrap, c.AST_NODE_ADD_SAT => .add_sat, c.AST_NODE_SUB_SAT => .sub_sat, c.AST_NODE_SHL => .shl, c.AST_NODE_SHL_SAT => .shl_sat, c.AST_NODE_SHR => .shr, c.AST_NODE_BIT_AND => .bit_and, c.AST_NODE_BIT_XOR => .bit_xor, c.AST_NODE_BIT_OR => .bit_or, c.AST_NODE_ORELSE => .@"orelse", c.AST_NODE_BOOL_AND => .bool_and, c.AST_NODE_BOOL_OR => .bool_or, c.AST_NODE_BOOL_NOT => .bool_not, c.AST_NODE_NEGATION => .negation, c.AST_NODE_BIT_NOT => .bit_not, c.AST_NODE_NEGATION_WRAP => .negation_wrap, c.AST_NODE_ADDRESS_OF => .address_of, c.AST_NODE_TRY => .@"try", c.AST_NODE_OPTIONAL_TYPE => .optional_type, c.AST_NODE_ARRAY_TYPE => .array_type, c.AST_NODE_ARRAY_TYPE_SENTINEL => .array_type_sentinel, c.AST_NODE_PTR_TYPE_ALIGNED => .ptr_type_aligned, c.AST_NODE_PTR_TYPE_SENTINEL => .ptr_type_sentinel, c.AST_NODE_PTR_TYPE => .ptr_type, c.AST_NODE_PTR_TYPE_BIT_RANGE => .ptr_type_bit_range, c.AST_NODE_SLICE_OPEN => .slice_open, c.AST_NODE_SLICE => .slice, c.AST_NODE_SLICE_SENTINEL => .slice_sentinel, c.AST_NODE_DEREF => .deref, c.AST_NODE_ARRAY_ACCESS => .array_access, c.AST_NODE_ARRAY_INIT_ONE => .array_init_one, c.AST_NODE_ARRAY_INIT_ONE_COMMA => .array_init_one_comma, c.AST_NODE_ARRAY_INIT_DOT_TWO => .array_init_dot_two, c.AST_NODE_ARRAY_INIT_DOT_TWO_COMMA => .array_init_dot_two_comma, c.AST_NODE_ARRAY_INIT_DOT => .array_init_dot, c.AST_NODE_ARRAY_INIT_DOT_COMMA => .array_init_dot_comma, c.AST_NODE_ARRAY_INIT => .array_init, c.AST_NODE_ARRAY_INIT_COMMA => .array_init_comma, c.AST_NODE_STRUCT_INIT_ONE => .struct_init_one, c.AST_NODE_STRUCT_INIT_ONE_COMMA => .struct_init_one_comma, c.AST_NODE_STRUCT_INIT_DOT_TWO => .struct_init_dot_two, c.AST_NODE_STRUCT_INIT_DOT_TWO_COMMA => .struct_init_dot_two_comma, c.AST_NODE_STRUCT_INIT_DOT => .struct_init_dot, c.AST_NODE_STRUCT_INIT_DOT_COMMA => .struct_init_dot_comma, c.AST_NODE_STRUCT_INIT => .struct_init, c.AST_NODE_STRUCT_INIT_COMMA => .struct_init_comma, c.AST_NODE_CALL_ONE => .call_one, c.AST_NODE_CALL_ONE_COMMA => .call_one_comma, c.AST_NODE_CALL => .call, c.AST_NODE_CALL_COMMA => .call_comma, c.AST_NODE_SWITCH => .@"switch", c.AST_NODE_SWITCH_COMMA => .switch_comma, c.AST_NODE_SWITCH_CASE_ONE => .switch_case_one, c.AST_NODE_SWITCH_CASE_INLINE_ONE => .switch_case_inline_one, c.AST_NODE_SWITCH_CASE => .switch_case, c.AST_NODE_SWITCH_CASE_INLINE => .switch_case_inline, c.AST_NODE_SWITCH_RANGE => .switch_range, c.AST_NODE_WHILE_SIMPLE => .while_simple, c.AST_NODE_WHILE_CONT => .while_cont, c.AST_NODE_WHILE => .@"while", c.AST_NODE_FOR_SIMPLE => .for_simple, c.AST_NODE_FOR => .@"for", c.AST_NODE_FOR_RANGE => .for_range, c.AST_NODE_IF_SIMPLE => .if_simple, c.AST_NODE_IF => .@"if", c.AST_NODE_SUSPEND => .@"suspend", c.AST_NODE_RESUME => .@"resume", c.AST_NODE_CONTINUE => .@"continue", c.AST_NODE_BREAK => .@"break", c.AST_NODE_RETURN => .@"return", c.AST_NODE_FN_PROTO_SIMPLE => .fn_proto_simple, c.AST_NODE_FN_PROTO_MULTI => .fn_proto_multi, c.AST_NODE_FN_PROTO_ONE => .fn_proto_one, c.AST_NODE_FN_PROTO => .fn_proto, c.AST_NODE_FN_DECL => .fn_decl, c.AST_NODE_ANYFRAME_TYPE => .anyframe_type, c.AST_NODE_ANYFRAME_LITERAL => .anyframe_literal, c.AST_NODE_CHAR_LITERAL => .char_literal, c.AST_NODE_NUMBER_LITERAL => .number_literal, c.AST_NODE_UNREACHABLE_LITERAL => .unreachable_literal, c.AST_NODE_IDENTIFIER => .identifier, c.AST_NODE_ENUM_LITERAL => .enum_literal, c.AST_NODE_STRING_LITERAL => .string_literal, c.AST_NODE_MULTILINE_STRING_LITERAL => .multiline_string_literal, c.AST_NODE_GROUPED_EXPRESSION => .grouped_expression, c.AST_NODE_BUILTIN_CALL_TWO => .builtin_call_two, c.AST_NODE_BUILTIN_CALL_TWO_COMMA => .builtin_call_two_comma, c.AST_NODE_BUILTIN_CALL => .builtin_call, c.AST_NODE_BUILTIN_CALL_COMMA => .builtin_call_comma, c.AST_NODE_ERROR_SET_DECL => .error_set_decl, c.AST_NODE_CONTAINER_DECL => .container_decl, c.AST_NODE_CONTAINER_DECL_TRAILING => .container_decl_trailing, c.AST_NODE_CONTAINER_DECL_TWO => .container_decl_two, c.AST_NODE_CONTAINER_DECL_TWO_TRAILING => .container_decl_two_trailing, c.AST_NODE_CONTAINER_DECL_ARG => .container_decl_arg, c.AST_NODE_CONTAINER_DECL_ARG_TRAILING => .container_decl_arg_trailing, c.AST_NODE_TAGGED_UNION => .tagged_union, c.AST_NODE_TAGGED_UNION_TRAILING => .tagged_union_trailing, c.AST_NODE_TAGGED_UNION_TWO => .tagged_union_two, c.AST_NODE_TAGGED_UNION_TWO_TRAILING => .tagged_union_two_trailing, c.AST_NODE_TAGGED_UNION_ENUM_TAG => .tagged_union_enum_tag, c.AST_NODE_TAGGED_UNION_ENUM_TAG_TRAILING => .tagged_union_enum_tag_trailing, c.AST_NODE_CONTAINER_FIELD_INIT => .container_field_init, c.AST_NODE_CONTAINER_FIELD_ALIGN => .container_field_align, c.AST_NODE_CONTAINER_FIELD => .container_field, c.AST_NODE_COMPTIME => .@"comptime", c.AST_NODE_NOSUSPEND => .@"nosuspend", c.AST_NODE_BLOCK_TWO => .block_two, c.AST_NODE_BLOCK_TWO_SEMICOLON => .block_two_semicolon, c.AST_NODE_BLOCK => .block, c.AST_NODE_BLOCK_SEMICOLON => .block_semicolon, c.AST_NODE_ASM_SIMPLE => .asm_simple, c.AST_NODE_ASM_LEGACY => .asm_legacy, c.AST_NODE_ASM => .@"asm", c.AST_NODE_ASM_OUTPUT => .asm_output, c.AST_NODE_ASM_INPUT => .asm_input, c.AST_NODE_ERROR_VALUE => .error_value, c.AST_NODE_ERROR_UNION => .error_union, else => @as(Ast.Node.Tag, @enumFromInt(std.math.maxInt(@typeInfo(Ast.Node.Tag).@"enum".tag_type))), }; } fn toIndex(v: u32) Ast.Node.Index { return @enumFromInt(v); } fn toOptIndex(v: u32) Ast.Node.OptionalIndex { return if (v == 0) .none else @enumFromInt(v); } fn toExtraIndex(v: u32) Ast.ExtraIndex { return @enumFromInt(v); } fn toOptTokenIndex(v: u32) Ast.OptionalTokenIndex { return @enumFromInt(v); } fn zigData(tag: Ast.Node.Tag, lhs: u32, rhs: u32) Ast.Node.Data { return switch (tag) { // data unused .identifier, .string_literal, .char_literal, .number_literal, .unreachable_literal, .anyframe_literal, .enum_literal, .error_value, => .{ .opt_node_and_opt_node = .{ toOptIndex(lhs), toOptIndex(rhs) } }, // .node (single node index) .@"defer", .@"comptime", .@"nosuspend", .@"suspend", .@"resume", .bool_not, .negation, .bit_not, .negation_wrap, .address_of, .@"try", .deref, .optional_type, => .{ .node = toIndex(lhs) }, // .opt_node (single optional node) .@"return", => .{ .opt_node = toOptIndex(lhs) }, // .node_and_node .fn_decl, .container_field_align, .error_union, .@"catch", .equal_equal, .bang_equal, .less_than, .greater_than, .less_or_equal, .greater_or_equal, .assign_mul, .assign_div, .assign_mod, .assign_add, .assign_sub, .assign_shl, .assign_shl_sat, .assign_shr, .assign_bit_and, .assign_bit_xor, .assign_bit_or, .assign_mul_wrap, .assign_add_wrap, .assign_sub_wrap, .assign_mul_sat, .assign_add_sat, .assign_sub_sat, .assign, .merge_error_sets, .mul, .div, .mod, .array_mult, .mul_wrap, .mul_sat, .add, .sub, .array_cat, .add_wrap, .sub_wrap, .add_sat, .sub_sat, .shl, .shl_sat, .shr, .bit_and, .bit_xor, .bit_or, .@"orelse", .bool_and, .bool_or, .array_type, .array_access, .switch_range, => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, // .opt_node_and_opt_node .fn_proto_simple, .simple_var_decl, .block_two, .block_two_semicolon, .builtin_call_two, .builtin_call_two_comma, .container_decl_two, .container_decl_two_trailing, .tagged_union_two, .tagged_union_two_trailing, .struct_init_dot_two, .struct_init_dot_two_comma, .array_init_dot_two, .array_init_dot_two_comma, => .{ .opt_node_and_opt_node = .{ toOptIndex(lhs), toOptIndex(rhs) } }, // .node_and_opt_node .call_one, .call_one_comma, .struct_init_one, .struct_init_one_comma, .container_field_init, .aligned_var_decl, => .{ .node_and_opt_node = .{ toIndex(lhs), toOptIndex(rhs) } }, // .node_and_node (array_init_one uses node_and_node, not // node_and_opt_node) .array_init_one, .array_init_one_comma, => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, // .opt_node_and_node .ptr_type_aligned, .ptr_type_sentinel, .switch_case_one, .switch_case_inline_one, => .{ .opt_node_and_node = .{ toOptIndex(lhs), toIndex(rhs) } }, // .node_and_extra .call, .call_comma, .container_field, .array_type_sentinel, .slice, .slice_sentinel, .array_init, .array_init_comma, .struct_init, .struct_init_comma, .@"switch", .switch_comma, .container_decl_arg, .container_decl_arg_trailing, .tagged_union_enum_tag, .tagged_union_enum_tag_trailing, .@"asm", => .{ .node_and_extra = .{ toIndex(lhs), toExtraIndex(rhs) } }, // .extra_and_node .assign_destructure, .switch_case, .switch_case_inline, .ptr_type, .ptr_type_bit_range, => .{ .extra_and_node = .{ toExtraIndex(lhs), toIndex(rhs) } }, // .extra_and_opt_node .global_var_decl, .local_var_decl, .fn_proto_multi, .fn_proto_one, .fn_proto, => .{ .extra_and_opt_node = .{ toExtraIndex(lhs), toOptIndex(rhs) } }, // .extra_range (SubRange) .root, .block, .block_semicolon, .builtin_call, .builtin_call_comma, .container_decl, .container_decl_trailing, .tagged_union, .tagged_union_trailing, .array_init_dot, .array_init_dot_comma, .struct_init_dot, .struct_init_dot_comma, => .{ .extra_range = .{ .start = toExtraIndex(lhs), .end = toExtraIndex(rhs) } }, // .node_and_token .grouped_expression, .asm_input, .asm_simple, .field_access, .unwrap_optional, => .{ .node_and_token = .{ toIndex(lhs), rhs } }, // .opt_node_and_token .asm_output, => .{ .opt_node_and_token = .{ toOptIndex(lhs), rhs } }, // .opt_token_and_node .test_decl, .@"errdefer", => .{ .opt_token_and_node = .{ toOptTokenIndex(lhs), toIndex(rhs) } }, // .opt_token_and_opt_node .@"break", .@"continue", => .{ .opt_token_and_opt_node = .{ toOptTokenIndex(lhs), toOptIndex(rhs) } }, // .token_and_token .error_set_decl, .multiline_string_literal, => .{ .token_and_token = .{ lhs, rhs } }, // .token_and_node .anyframe_type, => .{ .token_and_node = .{ lhs, toIndex(rhs) } }, // .node_and_node for slice_open (lhs[rhs..]) .slice_open, => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, .while_simple, .for_simple, .if_simple, => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, .while_cont, .@"while", .@"if", => .{ .node_and_extra = .{ toIndex(lhs), toExtraIndex(rhs) } }, .for_range, => .{ .node_and_opt_node = .{ toIndex(lhs), toOptIndex(rhs) } }, .@"for", => .{ .@"for" = .{ toExtraIndex(lhs), @bitCast(rhs) } }, .asm_legacy, => .{ .node_and_extra = .{ toIndex(lhs), toExtraIndex(rhs) } }, }; } // zigAst converts a c.Ast to std.Zig.Ast. The resulting Ast should be freed with deinit(). pub fn zigAst(gpa: Allocator, c_ast: c.Ast) !Ast { var tokens = Ast.TokenList{}; try tokens.resize(gpa, c_ast.tokens.len); errdefer tokens.deinit(gpa); for (0..c_ast.tokens.len) |i| tokens.set(i, .{ .tag = zigToken(c_ast.tokens.tags[i]), .start = c_ast.tokens.starts[i], }); var nodes = Ast.NodeList{}; try nodes.resize(gpa, c_ast.nodes.len); errdefer nodes.deinit(gpa); for (0..c_ast.nodes.len) |i| { const tag = zigNode(c_ast.nodes.tags[i]); nodes.set(i, .{ .tag = tag, .main_token = c_ast.nodes.main_tokens[i], .data = zigData(tag, c_ast.nodes.datas[i].lhs, c_ast.nodes.datas[i].rhs), }); } const extra_data = try gpa.alloc(u32, c_ast.extra_data.len); errdefer gpa.free(extra_data); @memcpy(extra_data, c_ast.extra_data.arr[0..c_ast.extra_data.len]); const errors = if (c_ast.has_error) blk: { const errs = try gpa.alloc(Ast.Error, 1); errs[0] = .{ .tag = .expected_token, .token = 0, .extra = .{ .none = {} } }; break :blk errs; } else try gpa.alloc(Ast.Error, 0); errdefer gpa.free(errors); return Ast{ .source = c_ast.source[0..c_ast.source_len :0], .mode = .zig, .tokens = tokens.slice(), .nodes = nodes.slice(), .extra_data = extra_data, .errors = errors, }; } // Returns the number of meaningful u32 fields in Node.Data for a given tag. // 0 = data is undefined/unused, 1 = only first u32 is meaningful, 2 = both meaningful. fn dataFieldCount(tag: Ast.Node.Tag) u2 { return switch (tag) { // data unused (undefined in Zig parser) .identifier, .string_literal, .char_literal, .number_literal, .unreachable_literal, .anyframe_literal, .enum_literal, .error_value, => 0, // .node or .opt_node — only first u32 .@"defer", .@"comptime", .@"nosuspend", .@"suspend", .@"resume", .bool_not, .negation, .bit_not, .negation_wrap, .address_of, .@"try", .deref, .optional_type, .@"return", => 1, // everything else — both u32 fields else => 2, }; } pub fn expectAstConsistent(c_tree: Ast, zig_tree: Ast, source: [:0]const u8) !void { _ = source; const print = std.debug.print; if (c_tree.tokens.len != zig_tree.tokens.len) { print("token count mismatch: c={d} zig={d}\n", .{ c_tree.tokens.len, zig_tree.tokens.len }); return error.TestExpectedEqual; } for (0..c_tree.tokens.len) |i| { if (c_tree.tokens.items(.start)[i] != zig_tree.tokens.items(.start)[i]) { print("token[{d}] start mismatch: c={d} zig={d}\n", .{ i, c_tree.tokens.items(.start)[i], zig_tree.tokens.items(.start)[i] }); return error.TestExpectedEqual; } if (c_tree.tokens.items(.tag)[i] != zig_tree.tokens.items(.tag)[i]) { print("token[{d}] tag mismatch: c={s} zig={s}\n", .{ i, @tagName(c_tree.tokens.items(.tag)[i]), @tagName(zig_tree.tokens.items(.tag)[i]) }); return error.TestExpectedEqual; } } if (c_tree.nodes.len != zig_tree.nodes.len) { print("node count mismatch: c={d} zig={d}\n", .{ c_tree.nodes.len, zig_tree.nodes.len }); return error.TestExpectedEqual; } for (0..c_tree.nodes.len) |i| { const c_tag = c_tree.nodes.items(.tag)[i]; const z_tag = zig_tree.nodes.items(.tag)[i]; if (c_tag != z_tag) { print("node[{d}] tag mismatch: c={s} zig={s}\n", .{ i, @tagName(c_tag), @tagName(z_tag) }); return error.TestExpectedEqual; } if (c_tree.nodes.items(.main_token)[i] != zig_tree.nodes.items(.main_token)[i]) { print("node[{d}] main_token mismatch: c={d} zig={d}\n", .{ i, c_tree.nodes.items(.main_token)[i], zig_tree.nodes.items(.main_token)[i] }); return error.TestExpectedEqual; } const field_count = dataFieldCount(c_tag); if (field_count >= 1) { const c_data: *const [2]u32 = @ptrCast(&c_tree.nodes.items(.data)[i]); const z_data: *const [2]u32 = @ptrCast(&zig_tree.nodes.items(.data)[i]); if (c_data[0] != z_data[0]) { print("node[{d}] data[0] mismatch: c={d} zig={d}\n", .{ i, c_data[0], z_data[0] }); return error.TestExpectedEqual; } if (field_count >= 2 and c_data[1] != z_data[1]) { print("node[{d}] data[1] mismatch: c={d} zig={d}\n", .{ i, c_data[1], z_data[1] }); return error.TestExpectedEqual; } } } if (c_tree.extra_data.len != zig_tree.extra_data.len) { print("extra_data length mismatch: c={d} zig={d}\n", .{ c_tree.extra_data.len, zig_tree.extra_data.len }); return error.TestExpectedEqual; } for (0..c_tree.extra_data.len) |i| { if (c_tree.extra_data[i] != zig_tree.extra_data[i]) { print("extra_data[{d}] mismatch: c={d} zig={d}\n", .{ i, c_tree.extra_data[i], zig_tree.extra_data[i] }); return error.TestExpectedEqual; } } }