--- /dev/null
+STEP3_DEPS = Sources/types.swift Sources/reader.swift Sources/printer.swift Sources/env.swift
+STEP4_DEPS = $(STEP3_DEPS) Sources/core.swift
+
+STEPS = step0_repl step1_read_print step2_eval step3_env \
+ step4_if_fn_do step5_tco step6_file step7_quote \
+ step8_macros step9_try stepA_mal
+
+step1_read_print step2_eval step3_env: $(STEP3_DEPS)
+step4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal: $(STEP4_DEPS)
+
+step%: Sources/step%/main.swift
+ swiftc $+ -o $@
+
+clean:
+ rm -f $(STEPS)
--- /dev/null
+func IntOp(op: (Int, Int) -> Int, _ a: MalVal, _ b: MalVal) throws -> MalVal {
+ switch (a, b) {
+ case (MV.MalInt(let i1), MV.MalInt(let i2)):
+ return MV.MalInt(op(i1, i2))
+ default:
+ throw MalError.General(msg: "Invalid IntOp call")
+ }
+}
+
+func CmpOp(op: (Int, Int) -> Bool, _ a: MalVal, _ b: MalVal) throws -> MalVal {
+ switch (a, b) {
+ case (MV.MalInt(let i1), MV.MalInt(let i2)):
+ return wraptf(op(i1, i2))
+ default:
+ throw MalError.General(msg: "Invalid CmpOp call")
+ }
+}
+
+
+
+let core_ns: Dictionary<String,(Array<MalVal>) throws -> MalVal> = [
+ "=": { wraptf(equal_Q($0[0], $0[1])) },
+
+ "pr-str": {
+ return MV.MalString($0.map { pr_str($0,true) }.joinWithSeparator(" "))
+ },
+ "str": {
+ return MV.MalString($0.map { pr_str($0,false) }.joinWithSeparator(""))
+ },
+ "prn": {
+ print($0.map { pr_str($0,true) }.joinWithSeparator(" "))
+ return MV.MalNil
+ },
+ "println": {
+ print($0.map { pr_str($0,false) }.joinWithSeparator(" "))
+ return MV.MalNil
+ },
+
+
+ "<": { try CmpOp({ $0 < $1}, $0[0], $0[1]) },
+ "<=": { try CmpOp({ $0 <= $1}, $0[0], $0[1]) },
+ ">": { try CmpOp({ $0 > $1}, $0[0], $0[1]) },
+ ">=": { try CmpOp({ $0 >= $1}, $0[0], $0[1]) },
+ "+": { try IntOp({ $0 + $1}, $0[0], $0[1]) },
+ "-": { try IntOp({ $0 - $1}, $0[0], $0[1]) },
+ "*": { try IntOp({ $0 * $1}, $0[0], $0[1]) },
+ "/": { try IntOp({ $0 / $1}, $0[0], $0[1]) },
+
+ "list": { MV.MalList($0) },
+ "list?": {
+ switch $0[0] {
+ case MV.MalList(_): return MV.MalTrue
+ default: return MV.MalFalse
+ }
+ },
+
+ "empty?": {
+ switch $0[0] {
+ case MV.MalList(let lst):
+ return lst.count == 0 ? MV.MalTrue : MV.MalFalse
+ case MV.MalNil: return MV.MalTrue
+ default: throw MalError.General(msg: "Invalid empty? call")
+ }
+ },
+ "count": {
+ switch $0[0] {
+ case MV.MalList(let lst): return MV.MalInt(lst.count)
+ case MV.MalNil: return MV.MalInt(0)
+ default: throw MalError.General(msg: "Invalid count call")
+ }
+ }
+]
--- /dev/null
+class Env {
+ var outer: Env? = nil
+ var data: Dictionary<String, MalVal> = [:]
+
+ init(_ outer: Env? = nil, binds: MalVal? = nil,
+ exprs: MalVal? = nil) throws {
+ self.outer = outer
+
+ if binds != nil {
+ switch (binds!, exprs!) {
+ case (MalVal.MalList(let bs), MalVal.MalList(let es)):
+ var pos = bs.startIndex
+
+ bhandle:
+ while pos < bs.endIndex {
+ let b = bs[pos]
+ switch b {
+ case MalVal.MalSymbol("&"):
+ switch bs[pos.successor()] {
+ case MalVal.MalSymbol(let sym):
+ if pos < es.endIndex {
+ let slc = es[pos..<es.endIndex]
+ data[sym] = MalVal.MalList(Array(slc))
+ } else {
+ data[sym] = MalVal.MalList([])
+ }
+ break bhandle
+ default:
+ throw MalError.General(msg: "Env invalid varargs")
+ }
+ case MalVal.MalSymbol(let sym):
+ let e = es[pos]
+ data[sym] = e
+ default:
+ throw MalError.General(msg: "Env binds has non-symbol")
+ }
+ pos = pos.successor()
+ }
+ default:
+ throw MalError.General(msg: "invalid Env init call")
+ }
+ }
+ }
+
+ func find(key: MalVal) throws -> Env? {
+ switch key {
+ case MalVal.MalSymbol(let str):
+ if data[str] != nil {
+ return self
+ } else if outer != nil {
+ return try outer!.find(key)
+ } else {
+ return nil
+ }
+ default:
+ throw MalError.General(msg: "invalid Env.find call")
+ }
+ }
+
+ func get(key: MalVal) throws -> MalVal {
+ switch key {
+ case MalVal.MalSymbol(let str):
+ let env = try self.find(key)
+ if env == nil {
+ throw MalError.General(msg: "'\(str)' not found")
+ }
+ return env!.data[str]!
+ default:
+ throw MalError.General(msg: "invalid Env.find call")
+ }
+ }
+
+ func set(key: MalVal, _ val: MalVal) throws -> MalVal {
+ switch key {
+ case MalVal.MalSymbol(let str):
+ data[str] = val
+ return val
+ default:
+ throw MalError.General(msg: "invalid Env.find call")
+ }
+ }
+}
--- /dev/null
+
+func pr_str(obj: MalVal, _ print_readably: Bool = true) -> String {
+ switch obj {
+ case MalVal.MalList(let lst):
+ let elems = lst.map { pr_str($0, print_readably) }
+ return "(" + elems.joinWithSeparator(" ") + ")"
+ case MalVal.MalVector(let lst):
+ let elems = lst.map { pr_str($0, print_readably) }
+ return "[" + elems.joinWithSeparator(" ") + "]"
+ case MalVal.MalString(let str):
+ if print_readably {
+ let s1 = str.stringByReplacingOccurrencesOfString(
+ "\\", withString: "\\\\")
+ let s2 = s1.stringByReplacingOccurrencesOfString(
+ "\"", withString: "\\\"")
+ let s3 = s2.stringByReplacingOccurrencesOfString(
+ "\n", withString: "\\n")
+ return "\"" + s3 + "\""
+ } else {
+ return str
+ }
+ case MalVal.MalSymbol(let str):
+ return str
+ case MalVal.MalInt(let i): return String(i)
+ case MalVal.MalNil: return "nil"
+ case MalVal.MalFalse: return "false"
+ case MalVal.MalTrue: return "true"
+ case MalVal.MalFunc(_, nil, _, _):
+ return "#<native function>"
+ case MalVal.MalFunc(_, let ast, _, let params):
+ return "(fn* \(pr_str(params![0])) \(pr_str(ast![0])))"
+ default: return String(obj)
+ }
+}
--- /dev/null
+let token_delim: Set<Character> = [
+ ";", ",", "\"", "`", " ", "\n", "{", "}", "(", ")", "[", "]"
+]
+
+let int_char: Set<Character> = [
+ "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
+]
+
+let float_char: Set<Character> = [
+ ".", "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
+]
+
+let whitespace: Set<Character> = [" ", "\t", "\n", ","]
+
+class Reader {
+ var str: String
+ var pos: String.Index
+ init(_ str: String) {
+ self.str = str
+ pos = str.startIndex
+ }
+}
+
+func read_int(rdr: Reader) -> MalVal {
+ let start = rdr.pos
+ for cidx in rdr.pos..<rdr.str.endIndex {
+ rdr.pos = cidx
+ if !int_char.contains(rdr.str[cidx]) { break }
+ rdr.pos = cidx.successor()
+ }
+ let matchStr = rdr.str.substringWithRange(start..<rdr.pos)
+ if matchStr == "-" {
+ return MalVal.MalSymbol("-")
+ } else {
+ return MalVal.MalInt(Int(matchStr)!)
+ }
+}
+
+func skip_whitespace(rdr: Reader) {
+ for cidx in rdr.pos..<rdr.str.endIndex {
+ rdr.pos = cidx
+ if !whitespace.contains(rdr.str[rdr.pos]) { break }
+ }
+}
+
+func read_string(rdr: Reader) throws -> MalVal {
+ let start = rdr.pos
+ var escaped = false
+ if rdr.str[rdr.pos] != "\"" {
+ throw MalError.Reader(msg: "read_string call on non-string")
+ }
+ for cidx in rdr.pos.successor()..<rdr.str.endIndex {
+ rdr.pos = cidx.successor()
+ if escaped {
+ escaped = false
+ continue
+ }
+ if rdr.str[cidx] == "\\" { escaped = true }
+ if rdr.str[cidx] == "\"" { break }
+ }
+ if rdr.pos > rdr.str.endIndex {
+ throw MalError.Reader(msg: "Expected '\"', got EOF")
+ }
+ let matchStr = rdr.str.substringWithRange(
+ start.successor()..<rdr.pos.predecessor())
+ let s1 = matchStr.stringByReplacingOccurrencesOfString(
+ "\\\"", withString: "\"")
+ let s2 = s1.stringByReplacingOccurrencesOfString(
+ "\\n", withString: "\n")
+ let s3 = s2.stringByReplacingOccurrencesOfString(
+ "\\\\", withString: "\\")
+ return MalVal.MalString(s3)
+}
+
+func read_symbol(rdr: Reader) throws -> MalVal {
+ let start = rdr.pos
+ for cidx in rdr.pos..<rdr.str.endIndex {
+ rdr.pos = cidx
+ if token_delim.contains(rdr.str[cidx]) { break }
+ rdr.pos = cidx.successor()
+ }
+ let matchStr = rdr.str.substringWithRange(start..<rdr.pos)
+ switch matchStr {
+ case "nil": return MalVal.MalNil
+ case "true": return MalVal.MalTrue
+ case "false": return MalVal.MalFalse
+ default: return MalVal.MalSymbol(matchStr)
+ }
+}
+
+func read_atom(rdr: Reader) throws -> MalVal {
+ if rdr.str.characters.count == 0 {
+ throw MalError.Reader(msg: "Empty string passed to read_atom")
+ }
+ switch rdr.str[rdr.pos] {
+ case let c where int_char.contains(c): return read_int(rdr)
+ case "\"": return try read_string(rdr)
+ default: return try read_symbol(rdr)
+ }
+}
+
+func read_list(rdr: Reader, start: Character = "(", end: Character = ")") throws -> Array<MalVal> {
+ if rdr.str[rdr.pos] != start {
+ throw MalError.Reader(msg: "expected '\(start)'")
+ }
+ rdr.pos = rdr.pos.successor()
+ var lst: [MalVal] = []
+ while rdr.pos < rdr.str.endIndex {
+ if (rdr.str[rdr.pos] == end) { break }
+ lst.append(try read_form(rdr))
+ }
+ if rdr.pos >= rdr.str.endIndex {
+ throw MalError.Reader(msg: "Expected '\(end)', got EOF")
+ }
+ rdr.pos = rdr.pos.successor()
+ return lst
+}
+
+func read_form(rdr: Reader) throws -> MalVal {
+ if rdr.str.characters.count == 0 {
+ throw MalError.Reader(msg: "Empty string passed to read_form")
+ }
+ //print("read_form: \(rdr.pos): \(rdr.str[rdr.pos])")
+ skip_whitespace(rdr)
+ var res: MalVal
+ switch rdr.str[rdr.pos] {
+ case "(": res = MalVal.MalList(try read_list(rdr))
+ case ")": throw MalError.Reader(msg: "unexpected ')'")
+
+ case "[": res = MalVal.MalVector(try read_list(rdr, start: "[", end: "]"))
+ case "]": throw MalError.Reader(msg: "unexpected ']'")
+
+ default: res = try read_atom(rdr)
+ }
+ skip_whitespace(rdr)
+ return res
+}
+
+func read_str(str: String) throws -> MalVal {
+ return try read_form(Reader(str))
+}
--- /dev/null
+import Foundation
+
+while true {
+ print("user> ", terminator: "")
+ let line = readLine(stripNewline: true)
+ if line == nil { break }
+ if line == "" { continue }
+
+ print("\(line!)")
+}
--- /dev/null
+import Foundation
+
+func READ(str: String) throws -> MalVal {
+ return try read_str(str)
+}
+
+func EVAL(ast: MalVal, _ env: String) throws -> MalVal {
+ return ast
+}
+
+func PRINT(exp: MalVal) -> String {
+ return pr_str(exp, true)
+}
+
+
+func rep(str:String) throws -> String {
+ return PRINT(try EVAL(try READ(str), ""))
+}
+
+while true {
+ print("user> ", terminator: "")
+ let line = readLine(stripNewline: true)
+ if line == nil { break }
+ if line == "" { continue }
+
+ do {
+ print(try rep(line!))
+ } catch (MalError.Reader(let msg)) {
+ print("Error: \(msg)")
+ }
+}
--- /dev/null
+import Foundation
+
+func READ(str: String) throws -> MalVal {
+ return try read_str(str)
+}
+
+func eval_ast(ast: MalVal, _ env: Dictionary<String, MalVal>) throws -> MalVal {
+ switch ast {
+ case MalVal.MalSymbol(let sym):
+ if env[sym] == nil {
+ throw MalError.General(msg: "'\(sym)' not found")
+ }
+ return env[sym]!
+ case MalVal.MalList(let lst):
+ return MalVal.MalList(try lst.map { try EVAL($0, env) })
+ default:
+ return ast
+ }
+}
+
+func EVAL(ast: MalVal, _ env: Dictionary<String, MalVal>) throws -> MalVal {
+ switch ast {
+ case MalVal.MalList: true
+ default: return try eval_ast(ast, env)
+ }
+
+ switch try eval_ast(ast, env) {
+ case MalVal.MalList(let elst):
+ switch elst[0] {
+ case MalVal.MalFunc(let fn,_,_,_):
+ let args = Array(elst[1..<elst.count])
+ return try fn(args)
+ default:
+ throw MalError.General(msg: "Cannot apply on '\(elst[0])'")
+ }
+ default: throw MalError.General(msg: "Invalid apply")
+ }
+}
+
+func PRINT(exp: MalVal) -> String {
+ return pr_str(exp, true)
+}
+
+
+func rep(str:String) throws -> String {
+ return PRINT(try EVAL(try READ(str), repl_env))
+}
+
+func IntOp(op: (Int, Int) -> Int, _ a: MalVal, _ b: MalVal) throws -> MalVal {
+ switch (a, b) {
+ case (MalVal.MalInt(let i1), MalVal.MalInt(let i2)):
+ return MalVal.MalInt(op(i1, i2))
+ default:
+ throw MalError.General(msg: "Invalid IntOp call")
+ }
+}
+
+var repl_env: Dictionary<String,MalVal> = [
+ "+": MalVal.MalFunc({ try IntOp({ $0 + $1}, $0[0], $0[1]) },
+ ast:nil, env:nil, params:nil),
+ "-": MalVal.MalFunc({ try IntOp({ $0 - $1}, $0[0], $0[1]) },
+ ast:nil, env:nil, params:nil),
+ "*": MalVal.MalFunc({ try IntOp({ $0 * $1}, $0[0], $0[1]) },
+ ast:nil, env:nil, params:nil),
+ "/": MalVal.MalFunc({ try IntOp({ $0 / $1}, $0[0], $0[1]) },
+ ast:nil, env:nil, params:nil)
+]
+
+while true {
+ print("user> ", terminator: "")
+ let line = readLine(stripNewline: true)
+ if line == nil { break }
+ if line == "" { continue }
+
+ do {
+ print(try rep(line!))
+ } catch (MalError.Reader(let msg)) {
+ print("Error: \(msg)")
+ } catch (MalError.General(let msg)) {
+ print("Error: \(msg)")
+ }
+}
--- /dev/null
+import Foundation
+
+func READ(str: String) throws -> MalVal {
+ return try read_str(str)
+}
+
+func eval_ast(ast: MalVal, _ env: Env) throws -> MalVal {
+ switch ast {
+ case MalVal.MalSymbol:
+ return try env.get(ast)
+ case MalVal.MalList(let lst):
+ return MalVal.MalList(try lst.map { try EVAL($0, env) })
+ default:
+ return ast
+ }
+}
+
+func EVAL(ast: MalVal, _ env: Env) throws -> MalVal {
+ switch ast {
+ case MalVal.MalList: true
+ default: return try eval_ast(ast, env)
+ }
+
+ switch ast {
+ case MalVal.MalList(let lst):
+ switch lst[0] {
+ case MalVal.MalSymbol("def!"):
+ return try env.set(lst[1], try EVAL(lst[2], env))
+ case MalVal.MalSymbol("let*"):
+ let let_env = try Env(env)
+ switch lst[1] {
+ case MalVal.MalList(let binds):
+ var idx = binds.startIndex
+ while idx < binds.endIndex {
+ let v = try EVAL(binds[idx.successor()], let_env)
+ try let_env.set(binds[idx], v)
+ idx = idx.successor().successor()
+ }
+ return try EVAL(lst[2], let_env)
+ default:
+ throw MalError.General(msg: "Invalid let* bindings")
+ }
+ default:
+ switch try eval_ast(ast, env) {
+ case MalVal.MalList(let elst):
+ switch elst[0] {
+ case MalVal.MalFunc(let fn,_,_,_):
+ let args = Array(elst[1..<elst.count])
+ return try fn(args)
+ default:
+ throw MalError.General(msg: "Cannot apply on '\(elst[0])'")
+ }
+ default: throw MalError.General(msg: "Invalid apply")
+ }
+ }
+ default:
+ throw MalError.General(msg: "Invalid apply")
+ }
+}
+
+func PRINT(exp: MalVal) -> String {
+ return pr_str(exp, true)
+}
+
+
+func rep(str:String) throws -> String {
+ return PRINT(try EVAL(try READ(str), repl_env))
+}
+
+func IntOp(op: (Int, Int) -> Int, _ a: MalVal, _ b: MalVal) throws -> MalVal {
+ switch (a, b) {
+ case (MalVal.MalInt(let i1), MalVal.MalInt(let i2)):
+ return MalVal.MalInt(op(i1, i2))
+ default:
+ throw MalError.General(msg: "Invalid IntOp call")
+ }
+}
+
+var repl_env: Env = try Env()
+try repl_env.set(MalVal.MalSymbol("+"),
+ MalVal.MalFunc({ try IntOp({ $0 + $1}, $0[0], $0[1]) },
+ ast:nil, env:nil, params:nil))
+try repl_env.set(MalVal.MalSymbol("-"),
+ MalVal.MalFunc({ try IntOp({ $0 - $1}, $0[0], $0[1]) },
+ ast:nil, env:nil, params:nil))
+try repl_env.set(MalVal.MalSymbol("*"),
+ MalVal.MalFunc({ try IntOp({ $0 * $1}, $0[0], $0[1]) },
+ ast:nil, env:nil, params:nil))
+try repl_env.set(MalVal.MalSymbol("/"),
+ MalVal.MalFunc({ try IntOp({ $0 / $1}, $0[0], $0[1]) },
+ ast:nil, env:nil, params:nil))
+
+
+while true {
+ print("user> ", terminator: "")
+ let line = readLine(stripNewline: true)
+ if line == nil { break }
+ if line == "" { continue }
+
+ do {
+ print(try rep(line!))
+ } catch (MalError.Reader(let msg)) {
+ print("Error: \(msg)")
+ } catch (MalError.General(let msg)) {
+ print("Error: \(msg)")
+ }
+}
--- /dev/null
+import Foundation
+
+func READ(str: String) throws -> MalVal {
+ return try read_str(str)
+}
+
+func eval_ast(ast: MalVal, _ env: Env) throws -> MalVal {
+ switch ast {
+ case MalVal.MalSymbol:
+ return try env.get(ast)
+ case MalVal.MalList(let lst):
+ return MalVal.MalList(try lst.map { try EVAL($0, env) })
+ default:
+ return ast
+ }
+}
+
+func EVAL(ast: MalVal, _ env: Env) throws -> MalVal {
+ switch ast {
+ case MalVal.MalList: true
+ default: return try eval_ast(ast, env)
+ }
+
+ switch ast {
+ case MalVal.MalList(let lst):
+ switch lst[0] {
+ case MalVal.MalSymbol("def!"):
+ return try env.set(lst[1], try EVAL(lst[2], env))
+ case MalVal.MalSymbol("let*"):
+ let let_env = try Env(env)
+ switch lst[1] {
+ case MalVal.MalList(let binds):
+ var idx = binds.startIndex
+ while idx < binds.endIndex {
+ let v = try EVAL(binds[idx.successor()], let_env)
+ try let_env.set(binds[idx], v)
+ idx = idx.successor().successor()
+ }
+ return try EVAL(lst[2], let_env)
+ default:
+ throw MalError.General(msg: "Invalid let* bindings")
+ }
+ case MalVal.MalSymbol("do"):
+ let slc = lst[lst.startIndex.successor()..<lst.endIndex]
+ switch try eval_ast(MV.MalList(Array(slc)), env) {
+ case MalVal.MalList(let elst):
+ return elst[elst.endIndex.predecessor()]
+ default:
+ throw MalError.General(msg: "Invalid do form")
+ }
+ case MalVal.MalSymbol("if"):
+ switch try EVAL(lst[1], env) {
+ case MalVal.MalFalse, MalVal.MalNil:
+ if lst.count > 3 {
+ return try EVAL(lst[3], env)
+ } else {
+ return MalVal.MalNil
+ }
+ default:
+ return try EVAL(lst[2], env)
+ }
+ case MalVal.MalSymbol("fn*"):
+ return MalVal.MalFunc( {
+ return try EVAL(lst[2], Env(env, binds: lst[1],
+ exprs: MalVal.MalList($0)))
+ }, ast:nil, env:nil, params:nil)
+ default:
+ switch try eval_ast(ast, env) {
+ case MalVal.MalList(let elst):
+ switch elst[0] {
+ case MalVal.MalFunc(let fn, _, _, _):
+ let args = Array(elst[1..<elst.count])
+ return try fn(args)
+ default:
+ throw MalError.General(msg: "Cannot apply on '\(elst[0])'")
+ }
+ default: throw MalError.General(msg: "Invalid apply")
+ }
+ }
+ default:
+ throw MalError.General(msg: "Invalid apply")
+ }
+}
+
+func PRINT(exp: MalVal) -> String {
+ return pr_str(exp, true)
+}
+
+
+func rep(str:String) throws -> String {
+ return PRINT(try EVAL(try READ(str), repl_env))
+}
+
+var repl_env: Env = try Env()
+
+// core.swift: defined using Swift
+for (k, fn) in core_ns {
+ try repl_env.set(MalVal.MalSymbol(k),
+ MalVal.MalFunc(fn,ast:nil,env:nil,params:nil))
+}
+
+// core.mal: defined using the language itself
+try rep("(def! not (fn* (a) (if a false true)))")
+
+
+while true {
+ print("user> ", terminator: "")
+ let line = readLine(stripNewline: true)
+ if line == nil { break }
+ if line == "" { continue }
+
+ do {
+ print(try rep(line!))
+ } catch (MalError.Reader(let msg)) {
+ print("Error: \(msg)")
+ } catch (MalError.General(let msg)) {
+ print("Error: \(msg)")
+ }
+}
--- /dev/null
+import Foundation
+
+func READ(str: String) throws -> MalVal {
+ return try read_str(str)
+}
+
+func eval_ast(ast: MalVal, _ env: Env) throws -> MalVal {
+ switch ast {
+ case MalVal.MalSymbol:
+ return try env.get(ast)
+ case MalVal.MalList(let lst):
+ return MalVal.MalList(try lst.map { try EVAL($0, env) })
+ default:
+ return ast
+ }
+}
+
+func EVAL(orig_ast: MalVal, _ orig_env: Env) throws -> MalVal {
+ var ast = orig_ast, env = orig_env
+ while true {
+ switch ast {
+ case MalVal.MalList: true
+ default: return try eval_ast(ast, env)
+ }
+
+ switch ast {
+ case MalVal.MalList(let lst):
+ switch lst[0] {
+ case MalVal.MalSymbol("def!"):
+ return try env.set(lst[1], try EVAL(lst[2], env))
+ case MalVal.MalSymbol("let*"):
+ let let_env = try Env(env)
+ switch lst[1] {
+ case MalVal.MalList(let binds):
+ var idx = binds.startIndex
+ while idx < binds.endIndex {
+ let v = try EVAL(binds[idx.successor()], let_env)
+ try let_env.set(binds[idx], v)
+ idx = idx.successor().successor()
+ }
+ env = let_env
+ ast = lst[2] // TCO
+ default:
+ throw MalError.General(msg: "Invalid let* bindings")
+ }
+ case MalVal.MalSymbol("do"):
+ let slc = lst[lst.startIndex.successor()..<lst.endIndex.predecessor()]
+ try eval_ast(MV.MalList(Array(slc)), env)
+ ast = lst[lst.endIndex.predecessor()]
+ case MalVal.MalSymbol("if"):
+ switch try EVAL(lst[1], env) {
+ case MalVal.MalFalse, MalVal.MalNil:
+ if lst.count > 3 {
+ ast = lst[3] // TCO
+ } else {
+ return MalVal.MalNil
+ }
+ default:
+ ast = lst[2] // TCO
+ }
+ case MalVal.MalSymbol("fn*"):
+ return MalVal.MalFunc( {
+ return try EVAL(lst[2], Env(env, binds: lst[1],
+ exprs: MalVal.MalList($0)))
+ }, ast:[lst[2]], env:env, params:[lst[1]])
+ default:
+ switch try eval_ast(ast, env) {
+ case MalVal.MalList(let elst):
+ switch elst[0] {
+ case MalVal.MalFunc(let fn, nil, _, _):
+ let args = Array(elst[1..<elst.count])
+ return try fn(args)
+ case MalVal.MalFunc(_, let a, let e, let p):
+ let args = Array(elst[1..<elst.count])
+ ast = a![0]
+ env = try Env(e, binds: p![0],
+ exprs: MalVal.MalList(args)) // TCO
+ default:
+ throw MalError.General(msg: "Cannot apply on '\(elst[0])'")
+ }
+ default: throw MalError.General(msg: "Invalid apply")
+ }
+ }
+ default:
+ throw MalError.General(msg: "Invalid apply")
+ }
+ }
+}
+
+func PRINT(exp: MalVal) -> String {
+ return pr_str(exp, true)
+}
+
+
+func rep(str:String) throws -> String {
+ return PRINT(try EVAL(try READ(str), repl_env))
+}
+
+var repl_env: Env = try Env()
+
+// core.swift: defined using Swift
+for (k, fn) in core_ns {
+ try repl_env.set(MalVal.MalSymbol(k),
+ MalVal.MalFunc(fn,ast:nil,env:nil,params:nil))
+}
+
+// core.mal: defined using the language itself
+try rep("(def! not (fn* (a) (if a false true)))")
+
+
+while true {
+ print("user> ", terminator: "")
+ let line = readLine(stripNewline: true)
+ if line == nil { break }
+ if line == "" { continue }
+
+ do {
+ print(try rep(line!))
+ } catch (MalError.Reader(let msg)) {
+ print("Error: \(msg)")
+ } catch (MalError.General(let msg)) {
+ print("Error: \(msg)")
+ }
+}
--- /dev/null
+
+enum MalError: ErrorType {
+ case Reader(msg: String)
+ case General(msg: String)
+}
+
+enum MalVal {
+ case MalNil
+ case MalTrue
+ case MalFalse
+ case MalInt(Int)
+ case MalFloat(Float)
+ case MalString(String)
+ case MalSymbol(String)
+ case MalList(Array<MalVal>)
+ case MalVector(Array<MalVal>)
+ // TODO: ast and params wrapped in arrays because otherwise
+ // compiler throws a fault
+ case MalFunc((Array<MalVal>) throws -> MalVal,
+ ast: Array<MalVal>?, env: Env?, params: Array<MalVal>?)
+}
+
+typealias MV = MalVal
+
+// General functions
+
+func wraptf(a: Bool) -> MalVal {
+ return a ? MV.MalTrue : MV.MalFalse
+}
+
+func cmp_seqs(a: Array<MalVal>, _ b: Array<MalVal>) -> Bool {
+ if a.count != b.count { return false }
+ var idx = a.startIndex
+ while idx < a.endIndex {
+ if !equal_Q(a[idx], b[idx]) { return false }
+ idx = idx.successor()
+ }
+ return true
+}
+
+func equal_Q(a: MalVal, _ b: MalVal) -> Bool {
+ switch (a, b) {
+ case (MV.MalNil, MV.MalNil): return true
+ case (MV.MalFalse, MV.MalFalse): return true
+ case (MV.MalTrue, MV.MalTrue): return true
+ case (MV.MalInt(let i1), MV.MalInt(let i2)): return i1 == i2
+ case (MV.MalString(let s1), MV.MalString(let s2)): return s1 == s2
+ case (MV.MalSymbol(let s1), MV.MalSymbol(let s2)): return s1 == s2
+ case (MV.MalList(let l1), MV.MalList(let l2)):
+ return cmp_seqs(l1, l2)
+ case (MV.MalList(let l1), MV.MalVector(let l2)):
+ return cmp_seqs(l1, l2)
+ case (MV.MalVector(let l1), MV.MalList(let l2)):
+ return cmp_seqs(l1, l2)
+ case (MV.MalVector(let l1), MV.MalVector(let l2)):
+ return cmp_seqs(l1, l2)
+ default:
+ return false
+ }
+}
+
+func rest(a: MalVal) throws -> MalVal {
+ switch a {
+ case MV.MalList(let lst):
+ let slc = lst[lst.startIndex.successor()..<lst.endIndex]
+ return MV.MalList(Array(slc))
+ default:
+ throw MalError.General(msg: "Invalid rest call")
+ }
+}