mirror of
https://github.com/kanaka/mal.git
synced 2024-10-27 06:40:14 +03:00
8c7d495eea
Update build system, syntax and library calls for zig 0.13.0. Rewrite the build system so that the steps can build separately. Drop intermediate symbolic links (unneeded complexity, confusing timestamps). Build with debugging options, this is a toy project full of memory leaks. Declare the allocators as global variables instead of passing and/or storing always the same reference everywhere for no benefit. Make apply_function a global variable instead of adding a reference to EVAL in each function. Pass arguments as a slice instead of using a different type for each argument count. There is no point in renaming default errors. Remove a lot of reference counting and some indirection levels. This fixes the current segmentation faults. Create each object as soon as possible and use errdefer so that it is deallocated if an exception occurs when computing its elemements. Use a global variable to convey a MAL object alongside a thrown error. Remove the unused logging_alloc module (but add a debug_alloc boolean in types.zig).
111 lines
3.2 KiB
Zig
111 lines
3.2 KiB
Zig
const std = @import("std");
|
|
const warn = std.log.warn;
|
|
const allocator = std.heap.c_allocator;
|
|
|
|
const MalType = @import("types.zig").MalType;
|
|
const MalHashMap = @import("hmap.zig").MalHashMap;
|
|
const MalError = @import("error.zig").MalError;
|
|
const hash_map = @import("hmap.zig");
|
|
const debug_alloc = @import("types.zig").debug_alloc;
|
|
|
|
pub const Env = struct {
|
|
outer: ?*Env,
|
|
data: MalHashMap,
|
|
refcount: i32 = 1,
|
|
|
|
pub fn new_root() Env {
|
|
return .{.outer = null, .data = .{}};
|
|
}
|
|
|
|
pub fn new(outer: *Env) !*Env {
|
|
// The caller is in charge of incremeting the reference count
|
|
// for outer if necessary.
|
|
const env = try allocator.create(Env);
|
|
env.* = .{ .outer = outer, .data = .{} };
|
|
if(debug_alloc) warn("Env: new {any}", .{env});
|
|
return env;
|
|
}
|
|
|
|
pub fn incref(env: *Env) void {
|
|
if(debug_alloc) {
|
|
warn("Env: incref {any}", .{env});
|
|
}
|
|
env.refcount += 1;
|
|
// std.debug.assert(env.refcount < 100);
|
|
}
|
|
|
|
pub fn decref(env: *Env) void {
|
|
var e = env;
|
|
while (true) {
|
|
if(debug_alloc) {
|
|
warn("Env: decref {any}", .{e});
|
|
e.print_keys();
|
|
}
|
|
std.debug.assert (0 < e.refcount);
|
|
e.refcount -= 1;
|
|
if(0 < e.refcount) {
|
|
break;
|
|
}
|
|
if(debug_alloc) {
|
|
warn("Env: FREE {any}", .{e});
|
|
}
|
|
const old = e;
|
|
if(e.outer) |outer| {
|
|
e = outer;
|
|
} else {
|
|
warn("INTERNAL ERROR: repl-env should never reach a 0 refcount.", .{});
|
|
break;
|
|
}
|
|
hash_map.map_destroy(&old.data);
|
|
allocator.destroy(old);
|
|
}
|
|
}
|
|
|
|
// Incref both the key and value.
|
|
pub fn set(env: *Env, key: *MalType, value: *MalType) !void {
|
|
// The caller is in charge of incremeting the reference count
|
|
// for the value if necessary.
|
|
switch (key.*) {
|
|
.Symbol => {
|
|
if(debug_alloc) {
|
|
warn("Env: set {s} {any}", .{key.Symbol.data, key});
|
|
}
|
|
try hash_map.map_insert_incref_key(&env.data, key, value);
|
|
},
|
|
else => return MalError.ArgError,
|
|
}
|
|
}
|
|
|
|
pub fn get(env: Env, key: *MalType) !?*MalType {
|
|
// The result is not increfed().
|
|
switch (key.*) {
|
|
.Symbol => {
|
|
if(debug_alloc) {
|
|
warn("Env: get {s} {any}", .{key.Symbol.data, key});
|
|
}
|
|
var e: * const Env = &env;
|
|
while(true) {
|
|
if(e.data.get(key)) |value| {
|
|
return value;
|
|
}
|
|
e = e.outer orelse return null;
|
|
}
|
|
},
|
|
else => return MalError.KeyError,
|
|
}
|
|
}
|
|
|
|
pub fn print_keys(env: Env) void {
|
|
var it = env.data.keyIterator();
|
|
var count: i32 = 5;
|
|
while (it.next()) |key| {
|
|
warn(" key={s},", .{key.*.Symbol.data});
|
|
count -= 1;
|
|
if(count <= 0) {
|
|
warn(" ...", .{});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|