mirror of
https://github.com/kanaka/mal.git
synced 2024-11-13 11:23:59 +03:00
132 lines
4.0 KiB
Swift
132 lines
4.0 KiB
Swift
import Foundation
|
|
|
|
// read
|
|
func READ(_ str: String) throws -> MalVal {
|
|
return try read_str(str)
|
|
}
|
|
|
|
// eval
|
|
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 list(try lst.map { try EVAL($0, env) })
|
|
case MalVal.MalVector(let lst, _):
|
|
return vector(try lst.map { try EVAL($0, env) })
|
|
case MalVal.MalHashMap(let dict, _):
|
|
var new_dict = Dictionary<String,MalVal>()
|
|
for (k,v) in dict { new_dict[k] = try EVAL(v, env) }
|
|
return hash_map(new_dict)
|
|
default:
|
|
return ast
|
|
}
|
|
}
|
|
|
|
func EVAL(_ ast: MalVal, _ env: Env) throws -> MalVal {
|
|
switch ast {
|
|
case MalVal.MalList(let lst, _): if lst.count == 0 { return ast }
|
|
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)
|
|
var binds = Array<MalVal>()
|
|
switch lst[1] {
|
|
case MalVal.MalList(let l, _): binds = l
|
|
case MalVal.MalVector(let l, _): binds = l
|
|
default:
|
|
throw MalError.General(msg: "Invalid let* bindings")
|
|
}
|
|
var idx = binds.startIndex
|
|
while idx < binds.endIndex {
|
|
let v = try EVAL(binds[binds.index(after: idx)], let_env)
|
|
try let_env.set(binds[idx], v)
|
|
idx = binds.index(idx, offsetBy: 2)
|
|
}
|
|
return try EVAL(lst[2], let_env)
|
|
case MalVal.MalSymbol("do"):
|
|
let slc = lst[lst.index(after: lst.startIndex)..<lst.endIndex]
|
|
switch try eval_ast(list(Array(slc)), env) {
|
|
case MalVal.MalList(let elst, _):
|
|
return elst[elst.index(before: elst.endIndex)]
|
|
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 malfunc( {
|
|
return try EVAL(lst[2], Env(env, binds: lst[1],
|
|
exprs: list($0)))
|
|
})
|
|
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")
|
|
}
|
|
}
|
|
|
|
// print
|
|
func PRINT(_ exp: MalVal) -> String {
|
|
return pr_str(exp, true)
|
|
}
|
|
|
|
|
|
// repl
|
|
@discardableResult
|
|
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), malfunc(fn))
|
|
}
|
|
|
|
// 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(strippingNewline: 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)")
|
|
}
|
|
}
|