.executable(name: "step0_repl", targets: ["step0_repl"]),
.executable(name: "step1_read_print", targets: ["step1_read_print"]),
.executable(name: "step2_eval", targets: ["step2_eval"]),
- .executable(name: "step3_env", targets: ["step3_env"])
+ .executable(name: "step3_env", targets: ["step3_env"]),
+ .executable(name: "step4_if_fn_do", targets: ["step4_if_fn_do"])
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.target(name: "step0_repl", dependencies: ["core"]),
.target(name: "step1_read_print", dependencies: ["core"]),
.target(name: "step2_eval", dependencies: ["core"]),
- .target(name: "step3_env", dependencies: ["core"])
+ .target(name: "step3_env", dependencies: ["core"]),
+ .target(name: "step4_if_fn_do", dependencies: ["core"])
]
)
--- /dev/null
+import Foundation
+
+private extension Func {
+ static func infixOperation(_ op: @escaping (Int, Int) -> Int) -> Func {
+ return Func { args in
+ guard args.count == 2,
+ case let .number(a) = args[0],
+ case let .number(b) = args[1] else { throw MalError("invalid arguments") }
+
+ return .number(op(a, b))
+ }
+ }
+
+ static func comparisonOperation(_ op: @escaping (Int, Int) -> Bool) -> Func {
+ return Func { args in
+ guard args.count == 2,
+ case let .number(a) = args[0],
+ case let .number(b) = args[1] else { throw MalError("invalid arguments") }
+
+ return .bool(op(a, b))
+ }
+ }
+
+ static let prn = Func { args in
+ let printFunc = curry(Expr.print)(true)
+ let result = args.map(printFunc).joined(separator: " ")
+ print(result)
+ return .null
+ }
+
+ static let str = Func { args in
+ let printFunc = curry(Expr.print)(false)
+ let result = args.map(printFunc).joined(separator: "")
+ return .string(result)
+ }
+
+ static let prStr = Func { args in
+ let printFunc = curry(Expr.print)(true)
+ let result = args.map(printFunc).joined(separator: " ")
+ return .string(result)
+ }
+
+ static let println = Func { args in
+ let printFunc = curry(Expr.print)(false)
+ let result = args.map(printFunc).joined(separator: " ")
+ print(result)
+ return .null
+ }
+
+ static let list = Func { args in .list(args) }
+
+ static let isList = Func { args in
+ if case .list = args.first {
+ return .bool(true)
+ }
+ return .bool(false)
+ }
+
+ static let isEmpty = Func { args in
+ switch args.first {
+ case let .list(xs), let .vector(xs):
+ return .bool(xs.isEmpty)
+ default:
+ return .bool(false)
+ }
+ }
+
+ static let count = Func { args in
+ switch args.first {
+ case let .list(xs), let .vector(xs):
+ return .number(xs.count)
+ default:
+ return .number(0)
+ }
+ }
+
+ static let eq = Func { args in
+ guard args.count == 2 else { throw MalError("eq: invalid arguments") }
+ return args[0] == args[1] ? .bool(true) : .bool(false)
+ }
+}
+
+private let data: [String: Expr] = [
+ "+": .function(.infixOperation(+)),
+ "-": .function(.infixOperation(-)),
+ "*": .function(.infixOperation(*)),
+ "/": .function(.infixOperation(/)),
+ "prn": .function(.prn),
+ "println": .function(.println),
+ "pr-str": .function(.prStr),
+ "str": .function(.str),
+ "list": .function(.list),
+ "list?": .function(.isList),
+ "empty?": .function(.isEmpty),
+ "count": .function(.count),
+ "=": .function(.eq),
+ "<": .function(.comparisonOperation(<)),
+ "<=": .function(.comparisonOperation(<=)),
+ ">": .function(.comparisonOperation(>)),
+ ">=": .function(.comparisonOperation(>=))
+]
+
+public enum Core {
+ public static let ns: Env = Env.init(data: data, outer: nil)
+}
import Foundation
-public struct Env {
- private var _outer: [Env]
- private var outer: Env? {
- return _outer.first
- }
-
- private var data: [String: Expr]
+public class Env {
+ private var outer: Env?
+ public private(set) var data: [String: Expr]
public init(data: [String: Expr] = [:], outer: Env? = nil) {
- self._outer = outer != nil ? [outer!] : []
+ self.outer = outer
self.data = data
}
- public mutating func set(forKey key: String, val: Expr) {
+ public init(binds: [String], exprs: [Expr], outer: Env? = nil) throws {
+ self.outer = outer
+ self.data = [:]
+
+ for i in 0..<binds.count {
+ let bindName = binds[i]
+ if bindName == "&" {
+ guard let key = binds[safe: i + 1] else { throw MalError("invalid variadic function definition") }
+ data[key] = .list(Array(exprs[i...]))
+ break
+ }
+ guard let exp = exprs[safe: i] else { throw MalError("function call: invalid arguments") }
+ data[bindName] = exp
+ }
+ }
+
+ public func set(forKey key: String, val: Expr) {
data[key] = val
}
return printString(s, readable: readable)
case let .symbol(s):
return s
+ case let .bool(b):
+ return b ? "true" : "false"
+ case .null:
+ return "nil"
case .function:
- return "#function"
+ return "#<function>"
}
}
}
if s.first == keywordMagic {
return ":" + s.dropFirst()
}
- return "\"" + (readable ? unescape(s) : s) + "\""
+ return readable ? ("\"" + unescape(s) + "\"") : s
}
private func unescape(_ s: String) -> String {
Expr.print(self)
}
}
-
-private func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C {
- return { (a: A) -> (B) -> C in { (b: B) -> C in function(a, b) } }
-}
hashmap,
eString,
number,
+ null,
+ bool,
symbol,
keyword,
sugar
static let symbolRest = oneOf(symbolHead, char(from: "0123456789."))
static let symbol = name.map(Expr.symbol)
+ static let bool = name.map(makeBool)
+ static func makeBool(_ s: String) -> Expr? {
+ switch s {
+ case "true": return .bool(true)
+ case "false": return .bool(false)
+ default: return nil
+ }
+ }
+
+ static let null = name.map(makeNull)
+ static func makeNull(_ s: String) -> Expr? {
+ if s == "nil" {
+ return .null
+ }
+ return nil
+ }
+
static let name = (symbolHead <*> symbolRest.zeroOrMore).map { String($0) + String($1) }
static let keyword = (":" *> name).map { Expr.string(String(keywordMagic) + $0) }
public enum Expr {
case number(Int)
+ case bool(Bool)
+ case null
case string(String)
case symbol(String)
case list([Expr])
case function(Func)
}
+extension Expr: Equatable {
+ public static func == (lhs: Self, rhs: Self) -> Bool {
+ switch (lhs, rhs) {
+ case let (.number(a), .number(b)):
+ return a == b
+ case let (.bool(a), .bool(b)):
+ return a == b
+ case (.null, .null):
+ return true
+ case let (.string(a), .string(b)):
+ return a == b
+ case let (.symbol(a), .symbol(b)):
+ return a == b
+ case let (.list(a), .list(b)),
+ let (.vector(a), .vector(b)),
+ let (.list(a), .vector(b)),
+ let (.vector(a), .list(b)):
+ return a == b
+ case let (.hashmap(a), .hashmap(b)):
+ return a == b
+ case let (.function(a), .function(b)):
+ return a == b
+
+ default:
+ return false
+ }
+ }
+}
+
public struct Func {
public let run: ([Expr]) throws -> Expr
self.run = run
}
}
+
+extension Func: Equatable {
+ public static func == (lhs: Self, rhs: Self) -> Bool {
+ return false
+ }
+}
--- /dev/null
+import Foundation
+
+public func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C {
+ return { (a: A) -> (B) -> C in { (b: B) -> C in function(a, b) } }
+}
+
+public extension Collection {
+ subscript (safe index: Index) -> Element? {
+ return indices.contains(index) ? self[index] : nil
+ }
+}
import Foundation
import core
-
-// MARK: - Special Forms
-
extension Func {
static fileprivate func infixOperation(_ op: @escaping (Int, Int) -> Int) -> Func {
return try Reader.read(s)
}
-func eval(_ expr: Expr, env: inout Env) throws -> Expr {
+func eval(_ expr: Expr, env: Env) throws -> Expr {
switch expr {
case let .symbol(name):
let value = try env.get(name)
return value
case let .vector(values):
- return .vector(try values.map { try eval($0, env: &env) })
+ return .vector(try values.map { try eval($0, env: env) })
case let .hashmap(values):
- return .hashmap(try values.mapValues { try eval($0, env: &env) })
+ return .hashmap(try values.mapValues { try eval($0, env: env) })
case .list:
- return try eval_list(expr, env: &env)
+ return try eval_list(expr, env: env)
default:
return expr
}
return isTaggedList(values, tagName: "def!")
}
-private func evalDefinition(_ values: [Expr], env: inout Env) throws -> Expr {
+private func evalDefinition(_ values: [Expr], env: Env) throws -> Expr {
guard values.count == 3 else { throw MalError("def!: invalid arguments") }
guard case let .symbol(name) = values[1] else { throw MalError("def!: invalid arguments") }
let expToEval = values[2]
- let val = try eval(expToEval, env: &env)
+ let val = try eval(expToEval, env: env)
env.set(forKey: name, val: val)
return val
}
return isTaggedList(values, tagName: "let*")
}
-private func evalLetForm(_ values: [Expr], env: inout Env) throws -> Expr {
+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):
- var letEnv = Env(outer: env)
+ 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("let*: invalid arguments") }
let value = bindable[i + 1]
- letEnv.set(forKey: key, val: try eval(value, env: &letEnv))
+ letEnv.set(forKey: key, val: try eval(value, env: letEnv))
}
let expToEval = values[2]
- return try eval(expToEval, env: &letEnv)
+ return try eval(expToEval, env: letEnv)
default:
throw MalError("let*: invalid arguments")
}
}
-func eval_list(_ expr: Expr, env: inout Env) throws -> Expr {
+func eval_list(_ expr: Expr, env: Env) throws -> Expr {
guard case let .list(values) = expr else { fatalError() }
if values.isEmpty {
}
if isDefinition(values) {
- return try evalDefinition(values, env: &env)
+ return try evalDefinition(values, env: env)
}
if isLetForm(values) {
- return try evalLetForm(values, env: &env)
+ return try evalLetForm(values, env: env)
}
- let evaluated = try values.map { try eval($0, env: &env) }
+ let evaluated = try values.map { try eval($0, env: env) }
guard case let .function(fn) = evaluated.first else { throw MalError("not a function: \(evaluated.first!)") }
return try fn.run(Array(evaluated.dropFirst()))
}
return Expr.print(expr)
}
-func rep(_ s: String, env: inout Env) -> String {
+func rep(_ s: String, env: Env) -> String {
do {
let expr = try read(s)
- let resExpr = try eval(expr, env: &env)
+ let resExpr = try eval(expr, env: env)
let resultStr = print(resExpr)
return resultStr
} catch {
while true {
print("user> ", terminator: "")
guard let s = readLine() else { break }
- print(rep(s, env: &replEnv))
+ print(rep(s, env: replEnv))
}
--- /dev/null
+import Foundation
+import core
+
+func read(_ s: String) throws -> Expr {
+ return try Reader.read(s)
+}
+
+func eval(_ expr: Expr, env: Env) throws -> Expr {
+ switch expr {
+ case let .symbol(name):
+ let value = try env.get(name)
+ return value
+ 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 .list:
+ return try eval_list(expr, env: env)
+ default:
+ return expr
+ }
+}
+
+private func isTaggedList(_ ast: [Expr], tagName: String) -> Bool {
+ if case let .symbol(name) = ast.first, name == tagName {
+ return true
+ }
+ return false
+}
+
+private func isDefinition(_ ast: [Expr]) -> Bool {
+ return isTaggedList(ast, tagName: "def!")
+}
+
+private func evalDefinition(_ ast: [Expr], env: Env) throws -> Expr {
+ guard ast.count == 3 else { throw MalError("def!: invalid arguments") }
+ guard case let .symbol(name) = ast[1] else { throw MalError("def!: invalid arguments") }
+
+ let expToEval = ast[2]
+ let val = try eval(expToEval, env: env)
+ env.set(forKey: name, val: val)
+ return val
+}
+
+private func isLetForm(_ ast: [Expr]) -> Bool {
+ return isTaggedList(ast, tagName: "let*")
+}
+
+private func evalLetForm(_ ast: [Expr], env: Env) throws -> Expr {
+ guard ast.count == 3 else { throw MalError("let*: invalid arguments") }
+
+ 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("let*: invalid arguments") }
+ let value = bindable[i + 1]
+ letEnv.set(forKey: key, val: try eval(value, env: letEnv))
+ }
+
+ let expToEval = ast[2]
+ return try eval(expToEval, env: letEnv)
+ default:
+ throw MalError("let*: invalid arguments")
+ }
+}
+
+private func isDoForm(_ ast: [Expr]) -> Bool {
+ return isTaggedList(ast, tagName: "do")
+}
+
+private func evalDoForm(_ ast: [Expr], env: Env) throws -> Expr {
+ let exprsToEval = ast.dropFirst()
+ if exprsToEval.isEmpty { throw MalError("do: invalid arguments") }
+
+ return try exprsToEval.map { try eval($0, env: env) }.last!
+}
+
+private func isIfForm(_ ast: [Expr]) -> Bool {
+ return isTaggedList(ast, tagName: "if")
+}
+
+private func evalIfForm(_ ast: [Expr], env: Env) throws -> Expr {
+ guard 3...4 ~= ast.count else { throw MalError("if: invalid arguments") }
+
+ let condExpr = ast[1]
+ switch try eval(condExpr, env: env) {
+ case .bool(false), .null:
+ if let falseExpr = ast[safe: 3] {
+ return try eval(falseExpr, env: env)
+ }
+ return .null
+ default:
+ return try eval(ast[2], env: env)
+ }
+}
+
+private func isFnForm(_ values: [Expr]) -> Bool {
+ return isTaggedList(values, tagName: "fn*")
+}
+
+private func evalFnForm(_ ast: [Expr], env: Env) throws -> Expr {
+ guard ast.count == 3 else { throw MalError("fn*: invalid arguments") }
+ 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("fn*: invalid arguments") }
+ return name
+ }
+ default:
+ throw MalError("fn*: invalid arguments")
+ }
+
+ let f = Func { args in
+ let fEnv = try Env(binds: binds, exprs: args, outer: env)
+ return try eval(ast[2], env: fEnv)
+ }
+ return .function(f)
+}
+
+func eval_list(_ expr: Expr, env: Env) throws -> Expr {
+ guard case let .list(ast) = expr else { fatalError() }
+
+ if ast.isEmpty {
+ return expr
+ }
+
+ if isDefinition(ast) {
+ return try evalDefinition(ast, env: env)
+ }
+
+ if isLetForm(ast) {
+ return try evalLetForm(ast, env: env)
+ }
+
+ if isDoForm(ast) {
+ return try evalDoForm(ast, env: env)
+ }
+
+ if isIfForm(ast) {
+ return try evalIfForm(ast, env: env)
+ }
+
+ if isFnForm(ast) {
+ return try evalFnForm(ast, env: env)
+ }
+
+ let evaluated = try ast.map { try eval($0, env: env) }
+ guard case let .function(fn) = evaluated.first else { throw MalError("not a function: \(evaluated.first!)") }
+ return try fn.run(Array(evaluated.dropFirst()))
+}
+
+func print(_ expr: Expr) -> String {
+ return Expr.print(expr)
+}
+
+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)
+
+_ = rep("(def! not (fn* (a) (if a false true)))", env: replEnv)
+
+while true {
+ print("user> ", terminator: "")
+ guard let s = readLine() else { break }
+ print(rep(s, env: replEnv))
+}