1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-20 10:07:45 +03:00
mal/d/mal_core.d
2016-02-12 10:16:05 -05:00

399 lines
10 KiB
D

import core.time;
import std.algorithm;
import std.array;
import std.datetime;
import std.file;
import std.stdio;
import env;
import main;
import reader;
import readline;
import types;
import printer;
static MalType mal_equal(MalType[] a ...)
{
verify_args_count(a, 2);
return bool_to_mal(a[0] == a[1]);
}
static MalType mal_throw(MalType[] a ...)
{
verify_args_count(a, 1);
throw new MalException(a[0]);
}
static MalType mal_symbol(MalType[] a ...)
{
verify_args_count(a, 1);
auto s = verify_cast!MalString(a[0]);
return new MalSymbol(s.val);
}
static MalType mal_string_q(MalType[] a ...)
{
verify_args_count(a, 1);
auto s = cast(MalString) a[0];
if (s is null) return mal_false;
return bool_to_mal(!s.is_keyword());
}
static MalType mal_keyword(MalType[] a ...)
{
verify_args_count(a, 1);
auto s = verify_cast!MalString(a[0]);
return new MalString("\u029e" ~ s.val);
}
static MalType mal_keyword_q(MalType[] a ...)
{
verify_args_count(a, 1);
auto s = cast(MalString) a[0];
if (s is null) return mal_false;
return bool_to_mal(s.is_keyword());
}
static MalType mal_pr_str(MalType[] a ...)
{
auto items_strs = a.map!(e => pr_str(e, true));
return new MalString(array(items_strs).join(" "));
}
static MalType mal_str(MalType[] a ...)
{
auto items_strs = a.map!(e => pr_str(e, false));
return new MalString(array(items_strs).join(""));
}
static MalType mal_prn(MalType[] a ...)
{
auto items_strs = a.map!(e => pr_str(e, true));
writeln(array(items_strs).join(" "));
return mal_nil;
}
static MalType mal_println(MalType[] a ...)
{
auto items_strs = a.map!(e => pr_str(e, false));
writeln(array(items_strs).join(" "));
return mal_nil;
}
static MalType mal_read_string(MalType[] a ...)
{
verify_args_count(a, 1);
auto s = verify_cast!MalString(a[0]);
return read_str(s.val);
}
static MalType mal_readline(MalType[] a ...)
{
verify_args_count(a, 1);
auto s = verify_cast!MalString(a[0]);
auto line = _readline(s.val);
return line is null ? mal_nil : new MalString(line);
}
static MalType mal_slurp(MalType[] a ...)
{
verify_args_count(a, 1);
auto filename = verify_cast!MalString(a[0]).val;
auto content = cast(string) std.file.read(filename);
return new MalString(content);
}
alias TwoIntFunc = MalType function(long x, long y);
MalType binary_int_op(TwoIntFunc f, MalType[] a ...)
{
verify_args_count(a, 2);
MalInteger i0 = verify_cast!MalInteger(a[0]);
MalInteger i1 = verify_cast!MalInteger(a[1]);
return f(i0.val, i1.val);
}
static MalType mal_time_ms(MalType[] a ...)
{
immutable epoch = SysTime(unixTimeToStdTime(0));
immutable hnsecs_since_epoch = Clock.currTime(UTC()) - epoch;
immutable ms = hnsecs_since_epoch.total!"msecs"();
return new MalInteger(ms);
}
static bool is_nil(MalType v)
{
return cast(MalNil)(v) !is null;
}
static MalType mal_assoc(MalType[] a ...)
{
verify_min_args_count(a, 1);
auto hm = verify_cast!MalHashmap(a[0]);
auto new_hm = new MalHashmap(hm.data.dup);
new_hm.put_kv_list(a[1..$]);
return new_hm;
}
static MalType mal_dissoc(MalType[] a ...)
{
verify_min_args_count(a, 1);
auto hm = verify_cast!MalHashmap(a[0]);
auto new_hm = new MalHashmap(hm.data.dup);
foreach (k; a[1..$])
{
new_hm.remove(k);
}
return new_hm;
}
static MalType mal_get(MalType[] a ...)
{
verify_args_count(a, 2);
if (is_nil(a[0])) return mal_nil;
auto hm = verify_cast!MalHashmap(a[0]);
return hm.get(a[1]);
}
static MalType mal_contains_q(MalType[] a ...)
{
verify_args_count(a, 2);
if (is_nil(a[0])) return mal_false;
auto hm = verify_cast!MalHashmap(a[0]);
return bool_to_mal(hm.contains(a[1]));
}
static MalType mal_keys(MalType[] a ...)
{
verify_args_count(a, 1);
auto hm = verify_cast!MalHashmap(a[0]);
auto keys = hm.data.keys.map!(s => cast(MalType)(new MalString(s)));
return new MalList(array(keys));
}
static MalType mal_vals(MalType[] a ...)
{
verify_args_count(a, 1);
auto hm = verify_cast!MalHashmap(a[0]);
return new MalList(hm.data.values);
}
static MalType mal_cons(MalType[] a ...)
{
verify_args_count(a, 2);
auto lst = verify_cast!MalSequential(a[1]);
return new MalList([a[0]] ~ lst.elements);
}
static MalType mal_concat(MalType[] a ...)
{
MalType[] res;
foreach (e; a)
{
auto lst = verify_cast!MalSequential(e);
res ~= lst.elements;
}
return new MalList(res);
}
static MalType mal_nth(MalType[] a ...)
{
verify_args_count(a, 2);
if (is_nil(a[0]))
{
throw new Exception("nth: index out of range");
}
auto seq = verify_cast!MalSequential(a[0]);
auto index = verify_cast!MalInteger(a[1]).val;
if (index >= seq.elements.length)
{
throw new Exception("nth: index out of range");
}
return seq.elements[index];
}
static MalType mal_first(MalType[] a ...)
{
verify_args_count(a, 1);
if (is_nil(a[0])) return mal_nil;
auto seq = verify_cast!MalSequential(a[0]);
if (seq.elements.length == 0) return mal_nil;
return seq.elements[0];
}
static MalType mal_rest(MalType[] a ...)
{
verify_args_count(a, 1);
if (is_nil(a[0])) return new MalList([]);
auto seq = verify_cast!MalSequential(a[0]);
if (seq.elements.length == 0) return new MalList([]);
return new MalList(seq.elements[1..$]);
}
static MalType mal_empty_q(MalType[] a ...)
{
verify_args_count(a, 1);
if (is_nil(a[0]))
{
return mal_true;
}
auto s = verify_cast!MalSequential(a[0]);
return bool_to_mal(s.elements.length == 0);
}
static MalType mal_count(MalType[] a ...)
{
verify_args_count(a, 1);
if (is_nil(a[0]))
{
return new MalInteger(0);
}
auto s = verify_cast!MalSequential(a[0]);
return new MalInteger(cast(int)(s.elements.length));
}
static MalType mal_apply(MalType[] a ...)
{
verify_min_args_count(a, 2);
auto last_seq_elems = verify_cast!MalSequential(a[$-1]).elements;
auto funcargs = a.length == 2 ? last_seq_elems : (a[1..$-1] ~ last_seq_elems);
auto builtinfn = cast(MalBuiltinFunc) a[0];
if (builtinfn !is null)
{
return builtinfn.fn(funcargs);
}
auto malfunc = verify_cast!MalFunc(a[0]);
auto callenv = new Env(malfunc.def_env, malfunc.arg_names, funcargs);
return EVAL(malfunc.func_body, callenv);
}
static MalType mal_map(MalType[] a ...)
{
verify_args_count(a, 2);
auto seq = verify_cast!MalSequential(a[1]);
auto mapped_items = seq.elements.map!(e => mal_apply(a[0], new MalList([e])));
return new MalList(array(mapped_items));
}
static MalType mal_conj(MalType[] a ...)
{
verify_min_args_count(a, 1);
auto seq = verify_cast!MalSequential(a[0]);
return reduce!((s,e) => s.conj(e))(seq, a[1..$]);
}
static MalType mal_seq(MalType[] a ...)
{
verify_args_count(a, 1);
auto seqobj = cast(HasSeq) a[0];
if (seqobj is null) return mal_nil;
return seqobj.seq();
}
static MalType mal_meta(MalType[] a ...)
{
verify_args_count(a, 1);
auto metaobj = cast(MalMeta) a[0];
if (metaobj is null) return mal_nil;
return metaobj.meta();
}
static MalType mal_with_meta(MalType[] a ...)
{
verify_args_count(a, 2);
auto metaobj = cast(MalMeta) a[0];
if (metaobj is null) return a[0];
return metaobj.with_meta(a[1]);
}
static MalType mal_reset_bang(MalType[] a ...)
{
verify_args_count(a, 2);
verify_cast!MalAtom(a[0]).val = a[1];
return a[1];
}
static MalType mal_swap_bang(MalType[] a ...)
{
verify_min_args_count(a, 2);
auto atom = verify_cast!MalAtom(a[0]);
auto args = [atom.val] ~ a[2..$];
auto newval = mal_apply([a[1], new MalList(args)]);
return mal_reset_bang([atom, newval]);
}
BuiltinStaticFuncType[string] core_ns;
static this()
{
core_ns = [
"=": &mal_equal,
"throw": &mal_throw,
"nil?": (a ...) => mal_type_q!MalNil(a),
"true?": (a ...) => mal_type_q!MalTrue(a),
"false?": (a ...) => mal_type_q!MalFalse(a),
"symbol": &mal_symbol,
"symbol?": (a ...) => mal_type_q!MalSymbol(a),
"string?": &mal_string_q,
"keyword": &mal_keyword,
"keyword?": &mal_keyword_q,
"pr-str": &mal_pr_str,
"str": &mal_str,
"prn": &mal_prn,
"println": &mal_println,
"read-string": &mal_read_string,
"readline": &mal_readline,
"slurp": &mal_slurp,
"<": (a ...) => binary_int_op((x,y) => bool_to_mal(x < y), a),
"<=": (a ...) => binary_int_op((x,y) => bool_to_mal(x <= y), a),
">": (a ...) => binary_int_op((x,y) => bool_to_mal(x > y), a),
">=": (a ...) => binary_int_op((x,y) => bool_to_mal(x >= y), a),
"+": (a ...) => binary_int_op((x,y) => new MalInteger(x + y), a),
"-": (a ...) => binary_int_op((x,y) => new MalInteger(x - y), a),
"*": (a ...) => binary_int_op((x,y) => new MalInteger(x * y), a),
"/": (a ...) => binary_int_op((x,y) => new MalInteger(x / y), a),
"time-ms": &mal_time_ms,
"list": (a ...) => new MalList(a),
"list?": (a ...) => mal_type_q!MalList(a),
"vector": (a ...) => new MalVector(a),
"vector?": (a ...) => mal_type_q!MalVector(a),
"hash-map": (a ...) => new MalHashmap(a),
"map?": (a ...) => mal_type_q!MalHashmap(a),
"assoc": &mal_assoc,
"dissoc": &mal_dissoc,
"get": &mal_get,
"contains?": &mal_contains_q,
"keys": &mal_keys,
"vals": &mal_vals,
"sequential?": (a ...) => mal_type_q!MalSequential(a),
"cons": &mal_cons,
"concat": &mal_concat,
"nth": &mal_nth,
"first": &mal_first,
"rest": &mal_rest,
"empty?": &mal_empty_q,
"count": &mal_count,
"apply": &mal_apply,
"map": &mal_map,
"conj": &mal_conj,
"seq": &mal_seq,
"meta": &mal_meta,
"with-meta": &mal_with_meta,
"atom": (a ...) => new MalAtom(verify_args_count(a, 1)[0]),
"atom?": (a ...) => mal_type_q!MalAtom(a),
"deref": (a ...) => verify_cast!MalAtom(verify_args_count(a, 1)[0]).val,
"reset!": &mal_reset_bang,
"swap!": &mal_swap_bang
];
}