1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-21 02:27:10 +03:00
mal/impls/zig/step6_file.zig

344 lines
11 KiB
Zig
Raw Normal View History

2019-12-08 23:07:41 +03:00
const std = @import("std");
const reader = @import("reader.zig");
const printer = @import("printer.zig");
const getline = @import("readline.zig").getline;
const string_eql = std.hash_map.eqlString;
2019-12-08 23:07:41 +03:00
const hash_map = @import("hmap.zig");
const core = @import("core.zig");
const Allocator = @import("std").heap.c_allocator;
const MalType = @import("types.zig").MalType;
const MalError = @import("error.zig").MalError;
const MalLinkedList = @import("linked_list.zig").MalLinkedList;
const Env = @import("env.zig").Env;
const get_error_data = @import("error.zig").get_error_data;
const throw = @import("error.zig").throw;
const stdout_file = std.io.getStdOut();
2019-12-08 23:07:41 +03:00
var repl_environment = Env.new_root();
2019-12-08 23:07:41 +03:00
fn READ(a: []const u8) !*MalType {
2019-12-08 23:07:41 +03:00
var read = try reader.read_str(a);
return reader.read_form(&read);
2019-12-08 23:07:41 +03:00
}
// Do not allocate this one on each EVAL run.
// The string is static, but will never be deallocated.
var DEBUG_EVAL = MalType { .Symbol = .{ .data = "DEBUG-EVAL" } };
fn EVAL(mal_arg: *MalType, env_arg: *Env, finally_destroy_env: bool) MalError!*MalType {
2019-12-08 23:07:41 +03:00
var mal = mal_arg;
var env = env_arg;
var fde = finally_destroy_env;
defer if(fde) env.decref();
2019-12-08 23:07:41 +03:00
while(true) {
if(try env.get(&DEBUG_EVAL)) |dbgeval| {
switch (dbgeval.*) {
.Nil, .False => {},
else => {
try stdout_file.writeAll("EVAL: ");
try PRINT(mal.*);
}
}
}
switch(mal.*) {
2019-12-08 23:07:41 +03:00
.List => |ll| {
const items = ll.data.items;
if(items.len == 0) {
mal.incref();
2019-12-08 23:07:41 +03:00
return mal;
}
const first_mal = items[0];
const symbol = switch(first_mal.*) {
.Symbol => |symbol| symbol.data,
2019-12-08 23:07:41 +03:00
else => "",
};
if(string_eql(symbol, "def!")) {
return EVAL_def(items[1..], env);
2019-12-08 23:07:41 +03:00
}
else if(string_eql(symbol, "let*")) {
try EVAL_let(items[1..], &mal, &env, &fde);
2019-12-08 23:07:41 +03:00
continue;
}
else if(string_eql(symbol, "do")) {
try EVAL_do(items[1..], &mal, env);
2019-12-08 23:07:41 +03:00
continue;
}
else if(string_eql(symbol, "if")) {
try EVAL_if(items[1..], &mal, env);
2019-12-08 23:07:41 +03:00
continue;
}
else if(string_eql(symbol, "fn*")) {
return EVAL_fn(items[1..], env);
2019-12-08 23:07:41 +03:00
}
else {
const evaluated_first = try EVAL(first_mal, env, false);
defer evaluated_first.decref();
// A slice would be sufficient, but a List is convenient
// for partial deallocation in case of error.
const args = try MalType.new_list();
defer args.decref();
for(items[1..]) |x| {
const new_item = try EVAL(x, env, false);
try args.List.data.append(Allocator, new_item);
}
switch(evaluated_first.*) {
.Func => |func_data| {
if(fde) {
env.decref();
}
else {
fde = true;
}
env = try func_data.gen_env(args.List.data.items);
mal = func_data.body;
continue;
},
else => {},
2019-12-08 23:07:41 +03:00
}
return apply_function(evaluated_first.*, args.List.data.items);
2019-12-08 23:07:41 +03:00
}
},
.Symbol => {
return EVAL_symbol(mal, env);
},
.Vector => |ll| {
return EVAL_vector(ll.data.items, env);
},
.HashMap => |hmap| {
return EVAL_map(hmap.data, env);
},
2019-12-08 23:07:41 +03:00
else => {
mal.incref();
return mal;
2019-12-08 23:07:41 +03:00
},
}
}
}
fn eval(args: []*MalType) !*MalType {
if(args.len != 1) return MalError.ArgError;
const a1 = args[0];
return EVAL(a1, &repl_environment, false);
2019-12-08 23:07:41 +03:00
}
fn EVAL_def(args: []*MalType, env: *Env) !*MalType {
if(args.len != 2) return MalError.ArgError;
const symbol_name = args[0];
const second_arg = args[1];
const new_value = try EVAL(second_arg, env, false);
2019-12-08 23:07:41 +03:00
try env.set(symbol_name, new_value);
new_value.incref();
return new_value;
2019-12-08 23:07:41 +03:00
}
fn EVAL_let(args: []*MalType, mal_ptr: **MalType, env_ptr: **Env, fde: *bool) !void {
if(args.len != 2) return MalError.ArgError;
2019-12-08 23:07:41 +03:00
const env = env_ptr.*;
const binding_arg = args[0];
const eval_arg = args[1];
const binds = try binding_arg.as_slice();
if(binds.len % 2 != 0) return MalError.ArgError;
const new_env = try Env.new(env);
// Change env and fde in case an error occurs later in this procedure
// and fde triggers an env.decref() at the exit of EVAL.
if(!fde.*) {
env.incref();
fde.* = true;
}
env_ptr.* = new_env;
for(0..binds.len / 2) |i| {
const key = binds[2*i];
const val_mal = binds[2*i + 1];
const evaled_mal = try EVAL(val_mal, new_env, false);
errdefer evaled_mal.decref();
2019-12-08 23:07:41 +03:00
try new_env.set(key, evaled_mal);
// Do not increment the refcount for the value.
2019-12-08 23:07:41 +03:00
}
mal_ptr.* = eval_arg;
}
fn EVAL_do(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {
if(args.len == 0) return MalError.ArgError;
const last_mal = args[args.len - 1];
for (args[0..args.len - 1]) |form| {
const item = try EVAL(form, env, false);
item.decref();
}
2019-12-08 23:07:41 +03:00
mal_ptr.* = last_mal;
}
fn EVAL_if(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {
if(args.len != 2 and args.len != 3) return MalError.ArgError;
const first_arg = args[0];
const evaled = try EVAL(first_arg, env, false);
const is_true = switch(evaled.*) {
2019-12-08 23:07:41 +03:00
.False => false,
.Nil => false,
else => true,
};
evaled.decref();
2019-12-08 23:07:41 +03:00
if(is_true) {
const second_arg = args[1];
mal_ptr.* = second_arg;
2019-12-08 23:07:41 +03:00
return;
}
if(args.len == 2) {
mal_ptr.* = &MalType.NIL;
2019-12-08 23:07:41 +03:00
return;
}
const third_arg = args[2];
mal_ptr.* = third_arg;
2019-12-08 23:07:41 +03:00
}
fn EVAL_fn(args: []*MalType, env: *Env) !*MalType {
if(args.len != 2) return MalError.ArgError;
const arg_mal = args[0];
const body_mal = args[1];
for (try arg_mal.as_slice()) |x| {
switch (x.*) {
.Symbol => {},
else => return MalError.TypeError,
}
}
const new_func = try MalType.newFunc(arg_mal, body_mal, env);
arg_mal.incref();
body_mal.incref();
env.incref();
2019-12-08 23:07:41 +03:00
return new_func;
}
fn PRINT(mal: MalType) !void {
try printer.one_stdout(mal);
try stdout_file.writeAll("\n");
2019-12-08 23:07:41 +03:00
}
fn rep(print: bool, input: []const u8) !void {
const read_input = try READ(input);
defer read_input.decref();
const eval_input = try EVAL(read_input, &repl_environment, false);
defer eval_input.decref();
if(print) {
try PRINT(eval_input.*);
}
}
fn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {
if(try env.get(mal)) |value| {
value.incref();
return value;
}
const err = try std.fmt.allocPrint(Allocator, "'{s}' not found",
.{mal.Symbol.data});
return throw(try MalType.new_string(err, false));
2019-12-08 23:07:41 +03:00
}
fn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {
const ret_mal = try MalType.new_vector();
errdefer ret_mal.decref();
for(ll) |x| {
const new_mal = try EVAL(x, env, false);
try ret_mal.Vector.data.append(Allocator, new_mal);
2019-12-08 23:07:41 +03:00
}
return ret_mal;
}
fn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {
const new_hashmap = try MalType.new_hashmap();
errdefer new_hashmap.decref();
2019-12-08 23:07:41 +03:00
var iterator = hmap.iterator();
while(iterator.next()) |pair| {
const key = pair.key_ptr.*;
const value = pair.value_ptr.*;
const evaled_value = try EVAL(value, env, false);
try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);
2019-12-08 23:07:41 +03:00
}
return new_hashmap;
}
fn make_environment() !void {
2019-12-08 23:07:41 +03:00
for(core.core_namespace) |pair| {
const name = try MalType.new_symbol(pair.name, true);
const func_mal = try MalType.newFnCore(pair.func);
try repl_environment.set(name, func_mal);
name.decref();
2019-12-08 23:07:41 +03:00
}
const eval_sym = try MalType.new_symbol("eval", true);
const eval_mal = try MalType.newFnCore(eval);
try repl_environment.set(eval_sym, eval_mal);
eval_sym.decref();
2019-12-08 23:07:41 +03:00
const def_not_string: [] const u8 =
\\(def! not (fn* (a) (if a false true)))
;
try rep(false, def_not_string);
2019-12-08 23:07:41 +03:00
const load_file_string: [] const u8 =
\\(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))
;
try rep(false, load_file_string);
2019-12-08 23:07:41 +03:00
}
pub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {
switch(f) {
.FnCore => |fncoredata| {
return fncoredata.data(args);
},
.Func => |funcdata| {
const apply_env = try funcdata.gen_env(args);
defer apply_env.decref();
return EVAL(funcdata.body, apply_env, false);
},
else => {
return MalError.ApplyError;
},
2019-12-08 23:07:41 +03:00
}
}
pub fn main() !void {
// Break a circular dependency between modules.
core.apply_function = &apply_function;
try make_environment();
2019-12-08 23:07:41 +03:00
const args = try std.process.argsAlloc(Allocator);
const arg_list = try MalType.new_list();
if(1 < args.len) {
for (args[2..]) |arg| {
const new_mal = try MalType.new_string(arg, false);
try arg_list.List.data.append(Allocator, new_mal);
}
2019-12-08 23:07:41 +03:00
}
const argv_sym = try MalType.new_symbol("*ARGV*", true);
try repl_environment.set(argv_sym, arg_list);
argv_sym.decref();
2019-12-08 23:07:41 +03:00
if(args.len > 1) {
const run_cmd = try std.fmt.allocPrint(Allocator, "(load-file \"{s}\")", .{args[1]});
try rep(false, run_cmd);
2019-12-08 23:07:41 +03:00
return;
}
while(try getline("user> ")) |line| {
defer Allocator.free(line);
rep(true, line) catch |err| {
try stdout_file.writeAll("Error: ");
try stdout_file.writeAll(@errorName(err));
try stdout_file.writeAll("\n");
if(get_error_data()) |mal| {
defer mal.decref();
try stdout_file.writeAll("MAL error object is: ");
try PRINT(mal.*);
}
};
2019-12-08 23:07:41 +03:00
}
}