.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.
.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"])
]
)
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,
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)
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)
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")
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")
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:
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
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([])
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")
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")
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 }))
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") }
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
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] = [
"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 {
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):
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 {
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,
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) {
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
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)
}
}
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)
}
}
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)
}
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
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)
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) {
}
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
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
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 {
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) {
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
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()))
}
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
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 {
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) {
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
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())
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
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 {
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) {
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
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())
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
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
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
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 {
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) {
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
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())
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
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
}
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
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 {
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
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)
}
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) {
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") }
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
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())
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
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
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 {
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
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)
}
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) {
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") }
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*") }
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
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())
--- /dev/null
+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))
+}