adding an name_of enum

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2025-03-14 17:30:34 +01:00
parent a1139cfaf1
commit 1640cc12c4
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
16 changed files with 216 additions and 47 deletions

6
.gitmodules vendored
View file

@ -1,9 +1,3 @@
[submodule "vendored-tests/lily"]
path = vendored-tests/lily
url = https://git.ablecorp.us/lily-org/lily.git
[submodule "vendored-tests/mumd"]
path = vendored-tests/mumd
url = https://github.com/Ducklett/mumd.git
[submodule "vendored/mumd"]
path = vendored/mumd
url = https://github.com/Ducklett/mumd.git

View file

@ -227,6 +227,17 @@ main := fn(): uint {
}
```
#### literals 3
```hb
expectations := .{
return_value: 69,
}
main := fn(): uint {
return 'E'
}
```
#### functions 1
```hb
expectations := .{
@ -1209,6 +1220,64 @@ main := fn(): uint {
}
```
#### enums 4
```hb
expectations := .{
return_value: 69,
}
NameMap := fn($Enum: type): type {
sum := 0
i: u8 = 0
$loop $if i == @len_of(Enum) break else {
sum += @int_cast(@name_of(@as(Enum, @bit_cast(i))).len)
i += 1
}
StrBuf := [sum]u8
IndexBuf := [@len_of(Enum) + 1]uint
return struct {
.buf: StrBuf;
.index: IndexBuf
new := fn(): @CurrentScope() {
buf: StrBuf = idk
index: IndexBuf = idk
index[0] = 0
ii: u8 = 0
bi := 0
$loop $if ii == @len_of(Enum) break else {
name := @name_of(@as(Enum, @bit_cast(ii)))
ij := 0
$loop $if ij == name.len break else {
buf[bi + ij] = name[ij]
ij += 1
}
bi += @int_cast(name.len)
ii += 1
index[ii] = bi
}
return .(buf, index)
}
get := fn(self: ^@CurrentScope(), k: Enum): []u8 {
return self.buf[self.index[k]..self.index[@as(u8, k) + 1]]
}
}
}
Nm := enum{.E; .bcd; .cd}
map := NameMap(Nm).new()
main := fn(): uint {
return map.get(.E)[0]
}
```
#### match 1
```hb
main := fn(): uint {
@ -1795,11 +1864,11 @@ func := fn(a: @Any(), b: @TypeOf(a)): uint {
#### directives 14 (@name_of)
```hb
expectations := .{
return_value: 4,
return_value: 7,
}
main := fn(): uint {
return @name_of(uint).len
return @name_of(uint).len + @name_of(enum{.foo}.foo).len
}
```

View file

@ -31,6 +31,7 @@ pub fn build(b: *std.Build) !void {
}
const test_step = b.step("test", "run tests");
const test_filter = b.option([]const u8, "tf", "passed as a filter to tests");
vendored_tests: {
const grn = b.addExecutable(.{
@ -53,6 +54,7 @@ pub fn build(b: *std.Build) !void {
.root_source_file = out,
.target = b.graph.host,
.optimize = optimize,
.filter = test_filter,
.use_llvm = false,
.use_lld = false,
});
@ -82,6 +84,7 @@ pub fn build(b: *std.Build) !void {
.root_source_file = out,
.target = b.graph.host,
.optimize = optimize,
.filter = test_filter,
.use_llvm = false,
.use_lld = false,
});
@ -199,7 +202,6 @@ pub fn build(b: *std.Build) !void {
};
testing: {
const test_filter = b.option([]const u8, "tf", "passed as a filter to tests");
const unit_tests = b.addTest(.{
.root_module = test_module,
.filter = test_filter,

View file

@ -102,19 +102,6 @@ pub fn addFieldStore(self: *Builder, base: *BuildNode, offset: i64, ty: DataType
_ = self.addStore(self.addFieldOffset(base, offset), ty, value);
}
pub fn addIndexOffset(self: *Builder, base: *BuildNode, op: enum(u8) {
iadd = @intFromEnum(BinOp.iadd),
isub = @intFromEnum(BinOp.isub),
}, elem_size: u64, subscript: *BuildNode) SpecificNode(.BinOp) {
const offset = if (elem_size == 1)
subscript
else if (subscript.kind == .CInt)
self.addIntImm(.int, subscript.extra(.CInt).* * @as(i64, @bitCast(elem_size)))
else
self.addBinOp(.imul, .int, subscript, self.addIntImm(.int, @bitCast(elem_size)));
return self.addBinOp(@enumFromInt(@intFromEnum(op)), .int, base, offset);
}
pub fn addSpill(self: *Builder, sloc: graph.Sloc, value: *BuildNode) SpecificNode(.Local) {
const local = self.addLocal(sloc, value.data_type.size());
_ = self.addStore(local, value.data_type, value);
@ -135,6 +122,19 @@ pub fn addGlobalAddr(self: *Builder, arbitrary_global_id: u32) SpecificNode(.Glo
// #MATH =======================================================================
pub fn addIndexOffset(self: *Builder, base: *BuildNode, op: enum(u8) {
iadd = @intFromEnum(BinOp.iadd),
isub = @intFromEnum(BinOp.isub),
}, elem_size: u64, subscript: *BuildNode) SpecificNode(.BinOp) {
const offset = if (elem_size == 1)
subscript
else if (subscript.kind == .CInt)
self.addIntImm(.int, subscript.extra(.CInt).* * @as(i64, @bitCast(elem_size)))
else
self.addBinOp(.imul, .int, subscript, self.addIntImm(.int, @bitCast(elem_size)));
return self.addBinOp(@enumFromInt(@intFromEnum(op)), .int, base, offset);
}
pub fn addIntImm(self: *Builder, ty: DataType, value: i64) SpecificNode(.CInt) {
std.debug.assert(ty != .bot);
const val = self.func.addNode(.CInt, ty, &.{null}, value);

View file

@ -409,7 +409,7 @@ pub fn GcmMixin(comptime MachNode: type) type {
var scheduled: usize = 0;
if (ready != scheduled) while (scheduled < outs.len) {
std.debug.assert(ready != scheduled);
if (ready == scheduled) root.panic("{} {} {}", .{ scheduled, outs.len, node });
var pick = scheduled;
for (outs[scheduled + 1 .. ready], scheduled + 1..) |o, i| {

View file

@ -183,6 +183,10 @@ pub const Expr = union(enum) {
pos: Pos,
end: u32,
},
Quotes: struct {
pos: Pos,
end: u32,
},
pub const Type = struct {
pos: Pos,

View file

@ -342,7 +342,7 @@ pub fn emit(self: *Codegen, ctx: Ctx, expr: Ast.Id) EmitError!Value {
.String => |e| {
const lit = ast.source[e.pos.index + 1 .. e.end - 1];
const data = switch (encodeString(lit, self.types.arena.alloc(u8, lit.len))) {
const data = switch (encodeString(lit, self.types.arena.alloc(u8, lit.len)) catch unreachable) {
.ok => |dt| dt,
.err => |err| {
var pos = e.pos;
@ -353,6 +353,24 @@ pub fn emit(self: *Codegen, ctx: Ctx, expr: Ast.Id) EmitError!Value {
return self.emitStirng(ctx, data, expr);
},
.Quotes => |e| {
const lit = ast.source[e.pos.index + 1 .. e.end - 1];
var char: [1]u8 = undefined;
const data = switch (encodeString(lit, &char) catch {
return self.report(expr, "the char encodes into more then 1 byte", .{});
}) {
.ok => |dt| dt,
.err => |err| {
var pos = e.pos;
pos.index += @intCast(err.pos);
return self.report(pos, "{s}", .{err.reason});
},
};
return .mkv(.u8, self.bl.addIntImm(.i8, data[0]));
},
.Integer => |e| {
var ty = ctx.ty orelse .uint;
if (!ty.isInteger()) ty = .uint;
@ -657,7 +675,8 @@ pub fn emit(self: *Codegen, ctx: Ctx, expr: Ast.Id) EmitError!Value {
// #OPS ========================================================================
.SliceTy => |e| {
const len: ?usize = if (e.len.tag() == .Void) null else @intCast(self.types.ct.evalIntConst(.{ .Tmp = self }, e.len) catch 0);
var value = if (e.len.tag() == .Void) null else try self.emitTyped(.{}, .uint, e.len);
const len: ?usize = if (value) |*vl| @intCast(self.partialEval(expr, vl.getValue(self)) catch 0) else null;
const elem = try self.resolveAnonTy(e.elem);
return self.emitTyConst(self.types.makeSlice(len, elem));
},
@ -1954,7 +1973,7 @@ pub const StringEncodeResutl = union(enum) {
pub fn encodeString(
literal: []const u8,
buf: []u8,
) StringEncodeResutl {
) !StringEncodeResutl {
const SPECIAL_CHARS = "nrt\\'\"0";
const TO_BYTES = "\n\r\t\\\'\"\x00";
@ -1964,7 +1983,7 @@ pub fn encodeString(
var bytes = std.mem.splitScalar(u8, literal, '\\');
while (bytes.next()) |chunk| {
str.appendSliceAssumeCapacity(chunk);
try str.appendSlice(std.testing.failing_allocator, chunk);
if (bytes.rest().len == 0) break;
switch (bytes.rest()[0]) {
'{' => {
@ -1977,14 +1996,14 @@ pub fn encodeString(
const byte_val = std.fmt.parseInt(u8, hex_bytes[i .. i + 2], 16) catch {
return .{ .err = .{ .reason = "expected hex digit or '}'", .pos = literal.len - bytes.rest().len } };
};
str.appendAssumeCapacity(byte_val);
try str.append(std.testing.failing_allocator, byte_val);
}
bytes.index.? += i + 1;
},
else => |b| {
for (SPECIAL_CHARS, TO_BYTES) |s, sb| {
if (s == b) {
str.appendAssumeCapacity(sb);
try str.append(std.testing.failing_allocator, sb);
break;
}
} else return .{ .err = .{ .reason = "unknown escape sequence", .pos = literal.len - bytes.rest().len } };
@ -2249,8 +2268,22 @@ fn emitDirective(self: *Codegen, ctx: Ctx, expr: Ast.Id, e: *const Ast.Store.Tag
.name_of => {
try static.assertArgs(self, expr, args, "<ty>");
const ty = try self.resolveAnonTy(args[0]);
const data = std.fmt.allocPrint(self.types.arena.allocator(), "{}", .{ty.fmt(self.types)}) catch unreachable;
var value = try self.emit(.{}, args[0]);
const data = if (value.ty == .type) dt: {
const ty = try self.unwrapTyConst(args[0], &value);
break :dt std.fmt.allocPrint(self.types.arena.allocator(), "{}", .{ty.fmt(self.types)}) catch unreachable;
} else switch (value.ty.data()) {
.Enum => |enum_ty| dt: {
if (enum_ty.getFields(self.types).len == 1) {
break :dt enum_ty.getFields(self.types)[0].name;
}
const id = try self.partialEval(args[0], value.getValue(self));
break :dt enum_ty.getFields(self.types)[@intCast(id)].name;
},
else => return self.report(args[0], "can't compute a name of {}", .{value.ty}),
};
return self.emitStirng(ctx, data, expr);
},

View file

@ -73,6 +73,7 @@ pub fn partialEval(self: *Comptime, bl: *Builder, expr: *Node) PartialEvalResult
while (true) {
const curr = work_list.pop().?;
if (curr.id == std.math.maxInt(u16)) continue;
const res = switch (curr.kind) {
.CInt => {
if (work_list.items.len == 0) {

View file

@ -228,6 +228,7 @@ fn fmtExprPrec(self: *Fmt, id: Id, prec: u8) Error!void {
.Float => |f| try self.buf.appendSlice(Lexer.peekStr(self.ast.source, f.index)),
.Bool => |b| try self.buf.appendSlice(if (b.value) "true" else "false"),
.String => |s| try self.buf.appendSlice(Lexer.peekStr(self.ast.source, s.pos.index)),
.Quotes => |s| try self.buf.appendSlice(Lexer.peekStr(self.ast.source, s.pos.index)),
.Null => try self.buf.appendSlice("null"),
}
}

View file

@ -317,6 +317,8 @@ pub fn next(self: *Lexer) Token {
block_comment,
block_comment_end,
line_commnet,
double_quotes,
double_quotes_slash,
quotes,
quotes_slash,
zero,
@ -340,13 +342,14 @@ pub fn next(self: *Lexer) Token {
'$', '@', 'a'...'z', 'A'...'Z', '_', 128...255 => continue :state .ident,
'0' => continue :state .zero,
'1'...'9' => continue :state .dec,
'"' => continue :state .quotes,
'"' => continue :state .double_quotes,
'\'' => continue :state .quotes,
'/' => continue :state .slash,
'.' => continue :state .dot,
'=' => continue :state .equal,
'<', '>' => continue :state .angle_bracket,
':', '+', '-', '*', '%', '|', '^', '&', '!' => continue :state .op_equal,
'#', '\'', '(', ')', ',', ';', '?', '[', '\\', ']', '`', '{', '}', '~' => |c| {
'#', '(', ')', ',', ';', '?', '[', '\\', ']', '`', '{', '}', '~' => |c| {
self.cursor += 1;
break :state @enumFromInt(c);
},
@ -468,9 +471,9 @@ pub fn next(self: *Lexer) Token {
.quotes => {
self.cursor += 1;
switch (self.source[self.cursor]) {
'"' => {
'\'' => {
self.cursor += 1;
break :state .@"\"";
break :state .@"'";
},
'\\' => {
self.cursor += 1;
@ -484,6 +487,25 @@ pub fn next(self: *Lexer) Token {
0 => break :state .@"unterminated string",
else => continue :state .quotes,
},
.double_quotes => {
self.cursor += 1;
switch (self.source[self.cursor]) {
'"' => {
self.cursor += 1;
break :state .@"\"";
},
'\\' => {
self.cursor += 1;
continue :state .double_quotes_slash;
},
0 => break :state .@"unterminated string",
else => continue :state .double_quotes,
}
},
.double_quotes_slash => switch (self.source[self.cursor]) {
0 => break :state .@"unterminated string",
else => continue :state .double_quotes,
},
.zero => {
self.cursor += 1;
switch (self.source[self.cursor]) {

View file

@ -471,6 +471,7 @@ fn parseUnitWithoutTail(self: *Parser) Error!Id {
.Float => .{ .Float = .init(token.pos) },
.true => .{ .Bool = .{ .value = true, .pos = .init(token.pos) } },
.@"\"" => .{ .String = .{ .pos = .init(token.pos), .end = token.end } },
.@"'" => .{ .Quotes = .{ .pos = .init(token.pos), .end = token.end } },
.false => .{ .Bool = .{ .value = false, .pos = .init(token.pos) } },
else => |k| {
self.report(token.pos, "no idea how to handle this: {s}", .{@tagName(k)});

View file

@ -546,7 +546,7 @@ pub const Id = enum(usize) {
pub fn len(self: Id, types: *Types) ?usize {
return switch (self.data()) {
inline .Struct, .Union => |s| s.getFields(types).len,
inline .Struct, .Union, .Enum => |s| s.getFields(types).len,
.Slice => |s| s.len,
else => null,
};

View file

@ -1,20 +1,25 @@
const std = @import("std");
pub const root = @import("utils.zig");
pub const utils = @import("utils.zig");
pub const root = @import("root.zig");
pub const test_util = @import("test_util.zig");
pub const hbc = @import("hbc.zig");
pub const fuzz = @import("fuzz.zig");
comptime {
if (@import("root") == @This()) std.testing.refAllDeclsRecursive(@This());
std.testing.refAllDeclsRecursive(@This());
}
var ran = false;
pub fn runTest(name: []const u8, code: [:0]const u8) !void {
root.Arena.initScratch(1024 * 1024);
defer root.Arena.deinitScratch();
if (!ran) {
utils.Arena.initScratch(1024 * 1024);
ran = true;
}
const gpa = std.testing.allocator;
try test_util.testFmt(name, name, code);
//try test_util.testFmt(name, name, code);
var out = std.ArrayList(u8).init(gpa);
defer out.deinit();
@ -22,7 +27,7 @@ pub fn runTest(name: []const u8, code: [:0]const u8) !void {
errdefer {
const stderr = std.io.getStdErr();
const colors = std.io.tty.detectConfig(stderr);
test_util.testBuilder(name, code, gpa, stderr.writer().any(), colors, true) catch unreachable;
test_util.testBuilder(name, code, gpa, stderr.writer().any(), colors, true) catch {};
}
try test_util.testBuilder(name, code, gpa, out.writer().any(), .no_color, false);
@ -32,8 +37,8 @@ pub fn runTest(name: []const u8, code: [:0]const u8) !void {
}
pub fn runFuzzFindingTest(name: []const u8, code: []const u8) !void {
root.Arena.initScratch(1024 * 1024);
defer root.Arena.deinitScratch();
utils.Arena.initScratch(1024 * 1024);
defer utils.Arena.deinitScratch();
const gpa = std.testing.allocator;
@ -49,8 +54,8 @@ pub fn runFuzzFindingTest(name: []const u8, code: []const u8) !void {
}
pub fn runVendoredTest(path: []const u8) !void {
if (true) return;
root.Arena.initScratch(1024 * 1024);
defer root.Arena.deinitScratch();
if (std.mem.count(u8, path, "lily") == 2) return;
utils.Arena.initScratch(1024 * 1024);
defer utils.Arena.deinitScratch();
try test_util.runVendoredTest(std.testing.allocator, path);
}

View file

@ -1,4 +1,5 @@
main:
li64 $13, 4
addi64 $13, $13, 3
cp $1, $13
tx

32
tests/enums 4.txt Normal file
View file

@ -0,0 +1,32 @@
main:
st $31, $254, -24, 24
addi64 $254, $254, -24
lra $32, $0, :map
li64 $33, 0
cp $1, $32
cp $2, $33
jal $31, $0, :NameMap(Enum = Nm)(StrBuf = [6]u8, IndexBuf = [4]uint).get
cp $32, $1
ld $32, $32, 0, 1
andi $32, $32, 255
cp $1, $32
addi64 $254, $254, 24
ld $31, $254, -24, 24
tx
NameMap(Enum = Nm)(StrBuf = [6]u8, IndexBuf = [4]uint).get:
cp $13, $1
cp $14, $2
andi $15, $14, 255
addi64 $15, $15, 1
addi64 $16, $13, 8
muli64 $14, $14, 8
muli64 $15, $15, 8
add64 $14, $16, $14
add64 $15, $16, $15
ld $14, $14, 0, 8
ld $15, $15, 0, 8
add64 $13, $13, $14
sub64 $14, $15, $14
cp $1, $13
cp $2, $14
jala $0, $31, 0

4
tests/literals 3.txt Normal file
View file

@ -0,0 +1,4 @@
main:
li64 $13, 69
cp $1, $13
tx