diff options
| author | Joel Martin <github@martintribe.org> | 2014-12-18 23:21:39 -0600 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-09 16:16:51 -0600 |
| commit | 821930dbd9febf45fad25aefa9bc64e8ace4c737 (patch) | |
| tree | 8afc5c6c99b5d670fefc75dbcc1b71acda1e2e35 | |
| parent | b8ee29b22fbaa7a01f2754b4d6dd9af52e02017c (diff) | |
| download | mal-821930dbd9febf45fad25aefa9bc64e8ace4c737.tar.gz mal-821930dbd9febf45fad25aefa9bc64e8ace4c737.zip | |
Scala: all steps 0-9 but no metadata.
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | docs/TODO | 4 | ||||
| -rw-r--r-- | scala/build.sbt | 15 | ||||
| -rw-r--r-- | scala/core.scala | 178 | ||||
| -rw-r--r-- | scala/env.scala | 40 | ||||
| -rw-r--r-- | scala/printer.scala | 45 | ||||
| -rw-r--r-- | scala/reader.scala | 88 | ||||
| -rw-r--r-- | scala/step0_repl.scala | 33 | ||||
| -rw-r--r-- | scala/step1_read_print.scala | 39 | ||||
| -rw-r--r-- | scala/step2_eval.scala | 73 | ||||
| -rw-r--r-- | scala/step3_env.scala | 93 | ||||
| -rw-r--r-- | scala/step4_if_fn_do.scala | 106 | ||||
| -rw-r--r-- | scala/step5_tco.scala | 117 | ||||
| -rw-r--r-- | scala/step6_file.scala | 126 | ||||
| -rw-r--r-- | scala/step7_quote.scala | 159 | ||||
| -rw-r--r-- | scala/step8_macros.scala | 204 | ||||
| -rw-r--r-- | scala/step9_try.scala | 224 | ||||
| -rw-r--r-- | scala/types.scala | 128 |
20 files changed, 1690 insertions, 1 deletions
@@ -35,3 +35,5 @@ rust/.cargo r/lib vb/*.exe vb/*.dll +scala/target +scala/project @@ -11,7 +11,7 @@ PYTHON = python # IMPLS = bash c clojure coffee cs go java js make mal perl php ps \ - python r ruby rust vb + python r ruby rust scala vb step0 = step0_repl step1 = step1_read_print @@ -65,6 +65,7 @@ python_STEP_TO_PROG = python/$($(1)).py r_STEP_TO_PROG = r/$($(1)).r ruby_STEP_TO_PROG = ruby/$($(1)).rb rust_STEP_TO_PROG = rust/target/$($(1)) +scala_STEP_TO_PROG = scala/$($(1)).scala vb_STEP_TO_PROG = vb/$($(1)).exe @@ -85,6 +86,7 @@ python_RUNSTEP = $(PYTHON) ../$(2) $(3) r_RUNSTEP = Rscript ../$(2) $(3) ruby_RUNSTEP = ruby ../$(2) $(3) rust_RUNSTEP = ../$(2) $(3) +scala_RUNSTEP = sbt 'run-main $($(1))$(if $(3), $(3),)' vb_RUNSTEP = mono ../$(2) --raw $(3) # Extra options to pass to runtest.py @@ -22,6 +22,7 @@ language. Mal is implemented from scratch in 18 different languages: * R * Ruby * Rust +* Scala * Visual Basic.NET @@ -214,6 +215,18 @@ cargo build ./target/stepX_YYY ``` +### Scala ### + +Install scala and sbt (http://www.scala-sbt.org/0.13/tutorial/Installing-sbt-on-Linux.html): + +``` +cd scala +sbt 'run-main stepX_YYY' + # OR +sbt compile +scala -classpath target/scala*/classes stepX_YYY +``` + ### Visual Basic.NET ### The VB.NET implementation of mal has been tested on Linux using the Mono @@ -122,6 +122,10 @@ Future Implementations: - http://groovy-lang.org/learn.html - http://groovy-lang.org/structure.html + - Scala + - aptitude install scala + - http://learnxinyminutes.com/docs/scala/ + - Visual Basic aptitude install mono-vbnc diff --git a/scala/build.sbt b/scala/build.sbt new file mode 100644 index 0000000..d2a2802 --- /dev/null +++ b/scala/build.sbt @@ -0,0 +1,15 @@ +lazy val root = (project in file(".")). + settings( + name := "mal", + version := "0.1", + scalaVersion := "2.11.4" + ) + +// Suppress message for command line execution + +onLoadMessage := "" + +showSuccess := false + +logLevel in runMain := Level.Warn + diff --git a/scala/core.scala b/scala/core.scala new file mode 100644 index 0000000..6310f89 --- /dev/null +++ b/scala/core.scala @@ -0,0 +1,178 @@ +import scala.collection.mutable +import scala.io.Source + +import printer._pr_list + +object core { + def mal_throw(a: List[Any]) = { + throw new types.MalException(printer._pr_str(a(0))).init(a(0)) + } + + // Scalar functions + def keyword(a: List[Any]) = { + "\u029e" + a(0).asInstanceOf[String] + } + + def keyword_Q(a: List[Any]) = { + a(0) match { + case s: String => s(0) == '\u029e' + case _ => false + } + } + + // 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") + } + + // 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]]) + } + + def dissoc(a: List[Any]): Any = { + var kSet = types._toList(a.drop(1)).toSet + a(0).asInstanceOf[Map[String,Any]] + .filterKeys{ !kSet.contains(_) } + } + + def get(a: List[Any]): Any = { + val hm = a(0).asInstanceOf[Map[String,Any]] + val key = a(1).asInstanceOf[String] + if (hm != null && hm.contains(key)) hm(key) else null + } + + def contains_Q(a: List[Any]): Any = { + a(0).asInstanceOf[Map[String,Any]] + .contains(a(1).asInstanceOf[String]) + } + + + // sequence functions + def concat(a: List[Any]): List[Any] = { + (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] + if (idx < lst.length) { + lst(idx) + } else { + throw new Exception("nth: index out of range") + } + } + + def first(a: List[Any]): Any = { + val lst = types._toList(a(0)) + if (lst.length > 0) lst(0) else null + } + + + 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)) + types._apply(f, args) + } + case _ => throw new Exception("invalid apply call") + } + } + + def do_map(a: List[Any]): Any = { + a match { + case f :: seq :: Nil => { + types._toList(seq).map(x => types._apply(f,List(x))) + } + case _ => throw new Exception("invalid map call") + } + } + + + // atom functions + def reset_BANG(a: List[Any]): Any = { + a(0).asInstanceOf[types.Atom].value = a(1) + a(1) + } + + def swap_BANG(a: List[Any]): Any = { + a match { + case a0 :: f :: rest => { + val atm = a0.asInstanceOf[types.Atom] + val args = atm.value +: rest + atm.value = types._apply(f, args) + atm.value + } + case _ => throw new Exception("invalid swap! call") + } + } + + + val ns: Map[String, Any] = Map( + "=" -> ((a: List[Any]) => types._equal_Q(a(0), a(1))), + "throw" -> mal_throw _, + "nil?" -> ((a: List[Any]) => a(0) == null), + "true?" -> ((a: List[Any]) => a(0) == true), + "false?" -> ((a: List[Any]) => a(0) == false), + "symbol" -> ((a: List[Any]) => Symbol(a(0).asInstanceOf[String])), + "symbol?" -> ((a: List[Any]) => a(0).isInstanceOf[Symbol]), + "keyword" -> keyword _, + "keyword?" -> keyword_Q _, + + "pr-str" -> ((a: List[Any]) => _pr_list(a, true, " ")), + "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}), + "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]), + "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), + + "sequential?" -> ((a: List[Any]) => types._sequential_Q(a(0))), + "cons" -> ((a: List[Any]) => a(0) +: types._toList(a(1))), + "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), + "apply" -> apply _, + "map" -> do_map _, + + "with-meta" -> ((a: List[Any]) => null), + "meta" -> ((a: List[Any]) => null), + "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), + "reset!" -> reset_BANG _, + "swap!" -> swap_BANG _ + ) +} + +// vim:ts=2:sw=2 diff --git a/scala/env.scala b/scala/env.scala new file mode 100644 index 0000000..9b6cff8 --- /dev/null +++ b/scala/env.scala @@ -0,0 +1,40 @@ +import scala.collection.mutable + +object env { + class Env(outer: Env = null, + binds: Iterator[Any] = null, + exprs: Iterator[Any] = null) { + val data: mutable.Map[Symbol, Any] = mutable.Map() + if (binds != null && exprs != null) { + binds.foreach(b => { + val k = b.asInstanceOf[Symbol] + if (k == '&) { + data(binds.next().asInstanceOf[Symbol]) = exprs.toList + } else { + data(k) = exprs.next() + } + }) + } + + def find(key: Symbol): Env = { + if (data.contains(key)) { + this + } else if (outer != null) { + outer.find(key) + } else { + null + } + } + def set(key: Symbol, value: Any): Any = { + data(key) = value + value + } + def get(key: Symbol): Any = { + val env = find(key) + if (env == null) throw new Exception("'" + key.name + "' not found") + env.data(key) + } + } +} + +// vim:ts=2:sw=2 diff --git a/scala/printer.scala b/scala/printer.scala new file mode 100644 index 0000000..74b1433 --- /dev/null +++ b/scala/printer.scala @@ -0,0 +1,45 @@ +import types.Function + +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 s: String => { + if (s.length > 0 && s(0) == '\u029e') { + ":" + s.substring(1,s.length) + } else if (_r) { + //println("here1: " + s) + "\"" + s.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\n", "\\n") + "\"" + } else { + s + } + } + case Symbol(s) => s + case a: types.Atom => "(atom " + a.value + ")" + case null => "nil" + case _ => { + if (obj.isInstanceOf[Function]) { + val f = obj.asInstanceOf[Function] + "<function (fn* " + _pr_str(f.params) + " " + _pr_str(f.ast) + ")>" + } else { + obj.toString + } + } + } + } + + def _pr_list(lst: List[Any], print_readably: Boolean = true, + sep: String = " "): String = { + lst.map{_pr_str(_, print_readably)}.mkString(sep) + } +} + +// vim: ts=2:sw=2 diff --git a/scala/reader.scala b/scala/reader.scala new file mode 100644 index 0000000..fc16f28 --- /dev/null +++ b/scala/reader.scala @@ -0,0 +1,88 @@ +import scala.util.matching.Regex + +object reader { + + class Reader (tokens: Array[String]) { + var data = tokens + var position: Int = 0 + def peek(): String = { + if (position >= data.length) return(null) + data(position) + } + def next(): String = { + if (position >= data.length) return(null) + position = position + 1 + data(position-1) + } + } + + def tokenize(str: String): Array[String] = { + val re = """[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)""".r + re.findAllMatchIn(str).map{ _.group(1) } + .filter{ s => s != "" && s(0) != ';' } + .toArray + } + + def parse_str(s: String): String = { + s.replace("\\\"", "\"").replace("\\n", "\n") + } + + def read_atom(rdr: Reader): Any = { + val token = rdr.next() + val re_int = """^(-?[0-9]+)$""".r + val re_flt = """^(-?[0-9][0-9.]*)$""".r + val re_str = """^"(.*)"$""".r + val re_key = """^:(.*)$""".r + return token match { + case re_int(i) => i.toInt // integer + case re_flt(f) => f.toDouble // float + case re_str(s) => parse_str(s) // string + case re_key(k) => "\u029e" + k // keyword + case "nil" => null + case "true" => true + case "false" => false + case _ => Symbol(token) // symbol + } + } + + def read_list(rdr: Reader, + start: String = "(", end: String = ")"): List[Any] = { + var ast: List[Any] = List() + var token = rdr.next() + if (token != start) throw new Exception("expected '" + start + "', got EOF") + while ({token = rdr.peek(); token != end}) { + if (token == null) throw new Exception("expected '" + end + "', got EOF") + ast = ast :+ read_form(rdr) + } + rdr.next() + ast + } + + 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; val meta = 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 "]" => throw new Exception("unexpected ']')") + case "{" => types._hash_map(read_list(rdr, "{", "}")) + case "}" => throw new Exception("unexpected '}')") + case _ => read_atom(rdr) + } + } + + def read_str(str: String): Any = { + val tokens = tokenize(str) + if (tokens.length == 0) return null + return read_form(new Reader(tokens)) + } +} + +// vim: ts=2:sw=2 diff --git a/scala/step0_repl.scala b/scala/step0_repl.scala new file mode 100644 index 0000000..5ec94fe --- /dev/null +++ b/scala/step0_repl.scala @@ -0,0 +1,33 @@ +object step0_repl { + def READ(str: String): String = { + str + } + + def EVAL(str: String, env: String): String = { + str + } + + def PRINT(str: String): String = { + str + } + + def REP(str: String): String = { + PRINT(EVAL(READ(str), "")) + } + + def main(args: Array[String]) { + var line:String = null + while ({line = readLine("user> "); line != null}) { + try { + println(REP(line)) + } catch { + case e : Exception => { + println("Error: " + e.getMessage) + println(" " + e.getStackTrace.mkString("\n ")) + } + } + } + } +} + +// vim: ts=2:sw=2 diff --git a/scala/step1_read_print.scala b/scala/step1_read_print.scala new file mode 100644 index 0000000..7485eb0 --- /dev/null +++ b/scala/step1_read_print.scala @@ -0,0 +1,39 @@ +import reader.tokenize + +object step1_read_print { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + def EVAL(ast: Any, env: String): Any = { + ast + } + + // print + def PRINT(exp: Any): String = { + printer._pr_str(exp, true) + } + + // repl + def main(args: Array[String]) = { + val REP = (str: String) => { + PRINT(EVAL(READ(str), "")) + } + + var line:String = null + while ({line = readLine("user> "); line != null}) { + try { + println(REP(line)) + } catch { + case e : Exception => { + println("Error: " + e.getMessage) + println(" " + e.getStackTrace.mkString("\n ")) + } + } + } + } +} + +// vim: ts=2:sw=2 diff --git a/scala/step2_eval.scala b/scala/step2_eval.scala new file mode 100644 index 0000000..b7b209d --- /dev/null +++ b/scala/step2_eval.scala @@ -0,0 +1,73 @@ +import reader.tokenize + +object step2_eval { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // 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 _ => ast + } + } + + def EVAL(ast: Any, env: Map[Symbol,Any]): Any = { + //println("EVAL: " + printer._pr_str(ast,true)) + if (!ast.isInstanceOf[List[Any]]) + return eval_ast(ast, env) + + // apply list + eval_ast(ast, env) match { + case f :: el => { + var fn: List[Any] => Any = null + try { + fn = f.asInstanceOf[(List[Any]) => Any] + } catch { + case _: Throwable => + throw new Exception("attempt to call non-function") + } + return fn(el) + } + 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: 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])) + val REP = (str: String) => { + PRINT(EVAL(READ(str), repl_env)) + } + + var line:String = null + while ({line = readLine("user> "); line != null}) { + try { + println(REP(line)) + } catch { + case e : Exception => { + println("Error: " + e.getMessage) + println(" " + e.getStackTrace.mkString("\n ")) + } + } + } + } +} + +// vim: ts=2:sw=2 diff --git a/scala/step3_env.scala b/scala/step3_env.scala new file mode 100644 index 0000000..12d335d --- /dev/null +++ b/scala/step3_env.scala @@ -0,0 +1,93 @@ +import env.Env + +object step3_env { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + 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 _ => ast + } + } + + def EVAL(ast: Any, env: Env): Any = { + //println("EVAL: " + printer._pr_str(ast,true)) + if (!ast.isInstanceOf[List[Any]]) + return eval_ast(ast, env) + + // apply list + ast.asInstanceOf[List[Any]] 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)) { + 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 { + case f :: el => { + var fn: List[Any] => Any = null + try { + fn = f.asInstanceOf[(List[Any]) => Any] + } catch { + case _: Throwable => + throw new Exception("attempt to call non-function") + } + return fn(el) + } + 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() + 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]) + val REP = (str: String) => { + PRINT(EVAL(READ(str), repl_env)) + } + + var line:String = null + while ({line = readLine("user> "); line != null}) { + try { + println(REP(line)) + } catch { + case e : Exception => { + println("Error: " + e.getMessage) + println(" " + e.getStackTrace.mkString("\n ")) + } + } + } + } +} + +// vim: ts=2:sw=2 diff --git a/scala/step4_if_fn_do.scala b/scala/step4_if_fn_do.scala new file mode 100644 index 0000000..a7a6b22 --- /dev/null +++ b/scala/step4_if_fn_do.scala @@ -0,0 +1,106 @@ +import env.Env + +object step4_if_fn_do { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + 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 _ => ast + } + } + + def EVAL(ast: Any, env: Env): Any = { + //println("EVAL: " + printer._pr_str(ast,true)) + if (!ast.isInstanceOf[List[Any]]) + return eval_ast(ast, env) + + // apply list + ast.asInstanceOf[List[Any]] 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)) { + 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 + } + case Symbol("if") :: a1 :: a2 :: rest => { + val cond = EVAL(a1, env) + if (cond == null || cond == false) { + if (rest.length == 0) return null + return EVAL(rest(0), env) + } else { + return EVAL(a2, env) + } + } + case Symbol("fn*") :: a1 :: a2 :: Nil => { + return (args: List[Any]) => { + EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) + } + } + case _ => { + // function call + eval_ast(ast, env) match { + case f :: el => { + var fn: List[Any] => Any = null + try { + fn = f.asInstanceOf[(List[Any]) => Any] + } catch { + case _: Throwable => + throw new Exception("attempt to call non-function") + } + return fn(el) + } + 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), v) }} + + // core.mal: defined using the language itself + REP("(def! not (fn* (a) (if a false true)))") + + var line:String = null + while ({line = readLine("user> "); line != null}) { + try { + println(REP(line)) + } catch { + case e : Exception => { + println("Error: " + e.getMessage) + println(" " + e.getStackTrace.mkString("\n ")) + } + } + } + } +} + +// vim: ts=2:sw=2 diff --git a/scala/step5_tco.scala b/scala/step5_tco.scala new file mode 100644 index 0000000..d54eaa7 --- /dev/null +++ b/scala/step5_tco.scala @@ -0,0 +1,117 @@ +import env.Env + +object step5_tco { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + 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 _ => 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 (!ast.isInstanceOf[List[Any]]) + return eval_ast(ast, env) + + // apply list + ast.asInstanceOf[List[Any]] 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)) { + 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) + } + 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 types.Function(a2, env, a1.asInstanceOf[List[Any]], + (args: List[Any]) => { + EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) + } + ) + } + case _ => { + // function call + eval_ast(ast, env) match { + case f :: el => { + f match { + case fn: types.Function => { + env = fn.gen_env(el) + ast = fn.ast // continue loop (TCO) + } + case fn: ((List[Any]) => Any) @unchecked => { + return fn(el) + } + case _ => { + throw new Exception("attempt to call non-function") + } + } + } + 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), v) }} + + // core.mal: defined using the language itself + REP("(def! not (fn* (a) (if a false true)))") + + 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/step6_file.scala b/scala/step6_file.scala new file mode 100644 index 0000000..0b4d228 --- /dev/null +++ b/scala/step6_file.scala @@ -0,0 +1,126 @@ +import env.Env + +object step6_file { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + 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 _ => 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 (!ast.isInstanceOf[List[Any]]) + return eval_ast(ast, env) + + // apply list + ast.asInstanceOf[List[Any]] 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)) { + 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) + } + 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 types.Function(a2, env, a1.asInstanceOf[List[Any]], + (args: List[Any]) => { + EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) + } + ) + } + case _ => { + // function call + eval_ast(ast, env) match { + case f :: el => { + f match { + case fn: types.Function => { + env = fn.gen_env(el) + ast = fn.ast // continue loop (TCO) + } + case fn: ((List[Any]) => Any) @unchecked => { + 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), 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.mal: defined using the language itself + REP("(def! not (fn* (a) (if a false true)))") + REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") + + if (args.length > 0) { + REP("(load-file \"" + args(0) + "\")") + System.exit(0) + } + + // repl loop + 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/step7_quote.scala b/scala/step7_quote.scala new file mode 100644 index 0000000..1bb617a --- /dev/null +++ b/scala/step7_quote.scala @@ -0,0 +1,159 @@ +import env.Env + +object step7_quote { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + def is_pair(x: Any): Boolean = { + types._sequential_Q(x) && types._toIter(x).length > 0 + } + + def quasiquote(ast: Any): Any = { + if (!is_pair(ast)) { + return List(Symbol("quote"), ast) + } else { + val a0 = types._toList(ast)(0) + if (types._symbol_Q(a0) && + a0.asInstanceOf[Symbol].name == "unquote") { + return types._toList(ast)(1) + } else if (is_pair(a0)) { + val a00 = types._toList(a0)(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("cons"), + quasiquote(a0), + quasiquote(types._toList(ast).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 _ => 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 (!ast.isInstanceOf[List[Any]]) + return eval_ast(ast, env) + + // apply list + ast.asInstanceOf[List[Any]] 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)) { + 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("do") :: rest => { + eval_ast(rest.slice(0,rest.length-1), env) + ast = ast.asInstanceOf[List[Any]].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 types.Function(a2, env, a1.asInstanceOf[List[Any]], + (args: List[Any]) => { + EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) + } + ) + } + case _ => { + // function call + eval_ast(ast, env) match { + case f :: el => { + f match { + case fn: types.Function => { + env = fn.gen_env(el) + ast = fn.ast // continue loop (TCO) + } + case fn: ((List[Any]) => Any) @unchecked => { + 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), 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.mal: defined using the language itself + REP("(def! not (fn* (a) (if a false true)))") + REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") + + if (args.length > 0) { + REP("(load-file \"" + args(0) + "\")") + System.exit(0) + } + + // repl loop + 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/step8_macros.scala b/scala/step8_macros.scala new file mode 100644 index 0000000..8e4cc56 --- /dev/null +++ b/scala/step8_macros.scala @@ -0,0 +1,204 @@ +import env.Env +import types.Function + +object step8_macros { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + def is_pair(x: Any): Boolean = { + types._sequential_Q(x) && types._toIter(x).length > 0 + } + + def quasiquote(ast: Any): Any = { + if (!is_pair(ast)) { + return List(Symbol("quote"), ast) + } else { + val a0 = types._toList(ast)(0) + if (types._symbol_Q(a0) && + a0.asInstanceOf[Symbol].name == "unquote") { + return types._toList(ast)(1) + } else if (is_pair(a0)) { + val a00 = types._toList(a0)(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("cons"), + quasiquote(a0), + quasiquote(types._toList(ast).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 _ => 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[List[Any]] match { + case f :: args => { + val mac = env.get(f.asInstanceOf[Symbol]) + ast = mac.asInstanceOf[Function](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 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 _ => 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 (!ast.isInstanceOf[List[Any]]) + return eval_ast(ast, env) + + // apply list + ast = macroexpand(ast, env) + if (!ast.isInstanceOf[List[Any]]) return ast + + ast.asInstanceOf[List[Any]] 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)) { + 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[Function].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) + } + 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 Function(a2, env, types._toList(a1), + (args: List[Any]) => { + EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) + } + ) + } + case _ => { + // function call + eval_ast(ast, env) match { + case f :: el => { + f match { + case fn: Function => { + env = fn.gen_env(el) + ast = fn.ast // continue loop (TCO) + } + case fn: ((List[Any]) => Any) @unchecked => { + 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), 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.mal: defined using the language itself + 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 + 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/step9_try.scala b/scala/step9_try.scala new file mode 100644 index 0000000..200d3cf --- /dev/null +++ b/scala/step9_try.scala @@ -0,0 +1,224 @@ +import env.Env +import types.Function + +object step9_try { + // read + def READ(str: String): Any = { + reader.read_str(str) + } + + // eval + def is_pair(x: Any): Boolean = { + types._sequential_Q(x) && types._toIter(x).length > 0 + } + + def quasiquote(ast: Any): Any = { + if (!is_pair(ast)) { + return List(Symbol("quote"), ast) + } else { + val a0 = types._toList(ast)(0) + if (types._symbol_Q(a0) && + a0.asInstanceOf[Symbol].name == "unquote") { + return types._toList(ast)(1) + } else if (is_pair(a0)) { + val a00 = types._toList(a0)(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("cons"), + quasiquote(a0), + quasiquote(types._toList(ast).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 _ => 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[List[Any]] match { + case f :: args => { + val mac = env.get(f.asInstanceOf[Symbol]) + ast = mac.asInstanceOf[Function](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 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 _ => 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 (!ast.isInstanceOf[List[Any]]) + return eval_ast(ast, env) + + // apply list + ast = macroexpand(ast, env) + if (!ast.isInstanceOf[List[Any]]) return ast + + ast.asInstanceOf[List[Any]] 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)) { + 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[Function].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) 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(rest.slice(0,rest.length-1), env) + ast = ast.asInstanceOf[List[Any]].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 Function(a2, env, types._toList(a1), + (args: List[Any]) => { + EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) + } + ) + } + case _ => { + // function call + eval_ast(ast, env) match { + case f :: el => { + f match { + case fn: Function => { + env = fn.gen_env(el) + ast = fn.ast // continue loop (TCO) + } + case fn: ((List[Any]) => Any) @unchecked => { + 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), 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.mal: defined using the language itself + 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 + 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 new file mode 100644 index 0000000..cf93ac2 --- /dev/null +++ b/scala/types.scala @@ -0,0 +1,128 @@ +import scala.collection._ +import scala.collection.generic._ + +import env.Env + +object types { + class MalException(msg: String) extends Throwable(msg) { + var value: Any = null + def init(obj: Any) = { value = obj; this } + } + + def _toIter(obj: Any): Iterator[Any] = { + obj match { + case l: List[Any] => l.iterator + case v: Array[Any] => v.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 == b + } + } + + def _sequential_Q(a: Any): Boolean = { + a match { + case l: List[Any] => true + case v: Array[Any] => true + case _ => false + } + } + + def _symbol_Q(a: Any) = { a.isInstanceOf[Symbol] } + + + // Lists + + class MalList[A](seq : A*) extends Traversable[A] + with GenericTraversableTemplate[A, MalList] + with TraversableLike[A, MalList[A]] { + 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:_*) + } + } + } + + + // 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) + } + 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:_*) + } + } + } + + + class Function(_ast: Any, _env: Env, _params: List[Any], + fn: ((List[Any]) => Any)) { + val ast = _ast + val env = _env + val params = _params + var ismacro = false + + def apply(args: List[Any]): Any = { + fn(args) + } + + def gen_env(args: List[Any]): Env = { + return new Env(env, params.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 _ => throw new Exception("attempt to call non-function") + } + } + + def _hash_map(lst: List[Any]): Any = { + lst.grouped(2).map( + (kv: List[Any]) => (kv(0).asInstanceOf[String], kv(1))).toMap + } + + class Atom(_value: Any) { + var value = _value + } +} + +// vim:ts=2:sw=2 |
