2021 lines
86 KiB
Plaintext
2021 lines
86 KiB
Plaintext
import io:*
|
|
import str:*
|
|
import vec:*
|
|
import vec_literals:*
|
|
import util:*
|
|
import map:*
|
|
|
|
import fungll:*
|
|
|
|
fun read_str(grammer: ref Grammer<MalResult,MalValue>, s: str): pair<int, MalResult> {
|
|
var BSR = fungll(grammer, grammer.start_symbol, s)
|
|
var longest = -1
|
|
for (var i = 0; i < BSR.data.size; i++;) {
|
|
if BSR.data[i].nonterminal == grammer.start_symbol && BSR.data[i].left == 0 && BSR.data[i].idx_into_rule == grammer.nonterminals[(-1*BSR.data[i].nonterminal)-1][BSR.data[i].rule_idx].size {
|
|
longest = BSR.data[i].right
|
|
}
|
|
}
|
|
if longest >= 0 {
|
|
return make_pair(longest, grammer.eval_BSR(s.slice(0, longest), BSR))
|
|
} else {
|
|
println("trying to parse: " + s)
|
|
println(str("length of BSR is: ") + BSR.size())
|
|
for (var i = 0; i < BSR.data.size; i++;) {
|
|
println(str() + i + ": " + grammer.to_string(BSR.data[i]))
|
|
}
|
|
println("Parse failed")
|
|
return make_pair(-1, MalResult::Err(MalValue::String(str("failed to parse"))))
|
|
}
|
|
}
|
|
adt MalValue {
|
|
Nil, // 0 0101 111
|
|
True, // 1 0011 111
|
|
False, // 0 0011 111
|
|
// 0 0001 111 don't yet have a character
|
|
|
|
Function: MalFunction, // 110
|
|
BuiltinFunction: MalBuiltinFunction, // '''
|
|
Symbol: str, // 101
|
|
Atom: *MalValue, // 100
|
|
String: str, // 011
|
|
Vector: vec<MalValue>, // 010
|
|
List: vec<MalValue>, // 001
|
|
Int: int // 000
|
|
|
|
}
|
|
fun equals_MalValue(a: MalValue, b: MalValue): bool {
|
|
match (a) {
|
|
MalValue::List(d) { match (b) {
|
|
MalValue::List(db) {
|
|
if d.size != db.size {
|
|
return false
|
|
}
|
|
for (var i = 0; i < d.size; i++;) {
|
|
if !equals_MalValue(d[i], db[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
MalValue::Vector(db) {
|
|
if d.size != db.size {
|
|
return false
|
|
}
|
|
for (var i = 0; i < d.size; i++;) {
|
|
if !equals_MalValue(d[i], db[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
} }
|
|
MalValue::Vector(d) { match (b) {
|
|
MalValue::List(db) {
|
|
if d.size != db.size {
|
|
return false
|
|
}
|
|
for (var i = 0; i < d.size; i++;) {
|
|
if !equals_MalValue(d[i], db[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
MalValue::Vector(db) {
|
|
if d.size != db.size {
|
|
return false
|
|
}
|
|
for (var i = 0; i < d.size; i++;) {
|
|
if !equals_MalValue(d[i], db[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
} }
|
|
MalValue::String(d) { match (b) { MalValue::String(db) { return d == db; } } }
|
|
MalValue::Int(d) { match (b) { MalValue::Int(db) { return d == db; } } }
|
|
MalValue::Symbol(d) { match (b) { MalValue::Symbol(db) { return d == db; } } }
|
|
MalValue::Function(d) { match (b) { MalValue::Function(db) { return d == db; } } }
|
|
MalValue::BuiltinFunction(d) { match (b) { MalValue::BuiltinFunction(db) { return d == db; } } }
|
|
MalValue::Atom(d) { match (b) { MalValue::Atom(db) { return d == db; } } }
|
|
MalValue::True() { match (b) { MalValue::True() { return true; } } }
|
|
MalValue::False() { match (b) { MalValue::False() { return true; } } }
|
|
MalValue::Nil() { match (b) { MalValue::Nil() { return true; } } }
|
|
}
|
|
return false
|
|
}
|
|
fun is_list(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::List(l) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun get_list(v: MalValue): vec<MalValue> {
|
|
match (v) {
|
|
MalValue::List(l) {
|
|
return l
|
|
}
|
|
}
|
|
error("Tried to get list on not a list")
|
|
}
|
|
fun is_vector(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::Vector(v) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun is_list_or_vec(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::List(l) {
|
|
return true
|
|
}
|
|
MalValue::Vector(v) {
|
|
return true
|
|
}
|
|
MalValue::Nil() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun get_list_or_vec(v: MalValue): vec<MalValue> {
|
|
match (v) {
|
|
MalValue::List(l) {
|
|
return l
|
|
}
|
|
MalValue::Vector(v) {
|
|
return v
|
|
}
|
|
MalValue::Nil() {
|
|
return vec<MalValue>()
|
|
}
|
|
}
|
|
error("Tried to get list or vec on not a list or vec")
|
|
}
|
|
fun is_symbol(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::Symbol(s) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun is_symbol(v: MalValue, text: *char): bool {
|
|
match (v) {
|
|
MalValue::Symbol(s) {
|
|
return s == text
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun get_symbol_text(v: MalValue): str {
|
|
match (v) {
|
|
MalValue::Symbol(s) {
|
|
return s
|
|
}
|
|
}
|
|
error("get_symbol_text on not symbol")
|
|
}
|
|
fun is_string(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::String(s) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun get_string(v: MalValue): str {
|
|
match (v) {
|
|
MalValue::String(s) {
|
|
return s
|
|
}
|
|
}
|
|
error("get_string on not a string")
|
|
}
|
|
fun is_int(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::Int(i) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun get_int(v: MalValue): int {
|
|
match (v) {
|
|
MalValue::Int(i) {
|
|
return i
|
|
}
|
|
}
|
|
error("get_int on not an int")
|
|
}
|
|
fun is_nil(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::Nil() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun is_atom(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::Atom(a) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun get_atom(v: MalValue): *MalValue {
|
|
match (v) {
|
|
MalValue::Atom(a) {
|
|
return a
|
|
}
|
|
}
|
|
error("Called get_atom on not an atom")
|
|
}
|
|
fun is_truthy(v: MalValue): bool {
|
|
match (v) {
|
|
MalValue::False() {
|
|
return false
|
|
}
|
|
MalValue::Nil() {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
fun bool_to_MalValue(b: bool): MalValue {
|
|
if b {
|
|
return MalValue::True()
|
|
} else {
|
|
return MalValue::False()
|
|
}
|
|
}
|
|
|
|
fun is_pair(v: MalValue): bool {
|
|
return is_list_or_vec(v) && get_list_or_vec(v).size > 0
|
|
}
|
|
|
|
fun is_macro_call(ast: MalValue, env: *Env): bool {
|
|
if !is_list(ast) {
|
|
return false
|
|
}
|
|
var l = get_list(ast)
|
|
if l.size == 0 || !is_symbol(l[0]) {
|
|
return false
|
|
}
|
|
var res = env->get(get_symbol_text(l[0]))
|
|
if is_err(res) {
|
|
return false
|
|
}
|
|
var v = get_value(res)
|
|
match (v) {
|
|
MalValue::Function(f) {
|
|
return f.is_macro
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fun macroexpand(ast: MalValue, env: *Env): MalResult {
|
|
while is_macro_call(ast, env) {
|
|
var l = get_list(ast)
|
|
var v = get_value(env->get(get_symbol_text(l[0])))
|
|
match (v) {
|
|
MalValue::Function(f) {
|
|
var params = l.slice(1,-1)
|
|
if (!f.is_variadic && f.parameters.size != params.size) || (f.is_variadic && f.parameters.size > params.size + 1) {
|
|
return MalResult::Err(MalValue::String(str("called with the wrong number of parameters")))
|
|
}
|
|
env = new<Env>()->construct(f.env)
|
|
for (var i = 0; i < f.parameters.size; i++;) {
|
|
if f.is_variadic && i == f.parameters.size - 1 {
|
|
env->set(f.parameters[i], MalValue::List(params.slice(i, -1)))
|
|
} else {
|
|
env->set(f.parameters[i], params[i])
|
|
}
|
|
}
|
|
var tmp = *f.body
|
|
var tmp2 = EVAL(env, tmp)
|
|
if is_err(tmp2) {
|
|
return tmp2
|
|
}
|
|
ast = get_value(tmp2)
|
|
}
|
|
}
|
|
}
|
|
return MalResult::Ok(ast)
|
|
}
|
|
|
|
fun quasiquote(ast: MalValue): MalValue {
|
|
if !is_pair(ast) {
|
|
return MalValue::List(vec(MalValue::Symbol(str("quote")), ast))
|
|
} else {
|
|
var ast_list = get_list_or_vec(ast)
|
|
if is_symbol(ast_list[0], "unquote") {
|
|
return ast_list[1]
|
|
} else {
|
|
if is_pair(ast_list[0]) && is_symbol(get_list_or_vec(ast_list[0])[0], "splice-unquote") {
|
|
return MalValue::List(vec(MalValue::Symbol(str("concat")), get_list_or_vec(ast_list[0])[1], quasiquote(MalValue::List(ast_list.slice(1,-1)))))
|
|
} else {
|
|
return MalValue::List(vec(MalValue::Symbol(str("cons")), quasiquote(ast_list[0]), quasiquote(MalValue::List(ast_list.slice(1,-1)))))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
obj Env (Object) {
|
|
var data: map<str, MalValue>
|
|
var outer: *Env
|
|
fun construct(): *Env {
|
|
return construct(null<Env>())
|
|
}
|
|
fun construct(outer: *Env): *Env {
|
|
data.construct()
|
|
this->outer = outer
|
|
return this
|
|
}
|
|
fun copy_construct(old: *Env): void {
|
|
data.copy_construct(&old->data)
|
|
outer = old->outer
|
|
}
|
|
fun destruct(): void {
|
|
data.destruct()
|
|
outer = null<Env>()
|
|
}
|
|
fun operator=(other:ref Env):void {
|
|
destruct()
|
|
copy_construct(&other)
|
|
}
|
|
fun set(key: str, val: MalValue) {
|
|
data.set(key, val)
|
|
}
|
|
fun remove(key: str) {
|
|
data.remove(key)
|
|
}
|
|
fun find(key: str): *Env {
|
|
if (data.contains_key(key)) {
|
|
return this
|
|
} else if (outer != null<Env>()) {
|
|
return outer->find(key)
|
|
} else {
|
|
return null<Env>()
|
|
}
|
|
}
|
|
fun get(key: str): MalResult {
|
|
var env = find(key)
|
|
if (env != null<Env>()) {
|
|
return MalResult::Ok(env->data.get(key))
|
|
} else {
|
|
return MalResult::Err(MalValue::String(str("'") + key + "' not found"))
|
|
}
|
|
}
|
|
fun to_string(): str {
|
|
var to_ret = str()
|
|
to_string(str("\t"), to_ret)
|
|
return to_ret
|
|
}
|
|
fun to_string(tabs: ref str, s: ref str) {
|
|
for (var i = 0; i < data.keys.size; i++;) {
|
|
/*s += tabs + data.keys[i] + ": " + data.values[i] + "\n"*/
|
|
s += tabs + data.keys[i] + "\n"
|
|
}
|
|
if outer != null<Env>() {
|
|
outer->to_string(tabs + "\t", s)
|
|
}
|
|
}
|
|
}
|
|
obj MalBuiltinFunction (Object) {
|
|
var fp: fun(vec<MalValue>): MalResult
|
|
fun construct(fp: fun(vec<MalValue>): MalResult): *MalBuiltinFunction {
|
|
this->fp = fp
|
|
return this
|
|
}
|
|
fun copy_construct(old: *MalBuiltinFunction): void {
|
|
this->fp = old->fp
|
|
}
|
|
fun destruct(): void {
|
|
}
|
|
fun operator=(other:ref MalBuiltinFunction):void {
|
|
destruct()
|
|
copy_construct(&other)
|
|
}
|
|
fun operator==(other: ref MalBuiltinFunction):bool {
|
|
return false
|
|
}
|
|
fun call(params: vec<MalValue>): MalResult {
|
|
return fp(params)
|
|
}
|
|
}
|
|
fun make_builtin_function(f: fun(vec<MalValue>): MalResult): MalValue {
|
|
var to_ret.construct(f): MalBuiltinFunction
|
|
return MalValue::BuiltinFunction(to_ret)
|
|
}
|
|
obj MalFunction (Object) {
|
|
var env: *Env
|
|
var parameters: vec<str>
|
|
var is_variadic: bool
|
|
var is_macro: bool
|
|
var body: *MalValue
|
|
fun construct(env: *Env, parameters: vec<str>, is_variadic: bool, is_macro: bool, body: MalValue): *MalFunction {
|
|
this->env = env
|
|
this->parameters.copy_construct(¶meters)
|
|
this->is_variadic = is_variadic
|
|
this->is_macro = is_macro
|
|
this->body = new<MalValue>()
|
|
this->body->copy_construct(&body)
|
|
return this
|
|
}
|
|
fun copy_construct(old: *MalFunction): void {
|
|
this->env = old->env
|
|
this->parameters.copy_construct(&old->parameters)
|
|
this->is_variadic = old->is_variadic
|
|
this->is_macro = old->is_macro
|
|
this->body = new<MalValue>()
|
|
this->body->copy_construct(old->body)
|
|
}
|
|
fun destruct(): void {
|
|
this->env = null<Env>()
|
|
parameters.destruct()
|
|
delete(body)
|
|
body = null<MalValue>()
|
|
}
|
|
fun operator=(other:ref MalFunction):void {
|
|
destruct()
|
|
copy_construct(&other)
|
|
}
|
|
fun operator==(other: ref MalFunction):bool {
|
|
// not sure about env
|
|
return env == other.env && parameters == other.parameters && is_variadic == other.is_variadic && is_macro == other.is_macro && equals_MalValue(*body, *other.body)
|
|
}
|
|
// no call b/c need to do in EVAL for TCO
|
|
fun prep_call(params: vec<MalValue>): pair<*Env, MalResult> {
|
|
// tco
|
|
if (!is_variadic && parameters.size != params.size) || (is_variadic && parameters.size > params.size + 1) {
|
|
return make_pair(null<Env>(), MalResult::Err(MalValue::String(str("called with the wrong number of parameters"))))
|
|
}
|
|
var new_env = new<Env>()->construct(env)
|
|
for (var i = 0; i < parameters.size; i++;) {
|
|
if is_variadic && i == parameters.size - 1 {
|
|
new_env->set(parameters[i], MalValue::List(params.slice(i, -1)))
|
|
} else {
|
|
new_env->set(parameters[i], params[i])
|
|
}
|
|
}
|
|
return make_pair(new_env, MalResult::Ok(*body))
|
|
}
|
|
}
|
|
|
|
fun function_call(f: MalValue, params: vec<MalValue>): MalResult {
|
|
match (f) {
|
|
MalValue::BuiltinFunction(f) {
|
|
return f.call(params)
|
|
}
|
|
MalValue::Function(f) {
|
|
var call_pair = f.prep_call(params)
|
|
if is_err(call_pair.second) {
|
|
return call_pair.second
|
|
}
|
|
return EVAL(call_pair.first, get_value(call_pair.second))
|
|
}
|
|
}
|
|
return MalResult::Err(MalValue::String(str("trying to apply not a function: ") + pr_str(f ,true)))
|
|
}
|
|
|
|
adt MalResult {
|
|
Ok: MalValue,
|
|
Err: MalValue
|
|
}
|
|
fun is_err(r: MalResult): bool {
|
|
match (r) {
|
|
MalResult::Err(e) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
fun get_err(r: MalResult): MalValue {
|
|
match (r) {
|
|
MalResult::Err(e) {
|
|
return e
|
|
}
|
|
}
|
|
return MalValue::String(str("not error"))
|
|
}
|
|
fun get_value(r: MalResult): MalValue {
|
|
match (r) {
|
|
MalResult::Ok(v) {
|
|
return v
|
|
}
|
|
}
|
|
return MalValue::Symbol(str("error"))
|
|
}
|
|
fun pr_str(v: MalValue, print_readably: bool): str {
|
|
match (v) {
|
|
MalValue::List(l) {
|
|
var to_ret = str("(")
|
|
for (var i = 0; i < l.size; i++;) {
|
|
if (i != 0) {
|
|
to_ret += " "
|
|
}
|
|
to_ret += pr_str(l[i], print_readably)
|
|
}
|
|
return to_ret + ")"
|
|
}
|
|
MalValue::Vector(l) {
|
|
var to_ret = str("[")
|
|
for (var i = 0; i < l.size; i++;) {
|
|
if (i != 0) {
|
|
to_ret += " "
|
|
}
|
|
to_ret += pr_str(l[i], print_readably)
|
|
}
|
|
return to_ret + "]"
|
|
}
|
|
MalValue::Int(i) {
|
|
return to_string(i)
|
|
}
|
|
MalValue::String(s) {
|
|
if print_readably {
|
|
var to_ret = str("\"") //"
|
|
for (var i = 0; i < s.length(); i++;) {
|
|
if s[i] == '\n' {
|
|
to_ret += '\\'
|
|
to_ret += 'n'
|
|
} else if s[i] == '\\' || s[i] == '"' {
|
|
to_ret += '\\'
|
|
to_ret += s[i]
|
|
} else {
|
|
to_ret += s[i]
|
|
}
|
|
}
|
|
return to_ret + "\"" //"
|
|
} else {
|
|
return s
|
|
}
|
|
}
|
|
MalValue::Symbol(s) {
|
|
return s
|
|
}
|
|
MalValue::BuiltinFunction(f) {
|
|
return str("builtin function")
|
|
}
|
|
MalValue::Function(f) {
|
|
return str("function")
|
|
}
|
|
MalValue::Atom(a) {
|
|
return str("(atom ") + pr_str(*a, print_readably) + ")"
|
|
}
|
|
MalValue::True() {
|
|
return str("true")
|
|
}
|
|
MalValue::False() {
|
|
return str("false")
|
|
}
|
|
MalValue::Nil() {
|
|
return str("nil")
|
|
}
|
|
}
|
|
error("can't print")
|
|
}
|
|
|
|
fun READ(grammer: ref Grammer<MalResult, MalValue>, s: str): MalResult {
|
|
var to_ret = read_str(grammer, s)
|
|
if to_ret.first != s.length() {
|
|
if is_err(to_ret.second) {
|
|
return to_ret.second
|
|
} else {
|
|
println("parsed to var: " + pr_str(get_value(to_ret.second), true))
|
|
}
|
|
error("parsed some, but not all")
|
|
}
|
|
return to_ret.second
|
|
}
|
|
|
|
fun eval_ast(env: *Env, ast: MalValue): MalResult {
|
|
match (ast) {
|
|
MalValue::List(l) {
|
|
var to_ret = vec<MalValue>()
|
|
for (var i = 0; i < l.size; i++;) {
|
|
var mid = EVAL(env, l[i])
|
|
if is_err(mid) {
|
|
return mid
|
|
}
|
|
to_ret.add(get_value(mid))
|
|
}
|
|
return MalResult::Ok(MalValue::List(to_ret))
|
|
}
|
|
MalValue::Vector(l) {
|
|
var to_ret = vec<MalValue>()
|
|
for (var i = 0; i < l.size; i++;) {
|
|
var mid = EVAL(env, l[i])
|
|
if is_err(mid) {
|
|
return mid
|
|
}
|
|
to_ret.add(get_value(mid))
|
|
}
|
|
return MalResult::Ok(MalValue::Vector(to_ret))
|
|
}
|
|
MalValue::Symbol(s) {
|
|
return env->get(s)
|
|
}
|
|
}
|
|
return MalResult::Ok(ast)
|
|
}
|
|
|
|
fun EVAL(env: *Env, ast: MalValue): MalResult {
|
|
// for tco
|
|
while (true) {
|
|
var expanded = macroexpand(ast, env)
|
|
if (is_err(expanded)) {
|
|
return expanded
|
|
}
|
|
ast = get_value(expanded)
|
|
if !is_list(ast) {
|
|
return eval_ast(env, ast)
|
|
}
|
|
match (ast) {
|
|
MalValue::List(l) {
|
|
if (l.size == 0) {
|
|
return MalResult::Ok(ast)
|
|
} else if (is_symbol(l[0], "def!")) {
|
|
if (l.size != 3) {
|
|
return MalResult::Err(MalValue::String(str("def! without exaclty key and value")))
|
|
}
|
|
if (!is_symbol(l[1])) {
|
|
return MalResult::Err(MalValue::String(str("def! not on symbol")))
|
|
}
|
|
if env->outer != null<Env>() {
|
|
return MalResult::Err(MalValue::String(str("def! not at top level")))
|
|
}
|
|
var value = EVAL(env, l[2])
|
|
if (is_err(value)) {
|
|
return value
|
|
}
|
|
env->set(get_symbol_text(l[1]), get_value(value))
|
|
return value
|
|
} else if (is_symbol(l[0], "defmacro!")) {
|
|
if (l.size != 3) {
|
|
return MalResult::Err(MalValue::String(str("defmacro! without exaclty key and value")))
|
|
}
|
|
if (!is_symbol(l[1])) {
|
|
return MalResult::Err(MalValue::String(str("defmacro! not on symbol")))
|
|
}
|
|
var value = EVAL(env, l[2])
|
|
if (is_err(value)) {
|
|
return value
|
|
}
|
|
var v = get_value(value)
|
|
match (v) {
|
|
MalValue::Function(f) {
|
|
f.is_macro = true
|
|
env->set(get_symbol_text(l[1]), MalValue::Function(f))
|
|
return value
|
|
}
|
|
}
|
|
return MalResult::Err(MalValue::String(str("defmacro! on not a function")))
|
|
} else if (is_symbol(l[0], "let*")) {
|
|
if (l.size != 3) {
|
|
return MalResult::Err(MalValue::String(str("let* without list of bindings & end value")))
|
|
}
|
|
if (!is_list_or_vec(l[1])) {
|
|
return MalResult::Err(MalValue::String(str("let* without list of bindings")))
|
|
}
|
|
var bindings = get_list_or_vec(l[1])
|
|
if (bindings.size & 1 != 0) {
|
|
return MalResult::Err(MalValue::String(str("let* list of bindings has odd number of entries")))
|
|
}
|
|
var new_env = new<Env>()->construct(env)
|
|
for (var i = 0; i < bindings.size; i+=2;) {
|
|
if (!is_symbol(bindings[i])) {
|
|
return MalResult::Err(MalValue::String(str("let* var name not symbol")))
|
|
}
|
|
var to_set_value = EVAL(new_env, bindings[i+1])
|
|
if (is_err(to_set_value)) {
|
|
return to_set_value
|
|
}
|
|
new_env->set(get_symbol_text(bindings[i]), get_value(to_set_value))
|
|
}
|
|
// tco
|
|
env = new_env
|
|
var tmp = l[2]
|
|
ast = tmp
|
|
continue
|
|
} else if (is_symbol(l[0], "do")) {
|
|
for (var i = 1; i < l.size-1; i++;) {
|
|
var mid = EVAL(env, l[i])
|
|
if is_err(mid) {
|
|
return mid
|
|
}
|
|
}
|
|
// tco
|
|
var tmp = l[l.size-1]
|
|
ast = tmp
|
|
continue
|
|
} else if (is_symbol(l[0], "if")) {
|
|
if l.size != 3 && l.size != 4 {
|
|
return MalResult::Err(MalValue::String(str("if needs 2 or 3 children")))
|
|
}
|
|
var cond = EVAL(env, l[1])
|
|
if is_err(cond) {
|
|
return cond
|
|
}
|
|
// tco
|
|
if is_truthy(get_value(cond)) {
|
|
var tmp = l[2]
|
|
ast = tmp
|
|
} else if l.size == 4 {
|
|
var tmp = l[3]
|
|
ast = tmp
|
|
} else {
|
|
return MalResult::Ok(MalValue::Nil())
|
|
}
|
|
continue
|
|
} else if (is_symbol(l[0], "fn*")) {
|
|
if l.size != 3 {
|
|
return MalResult::Err(MalValue::String(str("fn* needs 2 children")))
|
|
}
|
|
if (!is_list_or_vec(l[1])) {
|
|
return MalResult::Err(MalValue::String(str("fn* without list of parameters")))
|
|
}
|
|
var parameters = get_list_or_vec(l[1])
|
|
var parameters_str = vec<str>()
|
|
var is_variadic = false
|
|
for (var i = 0; i < parameters.size; i++;) {
|
|
if (!is_symbol(parameters[i])) {
|
|
return MalResult::Err(MalValue::String(str("fn* parameter name not symbol")))
|
|
}
|
|
var symbol_text = get_symbol_text(parameters[i])
|
|
if symbol_text == "&" {
|
|
if i != parameters.size - 2 {
|
|
return MalResult::Err(MalValue::String(str("fn* has wrong number of arguments after &")))
|
|
}
|
|
if (!is_symbol(parameters[i+1])) {
|
|
return MalResult::Err(MalValue::String(str("fn* parameter name not symbol")))
|
|
}
|
|
is_variadic = true
|
|
i++
|
|
symbol_text = get_symbol_text(parameters[i])
|
|
}
|
|
parameters_str.add(symbol_text)
|
|
}
|
|
var to_ret.construct(env, parameters_str, is_variadic, false, l[2]): MalFunction
|
|
return MalResult::Ok(MalValue::Function(to_ret))
|
|
} else if (is_symbol(l[0], "quote")) {
|
|
if l.size == 1 {
|
|
return MalResult::Err(MalValue::String(str("quote with no arguments")))
|
|
}
|
|
return MalResult::Ok(l[1])
|
|
} else if (is_symbol(l[0], "quasiquote")) {
|
|
if l.size == 1 {
|
|
return MalResult::Err(MalValue::String(str("quasiquote with no arguments")))
|
|
}
|
|
var tmp = quasiquote(l[1])
|
|
ast = tmp
|
|
continue
|
|
} else if (is_symbol(l[0], "macroexpand")) {
|
|
if l.size == 1 {
|
|
return MalResult::Err(MalValue::String(str("macroexpand with no arguments")))
|
|
}
|
|
return macroexpand(l[1], env)
|
|
} else if (is_symbol(l[0], "try*")) {
|
|
if l.size != 2 && (l.size != 3 || !is_list(l[2])) {
|
|
return MalResult::Err(MalValue::String(str("try* wrong arguments")))
|
|
}
|
|
var A = EVAL(env, l[1])
|
|
if l.size == 3 && is_err(A) {
|
|
var catch = get_list(l[2])
|
|
if catch.size != 3 || !is_symbol(catch[0], "catch*") || !is_symbol(catch[1]) {
|
|
return MalResult::Err(MalValue::String(str("catch* block malformed")))
|
|
}
|
|
var new_env = new<Env>()->construct(env)
|
|
env->set(get_symbol_text(catch[1]), get_err(A))
|
|
return EVAL(new_env, catch[2])
|
|
} else {
|
|
return A
|
|
}
|
|
} else {
|
|
var mid = eval_ast(env, ast)
|
|
if is_err(mid) {
|
|
return mid
|
|
}
|
|
var to_call = get_list(get_value(mid))
|
|
match (to_call[0]) {
|
|
MalValue::BuiltinFunction(f) {
|
|
return f.call(to_call.slice(1,-1))
|
|
}
|
|
MalValue::Function(f) {
|
|
var params = to_call.slice(1,-1)
|
|
var call_pair = f.prep_call(to_call.slice(1, -1))
|
|
if is_err(call_pair.second) {
|
|
return call_pair.second
|
|
}
|
|
env = call_pair.first
|
|
ast = get_value(call_pair.second)
|
|
continue
|
|
}
|
|
}
|
|
return MalResult::Err(MalValue::String(str("trying to call not a function:") + pr_str(to_call[0], true)))
|
|
}
|
|
}
|
|
}
|
|
return eval_ast(env, ast)
|
|
}
|
|
}
|
|
|
|
fun rep(grammer: ref Grammer<MalResult, MalValue>, env: *Env, a: str): str {
|
|
var read = READ(grammer, a)
|
|
if is_err(read) {
|
|
return pr_str(get_err(read), true)
|
|
} else {
|
|
var evaled = EVAL(env, get_value(read))
|
|
if is_err(evaled) {
|
|
return str("Exception: ") + pr_str(get_err(evaled), true)
|
|
} else {
|
|
return pr_str(get_value(evaled), true)
|
|
}
|
|
}
|
|
}
|
|
fun print_wrapper(params: vec<MalValue>, sep: *char, print_readably: bool): str {
|
|
var to_ret = str()
|
|
for (var i = 0; i < params.size; i++;) {
|
|
to_ret += pr_str(params[i], print_readably)
|
|
if i != params.size-1 {
|
|
to_ret += sep
|
|
}
|
|
}
|
|
return to_ret
|
|
}
|
|
|
|
fun main(argc: int, argv: **char): int {
|
|
|
|
var grammer.construct(): Grammer<MalResult, MalValue>
|
|
|
|
var ret_nil_term: fun(ref MalValue, ref str, int, int): MalResult = fun(_: ref MalValue, input: ref str, l: int, r: int): MalResult { return MalResult::Ok(MalValue::Nil()); }
|
|
var ret_nil_sym: fun(ref MalValue, ref vec<MalResult>): MalResult = fun(_: ref MalValue, x: ref vec<MalResult>): MalResult { return MalResult::Ok(MalValue::Nil()); }
|
|
var ret_0_sym: fun(ref MalValue, ref vec<MalResult>): MalResult = fun(_: ref MalValue, x: ref vec<MalResult>): MalResult { return x[0]; }
|
|
|
|
var WS = grammer.add_new_nonterminal("WS", vec(grammer.add_terminal("( | |
|
|
|(;[ -~]*
|
|
))+", MalValue::Nil(), ret_nil_term)), MalValue::Nil(), ret_nil_sym)
|
|
var optional_WS = grammer.add_new_nonterminal("optional_WS", vec<int>(), MalValue::Nil(), ret_nil_sym)
|
|
grammer.add_to_nonterminal(optional_WS, vec(WS), MalValue::Nil(), ret_nil_sym)
|
|
|
|
var atom = grammer.add_new_nonterminal("atom", vec(grammer.add_terminal("-?[0-9]+", MalValue::Nil(), fun(_: ref MalValue, input: ref str, l: int, r: int): MalResult { return MalResult::Ok(MalValue::Int(string_to_num<int>(input.slice(l,r)))); })), MalValue::Nil(), ret_0_sym)
|
|
grammer.add_to_nonterminal(atom, vec(grammer.add_terminal("\"([#-[]| |[]-~]|(\\\\)|(\\n)|(\\t)|(\\\*)|(\\0)|
|
|
|[ -!]|(\\\\\"))*\"", MalValue::Nil(), fun(_: ref MalValue, input: ref str, l: int, r: int): MalResult { //"
|
|
var to_ret = str()
|
|
for (var i = l+1; i < r-1; i++;) {
|
|
if input[i] == '\\' {
|
|
if input[i+1] == 'n' {
|
|
to_ret += '\n'
|
|
} else if input[i+1] == '\\' || input[i+1] == '"' {
|
|
to_ret += input[i+1]
|
|
} else {
|
|
return MalResult::Err(MalValue::String(str("bad string escape")))
|
|
}
|
|
// skip
|
|
i++
|
|
} else {
|
|
to_ret += input[i]
|
|
}
|
|
}
|
|
return MalResult::Ok(MalValue::String(to_ret));
|
|
})), MalValue::Nil(), ret_0_sym)
|
|
grammer.add_to_nonterminal(atom, vec(grammer.add_terminal("-|(([a-z]|[A-Z]|_|\\*|/|\\?|\\+|!|=|&|<|>)([a-z]|[A-Z]|_|[0-9]|\\*|\\?|\\+|-|!|=|&|<|>)*)", MalValue::Nil(), fun(_: ref MalValue, input: ref str, l: int, r: int): MalResult {
|
|
var s = input.slice(l,r)
|
|
if s == "true" {
|
|
return MalResult::Ok(MalValue::True());
|
|
} else if s == "false" {
|
|
return MalResult::Ok(MalValue::False());
|
|
} else if s == "nil" {
|
|
return MalResult::Ok(MalValue::Nil());
|
|
} else {
|
|
return MalResult::Ok(MalValue::Symbol(input.slice(l,r)));
|
|
}
|
|
})), MalValue::Nil(), ret_0_sym)
|
|
|
|
var form = grammer.add_new_nonterminal("form", vec(atom), MalValue::Nil(), ret_0_sym)
|
|
var space_forms = grammer.add_new_nonterminal("space_forms", vec<int>(), MalValue::Nil(), fun(_: ref MalValue, x: ref vec<MalResult>): MalResult {
|
|
return MalResult::Ok(MalValue::List(vec<MalValue>()))
|
|
})
|
|
grammer.add_to_nonterminal(space_forms, vec(form), MalValue::Nil(), fun(_: ref MalValue, x: ref vec<MalResult>): MalResult {
|
|
if is_err(x[0]) {
|
|
return x[0]
|
|
}
|
|
return MalResult::Ok(MalValue::List(vec(get_value(x[0]))))
|
|
})
|
|
grammer.add_to_nonterminal(space_forms, vec(form, WS, space_forms), MalValue::Nil(), fun(_: ref MalValue, x: ref vec<MalResult>): MalResult {
|
|
if is_err(x[0]) {
|
|
return x[0]
|
|
}
|
|
if is_err(x[1]) {
|
|
return x[1]
|
|
}
|
|
return MalResult::Ok(MalValue::List(vec(get_value(x[0])) + get_list(get_value(x[2]))))
|
|
})
|
|
|
|
grammer.add_to_nonterminal(form, vec(grammer.add_terminal("\\(", MalValue::Nil(), ret_nil_term),
|
|
optional_WS,
|
|
grammer.add_terminal("\\)", MalValue::Nil(), ret_nil_term)), MalValue::Nil(), fun(_: ref MalValue, x: ref vec<MalResult>): MalResult { return MalResult::Ok(MalValue::Vector(vec<MalValue>())); })
|
|
grammer.add_to_nonterminal(form, vec(grammer.add_terminal("\\(", MalValue::Nil(), ret_nil_term),
|
|
optional_WS,
|
|
space_forms,
|
|
optional_WS,
|
|
grammer.add_terminal("\\)", MalValue::Nil(), ret_nil_term)), MalValue::Nil(), fun(_: ref MalValue, x: ref vec<MalResult>): MalResult { return x[2]; })
|
|
grammer.add_to_nonterminal(form, vec(grammer.add_terminal("\\[", MalValue::Nil(), ret_nil_term),
|
|
optional_WS,
|
|
space_forms,
|
|
optional_WS,
|
|
grammer.add_terminal("]", MalValue::Nil(), ret_nil_term)), MalValue::Nil(), fun(_: ref MalValue, x: ref vec<MalResult>): MalResult {
|
|
if is_err(x[2]) {
|
|
return x[2]
|
|
}
|
|
return MalResult::Ok(MalValue::Vector(get_list(get_value(x[2]))))
|
|
})
|
|
|
|
|
|
grammer.set_start_symbol(form)
|
|
|
|
|
|
var env = new<Env>()->construct()
|
|
env->set(str("+"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
var to_ret = 0
|
|
for (var i = 0; i < params.size; i++;) {
|
|
match (params[i]) {
|
|
MalValue::Int(v) {
|
|
to_ret += v
|
|
continue
|
|
}
|
|
}
|
|
return MalResult::Err(MalValue::String(str("called + with not an int: ") + pr_str(params[i], false)))
|
|
}
|
|
return MalResult::Ok(MalValue::Int(to_ret))
|
|
}));
|
|
env->set(str("-"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
var to_ret = 0
|
|
for (var i = 0; i < params.size; i++;) {
|
|
match (params[i]) {
|
|
MalValue::Int(v) {
|
|
if (i == 0) {
|
|
to_ret += v
|
|
} else {
|
|
to_ret -= v
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
return MalResult::Err(MalValue::String(str("called - with not an int: ") + pr_str(params[i], false)))
|
|
}
|
|
return MalResult::Ok(MalValue::Int(to_ret))
|
|
}));
|
|
env->set(str("*"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
var to_ret = 1
|
|
for (var i = 0; i < params.size; i++;) {
|
|
match (params[i]) {
|
|
MalValue::Int(v) {
|
|
to_ret *= v
|
|
continue
|
|
}
|
|
}
|
|
return MalResult::Err(MalValue::String(str("called * with not an int: ") + pr_str(params[i], false)))
|
|
}
|
|
return MalResult::Ok(MalValue::Int(to_ret))
|
|
}));
|
|
env->set(str("/"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
var to_ret = 1
|
|
for (var i = 0; i < params.size; i++;) {
|
|
match (params[i]) {
|
|
MalValue::Int(v) {
|
|
if (i == 0) {
|
|
to_ret *= v
|
|
} else {
|
|
to_ret /= v
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
return MalResult::Err(MalValue::String(str("called / with not an int: ") + pr_str(params[i], false)))
|
|
}
|
|
return MalResult::Ok(MalValue::Int(to_ret))
|
|
}));
|
|
env->set(str("="), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 {
|
|
return MalResult::Err(MalValue::String(str("= with not two parameters")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(equals_MalValue(params[0], params[1])))
|
|
}
|
|
}));
|
|
env->set(str("<"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_int(params[0]) || !is_int(params[1]) {
|
|
return MalResult::Err(MalValue::String(str("< with not two numbers")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(get_int(params[0]) < get_int(params[1])))
|
|
}
|
|
}));
|
|
env->set(str("<="), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_int(params[0]) || !is_int(params[1]) {
|
|
return MalResult::Err(MalValue::String(str("<= with not two numbers")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(get_int(params[0]) <= get_int(params[1])))
|
|
}
|
|
}));
|
|
env->set(str(">"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_int(params[0]) || !is_int(params[1]) {
|
|
return MalResult::Err(MalValue::String(str("> with not two numbers")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(get_int(params[0]) > get_int(params[1])))
|
|
}
|
|
}));
|
|
env->set(str(">="), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_int(params[0]) || !is_int(params[1]) {
|
|
return MalResult::Err(MalValue::String(str(">= with not two numbers")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(get_int(params[0]) >= get_int(params[1])))
|
|
}
|
|
}));
|
|
env->set(str("str"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
return MalResult::Ok(MalValue::String(print_wrapper(params, "", false)))
|
|
}));
|
|
env->set(str("prn"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size == 0 {
|
|
return MalResult::Err(MalValue::String(str("Called prn with 0 parameters")))
|
|
}
|
|
println(pr_str(params[0], true))
|
|
return MalResult::Ok(MalValue::Nil())
|
|
}));
|
|
env->set(str("pr-str"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
return MalResult::Ok(MalValue::String(print_wrapper(params, " ", true)))
|
|
}));
|
|
env->set(str("prn"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
println(print_wrapper(params, " ", true))
|
|
return MalResult::Ok(MalValue::Nil())
|
|
}));
|
|
env->set(str("println"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
println(print_wrapper(params, " ", false))
|
|
return MalResult::Ok(MalValue::Nil())
|
|
}));
|
|
env->set(str("list"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
return MalResult::Ok(MalValue::List(params))
|
|
}));
|
|
env->set(str("list?"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size > 0 && (is_list(params[0]) || is_nil(params[0])) {
|
|
return MalResult::Ok(MalValue::True())
|
|
} else {
|
|
return MalResult::Ok(MalValue::False())
|
|
}
|
|
}));
|
|
env->set(str("empty?"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size == 0 || !is_list_or_vec(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("first parameter of empty? is not a list")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(get_list_or_vec(params[0]).size == 0))
|
|
}
|
|
}));
|
|
env->set(str("count"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size == 0 || !is_list_or_vec(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("first parameter of count is not a list")))
|
|
} else {
|
|
return MalResult::Ok(MalValue::Int(get_list_or_vec(params[0]).size))
|
|
}
|
|
}));
|
|
env->set(str("read-string"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 || !is_string(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("read-string with not a single string")))
|
|
} else {
|
|
return READ(grammer, get_string(params[0]))
|
|
}
|
|
}));
|
|
env->set(str("slurp"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 || !is_string(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("slurp with not a single string")))
|
|
} else {
|
|
return MalResult::Ok(MalValue::String(read_file(get_string(params[0]))))
|
|
}
|
|
}));
|
|
env->set(str("eval"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 {
|
|
return MalResult::Err(MalValue::String(str("eval with wrong number of params")))
|
|
} else {
|
|
return EVAL(env, params[0])
|
|
}
|
|
}));
|
|
env->set(str("atom"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 {
|
|
return MalResult::Err(MalValue::String(str("atom with wrong number of params")))
|
|
} else {
|
|
var atom = new<MalValue>()
|
|
atom->copy_construct(¶ms[0])
|
|
return MalResult::Ok(MalValue::Atom(atom))
|
|
}
|
|
}));
|
|
env->set(str("atom?"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 {
|
|
return MalResult::Err(MalValue::String(str("atom? with wrong number of params")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(is_atom(params[0])))
|
|
}
|
|
}));
|
|
env->set(str("deref"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 || !is_atom(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("deref called with wrong number of params or not an atom")))
|
|
} else {
|
|
return MalResult::Ok(*get_atom(params[0]))
|
|
}
|
|
}));
|
|
env->set(str("reset!"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_atom(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("reset! called with wrong number of params or first not an atom")))
|
|
} else {
|
|
var tmp = params[1]
|
|
*get_atom(params[0]) = tmp
|
|
return MalResult::Ok(params[1])
|
|
}
|
|
}));
|
|
env->set(str("swap!"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size < 2 || !is_atom(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("swap! called with wrong number of params or first not an atom")))
|
|
} else {
|
|
var call = vec(params[1], *get_atom(params[0])) + params.slice(2,-1)
|
|
var res = function_call(params[1], vec(*get_atom(params[0])) + params.slice(2, -1))
|
|
if !is_err(res) {
|
|
*get_atom(params[0]) = get_value(res)
|
|
}
|
|
return res
|
|
}
|
|
}));
|
|
env->set(str("cons"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_list_or_vec(params[1]) {
|
|
return MalResult::Err(MalValue::String(str("cons called with wrong number of params or second not an list/vec")))
|
|
} else {
|
|
return MalResult::Ok(MalValue::List(vec(params[0]) + get_list_or_vec(params[1])))
|
|
}
|
|
}));
|
|
env->set(str("concat"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
var to_ret = vec<MalValue>()
|
|
for (var i = 0; i < params.size; i++;) {
|
|
if !is_list_or_vec(params[i]) {
|
|
return MalResult::Err(MalValue::String(str("concat called with not an list")))
|
|
}
|
|
to_ret += get_list_or_vec(params[i])
|
|
}
|
|
return MalResult::Ok(MalValue::List(to_ret))
|
|
}));
|
|
env->set(str("nth"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_list_or_vec(params[0]) || !is_int(params[1]) {
|
|
return MalResult::Err(MalValue::String(str("nth called with wrong number or type of params")))
|
|
} else {
|
|
var list = get_list_or_vec(params[0])
|
|
var idx = get_int(params[1])
|
|
if idx >= list.size {
|
|
return MalResult::Err(MalValue::String(str("nth idx out of range")))
|
|
}
|
|
return MalResult::Ok(list[idx])
|
|
}
|
|
}));
|
|
env->set(str("first"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 || !is_list_or_vec(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("first called with wrong number or type of params") + pr_str(params[0], true)))
|
|
} else {
|
|
var list = get_list_or_vec(params[0])
|
|
if list.size == 0 {
|
|
return MalResult::Ok(MalValue::Nil())
|
|
}
|
|
return MalResult::Ok(list[0])
|
|
}
|
|
}));
|
|
env->set(str("rest"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 || !is_list_or_vec(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("rest called with wrong number or type of params")))
|
|
} else {
|
|
var list = get_list_or_vec(params[0])
|
|
if list.size == 0 {
|
|
return MalResult::Ok(MalValue::List(vec<MalValue>()))
|
|
}
|
|
return MalResult::Ok(MalValue::List(list.slice(1,-1)))
|
|
}
|
|
}));
|
|
env->set(str("throw"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 {
|
|
return MalResult::Err(MalValue::String(str("throw called with wrong number or type of params")))
|
|
} else {
|
|
return MalResult::Err(params[0])
|
|
}
|
|
}));
|
|
env->set(str("apply"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size < 2 || !is_list_or_vec(params[params.size-1]) {
|
|
return MalResult::Err(MalValue::String(str("apply called with wrong number or type of params")))
|
|
} else {
|
|
var inner_params = params.slice(1,-2) + get_list_or_vec(params[params.size-1])
|
|
return function_call(params[0], inner_params)
|
|
}
|
|
}));
|
|
env->set(str("map"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_list_or_vec(params[1]) {
|
|
return MalResult::Err(MalValue::String(str("map called with wrong number or type of params")))
|
|
} else {
|
|
var l = get_list_or_vec(params[1])
|
|
var to_ret = vec<MalValue>()
|
|
for (var i = 0; i < l.size; i++;) {
|
|
var mid = function_call(params[0], vec(l[i]))
|
|
if is_err(mid) {
|
|
return mid
|
|
}
|
|
to_ret.add(get_value(mid))
|
|
}
|
|
return MalResult::Ok(MalValue::List(to_ret))
|
|
}
|
|
}));
|
|
env->set(str("symbol?"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 {
|
|
return MalResult::Err(MalValue::String(str("symbol? called with wrong number of params")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(is_symbol(params[0])))
|
|
}
|
|
}));
|
|
env->set(str("symbol"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 || !is_string(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("symbol called with wrong number or type of params")))
|
|
} else {
|
|
return MalResult::Ok(MalValue::Symbol(get_string(params[0])))
|
|
}
|
|
}));
|
|
env->set(str("vector"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
return MalResult::Ok(MalValue::Vector(params))
|
|
}));
|
|
env->set(str("vector?"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 {
|
|
return MalResult::Err(MalValue::String(str("vector? called with wrong number of params")))
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(is_vector(params[0])))
|
|
}
|
|
}));
|
|
env->set(str("sequential?"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 {
|
|
return MalResult::Err(MalValue::String(str("sequential? called with wrong number of params")))
|
|
} else if is_nil(params[0]) {
|
|
return MalResult::Ok(MalValue::False())
|
|
} else {
|
|
return MalResult::Ok(bool_to_MalValue(is_list_or_vec(params[0])))
|
|
}
|
|
}));
|
|
env->set(str("readline"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 || !is_string(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("readline called with wrong number or type of params")))
|
|
} else {
|
|
var entered = get_line(get_string(params[0]), 1024)
|
|
if entered == "***EOF***" {
|
|
return MalResult::Ok(MalValue::Nil())
|
|
}
|
|
return MalResult::Ok(MalValue::String(entered))
|
|
}
|
|
}));
|
|
env->set(str("meta"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
return MalResult::Err(MalValue::String(str("not implemented")))
|
|
}));
|
|
env->set(str("with-meta"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
return MalResult::Err(MalValue::String(str("not implemented")))
|
|
}));
|
|
env->set(str("fn?"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
return MalResult::Err(MalValue::String(str("not implemented")))
|
|
}));
|
|
env->set(str("string?"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
return MalResult::Err(MalValue::String(str("not implemented")))
|
|
}));
|
|
// self-modifying grammer
|
|
env->set(str("add_terminal"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 2 || !is_string(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("add_terminal called with wrong number or type of params")))
|
|
} else {
|
|
return MalResult::Ok(MalValue::Int(grammer.add_terminal(get_string(params[0]), params[1], fun(f: ref MalValue, input: ref str, l: int, r: int): MalResult {
|
|
return function_call(f, vec(MalValue::String(input.slice(l,r))))
|
|
})))
|
|
}
|
|
}));
|
|
env->set(str("add_grammer_rule"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 3 || !is_symbol(params[0]) || !is_vector(params[1]) {
|
|
return MalResult::Err(MalValue::String(str("add_grammer_rule called with wrong number or type of params")))
|
|
} else {
|
|
var nonterminal_str = get_symbol_text(params[0])
|
|
var rule = get_list_or_vec(params[1])
|
|
var int_rule = vec<int>()
|
|
for (var i = 0; i < rule.size; i++;) {
|
|
if is_int(rule[i]) {
|
|
int_rule.add(get_int(rule[i]))
|
|
} else if is_symbol(rule[i]) {
|
|
var sub_nonterminal_idx = grammer.nonterminal_names.find(get_symbol_text(rule[i]))
|
|
if sub_nonterminal_idx == -1 {
|
|
return MalResult::Err(MalValue::String(str("Couldn't find nonterminal: ") + get_symbol_text(rule[i])))
|
|
}
|
|
var sub_nonterminal = -1*(sub_nonterminal_idx+1)
|
|
int_rule.add(sub_nonterminal)
|
|
} else if is_string(rule[i]) {
|
|
int_rule.add(grammer.add_terminal(get_string(rule[i]), MalValue::Nil(), fun(f: ref MalValue, input: ref str, l: int, r: int): MalResult {
|
|
return MalResult::Ok(MalValue::String(input.slice(l,r)))
|
|
}))
|
|
} else {
|
|
return MalResult::Err(MalValue::String(str("add_grammer_rule called with not symbol, int, or string in rule")))
|
|
}
|
|
}
|
|
grammer.add_to_or_create_nonterminal(nonterminal_str, int_rule, params[2], fun(f: ref MalValue, x: ref vec<MalResult>): MalResult {
|
|
var params = vec<MalValue>()
|
|
for (var i = 0; i < x.size; i++;) {
|
|
if is_err(x[i]) {
|
|
return x[i]
|
|
}
|
|
params.add(get_value(x[i]))
|
|
}
|
|
return function_call(f, vec(MalValue::List(params)))
|
|
})
|
|
}
|
|
}));
|
|
env->set(str("eval-read-string"), make_builtin_function(fun(params: vec<MalValue>): MalResult {
|
|
if params.size != 1 || !is_string(params[0]) {
|
|
return MalResult::Err(MalValue::String(str("eval-read-string called with wrong number or type of params")))
|
|
} else {
|
|
var input = get_string(params[0])
|
|
var i = 0
|
|
var current_ret = MalResult::Ok(MalValue::Nil())
|
|
/*println(str("eval-read-string length is ") + input.length())*/
|
|
if i < input.length() {
|
|
// initial handle whitespace
|
|
var BSR = fungll(grammer, optional_WS, input.slice(i, -1))
|
|
var longest = -1
|
|
for (var j = 0; j < BSR.data.size; j++;) {
|
|
if BSR.data[j].nonterminal == optional_WS && BSR.data[j].left == 0 {
|
|
if BSR.data[j].right > longest {
|
|
longest = BSR.data[j].right
|
|
}
|
|
}
|
|
}
|
|
if longest > 0 {
|
|
i += longest
|
|
}
|
|
/*println(str("after skip whitespace i is ") + i + "input is " + input.slice(i, -1))*/
|
|
}
|
|
while i < input.length() {
|
|
var r = read_str(grammer, input.slice(i, -1))
|
|
i += r.first
|
|
/*println(str("after read_str i is ") + i + " input is " + input.slice(i, -1))*/
|
|
if is_err(r.second) {
|
|
return r.second
|
|
}
|
|
/*println("here '" + pr_str(get_value(r.second), true) + "'")*/
|
|
current_ret = EVAL(env, get_value(r.second))
|
|
if is_err(current_ret) {
|
|
return current_ret
|
|
}
|
|
// handle whitespace again
|
|
var BSR = fungll(grammer, optional_WS, input.slice(i, -1))
|
|
var longest = -1
|
|
for (var j = 0; j < BSR.data.size; j++;) {
|
|
if BSR.data[j].nonterminal == optional_WS && BSR.data[j].left == 0 {
|
|
if BSR.data[j].right > longest {
|
|
longest = BSR.data[j].right
|
|
}
|
|
}
|
|
}
|
|
if longest > 0 {
|
|
i += longest
|
|
}
|
|
/*println(str("after skip whitespace i is ") + i + "input is " + input.slice(i, -1))*/
|
|
}
|
|
return current_ret
|
|
}
|
|
}));
|
|
// reader macros
|
|
rep(grammer, env, str("(add_grammer_rule (quote atom) [\"'\" (quote form)] (fn* (xs) (quasiquote (quote (unquote (nth xs 1))))))")) //'
|
|
// now we can use ' for the rest
|
|
rep(grammer, env, str("(add_grammer_rule 'atom [\"`\" 'form] (fn* (xs) (quasiquote (quasiquote (unquote (nth xs 1))))))"))
|
|
rep(grammer, env, str("(add_grammer_rule 'atom [\"~\" 'form] (fn* (xs) (list (quote unquote) (nth xs 1))))"))
|
|
// the standard appears to be for splice-unquote to be <symbol-for-unqoute><symbol-for-deref>, but unquote deref is a reasonable
|
|
// sequence of characters and causes ambigious parses! So I chose the other common unquote symbol to be splice-unquote
|
|
rep(grammer, env, str("(add_grammer_rule 'atom [\",\" 'form] (fn* (xs) (list (quote splice-unquote) (nth xs 1))))"))
|
|
rep(grammer, env, str("(add_grammer_rule 'atom [\"@\" 'form] (fn* (xs) `(deref ~(nth xs 1))))")) //"
|
|
|
|
rep(grammer, env, str("(def! not (fn* (a) (if a false true)))"))
|
|
rep(grammer, env, str("(def! load-file (fn* (f) (eval-read-string (slurp f))))"))
|
|
rep(grammer, env, str("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"))
|
|
rep(grammer, env, str("(def! nil? (fn* (a) (= nil a)))"))
|
|
var params = vec<MalValue>()
|
|
if argc > 1 {
|
|
for (var i = 2; i < argc; i++;) {
|
|
params.add(MalValue::String(str(argv[i])))
|
|
}
|
|
env->set(str("*ARGV*"), MalValue::List(params))
|
|
println(rep(grammer, env, str("(load-file \"") + argv[1] + "\")"))
|
|
// check for compile
|
|
var main = env->get(str("main"))
|
|
if !is_err(main) {
|
|
println("Starting compile!")
|
|
match (get_value(main)) {
|
|
MalValue::Function(f) {
|
|
var top_decs = str("#include <stdlib.h>\n#include <stdio.h>\n#include <stddef.h>\n#include <string.h>\n#include <setjmp.h>\n")
|
|
top_decs += """
|
|
jmp_buf *current_exception_handler;
|
|
size_t current_exception_value;
|
|
void error(char* message) {fprintf(stderr, "%s", message); exit(1);}
|
|
void check_num_params(size_t a, size_t b, char* function) { if (a!=b) {fprintf(stderr, "%s: expected num params to be %d\n", function, b); exit(1);}}
|
|
void check_int(size_t p, char* function) { if ((p&0x7)!=0) {fprintf(stderr, "%s: expected param to be int\n", function); exit(1);}}
|
|
void check_function(size_t p, char* message) { if ((p&0x7)!=0x6) {fprintf(stderr, "%s: expected a function\n", message); exit(1);}}
|
|
typedef struct {
|
|
size_t (*func)(size_t*,size_t,size_t*);
|
|
size_t* data;
|
|
} closure;
|
|
size_t _plus_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "+"); check_int(args[0], "+"); check_int(args[1], "+");
|
|
return (size_t)(((((ptrdiff_t)args[0]) >> 3) + (((ptrdiff_t)args[1]) >> 3)) << 3);
|
|
}"""
|
|
var top_defs = str("closure _plus_closure = { _plus_impl, NULL};\n")
|
|
top_defs += """size_t _minus_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "-"); check_int(args[0], "-"); check_int(args[1], "-");
|
|
return (size_t)(((((ptrdiff_t)args[0]) >> 3) - (((ptrdiff_t)args[1]) >> 3)) << 3);
|
|
}
|
|
closure _minus_closure =(closure){ _minus_impl, NULL};
|
|
size_t _mult_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "*"); check_int(args[0], "*"); check_int(args[1], "*");
|
|
return (size_t)(((((ptrdiff_t)args[0]) >> 3) * (((ptrdiff_t)args[1]) >> 3)) << 3);
|
|
}
|
|
closure _mult_closure = (closure){ _mult_impl, NULL};
|
|
size_t _div_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "/"); check_int(args[0], "/"); check_int(args[1], "/");
|
|
return (size_t)(((((ptrdiff_t)args[0]) >> 3) / (((ptrdiff_t)args[1]) >> 3)) << 3);
|
|
}
|
|
closure _div_closure = (closure){ _div_impl, NULL};
|
|
size_t _eq_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "=");
|
|
if (args[0] == args[1]) { return 0x9F; } else { return 0x1F; }
|
|
}
|
|
closure _eq_closure = (closure){ _eq_impl, NULL};
|
|
size_t _lt_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "<"); check_int(args[0], "<"); check_int(args[1], "<");
|
|
if (args[0] < args[1]) { return 0x9F; } else { return 0x1F; }
|
|
}
|
|
closure _lt_closure = (closure){ _lt_impl, NULL};
|
|
size_t _lte_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "<="); check_int(args[0], "<="); check_int(args[1], "<=");
|
|
if (args[0] <= args[1]) { return 0x9F; } else { return 0x1F; }
|
|
}
|
|
closure _lte_closure = (closure){ _lte_impl, NULL};
|
|
size_t _gt_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, ">"); check_int(args[0], ">"); check_int(args[1], ">");
|
|
if (args[0] > args[1]) { return 0x9F; } else { return 0x1F; }
|
|
}
|
|
closure _gt_closure = (closure){ _gt_impl, NULL};
|
|
size_t _gte_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, ">="); check_int(args[0], ">="); check_int(args[1], ">=");
|
|
if (args[0] >= args[1]) { return 0x9F; } else { return 0x1F; }
|
|
}
|
|
closure _gte_closure = (closure){ _gte_impl, NULL};
|
|
size_t _print_impl(size_t* _, size_t num, size_t* args) {
|
|
for (int _idx = 0; _idx < num; _idx++) {
|
|
if ((args[_idx] & 0xFF) == 0x2F) printf("nil");
|
|
else if ((args[_idx] & 0xFF) == 0x9F) printf("true");
|
|
else if ((args[_idx] & 0xFF) == 0x1F) printf("false");
|
|
else if ((args[_idx] & 0xFF) == 0x0F) printf("Char?");
|
|
else if ((args[_idx] & 0x07) == 0x06) printf("function");
|
|
else if ((args[_idx] & 0x07) == 0x05) printf("'%s", ((char*)(args[_idx]>>3))+sizeof(size_t));
|
|
else if ((args[_idx] & 0x07) == 0x04) {
|
|
printf("(atom ");
|
|
_print_impl(NULL, 1, ((size_t*)(args[0]>>3)));
|
|
printf(")");
|
|
} else if ((args[_idx] & 0x07) == 0x03) printf("%s", ((char*)(args[_idx]>>3))+sizeof(size_t));
|
|
else if ((args[_idx] & 0x07) == 0x02) {
|
|
printf("[ ");
|
|
size_t* vec = (size_t*)(args[_idx]>>3);
|
|
for (int i = 0; i < vec[0]; i++) {
|
|
_print_impl(NULL, 1, vec+i+1);
|
|
printf(" ");
|
|
}
|
|
printf("]");
|
|
} else if ((args[_idx] & 0x07) == 0x01) {
|
|
printf("( ");
|
|
size_t *node = (size_t*)(args[_idx]>>3);
|
|
while ((node[1] & 0x7) == 0x1) {
|
|
_print_impl(NULL, 1, node);
|
|
printf(" ");
|
|
node = (size_t*)(node[1]>>3);
|
|
}
|
|
_print_impl(NULL, 1, node);
|
|
printf(" )");
|
|
} else if ((args[_idx] & 0x07) == 0x00) printf("%d", args[_idx]>>3);
|
|
else printf("can't print");
|
|
}
|
|
return 0x2F;
|
|
}
|
|
closure _print_closure = (closure){ _print_impl, NULL};
|
|
size_t _println_impl(size_t* _, size_t num, size_t* args) {
|
|
_print_impl(_, num, args);
|
|
printf("\n");
|
|
return 0x2F;
|
|
}
|
|
closure _println_closure = (closure){ _println_impl, NULL};
|
|
|
|
size_t _list_impl(size_t* _, size_t num, size_t* args) {
|
|
size_t to_ret = 0x2F;
|
|
for (int i = num-1; i >= 0; i--) {
|
|
size_t *node = malloc(sizeof(size_t)*2);
|
|
node[0] = args[i];
|
|
node[1] = to_ret;
|
|
to_ret = ((((size_t)node)<<3)|0x1);
|
|
}
|
|
return to_ret;
|
|
}
|
|
closure _list_closure = (closure){ _list_impl, NULL};
|
|
|
|
size_t _vector_impl(size_t* _, size_t num, size_t* args) {
|
|
size_t *vec = malloc(sizeof(size_t)*(num + 1));
|
|
vec[0] = num;
|
|
for (int i = 0; i < num; i++)
|
|
vec[i+1] = args[i];
|
|
return ((((size_t)vec)<<3)|0x2);
|
|
}
|
|
closure _vector_closure = (closure){ _vector_impl, NULL};
|
|
|
|
size_t _nth_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "nth");
|
|
check_int(args[1], "nth");
|
|
if ((args[0] & 0x7) == 0x1) {
|
|
size_t *node = (size_t*)(args[0]>>3);
|
|
for (int i = 0; i < (args[1]>>3); i++) {
|
|
if ((node[1] & 0x7) != 0x1) {
|
|
error("nth idx out of range\n");
|
|
}
|
|
node = (size_t*)(node[1]>>3);
|
|
}
|
|
return node[0];
|
|
} else if ((args[0] & 0x7) == 0x2) {
|
|
size_t* vec = (size_t*)(args[0]>>3);
|
|
if (((ptrdiff_t)(args[1]>>3)) < 0 || (args[1]>>3) >= vec[0]) {
|
|
error("nth idx out of range\n");
|
|
}
|
|
return vec[(args[1]>>3)+1];
|
|
} else {
|
|
error("Passed not a list or vector to nth\n");
|
|
}
|
|
return 0x2F;
|
|
}
|
|
closure _nth_closure = (closure){ _nth_impl, NULL};
|
|
|
|
size_t _atom_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 1, "atom");
|
|
size_t* atom = malloc(sizeof(size_t));
|
|
*atom = args[0];
|
|
return (((size_t)atom)<<3)|0x4;
|
|
}
|
|
closure _atom_closure = (closure){ _atom_impl, NULL};
|
|
|
|
size_t _deref_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 1, "atom");
|
|
if ((args[0] & 0x7) == 0x4) {
|
|
return *((size_t*)(args[0]>>3));
|
|
} else {
|
|
error("deref not an atom");
|
|
}
|
|
}
|
|
closure _deref_closure = (closure){ _deref_impl, NULL};
|
|
|
|
size_t _reset_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 2, "reset");
|
|
if ((args[0] & 0x7) == 0x4) {
|
|
*((size_t*)(args[0]>>3)) = args[1];
|
|
return args[1];
|
|
} else {
|
|
error("reset 0 not an atom");
|
|
}
|
|
}
|
|
closure _reset_closure = (closure){ _reset_impl, NULL};
|
|
|
|
size_t _throw_impl(size_t* _, size_t num, size_t* args) {
|
|
check_num_params(num, 1, "throw");
|
|
current_exception_value = args[0];
|
|
longjmp(*current_exception_handler, 1);
|
|
}
|
|
closure _throw_closure = (closure){ _throw_impl, NULL};
|
|
|
|
""" //"
|
|
var main_s = str("int main(int argc, char** argv) {\n")
|
|
var main_body = str()
|
|
var inner_main = compile(&top_decs, &top_defs, &main_s, &main_body, f.env, *f.body)
|
|
main_s += main_body;
|
|
main_s += "size_t main_to_ret = " + inner_main + ";\n"
|
|
main_s += "check_int(main_to_ret, \"main return\");\n"
|
|
main_s += "return (main_to_ret)>>3;\n}\n"
|
|
write_file(str(argv[1]) + ".c", top_decs + top_defs + main_s)
|
|
println("Finished compile")
|
|
return 0
|
|
}
|
|
}
|
|
error("Main not a k' function")
|
|
} else {
|
|
println("No main function to compile, exiting")
|
|
}
|
|
} else {
|
|
env->set(str("*ARGV*"), MalValue::List(params))
|
|
rep(grammer, env, str("(println (str \"Mal [\" *host-language* \"]\"))"))
|
|
while (true) {
|
|
var line = get_line(str("user> "), 1024)
|
|
if (line == "***EOF***")
|
|
break
|
|
println(rep(grammer, env, line))
|
|
}
|
|
}
|
|
}
|
|
var tmp_idx: int = 0
|
|
fun new_tmp(): str {
|
|
tmp_idx += 1
|
|
return str("x") + tmp_idx
|
|
}
|
|
fun find_closed_vars(defined: set<str>, env: *Env, ast: MalValue): set<str> {
|
|
match (ast) {
|
|
MalValue::List(l) {
|
|
println("Find closed vars list")
|
|
if (l.size == 0) {
|
|
return set<str>()
|
|
} else if (is_symbol(l[0], "def!")) {
|
|
println("Find closed vars in def!")
|
|
defined.add(get_symbol_text(l[1]))
|
|
/*return find_closed_vars(defined, env, l[2])*/
|
|
var to_ret = find_closed_vars(defined, env, l[2])
|
|
println("end Find closed vars in def!")
|
|
return to_ret
|
|
} else if (is_symbol(l[0], "let*")) {
|
|
var bindings = get_list_or_vec(l[1])
|
|
var to_ret = set<str>()
|
|
var new_env = new<Env>()->construct(env)
|
|
for (var i = 0; i < bindings.size; i+=2;) {
|
|
defined.add(get_symbol_text(bindings[i]))
|
|
new_env->set(get_symbol_text(bindings[i]), MalValue::Nil())
|
|
to_ret += find_closed_vars(defined, new_env, bindings[i+1])
|
|
}
|
|
return to_ret + find_closed_vars(defined, new_env, l[2])
|
|
} else if is_symbol(l[0], "do") || is_symbol(l[0], "if") {
|
|
var to_ret = set<str>()
|
|
for (var i = 1; i < l.size; i++;) {
|
|
to_ret += find_closed_vars(defined, env, l[i])
|
|
}
|
|
return to_ret
|
|
} else if (is_symbol(l[0], "fn*")) {
|
|
println("Find closed vars fn*")
|
|
var f = EVAL(env, ast)
|
|
/*return find_closed_vars(defined, env, get_value(f))*/
|
|
var to_ret = find_closed_vars(defined, env, get_value(f))
|
|
println("end find closed vars fn*")
|
|
return to_ret
|
|
} else if (is_symbol(l[0], "quote")) {
|
|
return set<str>()
|
|
} else if (is_symbol(l[0], "quasiquote")) {
|
|
return find_closed_vars(defined, env, quasiquote(l[1]))
|
|
} else if (is_symbol(l[0], "macroexpand")) {
|
|
error("macroexpand doesn't make sense while finding closed vars")
|
|
} else if (is_symbol(l[0], "try*")) {
|
|
error("finding closed vars in try* unimplemented")
|
|
} else {
|
|
var to_ret = set<str>()
|
|
for (var i = 0; i < l.size; i++;) {
|
|
to_ret += find_closed_vars(defined, env, l[i])
|
|
}
|
|
return to_ret
|
|
}
|
|
println("end list")
|
|
}
|
|
MalValue::List(l) {
|
|
error("Can't get clsoure_vars for " + pr_str(ast, true))
|
|
}
|
|
MalValue::Vector(l) {
|
|
var to_ret = set<str>()
|
|
for (var i = 0; i < l.size; i++;) {
|
|
to_ret += find_closed_vars(defined, env, l[i])
|
|
}
|
|
return to_ret
|
|
}
|
|
MalValue::Symbol(s) {
|
|
if !defined.contains(s) {
|
|
var scope = env->find(s)
|
|
if scope == null<Env>() {
|
|
error("Can't find " + s + " in env when trying to find closed_vars\n" + env->to_string())
|
|
}
|
|
// don't do for top level vars
|
|
if scope->outer != null<Env>() {
|
|
return set(s)
|
|
}
|
|
}
|
|
return set<str>()
|
|
}
|
|
MalValue::Int(i) {
|
|
return set<str>()
|
|
}
|
|
MalValue::Nil() {
|
|
return set<str>()
|
|
}
|
|
MalValue::True() {
|
|
return set<str>()
|
|
}
|
|
MalValue::False() {
|
|
return set<str>()
|
|
}
|
|
MalValue::Function(f) {
|
|
var new_env = new<Env>()->construct(env)
|
|
for (var i = 0; i < f.parameters.size; i++;) {
|
|
new_env->set(f.parameters[i], MalValue::Nil())
|
|
}
|
|
println("Find closed vars going inside function:\n" + new_env->to_string())
|
|
/*return find_closed_vars(defined.union(from_vector(f.parameters)), new_env, *f.body)*/
|
|
var to_ret = find_closed_vars(defined.union(from_vector(f.parameters)), new_env, *f.body)
|
|
println("coming out of function")
|
|
return to_ret
|
|
}
|
|
}
|
|
error("Can't get clsoure_vars for " + pr_str(ast, true))
|
|
}
|
|
fun compile_value(top_decs: *str, top_defs: *str, main_init: *str, defs: *str, env: *Env, ast: MalValue, quoted: bool): str {
|
|
match (ast) {
|
|
MalValue::Nil() {
|
|
return str("0x2F")
|
|
}
|
|
MalValue::True() {
|
|
return str("0x9F")
|
|
}
|
|
MalValue::False() {
|
|
return str("0x1F")
|
|
}
|
|
MalValue::Function(f) {
|
|
if quoted {
|
|
error("cannot compile quoted function - does this even make sense?")
|
|
}
|
|
|
|
var fun_name = "fun_" + new_tmp()
|
|
*top_decs += "size_t " + fun_name + "(size_t*, size_t, size_t*);\n"
|
|
var function = "size_t " + fun_name + "(size_t* closed_vars, size_t num, size_t* args) {\n"
|
|
function += str("check_num_params(num, ") + f.parameters.size + ", \"lambda\");\n"
|
|
var new_env = new<Env>()->construct(env)
|
|
for (var i = 0; i < f.parameters.size; i++;) {
|
|
function += "size_t " + f.parameters[i] + " = args[" + i + "];\n"
|
|
new_env->set(f.parameters[i], MalValue::Nil())
|
|
}
|
|
var closed_vars = find_closed_vars(set<str>(), new_env, ast)
|
|
for (var i = 0; i < closed_vars.data.size; i++;) {
|
|
function += "size_t " + closed_vars.data[i] + " = closed_vars[" + i + "];\n"
|
|
}
|
|
var inner_value = compile(top_decs, top_defs, main_init, &function, new_env, *f.body)
|
|
function += "return " + inner_value + ";\n}\n"
|
|
*top_defs += function
|
|
*defs += "closure* " + fun_name + "_closure = malloc(sizeof(closure));\n"
|
|
*defs += fun_name + "_closure->func = " + fun_name + ";\n"
|
|
|
|
|
|
if closed_vars.data.size > 0 {
|
|
*defs += fun_name + "_closure->data = malloc(sizeof(size_t)*" + closed_vars.data.size + ");\n"
|
|
for (var i = 0; i < closed_vars.data.size; i++;) {
|
|
*defs += fun_name + "_closure->data[" + i + "] = " + closed_vars.data[i] + ";\n"
|
|
}
|
|
} else {
|
|
*defs += fun_name + "_closure->data = NULL;\n"
|
|
}
|
|
return "((((size_t)"+fun_name+"_closure)<<3)|0x6)"
|
|
}
|
|
MalValue::Symbol(s) {
|
|
if quoted {
|
|
var val_name = "sym_" + new_tmp()
|
|
*defs += "size_t *" + val_name + " = malloc(sizeof(size_t)+sizeof(char)*" + (s.length()+1) +");\n"
|
|
*defs += "*" + val_name + " = " + s.length() + ";\n"
|
|
*defs += "strcpy(((char*)(" + val_name + "+1)), \"" + s + "\");\n"
|
|
return "((((size_t)" + val_name + ")<<3)|0x5)"
|
|
}
|
|
if (s == "+") {
|
|
return str("((((size_t)&_plus_closure)<<3)|0x6)")
|
|
} else if (s == "-") {
|
|
return str("((((size_t)&_minus_closure)<<3)|0x6)")
|
|
} else if (s == "*") {
|
|
return str("((((size_t)&_mult_closure)<<3)|0x6)")
|
|
} else if (s == "/") {
|
|
return str("((((size_t)&_div_closure)<<3)|0x6)")
|
|
} else if (s == "=") {
|
|
return str("((((size_t)&_eq_closure)<<3)|0x6)")
|
|
} else if (s == "<") {
|
|
return str("((((size_t)&_lt_closure)<<3)|0x6)")
|
|
} else if (s == "<=") {
|
|
return str("((((size_t)&_lte_closure)<<3)|0x6)")
|
|
} else if (s == ">") {
|
|
return str("((((size_t)&_gt_closure)<<3)|0x6)")
|
|
} else if (s == ">=") {
|
|
return str("((((size_t)&_gte_closure)<<3)|0x6)")
|
|
} else if (s == "print") {
|
|
return str("((((size_t)&_print_closure)<<3)|0x6)")
|
|
} else if (s == "println") {
|
|
return str("((((size_t)&_println_closure)<<3)|0x6)")
|
|
} else if (s == "list") {
|
|
return str("((((size_t)&_list_closure)<<3)|0x6)")
|
|
} else if (s == "vector") {
|
|
return str("((((size_t)&_vector_closure)<<3)|0x6)")
|
|
} else if (s == "nth") {
|
|
return str("((((size_t)&_nth_closure)<<3)|0x6)")
|
|
} else if (s == "atom") {
|
|
return str("((((size_t)&_atom_closure)<<3)|0x6)")
|
|
} else if (s == "deref") {
|
|
return str("((((size_t)&_deref_closure)<<3)|0x6)")
|
|
} else if (s == "reset!") {
|
|
return str("((((size_t)&_reset_closure)<<3)|0x6)")
|
|
} else if (s == "throw") {
|
|
return str("((((size_t)&_throw_closure)<<3)|0x6)")
|
|
} else {
|
|
var e = env->find(s);
|
|
if e != null<Env>() && e->outer == null<Env>() {
|
|
println(s + " found in outer-est scope!")
|
|
var v = e->get(s)
|
|
e->remove(s)
|
|
var x = str()
|
|
var value = compile(top_decs, top_defs, main_init, &x, e, get_value(v))
|
|
*top_decs += "size_t " + s + ";\n"
|
|
*main_init += x
|
|
*main_init += s + " = " + value + ";\n"
|
|
}
|
|
return s
|
|
}
|
|
}
|
|
// Atom
|
|
MalValue::Atom(a) {
|
|
var val_name = "atom_" + new_tmp()
|
|
var x = str()
|
|
var value = compile(top_decs, top_defs, main_init, &x, env, *a)
|
|
*top_decs += "size_t " + val_name + ";\n"
|
|
*main_init += x
|
|
*main_init += val_name + " = (((size_t)malloc(sizeof(size_t)))<<3)|0x4;\n"
|
|
*main_init += "*((size_t*)(" + val_name + ">>3)) = " + value + ";\n"
|
|
return val_name
|
|
}
|
|
MalValue::String(s) {
|
|
var val_name = "str_" + new_tmp()
|
|
*defs += "size_t *" + val_name + " = malloc(sizeof(size_t)+sizeof(char)*" + (s.length()+1) +");\n"
|
|
*defs += "*" + val_name + " = " + s.length() + ";\n"
|
|
*defs += "strcpy(((char*)(" + val_name + "+1)), \"" + s + "\");\n"
|
|
return "((((size_t)" + val_name + ")<<3)|0x3)"
|
|
}
|
|
MalValue::Vector(l) {
|
|
var call_str = str("_vector_impl(NULL, ") + l.size + ", (size_t[]){ "
|
|
for (var i = 0; i < l.size; i++;) {
|
|
if i != 0 {
|
|
call_str += ", "
|
|
}
|
|
if quoted {
|
|
call_str += compile_value(top_decs, top_defs, main_init, defs, env, l[i], true)
|
|
} else {
|
|
call_str += compile(top_decs, top_defs, main_init, defs, env, l[i])
|
|
}
|
|
}
|
|
return call_str + "})"
|
|
}
|
|
MalValue::List(l) {
|
|
var call_str = str("_list_impl(NULL, ") + l.size + ", (size_t[]){ "
|
|
for (var i = 0; i < l.size; i++;) {
|
|
if i != 0 {
|
|
call_str += ", "
|
|
}
|
|
if quoted {
|
|
call_str += compile_value(top_decs, top_defs, main_init, defs, env, l[i], true)
|
|
} else {
|
|
call_str += compile(top_decs, top_defs, main_init, defs, env, l[i])
|
|
}
|
|
}
|
|
return call_str + "})"
|
|
}
|
|
MalValue::Int(i) {
|
|
return to_string(i<<3)
|
|
}
|
|
}
|
|
error("could not compile value: " + pr_str(ast, true))
|
|
}
|
|
fun compile(top_decs: *str, top_defs: *str, main_init: *str, defs: *str, env: *Env, ast: MalValue): str {
|
|
var expanded = macroexpand(ast, env)
|
|
if (is_err(expanded)) {
|
|
error("compile error in macroexpand: " + pr_str(get_err(expanded), true))
|
|
}
|
|
ast = get_value(expanded)
|
|
if !is_list(ast) {
|
|
return compile_value(top_decs, top_defs, main_init, defs, env, ast, false)
|
|
}
|
|
match (ast) {
|
|
MalValue::List(l) {
|
|
if (l.size == 0) {
|
|
return compile_value(top_decs, top_defs, main_init, defs, env, ast, false)
|
|
} else if (is_symbol(l[0], "def!")) {
|
|
if (l.size != 3) {
|
|
error("def! without exaclty key and value")
|
|
}
|
|
if (!is_symbol(l[1])) {
|
|
error("def! not on symbol")
|
|
}
|
|
if env->outer != null<Env>() {
|
|
error("def! not at top level")
|
|
}
|
|
var to_set_name = get_symbol_text(l[1])
|
|
var to_set_value = compile(top_decs, top_defs, main_init, defs, env, l[2])
|
|
*defs += "size_t " + to_set_name + " = " + to_set_value + ";\n"
|
|
return to_set_name
|
|
} else if (is_symbol(l[0], "defmacro!")) {
|
|
error("defmacro! doesn't make sense in compiled code")
|
|
} else if (is_symbol(l[0], "let*")) {
|
|
if (l.size != 3) {
|
|
error("let* without list of bindings & end value")
|
|
}
|
|
if (!is_list_or_vec(l[1])) {
|
|
error("let* without list of bindings")
|
|
}
|
|
var bindings = get_list_or_vec(l[1])
|
|
if (bindings.size & 1 != 0) {
|
|
error("let* list of bindings has odd number of entries")
|
|
}
|
|
var let_val = new_tmp()
|
|
*defs += "size_t " + let_val + ";\n{\n"
|
|
var new_env = new<Env>()->construct(env)
|
|
for (var i = 0; i < bindings.size; i+=2;) {
|
|
if (!is_symbol(bindings[i])) {
|
|
error("let* var name not symbol")
|
|
}
|
|
var to_set_value = compile(top_decs, top_defs, main_init, defs, new_env, bindings[i+1])
|
|
*defs += "size_t " + get_symbol_text(bindings[i]) + " = " + to_set_value + ";\n"
|
|
new_env->set(get_symbol_text(bindings[i]), MalValue::Nil())
|
|
}
|
|
*defs += let_val + " = " + compile(top_decs, top_defs, main_init, defs, new_env, l[2]) + ";\n}\n"
|
|
return let_val
|
|
} else if (is_symbol(l[0], "do")) {
|
|
for (var i = 1; i < l.size-1; i++;) {
|
|
var value_possible_side_effect = compile(top_decs, top_defs, main_init, defs, env, l[i])
|
|
*defs += value_possible_side_effect + ";\n"
|
|
}
|
|
return compile(top_decs, top_defs, main_init, defs, env, l[l.size-1])
|
|
} else if (is_symbol(l[0], "if")) {
|
|
if l.size != 3 && l.size != 4 {
|
|
error("if needs 2 or 3 children")
|
|
}
|
|
var cond = compile(top_decs, top_defs, main_init, defs, env, l[1])
|
|
var tmp_name = new_tmp()
|
|
*defs += "size_t " + tmp_name + "; if (" + cond + " != 0x1F) {\n"
|
|
var then = compile(top_decs, top_defs, main_init, defs, env, l[2])
|
|
*defs += tmp_name + " = " + then + ";\n} else {\n"
|
|
var else_ = compile(top_decs, top_defs, main_init, defs, env, l[3])
|
|
*defs += tmp_name + " = " + else_ + ";\n}\n"
|
|
return tmp_name
|
|
} else if (is_symbol(l[0], "fn*")) {
|
|
var f = EVAL(env, ast)
|
|
return compile_value(top_decs, top_defs, main_init, defs, env, get_value(f), false)
|
|
} else if (is_symbol(l[0], "quote")) {
|
|
if l.size == 1 {
|
|
error("compile quote with no arguments")
|
|
}
|
|
return compile_value(top_decs, top_defs, main_init, defs, env, l[1], true)
|
|
} else if (is_symbol(l[0], "quasiquote")) {
|
|
if l.size == 1 {
|
|
error("compile quasiquote with no arguments")
|
|
}
|
|
return compile(top_decs, top_defs, main_init, defs, env, quasiquote(l[1]))
|
|
} else if (is_symbol(l[0], "macroexpand")) {
|
|
error("macroexpand doesn't make sense while compiling")
|
|
} else if (is_symbol(l[0], "try*")) {
|
|
var tmp_name = new_tmp()
|
|
var tmp_exception_name = new_tmp()
|
|
var tmp_new_exception_name = new_tmp()
|
|
*defs += "size_t " + tmp_name + " = 0;\n"
|
|
*defs += "jmp_buf *" + tmp_exception_name + " = current_exception_handler;\n"
|
|
*defs += "jmp_buf " + tmp_new_exception_name + ";\n"
|
|
*defs += "current_exception_handler = &" + tmp_new_exception_name + ";\n"
|
|
*defs += "if (!setjmp(*current_exception_handler)) {\n"
|
|
*defs += tmp_name + " = " + compile(top_decs, top_defs, main_init, defs, env, l[1]) + ";\n"
|
|
*defs += "} else {\n"
|
|
if l.size == 3 {
|
|
var catch = get_list(l[2])
|
|
if catch.size != 3 || !is_symbol(catch[0], "catch*") || !is_symbol(catch[1]) {
|
|
error("catch* block malformed")
|
|
}
|
|
var new_env = new<Env>()->construct(env)
|
|
new_env->set(get_symbol_text(catch[1]), MalValue::Nil())
|
|
*defs += "size_t " + get_symbol_text(catch[1]) + " = current_exception_value;\n"
|
|
*defs += tmp_name + " = " + compile(top_decs, top_defs, main_init, defs, new_env, catch[2]) + ";\n"
|
|
} else {
|
|
*defs += tmp_name + " = current_exception_value;\n";
|
|
}
|
|
*defs += "}\n"
|
|
*defs += "current_exception_handler = " + tmp_exception_name + ";\n"
|
|
return tmp_name
|
|
} else {
|
|
var to_call = vec<str>()
|
|
for (var i = 0; i < l.size; i++;) {
|
|
to_call.add(compile(top_decs, top_defs, main_init, defs, env, l[i]))
|
|
}
|
|
var func_name = new_tmp()
|
|
*defs += "size_t " + func_name + "_r = " + to_call[0] + ";\n"
|
|
*defs += "check_function(" + func_name + "_r, \"trying to call\");\n";
|
|
*defs += "closure* " + func_name + " = (closure*)(" + func_name + "_r>>3);\n"
|
|
var params_name = new_tmp()
|
|
*defs += "size_t " + params_name + "_params[] = {"+str(", ").join(to_call.slice(1,-1))+"};\n"
|
|
return func_name + "->func(" + func_name + "->data, " + to_string(l.size-1) + ", " + params_name + "_params)"
|
|
}
|
|
}
|
|
}
|
|
return compile_value(top_decs, top_defs, main_init, defs, env, ast, false)
|
|
}
|