1
1
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:
Oleg Montak 2019-11-07 20:22:41 +03:00
parent 07e8a86d46
commit 590d589b07
14 changed files with 532 additions and 101 deletions

View File

@ -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"])
]
)

View File

@ -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 {

View File

@ -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):

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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()))
}

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View 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))
}