mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
swift5: stepA_mal
This commit is contained in:
parent
07e8a86d46
commit
590d589b07
@ -16,7 +16,8 @@ let package = Package(
|
||||
.executable(name: "step6_file", targets: ["step6_file"]),
|
||||
.executable(name: "step7_quote", targets: ["step7_quote"]),
|
||||
.executable(name: "step8_macros", targets: ["step8_macros"]),
|
||||
.executable(name: "step9_try", targets: ["step9_try"])
|
||||
.executable(name: "step9_try", targets: ["step9_try"]),
|
||||
.executable(name: "stepA_mal", targets: ["stepA_mal"])
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
@ -35,6 +36,7 @@ let package = Package(
|
||||
.target(name: "step6_file", dependencies: ["core"]),
|
||||
.target(name: "step7_quote", dependencies: ["core"]),
|
||||
.target(name: "step8_macros", dependencies: ["core"]),
|
||||
.target(name: "step9_try", dependencies: ["core"])
|
||||
.target(name: "step9_try", dependencies: ["core"]),
|
||||
.target(name: "stepA_mal", dependencies: ["core"])
|
||||
]
|
||||
)
|
||||
|
@ -13,6 +13,10 @@ private extension Func {
|
||||
return data
|
||||
}
|
||||
|
||||
static let notImplemented = Func { args in
|
||||
throw MalError("not implemented")
|
||||
}
|
||||
|
||||
static func infixOperation(_ op: @escaping (Int, Int) -> Int) -> Func {
|
||||
return Func { args in
|
||||
guard args.count == 2,
|
||||
@ -70,7 +74,7 @@ private extension Func {
|
||||
|
||||
static let isEmpty = Func { args in
|
||||
switch args.first {
|
||||
case let .list(xs), let .vector(xs):
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
return .bool(xs.isEmpty)
|
||||
default:
|
||||
return .bool(false)
|
||||
@ -79,7 +83,7 @@ private extension Func {
|
||||
|
||||
static let count = Func { args in
|
||||
switch args.first {
|
||||
case let .list(xs), let .vector(xs):
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
return .number(xs.count)
|
||||
default:
|
||||
return .number(0)
|
||||
@ -142,7 +146,7 @@ private extension Func {
|
||||
static let cons = Func { args in
|
||||
guard args.count == 2 else { throw MalError.invalidArguments("cons") }
|
||||
switch args[1] {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return .list([args[0]] + values)
|
||||
default:
|
||||
throw MalError.invalidArguments("cons")
|
||||
@ -152,7 +156,7 @@ private extension Func {
|
||||
static let concat = Func { args in
|
||||
let values = try args.flatMap { el throws -> [Expr] in
|
||||
switch el {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return values
|
||||
default:
|
||||
throw MalError.invalidArguments("concat")
|
||||
@ -166,7 +170,7 @@ private extension Func {
|
||||
guard case let .number(index) = args[1] else { throw MalError.invalidArguments("nth") }
|
||||
|
||||
switch args.first {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
guard values.indices ~= index else { throw MalError.outOfRange() }
|
||||
return values[index]
|
||||
default:
|
||||
@ -176,7 +180,7 @@ private extension Func {
|
||||
|
||||
static let first = Func { args in
|
||||
switch args.first {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return values.first ?? .null
|
||||
case .null:
|
||||
return .null
|
||||
@ -187,7 +191,7 @@ private extension Func {
|
||||
|
||||
static let rest = Func { args in
|
||||
switch args.first {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return .list(Array(values.dropFirst()))
|
||||
case .null:
|
||||
return .list([])
|
||||
@ -207,7 +211,7 @@ private extension Func {
|
||||
|
||||
let lastArgs: [Expr]
|
||||
switch args.last! {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
lastArgs = values
|
||||
default:
|
||||
throw MalError.invalidArguments("apply")
|
||||
@ -223,7 +227,7 @@ private extension Func {
|
||||
guard case let .function(fn) = args[0] else { throw MalError.invalidArguments("map") }
|
||||
|
||||
switch args[1] {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return .list(try values.map { try fn.run([$0]) })
|
||||
default:
|
||||
throw MalError.invalidArguments("map")
|
||||
@ -320,7 +324,7 @@ private extension Func {
|
||||
|
||||
static let assoc = Func { args in
|
||||
guard args.count > 0 else { throw MalError.invalidArguments("assoc") }
|
||||
guard case let .hashmap(data) = args[0] else { throw MalError.invalidArguments("assoc") }
|
||||
guard case let .hashmap(data, _) = args[0] else { throw MalError.invalidArguments("assoc") }
|
||||
|
||||
let newData = try hashMapDataFrom(Array(args.dropFirst()))
|
||||
return .hashmap(data.merging(newData, uniquingKeysWith: { _, new in new }))
|
||||
@ -328,7 +332,7 @@ private extension Func {
|
||||
|
||||
static let dissoc = Func { args in
|
||||
guard args.count > 0 else { throw MalError.invalidArguments("dissoc") }
|
||||
guard case var .hashmap(data) = args[0] else { throw MalError.invalidArguments("dissoc") }
|
||||
guard case var .hashmap(data, _) = args[0] else { throw MalError.invalidArguments("dissoc") }
|
||||
|
||||
for key in args.dropFirst() {
|
||||
guard case let .string(name) = key else { throw MalError.invalidArguments("dissoc") }
|
||||
@ -342,7 +346,7 @@ private extension Func {
|
||||
guard case let .string(key) = args[1] else { throw MalError.invalidArguments("get") }
|
||||
|
||||
switch args[0] {
|
||||
case let .hashmap(data):
|
||||
case let .hashmap(data, _):
|
||||
return data[key] ?? .null
|
||||
case .null:
|
||||
return .null
|
||||
@ -353,22 +357,137 @@ private extension Func {
|
||||
|
||||
static let contains = Func { args in
|
||||
guard args.count == 2 else { throw MalError.invalidArguments("contains?") }
|
||||
guard case let .hashmap(data) = args[0] else { throw MalError.invalidArguments("contains?") }
|
||||
guard case let .hashmap(data, _) = args[0] else { throw MalError.invalidArguments("contains?") }
|
||||
guard case let .string(key) = args[1] else { throw MalError.invalidArguments("contains?") }
|
||||
return data.keys.contains(key) ? .bool(true) : .bool(false)
|
||||
}
|
||||
|
||||
static let keys = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("keys") }
|
||||
guard case let .hashmap(data) = args[0] else { throw MalError.invalidArguments("keys") }
|
||||
guard case let .hashmap(data, _) = args[0] else { throw MalError.invalidArguments("keys") }
|
||||
return .list(data.keys.map(Expr.string))
|
||||
}
|
||||
|
||||
static let vals = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("vals") }
|
||||
guard case let .hashmap(data) = args[0] else { throw MalError.invalidArguments("vals") }
|
||||
guard case let .hashmap(data, _) = args[0] else { throw MalError.invalidArguments("vals") }
|
||||
return .list(Array(data.values))
|
||||
}
|
||||
|
||||
static let readline = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("readline") }
|
||||
guard case let .string(promt) = args[0] else { throw MalError.invalidArguments("readline") }
|
||||
print(promt, terminator: "")
|
||||
if let s = readLine() {
|
||||
return .string(s)
|
||||
}
|
||||
return .null
|
||||
}
|
||||
|
||||
static let timeMs = Func { args in
|
||||
guard args.count == 0 else { throw MalError.invalidArguments("time-ms") }
|
||||
return .number(Int(Date().timeIntervalSince1970 * 1000))
|
||||
}
|
||||
|
||||
static let isFunction = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("fn?") }
|
||||
if case let .function(fn) = args[0] {
|
||||
return .bool(!fn.isMacro)
|
||||
}
|
||||
return .bool(false)
|
||||
}
|
||||
|
||||
static let isMacro = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("macro?") }
|
||||
if case let .function(fn) = args[0] {
|
||||
return .bool(fn.isMacro)
|
||||
}
|
||||
return .bool(false)
|
||||
}
|
||||
|
||||
static let isString = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("string?") }
|
||||
if case let .string(s) = args[0] {
|
||||
return s.first == keywordMagic ? .bool(false) : .bool(true)
|
||||
}
|
||||
return .bool(false)
|
||||
}
|
||||
|
||||
static let isNumber = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("number?") }
|
||||
if case .number = args[0] {
|
||||
return .bool(true)
|
||||
}
|
||||
return .bool(false)
|
||||
}
|
||||
|
||||
static let seq = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("seq") }
|
||||
|
||||
switch args[0] {
|
||||
case .list([], _), .vector([], _), .string(""), .null:
|
||||
return .null
|
||||
case .list:
|
||||
return args[0]
|
||||
case let .vector(values, _):
|
||||
return .list(values)
|
||||
case let .string(s):
|
||||
if s.first == keywordMagic {
|
||||
throw MalError.invalidArguments("seq")
|
||||
}
|
||||
return .list(Array(s.map { .string(String($0)) }))
|
||||
default:
|
||||
throw MalError.invalidArguments("seq")
|
||||
}
|
||||
}
|
||||
|
||||
static let conj = Func { args in
|
||||
guard args.count > 0 else { throw MalError.invalidArguments("conj") }
|
||||
switch args[0] {
|
||||
case let .list(values, _):
|
||||
return .list(Array(args.dropFirst()).reversed() + values)
|
||||
case let .vector(values, _):
|
||||
return .vector(values + Array(args.dropFirst()))
|
||||
default:
|
||||
throw MalError.invalidArguments("conj")
|
||||
}
|
||||
}
|
||||
|
||||
static let meta = Func { args in
|
||||
guard args.count == 1 else { throw MalError.invalidArguments("meta") }
|
||||
switch args[0] {
|
||||
case let .function(fn):
|
||||
return fn.meta
|
||||
case let .list(_, meta):
|
||||
return meta
|
||||
case let .vector(_, meta):
|
||||
return meta
|
||||
case let .hashmap(_, meta):
|
||||
return meta
|
||||
case let .atom(atom):
|
||||
return atom.meta
|
||||
default:
|
||||
throw MalError.invalidArguments("meta")
|
||||
}
|
||||
}
|
||||
|
||||
static let withMeta = Func { args in
|
||||
guard args.count == 2 else { throw MalError.invalidArguments("with-meta") }
|
||||
switch args[0] {
|
||||
case let .function(fn):
|
||||
return .function(fn.withMeta(args[1]))
|
||||
case let .list(values, _):
|
||||
return .list(values, args[1])
|
||||
case let .vector(values, _):
|
||||
return .vector(values, args[1])
|
||||
case let .hashmap(data, _):
|
||||
return .hashmap(data, args[1])
|
||||
case let .atom(atom):
|
||||
return .atom(atom.withMeta(args[1]))
|
||||
default:
|
||||
throw MalError.invalidArguments("with-meta")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let data: [String: Expr] = [
|
||||
@ -421,7 +540,17 @@ private let data: [String: Expr] = [
|
||||
"get": .function(.get),
|
||||
"contains?": .function(.contains),
|
||||
"keys": .function(.keys),
|
||||
"vals": .function(.vals)
|
||||
"vals": .function(.vals),
|
||||
"readline": .function(.readline),
|
||||
"time-ms": .function(.timeMs),
|
||||
"meta": .function(.meta),
|
||||
"with-meta": .function(.withMeta),
|
||||
"fn?": .function(.isFunction),
|
||||
"macro?": .function(.isMacro),
|
||||
"string?": .function(.isString),
|
||||
"number?": .function(.isNumber),
|
||||
"seq": .function(.seq),
|
||||
"conj": .function(.conj)
|
||||
]
|
||||
|
||||
public enum Core {
|
||||
|
@ -8,13 +8,13 @@ extension Expr {
|
||||
switch expr {
|
||||
case let .number(value):
|
||||
return "\(value)"
|
||||
case let .list(arr):
|
||||
case let .list(arr, _):
|
||||
let inner: String = arr.map(print).joined(separator: " ")
|
||||
return "(" + inner + ")"
|
||||
case let .vector(arr):
|
||||
case let .vector(arr, _):
|
||||
let inner: String = arr.map(print).joined(separator: " ")
|
||||
return "[" + inner + "]"
|
||||
case let .hashmap(m):
|
||||
case let .hashmap(m, _):
|
||||
let inner = m.map { printString($0.key, readable: readable) + " " + print($0.value) }.joined(separator: " ")
|
||||
return "{" + inner + "}"
|
||||
case let .string(s):
|
||||
|
@ -40,8 +40,8 @@ private extension Parsers {
|
||||
return .number(value * factor)
|
||||
}
|
||||
|
||||
static let list = ("(" *> _form.zeroOrMore.spacesAround() <* string(")").orThrow(.unbalanced(expected: ")"))).map(Expr.list)
|
||||
static let vector = ("[" *> _form.zeroOrMore.spacesAround() <* string("]").orThrow(.unbalanced(expected: "]"))).map(Expr.vector)
|
||||
static let list = ("(" *> _form.zeroOrMore.spacesAround() <* string(")").orThrow(.unbalanced(expected: ")"))).map { Expr.list($0) }
|
||||
static let vector = ("[" *> _form.zeroOrMore.spacesAround() <* string("]").orThrow(.unbalanced(expected: "]"))).map { Expr.vector($0) }
|
||||
|
||||
static let hashmap = ("{" *> (hashmapKey <*> _form).zeroOrMore.spacesAround() <* string("}").orThrow(.unbalanced(expected: "}"))).map(makeExprHashmap)
|
||||
static func makeExprHashmap(_ xs: [(Expr, Expr)]) -> Expr {
|
||||
@ -95,12 +95,16 @@ private extension Parsers {
|
||||
static let name = (symbolHead <*> symbolRest.zeroOrMore).map { String($0) + String($1) }
|
||||
static let keyword = (":" *> name).map { Expr.string(String(keywordMagic) + $0) }
|
||||
|
||||
static let quote = ("'" *> _form).map { Expr.list([.symbol("quote"), $0]) }
|
||||
static let quasiquote = ("`" *> _form).map { Expr.list([.symbol("quasiquote"), $0]) }
|
||||
static let spliceUnquote = ("~@" *> _form).map { Expr.list([.symbol("splice-unquote"), $0]) }
|
||||
static let unquote = ("~" *> _form).map { Expr.list([.symbol("unquote"), $0]) }
|
||||
static let deref = ("@" *> _form).map { Expr.list([.symbol("deref"), $0]) }
|
||||
static let meta = ("^" *> hashmap <*> _form).map { Expr.list([.symbol("with-meta"), $1, $0]) }
|
||||
private static func expandMacros(_ symbolName: String, _ rest: Expr...) -> Expr {
|
||||
return Expr.list([.symbol(symbolName)] + rest)
|
||||
}
|
||||
|
||||
static let quote = ("'" *> _form).map { expandMacros("quote", $0) }
|
||||
static let quasiquote = ("`" *> _form).map { expandMacros("quasiquote", $0) }
|
||||
static let spliceUnquote = ("~@" *> _form).map { expandMacros("splice-unquote", $0) }
|
||||
static let unquote = ("~" *> _form).map { expandMacros("unquote", $0) }
|
||||
static let deref = ("@" *> _form).map { expandMacros("deref", $0) }
|
||||
static let meta = ("^" *> hashmap <*> _form).map { expandMacros("with-meta", $1, $0) }
|
||||
|
||||
static let sugar = oneOf(
|
||||
quote,
|
||||
|
@ -8,13 +8,27 @@ public enum Expr {
|
||||
case null
|
||||
case string(String)
|
||||
case symbol(String)
|
||||
case list([Expr])
|
||||
case vector([Expr])
|
||||
case hashmap([String: Expr])
|
||||
indirect case list([Expr], Expr)
|
||||
indirect case vector([Expr], Expr)
|
||||
indirect case hashmap([String: Expr], Expr)
|
||||
case function(Func)
|
||||
case atom(Atom)
|
||||
}
|
||||
|
||||
public extension Expr {
|
||||
static func list(_ arr: [Expr]) -> Expr {
|
||||
return .list(arr, .null)
|
||||
}
|
||||
|
||||
static func vector(_ arr: [Expr]) -> Expr {
|
||||
return .vector(arr, .null)
|
||||
}
|
||||
|
||||
static func hashmap(_ data: [String: Expr]) -> Expr {
|
||||
return .hashmap(data, .null)
|
||||
}
|
||||
}
|
||||
|
||||
extension Expr: Equatable {
|
||||
public static func == (lhs: Self, rhs: Self) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
@ -51,13 +65,15 @@ final public class Func {
|
||||
public let ast: Expr?
|
||||
public let params: [String]
|
||||
public let env: Env?
|
||||
public var isMacro: Bool
|
||||
public let isMacro: Bool
|
||||
public let meta: Expr
|
||||
|
||||
public init(
|
||||
ast: Expr? = nil,
|
||||
params: [String] = [],
|
||||
env: Env? = nil,
|
||||
isMacro: Bool = false,
|
||||
meta: Expr = .null,
|
||||
run: @escaping ([Expr]) throws -> Expr
|
||||
) {
|
||||
self.run = run
|
||||
@ -65,6 +81,15 @@ final public class Func {
|
||||
self.params = params
|
||||
self.env = env
|
||||
self.isMacro = isMacro
|
||||
self.meta = meta
|
||||
}
|
||||
|
||||
public func asMacros() -> Func {
|
||||
return Func(ast: ast, params: params, env: env, isMacro: true, meta: meta, run: run)
|
||||
}
|
||||
|
||||
public func withMeta(_ meta: Expr) -> Func {
|
||||
return Func(ast: ast, params: params, env: env, isMacro: isMacro, meta: meta, run: run)
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,9 +101,15 @@ extension Func: Equatable {
|
||||
|
||||
final public class Atom {
|
||||
public var val: Expr
|
||||
public let meta: Expr
|
||||
|
||||
public init(_ val: Expr) {
|
||||
public init(_ val: Expr, meta: Expr = .null) {
|
||||
self.val = val
|
||||
self.meta = meta
|
||||
}
|
||||
|
||||
public func withMeta(_ meta: Expr) -> Atom {
|
||||
return Atom(val, meta: meta)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,9 +29,9 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
case let .symbol(name):
|
||||
let value = try env.get(name)
|
||||
return value
|
||||
case let .vector(values):
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values):
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case .list:
|
||||
return try eval_list(expr, env: env)
|
||||
@ -41,7 +41,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
}
|
||||
|
||||
func eval_list(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard case let .list(values) = expr else { fatalError() }
|
||||
guard case let .list(values, _) = expr else { fatalError() }
|
||||
|
||||
if values.isEmpty {
|
||||
return expr
|
||||
|
@ -29,9 +29,9 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
case let .symbol(name):
|
||||
let value = try env.get(name)
|
||||
return value
|
||||
case let .vector(values):
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values):
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case .list:
|
||||
return try eval_list(expr, env: env)
|
||||
@ -69,7 +69,7 @@ private func evalLetForm(_ values: [Expr], env: Env) throws -> Expr {
|
||||
guard values.count == 3 else { throw MalError("let*: invalid arguments") }
|
||||
|
||||
switch values[1] {
|
||||
case let .list(bindable), let .vector(bindable):
|
||||
case let .list(bindable, _), let .vector(bindable, _):
|
||||
let letEnv = Env(outer: env)
|
||||
|
||||
for i in stride(from: 0, to: bindable.count - 1, by: 2) {
|
||||
@ -86,7 +86,7 @@ private func evalLetForm(_ values: [Expr], env: Env) throws -> Expr {
|
||||
}
|
||||
|
||||
func eval_list(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard case let .list(values) = expr else { fatalError() }
|
||||
guard case let .list(values, _) = expr else { fatalError() }
|
||||
|
||||
if values.isEmpty {
|
||||
return expr
|
||||
|
@ -9,11 +9,11 @@ private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
|
||||
switch expr {
|
||||
case let .symbol(name):
|
||||
return try env.get(name)
|
||||
case let .vector(values):
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values):
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case let .list(ast):
|
||||
case let .list(ast, _):
|
||||
return .list(try ast.map { try eval($0, env: env) })
|
||||
default:
|
||||
return expr
|
||||
@ -22,7 +22,7 @@ private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
|
||||
|
||||
func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
|
||||
guard case let .list(ast) = expr else {
|
||||
guard case let .list(ast, _) = expr else {
|
||||
return try evalAst(expr, env: env)
|
||||
}
|
||||
if ast.isEmpty {
|
||||
@ -43,7 +43,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("let*") }
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(bindable), let .vector(bindable):
|
||||
case let .list(bindable, _), let .vector(bindable, _):
|
||||
let letEnv = Env(outer: env)
|
||||
|
||||
for i in stride(from: 0, to: bindable.count - 1, by: 2) {
|
||||
@ -81,7 +81,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError("fn*") }
|
||||
let binds: [String]
|
||||
switch ast[1] {
|
||||
case let .list(xs), let .vector(xs):
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
binds = try xs.map {
|
||||
guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") }
|
||||
return name
|
||||
@ -97,7 +97,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
return .function(f)
|
||||
|
||||
default:
|
||||
guard case let .list(evaluatedList) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .list(evaluatedList, _) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
|
||||
return try fn.run(Array(evaluatedList.dropFirst()))
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
|
||||
switch expr {
|
||||
case let .symbol(name):
|
||||
return try env.get(name)
|
||||
case let .vector(values):
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values):
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case let .list(ast):
|
||||
case let .list(ast, _):
|
||||
return .list(try ast.map { try eval($0, env: env) })
|
||||
default:
|
||||
return expr
|
||||
@ -27,7 +27,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
|
||||
while true {
|
||||
|
||||
guard case let .list(ast) = expr else {
|
||||
guard case let .list(ast, _) = expr else {
|
||||
return try evalAst(expr, env: env)
|
||||
}
|
||||
if ast.isEmpty {
|
||||
@ -48,7 +48,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("let*") }
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(bindable), let .vector(bindable):
|
||||
case let .list(bindable, _), let .vector(bindable, _):
|
||||
let letEnv = Env(outer: env)
|
||||
|
||||
for i in stride(from: 0, to: bindable.count - 1, by: 2) {
|
||||
@ -88,7 +88,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
let binds: [String]
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(xs), let .vector(xs):
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
binds = try xs.map {
|
||||
guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") }
|
||||
return name
|
||||
@ -106,7 +106,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
return .function(f)
|
||||
|
||||
default:
|
||||
guard case let .list(evaluatedList) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .list(evaluatedList, _) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
|
||||
|
||||
let args = Array(evaluatedList.dropFirst())
|
||||
|
@ -9,11 +9,11 @@ private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
|
||||
switch expr {
|
||||
case let .symbol(name):
|
||||
return try env.get(name)
|
||||
case let .vector(values):
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values):
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case let .list(ast):
|
||||
case let .list(ast, _):
|
||||
return .list(try ast.map { try eval($0, env: env) })
|
||||
default:
|
||||
return expr
|
||||
@ -27,7 +27,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
|
||||
while true {
|
||||
|
||||
guard case let .list(ast) = expr else {
|
||||
guard case let .list(ast, _) = expr else {
|
||||
return try evalAst(expr, env: env)
|
||||
}
|
||||
if ast.isEmpty {
|
||||
@ -48,7 +48,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("let*") }
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(bindable), let .vector(bindable):
|
||||
case let .list(bindable, _), let .vector(bindable, _):
|
||||
let letEnv = Env(outer: env)
|
||||
|
||||
for i in stride(from: 0, to: bindable.count - 1, by: 2) {
|
||||
@ -88,7 +88,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
let binds: [String]
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(xs), let .vector(xs):
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
binds = try xs.map {
|
||||
guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") }
|
||||
return name
|
||||
@ -106,7 +106,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
return .function(f)
|
||||
|
||||
default:
|
||||
guard case let .list(evaluatedList) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .list(evaluatedList, _) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
|
||||
|
||||
let args = Array(evaluatedList.dropFirst())
|
||||
|
@ -7,7 +7,7 @@ func read(_ s: String) throws -> Expr {
|
||||
|
||||
private func isPair(_ expr: Expr) -> Bool {
|
||||
switch expr {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return !values.isEmpty
|
||||
default:
|
||||
return false
|
||||
@ -16,7 +16,7 @@ private func isPair(_ expr: Expr) -> Bool {
|
||||
|
||||
private func asListOrVector(_ expr: Expr) -> [Expr]? {
|
||||
switch expr {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return values
|
||||
default:
|
||||
return nil
|
||||
@ -52,11 +52,11 @@ private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
|
||||
switch expr {
|
||||
case let .symbol(name):
|
||||
return try env.get(name)
|
||||
case let .vector(values):
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values):
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case let .list(ast):
|
||||
case let .list(ast, _):
|
||||
return .list(try ast.map { try eval($0, env: env) })
|
||||
default:
|
||||
return expr
|
||||
@ -70,7 +70,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
|
||||
while true {
|
||||
|
||||
guard case let .list(ast) = expr else {
|
||||
guard case let .list(ast, _) = expr else {
|
||||
return try evalAst(expr, env: env)
|
||||
}
|
||||
if ast.isEmpty {
|
||||
@ -91,7 +91,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("let*") }
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(bindable), let .vector(bindable):
|
||||
case let .list(bindable, _), let .vector(bindable, _):
|
||||
let letEnv = Env(outer: env)
|
||||
|
||||
for i in stride(from: 0, to: bindable.count - 1, by: 2) {
|
||||
@ -139,7 +139,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
let binds: [String]
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(xs), let .vector(xs):
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
binds = try xs.map {
|
||||
guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") }
|
||||
return name
|
||||
@ -157,7 +157,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
return .function(f)
|
||||
|
||||
default:
|
||||
guard case let .list(evaluatedList) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .list(evaluatedList, _) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
|
||||
|
||||
let args = Array(evaluatedList.dropFirst())
|
||||
|
@ -7,7 +7,7 @@ func read(_ s: String) throws -> Expr {
|
||||
|
||||
private func isPair(_ expr: Expr) -> Bool {
|
||||
switch expr {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return !values.isEmpty
|
||||
default:
|
||||
return false
|
||||
@ -16,7 +16,7 @@ private func isPair(_ expr: Expr) -> Bool {
|
||||
|
||||
private func asListOrVector(_ expr: Expr) -> [Expr]? {
|
||||
switch expr {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return values
|
||||
default:
|
||||
return nil
|
||||
@ -49,7 +49,7 @@ private func quasiquote(_ expr: Expr) throws -> Expr {
|
||||
}
|
||||
|
||||
private func isMacroCall(_ expr: Expr, env: Env) -> Bool {
|
||||
if case let .list(ast) = expr,
|
||||
if case let .list(ast, _) = expr,
|
||||
case let .symbol(name) = ast.first,
|
||||
case let .function(fn) = try? env.get(name) {
|
||||
return fn.isMacro
|
||||
@ -60,7 +60,7 @@ private func isMacroCall(_ expr: Expr, env: Env) -> Bool {
|
||||
private func macroExpand(_ expr: Expr, env: Env) throws -> Expr {
|
||||
var expr = expr
|
||||
while true {
|
||||
guard case let .list(ast) = expr,
|
||||
guard case let .list(ast, _) = expr,
|
||||
case let .symbol(name) = ast.first,
|
||||
case let .function(fn) = try? env.get(name),
|
||||
fn.isMacro else {
|
||||
@ -76,11 +76,11 @@ private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
|
||||
switch expr {
|
||||
case let .symbol(name):
|
||||
return try env.get(name)
|
||||
case let .vector(values):
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values):
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case let .list(ast):
|
||||
case let .list(ast, _):
|
||||
return .list(try ast.map { try eval($0, env: env) })
|
||||
default:
|
||||
return expr
|
||||
@ -96,7 +96,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
|
||||
expr = try macroExpand(expr, env: env)
|
||||
|
||||
guard case let .list(ast) = expr else {
|
||||
guard case let .list(ast, _) = expr else {
|
||||
return try evalAst(expr, env: env)
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("let*") }
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(bindable), let .vector(bindable):
|
||||
case let .list(bindable, _), let .vector(bindable, _):
|
||||
let letEnv = Env(outer: env)
|
||||
|
||||
for i in stride(from: 0, to: bindable.count - 1, by: 2) {
|
||||
@ -145,10 +145,10 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("defmacro!") }
|
||||
guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments("defmacro!") }
|
||||
|
||||
guard case let .function(val) = try eval(ast[2], env: env) else { throw MalError.invalidArguments("defmacro!") }
|
||||
val.isMacro = true
|
||||
env.set(forKey: name, val: .function(val))
|
||||
return .function(val)
|
||||
guard case let .function(fn) = try eval(ast[2], env: env) else { throw MalError.invalidArguments("defmacro!") }
|
||||
let macros = fn.asMacros()
|
||||
env.set(forKey: name, val: .function(macros))
|
||||
return .function(macros)
|
||||
|
||||
case .symbol("macroexpand"):
|
||||
guard ast.count == 2 else { throw MalError.invalidArguments("macroexpand") }
|
||||
@ -179,7 +179,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
let binds: [String]
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(xs), let .vector(xs):
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
binds = try xs.map {
|
||||
guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") }
|
||||
return name
|
||||
@ -197,7 +197,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
return .function(f)
|
||||
|
||||
default:
|
||||
guard case let .list(evaluatedList) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .list(evaluatedList, _) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
|
||||
|
||||
let args = Array(evaluatedList.dropFirst())
|
||||
|
@ -7,7 +7,7 @@ func read(_ s: String) throws -> Expr {
|
||||
|
||||
private func isPair(_ expr: Expr) -> Bool {
|
||||
switch expr {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return !values.isEmpty
|
||||
default:
|
||||
return false
|
||||
@ -16,7 +16,7 @@ private func isPair(_ expr: Expr) -> Bool {
|
||||
|
||||
private func asListOrVector(_ expr: Expr) -> [Expr]? {
|
||||
switch expr {
|
||||
case let .list(values), let .vector(values):
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return values
|
||||
default:
|
||||
return nil
|
||||
@ -51,7 +51,7 @@ private func quasiquote(_ expr: Expr) throws -> Expr {
|
||||
private func macroExpand(_ expr: Expr, env: Env) throws -> Expr {
|
||||
var expr = expr
|
||||
while true {
|
||||
guard case let .list(ast) = expr,
|
||||
guard case let .list(ast, _) = expr,
|
||||
case let .symbol(name) = ast.first,
|
||||
case let .function(fn) = try? env.get(name),
|
||||
fn.isMacro else {
|
||||
@ -67,11 +67,11 @@ private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
|
||||
switch expr {
|
||||
case let .symbol(name):
|
||||
return try env.get(name)
|
||||
case let .vector(values):
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values):
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case let .list(ast):
|
||||
case let .list(ast, _):
|
||||
return .list(try ast.map { try eval($0, env: env) })
|
||||
default:
|
||||
return expr
|
||||
@ -87,7 +87,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
|
||||
expr = try macroExpand(expr, env: env)
|
||||
|
||||
guard case let .list(ast) = expr else {
|
||||
guard case let .list(ast, _) = expr else {
|
||||
return try evalAst(expr, env: env)
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("let*") }
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(bindable), let .vector(bindable):
|
||||
case let .list(bindable, _), let .vector(bindable, _):
|
||||
let letEnv = Env(outer: env)
|
||||
|
||||
for i in stride(from: 0, to: bindable.count - 1, by: 2) {
|
||||
@ -136,10 +136,10 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("defmacro!") }
|
||||
guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments("defmacro!") }
|
||||
|
||||
guard case let .function(val) = try eval(ast[2], env: env) else { throw MalError.invalidArguments("defmacro!") }
|
||||
val.isMacro = true
|
||||
env.set(forKey: name, val: .function(val))
|
||||
return .function(val)
|
||||
guard case let .function(fn) = try eval(ast[2], env: env) else { throw MalError.invalidArguments("defmacro!") }
|
||||
let macros = fn.asMacros()
|
||||
env.set(forKey: name, val: .function(macros))
|
||||
return .function(macros)
|
||||
|
||||
case .symbol("macroexpand"):
|
||||
guard ast.count == 2 else { throw MalError.invalidArguments("macroexpand") }
|
||||
@ -151,7 +151,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
continue
|
||||
}
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("try*") }
|
||||
guard case let .list(values) = ast[2], values.count == 3 else { throw MalError.invalidArguments("try*") }
|
||||
guard case let .list(values, _) = ast[2], values.count == 3 else { throw MalError.invalidArguments("try*") }
|
||||
guard case .symbol("catch*") = values[0] else { throw MalError.invalidArguments("try*") }
|
||||
guard case let .symbol(bind) = values[1] else { throw MalError.invalidArguments("catch*") }
|
||||
|
||||
@ -189,7 +189,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
let binds: [String]
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(xs), let .vector(xs):
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
binds = try xs.map {
|
||||
guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") }
|
||||
return name
|
||||
@ -207,7 +207,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
return .function(f)
|
||||
|
||||
default:
|
||||
guard case let .list(evaluatedList) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .list(evaluatedList, _) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
|
||||
|
||||
let args = Array(evaluatedList.dropFirst())
|
||||
|
265
swift5/Sources/stepA_mal/main.swift
Normal file
265
swift5/Sources/stepA_mal/main.swift
Normal file
@ -0,0 +1,265 @@
|
||||
import Foundation
|
||||
import core
|
||||
|
||||
func read(_ s: String) throws -> Expr {
|
||||
return try Reader.read(s)
|
||||
}
|
||||
|
||||
private func isPair(_ expr: Expr) -> Bool {
|
||||
switch expr {
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return !values.isEmpty
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private func asListOrVector(_ expr: Expr) -> [Expr]? {
|
||||
switch expr {
|
||||
case let .list(values, _), let .vector(values, _):
|
||||
return values
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func quasiquote(_ expr: Expr) throws -> Expr {
|
||||
if !isPair(expr) {
|
||||
return .list([.symbol("quote"), expr])
|
||||
}
|
||||
guard let ast = asListOrVector(expr), !ast.isEmpty else {
|
||||
throw MalError.invalidArguments("quasiquote")
|
||||
}
|
||||
|
||||
if case .symbol("unquote") = ast[0] {
|
||||
guard ast.count > 1 else { throw MalError.invalidArguments("unquote") }
|
||||
return ast[1]
|
||||
}
|
||||
|
||||
if isPair(ast[0]), let ast0 = asListOrVector(ast[0]) {
|
||||
if case .symbol("splice-unquote") = ast0.first {
|
||||
guard ast0.count > 1 else { throw MalError.invalidArguments("splice-unquote") }
|
||||
let rest = try quasiquote(.list(Array(ast[1...])))
|
||||
return .list([.symbol("concat"), ast0[1], rest])
|
||||
}
|
||||
}
|
||||
|
||||
let rest = try quasiquote(.list(Array(ast[1...])))
|
||||
return .list([.symbol("cons"), try quasiquote(ast[0]), rest])
|
||||
}
|
||||
|
||||
private func macroExpand(_ expr: Expr, env: Env) throws -> Expr {
|
||||
var expr = expr
|
||||
while true {
|
||||
guard case let .list(ast, _) = expr,
|
||||
case let .symbol(name) = ast.first,
|
||||
case let .function(fn) = try? env.get(name),
|
||||
fn.isMacro else {
|
||||
break
|
||||
}
|
||||
|
||||
expr = try fn.run(Array(ast.dropFirst()))
|
||||
}
|
||||
return expr
|
||||
}
|
||||
|
||||
private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
|
||||
switch expr {
|
||||
case let .symbol(name):
|
||||
return try env.get(name)
|
||||
case let .vector(values, _):
|
||||
return .vector(try values.map { try eval($0, env: env) })
|
||||
case let .hashmap(values, _):
|
||||
return .hashmap(try values.mapValues { try eval($0, env: env) })
|
||||
case let .list(ast, _):
|
||||
return .list(try ast.map { try eval($0, env: env) })
|
||||
default:
|
||||
return expr
|
||||
}
|
||||
}
|
||||
|
||||
func eval(_ expr: Expr, env: Env) throws -> Expr {
|
||||
|
||||
var env = env
|
||||
var expr = expr
|
||||
|
||||
while true {
|
||||
|
||||
expr = try macroExpand(expr, env: env)
|
||||
|
||||
guard case let .list(ast, _) = expr else {
|
||||
return try evalAst(expr, env: env)
|
||||
}
|
||||
|
||||
if ast.isEmpty {
|
||||
return expr
|
||||
}
|
||||
|
||||
switch ast[0] {
|
||||
|
||||
case .symbol("def!"):
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("def!") }
|
||||
guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments("def!") }
|
||||
|
||||
let val = try eval(ast[2], env: env)
|
||||
env.set(forKey: name, val: val)
|
||||
return val
|
||||
|
||||
case .symbol("let*"):
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("let*") }
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(bindable, _), let .vector(bindable, _):
|
||||
let letEnv = Env(outer: env)
|
||||
|
||||
for i in stride(from: 0, to: bindable.count - 1, by: 2) {
|
||||
guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments("let*") }
|
||||
let value = bindable[i + 1]
|
||||
letEnv.set(forKey: key, val: try eval(value, env: letEnv))
|
||||
}
|
||||
|
||||
expr = ast[2]
|
||||
env = letEnv
|
||||
default:
|
||||
throw MalError.invalidArguments("let*")
|
||||
}
|
||||
|
||||
case .symbol("quote"):
|
||||
guard ast.count == 2 else { throw MalError.invalidArguments("quote") }
|
||||
return ast[1]
|
||||
|
||||
case .symbol("quasiquote"):
|
||||
guard ast.count == 2 else { throw MalError.invalidArguments("quasiquote") }
|
||||
expr = try quasiquote(ast[1])
|
||||
|
||||
case .symbol("defmacro!"):
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("defmacro!") }
|
||||
guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments("defmacro!") }
|
||||
|
||||
guard case let .function(fn) = try eval(ast[2], env: env) else { throw MalError.invalidArguments("defmacro!") }
|
||||
let macros = fn.asMacros()
|
||||
env.set(forKey: name, val: .function(macros))
|
||||
return .function(macros)
|
||||
|
||||
case .symbol("macroexpand"):
|
||||
guard ast.count == 2 else { throw MalError.invalidArguments("macroexpand") }
|
||||
return try macroExpand(ast[1], env: env)
|
||||
|
||||
case .symbol("try*"):
|
||||
if ast.count == 2 {
|
||||
expr = ast[1]
|
||||
continue
|
||||
}
|
||||
guard ast.count == 3 else { throw MalError.invalidArguments("try*") }
|
||||
guard case let .list(values, _) = ast[2], values.count == 3 else { throw MalError.invalidArguments("try*") }
|
||||
guard case .symbol("catch*") = values[0] else { throw MalError.invalidArguments("try*") }
|
||||
guard case let .symbol(bind) = values[1] else { throw MalError.invalidArguments("catch*") }
|
||||
|
||||
do {
|
||||
expr = try eval(ast[1], env: env)
|
||||
} catch {
|
||||
let malErr = (error as? Expr) ?? .string(error.localizedDescription)
|
||||
let newEnv = try Env(binds: [bind], exprs: [malErr], outer: env)
|
||||
env = newEnv
|
||||
expr = values[2]
|
||||
}
|
||||
|
||||
case .symbol("do"):
|
||||
let exprsToEval = ast.dropFirst()
|
||||
guard !exprsToEval.isEmpty else { throw MalError.invalidArguments("do") }
|
||||
_ = try exprsToEval.dropLast().map { try eval($0, env: env) }
|
||||
expr = exprsToEval.last!
|
||||
|
||||
case .symbol("if"):
|
||||
guard 3...4 ~= ast.count else { throw MalError.invalidArguments("if") }
|
||||
|
||||
switch try eval(ast[1], env: env) {
|
||||
case .bool(false), .null:
|
||||
if let falseBranch = ast[safe: 3] {
|
||||
expr = falseBranch
|
||||
} else {
|
||||
expr = .null
|
||||
}
|
||||
default:
|
||||
expr = ast[2]
|
||||
}
|
||||
|
||||
case .symbol("fn*"):
|
||||
guard ast.count == 3 else { throw MalError("fn*") }
|
||||
let binds: [String]
|
||||
|
||||
switch ast[1] {
|
||||
case let .list(xs, _), let .vector(xs, _):
|
||||
binds = try xs.map {
|
||||
guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") }
|
||||
return name
|
||||
}
|
||||
default:
|
||||
throw MalError.invalidArguments("fn*")
|
||||
}
|
||||
|
||||
let run: ([Expr]) throws -> Expr = { args in
|
||||
let fEnv = try Env(binds: binds, exprs: args, outer: env)
|
||||
return try eval(ast[2], env: fEnv)
|
||||
}
|
||||
|
||||
let f = Func(ast: ast[2], params: binds, env: env, run: run)
|
||||
return .function(f)
|
||||
|
||||
default:
|
||||
guard case let .list(evaluatedList, _) = try evalAst(expr, env: env) else { fatalError() }
|
||||
guard case let .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
|
||||
|
||||
let args = Array(evaluatedList.dropFirst())
|
||||
if let ast = fn.ast, let fnEnv = fn.env {
|
||||
let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)
|
||||
env = newEnv
|
||||
expr = ast
|
||||
} else {
|
||||
return try fn.run(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func print(_ expr: Expr) -> String {
|
||||
return Expr.print(expr)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func rep(_ s: String, env: Env) -> String {
|
||||
do {
|
||||
let expr = try read(s)
|
||||
let resExpr = try eval(expr, env: env)
|
||||
let resultStr = print(resExpr)
|
||||
return resultStr
|
||||
} catch {
|
||||
return error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
let replEnv: Env = Env(data: Core.ns.data)
|
||||
|
||||
replEnv.set(forKey: "eval", val: .function(Func { args in
|
||||
guard let expr = args.first else { throw MalError.invalidArguments("eval") }
|
||||
return try eval(expr, env: replEnv)
|
||||
}))
|
||||
replEnv.set(forKey: "*ARGV*", val: .list(CommandLine.arguments.dropFirst(2).map(Expr.string)))
|
||||
replEnv.set(forKey: "*host-language*", val: .string("swift5"))
|
||||
|
||||
rep("(def! not (fn* (a) (if a false true)))", env: replEnv)
|
||||
rep(#"(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))"#, env: replEnv)
|
||||
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)))))))"#, env: replEnv)
|
||||
|
||||
if CommandLine.arguments.count > 1 {
|
||||
rep("(load-file \"" + CommandLine.arguments[1] + "\")", env: replEnv)
|
||||
exit(0)
|
||||
}
|
||||
|
||||
rep(#"(println (str "Mal [" *host-language* "]"))"#, env: replEnv)
|
||||
|
||||
while true {
|
||||
print("user> ", terminator: "")
|
||||
guard let s = readLine() else { break }
|
||||
print(rep(s, env: replEnv))
|
||||
}
|
Loading…
Reference in New Issue
Block a user