mirror of
https://github.com/kanaka/mal.git
synced 2024-10-26 14:22:25 +03:00
Scala: all steps 0-9 but no metadata.
This commit is contained in:
parent
b8ee29b22f
commit
821930dbd9
2
.gitignore
vendored
2
.gitignore
vendored
@ -35,3 +35,5 @@ rust/.cargo
|
||||
r/lib
|
||||
vb/*.exe
|
||||
vb/*.dll
|
||||
scala/target
|
||||
scala/project
|
||||
|
4
Makefile
4
Makefile
@ -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
|
||||
|
13
README.md
13
README.md
@ -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
|
||||
|
||||
|
15
scala/build.sbt
Normal file
15
scala/build.sbt
Normal file
@ -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
|
||||
|
178
scala/core.scala
Normal file
178
scala/core.scala
Normal file
@ -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
|
40
scala/env.scala
Normal file
40
scala/env.scala
Normal file
@ -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
|
45
scala/printer.scala
Normal file
45
scala/printer.scala
Normal file
@ -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
|
88
scala/reader.scala
Normal file
88
scala/reader.scala
Normal file
@ -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
|
33
scala/step0_repl.scala
Normal file
33
scala/step0_repl.scala
Normal file
@ -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
|
39
scala/step1_read_print.scala
Normal file
39
scala/step1_read_print.scala
Normal file
@ -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
|
73
scala/step2_eval.scala
Normal file
73
scala/step2_eval.scala
Normal file
@ -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
|
93
scala/step3_env.scala
Normal file
93
scala/step3_env.scala
Normal file
@ -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
|
106
scala/step4_if_fn_do.scala
Normal file
106
scala/step4_if_fn_do.scala
Normal file
@ -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
|
117
scala/step5_tco.scala
Normal file
117
scala/step5_tco.scala
Normal file
@ -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
|
126
scala/step6_file.scala
Normal file
126
scala/step6_file.scala
Normal file
@ -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
|
159
scala/step7_quote.scala
Normal file
159
scala/step7_quote.scala
Normal file
@ -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
|
204
scala/step8_macros.scala
Normal file
204
scala/step8_macros.scala
Normal file
@ -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
|
224
scala/step9_try.scala
Normal file
224
scala/step9_try.scala
Normal file
@ -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
|
128
scala/types.scala
Normal file
128
scala/types.scala
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user