1
1
mirror of https://github.com/kanaka/mal.git synced 2024-08-16 09:10:48 +03:00

swift5: refactor a bit

This commit is contained in:
Oleg Montak 2019-11-08 20:29:42 +03:00
parent 6aa9c258a5
commit b667adf5e3
18 changed files with 187 additions and 192 deletions

View File

@ -1026,8 +1026,7 @@ has been tested with Swift 5.1.1 release.
```
cd swift5
make
./stepX_YYY
swift run stepX_YYY
```
### Tcl 8.6

View File

@ -35,7 +35,7 @@ ENV SWIFT_PREFIX swift-5.1.1-RELEASE
ENV SWIFT_RELEASE ${SWIFT_PREFIX}-ubuntu16.04
RUN cd /opt && \
curl -O https://swift.org/builds/swift-4.2.3-release/ubuntu1604/${SWIFT_PREFIX}/${SWIFT_RELEASE}.tar.gz && \
curl -O https://swift.org/builds/swift-5.1.1-release/ubuntu1604/${SWIFT_PREFIX}/${SWIFT_RELEASE}.tar.gz && \
tar xvzf ${SWIFT_RELEASE}.tar.gz && \
rm ${SWIFT_RELEASE}.tar.gz

View File

@ -2,26 +2,22 @@ import Foundation
private extension Func {
private static func hashMapDataFrom(_ args: [Expr]) throws -> [String: Expr] {
guard args.count.isMultiple(of: 2) else { throw MalError("invalid arguments") }
guard args.count.isMultiple(of: 2) else { throw MalError.invalidArguments() }
var data: [String: Expr] = [:]
for i in stride(from: 0, to: args.count - 1, by: 2) {
guard case let .string(key) = args[i] else { throw MalError("invalid arguments") }
guard case let .string(key) = args[i] else { throw MalError.invalidArguments() }
let value = args[i + 1]
data[key] = value
}
return data
}
static let notImplemented = Func { args in
throw MalError("not implemented")
}
static func infixOperation(_ op: @escaping (Int, Int) -> Int) -> Func {
static func intOperation(_ 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") }
case let .number(b) = args[1] else { throw MalError.invalidArguments() }
return .number(op(a, b))
}
@ -31,7 +27,7 @@ private extension 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") }
case let .number(b) = args[1] else { throw MalError.invalidArguments() }
return .bool(op(a, b))
}
@ -135,7 +131,7 @@ private extension Func {
}
static let swap = Func { args in
guard args.count >= 2 else { throw MalError.invalidArguments("reset!") }
guard args.count >= 2 else { throw MalError.invalidArguments("swap!") }
guard case let .atom(atom) = args[0] else { throw MalError.invalidArguments("swap!") }
guard case let .function(fn) = args[1] else { throw MalError.invalidArguments("swap!") }
let otherArgs = args.dropFirst(2)
@ -491,10 +487,10 @@ private extension Func {
}
private let data: [String: Expr] = [
"+": .function(.infixOperation(+)),
"-": .function(.infixOperation(-)),
"*": .function(.infixOperation(*)),
"/": .function(.infixOperation(/)),
"+": .function(.intOperation(+)),
"-": .function(.intOperation(-)),
"*": .function(.intOperation(*)),
"/": .function(.intOperation(/)),
"prn": .function(.prn),
"println": .function(.println),
"pr-str": .function(.prStr),

View File

@ -16,11 +16,11 @@ public class Env {
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") }
guard let key = binds[safe: i + 1] else { throw MalError.invalidVariadicFunction() }
data[key] = .list(Array(exprs[i...]))
break
}
guard let exp = exprs[safe: i] else { throw MalError("function call: invalid arguments") }
guard let exp = exprs[safe: i] else { throw MalError.invalidArguments() }
data[bindName] = exp
}
}
@ -30,7 +30,7 @@ public class Env {
}
public func get(_ key: String) throws -> Expr {
guard let val = find(key) else { throw MalError("'\(key)' not found") }
guard let val = find(key) else { throw MalError.symbolNotFound(key) }
return val
}

View File

@ -13,15 +13,6 @@ public struct MalError: Error, LocalizedError {
}
extension MalError {
public static func arityMismath(name: String, expected: String, given: Int) -> MalError {
let message = """
\(name): arity mismatch
expected: \(expected)
given: \(given)
"""
return MalError(message)
}
public static func unbalanced(expected: String) -> MalError {
return MalError("unbalanced: expected \(expected)")
}
@ -34,9 +25,29 @@ extension MalError {
return MalError("\(name): invalid arguments")
}
public static func invalidArguments() -> MalError {
return MalError("invalid arguments")
}
public static func outOfRange() -> MalError {
return MalError("index out of range")
}
public static func invalidFunctionCall(_ expr: Expr) -> MalError {
return MalError("not a function: \(expr)")
}
public static func symbolNotFound(_ s: String) -> MalError {
return MalError("'\(s)' not found")
}
public static func invalidVariadicFunction() -> MalError {
return MalError("invalid variadic function definition")
}
public static func reader() -> MalError {
return MalError("can't parse")
}
}
extension Expr: Error, LocalizedError {

View File

@ -51,6 +51,9 @@ extension Parsers {
static func string(excluding string: String) -> Parser<String> {
char(excluding: string).oneOrMore.map { String($0) }
}
static let digit = char(from: "0123456789")
static let naturalNumber = digit.oneOrMore.map { Int(String($0)) }
}
extension Parser: ExpressibleByStringLiteral, ExpressibleByUnicodeScalarLiteral, ExpressibleByExtendedGraphemeClusterLiteral where A == Void {
@ -126,11 +129,6 @@ extension Parser {
var oneOrMore: Parser<[A]> {
zeroOrMore.map { $0.isEmpty ? nil : $0 }
}
/// Matches of the parser produces no matches (inverts the parser).
var zero: Parser<Void> {
map { _ in nil }
}
}
// MARK: - Parser (Optional)
@ -144,15 +142,6 @@ func optional<A>(_ parser: Parser<A>) -> Parser<A?> {
}
}
func optional(_ parser: Parser<Void>) -> Parser<Bool> {
Parser<Bool> { str -> (Bool, Substring)? in
guard let match = try parser.parse(str) else {
return (false, str) // Return empty match without consuming any characters
}
return (true, match.1)
}
}
// MARK: - Parser (Error Reporting)
extension Parser {

View File

@ -1,6 +1,7 @@
import Foundation
extension Expr {
public static func print(readable: Bool = true, _ expr: Expr) -> String {
let print = curry(Self.print)(readable)

View File

@ -3,7 +3,7 @@ import Foundation
public enum Reader {
public static func read(_ str: String) throws -> Expr {
return try Parsers.expr.orThrow(MalError("Can't parse")).parse(str)!
return try Parsers.expr.orThrow(MalError.reader()).parse(str)!
}
}
@ -11,20 +11,6 @@ private extension Parsers {
static let expr = form <* endPattern
static let form = oneOf(
list,
vector,
hashmap,
eString,
number,
null,
bool,
symbol,
keyword,
sugar
).spacesAround()
static let _form: Parser<Expr> = lazy(form)
static let endPattern = oneOf(
end,
char(from: ")").zeroOrThrow(.unbalanced(unexpected: ")")),
@ -32,19 +18,32 @@ private extension Parsers {
char(from: "}").zeroOrThrow(.unbalanced(unexpected: "}"))
)
static let digit = char(from: "0123456789")
static let naturalNumber = digit.oneOrMore.map { Int(String($0)) }
static let number = (optional(char(from: "-")) <*> naturalNumber).map(makeNumber)
static func makeNumber(_ negative: Character?, value: Int) -> Expr {
let factor = negative != nil ? -1 : 1
return .number(value * factor)
}
static let form = oneOf(
list,
vector,
hashmap,
atom,
readerMacros
).ignoreAround()
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 _form: Parser<Expr> = lazy(form)
static let hashmap = ("{" *> (hashmapKey <*> _form).zeroOrMore.spacesAround() <* string("}").orThrow(.unbalanced(expected: "}"))).map(makeExprHashmap)
static func makeExprHashmap(_ xs: [(Expr, Expr)]) -> Expr {
static let atom = oneOf(
malString,
number,
null,
bool,
symbol,
keyword
)
static let list = ("(" *> _form.zeroOrMore.ignoreAround() <* string(")").orThrow(.unbalanced(expected: ")"))).map { Expr.list($0) }
static let vector = ("[" *> _form.zeroOrMore.ignoreAround() <* string("]").orThrow(.unbalanced(expected: "]"))).map { Expr.vector($0) }
// MARK: - Hashmap
static let hashmap = ("{" *> (hashmapKey <*> _form).zeroOrMore.ignoreAround() <* string("}").orThrow(.unbalanced(expected: "}"))).map(makeHashmap)
static func makeHashmap(_ xs: [(Expr, Expr)]) -> Expr {
var dict: [String: Expr] = [:]
for x in xs {
guard case let .string(key) = x.0 else { fatalError() }
@ -53,7 +52,17 @@ private extension Parsers {
return .hashmap(dict)
}
static let hashmapKey = oneOf(eString, keyword)
static let hashmapKey = oneOf(malString, keyword)
// MARK: - Number
static let number = (optional(char(from: "-")) <*> naturalNumber).map(makeNumber)
static func makeNumber(_ negative: Character?, value: Int) -> Expr {
let factor = negative != nil ? -1 : 1
return .number(value * factor)
}
// MARK: - String
static let stringContent = oneOf(
string(excluding: "\\\""),
@ -63,18 +72,24 @@ private extension Parsers {
string("\\").map { "\\" }
)
static let eString = (
"\"" *> stringContent.zeroOrMore <* string("\"").orThrow(.unbalanced(expected: "\""))
).map(makeExprString)
static func makeExprString(_ xs: [String]) -> Expr {
static let malString = ("\"" *> stringContent.zeroOrMore <* string("\"").orThrow(.unbalanced(expected: "\""))).map(makeMalString)
static func makeMalString(_ xs: [String]) -> Expr {
return .string(xs.joined())
}
// MARK: - Keyword
static let keyword = (":" *> name).map { Expr.string(String(keywordMagic) + $0) }
// MARK: - Symbol
static let symbolHead = char(excluding: "0123456789^`'\"#~@:%()[]{} \n\r\t,")
static let symbolRest = oneOf(symbolHead, char(from: "0123456789."))
static let name = (symbolHead <*> symbolRest.zeroOrMore).map { String($0) + String($1) }
static let symbol = name.map(Expr.symbol)
// MARK: - Bool
static let bool = name.map(makeBool)
static func makeBool(_ s: String) -> Expr? {
switch s {
@ -84,29 +99,24 @@ private extension Parsers {
}
}
// MARK: - Null
static let null = name.map(makeNull)
static func makeNull(_ s: String) -> Expr? {
if s == "nil" {
return .null
}
return nil
return s == "nil" ? .null : nil
}
static let name = (symbolHead <*> symbolRest.zeroOrMore).map { String($0) + String($1) }
static let keyword = (":" *> name).map { Expr.string(String(keywordMagic) + $0) }
// MARK: - Reader macros
private static func expandMacros(_ symbolName: String, _ rest: Expr...) -> Expr {
return Expr.list([.symbol(symbolName)] + rest)
}
static let quote = ("'" *> _form).readerMacros("quote")
static let quasiquote = ("`" *> _form).readerMacros("quasiquote")
static let spliceUnquote = ("~@" *> _form).readerMacros("splice-unquote")
static let unquote = ("~" *> _form).readerMacros("unquote")
static let deref = ("@" *> _form).readerMacros("deref")
static let meta = ("^" *> hashmap <*> _form).map { Expr.list([.symbol("with-meta"), $1, $0]) }
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(
static let readerMacros = oneOf(
quote,
quasiquote,
spliceUnquote,
@ -114,18 +124,23 @@ private extension Parsers {
deref,
meta
)
}
extension Parsers {
// MARK: - Ignore
static let whitespace = char(from: " \n\r\t,")
static let comment = char(from: ";") <* char(excluding: "\n\r").zeroOrMore
static let trash = oneOf(whitespace, comment)
static let ignore = oneOf(whitespace, comment)
}
extension Parser {
func spacesAround() -> Parser {
return (Parsers.trash.zeroOrMore *> self <* Parsers.trash.zeroOrMore)
func ignoreAround() -> Parser {
return (Parsers.ignore.zeroOrMore *> self <* Parsers.ignore.zeroOrMore)
}
}
extension Parser where A == Expr {
func readerMacros(_ s: String) -> Parser<Expr> {
return map { Expr.list([.symbol(s), $0]) }
}
}

View File

@ -60,6 +60,8 @@ extension Expr: Equatable {
}
}
// MARK: - Func
final public class Func {
public let run: ([Expr]) throws -> Expr
public let ast: Expr?
@ -99,6 +101,8 @@ extension Func: Equatable {
}
}
// MARK: - Atom
final public class Atom {
public var val: Expr
public let meta: Expr

View File

@ -7,7 +7,7 @@ extension 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") }
case let .number(b) = args[1] else { throw MalError.invalidArguments() }
return .number(op(a, b))
}
@ -24,32 +24,33 @@ func read(_ s: String) throws -> Expr {
return try Reader.read(s)
}
func eval(_ expr: Expr, env: Env) throws -> Expr {
private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
switch expr {
case let .symbol(name):
let value = try env.get(name)
return value
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 .list:
return try eval_list(expr, env: env)
case let .list(ast, _):
return .list(try ast.map { try eval($0, env: env) })
default:
return expr
}
}
func eval_list(_ expr: Expr, env: Env) throws -> Expr {
guard case let .list(values, _) = expr else { fatalError() }
func eval(_ expr: Expr, env: Env) throws -> Expr {
guard case let .list(values, _) = expr else {
return try evalAst(expr, env: env)
}
if values.isEmpty {
return expr
}
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()))
let ast = try values.map { try eval($0, env: env) }
guard case let .function(fn) = ast.first else { throw MalError.invalidFunctionCall(ast[0]) }
return try fn.run(Array(ast.dropFirst()))
}
func print(_ expr: Expr) -> String {

View File

@ -7,7 +7,7 @@ extension 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") }
case let .number(b) = args[1] else { throw MalError.invalidArguments() }
return .number(op(a, b))
}
@ -24,87 +24,66 @@ func read(_ s: String) throws -> Expr {
return try Reader.read(s)
}
func eval(_ expr: Expr, env: Env) throws -> Expr {
private func evalAst(_ expr: Expr, env: Env) throws -> Expr {
switch expr {
case let .symbol(name):
let value = try env.get(name)
return value
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 .list:
return try eval_list(expr, env: env)
case let .list(ast, _):
return .list(try ast.map { try eval($0, env: env) })
default:
return expr
}
}
private func isTaggedList(_ values: [Expr], tagName: String) -> Bool {
if case let .symbol(name) = values.first, name == tagName {
return true
func eval(_ expr: Expr, env: Env) throws -> Expr {
guard case let .list(ast, _) = expr else {
return try evalAst(expr, env: env)
}
if ast.isEmpty {
return expr
}
return false
}
private func isDefinition(_ values: [Expr]) -> Bool {
return isTaggedList(values, tagName: "def!")
}
switch ast[0] {
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") }
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 expToEval = values[2]
let val = try eval(expToEval, env: env)
env.set(forKey: name, val: val)
return val
}
let val = try eval(ast[2], env: env)
env.set(forKey: name, val: val)
return val
private func isLetForm(_ values: [Expr]) -> Bool {
return isTaggedList(values, tagName: "let*")
}
case .symbol("let*"):
guard ast.count == 3 else { throw MalError.invalidArguments("let*") }
private func evalLetForm(_ values: [Expr], env: Env) throws -> Expr {
guard values.count == 3 else { throw MalError("let*: invalid arguments") }
switch ast[1] {
case let .list(bindable, _), let .vector(bindable, _):
let letEnv = Env(outer: env)
switch values[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))
}
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.invalidArguments("let*")
}
let expToEval = values[2]
return try eval(expToEval, env: letEnv)
default:
throw MalError("let*: invalid arguments")
guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() }
guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }
return try fn.run(Array(ast.dropFirst()))
}
}
func eval_list(_ expr: Expr, env: Env) throws -> Expr {
guard case let .list(values, _) = expr else { fatalError() }
if values.isEmpty {
return expr
}
if isDefinition(values) {
return try evalDefinition(values, env: env)
}
if isLetForm(values) {
return try evalLetForm(values, 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()))
}
func print(_ expr: Expr) -> String {
return Expr.print(expr)
}

View File

@ -78,7 +78,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
}
case .symbol("fn*"):
guard ast.count == 3 else { throw MalError("fn*") }
guard ast.count == 3 else { throw MalError.invalidArguments("fn*") }
let binds: [String]
switch ast[1] {
case let .list(xs, _), let .vector(xs, _):
@ -97,9 +97,9 @@ 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 .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
return try fn.run(Array(evaluatedList.dropFirst()))
guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() }
guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }
return try fn.run(Array(ast.dropFirst()))
}
}

View File

@ -84,7 +84,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
}
case .symbol("fn*"):
guard ast.count == 3 else { throw MalError("fn*") }
guard ast.count == 3 else { throw MalError.invalidArguments("fn*") }
let binds: [String]
switch ast[1] {
@ -106,10 +106,10 @@ 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 .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() }
guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }
let args = Array(evaluatedList.dropFirst())
let args = Array(ast.dropFirst())
if let ast = fn.ast, let fnEnv = fn.env {
let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)
env = newEnv

View File

@ -84,7 +84,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
}
case .symbol("fn*"):
guard ast.count == 3 else { throw MalError("fn*") }
guard ast.count == 3 else { throw MalError.invalidArguments("fn*") }
let binds: [String]
switch ast[1] {
@ -106,10 +106,10 @@ 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 .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() }
guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }
let args = Array(evaluatedList.dropFirst())
let args = Array(ast.dropFirst())
if let ast = fn.ast, let fnEnv = fn.env {
let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)
env = newEnv

View File

@ -135,7 +135,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
}
case .symbol("fn*"):
guard ast.count == 3 else { throw MalError("fn*") }
guard ast.count == 3 else { throw MalError.invalidArguments("fn*") }
let binds: [String]
switch ast[1] {
@ -157,10 +157,10 @@ 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 .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() }
guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }
let args = Array(evaluatedList.dropFirst())
let args = Array(ast.dropFirst())
if let ast = fn.ast, let fnEnv = fn.env {
let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)
env = newEnv

View File

@ -175,7 +175,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
}
case .symbol("fn*"):
guard ast.count == 3 else { throw MalError("fn*") }
guard ast.count == 3 else { throw MalError.invalidArguments("fn*") }
let binds: [String]
switch ast[1] {
@ -197,10 +197,10 @@ 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 .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() }
guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }
let args = Array(evaluatedList.dropFirst())
let args = Array(ast.dropFirst())
if let ast = fn.ast, let fnEnv = fn.env {
let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)
env = newEnv

View File

@ -185,7 +185,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
}
case .symbol("fn*"):
guard ast.count == 3 else { throw MalError("fn*") }
guard ast.count == 3 else { throw MalError.invalidArguments("fn*") }
let binds: [String]
switch ast[1] {
@ -207,10 +207,10 @@ 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 .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() }
guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }
let args = Array(evaluatedList.dropFirst())
let args = Array(ast.dropFirst())
if let ast = fn.ast, let fnEnv = fn.env {
let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)
env = newEnv

View File

@ -185,7 +185,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr {
}
case .symbol("fn*"):
guard ast.count == 3 else { throw MalError("fn*") }
guard ast.count == 3 else { throw MalError.invalidArguments("fn*") }
let binds: [String]
switch ast[1] {
@ -207,10 +207,10 @@ 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 .function(fn) = evaluatedList[0] else { throw MalError("not a function: \(evaluatedList[0])") }
guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() }
guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }
let args = Array(evaluatedList.dropFirst())
let args = Array(ast.dropFirst())
if let ast = fn.ast, let fnEnv = fn.env {
let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)
env = newEnv