mirror of
https://github.com/kanaka/mal.git
synced 2024-08-17 09:40:21 +03:00
- Reorder README to have implementation list after "learning tool" bullet. - This also moves tests/ and libs/ into impls. It would be preferrable to have these directories at the top level. However, this causes difficulties with the wasm implementations which need pre-open directories and have trouble with paths starting with "../../". So in lieu of that, symlink those directories to the top-level. - Move the run_argv_test.sh script into the tests directory for general hygiene.
161 lines
5.6 KiB
Zig
161 lines
5.6 KiB
Zig
const std = @import("std");
|
|
const warn = @import("std").debug.warn;
|
|
const Allocator = @import("std").mem.Allocator;
|
|
|
|
const string_copy = @import("utils.zig").string_copy;
|
|
const string_eql = @import("utils.zig").string_eql;
|
|
const MalType = @import("types.zig").MalType;
|
|
const MalTypeValue = @import("types.zig").MalTypeValue;
|
|
const MalHashMap = @import("hmap.zig").MalHashMap;
|
|
const MalLinkedList = @import("linked_list.zig").MalLinkedList;
|
|
const MalError = @import("error.zig").MalError;
|
|
const linked_list = @import("linked_list.zig");
|
|
const hash_map = @import("hmap.zig");
|
|
|
|
pub const Env = struct {
|
|
outer: ?**Env,
|
|
data: *MalHashMap,
|
|
allocator: *Allocator,
|
|
refcount: *i32,
|
|
|
|
pub fn new(allocator: *Allocator, optional_outer: ?*Env) MalError!*Env {
|
|
const env = allocator.create(Env) catch return MalError.SystemError;
|
|
env.refcount = allocator.create(i32) catch return MalError.SystemError;
|
|
env.refcount.* = 1;
|
|
if(optional_outer) |outer| {
|
|
const env_ptr = allocator.create(*Env) catch return MalError.SystemError;
|
|
env_ptr.* = try outer.copy(allocator);
|
|
env.outer = env_ptr;
|
|
} else {
|
|
env.outer = null;
|
|
}
|
|
env.data = allocator.create(MalHashMap) catch return MalError.SystemError;
|
|
env.data.* = MalHashMap.init(allocator);
|
|
env.allocator = allocator;
|
|
return env;
|
|
}
|
|
|
|
pub fn copy(env: *Env, allocator: *Allocator) MalError!*Env {
|
|
const new_env = allocator.create(Env) catch return MalError.SystemError;
|
|
new_env.refcount = env.refcount;
|
|
env.refcount.* += 1;
|
|
new_env.outer = env.outer;
|
|
new_env.data = env.data;
|
|
new_env.allocator = allocator;
|
|
return new_env;
|
|
}
|
|
|
|
pub fn delete(env: *Env) void {
|
|
env.refcount.* -= 1;
|
|
if(env.refcount.* <= 0) {
|
|
if(env.outer) |*outer| {
|
|
outer.*.*.delete();
|
|
env.allocator.destroy(env.outer.?);
|
|
}
|
|
//env.print_keys();
|
|
hash_map.destroy(env.allocator, env.data.*, false);
|
|
env.allocator.destroy(env.refcount);
|
|
env.allocator.destroy(env.data);
|
|
}
|
|
env.allocator.destroy(env);
|
|
}
|
|
|
|
pub fn set(env: *Env, key: []const u8, value: *MalType) MalError!void {
|
|
const optional_prev_mal = env.data.getValue(key);
|
|
if(optional_prev_mal) |prev_mal| {
|
|
prev_mal.delete(env.allocator);
|
|
}
|
|
//warn("Setting {}\n", key);
|
|
const key_copy = string_copy(env.allocator, key) catch return MalError.SystemError;
|
|
_ = env.data.put(key_copy, value) catch return MalError.SystemError;
|
|
}
|
|
|
|
pub fn root_set(env: *Env, key: []const u8, value: *MalType) MalError!void {
|
|
var root_env = env;
|
|
while(true) {
|
|
const outer_ptr = root_env.outer orelse break;
|
|
root_env = outer_ptr.*;
|
|
}
|
|
try root_env.set(key, value);
|
|
}
|
|
|
|
pub fn find(env: *const Env, key: []const u8) bool {
|
|
const optional_mal = env.data.getValue(key);
|
|
if(optional_mal) |mal| {
|
|
return true;
|
|
}
|
|
if(env.outer) |outer| {
|
|
return outer.*.find(key);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
pub fn get(env: *const Env, key: []const u8) MalError!*MalType {
|
|
const optional_mal = env.data.getValue(key);
|
|
if(optional_mal) |mal| {
|
|
//warn("Got for key '{}': {} (me: {})\n", key, mal, @ptrToInt(env));
|
|
return mal;
|
|
}
|
|
if(env.outer) |outer| {
|
|
return outer.*.get(key);
|
|
}
|
|
return MalError.EnvLookupError;
|
|
}
|
|
|
|
pub fn set_list(env: *Env, names: MalLinkedList, vals: MalLinkedList) MalError!void {
|
|
var name_arr = names.toSlice();
|
|
var vals_arr = vals.toSlice();
|
|
var i: usize = 0;
|
|
|
|
while(i < name_arr.len) {
|
|
const key = try name_arr[i].as_symbol();
|
|
if(!string_eql(key, "&")) {
|
|
try env.set(key, vals_arr[i]);
|
|
i += 1;
|
|
continue;
|
|
}
|
|
|
|
// Here we deal with variadic binding
|
|
if(i+1 >= name_arr.len) return MalError.OutOfBounds;
|
|
const var_key = try name_arr[i+1].as_symbol();
|
|
var new_ll = MalLinkedList.init(env.allocator);
|
|
new_ll.appendSlice(vals_arr[i..vals_arr.len]) catch return MalError.SystemError;
|
|
const new_mal = try MalType.new_list(env.allocator, new_ll);
|
|
try env.set(var_key, new_mal);
|
|
return;
|
|
}
|
|
}
|
|
|
|
pub fn set_slice(env: *Env, name_arr: []*MalType, vals_arr: []*MalType) MalError!void {
|
|
var i: usize = 0;
|
|
|
|
while(i < name_arr.len) {
|
|
const key = try name_arr[i].as_symbol();
|
|
if(!string_eql(key, "&")) {
|
|
try env.set(key, vals_arr[i]);
|
|
i += 1;
|
|
continue;
|
|
}
|
|
|
|
// Here we deal with variadic binding
|
|
if(i+1 >= name_arr.len) return MalError.OutOfBounds;
|
|
const var_key = try name_arr[i+1].as_symbol();
|
|
var new_ll = MalLinkedList.init(env.allocator);
|
|
new_ll.appendSlice(vals_arr[i..vals_arr.len]) catch return MalError.SystemError;
|
|
const new_mal = try MalType.new_list(env.allocator, new_ll);
|
|
try env.set(var_key, new_mal);
|
|
return;
|
|
}
|
|
}
|
|
|
|
pub fn print_keys(env: *Env) void {
|
|
var it = env.data.iterator();
|
|
var optional_pair = it.next();
|
|
while(optional_pair) |pair| {
|
|
warn("{},",pair.key);
|
|
optional_pair = it.next();
|
|
}
|
|
warn("\n");
|
|
}
|
|
};
|