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
|
@"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,11 +13675,7 @@ 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);
|
|
||||||
|
|
||||||
switch (unit.types.get(for_loop_value.type).*) {
|
|
||||||
.slice => |slice| {
|
|
||||||
_ = slice; // autofix
|
|
||||||
const name = try join_name(context, "__anon_i_", unit.anon_i, 10);
|
const name = try join_name(context, "__anon_i_", unit.anon_i, 10);
|
||||||
unit.anon_i += 1;
|
unit.anon_i += 1;
|
||||||
const emit = true;
|
const emit = true;
|
||||||
@ -13649,6 +13690,10 @@ pub const Builder = struct {
|
|||||||
.type = Type.usize,
|
.type = Type.usize,
|
||||||
}, emit, name);
|
}, 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, .{
|
const len_extract_value = try unit.instructions.append(context.my_allocator, .{
|
||||||
.extract_value = .{
|
.extract_value = .{
|
||||||
.expression = for_loop_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)),
|
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) = .{},
|
||||||
|
@ -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);
|
||||||
|
22
src/main.nat
22
src/main.nat
@ -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,
|
||||||
}
|
}
|
||||||
|
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