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

456 lines
9.8 KiB
D

import std.algorithm;
import std.array;
import std.conv;
import std.functional;
import std.range;
import env;
abstract class MalType
{
string print(bool readable) const;
bool is_truthy() const { return true; }
}
interface MalMeta
{
MalType meta();
MalType with_meta(MalType new_meta);
}
interface HasSeq
{
MalType seq();
}
class MalNil : MalType, HasSeq
{
override string print(bool readable) const { return "nil"; }
override bool is_truthy() const { return false; }
override bool opEquals(Object o) { return (cast(MalNil)(o) !is null); }
override MalType seq() { return this; }
}
class MalFalse : MalType
{
override string print(bool readable) const { return "false"; }
override bool is_truthy() const { return false; }
override bool opEquals(Object o) { return (cast(MalFalse)(o) !is null); }
}
class MalTrue : MalType
{
override string print(bool readable) const { return "true"; }
override bool opEquals(Object o) { return (cast(MalTrue)(o) !is null); }
}
MalNil mal_nil;
MalFalse mal_false;
MalTrue mal_true;
static this()
{
mal_nil = new MalNil;
mal_false = new MalFalse;
mal_true = new MalTrue;
}
MalType bool_to_mal(in bool b)
{
return b ? mal_true : mal_false;
}
class MalSymbol : MalType
{
const string name;
this(in string token) { name = token; }
override string print(bool readable) const { return name; }
override size_t toHash()
{
return typeid(name).getHash(&name);
}
override int opCmp(Object other)
{
MalSymbol o = cast(MalSymbol) other;
return cmp(name, o.name);
}
override bool opEquals(Object other)
{
auto o = cast(MalSymbol) other;
return (o !is null && name == o.name);
}
}
class MalInteger : MalType
{
const long val;
this(string token) { val = to!long(token); }
this(long v) { val = v; }
override string print(bool readable) const { return to!string(val); }
override bool opEquals(Object o)
{
auto oint = cast(MalInteger)(o);
return (oint !is null && val == oint.val);
}
}
class MalString : MalType, HasSeq
{
const string val;
this(in string token) { val = token; }
override string print(bool readable) const
{
if (is_keyword()) return ":" ~ val[2..$];
if (readable)
{
string escaped = val.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n");
return "\"" ~ escaped ~ "\"";
}
else
{
return val;
}
}
bool is_keyword() const
{
return val.length > 1 && val[0..2] == "\u029e";
}
override bool opEquals(Object o)
{
auto ostr = cast(MalString)(o);
return (ostr !is null && val == ostr.val);
}
override MalType seq() {
if (is_keyword() || val.length == 0) return mal_nil;
auto chars = val.map!(c => cast(MalType)(new MalString(to!string(c))));
return new MalList(array(chars));
}
}
abstract class MalSequential : MalType, HasSeq, MalMeta
{
MalType[] elements;
MalType meta_val;
this(MalType[] lst) {
elements = lst;
meta_val = mal_nil;
}
override bool opEquals(Object o)
{
auto oseq = cast(MalSequential)(o);
return (oseq !is null && elements == oseq.elements);
}
MalSequential conj(MalType element);
MalType seq() {
if (elements.length == 0) return mal_nil;
return new MalList(elements);
}
}
class MalList : MalSequential, MalMeta
{
this(MalType[] lst) { super(lst); }
this(MalList that, MalType new_meta)
{
super(that.elements);
meta_val = new_meta;
}
override string print(bool readable) const
{
auto items_strs = elements.map!(e => e.print(readable));
return "(" ~ array(items_strs).join(" ") ~ ")";
}
override MalSequential conj(MalType element)
{
return new MalList([element] ~ elements);
}
override MalType meta() { return meta_val; }
override MalType with_meta(MalType new_meta)
{
return new MalList(this, new_meta);
}
}
class MalVector : MalSequential, MalMeta
{
this(MalType[] lst) { super(lst); }
this(MalVector that, MalType new_meta)
{
super(that.elements);
meta_val = new_meta;
}
override string print(bool readable) const
{
auto items_strs = elements.map!(e => e.print(readable));
return "[" ~ array(items_strs).join(" ") ~ "]";
}
override MalSequential conj(MalType element)
{
return new MalVector(elements ~ [element]);
}
override MalType meta() { return meta_val; }
override MalType with_meta(MalType new_meta)
{
return new MalVector(this, new_meta);
}
}
class MalHashmap : MalType, MalMeta
{
MalType[string] data;
MalType meta_val;
this(MalType[string] map)
{
data = map;
meta_val = mal_nil;
}
this(MalType[] lst)
{
put_kv_list(lst);
meta_val = mal_nil;
}
this(MalHashmap that, MalType new_meta)
{
data = that.data;
meta_val = new_meta;
}
bool contains(in MalType key)
{
auto valp = (make_hash_key(key) in data);
return valp !is null;
}
MalType get(in MalType key)
{
auto valp = (make_hash_key(key) in data);
return valp is null ? mal_nil : *valp;
}
void remove(in MalType key)
{
data.remove(make_hash_key(key));
}
void put(in MalType key, MalType val)
{
data[make_hash_key(key)] = val;
}
void put_kv_list(MalType[] lst)
{
foreach (kv; chunks(lst, 2))
{
if (kv.length < 2) throw new Exception("requires even number of elements");
put(kv[0], kv[1]);
}
}
private string make_hash_key(in MalType key)
{
return verify_cast!MalString(key).val;
}
override string print(bool readable) const
{
string[] parts;
foreach (k, v; data)
{
parts ~= (new MalString(k)).print(readable);
parts ~= v.print(readable);
}
return "{" ~ parts.join(" ") ~ "}";
}
override bool opEquals(Object o)
{
auto ohm = cast(MalHashmap)(o);
return (ohm !is null && data == ohm.data);
}
override MalType meta() { return meta_val; }
override MalType with_meta(MalType new_meta)
{
return new MalHashmap(this, new_meta);
}
}
alias BuiltinStaticFuncType = MalType function(MalType[] a ...);
alias BuiltinFuncType = MalType delegate(MalType[] a ...);
class MalBuiltinFunc : MalType, MalMeta
{
const BuiltinFuncType fn;
const string name;
MalType meta_val;
this(in BuiltinFuncType fn_v, in string name_v)
{
fn = fn_v;
name = name_v;
meta_val = mal_nil;
}
this(in BuiltinStaticFuncType static_fn_v, in string name_v)
{
fn = toDelegate(static_fn_v);
name = name_v;
meta_val = mal_nil;
}
this(MalBuiltinFunc that, MalType new_meta)
{
fn = that.fn;
name = that.name;
meta_val = new_meta;
}
override string print(bool readable) const
{
return "<BuiltinFunc:" ~ name ~ ">";
}
override MalType meta() { return meta_val; }
override MalType with_meta(MalType new_meta)
{
return new MalBuiltinFunc(this, new_meta);
}
}
class MalFunc : MalType, MalMeta
{
MalType[] arg_names;
MalType func_body;
Env def_env;
bool is_macro;
MalType meta_val;
this(MalType[] arg_names_v, MalType func_body_v, Env def_env_v)
{
arg_names = arg_names_v;
func_body = func_body_v;
def_env = def_env_v;
is_macro = false;
meta_val = mal_nil;
}
this(MalFunc that, MalType new_meta)
{
arg_names = that.arg_names;
func_body = that.func_body;
def_env = that.def_env;
is_macro = that.is_macro;
meta_val = new_meta;
}
override string print(bool readable) const
{
return "<Function:args=" ~ array(arg_names.map!(e => e.print(true))).join(",") ~ ">";
}
override MalType meta() { return meta_val; }
override MalType with_meta(MalType new_meta)
{
return new MalFunc(this, new_meta);
}
}
class MalAtom : MalType, MalMeta
{
MalType val;
MalType meta_val;
this(MalType v)
{
val = v;
meta_val = mal_nil;
}
this(MalAtom that, MalType new_meta)
{
val = that.val;
meta_val = new_meta;
}
override string print(bool readable) const
{
return "(atom " ~ val.print(readable) ~ ")";
}
override bool opEquals(Object other)
{
auto o = cast(MalAtom) other;
return (o !is null && val == o.val);
}
override MalType meta() { return meta_val; }
override MalType with_meta(MalType new_meta)
{
return new MalAtom(this, new_meta);
}
}
class MalException : Exception
{
MalType data;
this(MalType val)
{
super("MalException");
data = val;
}
}
T verify_cast(T)(in MalType v)
{
T res = cast(T) v;
if (res is null) throw new Exception("Expected " ~ typeid(T).name);
return res;
}
MalType mal_type_q(T)(in MalType[] a)
{
verify_args_count(a, 1);
T res = cast(T) a[0];
return bool_to_mal(res !is null);
}
inout(MalType[]) verify_args_count(inout MalType[] args, in int expected_length)
{
if (args.length != expected_length)
{
throw new Exception("Expected " ~ to!string(expected_length) ~ " arguments");
}
return args;
}
void verify_min_args_count(in MalType[] args, in int min_expected_length)
{
if (args.length < min_expected_length)
{
throw new Exception("Expected at least " ~ to!string(min_expected_length) ~ " arguments");
}
}