swift5: stepA_mal
authorOleg Montak <montak.o@ya.ru>
Thu, 7 Nov 2019 17:22:41 +0000 (20:22 +0300)
committerOleg Montak <montak.o@ya.ru>
Fri, 8 Nov 2019 18:46:18 +0000 (21:46 +0300)
14 files changed:
swift5/Package.swift
swift5/Sources/core/Core.swift
swift5/Sources/core/Printer.swift
swift5/Sources/core/Reader.swift
swift5/Sources/core/Types.swift
swift5/Sources/step2_eval/main.swift
swift5/Sources/step3_env/main.swift
swift5/Sources/step4_if_fn_do/main.swift
swift5/Sources/step5_tco/main.swift
swift5/Sources/step6_file/main.swift
swift5/Sources/step7_quote/main.swift
swift5/Sources/step8_macros/main.swift
swift5/Sources/step9_try/main.swift
swift5/Sources/stepA_mal/main.swift [new file with mode: 0644]

index fcb396e..3f567c4 100644 (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"])
     ]
 )
index a2b3b59..73592c7 100644 (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 {
index 46676d6..91c8857 100644 (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):
index 8124017..cb6f74c 100644 (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,
index 22c1de7..8bb69e4 100644 (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)
     }
 }
 
index 2f07d0c..d4334ef 100644 (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
index 9c10dcf..4a1f5f9 100644 (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
index 2fac69a..583945c 100644 (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()))
     }
index fdf9c1e..99ece70 100644 (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())
index b0612d9..698d73d 100644 (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())
index 3b6a9fd..b08bcc6 100644 (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())
index 8934aaa..3d4ea47 100644 (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())
index ef9bdbf..4d0f264 100644 (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())
diff --git a/swift5/Sources/stepA_mal/main.swift b/swift5/Sources/stepA_mal/main.swift
new file mode 100644 (file)
index 0000000..132b1dc
--- /dev/null
@@ -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))
+}