diff options
| author | Joel Martin <github@martintribe.org> | 2014-12-18 23:52:59 -0600 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-09 16:16:51 -0600 |
| commit | a816262a057ecc4bd1fd07750d21cab81490f336 (patch) | |
| tree | f83bea9965ef09460d213812f738a32a2dc6803c | |
| parent | 821930dbd9febf45fad25aefa9bc64e8ace4c737 (diff) | |
| download | mal-a816262a057ecc4bd1fd07750d21cab81490f336.tar.gz mal-a816262a057ecc4bd1fd07750d21cab81490f336.zip | |
Scala: refactor collections with metadata.
Convert numeric to Long. Add time-ms.
Add Makefile for stats targets.
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | scala/Makefile | 20 | ||||
| -rw-r--r-- | scala/core.scala | 169 | ||||
| -rw-r--r-- | scala/env.scala | 4 | ||||
| -rw-r--r-- | scala/printer.scala | 16 | ||||
| -rw-r--r-- | scala/reader.scala | 24 | ||||
| -rw-r--r-- | scala/step2_eval.scala | 24 | ||||
| -rw-r--r-- | scala/step3_env.scala | 32 | ||||
| -rw-r--r-- | scala/step4_if_fn_do.scala | 36 | ||||
| -rw-r--r-- | scala/step5_tco.scala | 34 | ||||
| -rw-r--r-- | scala/step6_file.scala | 36 | ||||
| -rw-r--r-- | scala/step7_quote.scala | 58 | ||||
| -rw-r--r-- | scala/step8_macros.scala | 77 | ||||
| -rw-r--r-- | scala/step9_try.scala | 79 | ||||
| -rw-r--r-- | scala/stepA_interop.scala | 229 | ||||
| -rw-r--r-- | scala/types.scala | 182 | ||||
| -rw-r--r-- | tests/step6_file.mal | 7 |
17 files changed, 734 insertions, 297 deletions
@@ -3,7 +3,7 @@ ## Description Mal is an interpreter for a subset of the Clojure programming -language. Mal is implemented from scratch in 18 different languages: +language. Mal is implemented from scratch in 19 different languages: * Bash shell * C @@ -243,7 +243,7 @@ mono ./stepX_YYY.exe ## Running tests -The are nearly 400 generic Mal tests (for all implementations) in the +The are nearly 500 generic Mal tests (for all implementations) in the `tests/` directory. Each step has a corresponding test file containing tests specific to that step. The `runtest.py` test harness uses pexpect to launch a Mal step implementation and then feeds the tests diff --git a/scala/Makefile b/scala/Makefile new file mode 100644 index 0000000..10c2e11 --- /dev/null +++ b/scala/Makefile @@ -0,0 +1,20 @@ +TESTS = + +SOURCES_BASE = types.scala reader.scala printer.scala +SOURCES_LISP = env.scala core.scala stepA_interop.scala +SOURCES = $(SOURCES_BASE) $(SOURCES_LISP) + +#all: mal.scala + +.PHONY: stats tests $(TESTS) + +stats: $(SOURCES) + @wc $^ +stats-lisp: $(SOURCES_LISP) + @wc $^ + +tests: $(TESTS) + +$(TESTS): + @echo "Running $@"; \ + ruby $@ || exit 1; \ diff --git a/scala/core.scala b/scala/core.scala index 6310f89..3317d08 100644 --- a/scala/core.scala +++ b/scala/core.scala @@ -1,6 +1,10 @@ import scala.collection.mutable import scala.io.Source +import types.{MalList, _list, _list_Q, + MalVector, _vector, _vector_Q, + MalHashMap, _hash_map_Q, _hash_map, + Func, MalFunction} import printer._pr_list object core { @@ -20,65 +24,106 @@ object core { } } + + // number functions + def _bool_op(a: List[Any], op: (Long, Long) => Boolean) = { + op(a(0).asInstanceOf[Long],a(1).asInstanceOf[Long]) + } + + def _num_op(a: List[Any], op: (Long, Long) => Long) = { + op(a(0).asInstanceOf[Long],a(1).asInstanceOf[Long]) + } + + // string functions def read_string(a: List[Any]) = { reader.read_str(a(0).asInstanceOf[String]) } def slurp(a: List[Any]) = { - Source.fromFile(a(0).asInstanceOf[String]).getLines.mkString("\n") + Source.fromFile(a(0).asInstanceOf[String]).getLines.mkString("\n") + "\n" } // Hash Map functions def assoc(a: List[Any]): Any = { - a(0).asInstanceOf[Map[String,Any]] ++ - (types._hash_map(a.drop(1)).asInstanceOf[Map[String,Any]]) + a(0).asInstanceOf[MalHashMap] ++ _hash_map(a.drop(1):_*) } def dissoc(a: List[Any]): Any = { - var kSet = types._toList(a.drop(1)).toSet - a(0).asInstanceOf[Map[String,Any]] + var kSet = a.drop(1).toSet + a(0).asInstanceOf[MalHashMap] .filterKeys{ !kSet.contains(_) } } def get(a: List[Any]): Any = { - val hm = a(0).asInstanceOf[Map[String,Any]] + val hm = a(0).asInstanceOf[MalHashMap] val key = a(1).asInstanceOf[String] - if (hm != null && hm.contains(key)) hm(key) else null + if (hm != null && hm.value.contains(key)) hm(key) else null } def contains_Q(a: List[Any]): Any = { - a(0).asInstanceOf[Map[String,Any]] + a(0).asInstanceOf[MalHashMap].value .contains(a(1).asInstanceOf[String]) } // sequence functions - def concat(a: List[Any]): List[Any] = { - (for (sq <- a) yield types._toIter(sq)).flatten + def concat(a: List[Any]): Any = { + _list((for (sq <- a) yield types._toIter(sq)).flatten:_*) } def nth(a: List[Any]): Any = { - val lst = types._toList(a(0)) - val idx = a(1).asInstanceOf[Int] + val lst = a(0).asInstanceOf[MalList].value + val idx = a(1).asInstanceOf[Long] if (idx < lst.length) { - lst(idx) + lst(idx.toInt) } else { throw new Exception("nth: index out of range") } } def first(a: List[Any]): Any = { - val lst = types._toList(a(0)) + val lst = a(0).asInstanceOf[MalList].value if (lst.length > 0) lst(0) else null } + def rest(a: List[Any]): Any = { + a(0) match { + case null => true + case ml: MalList => _list(ml.drop(1).value:_*) + } + } + + def empty_Q(a: List[Any]): Any = { + a(0) match { + case null => true + case ml: MalList => ml.value.isEmpty + } + } + + def count(a: List[Any]): Any = { + a(0) match { + case null => 0 + case ml: MalList => ml.value.length.asInstanceOf[Long] + } + } + + def conj(a: List[Any]): Any = { + a(0) match { + case mv: MalVector => { + _vector(mv.value ++ a.slice(1,a.length):_*) + } + case ml: MalList => { + _list(a.slice(1,a.length).reverse ++ ml.value:_*) + } + } + } def apply(a: List[Any]): Any = { a match { case f :: rest => { var args1 = rest.slice(0,rest.length-1) - var args = args1 ++ types._toList(rest(rest.length-1)) + var args = args1 ++ rest(rest.length-1).asInstanceOf[MalList].value types._apply(f, args) } case _ => throw new Exception("invalid apply call") @@ -88,13 +133,52 @@ object core { def do_map(a: List[Any]): Any = { a match { case f :: seq :: Nil => { - types._toList(seq).map(x => types._apply(f,List(x))) + seq.asInstanceOf[MalList].map(x => types._apply(f,List(x))) } case _ => throw new Exception("invalid map call") } } + // meta functions + def with_meta(a: List[Any]): Any = { + val meta: Any = a(1) + a(0) match { + case ml: MalList => { + val new_ml = ml.clone() + new_ml.meta = meta + new_ml + } + case hm: MalHashMap => { + val new_hm = hm.clone() + new_hm.meta = meta + new_hm + } + case fn: Func => { + val new_fn = fn.clone() + new_fn.meta = meta + new_fn + } + case fn: MalFunction => { + val new_fn = fn.clone() + new_fn.meta = meta + new_fn + } + case _ => throw new Exception("no meta support for " + a(0).getClass) + } + } + + def meta(a: List[Any]): Any = { + a(0) match { + case ml: MalList => ml.meta + case hm: MalHashMap => hm.meta + case fn: Func => fn.meta + case fn: MalFunction => fn.meta + case _ => throw new Exception("no meta support for " + a(0).getClass) + } + } + + // atom functions def reset_BANG(a: List[Any]): Any = { a(0).asInstanceOf[types.Atom].value = a(1) @@ -114,7 +198,7 @@ object core { } - val ns: Map[String, Any] = Map( + val ns: Map[String, (List[Any]) => Any] = Map( "=" -> ((a: List[Any]) => types._equal_Q(a(0), a(1))), "throw" -> mal_throw _, "nil?" -> ((a: List[Any]) => a(0) == null), @@ -129,44 +213,47 @@ object core { "str" -> ((a: List[Any]) => _pr_list(a, false, "")), "prn" -> ((a: List[Any]) => { println(_pr_list(a, true, " ")); null}), "println" -> ((a: List[Any]) => { println(_pr_list(a, false, " ")); null}), + "readline" -> ((a: List[Any]) => readLine(a(0).asInstanceOf[String])), "read-string" -> read_string _, "slurp" -> slurp _, - "<" -> ((a: List[Any]) => a(0).asInstanceOf[Int] < a(1).asInstanceOf[Int]), - "<=" -> ((a: List[Any]) => a(0).asInstanceOf[Int] <= a(1).asInstanceOf[Int]), - ">" -> ((a: List[Any]) => a(0).asInstanceOf[Int] > a(1).asInstanceOf[Int]), - ">=" -> ((a: List[Any]) => a(0).asInstanceOf[Int] >= a(1).asInstanceOf[Int]), - "+" -> ((a: List[Any]) => a(0).asInstanceOf[Int] + a(1).asInstanceOf[Int]), - "-" -> ((a: List[Any]) => a(0).asInstanceOf[Int] - a(1).asInstanceOf[Int]), - "*" -> ((a: List[Any]) => a(0).asInstanceOf[Int] * a(1).asInstanceOf[Int]), - "/" -> ((a: List[Any]) => a(0).asInstanceOf[Int] / a(1).asInstanceOf[Int]), - - "list" -> ((a: List[Any]) => a), - "list?" -> ((a: List[Any]) => a(0).isInstanceOf[List[Any]]), - "vector" -> ((a: List[Any]) => a.toArray), - "vector?" -> ((a: List[Any]) => a(0).isInstanceOf[Array[Any]]), - "hash-map" -> ((a: List[Any]) => types._hash_map(a)), - "map?" -> ((a: List[Any]) => a(0).isInstanceOf[Map[String,Any] @unchecked]), + + "<" -> ((a: List[Any]) => _bool_op(a, _ < _)), + "<=" -> ((a: List[Any]) => _bool_op(a, _ <= _)), + ">" -> ((a: List[Any]) => _bool_op(a, _ > _)), + ">=" -> ((a: List[Any]) => _bool_op(a, _ >= _)), + "+" -> ((a: List[Any]) => _num_op(a, _ + _)), + "-" -> ((a: List[Any]) => _num_op(a, _ - _)), + "*" -> ((a: List[Any]) => _num_op(a, _ * _)), + "/" -> ((a: List[Any]) => _num_op(a, _ / _)), + "time-ms" -> ((a: List[Any]) => System.currentTimeMillis), + + "list" -> ((a: List[Any]) => _list(a:_*)), + "list?" -> ((a: List[Any]) => _list_Q(a(0))), + "vector" -> ((a: List[Any]) => _vector(a:_*)), + "vector?" -> ((a: List[Any]) => _vector_Q(a(0))), + "hash-map" -> ((a: List[Any]) => _hash_map(a:_*)), + "map?" -> ((a: List[Any]) => _hash_map_Q(a(0))), "assoc" -> assoc _, "dissoc" -> dissoc _, "get" -> get _, "contains?" -> contains_Q _, - "keys" -> ((a: List[Any]) => a(0).asInstanceOf[Map[String,Any]].keys.toList), - "vals" -> ((a: List[Any]) => a(0).asInstanceOf[Map[String,Any]].values.toList), + "keys" -> ((a: List[Any]) => a(0).asInstanceOf[MalHashMap].keys), + "vals" -> ((a: List[Any]) => a(0).asInstanceOf[MalHashMap].vals), "sequential?" -> ((a: List[Any]) => types._sequential_Q(a(0))), - "cons" -> ((a: List[Any]) => a(0) +: types._toList(a(1))), + "cons" -> ((a: List[Any]) => a(0) +: a(1).asInstanceOf[MalList]), "concat" -> concat _, "nth" -> nth _, "first" -> first _, - "rest" -> ((a: List[Any]) => types._toList(a(0)).drop(1)), - "empty?" -> ((a: List[Any]) => types._toIter(a(0)).isEmpty), - "count" -> ((a: List[Any]) => types._toIter(a(0)).length), - "conj" -> ((a: List[Any]) => null), + "rest" -> rest _, + "empty?" -> empty_Q _, + "count" -> count _, + "conj" -> conj _, "apply" -> apply _, "map" -> do_map _, - "with-meta" -> ((a: List[Any]) => null), - "meta" -> ((a: List[Any]) => null), + "with-meta" -> with_meta _, + "meta" -> meta _, "atom" -> ((a: List[Any]) => new types.Atom(a(0))), "atom?" -> ((a: List[Any]) => a(0).isInstanceOf[types.Atom]), "deref" -> ((a: List[Any]) => a(0).asInstanceOf[types.Atom].value), diff --git a/scala/env.scala b/scala/env.scala index 9b6cff8..33be545 100644 --- a/scala/env.scala +++ b/scala/env.scala @@ -1,3 +1,5 @@ +import types._list + import scala.collection.mutable object env { @@ -9,7 +11,7 @@ object env { binds.foreach(b => { val k = b.asInstanceOf[Symbol] if (k == '&) { - data(binds.next().asInstanceOf[Symbol]) = exprs.toList + data(binds.next().asInstanceOf[Symbol]) = _list(exprs.toSeq:_*) } else { data(k) = exprs.next() } diff --git a/scala/printer.scala b/scala/printer.scala index 74b1433..0a0e0b7 100644 --- a/scala/printer.scala +++ b/scala/printer.scala @@ -1,15 +1,13 @@ -import types.Function +import types.{MalList, MalVector, MalHashMap, MalFunction} + object printer { def _pr_str(obj: Any, print_readably: Boolean = true): String = { val _r = print_readably return obj match { - case l: List[Any] => "(" + l.map(_pr_str(_, _r)).mkString(" ") + ")" - case v: Array[Any] => "[" + v.map(_pr_str(_, _r)).mkString(" ") + "]" - case m: Map[String @unchecked,Any @unchecked] => { - val lst = m.map{case (k,v) => List(k, v)}.flatten - "{" + lst.map(_pr_str(_,_r)).mkString(" ") + "}" - } + case v: MalVector => v.toString(_r) + case l: MalList => l.toString(_r) + case hm: MalHashMap => hm.toString(_r) case s: String => { if (s.length > 0 && s(0) == '\u029e') { ":" + s.substring(1,s.length) @@ -26,8 +24,8 @@ object printer { case a: types.Atom => "(atom " + a.value + ")" case null => "nil" case _ => { - if (obj.isInstanceOf[Function]) { - val f = obj.asInstanceOf[Function] + if (obj.isInstanceOf[MalFunction]) { + val f = obj.asInstanceOf[MalFunction] "<function (fn* " + _pr_str(f.params) + " " + _pr_str(f.ast) + ")>" } else { obj.toString diff --git a/scala/reader.scala b/scala/reader.scala index fc16f28..de45923 100644 --- a/scala/reader.scala +++ b/scala/reader.scala @@ -1,5 +1,7 @@ import scala.util.matching.Regex +import types.{MalList, _list, MalVector, _vector, MalHashMap, _hash_map} + object reader { class Reader (tokens: Array[String]) { @@ -34,7 +36,7 @@ object reader { val re_str = """^"(.*)"$""".r val re_key = """^:(.*)$""".r return token match { - case re_int(i) => i.toInt // integer + case re_int(i) => i.toLong // integer case re_flt(f) => f.toDouble // float case re_str(s) => parse_str(s) // string case re_key(k) => "\u029e" + k // keyword @@ -46,8 +48,8 @@ object reader { } def read_list(rdr: Reader, - start: String = "(", end: String = ")"): List[Any] = { - var ast: List[Any] = List() + start: String = "(", end: String = ")"): MalList = { + var ast: MalList = _list() var token = rdr.next() if (token != start) throw new Exception("expected '" + start + "', got EOF") while ({token = rdr.peek(); token != end}) { @@ -60,19 +62,19 @@ object reader { def read_form(rdr: Reader): Any = { return rdr.peek() match { - case "'" => { rdr.next; List(Symbol("quote"), read_form(rdr)) } - case "`" => { rdr.next; List(Symbol("quasiquote"), read_form(rdr)) } - case "~" => { rdr.next; List(Symbol("unquote"), read_form(rdr)) } - case "~@" => { rdr.next; List(Symbol("splice-unquote"), read_form(rdr)) } + case "'" => { rdr.next; _list(Symbol("quote"), read_form(rdr)) } + case "`" => { rdr.next; _list(Symbol("quasiquote"), read_form(rdr)) } + case "~" => { rdr.next; _list(Symbol("unquote"), read_form(rdr)) } + case "~@" => { rdr.next; _list(Symbol("splice-unquote"), read_form(rdr)) } case "^" => { rdr.next; val meta = read_form(rdr); - List(Symbol("with-meta"), read_form(rdr), meta) } - case "@" => { rdr.next; List(Symbol("deref"), read_form(rdr)) } + _list(Symbol("with-meta"), read_form(rdr), meta) } + case "@" => { rdr.next; _list(Symbol("deref"), read_form(rdr)) } case "(" => read_list(rdr) case ")" => throw new Exception("unexpected ')')") - case "[" => read_list(rdr, "[", "]").toArray + case "[" => _vector(read_list(rdr, "[", "]").value:_*) case "]" => throw new Exception("unexpected ']')") - case "{" => types._hash_map(read_list(rdr, "{", "}")) + case "{" => _hash_map(read_list(rdr, "{", "}").value:_*) case "}" => throw new Exception("unexpected '}')") case _ => read_atom(rdr) } diff --git a/scala/step2_eval.scala b/scala/step2_eval.scala index b7b209d..d31d339 100644 --- a/scala/step2_eval.scala +++ b/scala/step2_eval.scala @@ -1,4 +1,4 @@ -import reader.tokenize +import types.{MalList, _list_Q, MalVector, MalHashMap, MalFunction} object step2_eval { // read @@ -10,10 +10,10 @@ object step2_eval { def eval_ast(ast: Any, env: Map[Symbol,Any]): Any = { ast match { case s : Symbol => env(s) - case l: List[Any] => l.map(EVAL(_, env)) - case v: Array[Any] => v.map(EVAL(_, env)).toArray - case m: Map[String @unchecked,Any @unchecked] => { - m.map{case (k: String,v: Any) => (k, EVAL(v, env))}.toMap + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} } case _ => ast } @@ -21,15 +21,15 @@ object step2_eval { def EVAL(ast: Any, env: Map[Symbol,Any]): Any = { //println("EVAL: " + printer._pr_str(ast,true)) - if (!ast.isInstanceOf[List[Any]]) + if (!_list_Q(ast)) return eval_ast(ast, env) // apply list - eval_ast(ast, env) match { + eval_ast(ast, env).asInstanceOf[MalList].value match { case f :: el => { var fn: List[Any] => Any = null try { - fn = f.asInstanceOf[(List[Any]) => Any] + fn = f.asInstanceOf[List[Any] => Any] } catch { case _: Throwable => throw new Exception("attempt to call non-function") @@ -48,10 +48,10 @@ object step2_eval { // repl def main(args: Array[String]) = { val repl_env: Map[Symbol,Any] = Map( - '+ -> ((a: List[Any]) => a(0).asInstanceOf[Int] + a(1).asInstanceOf[Int]), - '- -> ((a: List[Any]) => a(0).asInstanceOf[Int] - a(1).asInstanceOf[Int]), - '* -> ((a: List[Any]) => a(0).asInstanceOf[Int] * a(1).asInstanceOf[Int]), - '/ -> ((a: List[Any]) => a(0).asInstanceOf[Int] / a(1).asInstanceOf[Int])) + '+ -> ((a: List[Any]) => a(0).asInstanceOf[Long] + a(1).asInstanceOf[Long]), + '- -> ((a: List[Any]) => a(0).asInstanceOf[Long] - a(1).asInstanceOf[Long]), + '* -> ((a: List[Any]) => a(0).asInstanceOf[Long] * a(1).asInstanceOf[Long]), + '/ -> ((a: List[Any]) => a(0).asInstanceOf[Long] / a(1).asInstanceOf[Long])) val REP = (str: String) => { PRINT(EVAL(READ(str), repl_env)) } diff --git a/scala/step3_env.scala b/scala/step3_env.scala index 12d335d..37572fe 100644 --- a/scala/step3_env.scala +++ b/scala/step3_env.scala @@ -1,3 +1,4 @@ +import types.{MalList, _list_Q, MalVector, MalHashMap, MalFunction} import env.Env object step3_env { @@ -10,10 +11,10 @@ object step3_env { def eval_ast(ast: Any, env: Env): Any = { ast match { case s : Symbol => env.get(s) - case l: List[Any] => l.map(EVAL(_, env)) - case v: Array[Any] => v.map(EVAL(_, env)).toArray - case m: Map[String @unchecked,Any @unchecked] => { - m.map{case (k: String,v: Any) => (k, EVAL(v, env))}.toMap + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} } case _ => ast } @@ -21,29 +22,24 @@ object step3_env { def EVAL(ast: Any, env: Env): Any = { //println("EVAL: " + printer._pr_str(ast,true)) - if (!ast.isInstanceOf[List[Any]]) + if (!_list_Q(ast)) return eval_ast(ast, env) // apply list - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case Symbol("def!") :: a1 :: a2 :: Nil => { return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) } case Symbol("let*") :: a1 :: a2 :: Nil => { val let_env = new Env(env) - val it: Iterator[Any] = a1 match { - case l: List[Any] => l.iterator - case v: Array[Any] => v.iterator - case _ => throw new Exception("let* non-sequence bindings") - } - for (g <- it.grouped(2)) { + for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) } return EVAL(a2, let_env) } case _ => { // function call - eval_ast(ast, env) match { + eval_ast(ast, env).asInstanceOf[MalList].value match { case f :: el => { var fn: List[Any] => Any = null try { @@ -68,10 +64,10 @@ object step3_env { // repl def main(args: Array[String]) = { val repl_env: Env = new Env() - repl_env.set('+, (a: List[Any]) => a(0).asInstanceOf[Int] + a(1).asInstanceOf[Int]) - repl_env.set('-, (a: List[Any]) => a(0).asInstanceOf[Int] - a(1).asInstanceOf[Int]) - repl_env.set('*, (a: List[Any]) => a(0).asInstanceOf[Int] * a(1).asInstanceOf[Int]) - repl_env.set('/, (a: List[Any]) => a(0).asInstanceOf[Int] / a(1).asInstanceOf[Int]) + repl_env.set('+, (a: List[Any]) => a(0).asInstanceOf[Long] + a(1).asInstanceOf[Long]) + repl_env.set('-, (a: List[Any]) => a(0).asInstanceOf[Long] - a(1).asInstanceOf[Long]) + repl_env.set('*, (a: List[Any]) => a(0).asInstanceOf[Long] * a(1).asInstanceOf[Long]) + repl_env.set('/, (a: List[Any]) => a(0).asInstanceOf[Long] / a(1).asInstanceOf[Long]) val REP = (str: String) => { PRINT(EVAL(READ(str), repl_env)) } @@ -81,7 +77,7 @@ object step3_env { try { println(REP(line)) } catch { - case e : Exception => { + case e : Throwable => { println("Error: " + e.getMessage) println(" " + e.getStackTrace.mkString("\n ")) } diff --git a/scala/step4_if_fn_do.scala b/scala/step4_if_fn_do.scala index a7a6b22..b6c2dc1 100644 --- a/scala/step4_if_fn_do.scala +++ b/scala/step4_if_fn_do.scala @@ -1,3 +1,5 @@ +import types.{MalList, _list, _list_Q, MalVector, MalHashMap, + Func, MalFunction} import env.Env object step4_if_fn_do { @@ -10,10 +12,10 @@ object step4_if_fn_do { def eval_ast(ast: Any, env: Env): Any = { ast match { case s : Symbol => env.get(s) - case l: List[Any] => l.map(EVAL(_, env)) - case v: Array[Any] => v.map(EVAL(_, env)).toArray - case m: Map[String @unchecked,Any @unchecked] => { - m.map{case (k: String,v: Any) => (k, EVAL(v, env))}.toMap + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} } case _ => ast } @@ -21,24 +23,24 @@ object step4_if_fn_do { def EVAL(ast: Any, env: Env): Any = { //println("EVAL: " + printer._pr_str(ast,true)) - if (!ast.isInstanceOf[List[Any]]) + if (!_list_Q(ast)) return eval_ast(ast, env) // apply list - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case Symbol("def!") :: a1 :: a2 :: Nil => { return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) } case Symbol("let*") :: a1 :: a2 :: Nil => { val let_env = new Env(env) - for (g <- types._toIter(a1).grouped(2)) { + for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) } return EVAL(a2, let_env) } case Symbol("do") :: rest => { - val el = eval_ast(rest, env) - return el.asInstanceOf[List[Any]].last + val el = eval_ast(_list(rest:_*), env) + return el.asInstanceOf[MalList].value.last } case Symbol("if") :: a1 :: a2 :: rest => { val cond = EVAL(a1, env) @@ -50,17 +52,17 @@ object step4_if_fn_do { } } case Symbol("fn*") :: a1 :: a2 :: Nil => { - return (args: List[Any]) => { + return new Func((args: List[Any]) => { EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) - } + }) } case _ => { // function call - eval_ast(ast, env) match { + eval_ast(ast, env).asInstanceOf[MalList].value match { case f :: el => { - var fn: List[Any] => Any = null + var fn: Func = null try { - fn = f.asInstanceOf[(List[Any]) => Any] + fn = f.asInstanceOf[Func] } catch { case _: Throwable => throw new Exception("attempt to call non-function") @@ -84,7 +86,9 @@ object step4_if_fn_do { val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) // core.scala: defined using scala - core.ns.map{case (k: String,v: Any) => { repl_env.set(Symbol(k), v) }} + core.ns.map{case (k: String,v: Any) => { + repl_env.set(Symbol(k), new Func(v)) + }} // core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") @@ -94,7 +98,7 @@ object step4_if_fn_do { try { println(REP(line)) } catch { - case e : Exception => { + case e : Throwable => { println("Error: " + e.getMessage) println(" " + e.getStackTrace.mkString("\n ")) } diff --git a/scala/step5_tco.scala b/scala/step5_tco.scala index d54eaa7..c4511e5 100644 --- a/scala/step5_tco.scala +++ b/scala/step5_tco.scala @@ -1,3 +1,5 @@ +import types.{MalList, _list, _list_Q, MalVector, MalHashMap, + Func, MalFunction} import env.Env object step5_tco { @@ -10,10 +12,10 @@ object step5_tco { def eval_ast(ast: Any, env: Env): Any = { ast match { case s : Symbol => env.get(s) - case l: List[Any] => l.map(EVAL(_, env)) - case v: Array[Any] => v.map(EVAL(_, env)).toArray - case m: Map[String @unchecked,Any @unchecked] => { - m.map{case (k: String,v: Any) => (k, EVAL(v, env))}.toMap + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} } case _ => ast } @@ -24,25 +26,25 @@ object step5_tco { while (true) { //println("EVAL: " + printer._pr_str(ast,true)) - if (!ast.isInstanceOf[List[Any]]) + if (!_list_Q(ast)) return eval_ast(ast, env) // apply list - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case Symbol("def!") :: a1 :: a2 :: Nil => { return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) } case Symbol("let*") :: a1 :: a2 :: Nil => { val let_env = new Env(env) - for (g <- types._toIter(a1).grouped(2)) { + for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) } env = let_env ast = a2 // continue loop (TCO) } case Symbol("do") :: rest => { - eval_ast(rest.slice(1,rest.length-1), env) - ast = ast.asInstanceOf[List[Any]].last // continue loop (TCO) + eval_ast(_list(rest.slice(0,rest.length-1):_*), env) + ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO) } case Symbol("if") :: a1 :: a2 :: rest => { val cond = EVAL(a1, env) @@ -54,7 +56,7 @@ object step5_tco { } } case Symbol("fn*") :: a1 :: a2 :: Nil => { - return new types.Function(a2, env, a1.asInstanceOf[List[Any]], + return new MalFunction(a2, env, a1.asInstanceOf[MalList], (args: List[Any]) => { EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) } @@ -62,18 +64,18 @@ object step5_tco { } case _ => { // function call - eval_ast(ast, env) match { + eval_ast(ast, env).asInstanceOf[MalList].value match { case f :: el => { f match { - case fn: types.Function => { + case fn: MalFunction => { env = fn.gen_env(el) ast = fn.ast // continue loop (TCO) } - case fn: ((List[Any]) => Any) @unchecked => { + case fn: Func => { return fn(el) } case _ => { - throw new Exception("attempt to call non-function") + throw new Exception("attempt to call non-function: " + f) } } } @@ -95,7 +97,9 @@ object step5_tco { val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) // core.scala: defined using scala - core.ns.map{case (k: String,v: Any) => { repl_env.set(Symbol(k), v) }} + core.ns.map{case (k: String,v: Any) => { + repl_env.set(Symbol(k), new Func(v)) + }} // core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/scala/step6_file.scala b/scala/step6_file.scala index 0b4d228..0dad7cd 100644 --- a/scala/step6_file.scala +++ b/scala/step6_file.scala @@ -1,3 +1,5 @@ +import types.{MalList, _list, _list_Q, MalVector, MalHashMap, + Func, MalFunction} import env.Env object step6_file { @@ -10,10 +12,10 @@ object step6_file { def eval_ast(ast: Any, env: Env): Any = { ast match { case s : Symbol => env.get(s) - case l: List[Any] => l.map(EVAL(_, env)) - case v: Array[Any] => v.map(EVAL(_, env)).toArray - case m: Map[String @unchecked,Any @unchecked] => { - m.map{case (k: String,v: Any) => (k, EVAL(v, env))}.toMap + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} } case _ => ast } @@ -24,25 +26,25 @@ object step6_file { while (true) { //println("EVAL: " + printer._pr_str(ast,true)) - if (!ast.isInstanceOf[List[Any]]) + if (!_list_Q(ast)) return eval_ast(ast, env) // apply list - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case Symbol("def!") :: a1 :: a2 :: Nil => { return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) } case Symbol("let*") :: a1 :: a2 :: Nil => { val let_env = new Env(env) - for (g <- types._toIter(a1).grouped(2)) { + for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) } env = let_env ast = a2 // continue loop (TCO) } case Symbol("do") :: rest => { - eval_ast(rest.slice(0,rest.length-1), env) - ast = ast.asInstanceOf[List[Any]].last // continue loop (TCO) + eval_ast(_list(rest.slice(0,rest.length-1):_*), env) + ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO) } case Symbol("if") :: a1 :: a2 :: rest => { val cond = EVAL(a1, env) @@ -54,7 +56,7 @@ object step6_file { } } case Symbol("fn*") :: a1 :: a2 :: Nil => { - return new types.Function(a2, env, a1.asInstanceOf[List[Any]], + return new MalFunction(a2, env, a1.asInstanceOf[MalList], (args: List[Any]) => { EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) } @@ -62,14 +64,14 @@ object step6_file { } case _ => { // function call - eval_ast(ast, env) match { + eval_ast(ast, env).asInstanceOf[MalList].value match { case f :: el => { f match { - case fn: types.Function => { + case fn: MalFunction => { env = fn.gen_env(el) ast = fn.ast // continue loop (TCO) } - case fn: ((List[Any]) => Any) @unchecked => { + case fn: Func => { return fn(el) } case _ => { @@ -95,9 +97,11 @@ object step6_file { val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) // core.scala: defined using scala - core.ns.map{case (k: String,v: Any) => { repl_env.set(Symbol(k), v) }} - repl_env.set(Symbol("eval"), (a: List[Any]) => EVAL(a(0), repl_env)) - repl_env.set(Symbol("*ARGV*"), args.slice(1,args.length).toList) + core.ns.map{case (k: String,v: Any) => { + repl_env.set(Symbol(k), new Func(v)) + }} + repl_env.set(Symbol("eval"), new Func((a: List[Any]) => EVAL(a(0), repl_env))) + repl_env.set(Symbol("*ARGV*"), _list(args.slice(1,args.length):_*)) // core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/scala/step7_quote.scala b/scala/step7_quote.scala index 1bb617a..3f0a4d8 100644 --- a/scala/step7_quote.scala +++ b/scala/step7_quote.scala @@ -1,3 +1,5 @@ +import types.{MalList, _list, _list_Q, MalVector, MalHashMap, + Func, MalFunction} import env.Env object step7_quote { @@ -8,39 +10,39 @@ object step7_quote { // eval def is_pair(x: Any): Boolean = { - types._sequential_Q(x) && types._toIter(x).length > 0 + types._sequential_Q(x) && x.asInstanceOf[MalList].value.length > 0 } def quasiquote(ast: Any): Any = { if (!is_pair(ast)) { - return List(Symbol("quote"), ast) + return _list(Symbol("quote"), ast) } else { - val a0 = types._toList(ast)(0) + val a0 = ast.asInstanceOf[MalList](0) if (types._symbol_Q(a0) && a0.asInstanceOf[Symbol].name == "unquote") { - return types._toList(ast)(1) + return ast.asInstanceOf[MalList](1) } else if (is_pair(a0)) { - val a00 = types._toList(a0)(0) + val a00 = a0.asInstanceOf[MalList](0) if (types._symbol_Q(a00) && a00.asInstanceOf[Symbol].name == "splice-unquote") { - return List(Symbol("concat"), - types._toList(a0)(1), - quasiquote(types._toList(ast).drop(1))) + return _list(Symbol("concat"), + a0.asInstanceOf[MalList](1), + quasiquote(ast.asInstanceOf[MalList].drop(1))) } } - return List(Symbol("cons"), - quasiquote(a0), - quasiquote(types._toList(ast).drop(1))) + return _list(Symbol("cons"), + quasiquote(a0), + quasiquote(ast.asInstanceOf[MalList].drop(1))) } } def eval_ast(ast: Any, env: Env): Any = { ast match { case s : Symbol => env.get(s) - case l: List[Any] => l.map(EVAL(_, env)) - case v: Array[Any] => v.map(EVAL(_, env)).toArray - case m: Map[String @unchecked,Any @unchecked] => { - m.map{case (k: String,v: Any) => (k, EVAL(v, env))}.toMap + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} } case _ => ast } @@ -51,17 +53,17 @@ object step7_quote { while (true) { //println("EVAL: " + printer._pr_str(ast,true)) - if (!ast.isInstanceOf[List[Any]]) + if (!_list_Q(ast)) return eval_ast(ast, env) // apply list - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case Symbol("def!") :: a1 :: a2 :: Nil => { return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) } case Symbol("let*") :: a1 :: a2 :: Nil => { val let_env = new Env(env) - for (g <- types._toIter(a1).grouped(2)) { + for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) } env = let_env @@ -74,8 +76,8 @@ object step7_quote { ast = quasiquote(a1) // continue loop (TCO) } case Symbol("do") :: rest => { - eval_ast(rest.slice(0,rest.length-1), env) - ast = ast.asInstanceOf[List[Any]].last // continue loop (TCO) + eval_ast(_list(rest.slice(0,rest.length-1):_*), env) + ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO) } case Symbol("if") :: a1 :: a2 :: rest => { val cond = EVAL(a1, env) @@ -87,7 +89,7 @@ object step7_quote { } } case Symbol("fn*") :: a1 :: a2 :: Nil => { - return new types.Function(a2, env, a1.asInstanceOf[List[Any]], + return new MalFunction(a2, env, a1.asInstanceOf[MalList], (args: List[Any]) => { EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) } @@ -95,14 +97,14 @@ object step7_quote { } case _ => { // function call - eval_ast(ast, env) match { + eval_ast(ast, env).asInstanceOf[MalList].value match { case f :: el => { f match { - case fn: types.Function => { + case fn: MalFunction => { env = fn.gen_env(el) ast = fn.ast // continue loop (TCO) } - case fn: ((List[Any]) => Any) @unchecked => { + case fn: Func => { return fn(el) } case _ => { @@ -128,9 +130,11 @@ object step7_quote { val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) // core.scala: defined using scala - core.ns.map{case (k: String,v: Any) => { repl_env.set(Symbol(k), v) }} - repl_env.set(Symbol("eval"), (a: List[Any]) => EVAL(a(0), repl_env)) - repl_env.set(Symbol("*ARGV*"), args.slice(1,args.length).toList) + core.ns.map{case (k: String,v: Any) => { + repl_env.set(Symbol(k), new Func(v)) + }} + repl_env.set(Symbol("eval"), new Func((a: List[Any]) => EVAL(a(0), repl_env))) + repl_env.set(Symbol("*ARGV*"), _list(args.slice(1,args.length):_*)) // core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/scala/step8_macros.scala b/scala/step8_macros.scala index 8e4cc56..e1a2222 100644 --- a/scala/step8_macros.scala +++ b/scala/step8_macros.scala @@ -1,5 +1,6 @@ +import types.{MalList, _list, _list_Q, MalVector, MalHashMap, + Func, MalFunction} import env.Env -import types.Function object step8_macros { // read @@ -9,39 +10,39 @@ object step8_macros { // eval def is_pair(x: Any): Boolean = { - types._sequential_Q(x) && types._toIter(x).length > 0 + types._sequential_Q(x) && x.asInstanceOf[MalList].value.length > 0 } def quasiquote(ast: Any): Any = { if (!is_pair(ast)) { - return List(Symbol("quote"), ast) + return _list(Symbol("quote"), ast) } else { - val a0 = types._toList(ast)(0) + val a0 = ast.asInstanceOf[MalList](0) if (types._symbol_Q(a0) && a0.asInstanceOf[Symbol].name == "unquote") { - return types._toList(ast)(1) + return ast.asInstanceOf[MalList](1) } else if (is_pair(a0)) { - val a00 = types._toList(a0)(0) + val a00 = a0.asInstanceOf[MalList](0) if (types._symbol_Q(a00) && a00.asInstanceOf[Symbol].name == "splice-unquote") { - return List(Symbol("concat"), - types._toList(a0)(1), - quasiquote(types._toList(ast).drop(1))) + return _list(Symbol("concat"), + a0.asInstanceOf[MalList](1), + quasiquote(ast.asInstanceOf[MalList].drop(1))) } } - return List(Symbol("cons"), - quasiquote(a0), - quasiquote(types._toList(ast).drop(1))) + return _list(Symbol("cons"), + quasiquote(a0), + quasiquote(ast.asInstanceOf[MalList].drop(1))) } } def is_macro_call(ast: Any, env: Env): Boolean = { ast match { - case l: List[Any] => { - if (types._symbol_Q(l(0)) && - env.find(l(0).asInstanceOf[Symbol]) != null) { - env.get(l(0).asInstanceOf[Symbol]) match { - case f: Function => return f.ismacro + case ml: MalList => { + if (types._symbol_Q(ml(0)) && + env.find(ml(0).asInstanceOf[Symbol]) != null) { + env.get(ml(0).asInstanceOf[Symbol]) match { + case f: MalFunction => return f.ismacro case _ => return false } } @@ -54,10 +55,10 @@ object step8_macros { def macroexpand(orig_ast: Any, env: Env): Any = { var ast = orig_ast; while (is_macro_call(ast, env)) { - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case f :: args => { val mac = env.get(f.asInstanceOf[Symbol]) - ast = mac.asInstanceOf[Function](args) + ast = mac.asInstanceOf[MalFunction](args) } case _ => throw new Exception("macroexpand: invalid call") } @@ -68,10 +69,10 @@ object step8_macros { def eval_ast(ast: Any, env: Env): Any = { ast match { case s : Symbol => env.get(s) - case l: List[Any] => l.map(EVAL(_, env)) - case v: Array[Any] => v.map(EVAL(_, env)).toArray - case m: Map[String @unchecked,Any @unchecked] => { - m.map{case (k: String,v: Any) => (k, EVAL(v, env))}.toMap + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} } case _ => ast } @@ -82,20 +83,20 @@ object step8_macros { while (true) { //println("EVAL: " + printer._pr_str(ast,true)) - if (!ast.isInstanceOf[List[Any]]) + if (!_list_Q(ast)) return eval_ast(ast, env) // apply list ast = macroexpand(ast, env) - if (!ast.isInstanceOf[List[Any]]) return ast + if (!_list_Q(ast)) return ast - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case Symbol("def!") :: a1 :: a2 :: Nil => { return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) } case Symbol("let*") :: a1 :: a2 :: Nil => { val let_env = new Env(env) - for (g <- types._toIter(a1).grouped(2)) { + for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) } env = let_env @@ -109,15 +110,15 @@ object step8_macros { } case Symbol("defmacro!") :: a1 :: a2 :: Nil => { val f = EVAL(a2, env) - f.asInstanceOf[Function].ismacro = true + f.asInstanceOf[MalFunction].ismacro = true return env.set(a1.asInstanceOf[Symbol], f) } case Symbol("macroexpand") :: a1 :: Nil => { return macroexpand(a1, env) } case Symbol("do") :: rest => { - eval_ast(rest.slice(0,rest.length-1), env) - ast = ast.asInstanceOf[List[Any]].last // continue loop (TCO) + eval_ast(_list(rest.slice(0,rest.length-1):_*), env) + ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO) } case Symbol("if") :: a1 :: a2 :: rest => { val cond = EVAL(a1, env) @@ -129,7 +130,7 @@ object step8_macros { } } case Symbol("fn*") :: a1 :: a2 :: Nil => { - return new Function(a2, env, types._toList(a1), + return new MalFunction(a2, env, a1.asInstanceOf[MalList], (args: List[Any]) => { EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) } @@ -137,14 +138,14 @@ object step8_macros { } case _ => { // function call - eval_ast(ast, env) match { + eval_ast(ast, env).asInstanceOf[MalList].value match { case f :: el => { f match { - case fn: Function => { + case fn: MalFunction => { env = fn.gen_env(el) ast = fn.ast // continue loop (TCO) } - case fn: ((List[Any]) => Any) @unchecked => { + case fn: Func => { return fn(el) } case _ => { @@ -170,9 +171,11 @@ object step8_macros { val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) // core.scala: defined using scala - core.ns.map{case (k: String,v: Any) => { repl_env.set(Symbol(k), v) }} - repl_env.set(Symbol("eval"), (a: List[Any]) => EVAL(a(0), repl_env)) - repl_env.set(Symbol("*ARGV*"), args.slice(1,args.length).toList) + core.ns.map{case (k: String,v: Any) => { + repl_env.set(Symbol(k), new Func(v)) + }} + repl_env.set(Symbol("eval"), new Func((a: List[Any]) => EVAL(a(0), repl_env))) + repl_env.set(Symbol("*ARGV*"), _list(args.slice(1,args.length):_*)) // core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/scala/step9_try.scala b/scala/step9_try.scala index 200d3cf..02c19be 100644 --- a/scala/step9_try.scala +++ b/scala/step9_try.scala @@ -1,5 +1,6 @@ +import types.{MalList, _list, _list_Q, MalVector, MalHashMap, + Func, MalFunction} import env.Env -import types.Function object step9_try { // read @@ -9,39 +10,39 @@ object step9_try { // eval def is_pair(x: Any): Boolean = { - types._sequential_Q(x) && types._toIter(x).length > 0 + types._sequential_Q(x) && x.asInstanceOf[MalList].value.length > 0 } def quasiquote(ast: Any): Any = { if (!is_pair(ast)) { - return List(Symbol("quote"), ast) + return _list(Symbol("quote"), ast) } else { - val a0 = types._toList(ast)(0) + val a0 = ast.asInstanceOf[MalList](0) if (types._symbol_Q(a0) && a0.asInstanceOf[Symbol].name == "unquote") { - return types._toList(ast)(1) + return ast.asInstanceOf[MalList](1) } else if (is_pair(a0)) { - val a00 = types._toList(a0)(0) + val a00 = a0.asInstanceOf[MalList](0) if (types._symbol_Q(a00) && a00.asInstanceOf[Symbol].name == "splice-unquote") { - return List(Symbol("concat"), - types._toList(a0)(1), - quasiquote(types._toList(ast).drop(1))) + return _list(Symbol("concat"), + a0.asInstanceOf[MalList](1), + quasiquote(ast.asInstanceOf[MalList].drop(1))) } } - return List(Symbol("cons"), - quasiquote(a0), - quasiquote(types._toList(ast).drop(1))) + return _list(Symbol("cons"), + quasiquote(a0), + quasiquote(ast.asInstanceOf[MalList].drop(1))) } } def is_macro_call(ast: Any, env: Env): Boolean = { ast match { - case l: List[Any] => { - if (types._symbol_Q(l(0)) && - env.find(l(0).asInstanceOf[Symbol]) != null) { - env.get(l(0).asInstanceOf[Symbol]) match { - case f: Function => return f.ismacro + case ml: MalList => { + if (types._symbol_Q(ml(0)) && + env.find(ml(0).asInstanceOf[Symbol]) != null) { + env.get(ml(0).asInstanceOf[Symbol]) match { + case f: MalFunction => return f.ismacro case _ => return false } } @@ -54,10 +55,10 @@ object step9_try { def macroexpand(orig_ast: Any, env: Env): Any = { var ast = orig_ast; while (is_macro_call(ast, env)) { - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case f :: args => { val mac = env.get(f.asInstanceOf[Symbol]) - ast = mac.asInstanceOf[Function](args) + ast = mac.asInstanceOf[MalFunction](args) } case _ => throw new Exception("macroexpand: invalid call") } @@ -68,10 +69,10 @@ object step9_try { def eval_ast(ast: Any, env: Env): Any = { ast match { case s : Symbol => env.get(s) - case l: List[Any] => l.map(EVAL(_, env)) - case v: Array[Any] => v.map(EVAL(_, env)).toArray - case m: Map[String @unchecked,Any @unchecked] => { - m.map{case (k: String,v: Any) => (k, EVAL(v, env))}.toMap + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} } case _ => ast } @@ -82,20 +83,20 @@ object step9_try { while (true) { //println("EVAL: " + printer._pr_str(ast,true)) - if (!ast.isInstanceOf[List[Any]]) + if (!_list_Q(ast)) return eval_ast(ast, env) // apply list ast = macroexpand(ast, env) - if (!ast.isInstanceOf[List[Any]]) return ast + if (!_list_Q(ast)) return ast - ast.asInstanceOf[List[Any]] match { + ast.asInstanceOf[MalList].value match { case Symbol("def!") :: a1 :: a2 :: Nil => { return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) } case Symbol("let*") :: a1 :: a2 :: Nil => { val let_env = new Env(env) - for (g <- types._toIter(a1).grouped(2)) { + for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) } env = let_env @@ -109,7 +110,7 @@ object step9_try { } case Symbol("defmacro!") :: a1 :: a2 :: Nil => { val f = EVAL(a2, env) - f.asInstanceOf[Function].ismacro = true + f.asInstanceOf[MalFunction].ismacro = true return env.set(a1.asInstanceOf[Symbol], f) } case Symbol("macroexpand") :: a1 :: Nil => { @@ -120,7 +121,7 @@ object step9_try { return EVAL(a1, env) } catch { case t: Throwable => { - rest(0) match { + rest(0).asInstanceOf[MalList].value match { case List(Symbol("catch*"), a21, a22) => { val exc: Any = t match { case mex: types.MalException => mex.value @@ -136,8 +137,8 @@ object step9_try { } } case Symbol("do") :: rest => { - eval_ast(rest.slice(0,rest.length-1), env) - ast = ast.asInstanceOf[List[Any]].last // continue loop (TCO) + eval_ast(_list(rest.slice(0,rest.length-1):_*), env) + ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO) } case Symbol("if") :: a1 :: a2 :: rest => { val cond = EVAL(a1, env) @@ -149,7 +150,7 @@ object step9_try { } } case Symbol("fn*") :: a1 :: a2 :: Nil => { - return new Function(a2, env, types._toList(a1), + return new MalFunction(a2, env, a1.asInstanceOf[MalList], (args: List[Any]) => { EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) } @@ -157,14 +158,14 @@ object step9_try { } case _ => { // function call - eval_ast(ast, env) match { + eval_ast(ast, env).asInstanceOf[MalList].value match { case f :: el => { f match { - case fn: Function => { + case fn: MalFunction => { env = fn.gen_env(el) ast = fn.ast // continue loop (TCO) } - case fn: ((List[Any]) => Any) @unchecked => { + case fn: Func => { return fn(el) } case _ => { @@ -190,9 +191,11 @@ object step9_try { val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) // core.scala: defined using scala - core.ns.map{case (k: String,v: Any) => { repl_env.set(Symbol(k), v) }} - repl_env.set(Symbol("eval"), (a: List[Any]) => EVAL(a(0), repl_env)) - repl_env.set(Symbol("*ARGV*"), args.slice(1,args.length).toList) + core.ns.map{case (k: String,v: Any) => { + repl_env.set(Symbol(k), new Func(v)) + }} + repl_env.set(Symbol("eval"), new Func((a: List[Any]) => EVAL(a(0), repl_env))) + repl_env.set(Symbol("*ARGV*"), _list(args.slice(1,args.length):_*)) // core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") diff --git a/scala/stepA_interop.scala b/scala/stepA_interop.scala new file mode 100644 index 0000000..2ba6b5a --- /dev/null +++ b/scala/stepA_interop.scala @@ -0,0 +1,229 @@ +import types.{MalList, _list, _list_Q, MalVector, MalHashMap, + Func, MalFunction} +import env.Env + +object stepA_interop { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + def is_pair(x: Any): Boolean = { + types._sequential_Q(x) && x.asInstanceOf[MalList].value.length > 0 + } + + def quasiquote(ast: Any): Any = { + if (!is_pair(ast)) { + return _list(Symbol("quote"), ast) + } else { + val a0 = ast.asInstanceOf[MalList](0) + if (types._symbol_Q(a0) && + a0.asInstanceOf[Symbol].name == "unquote") { + return ast.asInstanceOf[MalList](1) + } else if (is_pair(a0)) { + val a00 = a0.asInstanceOf[MalList](0) + if (types._symbol_Q(a00) && + a00.asInstanceOf[Symbol].name == "splice-unquote") { + return _list(Symbol("concat"), + a0.asInstanceOf[MalList](1), + quasiquote(ast.asInstanceOf[MalList].drop(1))) + } + } + return _list(Symbol("cons"), + quasiquote(a0), + quasiquote(ast.asInstanceOf[MalList].drop(1))) + } + } + + def is_macro_call(ast: Any, env: Env): Boolean = { + ast match { + case ml: MalList => { + if (types._symbol_Q(ml(0)) && + env.find(ml(0).asInstanceOf[Symbol]) != null) { + env.get(ml(0).asInstanceOf[Symbol]) match { + case f: MalFunction => return f.ismacro + case _ => return false + } + } + return false + } + case _ => return false + } + } + + def macroexpand(orig_ast: Any, env: Env): Any = { + var ast = orig_ast; + while (is_macro_call(ast, env)) { + ast.asInstanceOf[MalList].value match { + case f :: args => { + val mac = env.get(f.asInstanceOf[Symbol]) + ast = mac.asInstanceOf[MalFunction](args) + } + case _ => throw new Exception("macroexpand: invalid call") + } + } + ast + } + + def eval_ast(ast: Any, env: Env): Any = { + ast match { + case s : Symbol => env.get(s) + case v: MalVector => v.map(EVAL(_, env)) + case l: MalList => l.map(EVAL(_, env)) + case m: MalHashMap => { + m.map{case (k: String,v: Any) => (k, EVAL(v, env))} + } + case _ => ast + } + } + + def EVAL(orig_ast: Any, orig_env: Env): Any = { + var ast = orig_ast; var env = orig_env; + while (true) { + + //println("EVAL: " + printer._pr_str(ast,true)) + if (!_list_Q(ast)) + return eval_ast(ast, env) + + // apply list + ast = macroexpand(ast, env) + if (!_list_Q(ast)) return ast + + ast.asInstanceOf[MalList].value match { + case Symbol("def!") :: a1 :: a2 :: Nil => { + return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) + } + case Symbol("let*") :: a1 :: a2 :: Nil => { + val let_env = new Env(env) + for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { + let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) + } + env = let_env + ast = a2 // continue loop (TCO) + } + case Symbol("quote") :: a1 :: Nil => { + return a1 + } + case Symbol("quasiquote") :: a1 :: Nil => { + ast = quasiquote(a1) // continue loop (TCO) + } + case Symbol("defmacro!") :: a1 :: a2 :: Nil => { + val f = EVAL(a2, env) + f.asInstanceOf[MalFunction].ismacro = true + return env.set(a1.asInstanceOf[Symbol], f) + } + case Symbol("macroexpand") :: a1 :: Nil => { + return macroexpand(a1, env) + } + case Symbol("try*") :: a1 :: rest => { + try { + return EVAL(a1, env) + } catch { + case t: Throwable => { + rest(0).asInstanceOf[MalList].value match { + case List(Symbol("catch*"), a21, a22) => { + val exc: Any = t match { + case mex: types.MalException => mex.value + case _ => t.getMessage + } + return EVAL(a22, new Env(env, + List(a21).iterator, + List(exc).iterator)) + } + } + throw t + } + } + } + case Symbol("do") :: rest => { + eval_ast(_list(rest.slice(0,rest.length-1):_*), env) + ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO) + } + case Symbol("if") :: a1 :: a2 :: rest => { + val cond = EVAL(a1, env) + if (cond == null || cond == false) { + if (rest.length == 0) return null + ast = rest(0) // continue loop (TCO) + } else { + ast = a2 // continue loop (TCO) + } + } + case Symbol("fn*") :: a1 :: a2 :: Nil => { + return new MalFunction(a2, env, a1.asInstanceOf[MalList], + (args: List[Any]) => { + EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) + } + ) + } + case _ => { + // function call + eval_ast(ast, env).asInstanceOf[MalList].value match { + case f :: el => { + f match { + case fn: MalFunction => { + env = fn.gen_env(el) + ast = fn.ast // continue loop (TCO) + } + case fn: Func => { + return fn(el) + } + case _ => { + throw new Exception("attempt to call non-function: " + f) + } + } + } + case _ => throw new Exception("invalid apply") + } + } + } + } + } + + // print + def PRINT(exp: Any): String = { + printer._pr_str(exp, true) + } + + // repl + def main(args: Array[String]) = { + val repl_env: Env = new Env() + val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) + + // core.scala: defined using scala + core.ns.map{case (k: String,v: Any) => { + repl_env.set(Symbol(k), new Func(v)) + }} + repl_env.set(Symbol("eval"), new Func((a: List[Any]) => EVAL(a(0), repl_env))) + repl_env.set(Symbol("*ARGV*"), _list(args.slice(1,args.length):_*)) + + // core.mal: defined using the language itself + REP("(def! *host-language* \"scala\")") + REP("(def! not (fn* (a) (if a false true)))") + REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") + REP("(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("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") + + + if (args.length > 0) { + REP("(load-file \"" + args(0) + "\")") + System.exit(0) + } + + // repl loop + REP("(println (str \"Mal [\" *host-language* \"]\"))") + var line:String = null + while ({line = readLine("user> "); line != null}) { + try { + println(REP(line)) + } catch { + case e : Throwable => { + println("Error: " + e.getMessage) + println(" " + e.getStackTrace.mkString("\n ")) + } + } + } + } +} + +// vim: ts=2:sw=2 diff --git a/scala/types.scala b/scala/types.scala index cf93ac2..91af068 100644 --- a/scala/types.scala +++ b/scala/types.scala @@ -2,6 +2,7 @@ import scala.collection._ import scala.collection.generic._ import env.Env +import printer._pr_str object types { class MalException(msg: String) extends Throwable(msg) { @@ -11,40 +12,25 @@ object types { def _toIter(obj: Any): Iterator[Any] = { obj match { - case l: List[Any] => l.iterator - case v: Array[Any] => v.iterator + case v: MalVector => v.value.iterator + case l: MalList => l.value.iterator case null => Iterator.empty case _ => throw new Exception("cannot convert " + obj.getClass + " to iterator") } } - def _toList(obj: Any): List[Any] = { - obj match { - case l: List[Any] => l - case v: Array[Any] => v.toList - case null => List() - case _ => throw new Exception("cannot convert " + - obj.getClass + " to list") - } - } - def _equal_Q(a: Any, b: Any): Any = { (a, b) match { - case (a: List[Any], b: List[Any]) => a == b - case (a: Array[Any], b: Array[Any]) => a.deep == b.deep - case (a: List[Any], b: Array[Any]) => a == b.deep - case (a: Array[Any], b: List[Any]) => a.deep == b - case (a: Map[String @unchecked,Any @unchecked], - b: Map[String @unchecked,Any @unchecked]) => a == b + case (a: MalList, b: MalList) => a.value == b.value + case (a: MalHashMap, b: MalHashMap) => a.value == b.value case _ => a == b } } def _sequential_Q(a: Any): Boolean = { a match { - case l: List[Any] => true - case v: Array[Any] => true + case l: MalList => true case _ => false } } @@ -53,64 +39,152 @@ object types { // Lists - - class MalList[A](seq : A*) extends Traversable[A] - with GenericTraversableTemplate[A, MalList] - with TraversableLike[A, MalList[A]] { + class MalList(seq: Any*) { + var value: List[Any] = seq.toList var meta: Any = null - override def companion = MalList - def foreach[U](f: A => U) = seq.foreach(f) - } - object MalList extends TraversableFactory[MalList] { - implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MalList[A]] = new GenericCanBuildFrom[A] - def newBuilder[A] = new scala.collection.mutable.LazyBuilder[A,MalList[A]] { - def result = { - val data = parts.foldLeft(List[A]()){(l,n) => l ++ n} - new MalList(data:_*) - } + + override def clone(): MalList = { + val new_ml = new MalList() + new_ml.value = value + new_ml.meta = meta + new_ml + } + + def apply(idx: Int): Any = value(idx) + def map(f: Any => Any) = new MalList(value.map(f):_*) + def drop(cnt: Int) = new MalList(value.drop(cnt):_*) + def :+(that: Any) = new MalList((value :+ that):_*) + def +:(that: Any) = new MalList((that +: value):_*) + + override def toString() = { + "(" + value.map(_pr_str(_, true)).mkString(" ") + ")" + } + def toString(print_readably: Boolean) = { + "(" + value.map(_pr_str(_, print_readably)).mkString(" ") + ")" } } + def _list(seq: Any*) = { + new MalList(seq:_*) + } + def _list_Q(obj: Any) = { + obj.isInstanceOf[MalList] && !obj.isInstanceOf[MalVector] + } // Vectors - class MalVector[A](seq : A*) extends Traversable[A] - with GenericTraversableTemplate[A, MalVector] - with TraversableLike[A, MalVector[A]] { - var meta: Any = null - override def companion = MalVector - def foreach[U](f: A => U) = seq.foreach(f) + class MalVector(seq: Any*) extends MalList(seq:_*) { + override def clone() = { + val new_mv = new MalVector() + new_mv.value = value + new_mv.meta = meta + new_mv + } + + override def map(f: Any => Any) = new MalVector(value.map(f):_*) + override def drop(cnt: Int) = new MalVector(value.drop(cnt):_*) + + override def toString() = { + "[" + value.map(_pr_str(_, true)).mkString(" ") + "]" + } + override def toString(print_readably: Boolean) = { + "[" + value.map(_pr_str(_, print_readably)).mkString(" ") + "]" + } + } + def _vector(seq: Any*) = { + new MalVector(seq:_*) } - object MalVector extends TraversableFactory[MalVector] { - implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MalVector[A]] = new GenericCanBuildFrom[A] - def newBuilder[A] = new scala.collection.mutable.LazyBuilder[A,MalVector[A]] { - def result = { - val data = parts.foldLeft(List[A]()){(l,n) => l ++ n} - new MalVector(data:_*) - } + def _vector_Q(obj: Any) = { + obj.isInstanceOf[MalVector] + } + + + // Hash Maps + class MalHashMap(seq: Any*) { + var flat_value: List[Any] = seq.toList + var value: Map[String,Any] = flat_value.grouped(2).map( + (kv: List[Any]) => (kv(0).asInstanceOf[String], kv(1))).toMap + var meta: Any = null + + override def clone(): MalHashMap = { + val new_hm = new MalHashMap() + new_hm.value = value + new_hm.flat_value = flat_value + new_hm.meta = meta + new_hm + } + + def keys(): MalList = new MalList(value.keys.toSeq:_*) + def vals(): MalList = new MalList(value.values.toSeq:_*) + + def apply(key: String): Any = value(key) + def map(f: ((String, Any)) => (String, Any)) = { + val res = value.map(f).map{case (k,v) => List(k,v)} + new MalHashMap(res.flatten.toSeq:_*) + } + def filterKeys(f: String => Boolean) = { + val res = value.filterKeys(f).map{case (k,v) => List(k,v)} + new MalHashMap(res.flatten.toSeq:_*) + } + def ++(that: MalHashMap) = { + new MalHashMap((flat_value ++ that.flat_value):_*) + } + + override def toString() = { + "{" + flat_value.map(_pr_str(_, true)).mkString(" ") + "}" + } + def toString(print_readably: Boolean) = { + "{" + flat_value.map(_pr_str(_, print_readably)).mkString(" ") + "}" } } + def _hash_map(seq: Any*) = { + new MalHashMap(seq:_*) + } + def _hash_map_Q(obj: Any) = { + obj.isInstanceOf[MalHashMap] + } + + // Function types - class Function(_ast: Any, _env: Env, _params: List[Any], - fn: ((List[Any]) => Any)) { + class Func(_fn: ((List[Any]) => Any)) { + val fn = _fn + var meta: Any = null + + override def clone(): Func = { + val new_fn = new Func(fn) + new_fn.meta = meta + new_fn + } + + def apply(args: List[Any]): Any = fn(args) + } + + class MalFunction(_ast: Any, _env: Env, _params: MalList, + fn: ((List[Any]) => Any)) { val ast = _ast val env = _env val params = _params var ismacro = false + var meta: Any = null - def apply(args: List[Any]): Any = { - fn(args) + override def clone(): MalFunction = { + val new_fn = new MalFunction(ast, env, params, fn) + new_fn.ismacro = ismacro + new_fn.meta = meta + new_fn } + def apply(args: List[Any]): Any = fn(args) + def gen_env(args: List[Any]): Env = { - return new Env(env, params.iterator, args.iterator) + return new Env(env, params.value.iterator, args.iterator) } } def _apply(f: Any, args: List[Any]): Any = { f match { - case fn: types.Function => fn(args) - case fn: ((List[Any]) => Any) @unchecked => fn(args) + case fn: types.MalFunction => fn(args) + case fn: Func => fn(args) case _ => throw new Exception("attempt to call non-function") } } diff --git a/tests/step6_file.mal b/tests/step6_file.mal index f102c34..f76e4f5 100644 --- a/tests/step6_file.mal +++ b/tests/step6_file.mal @@ -27,6 +27,13 @@ ;=>2 ;; +;; Testing that *ARGV* exists and is an empty list +(list? *ARGV*) +;=>true +*ARGV* +;=>() + +;; ;; -------- Optional Functionality -------- (load-file "../tests/incB.mal") |
