Merge pull request #153 from birth-software/basic-enum-metaprogramming

Basic enum metaprogramming
This commit is contained in:
David 2024-04-18 17:39:36 -06:00 committed by GitHub
commit 6b63c21e03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 208 additions and 54 deletions

View File

@ -4358,11 +4358,13 @@ pub const IntrinsicId = enum {
@"asm", //this is processed separately as it need special parsing @"asm", //this is processed separately as it need special parsing
cast, cast,
enum_to_int, enum_to_int,
fields,
@"export", @"export",
@"error", @"error",
int_to_pointer, int_to_pointer,
import, import,
min, min,
name,
size, size,
sign_extend, sign_extend,
syscall, syscall,
@ -4506,42 +4508,6 @@ pub const Builder = struct {
} }
} }
fn processArrayLiteral(builder: *Builder, unit: *Unit, context: *const Context, constant_array_index: V.Comptime.ConstantArray.Index, token: Token.Index) !*Debug.Declaration.Global {
if (unit.global_array_constants.get(constant_array_index)) |global| {
return global;
} else {
const token_debug_info = builder.getTokenDebugInfo(unit, token);
const name = try join_name(context, "_anon_arr_", unit.global_array_constants.length, 10);
const identifier = try unit.processIdentifier(context, name);
const constant_array = unit.constant_arrays.get(constant_array_index);
const global_declaration_index = try unit.global_declarations.append(context.allocator, .{
.declaration = .{
.scope = builder.current_scope,
.name = identifier,
.type = constant_array.type,
.line = token_debug_info.line,
.column = token_debug_info.column,
.mutability = .@"const",
.kind = .global,
},
.initial_value = .{
.constant_array = constant_array_index,
},
.type_node_index = .null,
.attributes = Debug.Declaration.Global.Attributes.initMany(&.{
.@"export",
}),
});
const global_declaration = unit.global_declarations.get(global_declaration_index);
try unit.data_to_emit.append(context.allocator, global_declaration);
try unit.global_array_constants.put_no_clobber(context.my_allocator, constant_array_index, global_declaration);
return global_declaration;
}
}
fn processStringLiteralFromToken(builder: *Builder, unit: *Unit, context: *const Context, token_index: Token.Index) !*Debug.Declaration.Global { fn processStringLiteralFromToken(builder: *Builder, unit: *Unit, context: *const Context, token_index: Token.Index) !*Debug.Declaration.Global {
const string = try unit.fixupStringLiteral(context, token_index); const string = try unit.fixupStringLiteral(context, token_index);
const token_debug_info = builder.getTokenDebugInfo(unit, token_index); const token_debug_info = builder.getTokenDebugInfo(unit, token_index);
@ -4887,10 +4853,89 @@ pub const Builder = struct {
.type = .noreturn, .type = .noreturn,
}; };
}, },
.fields => {
assert(argument_node_list.len == 1);
const container_type_index = try builder.resolveType(unit, context, argument_node_list[0], &.{});
const fields = try builder.get_fields_array(unit, context, container_type_index, unit.getNode(argument_node_list[0]).token);
return .{
.value = .{
.@"comptime" = .{
.global = fields,
},
},
.type = try unit.getPointerType(context, .{
.type = fields.declaration.type,
.termination = .none,
.mutability = .@"const",
.many = false,
.nullable = false,
}),
};
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
} }
fn get_fields_array(builder: *Builder, unit: *Unit, context: *const Context, container_type_index: Type.Index, token: Token.Index) !*Debug.Declaration.Global{
if (unit.fields_array.get(container_type_index)) |result| return result else {
const container_type = unit.types.get(container_type_index);
switch (container_type.*) {
.integer => |*integer| switch (integer.kind) {
.@"enum" => |*enum_type| {
const enum_count = enum_type.fields.length;
const array_type = try unit.getArrayType(context, .{
.type = container_type_index,
.count = enum_count,
.termination = .none,
});
var fields = try UnpinnedArray(V.Comptime).initialize_with_capacity(context.my_allocator, enum_count);
for (enum_type.fields.slice()) |enum_field_index| {
fields.append_with_capacity(V.Comptime{
.enum_value = enum_field_index,
});
}
const constant_array = try unit.constant_arrays.append(context.my_allocator, .{
.values = fields.slice(),
.type = array_type,
});
const token_debug_info = builder.getTokenDebugInfo(unit, token);
const name = try join_name(context, "_field_array_", unit.fields_array.length, 10);
const identifier = try unit.processIdentifier(context, name);
const global_declaration_index = try unit.global_declarations.append(context.my_allocator, .{
.declaration = .{
.scope = builder.current_scope,
.name = identifier,
.type = array_type,
.line = token_debug_info.line,
.column = token_debug_info.column,
.mutability = .@"const",
.kind = .global,
},
.initial_value = .{
.constant_array = constant_array,
},
.type_node_index = .null,
.attributes = Debug.Declaration.Global.Attributes.initMany(&.{
.@"export",
}),
});
const global_declaration = unit.global_declarations.get(global_declaration_index);
try unit.data_to_emit.append(context.my_allocator, global_declaration);
try unit.fields_array.put_no_clobber(context.my_allocator, container_type_index, global_declaration);
return global_declaration;
},
else => |t| @panic(@tagName(t)),
},
else => |t| @panic(@tagName(t)),
}
}
}
fn resolveCast(builder: *Builder, unit: *Unit, context: *const Context, type_index: Type.Index, value: V) !Instruction.Cast.Id { fn resolveCast(builder: *Builder, unit: *Unit, context: *const Context, type_index: Type.Index, value: V) !Instruction.Cast.Id {
_ = builder; // autofix _ = builder; // autofix
_ = context; // autofix _ = context; // autofix
@ -13630,24 +13675,24 @@ pub const Builder = struct {
}, },
else => blk: { else => blk: {
const for_loop_value = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, last_element_node_index, .right); const for_loop_value = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, last_element_node_index, .right);
try slices.append(context.my_allocator, for_loop_value);
const name = try join_name(context, "__anon_i_", unit.anon_i, 10);
unit.anon_i += 1;
const emit = true;
const stack_slot = try builder.emitLocalVariableDeclaration(unit, context, last_element_payload.token, .@"var", Type.usize, .{
.value = .{
.@"comptime" = .{
.constant_int = .{
.value = 0,
},
},
},
.type = Type.usize,
}, emit, name);
switch (unit.types.get(for_loop_value.type).*) { switch (unit.types.get(for_loop_value.type).*) {
.slice => |slice| { .slice => {
_ = slice; // autofix try slices.append(context.my_allocator, for_loop_value);
const name = try join_name(context, "__anon_i_", unit.anon_i, 10);
unit.anon_i += 1;
const emit = true;
const stack_slot = try builder.emitLocalVariableDeclaration(unit, context, last_element_payload.token, .@"var", Type.usize, .{
.value = .{
.@"comptime" = .{
.constant_int = .{
.value = 0,
},
},
},
.type = Type.usize,
}, emit, name);
const len_extract_value = try unit.instructions.append(context.my_allocator, .{ const len_extract_value = try unit.instructions.append(context.my_allocator, .{
.extract_value = .{ .extract_value = .{
@ -13667,6 +13712,59 @@ pub const Builder = struct {
}, },
}; };
}, },
.pointer => |pointer| switch (unit.types.get(pointer.type).*){
.array => |array| {
const slice_type = try unit.getSliceType(context, .{
.child_pointer_type = try unit.getPointerType(context, .{
.type = array.type,
.termination = pointer.termination,
.mutability = pointer.mutability,
.many = true,
.nullable = pointer.nullable,
}),
.child_type = array.type,
.termination = pointer.termination,
.mutability = pointer.mutability,
.nullable = pointer.nullable,
});
const slice = try unit.constant_slices.append(context.my_allocator, .{
.array = switch (for_loop_value.value) {
.@"comptime" => |ct| switch (ct) {
.global => |global| global,
else => |t| @panic(@tagName(t)),
},
else => |t| @panic(@tagName(t)),
},
.start = 0,
.end = array.count,
.type = slice_type,
});
const slice_value = V{
.value = .{
.@"comptime" = .{
.constant_slice = slice,
},
},
.type = slice_type,
};
try slices.append(context.my_allocator, slice_value);
break :blk .{
.stack_slot = stack_slot,
.end = .{
.value = .{
.@"comptime" = .{
.constant_int = .{
.value = array.count,
},
},
},
.type = Type.usize,
},
};
// TODO: fix this
},
else => |t| @panic(@tagName(t)),
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
}, },
@ -15916,7 +16014,7 @@ pub const Unit = struct {
integers: MyHashMap(Type.Integer, Type.Index) = .{}, integers: MyHashMap(Type.Integer, Type.Index) = .{},
error_unions: MyHashMap(Type.Error.Union.Descriptor, Type.Index) = .{}, error_unions: MyHashMap(Type.Error.Union.Descriptor, Type.Index) = .{},
two_structs: MyHashMap([2]Type.Index, Type.Index) = .{}, two_structs: MyHashMap([2]Type.Index, Type.Index) = .{},
global_array_constants: MyHashMap(V.Comptime.ConstantArray.Index, *Debug.Declaration.Global) = .{}, fields_array: MyHashMap(Type.Index, *Debug.Declaration.Global) = .{},
error_count: u32 = 0, error_count: u32 = 0,
code_to_emit: MyHashMap(Function.Definition.Index, *Debug.Declaration.Global) = .{}, code_to_emit: MyHashMap(Function.Definition.Index, *Debug.Declaration.Global) = .{},

View File

@ -2016,7 +2016,7 @@ pub const LLVM = struct {
.file => { .file => {
unreachable; unreachable;
}, },
.file_container => { .file_container, .struct_type => {
if (llvm.scope_map.get(sema_scope)) |scope| { if (llvm.scope_map.get(sema_scope)) |scope| {
if (true) unreachable; if (true) unreachable;
return scope; return scope;
@ -2087,6 +2087,16 @@ pub const LLVM = struct {
}, },
.constant_slice => |constant_slice_index| try llvm.getConstantSlice(unit, context, constant_slice_index), .constant_slice => |constant_slice_index| try llvm.getConstantSlice(unit, context, constant_slice_index),
.constant_struct => |constant_struct_index| try llvm.getConstantStruct(unit, context, constant_struct_index), .constant_struct => |constant_struct_index| try llvm.getConstantStruct(unit, context, constant_struct_index),
.enum_value => |enum_field_index| b: {
const integer_type = unit.types.get(sema_array_type.type).integer;
const signed = switch (integer_type.signedness) {
.signed => true,
.unsigned => false,
};
assert(!signed);
const constant_int = llvm.context.getConstantInt(integer_type.bit_count, unit.enum_fields.get(enum_field_index).value, signed) orelse unreachable;
break :b constant_int.toConstant();
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
list.append_with_capacity(value); list.append_with_capacity(value);

View File

@ -12,6 +12,10 @@ const ArgumentProcessingError = error{
const Token = struct { const Token = struct {
const Id = enum(u8) { const Id = enum(u8) {
invalid,
keyword_unsigned_integer,
keyword_signed_integer,
identifier,
}; };
}; };
@ -42,6 +46,7 @@ const lex = fn (arena: &Arena, bytes: []const u8) *!void {
while (index < length) { while (index < length) {
const start_index = index; const start_index = index;
const start_character = bytes[index]; const start_character = bytes[index];
var token_id = Token.Id.invalid;
switch (start_character) { switch (start_character) {
'a'...'z', 'A'...'Z', '_' => { 'a'...'z', 'A'...'Z', '_' => {
@ -54,6 +59,23 @@ const lex = fn (arena: &Arena, bytes: []const u8) *!void {
break; break;
} }
if (start_character == 'u' or start_character == 's' and bytes[start_index + 1] >= '0' and bytes[start_index + 1] <= '9') {
var index_integer = start_index + 1;
while (bytes[index_integer] >= '0' and bytes[index_integer] <= '9') {
index_integer += 1;
}
if (index_integer == index) {
token_id = switch (start_character) {
'u' => .keyword_unsigned_integer,
's' => .keyword_signed_integer,
else => unreachable,
};
} else {
//unreachable;
}
}
}, },
else => index += 1, else => index += 1,
} }

View File

@ -0,0 +1,24 @@
const std = #import("std");
const print = std.print;
const print_usize = std.print_usize;
const expect = std.testing.expect;
const Enum = enum(u32) {
a, b, c,
};
const main = fn () *!void {
var result: u32 = 0;
for (#fields(Enum)) |e| {
//print(#name(e));
//print(": ");
const value: u32 = #cast(e);
print_usize(value);
print("\n");
result += value;
}
//try expect(#fields(Enum).length == 3);
try expect(result == 3);
}