commit 68378e629d0ced133499efba76b570bd7a5e4068
parent c28ffd6c01e94fde49e78fc710a307922efbaf0a
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Thu, 27 Aug 2026 17:29:58 +0200
Anonymous functions
Diffstat:
11 files changed, 45 insertions(+), 75 deletions(-)
diff --git a/zlox/fib.lox b/zlox/fib.lox
@@ -1,9 +1,9 @@
-fun fib(n) {
+con fib = fun(n) {
if (n < 2) return n;
return fib(n - 2) + fib(n - 1);
-}
+};
-fun printf(fmt, args) {
+con printf = fun(fmt, args) {
var args_i = 0;
for(var i = 0; fmt[i]; i = i + 1) {
if(fmt[i] == '{' and fmt[i+1] == '}') {
@@ -15,15 +15,15 @@ fun printf(fmt, args) {
}
}
print "";
-}
+};
-fun main() {
+con main = fun() {
con num = 28;
con start = clock();
con result = fib(num);
con end = clock() - start;
printf("fib({}) == {}, time: {} ms", [num, result, end]);
-}
+};
main();
diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig
@@ -115,6 +115,7 @@ pub fn Compiler(size: comptime_int) type {
T.NIL => R(S.literal, null, P.NONE ),
T.OR => R(null, S._or, P.OR ),
T.TRUE => R(S.literal, null, P.NONE ),
+ T.FUN => R(S.function, null, P.NONE ),
else => R(null, null, P.NONE ),
// zig fmt: on
};
@@ -328,13 +329,11 @@ pub fn Compiler(size: comptime_int) type {
}
if (isList) {
self.currentChunk().code.set(offset, self.makeObj(.Native, Obj.Native.Arg{
- .name = "list",
.fun = vm_native.list,
.type = .Literal,
}) catch return) catch return;
} else {
self.currentChunk().code.set(offset, self.makeObj(.Native, Obj.Native.Arg{
- .name = "table",
.fun = vm_native.table,
.type = .Literal,
}) catch return) catch return;
@@ -515,9 +514,7 @@ pub fn Compiler(size: comptime_int) type {
}
fn declaration(self: *Self) void {
- if (self.match(Token.FUN)) {
- self.funDeclaration();
- } else if (self.match(Token.VAR)) {
+ if (self.match(Token.VAR)) {
self.varDeclaration();
} else if (self.match(Token.CON)) {
self.conDeclaration();
@@ -528,15 +525,8 @@ pub fn Compiler(size: comptime_int) type {
if (self.panicMode) self.synchronize();
}
- fn funDeclaration(self: *Self) void {
- const global = self.parseVariable("Expect function name.", true) catch return;
- self.markInitialized();
- self.function(self.previous.lexeme, Obj.Function.Type.Function);
- self.defineVariable(global, true);
- }
-
- fn function(self: *Self, name: []const u8, tp: Obj.Function.Type) void {
- var fun = self.objects.emplace(Obj.Type.Function, tp) catch |err| {
+ fn function(self: *Self, _: bool) void {
+ var fun = self.objects.emplace(Obj.Type.Function, .Function) catch |err| {
self.errorAtPrevious("Couldn't allocate function");
self.lastError = err;
return;
@@ -548,12 +538,6 @@ pub fn Compiler(size: comptime_int) type {
return;
};
- fun.set_name(self.objects.emplace(Obj.Type.String, &.{name}) catch |err| {
- self.errorAtPrevious("Couldn't allocate function name");
- self.lastError = err;
- return;
- });
-
compiler.consume(Token.LEFT_PAREN, "Expect '(' after function name");
if (!compiler.check(Token.RIGHT_PAREN)) {
while (true) {
@@ -734,7 +718,6 @@ pub fn Compiler(size: comptime_int) type {
self.consume(Token.LEFT_PAREN, "Expect '(' after 'switch'.");
self.emitObj(.Native, Obj.Native.Arg{
- .name = "switch",
.fun = vm_native.table,
.type = .Literal,
}) catch return;
@@ -1005,7 +988,7 @@ pub fn Compiler(size: comptime_int) type {
pub fn compile(source: []const u8, objects: *GC) CompilerError!*Obj.Function {
var scan = try scanner.Scanner.init(source);
- const fun = try objects.emplace(Obj.Type.Function, Obj.Function.Type.Script);
+ const fun = try objects.emplace(Obj.Type.Function, .Script);
var self = Self.init(&scan, objects, fun);
try objects.push_callback(&gc_callback, &self);
diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig
@@ -7,15 +7,15 @@ const Obj = @import("gc.zig").GC.Obj;
const Error = Obj.Error;
const print = std.debug.print;
-pub fn disassembleChunk(ch: *const chunk.Chunk, name: []const u8) Error!void {
- print("/= {s} =\\\n", .{name});
+pub fn disassembleChunk(ch: *const chunk.Chunk) Error!void {
+ print("/=======\\\n", .{});
var offset: usize = 0;
while (offset < ch.code.len) {
offset = try _disassembleInstruction(ch, offset, true);
}
- print("\\= {s} =/\n", .{name});
+ print("\\=======/\n", .{});
}
pub fn print_offset(ch: *const chunk.Chunk, offset: usize) !void {
@@ -88,14 +88,9 @@ fn constantInstruction(name: []const u8, ch: *const chunk.Chunk, offset: usize,
const constant = try ch.code.get(offset + 1);
const constval = try ch.constants.get(constant);
print("{s:<32} {d:4} '{f}'\n", .{ name, constant, constval });
- if (print_fn and constval.is(Obj.Type.Function)) {
- const function = constval.obj.cast(.Function) catch unreachable;
- if (function.name.get()) |fn_name| {
- try disassembleChunk(function.chunk.ptr(), fn_name.slice());
- } else {
- try disassembleChunk(function.chunk.ptr(), "<anon>");
- }
- }
+ if (print_fn) if (constval.cast_if(Obj.Type.Function)) |function| {
+ try disassembleChunk(function.chunk.ptr());
+ };
return offset + 2;
}
@@ -126,13 +121,8 @@ fn closureInstruction(name: []const u8, ch: *const chunk.Chunk, offset: usize, p
print("{s:<38}|-> {s} {d}\n", .{ "", if (isLocal == 1) "local" else "upvalue", idx });
off += 2;
}
- if (print_fn) {
- if (function.name.get()) |fn_name| {
- try disassembleChunk(function.chunk.ptr(), fn_name.slice());
- } else {
- try disassembleChunk(function.chunk.ptr(), "<anon>");
- }
- }
+ if (print_fn)
+ try disassembleChunk(function.chunk.ptr());
return off + 1;
}
diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig
@@ -99,8 +99,6 @@ pub const GC = struct {
}.fun);
},
*Obj.Function => {
- if (obj.name.ptr()) |name|
- self.mark("f", name);
for (obj.chunk.ptr().constants.slice()) |constant|
self.mark("f", constant);
},
diff --git a/zlox/src/lib::array.zig b/zlox/src/lib::array.zig
@@ -28,6 +28,12 @@ pub fn Array(comptime T: type, comptime S: type, comptime size: S) type {
return self.data[idx];
}
+ pub fn if_get(self: *const @This(), idx: S) ?T {
+ if (idx >= self.len)
+ return null;
+ return self.data[idx];
+ }
+
pub fn set(self: *const @This(), idx: S, val: T) !void {
if (idx >= self.len)
return error.IndexOutOfBounds;
@@ -40,6 +46,12 @@ pub fn Array(comptime T: type, comptime S: type, comptime size: S) type {
return self.data[self.len - 1];
}
+ pub fn if_last(self: *const @This()) ?T {
+ if (self.len == 0)
+ return null;
+ return self.data[self.len - 1];
+ }
+
pub fn deinit(self: *@This()) void {
self.allocator.free(self.data);
}
diff --git a/zlox/src/lib::packed.zig b/zlox/src/lib::packed.zig
@@ -118,7 +118,7 @@ pub fn Packed(Type: type) type {
}
}
- pub fn set(self: Self, val: if (many or slice) Ptr else Child) void {
+ pub fn set(self: Self, val: if (many or slice) utils.mod_ptr_t(Ptr, "const", true) else Child) void {
if (many or slice)
@memcpy(self.ptr(), val)
else
diff --git a/zlox/src/obj::closure.zig b/zlox/src/obj::closure.zig
@@ -33,13 +33,8 @@ pub fn Closure(fields: anytype) type {
return @ptrCast(self);
}
- pub fn format(self: *const Self, writer: *std.Io.Writer) !void {
+ pub fn format(_: *const Self, writer: *std.Io.Writer) !void {
_ = try writer.write("<C: ");
- if (self.function.ptr().name.get()) |fn_name| {
- _ = try writer.write(fn_name.slice());
- } else {
- _ = try writer.write("-");
- }
_ = try writer.writeAll(">");
}
diff --git a/zlox/src/obj::function.zig b/zlox/src/obj::function.zig
@@ -8,10 +8,10 @@ const Obj = @import("obj.zig").Obj;
pub fn Function(fields: anytype) type {
const Super = Obj(fields);
- const String = Super.String;
return packed struct {
const Self = @This();
+
pub const Arg = Type;
pub const Error = error{OutOfMemory};
@@ -20,28 +20,22 @@ pub fn Function(fields: anytype) type {
obj: Super,
arity: u8,
chunk: Packed(*chunk.Chunk),
- name: Packed(?*String),
type: Type,
upvalue_count: u8,
- pub fn init(tp: Arg, allocator: std.mem.Allocator) Error!*Self {
+ pub fn init(tpe: Arg, allocator: std.mem.Allocator) Error!*Self {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
.chunk = try Packed(*chunk.Chunk).create(allocator),
.arity = 0,
- .name = Packed(?*String).init(null),
- .type = tp,
+ .type = tpe,
.upvalue_count = 0,
};
self.chunk.set(try chunk.Chunk.init(allocator));
return self;
}
- pub fn set_name(self: *Self, name: *String) void {
- self.name = Packed(?*String).init(name);
- }
-
pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) {
return @ptrCast(self);
}
@@ -51,11 +45,6 @@ pub fn Function(fields: anytype) type {
.Function => _ = try writer.write("<F: "),
.Script => _ = try writer.write("<S: "),
}
- if (self.name.ptr()) |name| {
- _ = try writer.write(name.slice());
- } else {
- _ = try writer.write("-");
- }
_ = try writer.writeAll(">");
}
diff --git a/zlox/src/obj::native.zig b/zlox/src/obj::native.zig
@@ -25,7 +25,6 @@ pub fn Native(fields: anytype) type {
fun: Fn,
arity_min: u8 = ArityMin,
arity_max: u8 = ArityMax,
- name: []const u8 = "",
type: Type = .Builtin,
};
@@ -33,7 +32,6 @@ pub fn Native(fields: anytype) type {
fun: Packed(Fn),
arity_min: u8,
arity_max: u8,
- name: Packed([]const u8),
type: Type,
pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self {
@@ -43,7 +41,6 @@ pub fn Native(fields: anytype) type {
.fun = Packed(Fn).init(arg.fun),
.arity_min = arg.arity_min,
.arity_max = arg.arity_max,
- .name = Packed([]const u8).init(arg.name),
.type = arg.type,
};
return self;
@@ -63,7 +60,6 @@ pub fn Native(fields: anytype) type {
.Builtin => _ = try writer.write("<B: "),
.Literal => _ = try writer.write("<L: "),
}
- _ = try writer.write(self.name.ptr());
_ = try writer.writeAll(">");
}
diff --git a/zlox/src/value.zig b/zlox/src/value.zig
@@ -56,6 +56,13 @@ pub const Value = union(enum) {
};
}
+ pub fn cast_if(self: Self, comptime tag: anytype) if (@TypeOf(tag) == Tag) ?utils.typeFromTag(Self, tag) else ?*Obj.Type.get(tag) {
+ return switch (self) {
+ toTag(tag) => if (@TypeOf(tag) == Tag) self.get(tag) else self.obj.cast_if(tag),
+ else => null,
+ };
+ }
+
pub fn get(self: Self, comptime tag: anytype) tagType(tag) {
return @field(self, @tagName(toTag(tag)));
}
diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig
@@ -68,9 +68,9 @@ pub const VM = struct {
fn defineNative(self: *@This(), name: []const u8, arity_min: u8, arity_max: u8, fun: Obj.Native.Fn) !void {
const nameObj = try self.objects.emplace(.String, &.{name});
GC.exclude(nameObj.cast());
+
const funObj = try self.objects.emplace_cast(.Native, Obj.Native.Arg{
.fun = fun,
- .name = nameObj.slice(),
.arity_min = arity_min,
.arity_max = arity_max,
});
@@ -117,7 +117,7 @@ pub const VM = struct {
const function = try compiler.Compiler(stack_size).compile(source, &self.objects);
- if (dbg) try debug.disassembleChunk(function.chunk.ptr(), "Main");
+ if (dbg) try debug.disassembleChunk(function.chunk.ptr());
try Interpreter(callstack_size, stack_size).run(self, function, dbg);
}