Merge pull request #153 from birth-software/basic-enum-metaprogramming
Basic enum metaprogramming
This commit is contained in:
commit
6b63c21e03
@ -4358,11 +4358,13 @@ pub const IntrinsicId = enum {
|
||||
@"asm", //this is processed separately as it need special parsing
|
||||
cast,
|
||||
enum_to_int,
|
||||
fields,
|
||||
@"export",
|
||||
@"error",
|
||||
int_to_pointer,
|
||||
import,
|
||||
min,
|
||||
name,
|
||||
size,
|
||||
sign_extend,
|
||||
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 {
|
||||
const string = try unit.fixupStringLiteral(context, token_index);
|
||||
const token_debug_info = builder.getTokenDebugInfo(unit, token_index);
|
||||
@ -4887,10 +4853,89 @@ pub const Builder = struct {
|
||||
.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)),
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
_ = builder; // autofix
|
||||
_ = context; // autofix
|
||||
@ -13630,11 +13675,7 @@ pub const Builder = struct {
|
||||
},
|
||||
else => blk: {
|
||||
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);
|
||||
|
||||
switch (unit.types.get(for_loop_value.type).*) {
|
||||
.slice => |slice| {
|
||||
_ = slice; // autofix
|
||||
const name = try join_name(context, "__anon_i_", unit.anon_i, 10);
|
||||
unit.anon_i += 1;
|
||||
const emit = true;
|
||||
@ -13649,6 +13690,10 @@ pub const Builder = struct {
|
||||
.type = Type.usize,
|
||||
}, emit, name);
|
||||
|
||||
switch (unit.types.get(for_loop_value.type).*) {
|
||||
.slice => {
|
||||
try slices.append(context.my_allocator, for_loop_value);
|
||||
|
||||
const len_extract_value = try unit.instructions.append(context.my_allocator, .{
|
||||
.extract_value = .{
|
||||
.expression = for_loop_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)),
|
||||
}
|
||||
},
|
||||
@ -15916,7 +16014,7 @@ pub const Unit = struct {
|
||||
integers: MyHashMap(Type.Integer, Type.Index) = .{},
|
||||
error_unions: MyHashMap(Type.Error.Union.Descriptor, 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,
|
||||
|
||||
code_to_emit: MyHashMap(Function.Definition.Index, *Debug.Declaration.Global) = .{},
|
||||
|
@ -2016,7 +2016,7 @@ pub const LLVM = struct {
|
||||
.file => {
|
||||
unreachable;
|
||||
},
|
||||
.file_container => {
|
||||
.file_container, .struct_type => {
|
||||
if (llvm.scope_map.get(sema_scope)) |scope| {
|
||||
if (true) unreachable;
|
||||
return scope;
|
||||
@ -2087,6 +2087,16 @@ pub const LLVM = struct {
|
||||
},
|
||||
.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),
|
||||
.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)),
|
||||
};
|
||||
list.append_with_capacity(value);
|
||||
|
22
src/main.nat
22
src/main.nat
@ -12,6 +12,10 @@ const ArgumentProcessingError = error{
|
||||
|
||||
const Token = struct {
|
||||
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) {
|
||||
const start_index = index;
|
||||
const start_character = bytes[index];
|
||||
var token_id = Token.Id.invalid;
|
||||
|
||||
switch (start_character) {
|
||||
'a'...'z', 'A'...'Z', '_' => {
|
||||
@ -54,6 +59,23 @@ const lex = fn (arena: &Arena, bytes: []const u8) *!void {
|
||||
|
||||
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,
|
||||
}
|
||||
|
24
test/standalone/enum_metaprogramming/main.nat
Normal file
24
test/standalone/enum_metaprogramming/main.nat
Normal 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user