1
1
mirror of https://github.com/kanaka/mal.git synced 2024-08-17 09:40:21 +03:00
mal/impls/zig/env.zig
Joel Martin 8a19f60386 Move implementations into impls/ dir
- 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.
2020-02-10 23:50:16 -06:00

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");
}
};