r/lib
vb/*.exe
vb/*.dll
+scala/target
+scala/project
#
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
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
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
* R
* Ruby
* Rust
+* Scala
* Visual Basic.NET
./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
- 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
--- /dev/null
+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
+
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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