import io:* import str:* import vec:* import vec_literals:* import util:* import map:* import fungll:* fun read_str(grammer: ref Grammer, s: str): pair { 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, True, False, Int: int, String: str, Symbol: str, List: vec, Vector: vec, Function: MalFunction, BuiltinFunction: MalBuiltinFunction, Atom: *MalValue } 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 { 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 { match (v) { MalValue::List(l) { return l } MalValue::Vector(v) { return v } MalValue::Nil() { return vec() } } 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()->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 var outer: *Env fun construct(): *Env { return construct(null()) } 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() } fun operator=(other:ref Env):void { destruct() copy_construct(&other) } fun set(key: str, val: MalValue) { data.set(key, val) } fun find(key: str): *Env { if (data.contains_key(key)) { return this } else if (outer != null()) { return outer->find(key) } else { return null() } } fun get(key: str): MalResult { var env = find(key) if (env != null()) { return MalResult::Ok(env->data.get(key)) } else { return MalResult::Err(MalValue::String(str("'") + key + "' not found")) } } } obj MalBuiltinFunction (Object) { var fp: fun(vec): MalResult fun construct(fp: fun(vec): 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): MalResult { return fp(params) } } fun make_builtin_function(f: fun(vec): MalResult): MalValue { var to_ret.construct(f): MalBuiltinFunction return MalValue::BuiltinFunction(to_ret) } obj MalFunction (Object) { var env: *Env var parameters: vec var is_variadic: bool var is_macro: bool var body: *MalValue fun construct(env: *Env, parameters: vec, 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() 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() this->body->copy_construct(old->body) } fun destruct(): void { this->env = null() parameters.destruct() delete(body) body = null() } 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): pair<*Env, MalResult> { // tco if (!is_variadic && parameters.size != params.size) || (is_variadic && parameters.size > params.size + 1) { return make_pair(null(), MalResult::Err(MalValue::String(str("called with the wrong number of parameters")))) } var new_env = new()->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): 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, 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() 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() 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"))) } 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()->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() 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()->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 PRINT(v: MalValue): str { return pr_str(v, true) } fun rep(grammer: ref Grammer, env: *Env, a: str): str { var read = READ(grammer, a) if is_err(read) { return PRINT(get_err(read)) } else { var evaled = EVAL(env, get_value(read)) if is_err(evaled) { return str("Exception: ") + PRINT(get_err(evaled)) } else { return PRINT(get_value(evaled)) } } } fun print_wrapper(params: vec, 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 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 = fun(_: ref MalValue, x: ref vec): MalResult { return MalResult::Ok(MalValue::Nil()); } var ret_0_sym: fun(ref MalValue, ref vec): MalResult = fun(_: ref MalValue, x: ref vec): 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(), 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(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(), MalValue::Nil(), fun(_: ref MalValue, x: ref vec): MalResult { return MalResult::Ok(MalValue::List(vec())) }) grammer.add_to_nonterminal(space_forms, vec(form), MalValue::Nil(), fun(_: ref MalValue, x: ref vec): 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 { 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 { return MalResult::Ok(MalValue::Vector(vec())); }) 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 { 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 { 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()->construct() env->set(str("+"), make_builtin_function(fun(params: vec): 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): 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): 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): 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("prn"), make_builtin_function(fun(params: vec): 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("list"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Ok(MalValue::List(params)) })); env->set(str("list?"), make_builtin_function(fun(params: vec): 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): 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): 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("="), make_builtin_function(fun(params: vec): 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): 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): 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): 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): 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("pr-str"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Ok(MalValue::String(print_wrapper(params, " ", true))) })); env->set(str("str"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Ok(MalValue::String(print_wrapper(params, "", false))) })); env->set(str("prn"), make_builtin_function(fun(params: vec): MalResult { println(print_wrapper(params, " ", true)) return MalResult::Ok(MalValue::Nil()) })); env->set(str("println"), make_builtin_function(fun(params: vec): MalResult { println(print_wrapper(params, " ", false)) return MalResult::Ok(MalValue::Nil()) })); env->set(str("read-string"), make_builtin_function(fun(params: vec): 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): 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): 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): MalResult { if params.size != 1 { return MalResult::Err(MalValue::String(str("atom with wrong number of params"))) } else { var atom = new() atom->copy_construct(¶ms[0]) return MalResult::Ok(MalValue::Atom(atom)) } })); env->set(str("atom?"), make_builtin_function(fun(params: vec): 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): 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): 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): 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): 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): MalResult { var to_ret = vec() 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): 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): 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): 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())) } return MalResult::Ok(MalValue::List(list.slice(1,-1))) } })); env->set(str("throw"), make_builtin_function(fun(params: vec): 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): 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): 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() 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): 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): 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): MalResult { return MalResult::Ok(MalValue::Vector(params)) })); env->set(str("vector?"), make_builtin_function(fun(params: vec): 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): 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): 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): MalResult { return MalResult::Err(MalValue::String(str("not implemented"))) })); env->set(str("with-meta"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Err(MalValue::String(str("not implemented"))) })); env->set(str("fn?"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Err(MalValue::String(str("not implemented"))) })); env->set(str("string?"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Err(MalValue::String(str("not implemented"))) })); env->set(str("number?"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Err(MalValue::String(str("not implemented"))) })); env->set(str("seq"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Err(MalValue::String(str("not implemented"))) })); env->set(str("conj"), make_builtin_function(fun(params: vec): MalResult { return MalResult::Err(MalValue::String(str("not implemented"))) })); // self-modifying grammer env->set(str("add_terminal"), make_builtin_function(fun(params: vec): 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): 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() 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 { var params = vec() 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): 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))))))")) //' 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 , 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))))")) // let's use the new reader macros in another reader macro 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)))")) rep(grammer, env, str("(def! true? (fn* (a) (= true a)))")) rep(grammer, env, str("(def! false? (fn* (a) (= false a)))")) rep(grammer, env, str("(def! *host-language* \"kraken\")")) var params = vec() 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] + "\")")) } 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)) } } }