Merge pull request #87 from keith-rollin/optimize
authorJoel Martin <github@martintribe.org>
Tue, 22 Sep 2015 02:34:36 +0000 (21:34 -0500)
committerJoel Martin <github@martintribe.org>
Tue, 22 Sep 2015 02:34:36 +0000 (21:34 -0500)
Update for Xcode 7.0

21 files changed:
swift/Makefile
swift/core.swift
swift/env.swift
swift/printer.swift
swift/reader.swift
swift/readline.swift
swift/step0_repl.swift
swift/step1_read_print.swift
swift/step2_eval.swift
swift/step3_env.swift
swift/step4_if_fn_do.swift
swift/step5_tco.swift
swift/step6_file.swift
swift/step7_quote.swift
swift/step8_macros.swift
swift/step9_try.swift
swift/stepA_mal.swift
swift/templates/step.swift
swift/types.swift [deleted file]
swift/types_class.swift [new file with mode: 0644]
swift/types_enum.swift [new file with mode: 0644]

index cc5de0d..886d0f7 100644 (file)
@@ -110,6 +110,12 @@ SRCS := $(wildcard ./step*.swift)
 #
 EXES := $(patsubst %.swift,%,$(SRCS))
 
+#
+# Also generate references to any debug-symbol directories we may make when -g
+# is specified.
+#
+DSYMS := $(patsubst %.swift,%.dSYM,$(SRCS))
+
 #
 # Given a name like "./step<#>_foo", return <#>.
 #
@@ -168,12 +174,21 @@ $(foreach SRC,$(SRCS),$(eval ss$(call get_step_number,$(SRC)): $(SRC)))
 #
 # Various helpful variables.
 #
-SWIFT          := swiftc
+DEV_DIR                := $(firstword $(wildcard /Applications/Xcode-beta.app /Applications/Xcode.app))
+SWIFT          := $(shell DEVELOPER_DIR="$(DEV_DIR)" xcrun --find swiftc)
+SDKROOT                := $(shell DEVELOPER_DIR="$(DEV_DIR)" xcrun --show-sdk-path)
 STEP_TEMPLATE  := ./templates/step.swift
 FILTER         := ./templates/filter_steps.sh
 UTIL_SRC       := $(filter-out $(STEP_TEMPLATE) $(SRCS),$(wildcard ./*.swift))
-SDKROOT                ?= $(shell xcrun --show-sdk-path)
-OPT            := #-Ounchecked
+ifndef TYPES
+TYPES          := CLASS
+endif
+ifeq ($(TYPES), ENUM)
+UTIL_SRC       := $(filter-out ./types_class.swift,$(UTIL_SRC))
+else
+UTIL_SRC       := $(filter-out ./types_enum.swift,$(UTIL_SRC))
+endif
+OPT            := -Ounchecked -whole-module-optimization
 DEBUG          := #-g
 EXTRA          := #-v
 COMMON         := $(UTIL_SRC) $(OPT) $(DEBUG) $(EXTRA) -import-objc-header ./bridging-header.h -L /usr/lib -ledit -sdk $(SDKROOT)
@@ -182,7 +197,7 @@ COMMON              := $(UTIL_SRC) $(OPT) $(DEBUG) $(EXTRA) -import-objc-header ./bridging-h
 # Build the executable from the input sources consisting of the appropriate
 # "step" file and the supporting files in $(UTIL_SRC).
 #
-$(EXES) : % : %.swift $(UTIL_SRC)
+$(EXES) : % : %.swift $(UTIL_SRC) ./Makefile
        @echo "Making    : $@"
        @$(SWIFT) $< $(COMMON) -o $@
 
@@ -195,10 +210,10 @@ $(SRCS) : % : $(STEP_TEMPLATE) ./Makefile
        @$(FILTER) $(call get_step_number,$@) $< $@
 
 #
-# Delete all of the built executables.
+# Delete all of the build output (other than generated "step" source files)
 #
 clean:
-       @rm -f $(EXES)
+       @rm -rf $(EXES) $(DSYMS)
 
 #
 # Display some variables for debugging.
@@ -206,6 +221,8 @@ clean:
 dump:
        @echo "   SRCS = $(SRCS)"
        @echo "   EXES = $(EXES)"
+       @echo "  DSYMS = $(DSYMS)"
        @echo "   UTIL = $(UTIL_SRC)"
+       @echo "  SWIFT = $(SWIFT)"
        @echo "SDKROOT = $(SDKROOT)"
        @echo "  STEPS = $(call get_all_step_numbers)"
dissimilarity index 83%
index 2a56880..adf1d9d 100644 (file)
-//******************************************************************************
-// MAL - core
-//******************************************************************************
-
-import Foundation
-
-typealias MalVarArgs = ArraySlice<MalVal>
-
-func fn_eq(obj1: MalVal, obj2: MalVal) -> Bool {
-    return obj1 == obj2
-}
-
-func fn_throw(exception: MalVal) -> MalVal {
-    return MalError(object: exception)
-}
-
-func fn_nilQ(obj: MalVal) -> Bool {
-    return is_nil(obj)
-}
-
-func fn_trueQ(obj: MalVal) -> Bool {
-    return is_true(obj)
-}
-
-func fn_falseQ(obj: MalVal) -> Bool {
-    return is_false(obj)
-}
-
-func fn_symbol(s: String) -> MalVal {
-    return MalSymbol(symbol: s)
-}
-
-func fn_symbolQ(obj: MalVal) -> Bool {
-    return is_symbol(obj)
-}
-
-func fn_keyword(s: MalVal) -> MalVal {
-    if is_keyword(s) {
-        return s
-    }
-    if is_string(s) {
-        return MalKeyword(keyword: (s as! MalString).value)
-    }
-    return MalError(message: "expected string or keyword")
-}
-
-func fn_keywordQ(obj: MalVal) -> Bool {
-    return is_keyword(obj)
-}
-
-func fn_prstr(args: MalVarArgs) -> String {
-    let args_str_array = args.map { pr_str($0, true) }
-    return " ".join(args_str_array)
-}
-
-func fn_str(args: MalVarArgs) -> String {
-    let args_str_array = args.map { pr_str($0, false) }
-    return "".join(args_str_array)
-}
-
-func fn_prn(args: MalVarArgs) {
-    let args_str_array = args.map { pr_str($0, true) }
-    let args_str = " ".join(args_str_array)
-    println(args_str)
-}
-
-func fn_println(args: MalVarArgs) {
-    let args_str_array = args.map { pr_str($0, false) }
-    let args_str = " ".join(args_str_array)
-    println(args_str)
-}
-
-func fn_readstring(s: String) -> MalVal {
-    return read_str(s)
-}
-
-func fn_readline(s: String) -> String? {
-    return _readline(s)
-}
-
-func fn_slurp(s: String) -> MalVal {
-    var err: NSError? = nil
-    let result = String(contentsOfFile:s, encoding: NSUTF8StringEncoding, error:&err)
-    if result == nil {
-        if err != nil {
-            return MalError(message: "error reading file \(s): \(err!.localizedDescription)")
-        }
-        return MalError(message: "unknown error reading file \(s)")
-    }
-    return MalString(unescaped: result!);
-}
-
-func fn_lt(arg1: Int64, arg2: Int64) -> Bool {
-    return arg1 < arg2
-}
-
-func fn_lte(arg1: Int64, arg2: Int64) -> Bool {
-    return arg1 <= arg2
-}
-
-func fn_gt(arg1: Int64, arg2: Int64) -> Bool {
-    return arg1 > arg2
-}
-
-func fn_gte(arg1: Int64, arg2: Int64) -> Bool {
-    return arg1 >= arg2
-}
-
-func fn_add(arg1: Int64, arg2: Int64) -> Int64 {
-    return arg1 + arg2
-}
-
-func fn_subtract(arg1: Int64, arg2: Int64) -> Int64 {
-    return arg1 - arg2
-}
-
-func fn_multiply(arg1: Int64, arg2: Int64) -> Int64 {
-    return arg1 * arg2
-}
-
-func fn_divide(arg1: Int64, arg2: Int64) -> Int64 {
-    return arg1 / arg2
-}
-
-func fn_timems() -> Int64 {
-    var time = timeval(tv_sec:0, tv_usec:0)
-    let res = gettimeofday(&time, nil)
-    if res == 0 {
-        return (Int64(time.tv_sec) * 1_000_000 + Int64(time.tv_usec)) / 1000
-    }
-    return -1
-}
-
-func fn_list(args: MalVarArgs) -> MalVal {
-    return MalList(slice: args)
-}
-
-func fn_listQ(obj: MalVal) -> Bool {
-    return is_list(obj)
-}
-
-func fn_vector(args: MalVarArgs) -> MalVal {
-    return MalVector(slice: args)
-}
-
-func fn_vectorQ(obj: MalVal) -> Bool {
-    return is_vector(obj)
-}
-
-func fn_hashmap(args: MalVarArgs) -> MalVal {
-    return MalHashMap(slice: args)
-}
-
-func fn_hashmapQ(obj: MalVal) -> Bool {
-    return is_hashmap(obj)
-}
-
-func fn_assoc(hash: MalHashMap, args: MalVarArgs) -> MalVal {
-   if args.count % 2 != 0 {
-       return MalError(message: "expected even number of elements, got \(args.count)")
-   }
-   var new_dictionary = hash.hash
-   for var index = 0; index < args.count; index += 2 {
-       new_dictionary[args[index]] = args[index + 1]
-   }
-   return MalHashMap(hash: new_dictionary)
-}
-
-func fn_dissoc(hash: MalHashMap, args: MalVarArgs) -> MalVal {
-   var new_dictionary = hash.hash
-   for value in args {
-       new_dictionary.removeValueForKey(value)
-   }
-   return MalHashMap(hash: new_dictionary)
-}
-
-func fn_get(obj: MalVal, key: MalVal) -> MalVal {
-    if is_vector(obj) {
-        if !is_integer(key) { return MalError(message: "expected integer key for get(vector), got \(key)") }
-        let as_vector = obj as! MalVector
-        let index = key as! MalInteger
-        if Int(index.value) >= as_vector.count { return MalError(message: "index out of range: \(index) >= \(as_vector.count)") }
-        return as_vector[Int(index.value)]
-    }
-    if is_hashmap(obj) {
-        let as_hash = obj as! MalHashMap
-        if let value = as_hash[key] { return value }
-        return MalNil()
-    }
-    if is_nil(obj) {
-        return obj
-    }
-    return MalError(message: "get called on unsupported type: \(obj)")
-}
-
-func fn_containsQ(obj: MalVal, key: MalVal) -> MalVal {
-    if is_vector(obj) {
-        if !is_integer(key) { return MalError(message: "expected integer key for contains(vector), got \(key)") }
-        let as_vector = obj as! MalVector
-        let index = key as! MalInteger
-        return Int(index.value) < as_vector.count ? MalTrue() : MalFalse()
-    }
-    if is_hashmap(obj) {
-        let as_hash = obj as! MalHashMap
-        return as_hash[key] != nil ? MalTrue() : MalFalse()
-    }
-    return MalError(message: "contains? called on unsupported type: \(obj)")
-}
-
-func fn_keys(hash: MalHashMap) -> MalVal {
-    return MalList(array: hash.hash.keys.array)
-}
-
-func fn_values(hash: MalHashMap) -> MalVal {
-    return MalList(array: hash.hash.values.array)
-}
-
-func fn_sequentialQ(obj: MalVal) -> Bool {
-    return is_sequence(obj)
-}
-
-func fn_cons(first:MalVal, rest:MalSequence) -> MalVal {
-    var new_elements = [MalVal]([first])
-    new_elements.extend(rest.slice)
-    return MalList(array: new_elements)
-}
-
-func fn_concat(args: MalVarArgs) -> MalVal {
-    var result = [MalVal]()
-    for arg in args {
-        if !is_sequence(arg) { return MalError(message: "expected list, got \(arg)") }
-        result.extend((arg as! MalSequence).slice)
-    }
-    return MalList(array: result)
-}
-
-func fn_nth(list: MalSequence, index: Int) -> MalVal {
-    return list[index]
-}
-
-func fn_first(arg: MalVal) -> MalVal {
-    if is_nil(arg) {
-        return arg
-    }
-    if is_sequence(arg) {
-        let list = arg as! MalSequence
-        return list.first()
-    }
-    return MalError(message: "expected list, got \(arg)")
-}
-
-func fn_rest(list: MalSequence) -> MalVal {
-    return MalList(slice: list.rest().slice)
-}
-
-func fn_emptyQ(obj: MalVal) -> Bool {
-    if is_sequence(obj) {
-        let list = obj as! MalSequence
-        return list.isEmpty
-    }
-    return true
-}
-
-func fn_count(obj: MalVal) -> Int64 {
-    if is_nil(obj) {
-        return 0
-    }
-    if is_sequence(obj) {
-        let as_seq = obj as! MalSequence
-        return Int64(as_seq.count)
-    }
-    if is_hashmap(obj) {
-        let hash = obj as! MalHashMap
-        return Int64(hash.count)
-    }
-    if is_string(obj) {
-        let string = obj as! MalString
-        return Int64(count(string.value.utf16))
-    }
-    return 0
-}
-
-func fn_apply(args: MalVarArgs) -> MalVal {
-    if args.count < 2 { return MalError(message: "expected at least 2 arguments to apply, got \(args.count)") }
-    let first   = args[0]
-    var middle  = args[1 ..< args.count - 1]
-    let last    = args[args.count - 1]
-    if !is_function(first) { return MalError(message: "expected function for first argument to apply, got \(first)") }
-    if !is_sequence(last) { return MalError(message: "expected sequence for last argument to apply, got \(last)") }
-    middle.extend((last as! MalSequence).slice)
-    return (first as! MalFunction).apply(MalList(slice: middle))
-}
-
-func fn_map(fn: MalFunction, list: MalSequence) -> MalVal {
-    var result = [MalVal]()
-    result.reserveCapacity(list.count)
-    for var index = 0; index < list.count; ++index {
-        let apply_res = fn.apply(MalList(slice: list.slice[index...index]))
-        if is_error(apply_res) { return apply_res }
-        result.append(apply_res)
-    }
-    return MalList(array: result)
-}
-
-func fn_conj(first: MalSequence, rest: MalVarArgs) -> MalVal {
-    var result = [MalVal]()
-    result.reserveCapacity(first.count + rest.count)
-    if is_list(first) {
-        for var index = 0; index < rest.count; ++index {
-            result.append(rest[rest.count - 1 - index])
-        }
-        result.extend(first.slice)
-        return MalList(array: result)
-    } else {
-        result.extend(first.slice)
-        result.extend(rest)
-        return MalVector(array: result)
-    }
-}
-
-func fn_meta(obj: MalVal) -> MalVal {
-    return obj.meta != nil ? obj.meta! : MalNil()
-}
-
-func fn_withmeta(form:MalVal, meta:MalVal) -> MalVal {
-    var new_form = form.clone()
-    new_form.meta = meta
-    return new_form
-}
-
-func fn_atom(obj: MalVal) -> MalVal {
-    return MalAtom(object: obj)
-}
-
-func fn_atomQ(obj: MalVal) -> Bool {
-    return is_atom(obj)
-}
-
-func fn_deref(form:MalVal) -> MalVal {
-    if !is_atom(form) { return MalError(message: "expected atom, got \(form)") }
-    return (form as! MalAtom).value
-}
-
-func fn_resetBang(atom: MalAtom, obj: MalVal) -> MalVal {
-    atom.value = obj
-    return obj
-}
-
-func fn_swapBang(var atom: MalAtom, fn: MalFunction, rest: MalVarArgs) -> MalVal {
-    var new_args = [MalVal]([atom.value])
-    new_args.extend(rest)
-    let result = fn.apply(MalList(array: new_args))
-    if is_error(result) { return result }
-    atom.value = result
-    return result
-}
-
-//******************************************************************************
-//
-// The facility for invoking built-in functions makes use of a name ->
-// function-pointer table (defined down below). The function-pointers accept a
-// sequence of MalVals and return a MalVal as a result. Each built-in function
-// that does actual work, on the other hand, may expect a different set of
-// parameters of different types, and may naturally return a result of any type.
-// In order to convert between these two types of interfaces, we have these
-// unwrap functions. These functions implement the (MalSequence) -> MalVal
-// interface expected by EVAL, and convert that information into Ints, Strings,
-// etc. expected by the built-in functions.
-//
-//******************************************************************************
-
-func with_one_parameter(args: MalSequence, fn: (MalVal) -> MalVal) -> MalVal {
-    if args.count < 1 { return MalError(message: "expected at least 1 parameter, got \(args.count)") }
-    let arg1 = args[0]
-    //let rest = args[1..<args.count]
-    return fn(arg1)
-}
-
-func with_two_parameters(args: MalSequence, fn: (MalVal, MalVal) -> MalVal) -> MalVal {
-    if args.count < 2 { return MalError(message: "expected at least 2 parameter, got \(args.count)") }
-    let arg1 = args[0]
-    let arg2 = args[1]
-    //let rest = args[2..<args.count]
-    return fn(arg1, arg2)
-}
-
-// ========== 0-parameter functions ==========
-
-// () -> Int64
-
-func unwrap(args: MalSequence, fn: () -> Int64) -> MalVal {
-    return MalInteger(value: fn())
-}
-
-// () -> MalVal
-
-func unwrap(args: MalSequence, fn: () -> MalVal) -> MalVal {
-    return fn()
-}
-
-// ========== 1-parameter functions ==========
-
-// (MalHashMap) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalHashMap) -> MalVal) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        if !is_hashmap(arg1) { return MalError(message: "expected hashmap, got \(arg1)") }
-        return fn(arg1 as! MalHashMap)
-    }
-}
-
-// (MalSequence) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalSequence) -> MalVal) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        if !is_sequence(arg1) { return MalError(message: "expected list, got \(arg1)") }
-        return fn(arg1 as! MalSequence)
-    }
-}
-
-// (MalVal) -> Bool
-
-func unwrap(args: MalSequence, fn: (MalVal) -> Bool) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        return fn(arg1) ? MalTrue() : MalFalse()
-    }
-}
-
-// (MalVal) -> Int64
-
-func unwrap(args: MalSequence, fn: (MalVal) -> Int64) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        return MalInteger(value: fn(arg1))
-    }
-}
-
-// (MalVal) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalVal) -> MalVal) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        return fn(arg1)
-    }
-}
-
-// (String) -> MalVal
-
-func unwrap(args: MalSequence, fn: (String) -> MalVal) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        if !is_string(arg1) { return MalError(message: "expected string, got \(arg1)") }
-        return fn((arg1 as! MalString).value)
-    }
-}
-
-// (String) -> MalVal?
-
-func unwrap(args: MalSequence, fn: (String) -> MalVal?) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        if !is_string(arg1) { return MalError(message: "expected string, got \(arg1)") }
-        let res = fn((arg1 as! MalString).value)
-        return res != nil ? res! : MalNil()
-    }
-}
-
-// (String) -> String
-
-func unwrap(args: MalSequence, fn: (String) -> String) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        if !is_string(arg1) { return MalError(message: "expected string, got \(arg1)") }
-        return MalString(unescaped: fn((arg1 as! MalString).value))
-    }
-}
-
-// (String) -> String?
-
-func unwrap(args: MalSequence, fn: (String) -> String?) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        if !is_string(arg1) { return MalError(message: "expected string, got \(arg1)") }
-        let res = fn((arg1 as! MalString).value)
-        return res != nil ? MalString(unescaped:res!) : MalNil()
-    }
-}
-
-// ========== 2-parameter functions ==========
-
-// (Int64, Int64) -> Bool
-
-func unwrap(args: MalSequence, fn: (Int64, Int64) -> Bool) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        if !is_integer(arg1) { return MalError(message: "expected number, got \(arg1)") }
-        if !is_integer(arg2) { return MalError(message: "expected number, got \(arg2)") }
-        return fn((arg1 as! MalInteger).value, (arg2 as! MalInteger).value) ? MalTrue() : MalFalse()
-    }
-}
-
-// (Int64, Int64) -> Int64
-
-func unwrap(args: MalSequence, fn: (Int64, Int64) -> Int64) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        if !is_integer(arg1) { return MalError(message: "expected number, got \(arg1)") }
-        if !is_integer(arg2) { return MalError(message: "expected number, got \(arg2)") }
-        return MalInteger(value: fn((arg1 as! MalInteger).value, (arg2 as! MalInteger).value))
-    }
-}
-
-// (MalAtom, MalVal) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalAtom, MalVal) -> MalVal) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        if !is_atom(arg1) { return MalError(message: "expected atom, got \(arg1)") }
-        return fn((arg1 as! MalAtom), arg2)
-    }
-}
-
-// (MalFunction, MalSequence) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalFunction, MalSequence) -> MalVal) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        if !is_function(arg1) { return MalError(message: "expected function, got \(arg1)") }
-        if !is_sequence(arg2) { return MalError(message: "expected sequence, got \(arg2)") }
-        return fn((arg1 as! MalFunction), (arg2 as! MalSequence))
-    }
-}
-
-// (MalSequence, Int) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalSequence, Int) -> MalVal) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        if !is_sequence(arg1) { return MalError(message: "expected sequence, got \(arg1)") }
-        if !is_integer(arg2)  { return MalError(message: "expected number, got \(arg2)") }
-        return fn((arg1 as! MalSequence), Int((arg2 as! MalInteger).value))
-    }
-}
-
-// (MalVal, MalSequence) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalVal, MalSequence) -> MalVal) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        if !is_sequence(arg2) { return MalError(message: "expected sequence, got \(arg2)") }
-        return fn(arg1, (arg2 as! MalSequence))
-    }
-}
-
-// (MalVal, MalVal) -> Bool
-
-func unwrap(args: MalSequence, fn: (MalVal, MalVal) -> Bool) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        return fn(arg1, arg2) ? MalTrue() : MalFalse()
-    }
-}
-
-// (MalVal, MalVal) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalVal, MalVal) -> MalVal) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        return fn(arg1, arg2)
-    }
-}
-
-// ========== Variadic functions ==========
-
-// (MalVarArgs) -> ()
-
-func unwrap(args: MalSequence, fn: (MalVarArgs) -> ()) -> MalVal {
-    fn(args.slice)
-    return MalNil()
-}
-
-// (MalVarArgs) -> String
-
-func unwrap(args: MalSequence, fn: (MalVarArgs) -> String) -> MalVal {
-    return MalString(unescaped: fn(args.slice))
-}
-
-// (MalVarArgs) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalVarArgs) -> MalVal) -> MalVal {
-    return fn(args.slice)
-}
-
-// (MalAtom, MalFunction, MalVarArgs) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalAtom, MalFunction, MalVarArgs) -> MalVal) -> MalVal {
-    return with_two_parameters(args) { (arg1, arg2) -> MalVal in
-        if !is_atom(arg1) { return MalError(message: "expected atom, got \(arg1)") }
-        if !is_function(arg2) { return MalError(message: "expected function, got \(arg2)") }
-        return fn((arg1 as! MalAtom), (arg2 as! MalFunction), args[2..<args.count])
-    }
-}
-
-// (MalHashMap, MalVarArgs) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalHashMap, MalVarArgs) -> MalVal) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        if !is_hashmap(arg1) { return MalError(message: "expected hashmap, got \(arg1)") }
-        return fn((arg1 as! MalHashMap), args[1..<args.count])
-    }
-}
-
-// (MalSequence, MalVarArgs) -> MalVal
-
-func unwrap(args: MalSequence, fn: (MalSequence, MalVarArgs) -> MalVal) -> MalVal {
-    return with_one_parameter(args) { (arg1) -> MalVal in
-        if !is_sequence(arg1) { return MalError(message: "expected sequence, got \(arg1)") }
-        return fn((arg1 as! MalSequence), args[1..<args.count])
-    }
-}
-
-// *o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*
-
-let ns: [String: MalBuiltin.BuiltinSignature] = [
-    "=":            { unwrap($0, fn_eq) },
-    "throw":        { unwrap($0, fn_throw) },
-
-    "nil?":         { unwrap($0, fn_nilQ) },
-    "true?":        { unwrap($0, fn_trueQ) },
-    "false?":       { unwrap($0, fn_falseQ) },
-    "symbol":       { unwrap($0, fn_symbol) },
-    "symbol?":      { unwrap($0, fn_symbolQ) },
-    "keyword":      { unwrap($0, fn_keyword) },
-    "keyword?":     { unwrap($0, fn_keywordQ) },
-
-    "pr-str":       { unwrap($0, fn_prstr) },
-    "str":          { unwrap($0, fn_str) },
-    "prn":          { unwrap($0, fn_prn) },
-    "println":      { unwrap($0, fn_println) },
-    "read-string":  { unwrap($0, fn_readstring) },
-    "readline":     { unwrap($0, fn_readline) },
-    "slurp":        { unwrap($0, fn_slurp) },
-
-    "<":            { unwrap($0, fn_lt) },
-    "<=":           { unwrap($0, fn_lte) },
-    ">":            { unwrap($0, fn_gt) },
-    ">=":           { unwrap($0, fn_gte) },
-    "+":            { unwrap($0, fn_add) },
-    "-":            { unwrap($0, fn_subtract) },
-    "*":            { unwrap($0, fn_multiply) },
-    "/":            { unwrap($0, fn_divide) },
-    "time-ms":      { unwrap($0, fn_timems) },
-
-    "list":         { unwrap($0, fn_list) },
-    "list?":        { unwrap($0, fn_listQ) },
-    "vector":       { unwrap($0, fn_vector) },
-    "vector?":      { unwrap($0, fn_vectorQ) },
-    "hash-map":     { unwrap($0, fn_hashmap) },
-    "map?":         { unwrap($0, fn_hashmapQ) },
-    "assoc":        { unwrap($0, fn_assoc) },
-    "dissoc":       { unwrap($0, fn_dissoc) },
-    "get":          { unwrap($0, fn_get) },
-    "contains?":    { unwrap($0, fn_containsQ) },
-    "keys":         { unwrap($0, fn_keys) },
-    "vals":         { unwrap($0, fn_values) },
-
-    "sequential?":  { unwrap($0, fn_sequentialQ) },
-    "cons":         { unwrap($0, fn_cons) },
-    "concat":       { unwrap($0, fn_concat) },
-    "nth":          { unwrap($0, fn_nth) },
-    "first":        { unwrap($0, fn_first) },
-    "rest":         { unwrap($0, fn_rest) },
-    "empty?":       { unwrap($0, fn_emptyQ) },
-    "count":        { unwrap($0, fn_count) },
-    "apply":        { unwrap($0, fn_apply) },
-    "map":          { unwrap($0, fn_map) },
-    "conj":         { unwrap($0, fn_conj) },
-
-    "meta":         { unwrap($0, fn_meta) },
-    "with-meta":    { unwrap($0, fn_withmeta) },
-    "atom":         { unwrap($0, fn_atom) },
-    "atom?":        { unwrap($0, fn_atomQ) },
-    "deref":        { unwrap($0, fn_deref) },
-    "reset!":       { unwrap($0, fn_resetBang) },
-    "swap!":        { unwrap($0, fn_swapBang) },
-]
-
-func load_builtins(env: Environment) {
-    for (name, fn) in ns {
-        env.set(MalSymbol(symbol: name), MalBuiltin(function: fn))
-    }
-}
+//******************************************************************************
+// MAL - core
+//******************************************************************************
+
+import Foundation
+
+// This is a simple type distinct from all MalVal types so that we can pass a
+// sequence to a function and be able to distinguish between those functions
+// that want a sequence as a parameter and those that want a sequence that holds
+// the rest of the function parameters.
+//
+final class MalVarArgs {
+    init(_ value: MalSequence) { self.value = value }
+    init(_ value: MalVal) { self.value = as_sequence(value) }
+    let value: MalSequence
+}
+
+private func fn_eq(obj1: MalVal, obj2: MalVal) throws -> Bool {
+    return obj1 == obj2
+}
+
+private func fn_throw(exception: MalVal) throws -> MalVal {
+    try throw_error(exception)
+}
+
+private func fn_nilQ(obj: MalVal) throws -> Bool {
+    return is_nil(obj)
+}
+
+private func fn_trueQ(obj: MalVal) throws -> Bool {
+    return is_true(obj)
+}
+
+private func fn_falseQ(obj: MalVal) throws -> Bool {
+    return is_false(obj)
+}
+
+private func fn_symbol(s: String) throws -> MalVal {
+    return make_symbol(s)
+}
+
+private func fn_symbolQ(obj: MalVal) throws -> Bool {
+    return is_symbol(obj)
+}
+
+private func fn_keyword(s: MalVal) throws -> MalVal {
+    if is_keyword(s) {
+        return s
+    }
+    if is_string(s) {
+        return make_keyword(as_string(s))
+    }
+    try throw_error("expected string or keyword")
+}
+
+private func fn_keywordQ(obj: MalVal) throws -> Bool {
+    return is_keyword(obj)
+}
+
+private func fn_prstr(args: MalVarArgs) throws -> String {
+    let args_str_array = args.value.map { pr_str($0, true) }
+    return args_str_array.joinWithSeparator(" ")
+}
+
+private func fn_str(args: MalVarArgs) throws -> String {
+    let args_str_array = args.value.map { pr_str($0, false) }
+    return args_str_array.joinWithSeparator("")
+}
+
+private func fn_prn(args: MalVarArgs) {
+    let args_str_array = args.value.map { pr_str($0, true) }
+    let args_str = args_str_array.joinWithSeparator(" ")
+    print(args_str)
+}
+
+private func fn_println(args: MalVarArgs) {
+    let args_str_array = args.value.map { pr_str($0, false) }
+    let args_str = args_str_array.joinWithSeparator(" ")
+    print(args_str)
+}
+
+private func fn_readstring(s: String) throws -> MalVal {
+    return try read_str(s)
+}
+
+private func fn_readline(s: String) throws -> String? {
+    return _readline(s)
+}
+
+private func fn_slurp(s: String) throws -> MalVal {
+    do {
+        let result = try String(contentsOfFile: s, encoding: NSUTF8StringEncoding)
+        return make_string(result)
+    } catch let error as NSError {
+        try throw_error("unknown error reading file \(error)")
+    }
+}
+
+private func fn_lt(arg1: MalIntType, arg2: MalIntType) throws -> Bool {
+    return arg1 < arg2
+}
+
+private func fn_lte(arg1: MalIntType, arg2: MalIntType) throws -> Bool {
+    return arg1 <= arg2
+}
+
+private func fn_gt(arg1: MalIntType, arg2: MalIntType) throws -> Bool {
+    return arg1 > arg2
+}
+
+private func fn_gte(arg1: MalIntType, arg2: MalIntType) throws -> Bool {
+    return arg1 >= arg2
+}
+
+private func fn_add(arg1: MalIntType, arg2: MalIntType) throws -> MalIntType {
+    return arg1 + arg2
+}
+
+private func fn_subtract(arg1: MalIntType, arg2: MalIntType) throws -> MalIntType {
+    return arg1 - arg2
+}
+
+private func fn_multiply(arg1: MalIntType, arg2: MalIntType) throws -> MalIntType {
+    return arg1 * arg2
+}
+
+private func fn_divide(arg1: MalIntType, arg2: MalIntType) throws -> MalIntType {
+    return arg1 / arg2
+}
+
+private func fn_timems() throws -> MalIntType {
+    var time = timeval(tv_sec: 0, tv_usec: 0)
+    let res = gettimeofday(&time, nil)
+    if res == 0 {
+        return (MalIntType(time.tv_sec) * 1_000_000 + MalIntType(time.tv_usec)) / 1000
+    }
+    return -1
+}
+
+private func fn_list(args: MalVarArgs) throws -> MalVal {
+    return make_list(args.value)
+}
+
+private func fn_listQ(obj: MalVal) throws -> Bool {
+    return is_list(obj)
+}
+
+private func fn_vector(args: MalVarArgs) throws -> MalVal {
+    return make_vector(args.value)
+}
+
+private func fn_vectorQ(obj: MalVal) throws -> Bool {
+    return is_vector(obj)
+}
+
+private func fn_hashmap(args: MalVarArgs) throws -> MalVal {
+    return make_hashmap(args.value)
+}
+
+private func fn_hashmapQ(obj: MalVal) throws -> Bool {
+    return is_hashmap(obj)
+}
+
+private func fn_assoc(hash: MalHashMap, args: MalVarArgs) throws -> MalVal {
+   guard args.value.count % 2 == 0 else {
+       try throw_error("expected even number of elements, got \(args.value.count)")
+   }
+   var new_dictionary = hash.hash
+   for var index: MalIntType = 0; index < args.value.count; index += 2 {
+       new_dictionary[try! args.value.nth(index)] = try! args.value.nth(index + 1)
+   }
+   return make_hashmap(new_dictionary)
+}
+
+private func fn_dissoc(hash: MalHashMap, args: MalVarArgs) throws -> MalVal {
+    var new_dictionary = hash.hash
+    for value in args.value {
+        new_dictionary.removeValueForKey(value)
+    }
+    return make_hashmap(new_dictionary)
+}
+
+private func fn_get(obj: MalVal, key: MalVal) throws -> MalVal {
+    if let as_vec = as_vectorQ(obj) {
+        guard let index = as_integerQ(key) else {
+            try throw_error("expected integer key for get(vector), got \(key)")
+        }
+        let n = as_inttype(index)
+        guard n >= as_vec.count else { try throw_error("index out of range: \(n) >= \(as_vec.count)") }
+        return try! as_vec.nth(n)
+    }
+    if let as_hash = as_hashmapQ(obj) {
+        if let value = as_hash.value_for(key) { return value }
+        return make_nil()
+    }
+    if is_nil(obj) {
+        return obj
+    }
+    try throw_error("get called on unsupported type: \(obj)")
+}
+
+private func fn_containsQ(obj: MalVal, key: MalVal) throws -> MalVal {
+    if let as_vec = as_vectorQ(obj) {
+        guard let index = as_integerQ(key) else {
+            try throw_error("expected integer key for contains(vector), got \(key)")
+        }
+        let n = as_inttype(index)
+        return n < as_vec.count ? make_true() : make_false()
+    }
+    if let as_hash = as_hashmapQ(obj) {
+        return as_hash.value_for(key) != nil ? make_true() : make_false()
+    }
+    try throw_error("contains? called on unsupported type: \(obj)")
+}
+
+private func fn_keys(hash: MalHashMap) throws -> MalVal {
+    return hash.keys
+}
+
+private func fn_values(hash: MalHashMap) throws -> MalVal {
+    return hash.values
+}
+
+private func fn_sequentialQ(obj: MalVal) throws -> Bool {
+    return is_sequence(obj)
+}
+
+private func fn_cons(first: MalVal, rest: MalSequence) throws -> MalVal {
+    return rest.cons(first)
+}
+
+private func fn_concat(args: MalVarArgs) throws -> MalVal {
+    var result = make_list()
+    for arg in args.value {
+        guard let arg_as_seq = as_sequenceQ(arg) else {
+            try throw_error("expected list, got \(arg)")
+        }
+        result = try! as_sequence(result).concat(arg_as_seq)
+    }
+    return result
+}
+
+private func fn_nth(list: MalSequence, index: MalIntType) throws -> MalVal {
+    return try list.nth(index)
+}
+
+private func fn_first(arg: MalVal) throws -> MalVal {
+    if is_nil(arg) {
+        return arg
+    }
+    if let list = as_sequenceQ(arg) {
+        return list.first()
+    }
+    try throw_error("expected list, got \(arg)")
+}
+
+private func fn_rest(list: MalSequence) throws -> MalVal {
+    return list.rest()
+}
+
+private func fn_emptyQ(obj: MalVal) throws -> Bool {
+    if let list = as_sequenceQ(obj) {
+        return list.isEmpty
+    }
+    return true
+}
+
+private func fn_count(obj: MalVal) throws -> MalIntType {
+    if is_nil(obj) {
+        return 0
+    }
+    if let as_seq = as_sequenceQ(obj) {
+        return as_seq.count
+    }
+    if let as_hash = as_hashmapQ(obj) {
+        return as_hash.count
+    }
+    if let as_str = as_stringQ(obj) {
+        return MalIntType(as_stringtype(as_str).characters.count)
+    }
+    return 0
+}
+
+private func fn_apply(args: MalVarArgs) throws -> MalVal {
+    guard args.value.count >= 2 else {
+        try throw_error("expected at least 2 arguments to apply, got \(args.value.count)")
+    }
+
+    let first   = args.value.first()
+    let middle  = args.value.range_from(1, to: args.value.count - 1)
+    let last    = args.value.last()
+
+    guard let fn = as_functionQ(first) else {
+        try throw_error("expected function for first argument to apply, got \(first)")
+    }
+    guard let seq = as_sequenceQ(last) else {
+        try throw_error("expected sequence for last argument to apply, got \(last)")
+    }
+    let exprs = try! as_sequence(middle).concat(seq)
+    return try fn.apply(as_sequence(exprs))
+}
+
+private func fn_map(fn: MalFunction, list: MalSequence) throws -> MalVal {
+    var result = [MalVal]()
+    result.reserveCapacity(Int(list.count))
+    for var index: MalIntType = 0; index < list.count; ++index {
+        let apply_res = try fn.apply(as_sequence(make_list_from(try! list.nth(index))))
+        result.append(apply_res)
+    }
+    return make_list(result)
+}
+
+private func fn_conj(first: MalSequence, rest: MalVarArgs) throws -> MalVal {
+    return try first.conj(rest.value)
+}
+
+private func fn_meta(obj: MalVal) throws -> MalVal {
+    if let meta = get_meta(obj) {
+        return meta
+    }
+
+    return make_nil()
+}
+
+private func fn_withmeta(form: MalVal, meta: MalVal) throws -> MalVal {
+    return with_meta(form, meta)
+}
+
+private func fn_atom(obj: MalVal) throws -> MalVal {
+    return make_atom(obj)
+}
+
+private func fn_atomQ(obj: MalVal) throws -> Bool {
+    return is_atom(obj)
+}
+
+private func fn_deref(atom: MalAtom) throws -> MalVal {
+    return atom.object
+}
+
+private func fn_resetBang(atom: MalAtom, obj: MalVal) throws -> MalVal {
+    return atom.set_object(obj)
+}
+
+private func fn_swapBang(let atom: MalAtom, fn: MalFunction, rest: MalVarArgs) throws -> MalVal {
+    var new_args = make_list_from(atom.object)
+    new_args = try as_sequence(new_args).concat(rest.value)
+    let result = try fn.apply(as_sequence(new_args))
+    return atom.set_object(result)
+}
+
+//******************************************************************************
+//
+// The facility for invoking built-in functions makes use of a name ->
+// function-pointer table (defined down below). The function-pointers accept a
+// sequence of MalVals and return a MalVal as a result. Each built-in function
+// that does actual work, on the other hand, may expect a different set of
+// parameters of different types, and may naturally return a result of any type.
+// In order to convert between these two types of interfaces, we have these
+// unwrap_args functions. These functions implement the (MalSequence) -> MalVal
+// interface expected by EVAL, and convert that information into Ints, Strings,
+// etc. expected by the built-in functions.
+//
+//******************************************************************************
+
+private func with_one_parameter(args: MalSequence, @noescape fn: (MalVal) throws -> MalVal) throws -> MalVal {
+    guard args.count >= 1 else { try throw_error("expected at least 1 parameter, got \(args.count)") }
+    let arg1 = try! args.nth(0)
+    return try fn(arg1)
+}
+
+private func with_two_parameters(args: MalSequence, @noescape fn: (MalVal, MalVal) throws -> MalVal) throws -> MalVal {
+    guard args.count >= 2 else { try throw_error("expected at least 2 parameter, got \(args.count)") }
+    let arg1 = try! args.nth(0)
+    let arg2 = try! args.nth(1)
+    return try fn(arg1, arg2)
+}
+
+// ========== 0-parameter functions ==========
+
+// () -> MalIntType
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: () throws -> MalIntType) throws -> MalVal {
+    return make_integer(try fn())
+}
+
+// () -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: () throws -> MalVal) throws -> MalVal {
+    return try fn()
+}
+
+// ========== 1-parameter functions ==========
+
+// (MalAtom) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalAtom) throws -> MalVal) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let atom = as_atomQ(arg1) else {
+            try throw_error("expected atom, got \(arg1)")
+        }
+        return try fn(atom)
+    }
+}
+
+// (MalHashMap) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalHashMap) throws -> MalVal) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let hash = as_hashmapQ(arg1) else {
+            try throw_error("expected hashmap, got \(arg1)")
+        }
+        return try fn(hash)
+    }
+}
+
+// (MalSequence) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalSequence) throws -> MalVal) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let seq = as_sequenceQ(arg1) else {
+            try throw_error("expected list, got \(arg1)")
+        }
+        return try fn(seq)
+    }
+}
+
+// (MalVal) -> Bool
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal) throws -> Bool) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        return try fn(arg1) ? make_true() : make_false()
+    }
+}
+
+// (MalVal) -> MalIntType
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal) throws -> MalIntType) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        return make_integer(try fn(arg1))
+    }
+}
+
+// (MalVal) -> MalVal
+
+func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal) throws -> MalVal) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        return try fn(arg1)
+    }
+}
+
+// (String) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (String) throws -> MalVal) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let str = as_stringQ(arg1) else {
+            try throw_error("expected string, got \(arg1)")
+        }
+        return try fn(as_stringtype(str))
+    }
+}
+
+// (String) -> MalVal?
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (String) throws -> MalVal?) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let str = as_stringQ(arg1) else {
+            try throw_error("expected string, got \(arg1)")
+        }
+        let res = try fn(as_stringtype(str))
+        return res != nil ? res! : make_nil()
+    }
+}
+
+// (String) -> String
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (String) throws -> String) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let str = as_stringQ(arg1) else {
+            try throw_error("expected string, got \(arg1)")
+        }
+        return make_string(try fn(as_stringtype(str)))
+    }
+}
+
+// (String) -> String?
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (String) throws -> String?) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let str = as_stringQ(arg1) else {
+            try throw_error("expected string, got \(arg1)")
+        }
+        let res = try fn(as_stringtype(str))
+        return res != nil ? make_string(res!) : make_nil()
+    }
+}
+
+// ========== 2-parameter functions ==========
+
+// (MalIntType, MalIntType) -> Bool
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalIntType, MalIntType) throws -> Bool) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        guard let int1 = as_integerQ(arg1) else {
+            try throw_error("expected number, got \(arg1)")
+        }
+        guard let int2 = as_integerQ(arg2) else {
+            try throw_error("expected number, got \(arg2)")
+        }
+        return try fn(as_inttype(int1), as_inttype(int2)) ? make_true() : make_false()
+    }
+}
+
+// (MalIntType, MalIntType) -> MalIntType
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalIntType, MalIntType) throws -> MalIntType) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        guard let int1 = as_integerQ(arg1) else {
+            try throw_error("expected number, got \(arg1)")
+        }
+        guard let int2 = as_integerQ(arg2) else {
+            try throw_error("expected number, got \(arg2)")
+        }
+        return make_integer(try fn(as_inttype(int1), as_inttype(int2)))
+    }
+}
+
+// (MalAtom, MalVal) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalAtom, MalVal) throws -> MalVal) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        guard let atom = as_atomQ(arg1) else {
+            try throw_error("expected atom, got \(arg1)")
+        }
+        return try fn(atom, arg2)
+    }
+}
+
+// (MalFunction, MalSequence) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalFunction, MalSequence) throws -> MalVal) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        guard let fn1 = as_functionQ(arg1) else {
+            try throw_error("expected function, got \(arg1)")
+        }
+        guard let seq2 = as_sequenceQ(arg2) else {
+            try throw_error("expected sequence, got \(arg2)")
+        }
+        return try fn(fn1, seq2)
+    }
+}
+
+// (MalSequence, MalIntType) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalSequence, MalIntType) throws -> MalVal) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        guard let seq = as_sequenceQ(arg1) else {
+            try throw_error("expected sequence, got \(arg1)")
+        }
+        guard let int = as_integerQ(arg2) else {
+            try throw_error("expected number, got \(arg2)")
+        }
+        return try fn(seq, as_inttype(int))
+    }
+}
+
+// (MalVal, MalSequence) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal, MalSequence) throws -> MalVal) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        guard let seq = as_sequenceQ(arg2) else {
+            try throw_error("expected sequence, got \(arg2)")
+        }
+        return try fn(arg1, seq)
+    }
+}
+
+// (MalVal, MalVal) -> Bool
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal, MalVal) throws -> Bool) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        return try fn(arg1, arg2) ? make_true() : make_false()
+    }
+}
+
+// (MalVal, MalVal) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal, MalVal) throws -> MalVal) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        return try fn(arg1, arg2)
+    }
+}
+
+// ========== Variadic functions ==========
+
+// (MalVarArgs) -> ()
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVarArgs) throws -> ()) throws -> MalVal {
+    try fn(MalVarArgs(args))
+    return make_nil()
+}
+
+// (MalVarArgs) -> String
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVarArgs) throws -> String) throws -> MalVal {
+    return make_string(try fn(MalVarArgs(args)))
+}
+
+// (MalVarArgs) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVarArgs) throws -> MalVal) throws -> MalVal {
+    return try fn(MalVarArgs(args))
+}
+
+// (MalAtom, MalFunction, MalVarArgs) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalAtom, MalFunction, MalVarArgs) throws -> MalVal) throws -> MalVal {
+    return try with_two_parameters(args) { (arg1, arg2) -> MalVal in
+        guard let atom = as_atomQ(arg1) else {
+            try throw_error("expected atom, got \(arg1)")
+        }
+        guard let fn2 = as_functionQ(arg2) else {
+            try throw_error("expected function, got \(arg2)")
+        }
+        return try fn(atom, fn2, MalVarArgs(as_sequence(args.rest()).rest()))
+    }
+}
+
+// (MalHashMap, MalVarArgs) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalHashMap, MalVarArgs) throws -> MalVal) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let hash = as_hashmapQ(arg1) else {
+            try throw_error("expected hashmap, got \(arg1)")
+        }
+        return try fn(hash, MalVarArgs(args.rest()))
+    }
+}
+
+// (MalSequence, MalVarArgs) -> MalVal
+
+private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalSequence, MalVarArgs) throws -> MalVal) throws -> MalVal {
+    return try with_one_parameter(args) { (arg1) -> MalVal in
+        guard let seq = as_sequenceQ(arg1) else {
+            try throw_error("expected sequence, got \(arg1)")
+        }
+        return try fn(seq, MalVarArgs(args.rest()))
+    }
+}
+
+// *o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*
+
+let ns: [String: MalBuiltin.Signature] = [
+    "=":            { try unwrap_args($0, forFunction: fn_eq) },
+    "throw":        { try unwrap_args($0, forFunction: fn_throw) },
+
+    "nil?":         { try unwrap_args($0, forFunction: fn_nilQ) },
+    "true?":        { try unwrap_args($0, forFunction: fn_trueQ) },
+    "false?":       { try unwrap_args($0, forFunction: fn_falseQ) },
+    "symbol":       { try unwrap_args($0, forFunction: fn_symbol) },
+    "symbol?":      { try unwrap_args($0, forFunction: fn_symbolQ) },
+    "keyword":      { try unwrap_args($0, forFunction: fn_keyword) },
+    "keyword?":     { try unwrap_args($0, forFunction: fn_keywordQ) },
+
+    "pr-str":       { try unwrap_args($0, forFunction: fn_prstr) },
+    "str":          { try unwrap_args($0, forFunction: fn_str) },
+    "prn":          { try unwrap_args($0, forFunction: fn_prn) },
+    "println":      { try unwrap_args($0, forFunction: fn_println) },
+    "read-string":  { try unwrap_args($0, forFunction: fn_readstring) },
+    "readline":     { try unwrap_args($0, forFunction: fn_readline) },
+    "slurp":        { try unwrap_args($0, forFunction: fn_slurp) },
+
+    "<":            { try unwrap_args($0, forFunction: fn_lt) },
+    "<=":           { try unwrap_args($0, forFunction: fn_lte) },
+    ">":            { try unwrap_args($0, forFunction: fn_gt) },
+    ">=":           { try unwrap_args($0, forFunction: fn_gte) },
+    "+":            { try unwrap_args($0, forFunction: fn_add) },
+    "-":            { try unwrap_args($0, forFunction: fn_subtract) },
+    "*":            { try unwrap_args($0, forFunction: fn_multiply) },
+    "/":            { try unwrap_args($0, forFunction: fn_divide) },
+    "time-ms":      { try unwrap_args($0, forFunction: fn_timems) },
+
+    "list":         { try unwrap_args($0, forFunction: fn_list) },
+    "list?":        { try unwrap_args($0, forFunction: fn_listQ) },
+    "vector":       { try unwrap_args($0, forFunction: fn_vector) },
+    "vector?":      { try unwrap_args($0, forFunction: fn_vectorQ) },
+    "hash-map":     { try unwrap_args($0, forFunction: fn_hashmap) },
+    "map?":         { try unwrap_args($0, forFunction: fn_hashmapQ) },
+    "assoc":        { try unwrap_args($0, forFunction: fn_assoc) },
+    "dissoc":       { try unwrap_args($0, forFunction: fn_dissoc) },
+    "get":          { try unwrap_args($0, forFunction: fn_get) },
+    "contains?":    { try unwrap_args($0, forFunction: fn_containsQ) },
+    "keys":         { try unwrap_args($0, forFunction: fn_keys) },
+    "vals":         { try unwrap_args($0, forFunction: fn_values) },
+
+    "sequential?":  { try unwrap_args($0, forFunction: fn_sequentialQ) },
+    "cons":         { try unwrap_args($0, forFunction: fn_cons) },
+    "concat":       { try unwrap_args($0, forFunction: fn_concat) },
+    "nth":          { try unwrap_args($0, forFunction: fn_nth) },
+    "first":        { try unwrap_args($0, forFunction: fn_first) },
+    "rest":         { try unwrap_args($0, forFunction: fn_rest) },
+    "empty?":       { try unwrap_args($0, forFunction: fn_emptyQ) },
+    "count":        { try unwrap_args($0, forFunction: fn_count) },
+    "apply":        { try unwrap_args($0, forFunction: fn_apply) },
+    "map":          { try unwrap_args($0, forFunction: fn_map) },
+    "conj":         { try unwrap_args($0, forFunction: fn_conj) },
+
+    "meta":         { try unwrap_args($0, forFunction: fn_meta) },
+    "with-meta":    { try unwrap_args($0, forFunction: fn_withmeta) },
+    "atom":         { try unwrap_args($0, forFunction: fn_atom) },
+    "atom?":        { try unwrap_args($0, forFunction: fn_atomQ) },
+    "deref":        { try unwrap_args($0, forFunction: fn_deref) },
+    "reset!":       { try unwrap_args($0, forFunction: fn_resetBang) },
+    "swap!":        { try unwrap_args($0, forFunction: fn_swapBang) },
+]
+
+func load_builtins(env: Environment) {
+    for (name, fn) in ns {
+        env.set(as_symbol(make_symbol(name)), make_builtin(fn))
+    }
+}
index 77d9afd..ba6205d 100644 (file)
@@ -6,49 +6,58 @@ import Foundation
 
 typealias EnvironmentVars = [MalSymbol: MalVal]
 
-let kSymbolAmpersand = MalSymbol(symbol: "&")
-let kNil = MalNil()
-let kNilSymbol = MalSymbol(symbol: "")
+private let kSymbolAmpersand = as_symbol(make_symbol("&"))
+private let kSymbolNil       = as_symbol(make_symbol(""))
+private let kNil             = make_nil()
 
-class Environment {
+final class Environment {
     init(outer: Environment?) {
         self.outer = outer
     }
 
-    func set_bindings(binds: MalSequence, with_exprs exprs: MalSequence) -> MalVal {
-        for var index = 0; index < binds.count; ++index {
-            if !is_symbol(binds[index]) { return MalError(message: "an entry in binds was not a symbol: index=\(index), binds[index]=\(binds[index])") }
-            let sym = binds[index] as! MalSymbol
+    func set_bindings(binds: MalSequence, with_exprs exprs: MalSequence) throws -> MalVal {
+        for var index: MalIntType = 0; index < binds.count; ++index {
+            guard let sym = as_symbolQ(try! binds.nth(index)) else {
+                try throw_error("an entry in binds was not a symbol: index=\(index), binds[index]=\(try! binds.nth(index))")
+            }
             if sym != kSymbolAmpersand {
                 if index < exprs.count {
-                    set(sym, exprs[index])
+                    set(sym, try! exprs.nth(index))
                 } else {
                     set(sym, kNil)
                 }
                 continue
             }
-            // I keep getting messed up by the following, so here's an
-            // explanation. We are iterating over two lists, and are at this
-            // point:
-            // 
-            //           index
-            //             |
-            //             v
-            // binds: (... & name)
-            // exprs: (... a b    c d e ...)
-            //
-            // In the following, we increment index to get to "name", and then
-            // later decrement it to get to (a b c d e ...)
-            if ++index >= binds.count { return MalError(message: "found & but no symbol") }
-            if !is_symbol(binds[index]) { return MalError(message: "& was not followed by a symbol: index=\(index), binds[index]=\(binds[index])") }
-            let rest_sym = binds[index--] as! MalSymbol
-            let rest = exprs[index..<exprs.count]
-            set(rest_sym, MalList(slice: rest))
+
+            guard (index + 1) < binds.count else {
+                try throw_error("found & but no symbol")
+            }
+            guard let rest_sym = as_symbolQ(try! binds.nth(index + 1)) else {
+                try throw_error("& was not followed by a symbol: index=\(index), binds[index]=\(try! binds.nth(index))")
+            }
+            let rest = exprs.range_from(index, to: exprs.count)
+            set(rest_sym, rest)
             break
         }
         return kNil
     }
 
+    // In this implementation, rather than storing everything in a dictionary,
+    // we optimize for small environments by having a hard-coded set of four
+    // slots. We use these slots when creating small environments, such as when
+    // a function is invoked. Testing shows that supporting up to four variables
+    // in this way is a good trade-off. Otherwise, if we have more than four
+    // variables, we switch over to using a dictionary. Testing also shows that
+    // trying to use both the slots and the dictionary for large environments is
+    // not as efficient as just completely switching over to the dictionary.
+    //
+    // Interestingly, even though the MalVal return value is hardly ever used at
+    // the call site, removing it and returning nothing is a performance loss.
+    // This is because returning 'value' allows the compiler to skip calling
+    // swift_release on it. The result is that set() calls swift_release twice
+    // (on self and sym), as opposed to three times (on self, sym, and value) if
+    // it were to return something other than one of the parameters.
+
     func set(sym: MalSymbol, _ value: MalVal) -> MalVal {
         if num_bindings == 0 {
             slot_name0 = sym; slot_value0 = value; ++num_bindings
@@ -83,7 +92,7 @@ class Environment {
     }
 
     func get(sym: MalSymbol) -> MalVal? {
-        if num_bindings > 4 { if let val = data[sym] { return val } }
+        if num_bindings > 4 { if let val = data[sym] { return val }; return outer?.get(sym) }
         if num_bindings > 3 { if slot_name3 == sym { return slot_value3 } }
         if num_bindings > 2 { if slot_name2 == sym { return slot_value2 } }
         if num_bindings > 1 { if slot_name1 == sym { return slot_value1 } }
@@ -94,12 +103,12 @@ class Environment {
     private var outer: Environment?
     private var data = EnvironmentVars()
     private var num_bindings = 0
-    private var slot_name0: MalSymbol = kNilSymbol
-    private var slot_name1: MalSymbol = kNilSymbol
-    private var slot_name2: MalSymbol = kNilSymbol
-    private var slot_name3: MalSymbol = kNilSymbol
-    private var slot_value0: MalVal = kNil
-    private var slot_value1: MalVal = kNil
-    private var slot_value2: MalVal = kNil
-    private var slot_value3: MalVal = kNil
+    private var slot_name0 = kSymbolNil
+    private var slot_name1 = kSymbolNil
+    private var slot_name2 = kSymbolNil
+    private var slot_name3 = kSymbolNil
+    private var slot_value0 = kNil
+    private var slot_value1 = kNil
+    private var slot_value2 = kNil
+    private var slot_value3 = kNil
 }
index bb46a63..c6ed030 100644 (file)
@@ -4,6 +4,8 @@
 
 import Foundation
 
+var MalValPrintReadably = true
+
 func with_print_readably<T>(print_readably: Bool, fn: () -> T) -> T {
     let old = MalValPrintReadably
     MalValPrintReadably = print_readably
@@ -12,8 +14,14 @@ func with_print_readably<T>(print_readably: Bool, fn: () -> T) -> T {
     return result
 }
 
-func pr_str(m: MalVal, print_readably: Bool) -> String {
+func pr_str(m: MalVal, _ print_readably: Bool = MalValPrintReadably) -> String {
     return with_print_readably(print_readably) {
-        m.description
+        if is_string(m) {
+            return print_readably ? escape(m.description) : m.description
+        }
+        if is_keyword(m) {
+            return ":\(m.description)"
+        }
+        return m.description
     }
 }
dissimilarity index 60%
index e1ceeca..195cfe3 100644 (file)
-//******************************************************************************
-// MAL - reader
-//******************************************************************************
-
-import Foundation
-
-let kSymbolWithMeta = MalSymbol(symbol: "with-meta")
-let kSymbolDeref    = MalSymbol(symbol: "deref")
-
-let token_pattern =
-    "[[:space:],]*" +                       // Skip whitespace: a sequence of zero or more commas or [:space:]'s
-    "(" +
-        "~@" +                              // Literal "~@"
-        "|" +
-        "[\\[\\]{}()`'~^@]" +               // Punctuation: Any one of []{}()`'~^@
-        "|" +
-        "\"(?:\\\\.|[^\\\\\"])*\"" +        // Quoted string: characters other than \ or ", or any escaped characters
-        "|" +
-        ";.*" +                             // Comment: semicolon followed by anything
-        "|" +
-        "[^[:space:]\\[\\]{}()`'\",;]*" +   // Symbol, keyword, number, nil, true, false: any sequence of chars but [:space:] or []{}()`'",;
-    ")"
-
-let atom_pattern =
-    "(^;.*$)" +                 // Comment
-    "|" +
-    "(^-?[0-9]+$)" +            // Integer
-    "|" +
-    "(^-?[0-9][0-9.]*$)" +      // Float
-    "|" +
-    "(^nil$)" +                 // nil
-    "|" +
-    "(^true$)" +                // true
-    "|" +
-    "(^false$)" +               // false
-    "|" +
-    "(^\".*\"$)" +              // String
-    "|" +
-    "(:.*)" +                   // Keyword
-    "|" +
-    "(^[^\"]*$)"                // Symbol
-
-var token_regex: NSRegularExpression = NSRegularExpression(pattern:token_pattern, options:.allZeros, error:nil)!
-var atom_regex: NSRegularExpression = NSRegularExpression(pattern:atom_pattern, options:.allZeros, error:nil)!
-
-class Reader {
-
-    init(_ tokens: [String]) {
-        self.tokens = tokens
-        self.index = 0
-    }
-
-    func next() -> String? {
-        let token = peek()
-        increment()
-        return token
-    }
-
-    func peek() -> String? {
-        if index < tokens.count {
-            return tokens[index]
-        }
-        return nil
-    }
-
-    private func increment() {
-        ++index
-    }
-
-    private let tokens: [String]
-    private var index: Int
-}
-
-func tokenizer(s: String) -> [String] {
-    var tokens = [String]()
-    let range = NSMakeRange(0, count(s.utf16))
-    let matches = token_regex.matchesInString(s, options:.allZeros, range:range)
-    for match in matches as! [NSTextCheckingResult] {
-        if match.range.length > 0 {
-            let token = (s as NSString).substringWithRange(match.rangeAtIndex(1))
-            tokens.append(token)
-        }
-    }
-    return tokens
-}
-
-private func have_match_at(match: NSTextCheckingResult, index:Int) -> Bool {
-    return Int64(match.rangeAtIndex(index).location) < LLONG_MAX
-}
-
-func read_atom(token: String) -> MalVal {
-    let range = NSMakeRange(0, count(token.utf16))
-    let matches = atom_regex.matchesInString(token, options:.allZeros, range:range)
-    for match in matches as! [NSTextCheckingResult] {
-        if have_match_at(match, 1) {                // Comment
-            return MalComment(comment: token)
-        } else if have_match_at(match, 2) {         // Integer
-            if let value = NSNumberFormatter().numberFromString(token)?.longLongValue {
-                return MalInteger(value: value)
-            }
-            return MalError(message: "invalid integer: \(token)")
-        } else if have_match_at(match, 3) {         // Float
-            if let value = NSNumberFormatter().numberFromString(token)?.doubleValue {
-                return MalFloat(value: value)
-            }
-            return MalError(message: "invalid float: \(token)")
-        } else if have_match_at(match, 4) {         // nil
-            return MalNil()
-        } else if have_match_at(match, 5) {         // true
-            return MalTrue()
-        } else if have_match_at(match, 6) {         // false
-            return MalFalse()
-        } else if have_match_at(match, 7) {         // String
-            return MalString(escaped: token)
-        } else if have_match_at(match, 8) {         // Keyword
-            return MalKeyword(keyword: dropFirst(token))
-        } else if have_match_at(match, 9) {         // Symbol
-            return MalSymbol(symbol: token)
-        }
-    }
-    return MalError(message: "Unknown token=\(token)")
-}
-
-func read_elements(r: Reader, open: String, close: String) -> ([MalVal]?, MalVal?) {
-    var list = [MalVal]()
-    while let token = r.peek() {
-        if token == close {
-            r.increment() // Consume the closing paren
-            return (list, nil)
-        } else {
-            let item = read_form(r)
-            if is_error(item) {
-                return (nil, item)
-            }
-            if item.type != .TypeComment {
-                list.append(item)
-            }
-        }
-    }
-    return (nil, MalError(message: "ran out of tokens -- possibly unbalanced ()'s"))
-}
-
-func read_list(r: Reader) -> MalVal {
-    let (list, err) = read_elements(r, "(", ")")
-    return err != nil ? err! : MalList(array: list!)
-}
-
-func read_vector(r: Reader) -> MalVal {
-    let (list, err) = read_elements(r, "[", "]")
-    return err != nil ? err! : MalVector(array: list!)
-}
-
-func read_hashmap(r: Reader) -> MalVal {
-    let (list, err) = read_elements(r, "{", "}")
-    return err != nil ? err! : MalHashMap(array: list!)
-}
-
-func common_quote(r: Reader, symbol: String) -> MalVal {
-    let next = read_form(r)
-    if is_error(next) { return next }
-    return MalList(objects: MalSymbol(symbol: symbol), next)
-}
-
-func read_form(r: Reader) -> MalVal {
-    if let token = r.next() {
-        switch token {
-            case "(":
-                return read_list(r)
-            case ")":
-                return MalError(message: "unexpected \")\"")
-            case "[":
-                return read_vector(r)
-            case "]":
-                return MalError(message: "unexpected \"]\"")
-            case "{":
-                return read_hashmap(r)
-            case "}":
-                return MalError(message: "unexpected \"}\"")
-            case "`":
-                return common_quote(r, "quasiquote")
-            case "'":
-                return common_quote(r, "quote")
-            case "~":
-                return common_quote(r, "unquote")
-            case "~@":
-                return common_quote(r, "splice-unquote")
-            case "^":
-                let meta = read_form(r)
-                if is_error(meta) { return meta }
-                let form = read_form(r)
-                if is_error(form) { return form }
-                return MalList(objects: kSymbolWithMeta, form, meta)
-            case "@":
-                let form = read_form(r)
-                if is_error(form) { return form }
-                return MalList(objects: kSymbolDeref, form)
-            default:
-                return read_atom(token)
-        }
-    }
-    return MalError(message: "ran out of tokens -- possibly unbalanced ()'s")
-}
-
-func read_str(s: String) -> MalVal {
-    let tokens = tokenizer(s)
-    let reader = Reader(tokens)
-    let obj = read_form(reader)
-    return obj
-}
+//******************************************************************************
+// MAL - reader
+//******************************************************************************
+
+import Foundation
+
+private let kSymbolWithMeta = make_symbol("with-meta")
+private let kSymbolDeref    = make_symbol("deref")
+
+private let token_pattern =
+    "[[:space:],]*" +                       // Skip whitespace: a sequence of zero or more commas or [:space:]'s
+    "(" +
+        "~@" +                              // Literal "~@"
+        "|" +
+        "[\\[\\]{}()`'~^@]" +               // Punctuation: Any one of []{}()`'~^@
+        "|" +
+        "\"(?:\\\\.|[^\\\\\"])*\"" +        // Quoted string: characters other than \ or ", or any escaped characters
+        "|" +
+        ";.*" +                             // Comment: semicolon followed by anything
+        "|" +
+        "[^[:space:]\\[\\]{}()`'\",;]*" +   // Symbol, keyword, number, nil, true, false: any sequence of chars but [:space:] or []{}()`'",;
+    ")"
+
+private let atom_pattern =
+    "(^;.*$)" +                 // Comment
+    "|" +
+    "(^-?[0-9]+$)" +            // Integer
+    "|" +
+    "(^-?[0-9][0-9.]*$)" +      // Float
+    "|" +
+    "(^nil$)" +                 // nil
+    "|" +
+    "(^true$)" +                // true
+    "|" +
+    "(^false$)" +               // false
+    "|" +
+    "(^\".*\"$)" +              // String
+    "|" +
+    "(:.*)" +                   // Keyword
+    "|" +
+    "(^[^\"]*$)"                // Symbol
+
+private var token_regex: NSRegularExpression = try! NSRegularExpression(pattern: token_pattern, options: NSRegularExpressionOptions())
+private var atom_regex: NSRegularExpression = try! NSRegularExpression(pattern: atom_pattern, options: NSRegularExpressionOptions())
+
+private final class Reader {
+
+    init(_ tokens: [String]) {
+        self.tokens = tokens
+        self.index = 0
+    }
+
+    func next() -> String? {
+        let token = peek()
+        increment()
+        return token
+    }
+
+    func peek() -> String? {
+        if index < tokens.count {
+            return tokens[index]
+        }
+        return nil
+    }
+
+    private func increment() {
+        ++index
+    }
+
+    private let tokens: [String]
+    private var index: Int
+}
+
+private func tokenizer(s: String) -> [String] {
+    var tokens = [String]()
+    let range = NSMakeRange(0, s.characters.count)
+    let matches = token_regex.matchesInString(s, options: NSMatchingOptions(), range: range)
+    for match in matches {
+        if match.range.length > 0 {
+            let token = (s as NSString).substringWithRange(match.rangeAtIndex(1))
+            tokens.append(token)
+        }
+    }
+    return tokens
+}
+
+private func have_match(match: NSTextCheckingResult, at_index index: Int) -> Bool {
+    return Int64(match.rangeAtIndex(index).location) < LLONG_MAX
+}
+
+private func read_atom(token: String) throws -> MalVal {
+    let range = NSMakeRange(0, token.characters.count)
+    let matches = atom_regex.matchesInString(token, options: NSMatchingOptions(), range: range)
+    for match in matches {
+        if have_match(match, at_index: 1) {                // Comment
+            return make_comment()
+        } else if have_match(match, at_index: 2) {         // Integer
+            guard let value = NSNumberFormatter().numberFromString(token)?.longLongValue else {
+                try throw_error("invalid integer: \(token)")
+            }
+            return make_integer(value)
+        } else if have_match(match, at_index: 3) {         // Float
+            guard let value = NSNumberFormatter().numberFromString(token)?.doubleValue else {
+                try throw_error("invalid float: \(token)")
+            }
+            return make_float(value)
+        } else if have_match(match, at_index: 4) {         // nil
+            return make_nil()
+        } else if have_match(match, at_index: 5) {         // true
+            return make_true()
+        } else if have_match(match, at_index: 6) {         // false
+            return make_false()
+        } else if have_match(match, at_index: 7) {         // String
+            return make_string(unescape(token))
+        } else if have_match(match, at_index: 8) {         // Keyword
+            return make_keyword(token[token.startIndex.successor() ..< token.endIndex])
+        } else if have_match(match, at_index: 9) {         // Symbol
+            return make_symbol(token)
+        }
+    }
+    try throw_error("Unknown token=\(token)")
+}
+
+private func read_elements(r: Reader, _ open: String, _ close: String) throws -> [MalVal] {
+    var list = [MalVal]()
+    while let token = r.peek() {
+        if token == close {
+            r.increment() // Consume the closing paren
+            return list
+        } else {
+            let item = try read_form(r)
+            if !is_comment(item) {
+                list.append(item)
+            }
+        }
+    }
+    try throw_error("ran out of tokens -- possibly unbalanced ()'s")
+}
+
+private func read_list(r: Reader) throws -> MalVal {
+    return make_list(try read_elements(r, "(", ")"))
+}
+
+private func read_vector(r: Reader) throws -> MalVal {
+    return make_vector(try read_elements(r, "[", "]"))
+}
+
+private func read_hashmap(r: Reader) throws -> MalVal {
+    return make_hashmap(try read_elements(r, "{", "}"))
+}
+
+private func common_quote(r: Reader, _ symbol: String) throws -> MalVal {
+    let next = try read_form(r)
+    return make_list_from(make_symbol(symbol), next)
+}
+
+private func read_form(r: Reader) throws -> MalVal {
+    if let token = r.next() {
+        switch token {
+            case "(":
+                return try read_list(r)
+            case ")":
+                try throw_error("unexpected \")\"")
+            case "[":
+                return try read_vector(r)
+            case "]":
+                try throw_error("unexpected \"]\"")
+            case "{":
+                return try read_hashmap(r)
+            case "}":
+                try throw_error("unexpected \"}\"")
+            case "`":
+                return try common_quote(r, "quasiquote")
+            case "'":
+                return try common_quote(r, "quote")
+            case "~":
+                return try common_quote(r, "unquote")
+            case "~@":
+                return try common_quote(r, "splice-unquote")
+            case "^":
+                let meta = try read_form(r)
+                let form = try read_form(r)
+                return make_list_from(kSymbolWithMeta, form, meta)
+            case "@":
+                let form = try read_form(r)
+                return make_list_from(kSymbolDeref, form)
+            default:
+                return try read_atom(token)
+        }
+    }
+    try throw_error("ran out of tokens -- possibly unbalanced ()'s")
+}
+
+func read_str(s: String) throws -> MalVal {
+    let tokens = tokenizer(s)
+    let reader = Reader(tokens)
+    let obj = try read_form(reader)
+    return obj
+}
index 2a93b8a..3cb0084 100644 (file)
@@ -6,7 +6,7 @@ import Foundation
 
 private let HISTORY_FILE = "~/.mal-history"
 
-func with_history_file(do_to_history_file:(UnsafePointer<Int8>) -> ()) {
+private func with_history_file(do_to_history_file: (UnsafePointer<Int8>) -> ()) {
     HISTORY_FILE.withCString {
         (c_str) -> () in
         let abs_path = tilde_expand(UnsafeMutablePointer<Int8>(c_str))
index 8d04215..1de32e6 100644 (file)
@@ -11,27 +11,27 @@ import Foundation
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> String {
+private func READ(str: String) -> String {
     return str
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(ast: String) -> String {
+private func EVAL(ast: String) -> String {
     return ast
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: String) -> String {
+private func PRINT(exp: String) -> String {
     return exp
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String) -> String {
+private func RE(text: String) -> String {
     let ast = READ(text)
     let exp = EVAL(ast)
     return exp
@@ -39,19 +39,19 @@ func RE(text: String) -> String {
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String) -> String {
+private func REP(text: String) -> String {
     let exp = RE(text)
     return PRINT(exp)
 }
 
 // Perform the full REPL.
 //
-func REPL() {
+private func REPL() {
     while true {
         if let text = _readline("user> ") {
-            println("\(REP(text))")
+            print("\(REP(text))")
         } else {
-            println()
+            print("")
             break
         }
     }
index d8ef2c3..ee10da7 100644 (file)
@@ -11,46 +11,43 @@ import Foundation
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> MalVal {
-    return read_str(str)
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(ast: MalVal) -> MalVal {
-    if is_error(ast) { return ast }
+private func EVAL(ast: MalVal) -> MalVal {
     return ast
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
+private func PRINT(exp: MalVal) -> String {
     return pr_str(exp, true)
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
+private func RE(text: String) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+                return EVAL(ast)
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
     }
-    return exp
+    return nil
 }
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String) -> String? {
+private func REP(text: String) -> String? {
     let exp = RE(text)
     if exp == nil { return nil }
     return PRINT(exp!)
@@ -58,14 +55,14 @@ func REP(text: String) -> String? {
 
 // Perform the full REPL.
 //
-func REPL() {
+private func REPL() {
     while true {
         if let text = _readline("user> ") {
             if let output = REP(text) {
-                println("\(output)")
+                print("\(output)")
             }
         } else {
-            println()
+            print("")
             break
         }
     }
index be3b979..52aeab2 100644 (file)
@@ -11,8 +11,8 @@ import Foundation
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> MalVal {
-    return read_str(str)
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
 }
 
 // Perform a simple evaluation of the `ast` object. If it's a symbol,
@@ -20,129 +20,124 @@ func READ(str: String) -> MalVal {
 // elements (or just the values, in the case of the hashmap). Otherwise, return
 // the object unchanged.
 //
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
+    }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
     }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
+    }
+    return ast
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-        if is_error(ast) { return ast }
+private func EVAL(ast: MalVal, _ env: Environment) throws -> MalVal {
 
         if !is_list(ast) {
 
             // Not a list -- just evaluate and return.
 
-            let answer = eval_ast(ast, env)
+            let answer = try eval_ast(ast, env)
             return answer
         }
 
         // Special handling if it's a list.
 
-        var list = ast as! MalList
+        let list = as_list(ast)
 
         if list.isEmpty {
-            return list
+            return ast
         }
 
         // Standard list to be applied. Evaluate all the elements first.
 
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
+        let eval = try eval_ast(ast, env)
 
         // The result had better be a list and better be non-empty.
 
-        let eval_list = eval as! MalList
+        let eval_list = as_list(eval)
         if eval_list.isEmpty {
-            return eval_list
+            return eval
         }
 
         // Get the first element of the list and execute it.
 
         let first = eval_list.first()
-        let rest = eval_list.rest()
+        let rest = as_sequence(eval_list.rest())
 
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
             return answer
         }
 
         // The first element wasn't a function to be executed. Return an
         // error saying so.
 
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
+        try throw_error("first list item does not evaluate to a function: \(first)")
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
+private func PRINT(exp: MalVal) -> String {
     return pr_str(exp, true)
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
     }
-    return exp
+    return nil
 }
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String, env: Environment) -> String? {
+private func REP(text: String, _ env: Environment) -> String? {
     let exp = RE(text, env)
     if exp == nil { return nil }
     return PRINT(exp!)
@@ -150,21 +145,21 @@ func REP(text: String, env: Environment) -> String? {
 
 // Perform the full REPL.
 //
-func REPL(env: Environment) {
+private func REPL(env: Environment) {
     while true {
         if let text = _readline("user> ") {
             if let output = REP(text, env) {
-                println("\(output)")
+                print("\(output)")
             }
         } else {
-            println()
+            print("")
             break
         }
     }
 }
 
 func main() {
-    var env = Environment(outer: nil)
+    let env = Environment(outer: nil)
 
     load_history_file()
     load_builtins(env)
dissimilarity index 61%
index d1f5bac..7e3cde9 100644 (file)
-//******************************************************************************
-// MAL - step 3 - env
-//******************************************************************************
-// This file is automatically generated from templates/step.swift. Rather than
-// editing it directly, it's probably better to edit templates/step.swift and
-// regenerate this file. Otherwise, your change might be lost if/when someone
-// else performs that process.
-//******************************************************************************
-
-import Foundation
-
-// Symbols used in this module.
-//
-let kSymbolDef              = MalSymbol(symbol: "def!")
-let kSymbolLet              = MalSymbol(symbol: "let*")
-
-// Parse the string into an AST.
-//
-func READ(str: String) -> MalVal {
-    return read_str(str)
-}
-
-// Perform a simple evaluation of the `ast` object. If it's a symbol,
-// dereference it and return its value. If it's a collection, call EVAL on all
-// elements (or just the values, in the case of the hashmap). Otherwise, return
-// the object unchanged.
-//
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
-    }
-}
-
-// EVALuate "def!".
-//
-func eval_def(list: MalSequence, env: Environment) -> MalVal {
-    if list.count != 3 {
-        return MalError(message: "expected 2 arguments to def!, got \(list.count - 1)")
-    }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_symbol(arg1) {
-        return MalError(message: "expected symbol for first argument to def!")
-    }
-    let sym = arg1 as! MalSymbol
-    let value = EVAL(arg2, env)
-    if is_error(value) { return value }
-    return env.set(sym, value)
-}
-
-// EVALuate "let*".
-//
-func eval_let(list: MalSequence, env: Environment) -> MalVal {
-    if list.count != 3 {
-        return MalError(message: "expected 2 arguments to let*, got \(list.count - 1)")
-    }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_sequence(arg1) {
-        return MalError(message: "expected list for first argument to let*")
-    }
-    let bindings = arg1 as! MalSequence
-    if bindings.count % 2 == 1 {
-        return MalError(message: "expected even number of elements in bindings to let*, got \(bindings.count)")
-    }
-    var new_env = Environment(outer: env)
-    for var index = 0; index < bindings.count; index += 2 {
-        let binding_name = bindings[index]
-        let binding_value = bindings[index + 1]
-
-        if !is_symbol(binding_name) {
-            return MalError(message: "expected symbol for first element in binding pair")
-        }
-        let binding_symbol = binding_name as! MalSymbol
-        let evaluated_value = EVAL(binding_value, new_env)
-        if is_error(evaluated_value) { return evaluated_value }
-        new_env.set(binding_symbol, evaluated_value)
-    }
-    return EVAL(arg2, new_env)
-}
-
-// Walk the AST and completely evaluate it, handling macro expansions, special
-// forms and function calls.
-//
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-        if is_error(ast) { return ast }
-
-        if !is_list(ast) {
-
-            // Not a list -- just evaluate and return.
-
-            let answer = eval_ast(ast, env)
-            return answer
-        }
-
-        // Special handling if it's a list.
-
-        var list = ast as! MalList
-
-        if list.isEmpty {
-            return list
-        }
-
-        // Check for special forms, where we want to check the operation
-        // before evaluating all of the parameters.
-
-        let arg0 = list.first()
-        if is_symbol(arg0) {
-            let fn_symbol = arg0 as! MalSymbol
-
-            switch fn_symbol {
-                case kSymbolDef:            return eval_def(list, env)
-                case kSymbolLet:            return eval_let(list, env)
-                default:                    break
-            }
-        }
-
-        // Standard list to be applied. Evaluate all the elements first.
-
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
-
-        // The result had better be a list and better be non-empty.
-
-        let eval_list = eval as! MalList
-        if eval_list.isEmpty {
-            return eval_list
-        }
-
-        // Get the first element of the list and execute it.
-
-        let first = eval_list.first()
-        let rest = eval_list.rest()
-
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
-            return answer
-        }
-
-        // The first element wasn't a function to be executed. Return an
-        // error saying so.
-
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
-}
-
-// Convert the value into a human-readable string for printing.
-//
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
-    return pr_str(exp, true)
-}
-
-// Perform the READ and EVAL steps. Useful for when you don't care about the
-// printable result.
-//
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
-    }
-    return exp
-}
-
-// Perform the full READ/EVAL/PRINT, returning a printable string.
-//
-func REP(text: String, env: Environment) -> String? {
-    let exp = RE(text, env)
-    if exp == nil { return nil }
-    return PRINT(exp!)
-}
-
-// Perform the full REPL.
-//
-func REPL(env: Environment) {
-    while true {
-        if let text = _readline("user> ") {
-            if let output = REP(text, env) {
-                println("\(output)")
-            }
-        } else {
-            println()
-            break
-        }
-    }
-}
-
-func main() {
-    var env = Environment(outer: nil)
-
-    load_history_file()
-    load_builtins(env)
-
-    REPL(env)
-
-    save_history_file()
-}
+//******************************************************************************
+// MAL - step 3 - env
+//******************************************************************************
+// This file is automatically generated from templates/step.swift. Rather than
+// editing it directly, it's probably better to edit templates/step.swift and
+// regenerate this file. Otherwise, your change might be lost if/when someone
+// else performs that process.
+//******************************************************************************
+
+import Foundation
+
+// Symbols used in this module.
+//
+private let kValDef           = make_symbol("def!")
+private let kValLet           = make_symbol("let*")
+private let kValTry           = make_symbol("try*")
+
+private let kSymbolDef           = as_symbol(kValDef)
+private let kSymbolLet           = as_symbol(kValLet)
+
+// Parse the string into an AST.
+//
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
+}
+
+// Perform a simple evaluation of the `ast` object. If it's a symbol,
+// dereference it and return its value. If it's a collection, call EVAL on all
+// elements (or just the values, in the case of the hashmap). Otherwise, return
+// the object unchanged.
+//
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
+    }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
+    }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
+    }
+    return ast
+}
+
+// EVALuate "def!".
+//
+private func eval_def(list: MalSequence, _ env: Environment) throws -> MalVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")
+    }
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let sym = as_symbolQ(arg1) else {
+        try throw_error("expected symbol for first argument to def!")
+    }
+    let value = try EVAL(arg2, env)
+    return env.set(sym, value)
+}
+
+// EVALuate "let*".
+//
+private func eval_let(list: MalSequence, _ env: Environment) throws -> MalVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")
+    }
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let bindings = as_sequenceQ(arg1) else {
+        try throw_error("expected list for first argument to let*")
+    }
+    guard bindings.count % 2 == 0 else {
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")
+    }
+    let new_env = Environment(outer: env)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {
+        let binding_name = try! bindings.nth(index)
+        let binding_value = try! bindings.nth(index + 1)
+        guard let binding_symbol = as_symbolQ(binding_name) else {
+            try throw_error("expected symbol for first element in binding pair")
+        }
+        let evaluated_value = try EVAL(binding_value, new_env)
+        new_env.set(binding_symbol, evaluated_value)
+    }
+    return try EVAL(arg2, new_env)
+}
+
+// Walk the AST and completely evaluate it, handling macro expansions, special
+// forms and function calls.
+//
+private func EVAL(ast: MalVal, _ env: Environment) throws -> MalVal {
+
+        if !is_list(ast) {
+
+            // Not a list -- just evaluate and return.
+
+            let answer = try eval_ast(ast, env)
+            return answer
+        }
+
+        // Special handling if it's a list.
+
+        let list = as_list(ast)
+
+        if list.isEmpty {
+            return ast
+        }
+
+        // Check for special forms, where we want to check the operation
+        // before evaluating all of the parameters.
+
+        let arg0 = list.first()
+        if let fn_symbol = as_symbolQ(arg0) {
+
+            switch fn_symbol {
+                case kSymbolDef:            return try eval_def(list, env)
+                case kSymbolLet:            return try eval_let(list, env)
+                default:                    break
+            }
+        }
+
+        // Standard list to be applied. Evaluate all the elements first.
+
+        let eval = try eval_ast(ast, env)
+
+        // The result had better be a list and better be non-empty.
+
+        let eval_list = as_list(eval)
+        if eval_list.isEmpty {
+            return eval
+        }
+
+        // Get the first element of the list and execute it.
+
+        let first = eval_list.first()
+        let rest = as_sequence(eval_list.rest())
+
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
+            return answer
+        }
+
+        // The first element wasn't a function to be executed. Return an
+        // error saying so.
+
+        try throw_error("first list item does not evaluate to a function: \(first)")
+}
+
+// Convert the value into a human-readable string for printing.
+//
+private func PRINT(exp: MalVal) -> String {
+    return pr_str(exp, true)
+}
+
+// Perform the READ and EVAL steps. Useful for when you don't care about the
+// printable result.
+//
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
+    }
+    return nil
+}
+
+// Perform the full READ/EVAL/PRINT, returning a printable string.
+//
+private func REP(text: String, _ env: Environment) -> String? {
+    let exp = RE(text, env)
+    if exp == nil { return nil }
+    return PRINT(exp!)
+}
+
+// Perform the full REPL.
+//
+private func REPL(env: Environment) {
+    while true {
+        if let text = _readline("user> ") {
+            if let output = REP(text, env) {
+                print("\(output)")
+            }
+        } else {
+            print("")
+            break
+        }
+    }
+}
+
+func main() {
+    let env = Environment(outer: nil)
+
+    load_history_file()
+    load_builtins(env)
+
+    REPL(env)
+
+    save_history_file()
+}
dissimilarity index 66%
index fe61a07..c27ee29 100644 (file)
-//******************************************************************************
-// MAL - step 4 - if/fn/do
-//******************************************************************************
-// This file is automatically generated from templates/step.swift. Rather than
-// editing it directly, it's probably better to edit templates/step.swift and
-// regenerate this file. Otherwise, your change might be lost if/when someone
-// else performs that process.
-//******************************************************************************
-
-import Foundation
-
-// Symbols used in this module.
-//
-let kSymbolDef              = MalSymbol(symbol: "def!")
-let kSymbolDo               = MalSymbol(symbol: "do")
-let kSymbolFn               = MalSymbol(symbol: "fn*")
-let kSymbolIf               = MalSymbol(symbol: "if")
-let kSymbolLet              = MalSymbol(symbol: "let*")
-
-// Parse the string into an AST.
-//
-func READ(str: String) -> MalVal {
-    return read_str(str)
-}
-
-// Perform a simple evaluation of the `ast` object. If it's a symbol,
-// dereference it and return its value. If it's a collection, call EVAL on all
-// elements (or just the values, in the case of the hashmap). Otherwise, return
-// the object unchanged.
-//
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
-    }
-}
-
-// EVALuate "def!".
-//
-func eval_def(list: MalSequence, env: Environment) -> MalVal {
-    if list.count != 3 {
-        return MalError(message: "expected 2 arguments to def!, got \(list.count - 1)")
-    }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_symbol(arg1) {
-        return MalError(message: "expected symbol for first argument to def!")
-    }
-    let sym = arg1 as! MalSymbol
-    let value = EVAL(arg2, env)
-    if is_error(value) { return value }
-    return env.set(sym, value)
-}
-
-// EVALuate "let*".
-//
-func eval_let(list: MalSequence, env: Environment) -> MalVal {
-    if list.count != 3 {
-        return MalError(message: "expected 2 arguments to let*, got \(list.count - 1)")
-    }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_sequence(arg1) {
-        return MalError(message: "expected list for first argument to let*")
-    }
-    let bindings = arg1 as! MalSequence
-    if bindings.count % 2 == 1 {
-        return MalError(message: "expected even number of elements in bindings to let*, got \(bindings.count)")
-    }
-    var new_env = Environment(outer: env)
-    for var index = 0; index < bindings.count; index += 2 {
-        let binding_name = bindings[index]
-        let binding_value = bindings[index + 1]
-
-        if !is_symbol(binding_name) {
-            return MalError(message: "expected symbol for first element in binding pair")
-        }
-        let binding_symbol = binding_name as! MalSymbol
-        let evaluated_value = EVAL(binding_value, new_env)
-        if is_error(evaluated_value) { return evaluated_value }
-        new_env.set(binding_symbol, evaluated_value)
-    }
-    return EVAL(arg2, new_env)
-}
-
-// EVALuate "do".
-//
-func eval_do(list: MalSequence, env: Environment) -> MalVal {
-    let evaluated_ast = eval_ast(list.rest(), env)
-    if is_error(evaluated_ast) { return evaluated_ast }
-    let evaluated_seq = evaluated_ast as! MalSequence
-    return evaluated_seq.last()
-}
-
-// EVALuate "if".
-//
-func eval_if(list: MalSequence, env: Environment) -> MalVal {
-    if list.count < 3 {
-        return MalError(message: "expected at least 2 arguments to if, got \(list.count - 1)")
-    }
-    let cond_result = EVAL(list[1], env)
-    var new_ast = MalVal()
-    if is_truthy(cond_result) {
-        new_ast = list[2]
-    } else if list.count == 4 {
-        new_ast = list[3]
-    } else {
-        return MalNil()
-    }
-    return EVAL(new_ast, env)
-}
-
-// EVALuate "fn*".
-//
-func eval_fn(list: MalSequence, env: Environment) -> MalVal {
-    if list.count != 3 {
-        return MalError(message: "expected 2 arguments to fn*, got \(list.count - 1)")
-    }
-    if !is_sequence(list[1]) {
-        return MalError(message: "expected list or vector for first argument to fn*")
-    }
-    return MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env)
-}
-
-// Walk the AST and completely evaluate it, handling macro expansions, special
-// forms and function calls.
-//
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-        if is_error(ast) { return ast }
-
-        if !is_list(ast) {
-
-            // Not a list -- just evaluate and return.
-
-            let answer = eval_ast(ast, env)
-            return answer
-        }
-
-        // Special handling if it's a list.
-
-        var list = ast as! MalList
-
-        if list.isEmpty {
-            return list
-        }
-
-        // Check for special forms, where we want to check the operation
-        // before evaluating all of the parameters.
-
-        let arg0 = list.first()
-        if is_symbol(arg0) {
-            let fn_symbol = arg0 as! MalSymbol
-
-            switch fn_symbol {
-                case kSymbolDef:            return eval_def(list, env)
-                case kSymbolLet:            return eval_let(list, env)
-                case kSymbolDo:             return eval_do(list, env)
-                case kSymbolIf:             return eval_if(list, env)
-                case kSymbolFn:             return eval_fn(list, env)
-                default:                    break
-            }
-        }
-
-        // Standard list to be applied. Evaluate all the elements first.
-
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
-
-        // The result had better be a list and better be non-empty.
-
-        let eval_list = eval as! MalList
-        if eval_list.isEmpty {
-            return eval_list
-        }
-
-        // Get the first element of the list and execute it.
-
-        let first = eval_list.first()
-        let rest = eval_list.rest()
-
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
-            return answer
-        } else if is_closure(first) {
-            let fn = first as! MalClosure
-            var new_env = Environment(outer: fn.env)
-            let result = new_env.set_bindings(fn.args, with_exprs:rest)
-            if is_error(result) { return result }
-            let answer = EVAL(fn.body, new_env)
-            return answer
-        }
-
-        // The first element wasn't a function to be executed. Return an
-        // error saying so.
-
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
-}
-
-// Convert the value into a human-readable string for printing.
-//
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
-    return pr_str(exp, true)
-}
-
-// Perform the READ and EVAL steps. Useful for when you don't care about the
-// printable result.
-//
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
-    }
-    return exp
-}
-
-// Perform the full READ/EVAL/PRINT, returning a printable string.
-//
-func REP(text: String, env: Environment) -> String? {
-    let exp = RE(text, env)
-    if exp == nil { return nil }
-    return PRINT(exp!)
-}
-
-// Perform the full REPL.
-//
-func REPL(env: Environment) {
-    while true {
-        if let text = _readline("user> ") {
-            if let output = REP(text, env) {
-                println("\(output)")
-            }
-        } else {
-            println()
-            break
-        }
-    }
-}
-
-func main() {
-    var env = Environment(outer: nil)
-
-    load_history_file()
-    load_builtins(env)
-
-    RE("(def! not (fn* (a) (if a false true)))", env)
-    REPL(env)
-
-    save_history_file()
-}
+//******************************************************************************
+// MAL - step 4 - if/fn/do
+//******************************************************************************
+// This file is automatically generated from templates/step.swift. Rather than
+// editing it directly, it's probably better to edit templates/step.swift and
+// regenerate this file. Otherwise, your change might be lost if/when someone
+// else performs that process.
+//******************************************************************************
+
+import Foundation
+
+// Symbols used in this module.
+//
+private let kValDef           = make_symbol("def!")
+private let kValDo            = make_symbol("do")
+private let kValFn            = make_symbol("fn*")
+private let kValIf            = make_symbol("if")
+private let kValLet           = make_symbol("let*")
+private let kValTry           = make_symbol("try*")
+
+private let kSymbolDef           = as_symbol(kValDef)
+private let kSymbolDo            = as_symbol(kValDo)
+private let kSymbolFn            = as_symbol(kValFn)
+private let kSymbolIf            = as_symbol(kValIf)
+private let kSymbolLet           = as_symbol(kValLet)
+
+// Parse the string into an AST.
+//
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
+}
+
+// Perform a simple evaluation of the `ast` object. If it's a symbol,
+// dereference it and return its value. If it's a collection, call EVAL on all
+// elements (or just the values, in the case of the hashmap). Otherwise, return
+// the object unchanged.
+//
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
+    }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
+    }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
+    }
+    return ast
+}
+
+// EVALuate "def!".
+//
+private func eval_def(list: MalSequence, _ env: Environment) throws -> MalVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")
+    }
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let sym = as_symbolQ(arg1) else {
+        try throw_error("expected symbol for first argument to def!")
+    }
+    let value = try EVAL(arg2, env)
+    return env.set(sym, value)
+}
+
+// EVALuate "let*".
+//
+private func eval_let(list: MalSequence, _ env: Environment) throws -> MalVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")
+    }
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let bindings = as_sequenceQ(arg1) else {
+        try throw_error("expected list for first argument to let*")
+    }
+    guard bindings.count % 2 == 0 else {
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")
+    }
+    let new_env = Environment(outer: env)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {
+        let binding_name = try! bindings.nth(index)
+        let binding_value = try! bindings.nth(index + 1)
+        guard let binding_symbol = as_symbolQ(binding_name) else {
+            try throw_error("expected symbol for first element in binding pair")
+        }
+        let evaluated_value = try EVAL(binding_value, new_env)
+        new_env.set(binding_symbol, evaluated_value)
+    }
+    return try EVAL(arg2, new_env)
+}
+
+// EVALuate "do".
+//
+private func eval_do(list: MalSequence, _ env: Environment) throws -> MalVal {
+    let evaluated_ast = try eval_ast(list.rest(), env)
+    let evaluated_seq = as_sequence(evaluated_ast)
+    return evaluated_seq.last()
+}
+
+// EVALuate "if".
+//
+private func eval_if(list: MalSequence, _ env: Environment) throws -> MalVal {
+    guard list.count >= 3 else {
+        try throw_error("expected at least 2 arguments to if, got \(list.count - 1)")
+    }
+    let cond_result = try EVAL(try! list.nth(1), env)
+    var new_ast: MalVal
+    if is_truthy(cond_result) {
+        new_ast = try! list.nth(2)
+    } else if list.count == 4 {
+        new_ast = try! list.nth(3)
+    } else {
+        return make_nil()
+    }
+    return try EVAL(new_ast, env)
+}
+
+// EVALuate "fn*".
+//
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> MalVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to fn*, got \(list.count - 1)")
+    }
+    guard let seq = as_sequenceQ(try! list.nth(1)) else {
+        try throw_error("expected list or vector for first argument to fn*")
+    }
+    return make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env))
+}
+
+// Walk the AST and completely evaluate it, handling macro expansions, special
+// forms and function calls.
+//
+private func EVAL(ast: MalVal, _ env: Environment) throws -> MalVal {
+
+        if !is_list(ast) {
+
+            // Not a list -- just evaluate and return.
+
+            let answer = try eval_ast(ast, env)
+            return answer
+        }
+
+        // Special handling if it's a list.
+
+        let list = as_list(ast)
+
+        if list.isEmpty {
+            return ast
+        }
+
+        // Check for special forms, where we want to check the operation
+        // before evaluating all of the parameters.
+
+        let arg0 = list.first()
+        if let fn_symbol = as_symbolQ(arg0) {
+
+            switch fn_symbol {
+                case kSymbolDef:            return try eval_def(list, env)
+                case kSymbolLet:            return try eval_let(list, env)
+                case kSymbolDo:             return try eval_do(list, env)
+                case kSymbolIf:             return try eval_if(list, env)
+                case kSymbolFn:             return try eval_fn(list, env)
+                default:                    break
+            }
+        }
+
+        // Standard list to be applied. Evaluate all the elements first.
+
+        let eval = try eval_ast(ast, env)
+
+        // The result had better be a list and better be non-empty.
+
+        let eval_list = as_list(eval)
+        if eval_list.isEmpty {
+            return eval
+        }
+
+        // Get the first element of the list and execute it.
+
+        let first = eval_list.first()
+        let rest = as_sequence(eval_list.rest())
+
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
+            return answer
+        } else if let fn = as_closureQ(first) {
+            let new_env = Environment(outer: fn.env)
+            let _ = try new_env.set_bindings(fn.args, with_exprs: rest)
+            let answer = try EVAL(fn.body, new_env)
+            return answer
+        }
+
+        // The first element wasn't a function to be executed. Return an
+        // error saying so.
+
+        try throw_error("first list item does not evaluate to a function: \(first)")
+}
+
+// Convert the value into a human-readable string for printing.
+//
+private func PRINT(exp: MalVal) -> String {
+    return pr_str(exp, true)
+}
+
+// Perform the READ and EVAL steps. Useful for when you don't care about the
+// printable result.
+//
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
+    }
+    return nil
+}
+
+// Perform the full READ/EVAL/PRINT, returning a printable string.
+//
+private func REP(text: String, _ env: Environment) -> String? {
+    let exp = RE(text, env)
+    if exp == nil { return nil }
+    return PRINT(exp!)
+}
+
+// Perform the full REPL.
+//
+private func REPL(env: Environment) {
+    while true {
+        if let text = _readline("user> ") {
+            if let output = REP(text, env) {
+                print("\(output)")
+            }
+        } else {
+            print("")
+            break
+        }
+    }
+}
+
+func main() {
+    let env = Environment(outer: nil)
+
+    load_history_file()
+    load_builtins(env)
+
+    RE("(def! not (fn* (a) (if a false true)))", env)
+    REPL(env)
+
+    save_history_file()
+}
index 2110956..286414e 100644 (file)
@@ -12,28 +12,28 @@ import Foundation
 // The number of times EVAL has been entered recursively. We keep track of this
 // so that we can protect against overrunning the stack.
 //
-var EVAL_level = 0
+private var EVAL_level = 0
 
 // The maximum number of times we let EVAL recurse before throwing an exception.
 // Testing puts this at some place between 1800 and 1900. Let's keep it at 500
 // for safety's sake.
 //
-let EVAL_leval_max = 500
+private let EVAL_leval_max = 500
 
 // Control whether or not tail-call optimization (TCO) is enabled. We want it
 // `true` most of the time, but may disable it for debugging purposes (it's
 // easier to get a meaningful backtrace that way).
 //
-let TCO = true
+private let TCO = true
 
 // Control whether or not we emit debugging statements in EVAL.
 //
-let DEBUG_EVAL = false
+private let DEBUG_EVAL = false
 
 // String used to prefix information logged in EVAL. Increasing lengths of the
 // string are used the more EVAL is recursed.
 //
-let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
+private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
@@ -47,33 +47,31 @@ let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
 
 // Holds the prefix of INDENT_TEMPLATE used for actual logging.
 //
-var indent = String()
+private var indent = String()
 
 // Symbols used in this module.
 //
-let kSymbolDef              = MalSymbol(symbol: "def!")
-let kSymbolDo               = MalSymbol(symbol: "do")
-let kSymbolFn               = MalSymbol(symbol: "fn*")
-let kSymbolIf               = MalSymbol(symbol: "if")
-let kSymbolLet              = MalSymbol(symbol: "let*")
-
-// Class to help control the incrementing and decrementing of EVAL_level. We
-// create one of these on entry to EVAL, incrementing the level. When the
-// variable goes out of scope, the object is destroyed, decrementing the level.
-//
-class EVAL_Counter {
-    init() {
-        ++EVAL_level
-    }
-    deinit {
-        --EVAL_level
-    }
+private let kValDef           = make_symbol("def!")
+private let kValDo            = make_symbol("do")
+private let kValFn            = make_symbol("fn*")
+private let kValIf            = make_symbol("if")
+private let kValLet           = make_symbol("let*")
+private let kValTry           = make_symbol("try*")
+
+private let kSymbolDef           = as_symbol(kValDef)
+private let kSymbolDo            = as_symbol(kValDo)
+private let kSymbolFn            = as_symbol(kValFn)
+private let kSymbolIf            = as_symbol(kValIf)
+private let kSymbolLet           = as_symbol(kValLet)
+
+func substring(s: String, _ begin: Int, _ end: Int) -> String {
+    return s[s.startIndex.advancedBy(begin) ..< s.startIndex.advancedBy(end)]
 }
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> MalVal {
-    return read_str(str)
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
 }
 
 // Perform a simple evaluation of the `ast` object. If it's a symbol,
@@ -81,51 +79,45 @@ func READ(str: String) -> MalVal {
 // elements (or just the values, in the case of the hashmap). Otherwise, return
 // the object unchanged.
 //
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
+    }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
+    }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
     }
+    return ast
 }
 
-enum TCOVal {
+private enum TCOVal {
     case NoResult
     case Return(MalVal)
     case Continue(MalVal, Environment)
@@ -133,157 +125,147 @@ enum TCOVal {
     init() { self = .NoResult }
     init(_ result: MalVal) { self = .Return(result) }
     init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) }
-    init(_ e: String) { self = .Return(MalError(message: e)) }
 }
 
 // EVALuate "def!".
 //
-func eval_def(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to def!, got \(list.count - 1)")
+private func eval_def(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_symbol(arg1) {
-        return TCOVal("expected symbol for first argument to def!")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let sym = as_symbolQ(arg1) else {
+        try throw_error("expected symbol for first argument to def!")
     }
-    let sym = arg1 as! MalSymbol
-    let value = EVAL(arg2, env)
-    if is_error(value) { return TCOVal(value) }
+    let value = try EVAL(arg2, env)
     return TCOVal(env.set(sym, value))
 }
 
 // EVALuate "let*".
 //
-func eval_let(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to let*, got \(list.count - 1)")
+private func eval_let(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_sequence(arg1) {
-        return TCOVal("expected list for first argument to let*")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let bindings = as_sequenceQ(arg1) else {
+        try throw_error("expected list for first argument to let*")
     }
-    let bindings = arg1 as! MalSequence
-    if bindings.count % 2 == 1 {
-        return TCOVal("expected even number of elements in bindings to let*, got \(bindings.count)")
+    guard bindings.count % 2 == 0 else {
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")
     }
-    var new_env = Environment(outer: env)
-    for var index = 0; index < bindings.count; index += 2 {
-        let binding_name = bindings[index]
-        let binding_value = bindings[index + 1]
-
-        if !is_symbol(binding_name) {
-            return TCOVal("expected symbol for first element in binding pair")
+    let new_env = Environment(outer: env)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {
+        let binding_name = try! bindings.nth(index)
+        let binding_value = try! bindings.nth(index + 1)
+        guard let binding_symbol = as_symbolQ(binding_name) else {
+            try throw_error("expected symbol for first element in binding pair")
         }
-        let binding_symbol = binding_name as! MalSymbol
-        let evaluated_value = EVAL(binding_value, new_env)
-        if is_error(evaluated_value) { return TCOVal(evaluated_value) }
+        let evaluated_value = try EVAL(binding_value, new_env)
         new_env.set(binding_symbol, evaluated_value)
     }
     if TCO {
         return TCOVal(arg2, new_env)
     }
-    return TCOVal(EVAL(arg2, new_env))
+    return TCOVal(try EVAL(arg2, new_env))
 }
 
 // EVALuate "do".
 //
-func eval_do(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if TCO {
-        let eval = eval_ast(MalList(slice: list[1..<list.count-1]), env)
-        if is_error(eval) { return TCOVal(eval) }
+        let _ = try eval_ast(list.range_from(1, to: list.count-1), env)
         return TCOVal(list.last(), env)
     }
 
-    let evaluated_ast = eval_ast(list.rest(), env)
-    if is_error(evaluated_ast) { return TCOVal(evaluated_ast) }
-    let evaluated_seq = evaluated_ast as! MalSequence
+    let evaluated_ast = try eval_ast(list.rest(), env)
+    let evaluated_seq = as_sequence(evaluated_ast)
     return TCOVal(evaluated_seq.last())
 }
 
 // EVALuate "if".
 //
-func eval_if(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count < 3 {
-        return TCOVal("expected at least 2 arguments to if, got \(list.count - 1)")
+private func eval_if(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 3 else {
+        try throw_error("expected at least 2 arguments to if, got \(list.count - 1)")
     }
-    let cond_result = EVAL(list[1], env)
-    var new_ast = MalVal()
+    let cond_result = try EVAL(try! list.nth(1), env)
+    var new_ast: MalVal
     if is_truthy(cond_result) {
-        new_ast = list[2]
+        new_ast = try! list.nth(2)
     } else if list.count == 4 {
-        new_ast = list[3]
+        new_ast = try! list.nth(3)
     } else {
-        return TCOVal(MalNil())
+        return TCOVal(make_nil())
     }
     if TCO {
         return TCOVal(new_ast, env)
     }
-    return TCOVal(EVAL(new_ast, env))
+    return TCOVal(try EVAL(new_ast, env))
 }
 
 // EVALuate "fn*".
 //
-func eval_fn(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to fn*, got \(list.count - 1)")
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to fn*, got \(list.count - 1)")
     }
-    if !is_sequence(list[1]) {
-        return TCOVal("expected list or vector for first argument to fn*")
+    guard let seq = as_sequenceQ(try! list.nth(1)) else {
+        try throw_error("expected list or vector for first argument to fn*")
     }
-    return TCOVal(MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env))
+    return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env)))
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-    let x = EVAL_Counter()
-    if EVAL_level > EVAL_leval_max {
-        return MalError(message: "Recursing too many levels (> \(EVAL_leval_max))")
+private func EVAL(var ast: MalVal, var _ env: Environment) throws -> MalVal {
+    EVAL_level++
+    defer { EVAL_level-- }
+    guard EVAL_level <= EVAL_leval_max else {
+        try throw_error("Recursing too many levels (> \(EVAL_leval_max))")
     }
 
     if DEBUG_EVAL {
-        indent = prefix(INDENT_TEMPLATE, EVAL_level)
+        indent = substring(INDENT_TEMPLATE, 0, EVAL_level)
     }
 
     while true {
-        if is_error(ast) { return ast }
-        if DEBUG_EVAL { println("\(indent)>   \(ast)") }
+        if DEBUG_EVAL { print("\(indent)>   \(ast)") }
 
         if !is_list(ast) {
 
             // Not a list -- just evaluate and return.
 
-            let answer = eval_ast(ast, env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try eval_ast(ast, env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // Special handling if it's a list.
 
-        var list = ast as! MalList
-        if DEBUG_EVAL { println("\(indent)>.  \(list)") }
+        let list = as_list(ast)
+        if DEBUG_EVAL { print("\(indent)>.  \(list)") }
 
         if list.isEmpty {
-            return list
+            return ast
         }
 
         // Check for special forms, where we want to check the operation
         // before evaluating all of the parameters.
 
         let arg0 = list.first()
-        if is_symbol(arg0) {
-            var res: TCOVal
-            let fn_symbol = arg0 as! MalSymbol
+        if let fn_symbol = as_symbolQ(arg0) {
+            let res: TCOVal
 
             switch fn_symbol {
-                case kSymbolDef:            res = eval_def(list, env)
-                case kSymbolLet:            res = eval_let(list, env)
-                case kSymbolDo:             res = eval_do(list, env)
-                case kSymbolIf:             res = eval_if(list, env)
-                case kSymbolFn:             res = eval_fn(list, env)
+                case kSymbolDef:            res = try eval_def(list, env)
+                case kSymbolLet:            res = try eval_let(list, env)
+                case kSymbolDo:             res = try eval_do(list, env)
+                case kSymbolIf:             res = try eval_if(list, env)
+                case kSymbolFn:             res = try eval_fn(list, env)
                 default:                    res = TCOVal()
             }
             switch res {
@@ -295,78 +277,78 @@ func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
 
         // Standard list to be applied. Evaluate all the elements first.
 
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
+        let eval = try eval_ast(ast, env)
 
         // The result had better be a list and better be non-empty.
 
-        let eval_list = eval as! MalList
+        let eval_list = as_list(eval)
         if eval_list.isEmpty {
-            return eval_list
+            return eval
         }
 
-        if DEBUG_EVAL { println("\(indent)>>  \(eval)") }
+        if DEBUG_EVAL { print("\(indent)>>  \(eval)") }
 
         // Get the first element of the list and execute it.
 
         let first = eval_list.first()
-        let rest = eval_list.rest()
+        let rest = as_sequence(eval_list.rest())
 
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
-        } else if is_closure(first) {
-            let fn = first as! MalClosure
-            var new_env = Environment(outer: fn.env)
-            let result = new_env.set_bindings(fn.args, with_exprs:rest)
-            if is_error(result) { return result }
+        } else if let fn = as_closureQ(first) {
+            let new_env = Environment(outer: fn.env)
+            let _ = try new_env.set_bindings(fn.args, with_exprs: rest)
             if TCO {
                 env = new_env
                 ast = fn.body
                 continue
             }
-            let answer = EVAL(fn.body, new_env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try EVAL(fn.body, new_env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // The first element wasn't a function to be executed. Return an
         // error saying so.
 
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
+        try throw_error("first list item does not evaluate to a function: \(first)")
     }
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
+private func PRINT(exp: MalVal) -> String {
     return pr_str(exp, true)
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
     }
-    return exp
+    return nil
 }
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String, env: Environment) -> String? {
+private func REP(text: String, _ env: Environment) -> String? {
     let exp = RE(text, env)
     if exp == nil { return nil }
     return PRINT(exp!)
@@ -374,21 +356,21 @@ func REP(text: String, env: Environment) -> String? {
 
 // Perform the full REPL.
 //
-func REPL(env: Environment) {
+private func REPL(env: Environment) {
     while true {
         if let text = _readline("user> ") {
             if let output = REP(text, env) {
-                println("\(output)")
+                print("\(output)")
             }
         } else {
-            println()
+            print("")
             break
         }
     }
 }
 
 func main() {
-    var env = Environment(outer: nil)
+    let env = Environment(outer: nil)
 
     load_history_file()
     load_builtins(env)
index 809704d..45927ea 100644 (file)
@@ -12,28 +12,28 @@ import Foundation
 // The number of times EVAL has been entered recursively. We keep track of this
 // so that we can protect against overrunning the stack.
 //
-var EVAL_level = 0
+private var EVAL_level = 0
 
 // The maximum number of times we let EVAL recurse before throwing an exception.
 // Testing puts this at some place between 1800 and 1900. Let's keep it at 500
 // for safety's sake.
 //
-let EVAL_leval_max = 500
+private let EVAL_leval_max = 500
 
 // Control whether or not tail-call optimization (TCO) is enabled. We want it
 // `true` most of the time, but may disable it for debugging purposes (it's
 // easier to get a meaningful backtrace that way).
 //
-let TCO = true
+private let TCO = true
 
 // Control whether or not we emit debugging statements in EVAL.
 //
-let DEBUG_EVAL = false
+private let DEBUG_EVAL = false
 
 // String used to prefix information logged in EVAL. Increasing lengths of the
 // string are used the more EVAL is recursed.
 //
-let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
+private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
@@ -47,35 +47,35 @@ let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
 
 // Holds the prefix of INDENT_TEMPLATE used for actual logging.
 //
-var indent = String()
+private var indent = String()
 
 // Symbols used in this module.
 //
-let kSymbolArgv             = MalSymbol(symbol: "*ARGV*")
-let kSymbolDef              = MalSymbol(symbol: "def!")
-let kSymbolDo               = MalSymbol(symbol: "do")
-let kSymbolEval             = MalSymbol(symbol: "eval")
-let kSymbolFn               = MalSymbol(symbol: "fn*")
-let kSymbolIf               = MalSymbol(symbol: "if")
-let kSymbolLet              = MalSymbol(symbol: "let*")
-
-// Class to help control the incrementing and decrementing of EVAL_level. We
-// create one of these on entry to EVAL, incrementing the level. When the
-// variable goes out of scope, the object is destroyed, decrementing the level.
-//
-class EVAL_Counter {
-    init() {
-        ++EVAL_level
-    }
-    deinit {
-        --EVAL_level
-    }
+private let kValArgv          = make_symbol("*ARGV*")
+private let kValDef           = make_symbol("def!")
+private let kValDo            = make_symbol("do")
+private let kValEval          = make_symbol("eval")
+private let kValFn            = make_symbol("fn*")
+private let kValIf            = make_symbol("if")
+private let kValLet           = make_symbol("let*")
+private let kValTry           = make_symbol("try*")
+
+private let kSymbolArgv          = as_symbol(kValArgv)
+private let kSymbolDef           = as_symbol(kValDef)
+private let kSymbolDo            = as_symbol(kValDo)
+private let kSymbolEval          = as_symbol(kValEval)
+private let kSymbolFn            = as_symbol(kValFn)
+private let kSymbolIf            = as_symbol(kValIf)
+private let kSymbolLet           = as_symbol(kValLet)
+
+func substring(s: String, _ begin: Int, _ end: Int) -> String {
+    return s[s.startIndex.advancedBy(begin) ..< s.startIndex.advancedBy(end)]
 }
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> MalVal {
-    return read_str(str)
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
 }
 
 // Perform a simple evaluation of the `ast` object. If it's a symbol,
@@ -83,51 +83,45 @@ func READ(str: String) -> MalVal {
 // elements (or just the values, in the case of the hashmap). Otherwise, return
 // the object unchanged.
 //
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
+    }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
+    }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
     }
+    return ast
 }
 
-enum TCOVal {
+private enum TCOVal {
     case NoResult
     case Return(MalVal)
     case Continue(MalVal, Environment)
@@ -135,157 +129,147 @@ enum TCOVal {
     init() { self = .NoResult }
     init(_ result: MalVal) { self = .Return(result) }
     init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) }
-    init(_ e: String) { self = .Return(MalError(message: e)) }
 }
 
 // EVALuate "def!".
 //
-func eval_def(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to def!, got \(list.count - 1)")
+private func eval_def(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_symbol(arg1) {
-        return TCOVal("expected symbol for first argument to def!")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let sym = as_symbolQ(arg1) else {
+        try throw_error("expected symbol for first argument to def!")
     }
-    let sym = arg1 as! MalSymbol
-    let value = EVAL(arg2, env)
-    if is_error(value) { return TCOVal(value) }
+    let value = try EVAL(arg2, env)
     return TCOVal(env.set(sym, value))
 }
 
 // EVALuate "let*".
 //
-func eval_let(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to let*, got \(list.count - 1)")
+private func eval_let(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_sequence(arg1) {
-        return TCOVal("expected list for first argument to let*")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let bindings = as_sequenceQ(arg1) else {
+        try throw_error("expected list for first argument to let*")
     }
-    let bindings = arg1 as! MalSequence
-    if bindings.count % 2 == 1 {
-        return TCOVal("expected even number of elements in bindings to let*, got \(bindings.count)")
+    guard bindings.count % 2 == 0 else {
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")
     }
-    var new_env = Environment(outer: env)
-    for var index = 0; index < bindings.count; index += 2 {
-        let binding_name = bindings[index]
-        let binding_value = bindings[index + 1]
-
-        if !is_symbol(binding_name) {
-            return TCOVal("expected symbol for first element in binding pair")
+    let new_env = Environment(outer: env)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {
+        let binding_name = try! bindings.nth(index)
+        let binding_value = try! bindings.nth(index + 1)
+        guard let binding_symbol = as_symbolQ(binding_name) else {
+            try throw_error("expected symbol for first element in binding pair")
         }
-        let binding_symbol = binding_name as! MalSymbol
-        let evaluated_value = EVAL(binding_value, new_env)
-        if is_error(evaluated_value) { return TCOVal(evaluated_value) }
+        let evaluated_value = try EVAL(binding_value, new_env)
         new_env.set(binding_symbol, evaluated_value)
     }
     if TCO {
         return TCOVal(arg2, new_env)
     }
-    return TCOVal(EVAL(arg2, new_env))
+    return TCOVal(try EVAL(arg2, new_env))
 }
 
 // EVALuate "do".
 //
-func eval_do(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if TCO {
-        let eval = eval_ast(MalList(slice: list[1..<list.count-1]), env)
-        if is_error(eval) { return TCOVal(eval) }
+        let _ = try eval_ast(list.range_from(1, to: list.count-1), env)
         return TCOVal(list.last(), env)
     }
 
-    let evaluated_ast = eval_ast(list.rest(), env)
-    if is_error(evaluated_ast) { return TCOVal(evaluated_ast) }
-    let evaluated_seq = evaluated_ast as! MalSequence
+    let evaluated_ast = try eval_ast(list.rest(), env)
+    let evaluated_seq = as_sequence(evaluated_ast)
     return TCOVal(evaluated_seq.last())
 }
 
 // EVALuate "if".
 //
-func eval_if(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count < 3 {
-        return TCOVal("expected at least 2 arguments to if, got \(list.count - 1)")
+private func eval_if(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 3 else {
+        try throw_error("expected at least 2 arguments to if, got \(list.count - 1)")
     }
-    let cond_result = EVAL(list[1], env)
-    var new_ast = MalVal()
+    let cond_result = try EVAL(try! list.nth(1), env)
+    var new_ast: MalVal
     if is_truthy(cond_result) {
-        new_ast = list[2]
+        new_ast = try! list.nth(2)
     } else if list.count == 4 {
-        new_ast = list[3]
+        new_ast = try! list.nth(3)
     } else {
-        return TCOVal(MalNil())
+        return TCOVal(make_nil())
     }
     if TCO {
         return TCOVal(new_ast, env)
     }
-    return TCOVal(EVAL(new_ast, env))
+    return TCOVal(try EVAL(new_ast, env))
 }
 
 // EVALuate "fn*".
 //
-func eval_fn(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to fn*, got \(list.count - 1)")
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to fn*, got \(list.count - 1)")
     }
-    if !is_sequence(list[1]) {
-        return TCOVal("expected list or vector for first argument to fn*")
+    guard let seq = as_sequenceQ(try! list.nth(1)) else {
+        try throw_error("expected list or vector for first argument to fn*")
     }
-    return TCOVal(MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env))
+    return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env)))
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-    let x = EVAL_Counter()
-    if EVAL_level > EVAL_leval_max {
-        return MalError(message: "Recursing too many levels (> \(EVAL_leval_max))")
+private func EVAL(var ast: MalVal, var _ env: Environment) throws -> MalVal {
+    EVAL_level++
+    defer { EVAL_level-- }
+    guard EVAL_level <= EVAL_leval_max else {
+        try throw_error("Recursing too many levels (> \(EVAL_leval_max))")
     }
 
     if DEBUG_EVAL {
-        indent = prefix(INDENT_TEMPLATE, EVAL_level)
+        indent = substring(INDENT_TEMPLATE, 0, EVAL_level)
     }
 
     while true {
-        if is_error(ast) { return ast }
-        if DEBUG_EVAL { println("\(indent)>   \(ast)") }
+        if DEBUG_EVAL { print("\(indent)>   \(ast)") }
 
         if !is_list(ast) {
 
             // Not a list -- just evaluate and return.
 
-            let answer = eval_ast(ast, env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try eval_ast(ast, env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // Special handling if it's a list.
 
-        var list = ast as! MalList
-        if DEBUG_EVAL { println("\(indent)>.  \(list)") }
+        let list = as_list(ast)
+        if DEBUG_EVAL { print("\(indent)>.  \(list)") }
 
         if list.isEmpty {
-            return list
+            return ast
         }
 
         // Check for special forms, where we want to check the operation
         // before evaluating all of the parameters.
 
         let arg0 = list.first()
-        if is_symbol(arg0) {
-            var res: TCOVal
-            let fn_symbol = arg0 as! MalSymbol
+        if let fn_symbol = as_symbolQ(arg0) {
+            let res: TCOVal
 
             switch fn_symbol {
-                case kSymbolDef:            res = eval_def(list, env)
-                case kSymbolLet:            res = eval_let(list, env)
-                case kSymbolDo:             res = eval_do(list, env)
-                case kSymbolIf:             res = eval_if(list, env)
-                case kSymbolFn:             res = eval_fn(list, env)
+                case kSymbolDef:            res = try eval_def(list, env)
+                case kSymbolLet:            res = try eval_let(list, env)
+                case kSymbolDo:             res = try eval_do(list, env)
+                case kSymbolIf:             res = try eval_if(list, env)
+                case kSymbolFn:             res = try eval_fn(list, env)
                 default:                    res = TCOVal()
             }
             switch res {
@@ -297,78 +281,78 @@ func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
 
         // Standard list to be applied. Evaluate all the elements first.
 
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
+        let eval = try eval_ast(ast, env)
 
         // The result had better be a list and better be non-empty.
 
-        let eval_list = eval as! MalList
+        let eval_list = as_list(eval)
         if eval_list.isEmpty {
-            return eval_list
+            return eval
         }
 
-        if DEBUG_EVAL { println("\(indent)>>  \(eval)") }
+        if DEBUG_EVAL { print("\(indent)>>  \(eval)") }
 
         // Get the first element of the list and execute it.
 
         let first = eval_list.first()
-        let rest = eval_list.rest()
+        let rest = as_sequence(eval_list.rest())
 
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
-        } else if is_closure(first) {
-            let fn = first as! MalClosure
-            var new_env = Environment(outer: fn.env)
-            let result = new_env.set_bindings(fn.args, with_exprs:rest)
-            if is_error(result) { return result }
+        } else if let fn = as_closureQ(first) {
+            let new_env = Environment(outer: fn.env)
+            let _ = try new_env.set_bindings(fn.args, with_exprs: rest)
             if TCO {
                 env = new_env
                 ast = fn.body
                 continue
             }
-            let answer = EVAL(fn.body, new_env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try EVAL(fn.body, new_env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // The first element wasn't a function to be executed. Return an
         // error saying so.
 
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
+        try throw_error("first list item does not evaluate to a function: \(first)")
     }
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
+private func PRINT(exp: MalVal) -> String {
     return pr_str(exp, true)
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
     }
-    return exp
+    return nil
 }
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String, env: Environment) -> String? {
+private func REP(text: String, _ env: Environment) -> String? {
     let exp = RE(text, env)
     if exp == nil { return nil }
     return PRINT(exp!)
@@ -376,14 +360,14 @@ func REP(text: String, env: Environment) -> String? {
 
 // Perform the full REPL.
 //
-func REPL(env: Environment) {
+private func REPL(env: Environment) {
     while true {
         if let text = _readline("user> ") {
             if let output = REP(text, env) {
-                println("\(output)")
+                print("\(output)")
             }
         } else {
-            println()
+            print("")
             break
         }
     }
@@ -394,13 +378,13 @@ func REPL(env: Environment) {
 // taken as a script to execute. If one exists, it is executed in lieu of
 // running the REPL.
 //
-func process_command_line(args:[String], env:Environment) -> Bool {
-    var argv = MalList()
+private func process_command_line(args: [String], _ env: Environment) -> Bool {
+    var argv = make_list()
     if args.count > 2 {
         let args1 = args[2..<args.count]
-        let args2 = args1.map { MalString(unescaped: $0) as MalVal }
+        let args2 = args1.map { make_string($0) }
         let args3 = [MalVal](args2)
-        argv = MalList(array: args3)
+        argv = make_list(args3)
     }
     env.set(kSymbolArgv, argv)
 
@@ -413,7 +397,7 @@ func process_command_line(args:[String], env:Environment) -> Bool {
 }
 
 func main() {
-    var env = Environment(outer: nil)
+    let env = Environment(outer: nil)
 
     load_history_file()
     load_builtins(env)
@@ -421,10 +405,10 @@ func main() {
     RE("(def! not (fn* (a) (if a false true)))", env)
     RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env)
 
-    env.set(kSymbolEval, MalBuiltin(function: {
-         unwrap($0) {
-            (ast:MalVal) -> MalVal in
-            EVAL(ast, env)
+    env.set(kSymbolEval, make_builtin({
+         try! unwrap_args($0) {
+            (ast: MalVal) -> MalVal in
+            try EVAL(ast, env)
          }
     }))
 
index 8026c61..e5324d1 100644 (file)
@@ -12,28 +12,28 @@ import Foundation
 // The number of times EVAL has been entered recursively. We keep track of this
 // so that we can protect against overrunning the stack.
 //
-var EVAL_level = 0
+private var EVAL_level = 0
 
 // The maximum number of times we let EVAL recurse before throwing an exception.
 // Testing puts this at some place between 1800 and 1900. Let's keep it at 500
 // for safety's sake.
 //
-let EVAL_leval_max = 500
+private let EVAL_leval_max = 500
 
 // Control whether or not tail-call optimization (TCO) is enabled. We want it
 // `true` most of the time, but may disable it for debugging purposes (it's
 // easier to get a meaningful backtrace that way).
 //
-let TCO = true
+private let TCO = true
 
 // Control whether or not we emit debugging statements in EVAL.
 //
-let DEBUG_EVAL = false
+private let DEBUG_EVAL = false
 
 // String used to prefix information logged in EVAL. Increasing lengths of the
 // string are used the more EVAL is recursed.
 //
-let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
+private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
@@ -47,49 +47,56 @@ let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
 
 // Holds the prefix of INDENT_TEMPLATE used for actual logging.
 //
-var indent = String()
+private var indent = String()
 
 // Symbols used in this module.
 //
-let kSymbolArgv             = MalSymbol(symbol: "*ARGV*")
-let kSymbolConcat           = MalSymbol(symbol: "concat")
-let kSymbolCons             = MalSymbol(symbol: "cons")
-let kSymbolDef              = MalSymbol(symbol: "def!")
-let kSymbolDo               = MalSymbol(symbol: "do")
-let kSymbolEval             = MalSymbol(symbol: "eval")
-let kSymbolFn               = MalSymbol(symbol: "fn*")
-let kSymbolIf               = MalSymbol(symbol: "if")
-let kSymbolLet              = MalSymbol(symbol: "let*")
-let kSymbolQuasiQuote       = MalSymbol(symbol: "quasiquote")
-let kSymbolQuote            = MalSymbol(symbol: "quote")
-let kSymbolSpliceUnquote    = MalSymbol(symbol: "splice-unquote")
-let kSymbolUnquote          = MalSymbol(symbol: "unquote")
-
-// Class to help control the incrementing and decrementing of EVAL_level. We
-// create one of these on entry to EVAL, incrementing the level. When the
-// variable goes out of scope, the object is destroyed, decrementing the level.
-//
-class EVAL_Counter {
-    init() {
-        ++EVAL_level
-    }
-    deinit {
-        --EVAL_level
-    }
+private let kValArgv          = make_symbol("*ARGV*")
+private let kValConcat        = make_symbol("concat")
+private let kValCons          = make_symbol("cons")
+private let kValDef           = make_symbol("def!")
+private let kValDo            = make_symbol("do")
+private let kValEval          = make_symbol("eval")
+private let kValFn            = make_symbol("fn*")
+private let kValIf            = make_symbol("if")
+private let kValLet           = make_symbol("let*")
+private let kValQuasiQuote    = make_symbol("quasiquote")
+private let kValQuote         = make_symbol("quote")
+private let kValSpliceUnquote = make_symbol("splice-unquote")
+private let kValUnquote       = make_symbol("unquote")
+private let kValTry           = make_symbol("try*")
+
+private let kSymbolArgv          = as_symbol(kValArgv)
+private let kSymbolConcat        = as_symbol(kValConcat)
+private let kSymbolCons          = as_symbol(kValCons)
+private let kSymbolDef           = as_symbol(kValDef)
+private let kSymbolDo            = as_symbol(kValDo)
+private let kSymbolEval          = as_symbol(kValEval)
+private let kSymbolFn            = as_symbol(kValFn)
+private let kSymbolIf            = as_symbol(kValIf)
+private let kSymbolLet           = as_symbol(kValLet)
+private let kSymbolQuasiQuote    = as_symbol(kValQuasiQuote)
+private let kSymbolQuote         = as_symbol(kValQuote)
+private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote)
+private let kSymbolUnquote       = as_symbol(kValUnquote)
+
+func substring(s: String, _ begin: Int, _ end: Int) -> String {
+    return s[s.startIndex.advancedBy(begin) ..< s.startIndex.advancedBy(end)]
 }
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> MalVal {
-    return read_str(str)
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
 }
 
 // Return whether or not `val` is a non-empty list.
 //
-func is_pair(val:MalVal) -> Bool {
-    if !is_sequence(val) { return false }
-    let list = val as! MalSequence
-    return !list.isEmpty
+private func is_pair(val: MalVal) -> Bool {
+    if let seq = as_sequenceQ(val) {
+        return !seq.isEmpty
+    }
+    return false
 }
 
 // Evaluate `quasiquote`, possibly recursing in the process.
@@ -128,15 +135,15 @@ func is_pair(val:MalVal) -> Bool {
 // list) with the remaining items of the list containing that splice-quote
 // expression. However, it's not clear to me why the handling of "unquote" is
 // not handled similarly, for consistency's sake.
-
-func quasiquote(qq_arg:MalVal) -> MalVal {
+//
+private func quasiquote(qq_arg: MalVal) throws -> MalVal {
 
     // If the argument is an atom or empty list:
     //
     // Return: (quote <argument>)
 
     if !is_pair(qq_arg) {
-        return MalList(objects: kSymbolQuote, qq_arg)
+        return make_list_from(kValQuote, qq_arg)
     }
 
     // The argument is a non-empty list -- that is (item rest...)
@@ -146,12 +153,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //
     // Return: item
 
-    let qq_list = qq_arg as! MalSequence
-    if is_symbol(qq_list.first()) {
-        let sym = qq_list.first() as! MalSymbol
-        if sym == kSymbolUnquote {
-            return qq_list.count >= 2 ? qq_list[1] : MalNil()
-        }
+    let qq_list = as_sequence(qq_arg)
+    if let sym = as_symbolQ(qq_list.first()) where sym == kSymbolUnquote {
+        return qq_list.count >= 2 ? try! qq_list.nth(1) : make_nil()
     }
 
     // If the first item from the list is itself a non-empty list starting with
@@ -160,14 +164,10 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     // Return: (concat item quasiquote(rest...))
 
     if is_pair(qq_list.first()) {
-        let qq_list_item0 = qq_list.first() as! MalSequence
-        if is_symbol(qq_list_item0.first()) {
-            let sym = qq_list_item0.first() as! MalSymbol
-            if sym == kSymbolSpliceUnquote {
-                let result = quasiquote(qq_list.rest())
-                if is_error(result) { return result }
-                return MalList(array: [kSymbolConcat, qq_list_item0[1], result])
-            }
+        let qq_list_item0 = as_sequence(qq_list.first())
+        if let sym = as_symbolQ(qq_list_item0.first()) where sym == kSymbolSpliceUnquote {
+            let result = try quasiquote(qq_list.rest())
+            return make_list_from(kValConcat, try! qq_list_item0.nth(1), result)
         }
     }
 
@@ -175,13 +175,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //
     // Return: (cons (quasiquote item) (quasiquote (rest...))
 
-    let first = quasiquote(qq_list.first())
-    if is_error(first) { return first }
-
-    let rest = quasiquote(qq_list.rest())
-    if is_error(rest) { return rest }
-
-    return MalList(objects: kSymbolCons, first, rest)
+    let first = try quasiquote(qq_list.first())
+    let rest = try quasiquote(qq_list.rest())
+    return make_list_from(kValCons, first, rest)
 }
 
 // Perform a simple evaluation of the `ast` object. If it's a symbol,
@@ -189,51 +185,45 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
 // elements (or just the values, in the case of the hashmap). Otherwise, return
 // the object unchanged.
 //
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
     }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
+    }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
+    }
+    return ast
 }
 
-enum TCOVal {
+private enum TCOVal {
     case NoResult
     case Return(MalVal)
     case Continue(MalVal, Environment)
@@ -241,180 +231,170 @@ enum TCOVal {
     init() { self = .NoResult }
     init(_ result: MalVal) { self = .Return(result) }
     init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) }
-    init(_ e: String) { self = .Return(MalError(message: e)) }
 }
 
 // EVALuate "def!".
 //
-func eval_def(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to def!, got \(list.count - 1)")
+private func eval_def(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_symbol(arg1) {
-        return TCOVal("expected symbol for first argument to def!")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let sym = as_symbolQ(arg1) else {
+        try throw_error("expected symbol for first argument to def!")
     }
-    let sym = arg1 as! MalSymbol
-    let value = EVAL(arg2, env)
-    if is_error(value) { return TCOVal(value) }
+    let value = try EVAL(arg2, env)
     return TCOVal(env.set(sym, value))
 }
 
 // EVALuate "let*".
 //
-func eval_let(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to let*, got \(list.count - 1)")
+private func eval_let(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_sequence(arg1) {
-        return TCOVal("expected list for first argument to let*")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let bindings = as_sequenceQ(arg1) else {
+        try throw_error("expected list for first argument to let*")
     }
-    let bindings = arg1 as! MalSequence
-    if bindings.count % 2 == 1 {
-        return TCOVal("expected even number of elements in bindings to let*, got \(bindings.count)")
+    guard bindings.count % 2 == 0 else {
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")
     }
-    var new_env = Environment(outer: env)
-    for var index = 0; index < bindings.count; index += 2 {
-        let binding_name = bindings[index]
-        let binding_value = bindings[index + 1]
-
-        if !is_symbol(binding_name) {
-            return TCOVal("expected symbol for first element in binding pair")
+    let new_env = Environment(outer: env)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {
+        let binding_name = try! bindings.nth(index)
+        let binding_value = try! bindings.nth(index + 1)
+        guard let binding_symbol = as_symbolQ(binding_name) else {
+            try throw_error("expected symbol for first element in binding pair")
         }
-        let binding_symbol = binding_name as! MalSymbol
-        let evaluated_value = EVAL(binding_value, new_env)
-        if is_error(evaluated_value) { return TCOVal(evaluated_value) }
+        let evaluated_value = try EVAL(binding_value, new_env)
         new_env.set(binding_symbol, evaluated_value)
     }
     if TCO {
         return TCOVal(arg2, new_env)
     }
-    return TCOVal(EVAL(arg2, new_env))
+    return TCOVal(try EVAL(arg2, new_env))
 }
 
 // EVALuate "do".
 //
-func eval_do(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if TCO {
-        let eval = eval_ast(MalList(slice: list[1..<list.count-1]), env)
-        if is_error(eval) { return TCOVal(eval) }
+        let _ = try eval_ast(list.range_from(1, to: list.count-1), env)
         return TCOVal(list.last(), env)
     }
 
-    let evaluated_ast = eval_ast(list.rest(), env)
-    if is_error(evaluated_ast) { return TCOVal(evaluated_ast) }
-    let evaluated_seq = evaluated_ast as! MalSequence
+    let evaluated_ast = try eval_ast(list.rest(), env)
+    let evaluated_seq = as_sequence(evaluated_ast)
     return TCOVal(evaluated_seq.last())
 }
 
 // EVALuate "if".
 //
-func eval_if(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count < 3 {
-        return TCOVal("expected at least 2 arguments to if, got \(list.count - 1)")
+private func eval_if(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 3 else {
+        try throw_error("expected at least 2 arguments to if, got \(list.count - 1)")
     }
-    let cond_result = EVAL(list[1], env)
-    var new_ast = MalVal()
+    let cond_result = try EVAL(try! list.nth(1), env)
+    var new_ast: MalVal
     if is_truthy(cond_result) {
-        new_ast = list[2]
+        new_ast = try! list.nth(2)
     } else if list.count == 4 {
-        new_ast = list[3]
+        new_ast = try! list.nth(3)
     } else {
-        return TCOVal(MalNil())
+        return TCOVal(make_nil())
     }
     if TCO {
         return TCOVal(new_ast, env)
     }
-    return TCOVal(EVAL(new_ast, env))
+    return TCOVal(try EVAL(new_ast, env))
 }
 
 // EVALuate "fn*".
 //
-func eval_fn(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to fn*, got \(list.count - 1)")
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to fn*, got \(list.count - 1)")
     }
-    if !is_sequence(list[1]) {
-        return TCOVal("expected list or vector for first argument to fn*")
+    guard let seq = as_sequenceQ(try! list.nth(1)) else {
+        try throw_error("expected list or vector for first argument to fn*")
     }
-    return TCOVal(MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env))
+    return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env)))
 }
 
 // EVALuate "quote".
 //
-func eval_quote(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if list.count >= 2 {
-        return TCOVal(list[1])
+        return TCOVal(try! list.nth(1))
     }
-    return TCOVal(MalNil())
+    return TCOVal(make_nil())
 }
 
 // EVALuate "quasiquote".
 //
-func eval_quasiquote(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count >= 2 {
-        if TCO {
-            return TCOVal(quasiquote(list[1]), env)
-        }
-        return TCOVal(EVAL(quasiquote(list[1]), env))
+private func eval_quasiquote(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 2 else {
+        try throw_error("Expected non-nil parameter to 'quasiquote'")
+    }
+    if TCO {
+        return TCOVal(try quasiquote(try! list.nth(1)), env)
     }
-    return TCOVal("Expected non-nil parameter to 'quasiquote'")
+    return TCOVal(try EVAL(try quasiquote(try! list.nth(1)), env))
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-    let x = EVAL_Counter()
-    if EVAL_level > EVAL_leval_max {
-        return MalError(message: "Recursing too many levels (> \(EVAL_leval_max))")
+private func EVAL(var ast: MalVal, var _ env: Environment) throws -> MalVal {
+    EVAL_level++
+    defer { EVAL_level-- }
+    guard EVAL_level <= EVAL_leval_max else {
+        try throw_error("Recursing too many levels (> \(EVAL_leval_max))")
     }
 
     if DEBUG_EVAL {
-        indent = prefix(INDENT_TEMPLATE, EVAL_level)
+        indent = substring(INDENT_TEMPLATE, 0, EVAL_level)
     }
 
     while true {
-        if is_error(ast) { return ast }
-        if DEBUG_EVAL { println("\(indent)>   \(ast)") }
+        if DEBUG_EVAL { print("\(indent)>   \(ast)") }
 
         if !is_list(ast) {
 
             // Not a list -- just evaluate and return.
 
-            let answer = eval_ast(ast, env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try eval_ast(ast, env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // Special handling if it's a list.
 
-        var list = ast as! MalList
-        if DEBUG_EVAL { println("\(indent)>.  \(list)") }
+        let list = as_list(ast)
+        if DEBUG_EVAL { print("\(indent)>.  \(list)") }
 
         if list.isEmpty {
-            return list
+            return ast
         }
 
         // Check for special forms, where we want to check the operation
         // before evaluating all of the parameters.
 
         let arg0 = list.first()
-        if is_symbol(arg0) {
-            var res: TCOVal
-            let fn_symbol = arg0 as! MalSymbol
+        if let fn_symbol = as_symbolQ(arg0) {
+            let res: TCOVal
 
             switch fn_symbol {
-                case kSymbolDef:            res = eval_def(list, env)
-                case kSymbolLet:            res = eval_let(list, env)
-                case kSymbolDo:             res = eval_do(list, env)
-                case kSymbolIf:             res = eval_if(list, env)
-                case kSymbolFn:             res = eval_fn(list, env)
-                case kSymbolQuote:          res = eval_quote(list, env)
-                case kSymbolQuasiQuote:     res = eval_quasiquote(list, env)
+                case kSymbolDef:            res = try eval_def(list, env)
+                case kSymbolLet:            res = try eval_let(list, env)
+                case kSymbolDo:             res = try eval_do(list, env)
+                case kSymbolIf:             res = try eval_if(list, env)
+                case kSymbolFn:             res = try eval_fn(list, env)
+                case kSymbolQuote:          res = try eval_quote(list, env)
+                case kSymbolQuasiQuote:     res = try eval_quasiquote(list, env)
                 default:                    res = TCOVal()
             }
             switch res {
@@ -426,78 +406,78 @@ func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
 
         // Standard list to be applied. Evaluate all the elements first.
 
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
+        let eval = try eval_ast(ast, env)
 
         // The result had better be a list and better be non-empty.
 
-        let eval_list = eval as! MalList
+        let eval_list = as_list(eval)
         if eval_list.isEmpty {
-            return eval_list
+            return eval
         }
 
-        if DEBUG_EVAL { println("\(indent)>>  \(eval)") }
+        if DEBUG_EVAL { print("\(indent)>>  \(eval)") }
 
         // Get the first element of the list and execute it.
 
         let first = eval_list.first()
-        let rest = eval_list.rest()
+        let rest = as_sequence(eval_list.rest())
 
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
-        } else if is_closure(first) {
-            let fn = first as! MalClosure
-            var new_env = Environment(outer: fn.env)
-            let result = new_env.set_bindings(fn.args, with_exprs:rest)
-            if is_error(result) { return result }
+        } else if let fn = as_closureQ(first) {
+            let new_env = Environment(outer: fn.env)
+            let _ = try new_env.set_bindings(fn.args, with_exprs: rest)
             if TCO {
                 env = new_env
                 ast = fn.body
                 continue
             }
-            let answer = EVAL(fn.body, new_env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try EVAL(fn.body, new_env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // The first element wasn't a function to be executed. Return an
         // error saying so.
 
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
+        try throw_error("first list item does not evaluate to a function: \(first)")
     }
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
+private func PRINT(exp: MalVal) -> String {
     return pr_str(exp, true)
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
     }
-    return exp
+    return nil
 }
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String, env: Environment) -> String? {
+private func REP(text: String, _ env: Environment) -> String? {
     let exp = RE(text, env)
     if exp == nil { return nil }
     return PRINT(exp!)
@@ -505,14 +485,14 @@ func REP(text: String, env: Environment) -> String? {
 
 // Perform the full REPL.
 //
-func REPL(env: Environment) {
+private func REPL(env: Environment) {
     while true {
         if let text = _readline("user> ") {
             if let output = REP(text, env) {
-                println("\(output)")
+                print("\(output)")
             }
         } else {
-            println()
+            print("")
             break
         }
     }
@@ -523,13 +503,13 @@ func REPL(env: Environment) {
 // taken as a script to execute. If one exists, it is executed in lieu of
 // running the REPL.
 //
-func process_command_line(args:[String], env:Environment) -> Bool {
-    var argv = MalList()
+private func process_command_line(args: [String], _ env: Environment) -> Bool {
+    var argv = make_list()
     if args.count > 2 {
         let args1 = args[2..<args.count]
-        let args2 = args1.map { MalString(unescaped: $0) as MalVal }
+        let args2 = args1.map { make_string($0) }
         let args3 = [MalVal](args2)
-        argv = MalList(array: args3)
+        argv = make_list(args3)
     }
     env.set(kSymbolArgv, argv)
 
@@ -542,7 +522,7 @@ func process_command_line(args:[String], env:Environment) -> Bool {
 }
 
 func main() {
-    var env = Environment(outer: nil)
+    let env = Environment(outer: nil)
 
     load_history_file()
     load_builtins(env)
@@ -550,10 +530,10 @@ func main() {
     RE("(def! not (fn* (a) (if a false true)))", env)
     RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env)
 
-    env.set(kSymbolEval, MalBuiltin(function: {
-         unwrap($0) {
-            (ast:MalVal) -> MalVal in
-            EVAL(ast, env)
+    env.set(kSymbolEval, make_builtin({
+         try! unwrap_args($0) {
+            (ast: MalVal) -> MalVal in
+            try EVAL(ast, env)
          }
     }))
 
index 761d7f6..b01a467 100644 (file)
@@ -12,28 +12,28 @@ import Foundation
 // The number of times EVAL has been entered recursively. We keep track of this
 // so that we can protect against overrunning the stack.
 //
-var EVAL_level = 0
+private var EVAL_level = 0
 
 // The maximum number of times we let EVAL recurse before throwing an exception.
 // Testing puts this at some place between 1800 and 1900. Let's keep it at 500
 // for safety's sake.
 //
-let EVAL_leval_max = 500
+private let EVAL_leval_max = 500
 
 // Control whether or not tail-call optimization (TCO) is enabled. We want it
 // `true` most of the time, but may disable it for debugging purposes (it's
 // easier to get a meaningful backtrace that way).
 //
-let TCO = true
+private let TCO = true
 
 // Control whether or not we emit debugging statements in EVAL.
 //
-let DEBUG_EVAL = false
+private let DEBUG_EVAL = false
 
 // String used to prefix information logged in EVAL. Increasing lengths of the
 // string are used the more EVAL is recursed.
 //
-let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
+private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
@@ -47,75 +47,79 @@ let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
 
 // Holds the prefix of INDENT_TEMPLATE used for actual logging.
 //
-var indent = String()
+private var indent = String()
 
 // Symbols used in this module.
 //
-let kSymbolArgv             = MalSymbol(symbol: "*ARGV*")
-let kSymbolConcat           = MalSymbol(symbol: "concat")
-let kSymbolCons             = MalSymbol(symbol: "cons")
-let kSymbolDef              = MalSymbol(symbol: "def!")
-let kSymbolDefMacro         = MalSymbol(symbol: "defmacro!")
-let kSymbolDo               = MalSymbol(symbol: "do")
-let kSymbolEval             = MalSymbol(symbol: "eval")
-let kSymbolFn               = MalSymbol(symbol: "fn*")
-let kSymbolIf               = MalSymbol(symbol: "if")
-let kSymbolLet              = MalSymbol(symbol: "let*")
-let kSymbolMacroExpand      = MalSymbol(symbol: "macroexpand")
-let kSymbolQuasiQuote       = MalSymbol(symbol: "quasiquote")
-let kSymbolQuote            = MalSymbol(symbol: "quote")
-let kSymbolSpliceUnquote    = MalSymbol(symbol: "splice-unquote")
-let kSymbolUnquote          = MalSymbol(symbol: "unquote")
-
-// Class to help control the incrementing and decrementing of EVAL_level. We
-// create one of these on entry to EVAL, incrementing the level. When the
-// variable goes out of scope, the object is destroyed, decrementing the level.
-//
-class EVAL_Counter {
-    init() {
-        ++EVAL_level
-    }
-    deinit {
-        --EVAL_level
-    }
+private let kValArgv          = make_symbol("*ARGV*")
+private let kValConcat        = make_symbol("concat")
+private let kValCons          = make_symbol("cons")
+private let kValDef           = make_symbol("def!")
+private let kValDefMacro      = make_symbol("defmacro!")
+private let kValDo            = make_symbol("do")
+private let kValEval          = make_symbol("eval")
+private let kValFn            = make_symbol("fn*")
+private let kValIf            = make_symbol("if")
+private let kValLet           = make_symbol("let*")
+private let kValMacroExpand   = make_symbol("macroexpand")
+private let kValQuasiQuote    = make_symbol("quasiquote")
+private let kValQuote         = make_symbol("quote")
+private let kValSpliceUnquote = make_symbol("splice-unquote")
+private let kValUnquote       = make_symbol("unquote")
+private let kValTry           = make_symbol("try*")
+
+private let kSymbolArgv          = as_symbol(kValArgv)
+private let kSymbolConcat        = as_symbol(kValConcat)
+private let kSymbolCons          = as_symbol(kValCons)
+private let kSymbolDef           = as_symbol(kValDef)
+private let kSymbolDefMacro      = as_symbol(kValDefMacro)
+private let kSymbolDo            = as_symbol(kValDo)
+private let kSymbolEval          = as_symbol(kValEval)
+private let kSymbolFn            = as_symbol(kValFn)
+private let kSymbolIf            = as_symbol(kValIf)
+private let kSymbolLet           = as_symbol(kValLet)
+private let kSymbolMacroExpand   = as_symbol(kValMacroExpand)
+private let kSymbolQuasiQuote    = as_symbol(kValQuasiQuote)
+private let kSymbolQuote         = as_symbol(kValQuote)
+private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote)
+private let kSymbolUnquote       = as_symbol(kValUnquote)
+
+func substring(s: String, _ begin: Int, _ end: Int) -> String {
+    return s[s.startIndex.advancedBy(begin) ..< s.startIndex.advancedBy(end)]
 }
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> MalVal {
-    return read_str(str)
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
 }
 
 // Return whether or not `val` is a non-empty list.
 //
-func is_pair(val:MalVal) -> Bool {
-    if !is_sequence(val) { return false }
-    let list = val as! MalSequence
-    return !list.isEmpty
+private func is_pair(val: MalVal) -> Bool {
+    if let seq = as_sequenceQ(val) {
+        return !seq.isEmpty
+    }
+    return false
 }
 
 // Expand macros for as long as the expression looks like a macro invocation.
 //
-func macroexpand(var ast:MalVal, env:Environment) -> MalVal {
+private func macroexpand(var ast: MalVal, _ env: Environment) throws -> MalVal {
     while true {
-        if !is_list(ast) { break }
-        let ast_as_list = ast as! MalList
-        if ast_as_list.isEmpty { break }
-        let first = ast_as_list.first()
-        if !is_symbol(first) { break }
-        let macro_name = first as! MalSymbol
-        let obj = env.get(macro_name)
-        if obj == nil { break }
-        if !is_closure(obj!) { break }
-        let macro = obj! as! MalClosure
-        if !macro.is_macro { break }
-        var new_env = Environment(outer: macro.env)
-        let rest = ast_as_list.rest()
-        let res = new_env.set_bindings(macro.args, with_exprs:rest)
-        if is_error(res) { return res }
-        ast = EVAL(macro.body, new_env)
+        if  let ast_as_list = as_listQ(ast) where !ast_as_list.isEmpty,
+            let macro_name = as_symbolQ(ast_as_list.first()),
+            let obj = env.get(macro_name),
+            let macro = as_macroQ(obj)
+        {
+            let new_env = Environment(outer: macro.env)
+            let rest = as_sequence(ast_as_list.rest())
+            let _ = try new_env.set_bindings(macro.args, with_exprs: rest)
+            ast = try EVAL(macro.body, new_env)
+            continue
+        }
+        return ast
     }
-    return ast
 }
 
 // Evaluate `quasiquote`, possibly recursing in the process.
@@ -154,15 +158,15 @@ func macroexpand(var ast:MalVal, env:Environment) -> MalVal {
 // list) with the remaining items of the list containing that splice-quote
 // expression. However, it's not clear to me why the handling of "unquote" is
 // not handled similarly, for consistency's sake.
-
-func quasiquote(qq_arg:MalVal) -> MalVal {
+//
+private func quasiquote(qq_arg: MalVal) throws -> MalVal {
 
     // If the argument is an atom or empty list:
     //
     // Return: (quote <argument>)
 
     if !is_pair(qq_arg) {
-        return MalList(objects: kSymbolQuote, qq_arg)
+        return make_list_from(kValQuote, qq_arg)
     }
 
     // The argument is a non-empty list -- that is (item rest...)
@@ -172,12 +176,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //
     // Return: item
 
-    let qq_list = qq_arg as! MalSequence
-    if is_symbol(qq_list.first()) {
-        let sym = qq_list.first() as! MalSymbol
-        if sym == kSymbolUnquote {
-            return qq_list.count >= 2 ? qq_list[1] : MalNil()
-        }
+    let qq_list = as_sequence(qq_arg)
+    if let sym = as_symbolQ(qq_list.first()) where sym == kSymbolUnquote {
+        return qq_list.count >= 2 ? try! qq_list.nth(1) : make_nil()
     }
 
     // If the first item from the list is itself a non-empty list starting with
@@ -186,14 +187,10 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     // Return: (concat item quasiquote(rest...))
 
     if is_pair(qq_list.first()) {
-        let qq_list_item0 = qq_list.first() as! MalSequence
-        if is_symbol(qq_list_item0.first()) {
-            let sym = qq_list_item0.first() as! MalSymbol
-            if sym == kSymbolSpliceUnquote {
-                let result = quasiquote(qq_list.rest())
-                if is_error(result) { return result }
-                return MalList(array: [kSymbolConcat, qq_list_item0[1], result])
-            }
+        let qq_list_item0 = as_sequence(qq_list.first())
+        if let sym = as_symbolQ(qq_list_item0.first()) where sym == kSymbolSpliceUnquote {
+            let result = try quasiquote(qq_list.rest())
+            return make_list_from(kValConcat, try! qq_list_item0.nth(1), result)
         }
     }
 
@@ -201,13 +198,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //
     // Return: (cons (quasiquote item) (quasiquote (rest...))
 
-    let first = quasiquote(qq_list.first())
-    if is_error(first) { return first }
-
-    let rest = quasiquote(qq_list.rest())
-    if is_error(rest) { return rest }
-
-    return MalList(objects: kSymbolCons, first, rest)
+    let first = try quasiquote(qq_list.first())
+    let rest = try quasiquote(qq_list.rest())
+    return make_list_from(kValCons, first, rest)
 }
 
 // Perform a simple evaluation of the `ast` object. If it's a symbol,
@@ -215,51 +208,45 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
 // elements (or just the values, in the case of the hashmap). Otherwise, return
 // the object unchanged.
 //
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
+    }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
+    }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
     }
+    return ast
 }
 
-enum TCOVal {
+private enum TCOVal {
     case NoResult
     case Return(MalVal)
     case Continue(MalVal, Environment)
@@ -267,204 +254,192 @@ enum TCOVal {
     init() { self = .NoResult }
     init(_ result: MalVal) { self = .Return(result) }
     init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) }
-    init(_ e: String) { self = .Return(MalError(message: e)) }
 }
 
 // EVALuate "def!" and "defmacro!".
 //
-func eval_def(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to def!, got \(list.count - 1)")
-    }
-    let arg0 = list[0] as! MalSymbol
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_symbol(arg1) {
-        return TCOVal("expected symbol for first argument to def!")
-    }
-    let sym = arg1 as! MalSymbol
-    let value = EVAL(arg2, env)
-    if is_error(value) { return TCOVal(value) }
-    if arg0 == kSymbolDefMacro {
-        if is_closure(value) {
-            let as_closure = value as! MalClosure
-            as_closure.is_macro = true
-        } else {
-            return TCOVal("expected closure, got \(value)")
+private func eval_def(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")
+    }
+    let arg0 = try! list.nth(0)
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let sym = as_symbolQ(arg1) else {
+        try throw_error("expected symbol for first argument to def!")
+    }
+    var value = try EVAL(arg2, env)
+    if as_symbol(arg0) == kSymbolDefMacro {
+        guard let closure = as_closureQ(value) else {
+            try throw_error("expected closure, got \(value)")
         }
+        value = make_macro(closure)
     }
     return TCOVal(env.set(sym, value))
 }
 
 // EVALuate "let*".
 //
-func eval_let(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to let*, got \(list.count - 1)")
+private func eval_let(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_sequence(arg1) {
-        return TCOVal("expected list for first argument to let*")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let bindings = as_sequenceQ(arg1) else {
+        try throw_error("expected list for first argument to let*")
     }
-    let bindings = arg1 as! MalSequence
-    if bindings.count % 2 == 1 {
-        return TCOVal("expected even number of elements in bindings to let*, got \(bindings.count)")
+    guard bindings.count % 2 == 0 else {
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")
     }
-    var new_env = Environment(outer: env)
-    for var index = 0; index < bindings.count; index += 2 {
-        let binding_name = bindings[index]
-        let binding_value = bindings[index + 1]
-
-        if !is_symbol(binding_name) {
-            return TCOVal("expected symbol for first element in binding pair")
+    let new_env = Environment(outer: env)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {
+        let binding_name = try! bindings.nth(index)
+        let binding_value = try! bindings.nth(index + 1)
+        guard let binding_symbol = as_symbolQ(binding_name) else {
+            try throw_error("expected symbol for first element in binding pair")
         }
-        let binding_symbol = binding_name as! MalSymbol
-        let evaluated_value = EVAL(binding_value, new_env)
-        if is_error(evaluated_value) { return TCOVal(evaluated_value) }
+        let evaluated_value = try EVAL(binding_value, new_env)
         new_env.set(binding_symbol, evaluated_value)
     }
     if TCO {
         return TCOVal(arg2, new_env)
     }
-    return TCOVal(EVAL(arg2, new_env))
+    return TCOVal(try EVAL(arg2, new_env))
 }
 
 // EVALuate "do".
 //
-func eval_do(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if TCO {
-        let eval = eval_ast(MalList(slice: list[1..<list.count-1]), env)
-        if is_error(eval) { return TCOVal(eval) }
+        let _ = try eval_ast(list.range_from(1, to: list.count-1), env)
         return TCOVal(list.last(), env)
     }
 
-    let evaluated_ast = eval_ast(list.rest(), env)
-    if is_error(evaluated_ast) { return TCOVal(evaluated_ast) }
-    let evaluated_seq = evaluated_ast as! MalSequence
+    let evaluated_ast = try eval_ast(list.rest(), env)
+    let evaluated_seq = as_sequence(evaluated_ast)
     return TCOVal(evaluated_seq.last())
 }
 
 // EVALuate "if".
 //
-func eval_if(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count < 3 {
-        return TCOVal("expected at least 2 arguments to if, got \(list.count - 1)")
+private func eval_if(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 3 else {
+        try throw_error("expected at least 2 arguments to if, got \(list.count - 1)")
     }
-    let cond_result = EVAL(list[1], env)
-    var new_ast = MalVal()
+    let cond_result = try EVAL(try! list.nth(1), env)
+    var new_ast: MalVal
     if is_truthy(cond_result) {
-        new_ast = list[2]
+        new_ast = try! list.nth(2)
     } else if list.count == 4 {
-        new_ast = list[3]
+        new_ast = try! list.nth(3)
     } else {
-        return TCOVal(MalNil())
+        return TCOVal(make_nil())
     }
     if TCO {
         return TCOVal(new_ast, env)
     }
-    return TCOVal(EVAL(new_ast, env))
+    return TCOVal(try EVAL(new_ast, env))
 }
 
 // EVALuate "fn*".
 //
-func eval_fn(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to fn*, got \(list.count - 1)")
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to fn*, got \(list.count - 1)")
     }
-    if !is_sequence(list[1]) {
-        return TCOVal("expected list or vector for first argument to fn*")
+    guard let seq = as_sequenceQ(try! list.nth(1)) else {
+        try throw_error("expected list or vector for first argument to fn*")
     }
-    return TCOVal(MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env))
+    return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env)))
 }
 
 // EVALuate "quote".
 //
-func eval_quote(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if list.count >= 2 {
-        return TCOVal(list[1])
+        return TCOVal(try! list.nth(1))
     }
-    return TCOVal(MalNil())
+    return TCOVal(make_nil())
 }
 
 // EVALuate "quasiquote".
 //
-func eval_quasiquote(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count >= 2 {
-        if TCO {
-            return TCOVal(quasiquote(list[1]), env)
-        }
-        return TCOVal(EVAL(quasiquote(list[1]), env))
+private func eval_quasiquote(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 2 else {
+        try throw_error("Expected non-nil parameter to 'quasiquote'")
+    }
+    if TCO {
+        return TCOVal(try quasiquote(try! list.nth(1)), env)
     }
-    return TCOVal("Expected non-nil parameter to 'quasiquote'")
+    return TCOVal(try EVAL(try quasiquote(try! list.nth(1)), env))
 }
 
 // EVALuate "macroexpand".
 //
-func eval_macroexpand(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count >= 2 {
-        return TCOVal(macroexpand(list[1], env))
+private func eval_macroexpand(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 2 else {
+        try throw_error("Expected parameter to 'macroexpand'")
     }
-    return TCOVal("Expected parameter to 'macroexpand'")
+    return TCOVal(try macroexpand(try! list.nth(1), env))
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-    let x = EVAL_Counter()
-    if EVAL_level > EVAL_leval_max {
-        return MalError(message: "Recursing too many levels (> \(EVAL_leval_max))")
+private func EVAL(var ast: MalVal, var _ env: Environment) throws -> MalVal {
+    EVAL_level++
+    defer { EVAL_level-- }
+    guard EVAL_level <= EVAL_leval_max else {
+        try throw_error("Recursing too many levels (> \(EVAL_leval_max))")
     }
 
     if DEBUG_EVAL {
-        indent = prefix(INDENT_TEMPLATE, EVAL_level)
+        indent = substring(INDENT_TEMPLATE, 0, EVAL_level)
     }
 
     while true {
-        if is_error(ast) { return ast }
-        if DEBUG_EVAL { println("\(indent)>   \(ast)") }
+        if DEBUG_EVAL { print("\(indent)>   \(ast)") }
 
         if !is_list(ast) {
 
             // Not a list -- just evaluate and return.
 
-            let answer = eval_ast(ast, env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try eval_ast(ast, env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // Special handling if it's a list.
 
-        var list = ast as! MalList
-        ast = macroexpand(ast, env)
+        var list = as_list(ast)
+        ast = try macroexpand(ast, env)
         if !is_list(ast) { return ast }
-        list = ast as! MalList
+        list = as_list(ast)
 
-        if DEBUG_EVAL { println("\(indent)>.  \(list)") }
+        if DEBUG_EVAL { print("\(indent)>.  \(list)") }
 
         if list.isEmpty {
-            return list
+            return ast
         }
 
         // Check for special forms, where we want to check the operation
         // before evaluating all of the parameters.
 
         let arg0 = list.first()
-        if is_symbol(arg0) {
-            var res: TCOVal
-            let fn_symbol = arg0 as! MalSymbol
+        if let fn_symbol = as_symbolQ(arg0) {
+            let res: TCOVal
 
             switch fn_symbol {
-                case kSymbolDef:            res = eval_def(list, env)
-                case kSymbolDefMacro:       res = eval_def(list, env)
-                case kSymbolLet:            res = eval_let(list, env)
-                case kSymbolDo:             res = eval_do(list, env)
-                case kSymbolIf:             res = eval_if(list, env)
-                case kSymbolFn:             res = eval_fn(list, env)
-                case kSymbolQuote:          res = eval_quote(list, env)
-                case kSymbolQuasiQuote:     res = eval_quasiquote(list, env)
-                case kSymbolMacroExpand:    res = eval_macroexpand(list, env)
+                case kSymbolDef:            res = try eval_def(list, env)
+                case kSymbolDefMacro:       res = try eval_def(list, env)
+                case kSymbolLet:            res = try eval_let(list, env)
+                case kSymbolDo:             res = try eval_do(list, env)
+                case kSymbolIf:             res = try eval_if(list, env)
+                case kSymbolFn:             res = try eval_fn(list, env)
+                case kSymbolQuote:          res = try eval_quote(list, env)
+                case kSymbolQuasiQuote:     res = try eval_quasiquote(list, env)
+                case kSymbolMacroExpand:    res = try eval_macroexpand(list, env)
                 default:                    res = TCOVal()
             }
             switch res {
@@ -476,78 +451,78 @@ func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
 
         // Standard list to be applied. Evaluate all the elements first.
 
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
+        let eval = try eval_ast(ast, env)
 
         // The result had better be a list and better be non-empty.
 
-        let eval_list = eval as! MalList
+        let eval_list = as_list(eval)
         if eval_list.isEmpty {
-            return eval_list
+            return eval
         }
 
-        if DEBUG_EVAL { println("\(indent)>>  \(eval)") }
+        if DEBUG_EVAL { print("\(indent)>>  \(eval)") }
 
         // Get the first element of the list and execute it.
 
         let first = eval_list.first()
-        let rest = eval_list.rest()
+        let rest = as_sequence(eval_list.rest())
 
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
-        } else if is_closure(first) {
-            let fn = first as! MalClosure
-            var new_env = Environment(outer: fn.env)
-            let result = new_env.set_bindings(fn.args, with_exprs:rest)
-            if is_error(result) { return result }
+        } else if let fn = as_closureQ(first) {
+            let new_env = Environment(outer: fn.env)
+            let _ = try new_env.set_bindings(fn.args, with_exprs: rest)
             if TCO {
                 env = new_env
                 ast = fn.body
                 continue
             }
-            let answer = EVAL(fn.body, new_env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try EVAL(fn.body, new_env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // The first element wasn't a function to be executed. Return an
         // error saying so.
 
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
+        try throw_error("first list item does not evaluate to a function: \(first)")
     }
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
+private func PRINT(exp: MalVal) -> String {
     return pr_str(exp, true)
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
     }
-    return exp
+    return nil
 }
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String, env: Environment) -> String? {
+private func REP(text: String, _ env: Environment) -> String? {
     let exp = RE(text, env)
     if exp == nil { return nil }
     return PRINT(exp!)
@@ -555,14 +530,14 @@ func REP(text: String, env: Environment) -> String? {
 
 // Perform the full REPL.
 //
-func REPL(env: Environment) {
+private func REPL(env: Environment) {
     while true {
         if let text = _readline("user> ") {
             if let output = REP(text, env) {
-                println("\(output)")
+                print("\(output)")
             }
         } else {
-            println()
+            print("")
             break
         }
     }
@@ -573,13 +548,13 @@ func REPL(env: Environment) {
 // taken as a script to execute. If one exists, it is executed in lieu of
 // running the REPL.
 //
-func process_command_line(args:[String], env:Environment) -> Bool {
-    var argv = MalList()
+private func process_command_line(args: [String], _ env: Environment) -> Bool {
+    var argv = make_list()
     if args.count > 2 {
         let args1 = args[2..<args.count]
-        let args2 = args1.map { MalString(unescaped: $0) as MalVal }
+        let args2 = args1.map { make_string($0) }
         let args3 = [MalVal](args2)
-        argv = MalList(array: args3)
+        argv = make_list(args3)
     }
     env.set(kSymbolArgv, argv)
 
@@ -592,7 +567,7 @@ func process_command_line(args:[String], env:Environment) -> Bool {
 }
 
 func main() {
-    var env = Environment(outer: nil)
+    let env = Environment(outer: nil)
 
     load_history_file()
     load_builtins(env)
@@ -604,10 +579,10 @@ func main() {
     RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) " +
        "`(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", env)
 
-    env.set(kSymbolEval, MalBuiltin(function: {
-         unwrap($0) {
-            (ast:MalVal) -> MalVal in
-            EVAL(ast, env)
+    env.set(kSymbolEval, make_builtin({
+         try! unwrap_args($0) {
+            (ast: MalVal) -> MalVal in
+            try EVAL(ast, env)
          }
     }))
 
index ad61e37..d141242 100644 (file)
@@ -12,28 +12,28 @@ import Foundation
 // The number of times EVAL has been entered recursively. We keep track of this
 // so that we can protect against overrunning the stack.
 //
-var EVAL_level = 0
+private var EVAL_level = 0
 
 // The maximum number of times we let EVAL recurse before throwing an exception.
 // Testing puts this at some place between 1800 and 1900. Let's keep it at 500
 // for safety's sake.
 //
-let EVAL_leval_max = 500
+private let EVAL_leval_max = 500
 
 // Control whether or not tail-call optimization (TCO) is enabled. We want it
 // `true` most of the time, but may disable it for debugging purposes (it's
 // easier to get a meaningful backtrace that way).
 //
-let TCO = true
+private let TCO = true
 
 // Control whether or not we emit debugging statements in EVAL.
 //
-let DEBUG_EVAL = false
+private let DEBUG_EVAL = false
 
 // String used to prefix information logged in EVAL. Increasing lengths of the
 // string are used the more EVAL is recursed.
 //
-let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
+private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
@@ -47,77 +47,82 @@ let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
 
 // Holds the prefix of INDENT_TEMPLATE used for actual logging.
 //
-var indent = String()
+private var indent = String()
 
 // Symbols used in this module.
 //
-let kSymbolArgv             = MalSymbol(symbol: "*ARGV*")
-let kSymbolCatch            = MalSymbol(symbol: "catch*")
-let kSymbolConcat           = MalSymbol(symbol: "concat")
-let kSymbolCons             = MalSymbol(symbol: "cons")
-let kSymbolDef              = MalSymbol(symbol: "def!")
-let kSymbolDefMacro         = MalSymbol(symbol: "defmacro!")
-let kSymbolDo               = MalSymbol(symbol: "do")
-let kSymbolEval             = MalSymbol(symbol: "eval")
-let kSymbolFn               = MalSymbol(symbol: "fn*")
-let kSymbolIf               = MalSymbol(symbol: "if")
-let kSymbolLet              = MalSymbol(symbol: "let*")
-let kSymbolMacroExpand      = MalSymbol(symbol: "macroexpand")
-let kSymbolQuasiQuote       = MalSymbol(symbol: "quasiquote")
-let kSymbolQuote            = MalSymbol(symbol: "quote")
-let kSymbolSpliceUnquote    = MalSymbol(symbol: "splice-unquote")
-let kSymbolUnquote          = MalSymbol(symbol: "unquote")
-let kSymbolTry              = MalSymbol(symbol: "try*")
-
-// Class to help control the incrementing and decrementing of EVAL_level. We
-// create one of these on entry to EVAL, incrementing the level. When the
-// variable goes out of scope, the object is destroyed, decrementing the level.
-//
-class EVAL_Counter {
-    init() {
-        ++EVAL_level
-    }
-    deinit {
-        --EVAL_level
-    }
+private let kValArgv          = make_symbol("*ARGV*")
+private let kValCatch         = make_symbol("catch*")
+private let kValConcat        = make_symbol("concat")
+private let kValCons          = make_symbol("cons")
+private let kValDef           = make_symbol("def!")
+private let kValDefMacro      = make_symbol("defmacro!")
+private let kValDo            = make_symbol("do")
+private let kValEval          = make_symbol("eval")
+private let kValFn            = make_symbol("fn*")
+private let kValIf            = make_symbol("if")
+private let kValLet           = make_symbol("let*")
+private let kValMacroExpand   = make_symbol("macroexpand")
+private let kValQuasiQuote    = make_symbol("quasiquote")
+private let kValQuote         = make_symbol("quote")
+private let kValSpliceUnquote = make_symbol("splice-unquote")
+private let kValUnquote       = make_symbol("unquote")
+private let kValTry           = make_symbol("try*")
+
+private let kSymbolArgv          = as_symbol(kValArgv)
+private let kSymbolCatch         = as_symbol(kValCatch)
+private let kSymbolConcat        = as_symbol(kValConcat)
+private let kSymbolCons          = as_symbol(kValCons)
+private let kSymbolDef           = as_symbol(kValDef)
+private let kSymbolDefMacro      = as_symbol(kValDefMacro)
+private let kSymbolDo            = as_symbol(kValDo)
+private let kSymbolEval          = as_symbol(kValEval)
+private let kSymbolFn            = as_symbol(kValFn)
+private let kSymbolIf            = as_symbol(kValIf)
+private let kSymbolLet           = as_symbol(kValLet)
+private let kSymbolMacroExpand   = as_symbol(kValMacroExpand)
+private let kSymbolQuasiQuote    = as_symbol(kValQuasiQuote)
+private let kSymbolQuote         = as_symbol(kValQuote)
+private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote)
+private let kSymbolUnquote       = as_symbol(kValUnquote)
+private let kSymbolTry           = as_symbol(kValTry)
+
+func substring(s: String, _ begin: Int, _ end: Int) -> String {
+    return s[s.startIndex.advancedBy(begin) ..< s.startIndex.advancedBy(end)]
 }
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> MalVal {
-    return read_str(str)
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
 }
 
 // Return whether or not `val` is a non-empty list.
 //
-func is_pair(val:MalVal) -> Bool {
-    if !is_sequence(val) { return false }
-    let list = val as! MalSequence
-    return !list.isEmpty
+private func is_pair(val: MalVal) -> Bool {
+    if let seq = as_sequenceQ(val) {
+        return !seq.isEmpty
+    }
+    return false
 }
 
 // Expand macros for as long as the expression looks like a macro invocation.
 //
-func macroexpand(var ast:MalVal, env:Environment) -> MalVal {
+private func macroexpand(var ast: MalVal, _ env: Environment) throws -> MalVal {
     while true {
-        if !is_list(ast) { break }
-        let ast_as_list = ast as! MalList
-        if ast_as_list.isEmpty { break }
-        let first = ast_as_list.first()
-        if !is_symbol(first) { break }
-        let macro_name = first as! MalSymbol
-        let obj = env.get(macro_name)
-        if obj == nil { break }
-        if !is_closure(obj!) { break }
-        let macro = obj! as! MalClosure
-        if !macro.is_macro { break }
-        var new_env = Environment(outer: macro.env)
-        let rest = ast_as_list.rest()
-        let res = new_env.set_bindings(macro.args, with_exprs:rest)
-        if is_error(res) { return res }
-        ast = EVAL(macro.body, new_env)
+        if  let ast_as_list = as_listQ(ast) where !ast_as_list.isEmpty,
+            let macro_name = as_symbolQ(ast_as_list.first()),
+            let obj = env.get(macro_name),
+            let macro = as_macroQ(obj)
+        {
+            let new_env = Environment(outer: macro.env)
+            let rest = as_sequence(ast_as_list.rest())
+            let _ = try new_env.set_bindings(macro.args, with_exprs: rest)
+            ast = try EVAL(macro.body, new_env)
+            continue
+        }
+        return ast
     }
-    return ast
 }
 
 // Evaluate `quasiquote`, possibly recursing in the process.
@@ -156,15 +161,15 @@ func macroexpand(var ast:MalVal, env:Environment) -> MalVal {
 // list) with the remaining items of the list containing that splice-quote
 // expression. However, it's not clear to me why the handling of "unquote" is
 // not handled similarly, for consistency's sake.
-
-func quasiquote(qq_arg:MalVal) -> MalVal {
+//
+private func quasiquote(qq_arg: MalVal) throws -> MalVal {
 
     // If the argument is an atom or empty list:
     //
     // Return: (quote <argument>)
 
     if !is_pair(qq_arg) {
-        return MalList(objects: kSymbolQuote, qq_arg)
+        return make_list_from(kValQuote, qq_arg)
     }
 
     // The argument is a non-empty list -- that is (item rest...)
@@ -174,12 +179,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //
     // Return: item
 
-    let qq_list = qq_arg as! MalSequence
-    if is_symbol(qq_list.first()) {
-        let sym = qq_list.first() as! MalSymbol
-        if sym == kSymbolUnquote {
-            return qq_list.count >= 2 ? qq_list[1] : MalNil()
-        }
+    let qq_list = as_sequence(qq_arg)
+    if let sym = as_symbolQ(qq_list.first()) where sym == kSymbolUnquote {
+        return qq_list.count >= 2 ? try! qq_list.nth(1) : make_nil()
     }
 
     // If the first item from the list is itself a non-empty list starting with
@@ -188,14 +190,10 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     // Return: (concat item quasiquote(rest...))
 
     if is_pair(qq_list.first()) {
-        let qq_list_item0 = qq_list.first() as! MalSequence
-        if is_symbol(qq_list_item0.first()) {
-            let sym = qq_list_item0.first() as! MalSymbol
-            if sym == kSymbolSpliceUnquote {
-                let result = quasiquote(qq_list.rest())
-                if is_error(result) { return result }
-                return MalList(array: [kSymbolConcat, qq_list_item0[1], result])
-            }
+        let qq_list_item0 = as_sequence(qq_list.first())
+        if let sym = as_symbolQ(qq_list_item0.first()) where sym == kSymbolSpliceUnquote {
+            let result = try quasiquote(qq_list.rest())
+            return make_list_from(kValConcat, try! qq_list_item0.nth(1), result)
         }
     }
 
@@ -203,13 +201,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //
     // Return: (cons (quasiquote item) (quasiquote (rest...))
 
-    let first = quasiquote(qq_list.first())
-    if is_error(first) { return first }
-
-    let rest = quasiquote(qq_list.rest())
-    if is_error(rest) { return rest }
-
-    return MalList(objects: kSymbolCons, first, rest)
+    let first = try quasiquote(qq_list.first())
+    let rest = try quasiquote(qq_list.rest())
+    return make_list_from(kValCons, first, rest)
 }
 
 // Perform a simple evaluation of the `ast` object. If it's a symbol,
@@ -217,51 +211,45 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
 // elements (or just the values, in the case of the hashmap). Otherwise, return
 // the object unchanged.
 //
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
+    }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
+    }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
     }
+    return ast
 }
 
-enum TCOVal {
+private enum TCOVal {
     case NoResult
     case Return(MalVal)
     case Continue(MalVal, Environment)
@@ -269,252 +257,222 @@ enum TCOVal {
     init() { self = .NoResult }
     init(_ result: MalVal) { self = .Return(result) }
     init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) }
-    init(_ e: String) { self = .Return(MalError(message: e)) }
 }
 
 // EVALuate "def!" and "defmacro!".
 //
-func eval_def(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to def!, got \(list.count - 1)")
-    }
-    let arg0 = list[0] as! MalSymbol
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_symbol(arg1) {
-        return TCOVal("expected symbol for first argument to def!")
-    }
-    let sym = arg1 as! MalSymbol
-    let value = EVAL(arg2, env)
-    if is_error(value) { return TCOVal(value) }
-    if arg0 == kSymbolDefMacro {
-        if is_closure(value) {
-            let as_closure = value as! MalClosure
-            as_closure.is_macro = true
-        } else {
-            return TCOVal("expected closure, got \(value)")
+private func eval_def(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")
+    }
+    let arg0 = try! list.nth(0)
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let sym = as_symbolQ(arg1) else {
+        try throw_error("expected symbol for first argument to def!")
+    }
+    var value = try EVAL(arg2, env)
+    if as_symbol(arg0) == kSymbolDefMacro {
+        guard let closure = as_closureQ(value) else {
+            try throw_error("expected closure, got \(value)")
         }
+        value = make_macro(closure)
     }
     return TCOVal(env.set(sym, value))
 }
 
 // EVALuate "let*".
 //
-func eval_let(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to let*, got \(list.count - 1)")
+private func eval_let(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_sequence(arg1) {
-        return TCOVal("expected list for first argument to let*")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let bindings = as_sequenceQ(arg1) else {
+        try throw_error("expected list for first argument to let*")
     }
-    let bindings = arg1 as! MalSequence
-    if bindings.count % 2 == 1 {
-        return TCOVal("expected even number of elements in bindings to let*, got \(bindings.count)")
+    guard bindings.count % 2 == 0 else {
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")
     }
-    var new_env = Environment(outer: env)
-    for var index = 0; index < bindings.count; index += 2 {
-        let binding_name = bindings[index]
-        let binding_value = bindings[index + 1]
-
-        if !is_symbol(binding_name) {
-            return TCOVal("expected symbol for first element in binding pair")
+    let new_env = Environment(outer: env)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {
+        let binding_name = try! bindings.nth(index)
+        let binding_value = try! bindings.nth(index + 1)
+        guard let binding_symbol = as_symbolQ(binding_name) else {
+            try throw_error("expected symbol for first element in binding pair")
         }
-        let binding_symbol = binding_name as! MalSymbol
-        let evaluated_value = EVAL(binding_value, new_env)
-        if is_error(evaluated_value) { return TCOVal(evaluated_value) }
+        let evaluated_value = try EVAL(binding_value, new_env)
         new_env.set(binding_symbol, evaluated_value)
     }
     if TCO {
         return TCOVal(arg2, new_env)
     }
-    return TCOVal(EVAL(arg2, new_env))
+    return TCOVal(try EVAL(arg2, new_env))
 }
 
 // EVALuate "do".
 //
-func eval_do(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if TCO {
-        let eval = eval_ast(MalList(slice: list[1..<list.count-1]), env)
-        if is_error(eval) { return TCOVal(eval) }
+        let _ = try eval_ast(list.range_from(1, to: list.count-1), env)
         return TCOVal(list.last(), env)
     }
 
-    let evaluated_ast = eval_ast(list.rest(), env)
-    if is_error(evaluated_ast) { return TCOVal(evaluated_ast) }
-    let evaluated_seq = evaluated_ast as! MalSequence
+    let evaluated_ast = try eval_ast(list.rest(), env)
+    let evaluated_seq = as_sequence(evaluated_ast)
     return TCOVal(evaluated_seq.last())
 }
 
 // EVALuate "if".
 //
-func eval_if(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count < 3 {
-        return TCOVal("expected at least 2 arguments to if, got \(list.count - 1)")
+private func eval_if(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 3 else {
+        try throw_error("expected at least 2 arguments to if, got \(list.count - 1)")
     }
-    let cond_result = EVAL(list[1], env)
-    var new_ast = MalVal()
+    let cond_result = try EVAL(try! list.nth(1), env)
+    var new_ast: MalVal
     if is_truthy(cond_result) {
-        new_ast = list[2]
+        new_ast = try! list.nth(2)
     } else if list.count == 4 {
-        new_ast = list[3]
+        new_ast = try! list.nth(3)
     } else {
-        return TCOVal(MalNil())
+        return TCOVal(make_nil())
     }
     if TCO {
         return TCOVal(new_ast, env)
     }
-    return TCOVal(EVAL(new_ast, env))
+    return TCOVal(try EVAL(new_ast, env))
 }
 
 // EVALuate "fn*".
 //
-func eval_fn(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to fn*, got \(list.count - 1)")
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to fn*, got \(list.count - 1)")
     }
-    if !is_sequence(list[1]) {
-        return TCOVal("expected list or vector for first argument to fn*")
+    guard let seq = as_sequenceQ(try! list.nth(1)) else {
+        try throw_error("expected list or vector for first argument to fn*")
     }
-    return TCOVal(MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env))
+    return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env)))
 }
 
 // EVALuate "quote".
 //
-func eval_quote(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if list.count >= 2 {
-        return TCOVal(list[1])
+        return TCOVal(try! list.nth(1))
     }
-    return TCOVal(MalNil())
+    return TCOVal(make_nil())
 }
 
 // EVALuate "quasiquote".
 //
-func eval_quasiquote(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count >= 2 {
-        if TCO {
-            return TCOVal(quasiquote(list[1]), env)
-        }
-        return TCOVal(EVAL(quasiquote(list[1]), env))
+private func eval_quasiquote(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 2 else {
+        try throw_error("Expected non-nil parameter to 'quasiquote'")
+    }
+    if TCO {
+        return TCOVal(try quasiquote(try! list.nth(1)), env)
     }
-    return TCOVal("Expected non-nil parameter to 'quasiquote'")
+    return TCOVal(try EVAL(try quasiquote(try! list.nth(1)), env))
 }
 
 // EVALuate "macroexpand".
 //
-func eval_macroexpand(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count >= 2 {
-        return TCOVal(macroexpand(list[1], env))
+private func eval_macroexpand(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 2 else {
+        try throw_error("Expected parameter to 'macroexpand'")
     }
-    return TCOVal("Expected parameter to 'macroexpand'")
+    return TCOVal(try macroexpand(try! list.nth(1), env))
 }
 
 // EVALuate "try*" (and "catch*").
 //
-func eval_try(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_try(list: MalSequence, _ env: Environment) throws -> TCOVal {
     // This is a subset of the Clojure try/catch:
     //
     //      (try* expr (catch exception-name expr))
 
-    if list.count < 2 {
-        println("try*: no body parameter")
-        return TCOVal(MalNil()) // No body parameter
-    }
-    let res = EVAL(list[1], env)
-    if !is_error(res) { return TCOVal(res) }
-    if list.count < 3 {
-        println("try*: no catch parameter")
-        return TCOVal(MalNil()) // No catch parameter
-    }
-    if !is_sequence(list[2]) {
-        println("try*: second parameter to 'try' is not a sequence")
-        return TCOVal(MalNil()) // Second parameter to 'try' is not a sequence
-    }
-    let catch_list = list[2] as! MalSequence
-    if catch_list.count < 3 {
-        println("try*: not enough catch parameters")
-        return TCOVal(MalNil()) // Not enough catch parameters
-    }
-    if !is_symbol(catch_list[0]) {
-        println("try*: first parameter in catch list is not a symbol")
-        return TCOVal(MalNil()) // First parameter in catch list is not a symbol
-    }
-    let catch_symbol = catch_list[0] as! MalSymbol
-    if catch_symbol != kSymbolCatch {
-        println("try*: first parameter in catch list is not 'catch'")
-        return TCOVal(MalNil()) // First parameter in catch list is not 'catch'
-    }
-    if !is_symbol(catch_list[1]) {
-        println("try*: first parameter to 'catch' is not a symbol")
-        return TCOVal(MalNil()) // First parameter to 'catch' is not a symbol
-    }
-    let catch_name = catch_list[1] as! MalSymbol
-    let catch_expr = catch_list[2]
-    let catch_env = Environment(outer: env)
-    let error = res as! MalError
-    catch_env.set_bindings(MalList(objects: catch_name), with_exprs: MalList(objects: error.value))
-    return TCOVal(EVAL(catch_expr, catch_env))
+    guard list.count >= 2 else {
+        try throw_error("try*: no body parameter")
+    }
+
+    do {
+        return TCOVal(try EVAL(try! list.nth(1), env))
+    } catch let error as MalException {
+        guard list.count >= 3,
+            let catch_list = as_sequenceQ(try! list.nth(2)) where catch_list.count >= 3,
+            let _ = as_symbolQ(try! catch_list.nth(0)) else
+        {
+            throw error // No catch parameter
+        }
+        let catch_name = try! catch_list.nth(1)
+        let catch_expr = try! catch_list.nth(2)
+        let catch_env = Environment(outer: env)
+        try catch_env.set_bindings(as_sequence(make_list_from(catch_name)),
+                with_exprs: as_sequence(make_list_from(error.exception)))
+        return TCOVal(try EVAL(catch_expr, catch_env))
+    }
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-    let x = EVAL_Counter()
-    if EVAL_level > EVAL_leval_max {
-        return MalError(message: "Recursing too many levels (> \(EVAL_leval_max))")
+private func EVAL(var ast: MalVal, var _ env: Environment) throws -> MalVal {
+    EVAL_level++
+    defer { EVAL_level-- }
+    guard EVAL_level <= EVAL_leval_max else {
+        try throw_error("Recursing too many levels (> \(EVAL_leval_max))")
     }
 
     if DEBUG_EVAL {
-        indent = prefix(INDENT_TEMPLATE, EVAL_level)
+        indent = substring(INDENT_TEMPLATE, 0, EVAL_level)
     }
 
     while true {
-        if is_error(ast) { return ast }
-        if DEBUG_EVAL { println("\(indent)>   \(ast)") }
+        if DEBUG_EVAL { print("\(indent)>   \(ast)") }
 
         if !is_list(ast) {
 
             // Not a list -- just evaluate and return.
 
-            let answer = eval_ast(ast, env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try eval_ast(ast, env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // Special handling if it's a list.
 
-        var list = ast as! MalList
-        ast = macroexpand(ast, env)
+        var list = as_list(ast)
+        ast = try macroexpand(ast, env)
         if !is_list(ast) { return ast }
-        list = ast as! MalList
+        list = as_list(ast)
 
-        if DEBUG_EVAL { println("\(indent)>.  \(list)") }
+        if DEBUG_EVAL { print("\(indent)>.  \(list)") }
 
         if list.isEmpty {
-            return list
+            return ast
         }
 
         // Check for special forms, where we want to check the operation
         // before evaluating all of the parameters.
 
         let arg0 = list.first()
-        if is_symbol(arg0) {
-            var res: TCOVal
-            let fn_symbol = arg0 as! MalSymbol
+        if let fn_symbol = as_symbolQ(arg0) {
+            let res: TCOVal
 
             switch fn_symbol {
-                case kSymbolDef:            res = eval_def(list, env)
-                case kSymbolDefMacro:       res = eval_def(list, env)
-                case kSymbolLet:            res = eval_let(list, env)
-                case kSymbolDo:             res = eval_do(list, env)
-                case kSymbolIf:             res = eval_if(list, env)
-                case kSymbolFn:             res = eval_fn(list, env)
-                case kSymbolQuote:          res = eval_quote(list, env)
-                case kSymbolQuasiQuote:     res = eval_quasiquote(list, env)
-                case kSymbolMacroExpand:    res = eval_macroexpand(list, env)
-                case kSymbolTry:            res = eval_try(list, env)
+                case kSymbolDef:            res = try eval_def(list, env)
+                case kSymbolDefMacro:       res = try eval_def(list, env)
+                case kSymbolLet:            res = try eval_let(list, env)
+                case kSymbolDo:             res = try eval_do(list, env)
+                case kSymbolIf:             res = try eval_if(list, env)
+                case kSymbolFn:             res = try eval_fn(list, env)
+                case kSymbolQuote:          res = try eval_quote(list, env)
+                case kSymbolQuasiQuote:     res = try eval_quasiquote(list, env)
+                case kSymbolMacroExpand:    res = try eval_macroexpand(list, env)
+                case kSymbolTry:            res = try eval_try(list, env)
                 default:                    res = TCOVal()
             }
             switch res {
@@ -526,78 +484,78 @@ func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
 
         // Standard list to be applied. Evaluate all the elements first.
 
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
+        let eval = try eval_ast(ast, env)
 
         // The result had better be a list and better be non-empty.
 
-        let eval_list = eval as! MalList
+        let eval_list = as_list(eval)
         if eval_list.isEmpty {
-            return eval_list
+            return eval
         }
 
-        if DEBUG_EVAL { println("\(indent)>>  \(eval)") }
+        if DEBUG_EVAL { print("\(indent)>>  \(eval)") }
 
         // Get the first element of the list and execute it.
 
         let first = eval_list.first()
-        let rest = eval_list.rest()
+        let rest = as_sequence(eval_list.rest())
 
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
-        } else if is_closure(first) {
-            let fn = first as! MalClosure
-            var new_env = Environment(outer: fn.env)
-            let result = new_env.set_bindings(fn.args, with_exprs:rest)
-            if is_error(result) { return result }
+        } else if let fn = as_closureQ(first) {
+            let new_env = Environment(outer: fn.env)
+            let _ = try new_env.set_bindings(fn.args, with_exprs: rest)
             if TCO {
                 env = new_env
                 ast = fn.body
                 continue
             }
-            let answer = EVAL(fn.body, new_env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try EVAL(fn.body, new_env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // The first element wasn't a function to be executed. Return an
         // error saying so.
 
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
+        try throw_error("first list item does not evaluate to a function: \(first)")
     }
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
+private func PRINT(exp: MalVal) -> String {
     return pr_str(exp, true)
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
     }
-    return exp
+    return nil
 }
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String, env: Environment) -> String? {
+private func REP(text: String, _ env: Environment) -> String? {
     let exp = RE(text, env)
     if exp == nil { return nil }
     return PRINT(exp!)
@@ -605,14 +563,14 @@ func REP(text: String, env: Environment) -> String? {
 
 // Perform the full REPL.
 //
-func REPL(env: Environment) {
+private func REPL(env: Environment) {
     while true {
         if let text = _readline("user> ") {
             if let output = REP(text, env) {
-                println("\(output)")
+                print("\(output)")
             }
         } else {
-            println()
+            print("")
             break
         }
     }
@@ -623,13 +581,13 @@ func REPL(env: Environment) {
 // taken as a script to execute. If one exists, it is executed in lieu of
 // running the REPL.
 //
-func process_command_line(args:[String], env:Environment) -> Bool {
-    var argv = MalList()
+private func process_command_line(args: [String], _ env: Environment) -> Bool {
+    var argv = make_list()
     if args.count > 2 {
         let args1 = args[2..<args.count]
-        let args2 = args1.map { MalString(unescaped: $0) as MalVal }
+        let args2 = args1.map { make_string($0) }
         let args3 = [MalVal](args2)
-        argv = MalList(array: args3)
+        argv = make_list(args3)
     }
     env.set(kSymbolArgv, argv)
 
@@ -642,7 +600,7 @@ func process_command_line(args:[String], env:Environment) -> Bool {
 }
 
 func main() {
-    var env = Environment(outer: nil)
+    let env = Environment(outer: nil)
 
     load_history_file()
     load_builtins(env)
@@ -654,10 +612,10 @@ func main() {
     RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) " +
        "`(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", env)
 
-    env.set(kSymbolEval, MalBuiltin(function: {
-         unwrap($0) {
-            (ast:MalVal) -> MalVal in
-            EVAL(ast, env)
+    env.set(kSymbolEval, make_builtin({
+         try! unwrap_args($0) {
+            (ast: MalVal) -> MalVal in
+            try EVAL(ast, env)
          }
     }))
 
index 1761c19..b29be0f 100644 (file)
@@ -12,28 +12,28 @@ import Foundation
 // The number of times EVAL has been entered recursively. We keep track of this
 // so that we can protect against overrunning the stack.
 //
-var EVAL_level = 0
+private var EVAL_level = 0
 
 // The maximum number of times we let EVAL recurse before throwing an exception.
 // Testing puts this at some place between 1800 and 1900. Let's keep it at 500
 // for safety's sake.
 //
-let EVAL_leval_max = 500
+private let EVAL_leval_max = 500
 
 // Control whether or not tail-call optimization (TCO) is enabled. We want it
 // `true` most of the time, but may disable it for debugging purposes (it's
 // easier to get a meaningful backtrace that way).
 //
-let TCO = true
+private let TCO = true
 
 // Control whether or not we emit debugging statements in EVAL.
 //
-let DEBUG_EVAL = false
+private let DEBUG_EVAL = false
 
 // String used to prefix information logged in EVAL. Increasing lengths of the
 // string are used the more EVAL is recursed.
 //
-let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
+private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
     "----|----|----|----|----|----|----|----|----|----|----|" +
@@ -47,77 +47,82 @@ let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
 
 // Holds the prefix of INDENT_TEMPLATE used for actual logging.
 //
-var indent = String()
+private var indent = String()
 
 // Symbols used in this module.
 //
-let kSymbolArgv             = MalSymbol(symbol: "*ARGV*")
-let kSymbolCatch            = MalSymbol(symbol: "catch*")
-let kSymbolConcat           = MalSymbol(symbol: "concat")
-let kSymbolCons             = MalSymbol(symbol: "cons")
-let kSymbolDef              = MalSymbol(symbol: "def!")
-let kSymbolDefMacro         = MalSymbol(symbol: "defmacro!")
-let kSymbolDo               = MalSymbol(symbol: "do")
-let kSymbolEval             = MalSymbol(symbol: "eval")
-let kSymbolFn               = MalSymbol(symbol: "fn*")
-let kSymbolIf               = MalSymbol(symbol: "if")
-let kSymbolLet              = MalSymbol(symbol: "let*")
-let kSymbolMacroExpand      = MalSymbol(symbol: "macroexpand")
-let kSymbolQuasiQuote       = MalSymbol(symbol: "quasiquote")
-let kSymbolQuote            = MalSymbol(symbol: "quote")
-let kSymbolSpliceUnquote    = MalSymbol(symbol: "splice-unquote")
-let kSymbolUnquote          = MalSymbol(symbol: "unquote")
-let kSymbolTry              = MalSymbol(symbol: "try*")
-
-// Class to help control the incrementing and decrementing of EVAL_level. We
-// create one of these on entry to EVAL, incrementing the level. When the
-// variable goes out of scope, the object is destroyed, decrementing the level.
-//
-class EVAL_Counter {
-    init() {
-        ++EVAL_level
-    }
-    deinit {
-        --EVAL_level
-    }
+private let kValArgv          = make_symbol("*ARGV*")
+private let kValCatch         = make_symbol("catch*")
+private let kValConcat        = make_symbol("concat")
+private let kValCons          = make_symbol("cons")
+private let kValDef           = make_symbol("def!")
+private let kValDefMacro      = make_symbol("defmacro!")
+private let kValDo            = make_symbol("do")
+private let kValEval          = make_symbol("eval")
+private let kValFn            = make_symbol("fn*")
+private let kValIf            = make_symbol("if")
+private let kValLet           = make_symbol("let*")
+private let kValMacroExpand   = make_symbol("macroexpand")
+private let kValQuasiQuote    = make_symbol("quasiquote")
+private let kValQuote         = make_symbol("quote")
+private let kValSpliceUnquote = make_symbol("splice-unquote")
+private let kValUnquote       = make_symbol("unquote")
+private let kValTry           = make_symbol("try*")
+
+private let kSymbolArgv          = as_symbol(kValArgv)
+private let kSymbolCatch         = as_symbol(kValCatch)
+private let kSymbolConcat        = as_symbol(kValConcat)
+private let kSymbolCons          = as_symbol(kValCons)
+private let kSymbolDef           = as_symbol(kValDef)
+private let kSymbolDefMacro      = as_symbol(kValDefMacro)
+private let kSymbolDo            = as_symbol(kValDo)
+private let kSymbolEval          = as_symbol(kValEval)
+private let kSymbolFn            = as_symbol(kValFn)
+private let kSymbolIf            = as_symbol(kValIf)
+private let kSymbolLet           = as_symbol(kValLet)
+private let kSymbolMacroExpand   = as_symbol(kValMacroExpand)
+private let kSymbolQuasiQuote    = as_symbol(kValQuasiQuote)
+private let kSymbolQuote         = as_symbol(kValQuote)
+private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote)
+private let kSymbolUnquote       = as_symbol(kValUnquote)
+private let kSymbolTry           = as_symbol(kValTry)
+
+func substring(s: String, _ begin: Int, _ end: Int) -> String {
+    return s[s.startIndex.advancedBy(begin) ..< s.startIndex.advancedBy(end)]
 }
 
 // Parse the string into an AST.
 //
-func READ(str: String) -> MalVal {
-    return read_str(str)
+private func READ(str: String) throws -> MalVal {
+    return try read_str(str)
 }
 
 // Return whether or not `val` is a non-empty list.
 //
-func is_pair(val:MalVal) -> Bool {
-    if !is_sequence(val) { return false }
-    let list = val as! MalSequence
-    return !list.isEmpty
+private func is_pair(val: MalVal) -> Bool {
+    if let seq = as_sequenceQ(val) {
+        return !seq.isEmpty
+    }
+    return false
 }
 
 // Expand macros for as long as the expression looks like a macro invocation.
 //
-func macroexpand(var ast:MalVal, env:Environment) -> MalVal {
+private func macroexpand(var ast: MalVal, _ env: Environment) throws -> MalVal {
     while true {
-        if !is_list(ast) { break }
-        let ast_as_list = ast as! MalList
-        if ast_as_list.isEmpty { break }
-        let first = ast_as_list.first()
-        if !is_symbol(first) { break }
-        let macro_name = first as! MalSymbol
-        let obj = env.get(macro_name)
-        if obj == nil { break }
-        if !is_closure(obj!) { break }
-        let macro = obj! as! MalClosure
-        if !macro.is_macro { break }
-        var new_env = Environment(outer: macro.env)
-        let rest = ast_as_list.rest()
-        let res = new_env.set_bindings(macro.args, with_exprs:rest)
-        if is_error(res) { return res }
-        ast = EVAL(macro.body, new_env)
+        if  let ast_as_list = as_listQ(ast) where !ast_as_list.isEmpty,
+            let macro_name = as_symbolQ(ast_as_list.first()),
+            let obj = env.get(macro_name),
+            let macro = as_macroQ(obj)
+        {
+            let new_env = Environment(outer: macro.env)
+            let rest = as_sequence(ast_as_list.rest())
+            let _ = try new_env.set_bindings(macro.args, with_exprs: rest)
+            ast = try EVAL(macro.body, new_env)
+            continue
+        }
+        return ast
     }
-    return ast
 }
 
 // Evaluate `quasiquote`, possibly recursing in the process.
@@ -156,15 +161,15 @@ func macroexpand(var ast:MalVal, env:Environment) -> MalVal {
 // list) with the remaining items of the list containing that splice-quote
 // expression. However, it's not clear to me why the handling of "unquote" is
 // not handled similarly, for consistency's sake.
-
-func quasiquote(qq_arg:MalVal) -> MalVal {
+//
+private func quasiquote(qq_arg: MalVal) throws -> MalVal {
 
     // If the argument is an atom or empty list:
     //
     // Return: (quote <argument>)
 
     if !is_pair(qq_arg) {
-        return MalList(objects: kSymbolQuote, qq_arg)
+        return make_list_from(kValQuote, qq_arg)
     }
 
     // The argument is a non-empty list -- that is (item rest...)
@@ -174,12 +179,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //
     // Return: item
 
-    let qq_list = qq_arg as! MalSequence
-    if is_symbol(qq_list.first()) {
-        let sym = qq_list.first() as! MalSymbol
-        if sym == kSymbolUnquote {
-            return qq_list.count >= 2 ? qq_list[1] : MalNil()
-        }
+    let qq_list = as_sequence(qq_arg)
+    if let sym = as_symbolQ(qq_list.first()) where sym == kSymbolUnquote {
+        return qq_list.count >= 2 ? try! qq_list.nth(1) : make_nil()
     }
 
     // If the first item from the list is itself a non-empty list starting with
@@ -188,14 +190,10 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     // Return: (concat item quasiquote(rest...))
 
     if is_pair(qq_list.first()) {
-        let qq_list_item0 = qq_list.first() as! MalSequence
-        if is_symbol(qq_list_item0.first()) {
-            let sym = qq_list_item0.first() as! MalSymbol
-            if sym == kSymbolSpliceUnquote {
-                let result = quasiquote(qq_list.rest())
-                if is_error(result) { return result }
-                return MalList(array: [kSymbolConcat, qq_list_item0[1], result])
-            }
+        let qq_list_item0 = as_sequence(qq_list.first())
+        if let sym = as_symbolQ(qq_list_item0.first()) where sym == kSymbolSpliceUnquote {
+            let result = try quasiquote(qq_list.rest())
+            return make_list_from(kValConcat, try! qq_list_item0.nth(1), result)
         }
     }
 
@@ -203,13 +201,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //
     // Return: (cons (quasiquote item) (quasiquote (rest...))
 
-    let first = quasiquote(qq_list.first())
-    if is_error(first) { return first }
-
-    let rest = quasiquote(qq_list.rest())
-    if is_error(rest) { return rest }
-
-    return MalList(objects: kSymbolCons, first, rest)
+    let first = try quasiquote(qq_list.first())
+    let rest = try quasiquote(qq_list.rest())
+    return make_list_from(kValCons, first, rest)
 }
 
 // Perform a simple evaluation of the `ast` object. If it's a symbol,
@@ -217,51 +211,45 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
 // elements (or just the values, in the case of the hashmap). Otherwise, return
 // the object unchanged.
 //
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {
-    switch ast.type {
-        case .TypeSymbol:
-            let symbol = ast as! MalSymbol
-            if let val = env.get(symbol) {
-                return val
-            }
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests
-        case .TypeList:
-            let list = ast as! MalList
-            var result = [MalVal]()
-            result.reserveCapacity(list.count)
-            for item in list {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalList(array: result)
-        case .TypeVector:
-            let vec = ast as! MalVector
-            var result = [MalVal]()
-            result.reserveCapacity(vec.count)
-            for item in vec {
-                let eval = EVAL(item, env)
-                if is_error(eval) { return eval }
-                result.append(eval)
-            }
-            return MalVector(array: result)
-        case .TypeHashMap:
-            let hash = ast as! MalHashMap
-            var result = [MalVal]()
-            result.reserveCapacity(hash.count * 2)
-            for (k, v) in hash {
-                let new_v = EVAL(v, env)
-                if is_error(new_v) { return new_v }
-                result.append(k)
-                result.append(new_v)
-            }
-            return MalHashMap(array: result)
-        default:
-            return ast
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
+    if let symbol = as_symbolQ(ast) {
+        guard let val = env.get(symbol) else {
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests
+        }
+        return val
+    }
+    if let list = as_listQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(list.count))
+        for item in list {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_list(result)
+    }
+    if let vec = as_vectorQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(vec.count))
+        for item in vec {
+            let eval = try EVAL(item, env)
+            result.append(eval)
+        }
+        return make_vector(result)
+    }
+    if let hash = as_hashmapQ(ast) {
+        var result = [MalVal]()
+        result.reserveCapacity(Int(hash.count) * 2)
+        for (k, v) in hash {
+            let new_v = try EVAL(v, env)
+            result.append(k)
+            result.append(new_v)
+        }
+        return make_hashmap(result)
     }
+    return ast
 }
 
-enum TCOVal {
+private enum TCOVal {
     case NoResult
     case Return(MalVal)
     case Continue(MalVal, Environment)
@@ -269,252 +257,222 @@ enum TCOVal {
     init() { self = .NoResult }
     init(_ result: MalVal) { self = .Return(result) }
     init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) }
-    init(_ e: String) { self = .Return(MalError(message: e)) }
 }
 
 // EVALuate "def!" and "defmacro!".
 //
-func eval_def(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to def!, got \(list.count - 1)")
-    }
-    let arg0 = list[0] as! MalSymbol
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_symbol(arg1) {
-        return TCOVal("expected symbol for first argument to def!")
-    }
-    let sym = arg1 as! MalSymbol
-    let value = EVAL(arg2, env)
-    if is_error(value) { return TCOVal(value) }
-    if arg0 == kSymbolDefMacro {
-        if is_closure(value) {
-            let as_closure = value as! MalClosure
-            as_closure.is_macro = true
-        } else {
-            return TCOVal("expected closure, got \(value)")
+private func eval_def(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")
+    }
+    let arg0 = try! list.nth(0)
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let sym = as_symbolQ(arg1) else {
+        try throw_error("expected symbol for first argument to def!")
+    }
+    var value = try EVAL(arg2, env)
+    if as_symbol(arg0) == kSymbolDefMacro {
+        guard let closure = as_closureQ(value) else {
+            try throw_error("expected closure, got \(value)")
         }
+        value = make_macro(closure)
     }
     return TCOVal(env.set(sym, value))
 }
 
 // EVALuate "let*".
 //
-func eval_let(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to let*, got \(list.count - 1)")
+private func eval_let(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")
     }
-    let arg1 = list[1]
-    let arg2 = list[2]
-    if !is_sequence(arg1) {
-        return TCOVal("expected list for first argument to let*")
+    let arg1 = try! list.nth(1)
+    let arg2 = try! list.nth(2)
+    guard let bindings = as_sequenceQ(arg1) else {
+        try throw_error("expected list for first argument to let*")
     }
-    let bindings = arg1 as! MalSequence
-    if bindings.count % 2 == 1 {
-        return TCOVal("expected even number of elements in bindings to let*, got \(bindings.count)")
+    guard bindings.count % 2 == 0 else {
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")
     }
-    var new_env = Environment(outer: env)
-    for var index = 0; index < bindings.count; index += 2 {
-        let binding_name = bindings[index]
-        let binding_value = bindings[index + 1]
-
-        if !is_symbol(binding_name) {
-            return TCOVal("expected symbol for first element in binding pair")
+    let new_env = Environment(outer: env)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {
+        let binding_name = try! bindings.nth(index)
+        let binding_value = try! bindings.nth(index + 1)
+        guard let binding_symbol = as_symbolQ(binding_name) else {
+            try throw_error("expected symbol for first element in binding pair")
         }
-        let binding_symbol = binding_name as! MalSymbol
-        let evaluated_value = EVAL(binding_value, new_env)
-        if is_error(evaluated_value) { return TCOVal(evaluated_value) }
+        let evaluated_value = try EVAL(binding_value, new_env)
         new_env.set(binding_symbol, evaluated_value)
     }
     if TCO {
         return TCOVal(arg2, new_env)
     }
-    return TCOVal(EVAL(arg2, new_env))
+    return TCOVal(try EVAL(arg2, new_env))
 }
 
 // EVALuate "do".
 //
-func eval_do(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if TCO {
-        let eval = eval_ast(MalList(slice: list[1..<list.count-1]), env)
-        if is_error(eval) { return TCOVal(eval) }
+        let _ = try eval_ast(list.range_from(1, to: list.count-1), env)
         return TCOVal(list.last(), env)
     }
 
-    let evaluated_ast = eval_ast(list.rest(), env)
-    if is_error(evaluated_ast) { return TCOVal(evaluated_ast) }
-    let evaluated_seq = evaluated_ast as! MalSequence
+    let evaluated_ast = try eval_ast(list.rest(), env)
+    let evaluated_seq = as_sequence(evaluated_ast)
     return TCOVal(evaluated_seq.last())
 }
 
 // EVALuate "if".
 //
-func eval_if(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count < 3 {
-        return TCOVal("expected at least 2 arguments to if, got \(list.count - 1)")
+private func eval_if(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 3 else {
+        try throw_error("expected at least 2 arguments to if, got \(list.count - 1)")
     }
-    let cond_result = EVAL(list[1], env)
-    var new_ast = MalVal()
+    let cond_result = try EVAL(try! list.nth(1), env)
+    var new_ast: MalVal
     if is_truthy(cond_result) {
-        new_ast = list[2]
+        new_ast = try! list.nth(2)
     } else if list.count == 4 {
-        new_ast = list[3]
+        new_ast = try! list.nth(3)
     } else {
-        return TCOVal(MalNil())
+        return TCOVal(make_nil())
     }
     if TCO {
         return TCOVal(new_ast, env)
     }
-    return TCOVal(EVAL(new_ast, env))
+    return TCOVal(try EVAL(new_ast, env))
 }
 
 // EVALuate "fn*".
 //
-func eval_fn(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count != 3 {
-        return TCOVal("expected 2 arguments to fn*, got \(list.count - 1)")
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count == 3 else {
+        try throw_error("expected 2 arguments to fn*, got \(list.count - 1)")
     }
-    if !is_sequence(list[1]) {
-        return TCOVal("expected list or vector for first argument to fn*")
+    guard let seq = as_sequenceQ(try! list.nth(1)) else {
+        try throw_error("expected list or vector for first argument to fn*")
     }
-    return TCOVal(MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env))
+    return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env)))
 }
 
 // EVALuate "quote".
 //
-func eval_quote(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal {
     if list.count >= 2 {
-        return TCOVal(list[1])
+        return TCOVal(try! list.nth(1))
     }
-    return TCOVal(MalNil())
+    return TCOVal(make_nil())
 }
 
 // EVALuate "quasiquote".
 //
-func eval_quasiquote(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count >= 2 {
-        if TCO {
-            return TCOVal(quasiquote(list[1]), env)
-        }
-        return TCOVal(EVAL(quasiquote(list[1]), env))
+private func eval_quasiquote(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 2 else {
+        try throw_error("Expected non-nil parameter to 'quasiquote'")
+    }
+    if TCO {
+        return TCOVal(try quasiquote(try! list.nth(1)), env)
     }
-    return TCOVal("Expected non-nil parameter to 'quasiquote'")
+    return TCOVal(try EVAL(try quasiquote(try! list.nth(1)), env))
 }
 
 // EVALuate "macroexpand".
 //
-func eval_macroexpand(list: MalSequence, env: Environment) -> TCOVal {
-    if list.count >= 2 {
-        return TCOVal(macroexpand(list[1], env))
+private func eval_macroexpand(list: MalSequence, _ env: Environment) throws -> TCOVal {
+    guard list.count >= 2 else {
+        try throw_error("Expected parameter to 'macroexpand'")
     }
-    return TCOVal("Expected parameter to 'macroexpand'")
+    return TCOVal(try macroexpand(try! list.nth(1), env))
 }
 
 // EVALuate "try*" (and "catch*").
 //
-func eval_try(list: MalSequence, env: Environment) -> TCOVal {
+private func eval_try(list: MalSequence, _ env: Environment) throws -> TCOVal {
     // This is a subset of the Clojure try/catch:
     //
     //      (try* expr (catch exception-name expr))
 
-    if list.count < 2 {
-        println("try*: no body parameter")
-        return TCOVal(MalNil()) // No body parameter
-    }
-    let res = EVAL(list[1], env)
-    if !is_error(res) { return TCOVal(res) }
-    if list.count < 3 {
-        println("try*: no catch parameter")
-        return TCOVal(MalNil()) // No catch parameter
-    }
-    if !is_sequence(list[2]) {
-        println("try*: second parameter to 'try' is not a sequence")
-        return TCOVal(MalNil()) // Second parameter to 'try' is not a sequence
-    }
-    let catch_list = list[2] as! MalSequence
-    if catch_list.count < 3 {
-        println("try*: not enough catch parameters")
-        return TCOVal(MalNil()) // Not enough catch parameters
-    }
-    if !is_symbol(catch_list[0]) {
-        println("try*: first parameter in catch list is not a symbol")
-        return TCOVal(MalNil()) // First parameter in catch list is not a symbol
-    }
-    let catch_symbol = catch_list[0] as! MalSymbol
-    if catch_symbol != kSymbolCatch {
-        println("try*: first parameter in catch list is not 'catch'")
-        return TCOVal(MalNil()) // First parameter in catch list is not 'catch'
-    }
-    if !is_symbol(catch_list[1]) {
-        println("try*: first parameter to 'catch' is not a symbol")
-        return TCOVal(MalNil()) // First parameter to 'catch' is not a symbol
-    }
-    let catch_name = catch_list[1] as! MalSymbol
-    let catch_expr = catch_list[2]
-    let catch_env = Environment(outer: env)
-    let error = res as! MalError
-    catch_env.set_bindings(MalList(objects: catch_name), with_exprs: MalList(objects: error.value))
-    return TCOVal(EVAL(catch_expr, catch_env))
+    guard list.count >= 2 else {
+        try throw_error("try*: no body parameter")
+    }
+
+    do {
+        return TCOVal(try EVAL(try! list.nth(1), env))
+    } catch let error as MalException {
+        guard list.count >= 3,
+            let catch_list = as_sequenceQ(try! list.nth(2)) where catch_list.count >= 3,
+            let _ = as_symbolQ(try! catch_list.nth(0)) else
+        {
+            throw error // No catch parameter
+        }
+        let catch_name = try! catch_list.nth(1)
+        let catch_expr = try! catch_list.nth(2)
+        let catch_env = Environment(outer: env)
+        try catch_env.set_bindings(as_sequence(make_list_from(catch_name)),
+                with_exprs: as_sequence(make_list_from(error.exception)))
+        return TCOVal(try EVAL(catch_expr, catch_env))
+    }
 }
 
 // Walk the AST and completely evaluate it, handling macro expansions, special
 // forms and function calls.
 //
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
-    let x = EVAL_Counter()
-    if EVAL_level > EVAL_leval_max {
-        return MalError(message: "Recursing too many levels (> \(EVAL_leval_max))")
+private func EVAL(var ast: MalVal, var _ env: Environment) throws -> MalVal {
+    EVAL_level++
+    defer { EVAL_level-- }
+    guard EVAL_level <= EVAL_leval_max else {
+        try throw_error("Recursing too many levels (> \(EVAL_leval_max))")
     }
 
     if DEBUG_EVAL {
-        indent = prefix(INDENT_TEMPLATE, EVAL_level)
+        indent = substring(INDENT_TEMPLATE, 0, EVAL_level)
     }
 
     while true {
-        if is_error(ast) { return ast }
-        if DEBUG_EVAL { println("\(indent)>   \(ast)") }
+        if DEBUG_EVAL { print("\(indent)>   \(ast)") }
 
         if !is_list(ast) {
 
             // Not a list -- just evaluate and return.
 
-            let answer = eval_ast(ast, env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try eval_ast(ast, env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // Special handling if it's a list.
 
-        var list = ast as! MalList
-        ast = macroexpand(ast, env)
+        var list = as_list(ast)
+        ast = try macroexpand(ast, env)
         if !is_list(ast) { return ast }
-        list = ast as! MalList
+        list = as_list(ast)
 
-        if DEBUG_EVAL { println("\(indent)>.  \(list)") }
+        if DEBUG_EVAL { print("\(indent)>.  \(list)") }
 
         if list.isEmpty {
-            return list
+            return ast
         }
 
         // Check for special forms, where we want to check the operation
         // before evaluating all of the parameters.
 
         let arg0 = list.first()
-        if is_symbol(arg0) {
-            var res: TCOVal
-            let fn_symbol = arg0 as! MalSymbol
+        if let fn_symbol = as_symbolQ(arg0) {
+            let res: TCOVal
 
             switch fn_symbol {
-                case kSymbolDef:            res = eval_def(list, env)
-                case kSymbolDefMacro:       res = eval_def(list, env)
-                case kSymbolLet:            res = eval_let(list, env)
-                case kSymbolDo:             res = eval_do(list, env)
-                case kSymbolIf:             res = eval_if(list, env)
-                case kSymbolFn:             res = eval_fn(list, env)
-                case kSymbolQuote:          res = eval_quote(list, env)
-                case kSymbolQuasiQuote:     res = eval_quasiquote(list, env)
-                case kSymbolMacroExpand:    res = eval_macroexpand(list, env)
-                case kSymbolTry:            res = eval_try(list, env)
+                case kSymbolDef:            res = try eval_def(list, env)
+                case kSymbolDefMacro:       res = try eval_def(list, env)
+                case kSymbolLet:            res = try eval_let(list, env)
+                case kSymbolDo:             res = try eval_do(list, env)
+                case kSymbolIf:             res = try eval_if(list, env)
+                case kSymbolFn:             res = try eval_fn(list, env)
+                case kSymbolQuote:          res = try eval_quote(list, env)
+                case kSymbolQuasiQuote:     res = try eval_quasiquote(list, env)
+                case kSymbolMacroExpand:    res = try eval_macroexpand(list, env)
+                case kSymbolTry:            res = try eval_try(list, env)
                 default:                    res = TCOVal()
             }
             switch res {
@@ -526,78 +484,78 @@ func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
 
         // Standard list to be applied. Evaluate all the elements first.
 
-        let eval = eval_ast(ast, env)
-        if is_error(eval) { return eval }
+        let eval = try eval_ast(ast, env)
 
         // The result had better be a list and better be non-empty.
 
-        let eval_list = eval as! MalList
+        let eval_list = as_list(eval)
         if eval_list.isEmpty {
-            return eval_list
+            return eval
         }
 
-        if DEBUG_EVAL { println("\(indent)>>  \(eval)") }
+        if DEBUG_EVAL { print("\(indent)>>  \(eval)") }
 
         // Get the first element of the list and execute it.
 
         let first = eval_list.first()
-        let rest = eval_list.rest()
+        let rest = as_sequence(eval_list.rest())
 
-        if is_builtin(first) {
-            let fn = first as! MalBuiltin
-            let answer = fn.apply(rest)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+        if let fn = as_builtinQ(first) {
+            let answer = try fn.apply(rest)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
-        } else if is_closure(first) {
-            let fn = first as! MalClosure
-            var new_env = Environment(outer: fn.env)
-            let result = new_env.set_bindings(fn.args, with_exprs:rest)
-            if is_error(result) { return result }
+        } else if let fn = as_closureQ(first) {
+            let new_env = Environment(outer: fn.env)
+            let _ = try new_env.set_bindings(fn.args, with_exprs: rest)
             if TCO {
                 env = new_env
                 ast = fn.body
                 continue
             }
-            let answer = EVAL(fn.body, new_env)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }
+            let answer = try EVAL(fn.body, new_env)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }
             return answer
         }
 
         // The first element wasn't a function to be executed. Return an
         // error saying so.
 
-        return MalError(message: "first list item does not evaluate to a function: \(first)")
+        try throw_error("first list item does not evaluate to a function: \(first)")
     }
 }
 
 // Convert the value into a human-readable string for printing.
 //
-func PRINT(exp: MalVal) -> String? {
-    if is_error(exp) { return nil }
+private func PRINT(exp: MalVal) -> String {
     return pr_str(exp, true)
 }
 
 // Perform the READ and EVAL steps. Useful for when you don't care about the
 // printable result.
 //
-func RE(text: String, env: Environment) -> MalVal? {
-    if text.isEmpty { return nil }
-    let ast = READ(text)
-    if is_error(ast) {
-        println("Error parsing input: \(ast)")
-        return nil
-    }
-    let exp = EVAL(ast, env)
-    if is_error(exp) {
-        println("Error evaluating input: \(exp)")
-        return nil
+private func RE(text: String, _ env: Environment) -> MalVal? {
+    if !text.isEmpty {
+        do {
+            let ast = try READ(text)
+            do {
+                return try EVAL(ast, env)
+            } catch let error as MalException {
+                print("Error evaluating input: \(error)")
+            } catch {
+                print("Error evaluating input: \(error)")
+            }
+        } catch let error as MalException {
+            print("Error parsing input: \(error)")
+        } catch {
+            print("Error parsing input: \(error)")
+        }
     }
-    return exp
+    return nil
 }
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.
 //
-func REP(text: String, env: Environment) -> String? {
+private func REP(text: String, _ env: Environment) -> String? {
     let exp = RE(text, env)
     if exp == nil { return nil }
     return PRINT(exp!)
@@ -605,14 +563,14 @@ func REP(text: String, env: Environment) -> String? {
 
 // Perform the full REPL.
 //
-func REPL(env: Environment) {
+private func REPL(env: Environment) {
     while true {
         if let text = _readline("user> ") {
             if let output = REP(text, env) {
-                println("\(output)")
+                print("\(output)")
             }
         } else {
-            println()
+            print("")
             break
         }
     }
@@ -623,13 +581,13 @@ func REPL(env: Environment) {
 // taken as a script to execute. If one exists, it is executed in lieu of
 // running the REPL.
 //
-func process_command_line(args:[String], env:Environment) -> Bool {
-    var argv = MalList()
+private func process_command_line(args: [String], _ env: Environment) -> Bool {
+    var argv = make_list()
     if args.count > 2 {
         let args1 = args[2..<args.count]
-        let args2 = args1.map { MalString(unescaped: $0) as MalVal }
+        let args2 = args1.map { make_string($0) }
         let args3 = [MalVal](args2)
-        argv = MalList(array: args3)
+        argv = make_list(args3)
     }
     env.set(kSymbolArgv, argv)
 
@@ -642,12 +600,12 @@ func process_command_line(args:[String], env:Environment) -> Bool {
 }
 
 func main() {
-    var env = Environment(outer: nil)
+    let env = Environment(outer: nil)
 
     load_history_file()
     load_builtins(env)
 
-    RE("(def! *host-language* \"swift\")", env);
+    RE("(def! *host-language* \"swift\")", env)
     RE("(def! not (fn* (a) (if a false true)))", env)
     RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env)
     RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) " +
@@ -655,15 +613,15 @@ func main() {
     RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) " +
        "`(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", env)
 
-    env.set(kSymbolEval, MalBuiltin(function: {
-         unwrap($0) {
-            (ast:MalVal) -> MalVal in
-            EVAL(ast, env)
+    env.set(kSymbolEval, make_builtin({
+         try! unwrap_args($0) {
+            (ast: MalVal) -> MalVal in
+            try EVAL(ast, env)
          }
     }))
 
     if process_command_line(Process.arguments, env) {
-        RE("(println (str \"Mal [\" *host-language*\"]\"))", env);
+        RE("(println (str \"Mal [\" *host-language*\"]\"))", env)
         REPL(env)
     }
 
index 5dd45ae..980d973 100644 (file)
@@ -73,28 +73,28 @@ import Foundation
 // The number of times EVAL has been entered recursively. We keep track of this                                                 // malstep(5,6,7,8,9,A)
 // so that we can protect against overrunning the stack.                                                                        // malstep(5,6,7,8,9,A)
 //                                                                                                                              // malstep(5,6,7,8,9,A)
-var EVAL_level = 0                                                                                                              // malstep(5,6,7,8,9,A)
+private var EVAL_level = 0                                                                                                      // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
 // The maximum number of times we let EVAL recurse before throwing an exception.                                                // malstep(5,6,7,8,9,A)
 // Testing puts this at some place between 1800 and 1900. Let's keep it at 500                                                  // malstep(5,6,7,8,9,A)
 // for safety's sake.                                                                                                           // malstep(5,6,7,8,9,A)
 //                                                                                                                              // malstep(5,6,7,8,9,A)
-let EVAL_leval_max = 500                                                                                                        // malstep(5,6,7,8,9,A)
+private let EVAL_leval_max = 500                                                                                                // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
 // Control whether or not tail-call optimization (TCO) is enabled. We want it                                                   // malstep(5,6,7,8,9,A)
 // `true` most of the time, but may disable it for debugging purposes (it's                                                     // malstep(5,6,7,8,9,A)
 // easier to get a meaningful backtrace that way).                                                                              // malstep(5,6,7,8,9,A)
 //                                                                                                                              // malstep(5,6,7,8,9,A)
-let TCO = true                                                                                                                  // malstep(5,6,7,8,9,A)
+private let TCO = true                                                                                                          // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
 // Control whether or not we emit debugging statements in EVAL.                                                                 // malstep(5,6,7,8,9,A)
 //                                                                                                                              // malstep(5,6,7,8,9,A)
-let DEBUG_EVAL = false                                                                                                          // malstep(5,6,7,8,9,A)
+private let DEBUG_EVAL = false                                                                                                  // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
 // String used to prefix information logged in EVAL. Increasing lengths of the                                                  // malstep(5,6,7,8,9,A)
 // string are used the more EVAL is recursed.                                                                                   // malstep(5,6,7,8,9,A)
 //                                                                                                                              // malstep(5,6,7,8,9,A)
-let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +                                                             // malstep(5,6,7,8,9,A)
+private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +                                                     // malstep(5,6,7,8,9,A)
     "----|----|----|----|----|----|----|----|----|----|----|" +                                                                 // malstep(5,6,7,8,9,A)
     "----|----|----|----|----|----|----|----|----|----|----|" +                                                                 // malstep(5,6,7,8,9,A)
     "----|----|----|----|----|----|----|----|----|----|----|" +                                                                 // malstep(5,6,7,8,9,A)
@@ -108,39 +108,48 @@ let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" +
                                                                                                                                 // malstep(5,6,7,8,9,A)
 // Holds the prefix of INDENT_TEMPLATE used for actual logging.                                                                 // malstep(5,6,7,8,9,A)
 //                                                                                                                              // malstep(5,6,7,8,9,A)
-var indent = String()                                                                                                           // malstep(5,6,7,8,9,A)
+private var indent = String()                                                                                                   // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
 // Symbols used in this module.                                                                                                 // malstep(3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(3,4,5,6,7,8,9,A)
-let kSymbolArgv             = MalSymbol(symbol: "*ARGV*")                                                                       // malstep(6,7,8,9,A)
-let kSymbolCatch            = MalSymbol(symbol: "catch*")                                                                       // malstep(9,A)
-let kSymbolConcat           = MalSymbol(symbol: "concat")                                                                       // malstep(7,8,9,A)
-let kSymbolCons             = MalSymbol(symbol: "cons")                                                                         // malstep(7,8,9,A)
-let kSymbolDef              = MalSymbol(symbol: "def!")                                                                         // malstep(3,4,5,6,7,8,9,A)
-let kSymbolDefMacro         = MalSymbol(symbol: "defmacro!")                                                                    // malstep(8,9,A)
-let kSymbolDo               = MalSymbol(symbol: "do")                                                                           // malstep(4,5,6,7,8,9,A)
-let kSymbolEval             = MalSymbol(symbol: "eval")                                                                         // malstep(6,7,8,9,A)
-let kSymbolFn               = MalSymbol(symbol: "fn*")                                                                          // malstep(4,5,6,7,8,9,A)
-let kSymbolIf               = MalSymbol(symbol: "if")                                                                           // malstep(4,5,6,7,8,9,A)
-let kSymbolLet              = MalSymbol(symbol: "let*")                                                                         // malstep(3,4,5,6,7,8,9,A)
-let kSymbolMacroExpand      = MalSymbol(symbol: "macroexpand")                                                                  // malstep(8,9,A)
-let kSymbolQuasiQuote       = MalSymbol(symbol: "quasiquote")                                                                   // malstep(7,8,9,A)
-let kSymbolQuote            = MalSymbol(symbol: "quote")                                                                        // malstep(7,8,9,A)
-let kSymbolSpliceUnquote    = MalSymbol(symbol: "splice-unquote")                                                               // malstep(7,8,9,A)
-let kSymbolUnquote          = MalSymbol(symbol: "unquote")                                                                      // malstep(7,8,9,A)
-let kSymbolTry              = MalSymbol(symbol: "try*")                                                                         // malstep(9,A)
+private let kValArgv          = make_symbol("*ARGV*")                                                                           // malstep(6,7,8,9,A)
+private let kValCatch         = make_symbol("catch*")                                                                           // malstep(9,A)
+private let kValConcat        = make_symbol("concat")                                                                           // malstep(7,8,9,A)
+private let kValCons          = make_symbol("cons")                                                                             // malstep(7,8,9,A)
+private let kValDef           = make_symbol("def!")                                                                             // malstep(3,4,5,6,7,8,9,A)
+private let kValDefMacro      = make_symbol("defmacro!")                                                                        // malstep(8,9,A)
+private let kValDo            = make_symbol("do")                                                                               // malstep(4,5,6,7,8,9,A)
+private let kValEval          = make_symbol("eval")                                                                             // malstep(6,7,8,9,A)
+private let kValFn            = make_symbol("fn*")                                                                              // malstep(4,5,6,7,8,9,A)
+private let kValIf            = make_symbol("if")                                                                               // malstep(4,5,6,7,8,9,A)
+private let kValLet           = make_symbol("let*")                                                                             // malstep(3,4,5,6,7,8,9,A)
+private let kValMacroExpand   = make_symbol("macroexpand")                                                                      // malstep(8,9,A)
+private let kValQuasiQuote    = make_symbol("quasiquote")                                                                       // malstep(7,8,9,A)
+private let kValQuote         = make_symbol("quote")                                                                            // malstep(7,8,9,A)
+private let kValSpliceUnquote = make_symbol("splice-unquote")                                                                   // malstep(7,8,9,A)
+private let kValUnquote       = make_symbol("unquote")                                                                          // malstep(7,8,9,A)
+private let kValTry           = make_symbol("try*")                                                                             // malstep(3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(3,4,5,6,7,8,9,A)
-// Class to help control the incrementing and decrementing of EVAL_level. We                                                    // malstep(5,6,7,8,9,A)
-// create one of these on entry to EVAL, incrementing the level. When the                                                       // malstep(5,6,7,8,9,A)
-// variable goes out of scope, the object is destroyed, decrementing the level.                                                 // malstep(5,6,7,8,9,A)
-//                                                                                                                              // malstep(5,6,7,8,9,A)
-class EVAL_Counter {                                                                                                            // malstep(5,6,7,8,9,A)
-    init() {                                                                                                                    // malstep(5,6,7,8,9,A)
-        ++EVAL_level                                                                                                            // malstep(5,6,7,8,9,A)
-    }                                                                                                                           // malstep(5,6,7,8,9,A)
-    deinit {                                                                                                                    // malstep(5,6,7,8,9,A)
-        --EVAL_level                                                                                                            // malstep(5,6,7,8,9,A)
-    }                                                                                                                           // malstep(5,6,7,8,9,A)
+private let kSymbolArgv          = as_symbol(kValArgv)                                                                          // malstep(6,7,8,9,A)
+private let kSymbolCatch         = as_symbol(kValCatch)                                                                         // malstep(9,A)
+private let kSymbolConcat        = as_symbol(kValConcat)                                                                        // malstep(7,8,9,A)
+private let kSymbolCons          = as_symbol(kValCons)                                                                          // malstep(7,8,9,A)
+private let kSymbolDef           = as_symbol(kValDef)                                                                           // malstep(3,4,5,6,7,8,9,A)
+private let kSymbolDefMacro      = as_symbol(kValDefMacro)                                                                      // malstep(8,9,A)
+private let kSymbolDo            = as_symbol(kValDo)                                                                            // malstep(4,5,6,7,8,9,A)
+private let kSymbolEval          = as_symbol(kValEval)                                                                          // malstep(6,7,8,9,A)
+private let kSymbolFn            = as_symbol(kValFn)                                                                            // malstep(4,5,6,7,8,9,A)
+private let kSymbolIf            = as_symbol(kValIf)                                                                            // malstep(4,5,6,7,8,9,A)
+private let kSymbolLet           = as_symbol(kValLet)                                                                           // malstep(3,4,5,6,7,8,9,A)
+private let kSymbolMacroExpand   = as_symbol(kValMacroExpand)                                                                   // malstep(8,9,A)
+private let kSymbolQuasiQuote    = as_symbol(kValQuasiQuote)                                                                    // malstep(7,8,9,A)
+private let kSymbolQuote         = as_symbol(kValQuote)                                                                         // malstep(7,8,9,A)
+private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote)                                                                 // malstep(7,8,9,A)
+private let kSymbolUnquote       = as_symbol(kValUnquote)                                                                       // malstep(7,8,9,A)
+private let kSymbolTry           = as_symbol(kValTry)                                                                           // malstep(9,A)
+                                                                                                                                // malstep(3,4,5,6,7,8,9,A)
+func substring(s: String, _ begin: Int, _ end: Int) -> String {                                                                 // malstep(5,6,7,8,9,A)
+    return s[s.startIndex.advancedBy(begin) ..< s.startIndex.advancedBy(end)]                                                   // malstep(5,6,7,8,9,A)
 }                                                                                                                               // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
 //
@@ -150,43 +159,39 @@ class EVAL_Counter {
 
 // Parse the string into an AST.                                                                                                // malstep(0,1,2,3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(0,1,2,3,4,5,6,7,8,9,A)
-func READ(str: String) -> String {                                                                                              // malstep(0)
+private func READ(str: String) -> String {                                                                                      // malstep(0)
     return str                                                                                                                  // malstep(0)
 }                                                                                                                               // malstep(0)
-func READ(str: String) -> MalVal {                                                                                              // malstep(1,2,3,4,5,6,7,8,9,A)
-    return read_str(str)                                                                                                        // malstep(1,2,3,4,5,6,7,8,9,A)
+private func READ(str: String) throws -> MalVal {                                                                               // malstep(1,2,3,4,5,6,7,8,9,A)
+    return try read_str(str)                                                                                                    // malstep(1,2,3,4,5,6,7,8,9,A)
 }                                                                                                                               // malstep(1,2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(0,1,2,3,4,5,6,7,8,9,A)
 // Return whether or not `val` is a non-empty list.                                                                             // malstep(7,8,9,A)
 //                                                                                                                              // malstep(7,8,9,A)
-func is_pair(val:MalVal) -> Bool {                                                                                              // malstep(7,8,9,A)
-    if !is_sequence(val) { return false }                                                                                       // malstep(7,8,9,A)
-    let list = val as! MalSequence                                                                                              // malstep(7,8,9,A)
-    return !list.isEmpty                                                                                                        // malstep(7,8,9,A)
+private func is_pair(val: MalVal) -> Bool {                                                                                     // malstep(7,8,9,A)
+    if let seq = as_sequenceQ(val) {                                                                                            // malstep(7,8,9,A)
+        return !seq.isEmpty                                                                                                     // malstep(7,8,9,A)
+    }                                                                                                                           // malstep(7,8,9,A)
+    return false                                                                                                                // malstep(7,8,9,A)
 }                                                                                                                               // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
 // Expand macros for as long as the expression looks like a macro invocation.                                                   // malstep(8,9,A)
 //                                                                                                                              // malstep(8,9,A)
-func macroexpand(var ast:MalVal, env:Environment) -> MalVal {                                                                   // malstep(8,9,A)
+private func macroexpand(var ast: MalVal, _ env: Environment) throws -> MalVal {                                                // malstep(8,9,A)
     while true {                                                                                                                // malstep(8,9,A)
-        if !is_list(ast) { break }                                                                                              // malstep(8,9,A)
-        let ast_as_list = ast as! MalList                                                                                       // malstep(8,9,A)
-        if ast_as_list.isEmpty { break }                                                                                        // malstep(8,9,A)
-        let first = ast_as_list.first()                                                                                         // malstep(8,9,A)
-        if !is_symbol(first) { break }                                                                                          // malstep(8,9,A)
-        let macro_name = first as! MalSymbol                                                                                    // malstep(8,9,A)
-        let obj = env.get(macro_name)                                                                                           // malstep(8,9,A)
-        if obj == nil { break }                                                                                                 // malstep(8,9,A)
-        if !is_closure(obj!) { break }                                                                                          // malstep(8,9,A)
-        let macro = obj! as! MalClosure                                                                                         // malstep(8,9,A)
-        if !macro.is_macro { break }                                                                                            // malstep(8,9,A)
-        var new_env = Environment(outer: macro.env)                                                                             // malstep(8,9,A)
-        let rest = ast_as_list.rest()                                                                                           // malstep(8,9,A)
-        let res = new_env.set_bindings(macro.args, with_exprs:rest)                                                             // malstep(8,9,A)
-        if is_error(res) { return res }                                                                                         // malstep(8,9,A)
-        ast = EVAL(macro.body, new_env)                                                                                         // malstep(8,9,A)
+        if  let ast_as_list = as_listQ(ast) where !ast_as_list.isEmpty,                                                         // malstep(8,9,A)
+            let macro_name = as_symbolQ(ast_as_list.first()),                                                                   // malstep(8,9,A)
+            let obj = env.get(macro_name),                                                                                      // malstep(8,9,A)
+            let macro = as_macroQ(obj)                                                                                          // malstep(8,9,A)
+        {                                                                                                                       // malstep(8,9,A)
+            let new_env = Environment(outer: macro.env)                                                                         // malstep(8,9,A)
+            let rest = as_sequence(ast_as_list.rest())                                                                          // malstep(8,9,A)
+            let _ = try new_env.set_bindings(macro.args, with_exprs: rest)                                                      // malstep(8,9,A)
+            ast = try EVAL(macro.body, new_env)                                                                                 // malstep(8,9,A)
+            continue                                                                                                            // malstep(8,9,A)
+        }                                                                                                                       // malstep(8,9,A)
+        return ast                                                                                                              // malstep(8,9,A)
     }                                                                                                                           // malstep(8,9,A)
-    return ast                                                                                                                  // malstep(8,9,A)
 }                                                                                                                               // malstep(8,9,A)
                                                                                                                                 // malstep(8,9,A)
 // Evaluate `quasiquote`, possibly recursing in the process.                                                                    // malstep(7,8,9,A)
@@ -225,15 +230,15 @@ func macroexpand(var ast:MalVal, env:Environment) -> MalVal {
 // list) with the remaining items of the list containing that splice-quote                                                      // malstep(7,8,9,A)
 // expression. However, it's not clear to me why the handling of "unquote" is                                                   // malstep(7,8,9,A)
 // not handled similarly, for consistency's sake.                                                                               // malstep(7,8,9,A)
-                                                                                                                                // malstep(7,8,9,A)
-func quasiquote(qq_arg:MalVal) -> MalVal {                                                                                      // malstep(7,8,9,A)
+//                                                                                                                              // malstep(7,8,9,A)
+private func quasiquote(qq_arg: MalVal) throws -> MalVal {                                                                      // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
     // If the argument is an atom or empty list:                                                                                // malstep(7,8,9,A)
     //                                                                                                                          // malstep(7,8,9,A)
     // Return: (quote <argument>)                                                                                               // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
     if !is_pair(qq_arg) {                                                                                                       // malstep(7,8,9,A)
-        return MalList(objects: kSymbolQuote, qq_arg)                                                                           // malstep(7,8,9,A)
+        return make_list_from(kValQuote, qq_arg)                                                                                // malstep(7,8,9,A)
     }                                                                                                                           // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
     // The argument is a non-empty list -- that is (item rest...)                                                               // malstep(7,8,9,A)
@@ -243,12 +248,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //                                                                                                                          // malstep(7,8,9,A)
     // Return: item                                                                                                             // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
-    let qq_list = qq_arg as! MalSequence                                                                                        // malstep(7,8,9,A)
-    if is_symbol(qq_list.first()) {                                                                                             // malstep(7,8,9,A)
-        let sym = qq_list.first() as! MalSymbol                                                                                 // malstep(7,8,9,A)
-        if sym == kSymbolUnquote {                                                                                              // malstep(7,8,9,A)
-            return qq_list.count >= 2 ? qq_list[1] : MalNil()                                                                   // malstep(7,8,9,A)
-        }                                                                                                                       // malstep(7,8,9,A)
+    let qq_list = as_sequence(qq_arg)                                                                                           // malstep(7,8,9,A)
+    if let sym = as_symbolQ(qq_list.first()) where sym == kSymbolUnquote {                                                      // malstep(7,8,9,A)
+        return qq_list.count >= 2 ? try! qq_list.nth(1) : make_nil()                                                            // malstep(7,8,9,A)
     }                                                                                                                           // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
     // If the first item from the list is itself a non-empty list starting with                                                 // malstep(7,8,9,A)
@@ -257,14 +259,10 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     // Return: (concat item quasiquote(rest...))                                                                                // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
     if is_pair(qq_list.first()) {                                                                                               // malstep(7,8,9,A)
-        let qq_list_item0 = qq_list.first() as! MalSequence                                                                     // malstep(7,8,9,A)
-        if is_symbol(qq_list_item0.first()) {                                                                                   // malstep(7,8,9,A)
-            let sym = qq_list_item0.first() as! MalSymbol                                                                       // malstep(7,8,9,A)
-            if sym == kSymbolSpliceUnquote {                                                                                    // malstep(7,8,9,A)
-                let result = quasiquote(qq_list.rest())                                                                         // malstep(7,8,9,A)
-                if is_error(result) { return result }                                                                           // malstep(7,8,9,A)
-                return MalList(array: [kSymbolConcat, qq_list_item0[1], result])                                                // malstep(7,8,9,A)
-            }                                                                                                                   // malstep(7,8,9,A)
+        let qq_list_item0 = as_sequence(qq_list.first())                                                                        // malstep(7,8,9,A)
+        if let sym = as_symbolQ(qq_list_item0.first()) where sym == kSymbolSpliceUnquote {                                      // malstep(7,8,9,A)
+            let result = try quasiquote(qq_list.rest())                                                                         // malstep(7,8,9,A)
+            return make_list_from(kValConcat, try! qq_list_item0.nth(1), result)                                                // malstep(7,8,9,A)
         }                                                                                                                       // malstep(7,8,9,A)
     }                                                                                                                           // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
@@ -272,13 +270,9 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
     //                                                                                                                          // malstep(7,8,9,A)
     // Return: (cons (quasiquote item) (quasiquote (rest...))                                                                   // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
-    let first = quasiquote(qq_list.first())                                                                                     // malstep(7,8,9,A)
-    if is_error(first) { return first }                                                                                         // malstep(7,8,9,A)
-                                                                                                                                // malstep(7,8,9,A)
-    let rest = quasiquote(qq_list.rest())                                                                                       // malstep(7,8,9,A)
-    if is_error(rest) { return rest }                                                                                           // malstep(7,8,9,A)
-                                                                                                                                // malstep(7,8,9,A)
-    return MalList(objects: kSymbolCons, first, rest)                                                                           // malstep(7,8,9,A)
+    let first = try quasiquote(qq_list.first())                                                                                 // malstep(7,8,9,A)
+    let rest = try quasiquote(qq_list.rest())                                                                                   // malstep(7,8,9,A)
+    return make_list_from(kValCons, first, rest)                                                                                // malstep(7,8,9,A)
 }                                                                                                                               // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
 // Perform a simple evaluation of the `ast` object. If it's a symbol,                                                           // malstep(2,3,4,5,6,7,8,9,A)
@@ -286,51 +280,45 @@ func quasiquote(qq_arg:MalVal) -> MalVal {
 // elements (or just the values, in the case of the hashmap). Otherwise, return                                                 // malstep(2,3,4,5,6,7,8,9,A)
 // the object unchanged.                                                                                                        // malstep(2,3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(2,3,4,5,6,7,8,9,A)
-func eval_ast(ast: MalVal, env: Environment) -> MalVal {                                                                        // malstep(2,3,4,5,6,7,8,9,A)
-    switch ast.type {                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
-        case .TypeSymbol:                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
-            let symbol = ast as! MalSymbol                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
-            if let val = env.get(symbol) {                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
-                return val                                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
-            }                                                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
-            return MalError(message: "'\(symbol)' not found")    // Specific text needed to match MAL unit tests                // malstep(2,3,4,5,6,7,8,9,A)
-        case .TypeList:                                                                                                         // malstep(2,3,4,5,6,7,8,9,A)
-            let list = ast as! MalList                                                                                          // malstep(2,3,4,5,6,7,8,9,A)
-            var result = [MalVal]()                                                                                             // malstep(2,3,4,5,6,7,8,9,A)
-            result.reserveCapacity(list.count)                                                                                  // malstep(2,3,4,5,6,7,8,9,A)
-            for item in list {                                                                                                  // malstep(2,3,4,5,6,7,8,9,A)
-                let eval = EVAL(item, env)                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
-                if is_error(eval) { return eval }                                                                               // malstep(2,3,4,5,6,7,8,9,A)
-                result.append(eval)                                                                                             // malstep(2,3,4,5,6,7,8,9,A)
-            }                                                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
-            return MalList(array: result)                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
-        case .TypeVector:                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
-            let vec = ast as! MalVector                                                                                         // malstep(2,3,4,5,6,7,8,9,A)
-            var result = [MalVal]()                                                                                             // malstep(2,3,4,5,6,7,8,9,A)
-            result.reserveCapacity(vec.count)                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
-            for item in vec {                                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
-                let eval = EVAL(item, env)                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
-                if is_error(eval) { return eval }                                                                               // malstep(2,3,4,5,6,7,8,9,A)
-                result.append(eval)                                                                                             // malstep(2,3,4,5,6,7,8,9,A)
-            }                                                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
-            return MalVector(array: result)                                                                                     // malstep(2,3,4,5,6,7,8,9,A)
-        case .TypeHashMap:                                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
-            let hash = ast as! MalHashMap                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
-            var result = [MalVal]()                                                                                             // malstep(2,3,4,5,6,7,8,9,A)
-            result.reserveCapacity(hash.count * 2)                                                                              // malstep(2,3,4,5,6,7,8,9,A)
-            for (k, v) in hash {                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
-                let new_v = EVAL(v, env)                                                                                        // malstep(2,3,4,5,6,7,8,9,A)
-                if is_error(new_v) { return new_v }                                                                             // malstep(2,3,4,5,6,7,8,9,A)
-                result.append(k)                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
-                result.append(new_v)                                                                                            // malstep(2,3,4,5,6,7,8,9,A)
-            }                                                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
-            return MalHashMap(array: result)                                                                                    // malstep(2,3,4,5,6,7,8,9,A)
-        default:                                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
-            return ast                                                                                                          // malstep(2,3,4,5,6,7,8,9,A)
+private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {                                                       // malstep(2,3,4,5,6,7,8,9,A)
+    if let symbol = as_symbolQ(ast) {                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+        guard let val = env.get(symbol) else {                                                                                  // malstep(2,3,4,5,6,7,8,9,A)
+            try throw_error("'\(symbol)' not found")    // Specific text needed to match MAL unit tests                         // malstep(2,3,4,5,6,7,8,9,A)
+        }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+        return val                                                                                                              // malstep(2,3,4,5,6,7,8,9,A)
+    }                                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+    if let list = as_listQ(ast) {                                                                                               // malstep(2,3,4,5,6,7,8,9,A)
+        var result = [MalVal]()                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
+        result.reserveCapacity(Int(list.count))                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
+        for item in list {                                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
+            let eval = try EVAL(item, env)                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
+            result.append(eval)                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
+        }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+        return make_list(result)                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
+    }                                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+    if let vec = as_vectorQ(ast) {                                                                                              // malstep(2,3,4,5,6,7,8,9,A)
+        var result = [MalVal]()                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
+        result.reserveCapacity(Int(vec.count))                                                                                  // malstep(2,3,4,5,6,7,8,9,A)
+        for item in vec {                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+            let eval = try EVAL(item, env)                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
+            result.append(eval)                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
+        }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+        return make_vector(result)                                                                                              // malstep(2,3,4,5,6,7,8,9,A)
+    }                                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+    if let hash = as_hashmapQ(ast) {                                                                                            // malstep(2,3,4,5,6,7,8,9,A)
+        var result = [MalVal]()                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
+        result.reserveCapacity(Int(hash.count) * 2)                                                                             // malstep(2,3,4,5,6,7,8,9,A)
+        for (k, v) in hash {                                                                                                    // malstep(2,3,4,5,6,7,8,9,A)
+            let new_v = try EVAL(v, env)                                                                                        // malstep(2,3,4,5,6,7,8,9,A)
+            result.append(k)                                                                                                    // malstep(2,3,4,5,6,7,8,9,A)
+            result.append(new_v)                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
+        }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+        return make_hashmap(result)                                                                                             // malstep(2,3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+    return ast                                                                                                                  // malstep(2,3,4,5,6,7,8,9,A)
 }                                                                                                                               // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-enum TCOVal {                                                                                                                   // malstep(5,6,7,8,9,A)
+private enum TCOVal {                                                                                                           // malstep(5,6,7,8,9,A)
     case NoResult                                                                                                               // malstep(5,6,7,8,9,A)
     case Return(MalVal)                                                                                                         // malstep(5,6,7,8,9,A)
     case Continue(MalVal, Environment)                                                                                          // malstep(5,6,7,8,9,A)
@@ -338,36 +326,29 @@ enum TCOVal {
     init() { self = .NoResult }                                                                                                 // malstep(5,6,7,8,9,A)
     init(_ result: MalVal) { self = .Return(result) }                                                                           // malstep(5,6,7,8,9,A)
     init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) }                                                      // malstep(5,6,7,8,9,A)
-    init(_ e: String) { self = .Return(MalError(message: e)) }                                                                  // malstep(5,6,7,8,9,A)
 }                                                                                                                               // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
 // EVALuate "def!".                                                                                                             // malstep(3,4,5,6,7)
 // EVALuate "def!" and "defmacro!".                                                                                             // malstep(8,9,A)
 //                                                                                                                              // malstep(3,4,5,6,7,8,9,A)
-func eval_def(list: MalSequence, env: Environment) -> MalVal {                                                                  // malstep(3,4)
-func eval_def(list: MalSequence, env: Environment) -> TCOVal {                                                                  // malstep(5,6,7,8,9,A)
-    if list.count != 3 {                                                                                                        // malstep(3,4,5,6,7,8,9,A)
-        return MalError(message: "expected 2 arguments to def!, got \(list.count - 1)")                                         // malstep(3,4)
-        return TCOVal("expected 2 arguments to def!, got \(list.count - 1)")                                                    // malstep(5,6,7,8,9,A)
+private func eval_def(list: MalSequence, _ env: Environment) throws -> MalVal {                                                 // malstep(3,4)
+private func eval_def(list: MalSequence, _ env: Environment) throws -> TCOVal {                                                 // malstep(5,6,7,8,9,A)
+    guard list.count == 3 else {                                                                                                // malstep(3,4,5,6,7,8,9,A)
+        try throw_error("expected 2 arguments to def!, got \(list.count - 1)")                                                  // malstep(3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(3,4,5,6,7,8,9,A)
-    let arg0 = list[0] as! MalSymbol                                                                                            // malstep(8,9,A)
-    let arg1 = list[1]                                                                                                          // malstep(3,4,5,6,7,8,9,A)
-    let arg2 = list[2]                                                                                                          // malstep(3,4,5,6,7,8,9,A)
-    if !is_symbol(arg1) {                                                                                                       // malstep(3,4,5,6,7,8,9,A)
-        return MalError(message: "expected symbol for first argument to def!")                                                  // malstep(3,4)
-        return TCOVal("expected symbol for first argument to def!")                                                             // malstep(5,6,7,8,9,A)
+    let arg0 = try! list.nth(0)                                                                                                 // malstep(8,9,A)
+    let arg1 = try! list.nth(1)                                                                                                 // malstep(3,4,5,6,7,8,9,A)
+    let arg2 = try! list.nth(2)                                                                                                 // malstep(3,4,5,6,7,8,9,A)
+    guard let sym = as_symbolQ(arg1) else {                                                                                     // malstep(3,4,5,6,7,8,9,A)
+        try throw_error("expected symbol for first argument to def!")                                                           // malstep(3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(3,4,5,6,7,8,9,A)
-    let sym = arg1 as! MalSymbol                                                                                                // malstep(3,4,5,6,7,8,9,A)
-    let value = EVAL(arg2, env)                                                                                                 // malstep(3,4,5,6,7,8,9,A)
-    if is_error(value) { return value }                                                                                         // malstep(3,4)
-    if is_error(value) { return TCOVal(value) }                                                                                 // malstep(5,6,7,8,9,A)
-    if arg0 == kSymbolDefMacro {                                                                                                // malstep(8,9,A)
-        if is_closure(value) {                                                                                                  // malstep(8,9,A)
-            let as_closure = value as! MalClosure                                                                               // malstep(8,9,A)
-            as_closure.is_macro = true                                                                                          // malstep(8,9,A)
-        } else {                                                                                                                // malstep(8,9,A)
-            return TCOVal("expected closure, got \(value)")                                                                     // malstep(8,9,A)
+    let value = try EVAL(arg2, env)                                                                                             // malstep(3,4,5,6,7)
+    var value = try EVAL(arg2, env)                                                                                             // malstep(8,9,A)
+    if as_symbol(arg0) == kSymbolDefMacro {                                                                                     // malstep(8,9,A)
+        guard let closure = as_closureQ(value) else {                                                                           // malstep(8,9,A)
+            try throw_error("expected closure, got \(value)")                                                                   // malstep(8,9,A)
         }                                                                                                                       // malstep(8,9,A)
+        value = make_macro(closure)                                                                                             // malstep(8,9,A)
     }                                                                                                                           // malstep(8,9,A)
     return env.set(sym, value)                                                                                                  // malstep(3,4)
     return TCOVal(env.set(sym, value))                                                                                          // malstep(5,6,7,8,9,A)
@@ -375,179 +356,146 @@ func eval_def(list: MalSequence, env: Environment) -> TCOVal {
                                                                                                                                 // malstep(3,4,5,6,7,8,9,A)
 // EVALuate "let*".                                                                                                             // malstep(3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(3,4,5,6,7,8,9,A)
-func eval_let(list: MalSequence, env: Environment) -> MalVal {                                                                  // malstep(3,4)
-func eval_let(list: MalSequence, env: Environment) -> TCOVal {                                                                  // malstep(5,6,7,8,9,A)
-    if list.count != 3 {                                                                                                        // malstep(3,4,5,6,7,8,9,A)
-        return MalError(message: "expected 2 arguments to let*, got \(list.count - 1)")                                         // malstep(3,4)
-        return TCOVal("expected 2 arguments to let*, got \(list.count - 1)")                                                    // malstep(5,6,7,8,9,A)
+private func eval_let(list: MalSequence, _ env: Environment) throws -> MalVal {                                                 // malstep(3,4)
+private func eval_let(list: MalSequence, _ env: Environment) throws -> TCOVal {                                                 // malstep(5,6,7,8,9,A)
+    guard list.count == 3 else {                                                                                                // malstep(3,4,5,6,7,8,9,A)
+        try throw_error("expected 2 arguments to let*, got \(list.count - 1)")                                                  // malstep(3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(3,4,5,6,7,8,9,A)
-    let arg1 = list[1]                                                                                                          // malstep(3,4,5,6,7,8,9,A)
-    let arg2 = list[2]                                                                                                          // malstep(3,4,5,6,7,8,9,A)
-    if !is_sequence(arg1) {                                                                                                     // malstep(3,4,5,6,7,8,9,A)
-        return MalError(message: "expected list for first argument to let*")                                                    // malstep(3,4)
-        return TCOVal("expected list for first argument to let*")                                                               // malstep(5,6,7,8,9,A)
+    let arg1 = try! list.nth(1)                                                                                                 // malstep(3,4,5,6,7,8,9,A)
+    let arg2 = try! list.nth(2)                                                                                                 // malstep(3,4,5,6,7,8,9,A)
+    guard let bindings = as_sequenceQ(arg1) else {                                                                              // malstep(3,4,5,6,7,8,9,A)
+        try throw_error("expected list for first argument to let*")                                                             // malstep(3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(3,4,5,6,7,8,9,A)
-    let bindings = arg1 as! MalSequence                                                                                         // malstep(3,4,5,6,7,8,9,A)
-    if bindings.count % 2 == 1 {                                                                                                // malstep(3,4,5,6,7,8,9,A)
-        return MalError(message: "expected even number of elements in bindings to let*, got \(bindings.count)")                 // malstep(3,4)
-        return TCOVal("expected even number of elements in bindings to let*, got \(bindings.count)")                            // malstep(5,6,7,8,9,A)
+    guard bindings.count % 2 == 0 else {                                                                                        // malstep(3,4,5,6,7,8,9,A)
+        try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)")                          // malstep(3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(3,4,5,6,7,8,9,A)
-    var new_env = Environment(outer: env)                                                                                       // malstep(3,4,5,6,7,8,9,A)
-    for var index = 0; index < bindings.count; index += 2 {                                                                     // malstep(3,4,5,6,7,8,9,A)
-        let binding_name = bindings[index]                                                                                      // malstep(3,4,5,6,7,8,9,A)
-        let binding_value = bindings[index + 1]                                                                                 // malstep(3,4,5,6,7,8,9,A)
-                                                                                                                                // malstep(3,4,5,6,7,8,9,A)
-        if !is_symbol(binding_name) {                                                                                           // malstep(3,4,5,6,7,8,9,A)
-            return MalError(message: "expected symbol for first element in binding pair")                                       // malstep(3,4)
-            return TCOVal("expected symbol for first element in binding pair")                                                  // malstep(5,6,7,8,9,A)
+    let new_env = Environment(outer: env)                                                                                       // malstep(3,4,5,6,7,8,9,A)
+    for var index: MalIntType = 0; index < bindings.count; index += 2 {                                                         // malstep(3,4,5,6,7,8,9,A)
+        let binding_name = try! bindings.nth(index)                                                                             // malstep(3,4,5,6,7,8,9,A)
+        let binding_value = try! bindings.nth(index + 1)                                                                        // malstep(3,4,5,6,7,8,9,A)
+        guard let binding_symbol = as_symbolQ(binding_name) else {                                                              // malstep(3,4,5,6,7,8,9,A)
+            try throw_error("expected symbol for first element in binding pair")                                                // malstep(3,4,5,6,7,8,9,A)
         }                                                                                                                       // malstep(3,4,5,6,7,8,9,A)
-        let binding_symbol = binding_name as! MalSymbol                                                                         // malstep(3,4,5,6,7,8,9,A)
-        let evaluated_value = EVAL(binding_value, new_env)                                                                      // malstep(3,4,5,6,7,8,9,A)
-        if is_error(evaluated_value) { return evaluated_value }                                                                 // malstep(3,4)
-        if is_error(evaluated_value) { return TCOVal(evaluated_value) }                                                         // malstep(5,6,7,8,9,A)
+        let evaluated_value = try EVAL(binding_value, new_env)                                                                  // malstep(3,4,5,6,7,8,9,A)
         new_env.set(binding_symbol, evaluated_value)                                                                            // malstep(3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(3,4,5,6,7,8,9,A)
     if TCO {                                                                                                                    // malstep(5,6,7,8,9,A)
         return TCOVal(arg2, new_env)                                                                                            // malstep(5,6,7,8,9,A)
     }                                                                                                                           // malstep(5,6,7,8,9,A)
-    return EVAL(arg2, new_env)                                                                                                  // malstep(3,4)
-    return TCOVal(EVAL(arg2, new_env))                                                                                          // malstep(5,6,7,8,9,A)
+    return try EVAL(arg2, new_env)                                                                                              // malstep(3,4)
+    return TCOVal(try EVAL(arg2, new_env))                                                                                      // malstep(5,6,7,8,9,A)
 }                                                                                                                               // malstep(3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(3,4,5,6,7,8,9,A)
 // EVALuate "do".                                                                                                               // malstep(4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(4,5,6,7,8,9,A)
-func eval_do(list: MalSequence, env: Environment) -> MalVal {                                                                   // malstep(4)
-func eval_do(list: MalSequence, env: Environment) -> TCOVal {                                                                   // malstep(5,6,7,8,9,A)
+private func eval_do(list: MalSequence, _ env: Environment) throws -> MalVal {                                                  // malstep(4)
+private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal {                                                  // malstep(5,6,7,8,9,A)
     if TCO {                                                                                                                    // malstep(5,6,7,8,9,A)
-        let eval = eval_ast(MalList(slice: list[1..<list.count-1]), env)                                                        // malstep(5,6,7,8,9,A)
-        if is_error(eval) { return TCOVal(eval) }                                                                               // malstep(5,6,7,8,9,A)
+        let _ = try eval_ast(list.range_from(1, to: list.count-1), env)                                                         // malstep(5,6,7,8,9,A)
         return TCOVal(list.last(), env)                                                                                         // malstep(5,6,7,8,9,A)
     }                                                                                                                           // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
-    let evaluated_ast = eval_ast(list.rest(), env)                                                                              // malstep(4,5,6,7,8,9,A)
-    if is_error(evaluated_ast) { return evaluated_ast }                                                                         // malstep(4)
-    if is_error(evaluated_ast) { return TCOVal(evaluated_ast) }                                                                 // malstep(5,6,7,8,9,A)
-    let evaluated_seq = evaluated_ast as! MalSequence                                                                           // malstep(4,5,6,7,8,9,A)
+    let evaluated_ast = try eval_ast(list.rest(), env)                                                                          // malstep(4,5,6,7,8,9,A)
+    let evaluated_seq = as_sequence(evaluated_ast)                                                                              // malstep(4,5,6,7,8,9,A)
     return evaluated_seq.last()                                                                                                 // malstep(4)
     return TCOVal(evaluated_seq.last())                                                                                         // malstep(5,6,7,8,9,A)
 }                                                                                                                               // malstep(4,5,6,7,8,9,A)
                                                                                                                                 // malstep(4,5,6,7,8,9,A)
 // EVALuate "if".                                                                                                               // malstep(4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(4,5,6,7,8,9,A)
-func eval_if(list: MalSequence, env: Environment) -> MalVal {                                                                   // malstep(4)
-func eval_if(list: MalSequence, env: Environment) -> TCOVal {                                                                   // malstep(5,6,7,8,9,A)
-    if list.count < 3 {                                                                                                         // malstep(4,5,6,7,8,9,A)
-        return MalError(message: "expected at least 2 arguments to if, got \(list.count - 1)")                                  // malstep(4)
-        return TCOVal("expected at least 2 arguments to if, got \(list.count - 1)")                                             // malstep(5,6,7,8,9,A)
+private func eval_if(list: MalSequence, _ env: Environment) throws -> MalVal {                                                  // malstep(4)
+private func eval_if(list: MalSequence, _ env: Environment) throws -> TCOVal {                                                  // malstep(5,6,7,8,9,A)
+    guard list.count >= 3 else {                                                                                                // malstep(4,5,6,7,8,9,A)
+        try throw_error("expected at least 2 arguments to if, got \(list.count - 1)")                                           // malstep(4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(4,5,6,7,8,9,A)
-    let cond_result = EVAL(list[1], env)                                                                                        // malstep(4,5,6,7,8,9,A)
-    var new_ast = MalVal()                                                                                                      // malstep(4,5,6,7,8,9,A)
+    let cond_result = try EVAL(try! list.nth(1), env)                                                                           // malstep(4,5,6,7,8,9,A)
+    var new_ast: MalVal                                                                                                         // malstep(4,5,6,7,8,9,A)
     if is_truthy(cond_result) {                                                                                                 // malstep(4,5,6,7,8,9,A)
-        new_ast = list[2]                                                                                                       // malstep(4,5,6,7,8,9,A)
+        new_ast = try! list.nth(2)                                                                                              // malstep(4,5,6,7,8,9,A)
     } else if list.count == 4 {                                                                                                 // malstep(4,5,6,7,8,9,A)
-        new_ast = list[3]                                                                                                       // malstep(4,5,6,7,8,9,A)
+        new_ast = try! list.nth(3)                                                                                              // malstep(4,5,6,7,8,9,A)
     } else {                                                                                                                    // malstep(4,5,6,7,8,9,A)
-        return MalNil()                                                                                                         // malstep(4)
-        return TCOVal(MalNil())                                                                                                 // malstep(5,6,7,8,9,A)
+        return make_nil()                                                                                                       // malstep(4)
+        return TCOVal(make_nil())                                                                                               // malstep(5,6,7,8,9,A)
     }                                                                                                                           // malstep(4,5,6,7,8,9,A)
     if TCO {                                                                                                                    // malstep(5,6,7,8,9,A)
         return TCOVal(new_ast, env)                                                                                             // malstep(5,6,7,8,9,A)
     }                                                                                                                           // malstep(5,6,7,8,9,A)
-    return EVAL(new_ast, env)                                                                                                   // malstep(4)
-    return TCOVal(EVAL(new_ast, env))                                                                                           // malstep(5,6,7,8,9,A)
+    return try EVAL(new_ast, env)                                                                                               // malstep(4)
+    return TCOVal(try EVAL(new_ast, env))                                                                                       // malstep(5,6,7,8,9,A)
 }                                                                                                                               // malstep(4,5,6,7,8,9,A)
                                                                                                                                 // malstep(4,5,6,7,8,9,A)
 // EVALuate "fn*".                                                                                                              // malstep(4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(4,5,6,7,8,9,A)
-func eval_fn(list: MalSequence, env: Environment) -> MalVal {                                                                   // malstep(4)
-func eval_fn(list: MalSequence, env: Environment) -> TCOVal {                                                                   // malstep(5,6,7,8,9,A)
-    if list.count != 3 {                                                                                                        // malstep(4,5,6,7,8,9,A)
-        return MalError(message: "expected 2 arguments to fn*, got \(list.count - 1)")                                          // malstep(4)
-        return TCOVal("expected 2 arguments to fn*, got \(list.count - 1)")                                                     // malstep(5,6,7,8,9,A)
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> MalVal {                                                  // malstep(4)
+private func eval_fn(list: MalSequence, _ env: Environment) throws -> TCOVal {                                                  // malstep(5,6,7,8,9,A)
+    guard list.count == 3 else {                                                                                                // malstep(4,5,6,7,8,9,A)
+        try throw_error("expected 2 arguments to fn*, got \(list.count - 1)")                                                   // malstep(4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(4,5,6,7,8,9,A)
-    if !is_sequence(list[1]) {                                                                                                  // malstep(4,5,6,7,8,9,A)
-        return MalError(message: "expected list or vector for first argument to fn*")                                           // malstep(4)
-        return TCOVal("expected list or vector for first argument to fn*")                                                      // malstep(5,6,7,8,9,A)
+    guard let seq = as_sequenceQ(try! list.nth(1)) else {                                                                       // malstep(4,5,6,7,8,9,A)
+        try throw_error("expected list or vector for first argument to fn*")                                                    // malstep(4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(4,5,6,7,8,9,A)
-    return MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env)                                          // malstep(4)
-    return TCOVal(MalClosure(eval: EVAL, args:list[1] as! MalSequence, body:list[2], env:env))                                  // malstep(5,6,7,8,9,A)
+    return make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env))                                              // malstep(4)
+    return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env)))                                      // malstep(5,6,7,8,9,A)
 }                                                                                                                               // malstep(4,5,6,7,8,9,A)
                                                                                                                                 // malstep(4,5,6,7,8,9,A)
 // EVALuate "quote".                                                                                                            // malstep(7,8,9,A)
 //                                                                                                                              // malstep(7,8,9,A)
-func eval_quote(list: MalSequence, env: Environment) -> TCOVal {                                                                // malstep(7,8,9,A)
+private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal {                                               // malstep(7,8,9,A)
     if list.count >= 2 {                                                                                                        // malstep(7,8,9,A)
-        return TCOVal(list[1])                                                                                                  // malstep(7,8,9,A)
+        return TCOVal(try! list.nth(1))                                                                                         // malstep(7,8,9,A)
     }                                                                                                                           // malstep(7,8,9,A)
-    return TCOVal(MalNil())                                                                                                     // malstep(7,8,9,A)
+    return TCOVal(make_nil())                                                                                                   // malstep(7,8,9,A)
 }                                                                                                                               // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
 // EVALuate "quasiquote".                                                                                                       // malstep(7,8,9,A)
 //                                                                                                                              // malstep(7,8,9,A)
-func eval_quasiquote(list: MalSequence, env: Environment) -> TCOVal {                                                           // malstep(7,8,9,A)
-    if list.count >= 2 {                                                                                                        // malstep(7,8,9,A)
-        if TCO {                                                                                                                // malstep(7,8,9,A)
-            return TCOVal(quasiquote(list[1]), env)                                                                             // malstep(7,8,9,A)
-        }                                                                                                                       // malstep(7,8,9,A)
-        return TCOVal(EVAL(quasiquote(list[1]), env))                                                                           // malstep(7,8,9,A)
+private func eval_quasiquote(list: MalSequence, _ env: Environment) throws -> TCOVal {                                          // malstep(7,8,9,A)
+    guard list.count >= 2 else {                                                                                                // malstep(7,8,9,A)
+        try throw_error("Expected non-nil parameter to 'quasiquote'")                                                           // malstep(7,8,9,A)
+    }                                                                                                                           // malstep(7,8,9,A)
+    if TCO {                                                                                                                    // malstep(7,8,9,A)
+        return TCOVal(try quasiquote(try! list.nth(1)), env)                                                                    // malstep(7,8,9,A)
     }                                                                                                                           // malstep(7,8,9,A)
-    return TCOVal("Expected non-nil parameter to 'quasiquote'")                                                                 // malstep(7,8,9,A)
+    return TCOVal(try EVAL(try quasiquote(try! list.nth(1)), env))                                                              // malstep(7,8,9,A)
 }                                                                                                                               // malstep(7,8,9,A)
                                                                                                                                 // malstep(7,8,9,A)
 // EVALuate "macroexpand".                                                                                                      // malstep(8,9,A)
 //                                                                                                                              // malstep(8,9,A)
-func eval_macroexpand(list: MalSequence, env: Environment) -> TCOVal {                                                          // malstep(8,9,A)
-    if list.count >= 2 {                                                                                                        // malstep(8,9,A)
-        return TCOVal(macroexpand(list[1], env))                                                                                // malstep(8,9,A)
+private func eval_macroexpand(list: MalSequence, _ env: Environment) throws -> TCOVal {                                         // malstep(8,9,A)
+    guard list.count >= 2 else {                                                                                                // malstep(8,9,A)
+        try throw_error("Expected parameter to 'macroexpand'")                                                                  // malstep(8,9,A)
     }                                                                                                                           // malstep(8,9,A)
-    return TCOVal("Expected parameter to 'macroexpand'")                                                                        // malstep(8,9,A)
+    return TCOVal(try macroexpand(try! list.nth(1), env))                                                                       // malstep(8,9,A)
 }                                                                                                                               // malstep(8,9,A)
                                                                                                                                 // malstep(8,9,A)
 // EVALuate "try*" (and "catch*").                                                                                              // malstep(9,A)
 //                                                                                                                              // malstep(9,A)
-func eval_try(list: MalSequence, env: Environment) -> TCOVal {                                                                  // malstep(9,A)
+private func eval_try(list: MalSequence, _ env: Environment) throws -> TCOVal {                                                 // malstep(9,A)
     // This is a subset of the Clojure try/catch:                                                                               // malstep(9,A)
     //                                                                                                                          // malstep(9,A)
     //      (try* expr (catch exception-name expr))                                                                             // malstep(9,A)
                                                                                                                                 // malstep(9,A)
-    if list.count < 2 {                                                                                                         // malstep(9,A)
-        println("try*: no body parameter")                                                                                      // malstep(9,A)
-        return TCOVal(MalNil()) // No body parameter                                                                            // malstep(9,A)
+    guard list.count >= 2 else {                                                                                                // malstep(9,A)
+        try throw_error("try*: no body parameter")                                                                              // malstep(9,A)
     }                                                                                                                           // malstep(9,A)
-    let res = EVAL(list[1], env)                                                                                                // malstep(9,A)
-    if !is_error(res) { return TCOVal(res) }                                                                                    // malstep(9,A)
-    if list.count < 3 {                                                                                                         // malstep(9,A)
-        println("try*: no catch parameter")                                                                                     // malstep(9,A)
-        return TCOVal(MalNil()) // No catch parameter                                                                           // malstep(9,A)
-    }                                                                                                                           // malstep(9,A)
-    if !is_sequence(list[2]) {                                                                                                  // malstep(9,A)
-        println("try*: second parameter to 'try' is not a sequence")                                                            // malstep(9,A)
-        return TCOVal(MalNil()) // Second parameter to 'try' is not a sequence                                                  // malstep(9,A)
-    }                                                                                                                           // malstep(9,A)
-    let catch_list = list[2] as! MalSequence                                                                                    // malstep(9,A)
-    if catch_list.count < 3 {                                                                                                   // malstep(9,A)
-        println("try*: not enough catch parameters")                                                                            // malstep(9,A)
-        return TCOVal(MalNil()) // Not enough catch parameters                                                                  // malstep(9,A)
-    }                                                                                                                           // malstep(9,A)
-    if !is_symbol(catch_list[0]) {                                                                                              // malstep(9,A)
-        println("try*: first parameter in catch list is not a symbol")                                                          // malstep(9,A)
-        return TCOVal(MalNil()) // First parameter in catch list is not a symbol                                                // malstep(9,A)
-    }                                                                                                                           // malstep(9,A)
-    let catch_symbol = catch_list[0] as! MalSymbol                                                                              // malstep(9,A)
-    if catch_symbol != kSymbolCatch {                                                                                           // malstep(9,A)
-        println("try*: first parameter in catch list is not 'catch'")                                                           // malstep(9,A)
-        return TCOVal(MalNil()) // First parameter in catch list is not 'catch'                                                 // malstep(9,A)
-    }                                                                                                                           // malstep(9,A)
-    if !is_symbol(catch_list[1]) {                                                                                              // malstep(9,A)
-        println("try*: first parameter to 'catch' is not a symbol")                                                             // malstep(9,A)
-        return TCOVal(MalNil()) // First parameter to 'catch' is not a symbol                                                   // malstep(9,A)
+                                                                                                                                // malstep(9,A)
+    do {                                                                                                                        // malstep(9,A)
+        return TCOVal(try EVAL(try! list.nth(1), env))                                                                          // malstep(9,A)
+    } catch let error as MalException {                                                                                         // malstep(9,A)
+        guard list.count >= 3,                                                                                                  // malstep(9,A)
+            let catch_list = as_sequenceQ(try! list.nth(2)) where catch_list.count >= 3,                                        // malstep(9,A)
+            let _ = as_symbolQ(try! catch_list.nth(0)) else                                                                     // malstep(9,A)
+        {                                                                                                                       // malstep(9,A)
+            throw error // No catch parameter                                                                                   // malstep(9,A)
+        }                                                                                                                       // malstep(9,A)
+        let catch_name = try! catch_list.nth(1)                                                                                 // malstep(9,A)
+        let catch_expr = try! catch_list.nth(2)                                                                                 // malstep(9,A)
+        let catch_env = Environment(outer: env)                                                                                 // malstep(9,A)
+        try catch_env.set_bindings(as_sequence(make_list_from(catch_name)),                                                     // malstep(9,A)
+                with_exprs: as_sequence(make_list_from(error.exception)))                                                       // malstep(9,A)
+        return TCOVal(try EVAL(catch_expr, catch_env))                                                                          // malstep(9,A)
     }                                                                                                                           // malstep(9,A)
-    let catch_name = catch_list[1] as! MalSymbol                                                                                // malstep(9,A)
-    let catch_expr = catch_list[2]                                                                                              // malstep(9,A)
-    let catch_env = Environment(outer: env)                                                                                     // malstep(9,A)
-    let error = res as! MalError                                                                                                // malstep(9,A)
-    catch_env.set_bindings(MalList(objects: catch_name), with_exprs: MalList(objects: error.value))                             // malstep(9,A)
-    return TCOVal(EVAL(catch_expr, catch_env))                                                                                  // malstep(9,A)
 }                                                                                                                               // malstep(9,A)
                                                                                                                                 // malstep(9,A)
 //
@@ -560,73 +508,73 @@ func eval_try(list: MalSequence, env: Environment) -> TCOVal {
 // Walk the AST and completely evaluate it, handling macro expansions, special                                                  // malstep(0,1,2,3,4,5,6,7,8,9,A)
 // forms and function calls.                                                                                                    // malstep(0,1,2,3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(0,1,2,3,4,5,6,7,8,9,A)
-func EVAL(ast: String) -> String {                                                                                              // malstep(0)
+private func EVAL(ast: String) -> String {                                                                                      // malstep(0)
     return ast                                                                                                                  // malstep(0)
 }                                                                                                                               // malstep(0)
-func EVAL(ast: MalVal) -> MalVal {                                                                                              // malstep(1)
-    if is_error(ast) { return ast }                                                                                             // malstep(1)
+private func EVAL(ast: MalVal) -> MalVal {                                                                                      // malstep(1)
     return ast                                                                                                                  // malstep(1)
 }                                                                                                                               // malstep(1)
-func EVAL(var ast: MalVal, var env: Environment) -> MalVal {                                                                    // malstep(2,3,4,5,6,7,8,9,A)
-    let x = EVAL_Counter()                                                                                                      // malstep(5,6,7,8,9,A)
-    if EVAL_level > EVAL_leval_max {                                                                                            // malstep(5,6,7,8,9,A)
-        return MalError(message: "Recursing too many levels (> \(EVAL_leval_max))")                                             // malstep(5,6,7,8,9,A)
+private func EVAL(ast: MalVal, _ env: Environment) throws -> MalVal {                                                           // malstep(2,3,4)
+private func EVAL(var ast: MalVal, var _ env: Environment) throws -> MalVal {                                                   // malstep(5,6,7,8,9,A)
+    EVAL_level++                                                                                                                // malstep(5,6,7,8,9,A)
+    defer { EVAL_level-- }                                                                                                      // malstep(5,6,7,8,9,A)
+    guard EVAL_level <= EVAL_leval_max else {                                                                                   // malstep(5,6,7,8,9,A)
+        try throw_error("Recursing too many levels (> \(EVAL_leval_max))")                                                      // malstep(5,6,7,8,9,A)
     }                                                                                                                           // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
     if DEBUG_EVAL {                                                                                                             // malstep(5,6,7,8,9,A)
-        indent = prefix(INDENT_TEMPLATE, EVAL_level)                                                                            // malstep(5,6,7,8,9,A)
+        indent = substring(INDENT_TEMPLATE, 0, EVAL_level)                                                                      // malstep(5,6,7,8,9,A)
     }                                                                                                                           // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
     while true {                                                                                                                // malstep(5,6,7,8,9,A)
-        if is_error(ast) { return ast }                                                                                         // malstep(2,3,4,5,6,7,8,9,A)
-        if DEBUG_EVAL { println("\(indent)>   \(ast)") }                                                                        // malstep(5,6,7,8,9,A)
+        if DEBUG_EVAL { print("\(indent)>   \(ast)") }                                                                          // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
         if !is_list(ast) {                                                                                                      // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
             // Not a list -- just evaluate and return.                                                                          // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-            let answer = eval_ast(ast, env)                                                                                     // malstep(2,3,4,5,6,7,8,9,A)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }                                                                 // malstep(5,6,7,8,9,A)
+            let answer = try eval_ast(ast, env)                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }                                                                   // malstep(5,6,7,8,9,A)
             return answer                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
         }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
         // Special handling if it's a list.                                                                                     // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-        var list = ast as! MalList                                                                                              // malstep(2,3,4,5,6,7,8,9,A)
-        ast = macroexpand(ast, env)                                                                                             // malstep(8,9,A)
+        let list = as_list(ast)                                                                                                 // malstep(2,3,4,5,6,7)
+        var list = as_list(ast)                                                                                                 // malstep(8,9,A)
+        ast = try macroexpand(ast, env)                                                                                         // malstep(8,9,A)
         if !is_list(ast) { return ast }                                                                                         // malstep(8,9,A)
-        list = ast as! MalList                                                                                                  // malstep(8,9,A)
+        list = as_list(ast)                                                                                                     // malstep(8,9,A)
                                                                                                                                 // malstep(8,9,A)
-        if DEBUG_EVAL { println("\(indent)>.  \(list)") }                                                                       // malstep(5,6,7,8,9,A)
+        if DEBUG_EVAL { print("\(indent)>.  \(list)") }                                                                         // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
         if list.isEmpty {                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
-            return list                                                                                                         // malstep(2,3,4,5,6,7,8,9,A)
+            return ast                                                                                                          // malstep(2,3,4,5,6,7,8,9,A)
         }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
         // Check for special forms, where we want to check the operation                                                        // malstep(3,4,5,6,7,8,9,A)
         // before evaluating all of the parameters.                                                                             // malstep(3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(3,4,5,6,7,8,9,A)
         let arg0 = list.first()                                                                                                 // malstep(3,4,5,6,7,8,9,A)
-        if is_symbol(arg0) {                                                                                                    // malstep(3,4,5,6,7,8,9,A)
-            var res: TCOVal                                                                                                     // malstep(5,6,7,8,9,A)
-            let fn_symbol = arg0 as! MalSymbol                                                                                  // malstep(3,4,5,6,7,8,9,A)
+        if let fn_symbol = as_symbolQ(arg0) {                                                                                   // malstep(3,4,5,6,7,8,9,A)
+            let res: TCOVal                                                                                                     // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(3,4,5,6,7,8,9,A)
             switch fn_symbol {                                                                                                  // malstep(3,4,5,6,7,8,9,A)
-                case kSymbolDef:            return eval_def(list, env)                                                          // malstep(3,4)
-                case kSymbolDef:            res = eval_def(list, env)                                                           // malstep(5,6,7,8,9,A)
-                case kSymbolDefMacro:       res = eval_def(list, env)                                                           // malstep(8,9,A)
-                case kSymbolLet:            return eval_let(list, env)                                                          // malstep(3,4)
-                case kSymbolLet:            res = eval_let(list, env)                                                           // malstep(5,6,7,8,9,A)
-                case kSymbolDo:             return eval_do(list, env)                                                           // malstep(4)
-                case kSymbolDo:             res = eval_do(list, env)                                                            // malstep(5,6,7,8,9,A)
-                case kSymbolIf:             return eval_if(list, env)                                                           // malstep(4)
-                case kSymbolIf:             res = eval_if(list, env)                                                            // malstep(5,6,7,8,9,A)
-                case kSymbolFn:             return eval_fn(list, env)                                                           // malstep(4)
-                case kSymbolFn:             res = eval_fn(list, env)                                                            // malstep(5,6,7,8,9,A)
-                case kSymbolQuote:          res = eval_quote(list, env)                                                         // malstep(7,8,9,A)
-                case kSymbolQuasiQuote:     res = eval_quasiquote(list, env)                                                    // malstep(7,8,9,A)
-                case kSymbolMacroExpand:    res = eval_macroexpand(list, env)                                                   // malstep(8,9,A)
-                case kSymbolTry:            res = eval_try(list, env)                                                           // malstep(9,A)
+                case kSymbolDef:            return try eval_def(list, env)                                                      // malstep(3,4)
+                case kSymbolDef:            res = try eval_def(list, env)                                                       // malstep(5,6,7,8,9,A)
+                case kSymbolDefMacro:       res = try eval_def(list, env)                                                       // malstep(8,9,A)
+                case kSymbolLet:            return try eval_let(list, env)                                                      // malstep(3,4)
+                case kSymbolLet:            res = try eval_let(list, env)                                                       // malstep(5,6,7,8,9,A)
+                case kSymbolDo:             return try eval_do(list, env)                                                       // malstep(4)
+                case kSymbolDo:             res = try eval_do(list, env)                                                        // malstep(5,6,7,8,9,A)
+                case kSymbolIf:             return try eval_if(list, env)                                                       // malstep(4)
+                case kSymbolIf:             res = try eval_if(list, env)                                                        // malstep(5,6,7,8,9,A)
+                case kSymbolFn:             return try eval_fn(list, env)                                                       // malstep(4)
+                case kSymbolFn:             res = try eval_fn(list, env)                                                        // malstep(5,6,7,8,9,A)
+                case kSymbolQuote:          res = try eval_quote(list, env)                                                     // malstep(7,8,9,A)
+                case kSymbolQuasiQuote:     res = try eval_quasiquote(list, env)                                                // malstep(7,8,9,A)
+                case kSymbolMacroExpand:    res = try eval_macroexpand(list, env)                                               // malstep(8,9,A)
+                case kSymbolTry:            res = try eval_try(list, env)                                                       // malstep(9,A)
                 default:                    break                                                                               // malstep(3,4)
                 default:                    res = TCOVal()                                                                      // malstep(5,6,7,8,9,A)
             }                                                                                                                   // malstep(3,4,5,6,7,8,9,A)
@@ -639,57 +587,52 @@ func EVAL(var ast: MalVal, var env: Environment) -> MalVal {
                                                                                                                                 // malstep(3,4,5,6,7,8,9,A)
         // Standard list to be applied. Evaluate all the elements first.                                                        // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-        let eval = eval_ast(ast, env)                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
-        if is_error(eval) { return eval }                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+        let eval = try eval_ast(ast, env)                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
         // The result had better be a list and better be non-empty.                                                             // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-        let eval_list = eval as! MalList                                                                                        // malstep(2,3,4,5,6,7,8,9,A)
+        let eval_list = as_list(eval)                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
         if eval_list.isEmpty {                                                                                                  // malstep(2,3,4,5,6,7,8,9,A)
-            return eval_list                                                                                                    // malstep(2,3,4,5,6,7,8,9,A)
+            return eval                                                                                                         // malstep(2,3,4,5,6,7,8,9,A)
         }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-        if DEBUG_EVAL { println("\(indent)>>  \(eval)") }                                                                       // malstep(5,6,7,8,9,A)
+        if DEBUG_EVAL { print("\(indent)>>  \(eval)") }                                                                         // malstep(5,6,7,8,9,A)
                                                                                                                                 // malstep(5,6,7,8,9,A)
         // Get the first element of the list and execute it.                                                                    // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
         let first = eval_list.first()                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
-        let rest = eval_list.rest()                                                                                             // malstep(2,3,4,5,6,7,8,9,A)
+        let rest = as_sequence(eval_list.rest())                                                                                // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-        if is_builtin(first) {                                                                                                  // malstep(2,3,4,5,6,7,8,9,A)
-            let fn = first as! MalBuiltin                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
-            let answer = fn.apply(rest)                                                                                         // malstep(2,3,4,5,6,7,8,9,A)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }                                                                 // malstep(5,6,7,8,9,A)
+        if let fn = as_builtinQ(first) {                                                                                        // malstep(2,3,4,5,6,7,8,9,A)
+            let answer = try fn.apply(rest)                                                                                     // malstep(2,3,4,5,6,7,8,9,A)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }                                                                   // malstep(5,6,7,8,9,A)
             return answer                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
-        } else if is_closure(first) {                                                                                           // malstep(4,5,6,7,8,9,A)
-            let fn = first as! MalClosure                                                                                       // malstep(4,5,6,7,8,9,A)
-            var new_env = Environment(outer: fn.env)                                                                            // malstep(4,5,6,7,8,9,A)
-            let result = new_env.set_bindings(fn.args, with_exprs:rest)                                                         // malstep(4,5,6,7,8,9,A)
-            if is_error(result) { return result }                                                                               // malstep(4,5,6,7,8,9,A)
+        } else if let fn = as_closureQ(first) {                                                                                 // malstep(4,5,6,7,8,9,A)
+            let new_env = Environment(outer: fn.env)                                                                            // malstep(4,5,6,7,8,9,A)
+            let _ = try new_env.set_bindings(fn.args, with_exprs: rest)                                                         // malstep(4,5,6,7,8,9,A)
             if TCO {                                                                                                            // malstep(5,6,7,8,9,A)
                 env = new_env                                                                                                   // malstep(5,6,7,8,9,A)
                 ast = fn.body                                                                                                   // malstep(5,6,7,8,9,A)
                 continue                                                                                                        // malstep(5,6,7,8,9,A)
             }                                                                                                                   // malstep(5,6,7,8,9,A)
-            let answer = EVAL(fn.body, new_env)                                                                                 // malstep(4,5,6,7,8,9,A)
-            if DEBUG_EVAL { println("\(indent)>>> \(answer)") }                                                                 // malstep(5,6,7,8,9,A)
+            let answer = try EVAL(fn.body, new_env)                                                                             // malstep(4,5,6,7,8,9,A)
+            if DEBUG_EVAL { print("\(indent)>>> \(answer)") }                                                                   // malstep(5,6,7,8,9,A)
             return answer                                                                                                       // malstep(4,5,6,7,8,9,A)
         }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
         // The first element wasn't a function to be executed. Return an                                                        // malstep(2,3,4,5,6,7,8,9,A)
         // error saying so.                                                                                                     // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-        return MalError(message: "first list item does not evaluate to a function: \(first)")                                   // malstep(2,3,4,5,6,7,8,9,A)
+        try throw_error("first list item does not evaluate to a function: \(first)")                                            // malstep(2,3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(5,6,7,8,9,A)
 }                                                                                                                               // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(0,1,2,3,4,5,6,7,8,9,A)
 // Convert the value into a human-readable string for printing.                                                                 // malstep(0,1,2,3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(0,1,2,3,4,5,6,7,8,9,A)
-func PRINT(exp: String) -> String {                                                                                             // malstep(0)
+private func PRINT(exp: String) -> String {                                                                                     // malstep(0)
     return exp                                                                                                                  // malstep(0)
 }                                                                                                                               // malstep(0)
-func PRINT(exp: MalVal) -> String? {                                                                                            // malstep(1,2,3,4,5,6,7,8,9,A)
-    if is_error(exp) { return nil }                                                                                             // malstep(1,2,3,4,5,6,7,8,9,A)
+private func PRINT(exp: MalVal) -> String {                                                                                     // malstep(1,2,3,4,5,6,7,8,9,A)
     return pr_str(exp, true)                                                                                                    // malstep(1,2,3,4,5,6,7,8,9,A)
 }                                                                                                                               // malstep(1,2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(0,1,2,3,4,5,6,7,8,9,A)
@@ -701,26 +644,31 @@ func PRINT(exp: MalVal) -> String? {
 // Perform the READ and EVAL steps. Useful for when you don't care about the                                                    // malstep(0,1,2,3,4,5,6,7,8,9,A)
 // printable result.                                                                                                            // malstep(0,1,2,3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(0,1,2,3,4,5,6,7,8,9,A)
-func RE(text: String) -> String {                                                                                               // malstep(0)
+private func RE(text: String) -> String {                                                                                       // malstep(0)
     let ast = READ(text)                                                                                                        // malstep(0)
     let exp = EVAL(ast)                                                                                                         // malstep(0)
     return exp                                                                                                                  // malstep(0)
 }                                                                                                                               // malstep(0)
-func RE(text: String) -> MalVal? {                                                                                              // malstep(1)
-func RE(text: String, env: Environment) -> MalVal? {                                                                            // malstep(2,3,4,5,6,7,8,9,A)
-    if text.isEmpty { return nil }                                                                                              // malstep(1,2,3,4,5,6,7,8,9,A)
-    let ast = READ(text)                                                                                                        // malstep(1,2,3,4,5,6,7,8,9,A)
-    if is_error(ast) {                                                                                                          // malstep(1,2,3,4,5,6,7,8,9,A)
-        println("Error parsing input: \(ast)")                                                                                  // malstep(1,2,3,4,5,6,7,8,9,A)
-        return nil                                                                                                              // malstep(1,2,3,4,5,6,7,8,9,A)
-    }                                                                                                                           // malstep(1,2,3,4,5,6,7,8,9,A)
-    let exp = EVAL(ast)                                                                                                         // malstep(1)
-    let exp = EVAL(ast, env)                                                                                                    // malstep(2,3,4,5,6,7,8,9,A)
-    if is_error(exp) {                                                                                                          // malstep(1,2,3,4,5,6,7,8,9,A)
-        println("Error evaluating input: \(exp)")                                                                               // malstep(1,2,3,4,5,6,7,8,9,A)
-        return nil                                                                                                              // malstep(1,2,3,4,5,6,7,8,9,A)
+private func RE(text: String) -> MalVal? {                                                                                      // malstep(1)
+private func RE(text: String, _ env: Environment) -> MalVal? {                                                                  // malstep(2,3,4,5,6,7,8,9,A)
+    if !text.isEmpty {                                                                                                          // malstep(1,2,3,4,5,6,7,8,9,A)
+        do {                                                                                                                    // malstep(1,2,3,4,5,6,7,8,9,A)
+            let ast = try READ(text)                                                                                            // malstep(1,2,3,4,5,6,7,8,9,A)
+            do {                                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
+                return EVAL(ast)                                                                                                // malstep(1)
+                return try EVAL(ast, env)                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+            } catch let error as MalException {                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
+                print("Error evaluating input: \(error)")                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+            } catch {                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+                print("Error evaluating input: \(error)")                                                                       // malstep(2,3,4,5,6,7,8,9,A)
+            }                                                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
+        } catch let error as MalException {                                                                                     // malstep(1,2,3,4,5,6,7,8,9,A)
+            print("Error parsing input: \(error)")                                                                              // malstep(1,2,3,4,5,6,7,8,9,A)
+        } catch {                                                                                                               // malstep(1,2,3,4,5,6,7,8,9,A)
+            print("Error parsing input: \(error)")                                                                              // malstep(1,2,3,4,5,6,7,8,9,A)
+        }                                                                                                                       // malstep(1,2,3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(1,2,3,4,5,6,7,8,9,A)
-    return exp                                                                                                                  // malstep(1,2,3,4,5,6,7,8,9,A)
+    return nil                                                                                                                  // malstep(1,2,3,4,5,6,7,8,9,A)
 }                                                                                                                               // malstep(1,2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(0,1,2,3,4,5,6,7,8,9,A)
 //
@@ -729,16 +677,16 @@ func RE(text: String, env: Environment) -> MalVal? {
 
 // Perform the full READ/EVAL/PRINT, returning a printable string.                                                              // malstep(0,1,2,3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(0,1,2,3,4,5,6,7,8,9,A)
-func REP(text: String) -> String {                                                                                              // malstep(0)
+private func REP(text: String) -> String {                                                                                      // malstep(0)
     let exp = RE(text)                                                                                                          // malstep(0)
     return PRINT(exp)                                                                                                           // malstep(0)
 }                                                                                                                               // malstep(0)
-func REP(text: String) -> String? {                                                                                             // malstep(1)
+private func REP(text: String) -> String? {                                                                                     // malstep(1)
     let exp = RE(text)                                                                                                          // malstep(1)
     if exp == nil { return nil }                                                                                                // malstep(1)
     return PRINT(exp!)                                                                                                          // malstep(1)
 }                                                                                                                               // malstep(1)
-func REP(text: String, env: Environment) -> String? {                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+private func REP(text: String, _ env: Environment) -> String? {                                                                 // malstep(2,3,4,5,6,7,8,9,A)
     let exp = RE(text, env)                                                                                                     // malstep(2,3,4,5,6,7,8,9,A)
     if exp == nil { return nil }                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
     return PRINT(exp!)                                                                                                          // malstep(2,3,4,5,6,7,8,9,A)
@@ -750,36 +698,36 @@ func REP(text: String, env: Environment) -> String? {
 
 // Perform the full REPL.                                                                                                       // malstep(0,1,2,3,4,5,6,7,8,9,A)
 //                                                                                                                              // malstep(0,1,2,3,4,5,6,7,8,9,A)
-func REPL() {                                                                                                                   // malstep(0)
+private func REPL() {                                                                                                           // malstep(0)
     while true {                                                                                                                // malstep(0)
         if let text = _readline("user> ") {                                                                                     // malstep(0)
-            println("\(REP(text))")                                                                                             // malstep(0)
+            print("\(REP(text))")                                                                                               // malstep(0)
         } else {                                                                                                                // malstep(0)
-            println()                                                                                                           // malstep(0)
+            print("")                                                                                                           // malstep(0)
             break                                                                                                               // malstep(0)
         }                                                                                                                       // malstep(0)
     }                                                                                                                           // malstep(0)
 }                                                                                                                               // malstep(0)
-func REPL() {                                                                                                                   // malstep(1)
+private func REPL() {                                                                                                           // malstep(1)
     while true {                                                                                                                // malstep(1)
         if let text = _readline("user> ") {                                                                                     // malstep(1)
             if let output = REP(text) {                                                                                         // malstep(1)
-                println("\(output)")                                                                                            // malstep(1)
+                print("\(output)")                                                                                              // malstep(1)
             }                                                                                                                   // malstep(1)
         } else {                                                                                                                // malstep(1)
-            println()                                                                                                           // malstep(1)
+            print("")                                                                                                           // malstep(1)
             break                                                                                                               // malstep(1)
         }                                                                                                                       // malstep(1)
     }                                                                                                                           // malstep(1)
 }                                                                                                                               // malstep(1)
-func REPL(env: Environment) {                                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
+private func REPL(env: Environment) {                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
     while true {                                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
         if let text = _readline("user> ") {                                                                                     // malstep(2,3,4,5,6,7,8,9,A)
             if let output = REP(text, env) {                                                                                    // malstep(2,3,4,5,6,7,8,9,A)
-                println("\(output)")                                                                                            // malstep(2,3,4,5,6,7,8,9,A)
+                print("\(output)")                                                                                              // malstep(2,3,4,5,6,7,8,9,A)
             }                                                                                                                   // malstep(2,3,4,5,6,7,8,9,A)
         } else {                                                                                                                // malstep(2,3,4,5,6,7,8,9,A)
-            println()                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+            print("")                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
             break                                                                                                               // malstep(2,3,4,5,6,7,8,9,A)
         }                                                                                                                       // malstep(2,3,4,5,6,7,8,9,A)
     }                                                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
@@ -790,13 +738,13 @@ func REPL(env: Environment) {
 // taken as a script to execute. If one exists, it is executed in lieu of                                                       // malstep(6,7,8,9,A)
 // running the REPL.                                                                                                            // malstep(6,7,8,9,A)
 //                                                                                                                              // malstep(6,7,8,9,A)
-func process_command_line(args:[String], env:Environment) -> Bool {                                                             // malstep(6,7,8,9,A)
-    var argv = MalList()                                                                                                        // malstep(6,7,8,9,A)
+private func process_command_line(args: [String], _ env: Environment) -> Bool {                                                 // malstep(6,7,8,9,A)
+    var argv = make_list()                                                                                                      // malstep(6,7,8,9,A)
     if args.count > 2 {                                                                                                         // malstep(6,7,8,9,A)
         let args1 = args[2..<args.count]                                                                                        // malstep(6,7,8,9,A)
-        let args2 = args1.map { MalString(unescaped: $0) as MalVal }                                                            // malstep(6,7,8,9,A)
+        let args2 = args1.map { make_string($0) }                                                                               // malstep(6,7,8,9,A)
         let args3 = [MalVal](args2)                                                                                             // malstep(6,7,8,9,A)
-        argv = MalList(array: args3)                                                                                            // malstep(6,7,8,9,A)
+        argv = make_list(args3)                                                                                                 // malstep(6,7,8,9,A)
     }                                                                                                                           // malstep(6,7,8,9,A)
     env.set(kSymbolArgv, argv)                                                                                                  // malstep(6,7,8,9,A)
                                                                                                                                 // malstep(6,7,8,9,A)
@@ -809,12 +757,12 @@ func process_command_line(args:[String], env:Environment) -> Bool {
 }                                                                                                                               // malstep(6,7,8,9,A)
                                                                                                                                 // malstep(6,7,8,9,A)
 func main() {                                                                                                                   // malstep(0,1,2,3,4,5,6,7,8,9,A)
-    var env = Environment(outer: nil)                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
+    let env = Environment(outer: nil)                                                                                           // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
     load_history_file()                                                                                                         // malstep(0,1,2,3,4,5,6,7,8,9,A)
     load_builtins(env)                                                                                                          // malstep(2,3,4,5,6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
-    RE("(def! *host-language* \"swift\")", env);                                                                                // malstep(A)
+    RE("(def! *host-language* \"swift\")", env)                                                                                 // malstep(A)
     RE("(def! not (fn* (a) (if a false true)))", env)                                                                           // malstep(4,5,6,7,8,9,A)
     RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", env)                                   // malstep(6,7,8,9,A)
     RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) " +               // malstep(8,9,A)
@@ -822,10 +770,10 @@ func main() {
     RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) " +                                       // malstep(8,9,A)
        "`(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", env)                                       // malstep(8,9,A)
                                                                                                                                 // malstep(6,7,8,9,A)
-    env.set(kSymbolEval, MalBuiltin(function: {                                                                                 // malstep(6,7,8,9,A)
-         unwrap($0) {                                                                                                           // malstep(6,7,8,9,A)
-            (ast:MalVal) -> MalVal in                                                                                           // malstep(6,7,8,9,A)
-            EVAL(ast, env)                                                                                                      // malstep(6,7,8,9,A)
+    env.set(kSymbolEval, make_builtin({                                                                                         // malstep(6,7,8,9,A)
+         try! unwrap_args($0) {                                                                                                 // malstep(6,7,8,9,A)
+            (ast: MalVal) -> MalVal in                                                                                          // malstep(6,7,8,9,A)
+            try EVAL(ast, env)                                                                                                  // malstep(6,7,8,9,A)
          }                                                                                                                      // malstep(6,7,8,9,A)
     }))                                                                                                                         // malstep(6,7,8,9,A)
                                                                                                                                 // malstep(6,7,8,9,A)
@@ -838,7 +786,7 @@ func main() {
     REPL()                                                                                                                      // malstep(0,1)
     REPL(env)                                                                                                                   // malstep(2,3,4,5)
     if process_command_line(Process.arguments, env) {                                                                           // malstep(6,7,8,9,A)
-        RE("(println (str \"Mal [\" *host-language*\"]\"))", env);                                                              // malstep(A)
+        RE("(println (str \"Mal [\" *host-language*\"]\"))", env)                                                               // malstep(A)
         REPL(env)                                                                                                               // malstep(6,7,8,9,A)
     }                                                                                                                           // malstep(6,7,8,9,A)
                                                                                                                                 // malstep(2,3,4,5,6,7,8,9,A)
diff --git a/swift/types.swift b/swift/types.swift
deleted file mode 100644 (file)
index 38b2569..0000000
+++ /dev/null
@@ -1,473 +0,0 @@
-//******************************************************************************
-// MAL - types
-//******************************************************************************
-//
-//  CLASS TREE:             NOTES:
-//  ---------------------   ----------------------------------------------------
-//  MalVal
-//  ├── MalNil              ┐
-//  ├── MalTrue             │ May make these subclasses of MalConstant
-//  ├── MalFalse            ┘
-//  ├── MalInteger          ┐ May make these subclasses of MalNumber along
-//  ├── MalFloat            ┘ with MalRatio and MalComplex
-//  ├── MalSymbol
-//  ├── MalKeyword
-//  ├── MalString
-//  ├── MalSequence         Allows us to use either List or Vector in the same context.
-//  │   ├── MalList
-//  │   └── MalVector
-//  ├── MalHashMap
-//  ├── MalAtom
-//  ├── MalFunction         Allows us to uniformly invoke either a built-in or a closure.
-//  │   ├── MalClosure
-//  │   └── MalBuiltin
-//  ├── MalComment
-//  └── MalError            Indicates an error in parsing or executing.
-//
-import Foundation
-
-// ===== MalVal =====
-
-var MalValPrintReadably = true
-
-class MalVal : Printable, Hashable {
-    enum MalType : String {
-        case TypeUnknown    = "Unknown"
-        case TypeNil        = "Nil"
-        case TypeTrue       = "True"
-        case TypeFalse      = "False"
-        case TypeInteger    = "Integer"
-        case TypeFloat      = "Float"
-        case TypeSymbol     = "Symbol"
-        case TypeKeyword    = "Keyword"
-        case TypeString     = "String"
-        case TypeList       = "List"
-        case TypeVector     = "Vector"
-        case TypeHashMap    = "HashMap"
-        case TypeAtom       = "Atom"
-        case TypeClosure    = "Closure"
-        case TypeBuiltin    = "Builtin"
-        case TypeComment    = "Comment"
-        case TypeError      = "Error"
-    }
-
-    init(meta: MalVal? = nil) { self.meta = meta }
-    init(other: MalVal) { self.meta = other.meta }
-    func clone() -> MalVal { return MalVal(other:self) }
-    var type: MalType { return .TypeUnknown }
-    var description: String { return type.rawValue }
-    var hashValue: Int { return description.hashValue }
-    var meta: MalVal?
-    //class var print_readably = true   // Swift does not yet support class variables.
-}
-
-func is_nil(v: MalVal) -> Bool       { return v.type == .TypeNil }
-func is_true(v: MalVal) -> Bool      { return v.type == .TypeTrue }
-func is_false(v: MalVal) -> Bool     { return v.type == .TypeFalse }
-func is_integer(v: MalVal) -> Bool   { return v.type == .TypeInteger }
-func is_float(v: MalVal) -> Bool     { return v.type == .TypeFloat }
-func is_symbol(v: MalVal) -> Bool    { return v.type == .TypeSymbol }
-func is_keyword(v: MalVal) -> Bool   { return v.type == .TypeKeyword }
-func is_string(v: MalVal) -> Bool    { return v.type == .TypeString }
-func is_list(v: MalVal) -> Bool      { return v.type == .TypeList }
-func is_vector(v: MalVal) -> Bool    { return v.type == .TypeVector }
-func is_hashmap(v: MalVal) -> Bool   { return v.type == .TypeHashMap }
-func is_atom(v: MalVal) -> Bool      { return v.type == .TypeAtom }
-func is_closure(v: MalVal) -> Bool   { return v.type == .TypeClosure }
-func is_builtin(v: MalVal) -> Bool   { return v.type == .TypeBuiltin }
-func is_comment(v: MalVal) -> Bool   { return v.type == .TypeComment }
-func is_error(v: MalVal) -> Bool     { return v.type == .TypeError }
-
-func is_truthy(v: MalVal) -> Bool    { return !is_falsey(v) }
-func is_falsey(v: MalVal) -> Bool    { let type = v.type; return type == .TypeNil || type == .TypeFalse }
-func is_number(v: MalVal) -> Bool    { let type = v.type; return type == .TypeInteger || type == .TypeFloat }
-func is_sequence(v: MalVal) -> Bool  { let type = v.type; return type == .TypeList || type == .TypeVector }
-func is_function(v: MalVal) -> Bool  { let type = v.type; return type == .TypeClosure || type == .TypeBuiltin }
-
-func == (left: MalVal, right: MalVal) -> Bool {
-    // Special case lists/vectors, since they are different types that are
-    // nonetheless comparable.
-    if is_sequence(left) && is_sequence(right) {
-        let left_seq = left as! MalSequence
-        let right_seq = right as! MalSequence
-        return left_seq == right_seq
-    }
-
-    if left.type != right.type {
-        return false
-    }
-
-    switch left.type {
-        case .TypeUnknown:  return false
-        case .TypeNil:      return (left as! MalNil) == (right as! MalNil)
-        case .TypeTrue:     return (left as! MalTrue) == (right as! MalTrue)
-        case .TypeFalse:    return (left as! MalFalse) == (right as! MalFalse)
-        case .TypeInteger:  return (left as! MalInteger) == (right as! MalInteger)
-        case .TypeFloat:    return (left as! MalFloat) == (right as! MalFloat)
-        case .TypeSymbol:   return (left as! MalSymbol) == (right as! MalSymbol)
-        case .TypeKeyword:  return (left as! MalKeyword) == (right as! MalKeyword)
-        case .TypeString:   return (left as! MalString) == (right as! MalString)
-        case .TypeList:     return (left as! MalList) == (right as! MalList)
-        case .TypeVector:   return (left as! MalVector) == (right as! MalVector)
-        case .TypeHashMap:  return (left as! MalHashMap) == (right as! MalHashMap)
-        case .TypeAtom:     return (left as! MalAtom) == (right as! MalAtom)
-        case .TypeClosure:  return (left as! MalClosure) == (right as! MalClosure)
-        case .TypeBuiltin:  return (left as! MalBuiltin) == (right as! MalBuiltin)
-        case .TypeComment:  return (left as! MalComment) == (right as! MalComment)
-        case .TypeError:    return (left as! MalError) == (right as! MalError)
-    }
-}
-
-func != (left: MalVal, right: MalVal) -> Bool {
-    return !(left == right)
-}
-
-// ===== MalNil =====
-
-class MalNil: MalVal {
-    override func clone() -> MalVal { return MalNil(other:self) }
-    override var type: MalType { return .TypeNil }
-    override var description: String { return "nil" }
-}
-func == (left: MalNil, right: MalNil) -> Bool { return true }
-
-// ===== MalTrue =====
-
-class MalTrue: MalVal {
-    override func clone() -> MalVal { return MalTrue(other:self) }
-    override var type: MalType { return .TypeTrue }
-    override var description: String { return "true" }
-}
-func == (left: MalTrue, right: MalTrue) -> Bool { return true }
-
-// ===== MalFalse =====
-
-class MalFalse: MalVal {
-    override func clone() -> MalVal { return MalFalse(other:self) }
-    override var type: MalType { return .TypeFalse }
-    override var description: String { return "false" }
-}
-func == (left: MalFalse, right: MalFalse) -> Bool { return true }
-
-// ===== MalInteger =====
-
-class MalInteger: MalVal {
-    init(value: Int64, meta: MalVal? = nil) { self.value = value; super.init(meta: meta) }
-    init(other: MalInteger) { self.value = other.value; super.init(other: other) }
-    override func clone() -> MalVal { return MalInteger(other:self) }
-    override var type: MalType { return .TypeInteger }
-    override var description: String { return "\(value)" }
-    let value: Int64
-}
-func == (left: MalInteger, right: MalInteger) -> Bool { return left.value == right.value }
-
-// ===== MalFloat =====
-
-class MalFloat: MalVal {
-    init(value: Double, meta: MalVal? = nil) { self.value = value; super.init(meta: meta) }
-    init(other: MalFloat) { self.value = other.value; super.init(other: other) }
-    override func clone() -> MalVal { return MalFloat(other:self) }
-    override var type: MalType { return .TypeFloat }
-    override var description: String { return "\(value)" }
-    let value: Double
-}
-func == (left: MalFloat, right: MalFloat) -> Bool { return left.value == right.value }
-
-// ===== MalSymbol =====
-
-var symbolHash = [String:Int]()
-var symbolArray = [String]()
-
-private func indexForSymbol(s: String) -> Int {
-    if let i = symbolHash[s] {
-        return i
-    }
-
-    symbolArray.append(s)
-    symbolHash[s] = symbolArray.count - 1
-    return symbolArray.count - 1
-}
-
-private func symbolForIndex(i: Int) -> String {
-    return symbolArray[i]
-}
-
-class MalSymbol: MalVal, Hashable {
-    init(symbol: String, meta: MalVal? = nil) { self.index = indexForSymbol(symbol); super.init(meta: meta) }
-    init(other: MalSymbol) { self.index = other.index; super.init(other: other) }
-    override func clone() -> MalVal { return MalSymbol(other:self) }
-    override var type: MalType { return .TypeSymbol }
-    override var description: String { return symbolForIndex(self.index) }
-    override var hashValue: Int { return self.index }
-    let index: Int
-}
-func == (left: MalSymbol, right: MalSymbol) -> Bool { return left.index == right.index }
-
-// ===== MalKeyword =====
-
-class MalKeyword: MalVal {
-    typealias Keyword = String
-    init(keyword: Keyword, meta: MalVal? = nil) { self.value = keyword; super.init(meta: meta) }
-    init(other: MalKeyword) { self.value = other.value; super.init(other: other) }
-    override func clone() -> MalVal { return MalKeyword(other:self) }
-    override var type: MalType { return .TypeKeyword }
-    override var description: String { return ":\(value)" }
-    let value: Keyword
-}
-func == (left: MalKeyword, right: MalKeyword) -> Bool { return left.value == right.value }
-
-// ===== MalString =====
-
-class MalString: MalVal {
-    init(escaped value: String, meta: MalVal? = nil) { self.value = MalString.unescape(value); super.init(meta: meta) }   // String is quoted, and special chars are escaped with \.
-    init(unescaped value: String, meta: MalVal? = nil) { self.value = value; super.init(meta: meta) }                     // String is just the way we like it.
-    init(other: MalString) { self.value = other.value; super.init(other: other) }
-    override func clone() -> MalVal { return MalString(other:self) }
-    override var type: MalType { return .TypeString }
-    override var description: String { return MalValPrintReadably ? MalString.escape(value) : value }
-    let value: String
-
-    class func unescape(s: String) -> String {
-        var index = 0
-        var prev_is_escape = false
-        var str = ""
-        for ch in s {
-            if index == count(s.utf16) - 1 { continue }
-            if index++ == 0 { continue }
-            if prev_is_escape {
-                prev_is_escape = false;
-                if ch == "n" { str += "\n" }
-                else if ch == "r" { str += "\r" }
-                else if ch == "t" { str += "\t" }
-                else { str.append(ch) }
-            } else if ch == "\\" {
-                prev_is_escape = true
-            } else {
-                str.append(ch)
-            }
-        }
-        return str
-    }
-
-    class func escape(s: String) -> String {
-        var str = ""
-        for ch in s {
-            if ch == "\n" { str += "\\n"; continue }
-            if ch == "\r" { str += "\\r"; continue }
-            if ch == "\t" { str += "\\t"; continue }
-            if ch == "\"" || ch == "\\" { str += "\\" }
-            str.append(ch)
-        }
-        str = "\"" + str + "\""
-        return str
-    }
-}
-func == (left: MalString, right: MalString) -> Bool { return left.value == right.value }
-
-// ===== MalSequence =====
-
-class MalSequence: MalVal, SequenceType {
-    init(slice: ArraySlice<MalVal>, meta: MalVal? = nil) { self.slice = slice; super.init(meta: meta) }
-    init(other: MalSequence) { self.slice = other.slice; super.init(other: other) }
-    override var type: MalType { return .TypeUnknown }
-    override var description: String { return "" }
-
-    func first() -> MalVal { return !isEmpty ? slice[0] : MalNil() }
-    func last() -> MalVal { return !isEmpty ? slice[slice.count - 1] : MalNil() }
-    func rest() -> MalSequence { return MalSequence(slice: ArraySlice<MalVal>()) }
-    func map<U>(transform: (MalVal) -> U) -> ArraySlice<U> { return slice.map(transform) }
-    func reduce<U>(initial: U, combine: (U, MalVal) -> U) -> U { return slice.reduce(initial, combine: combine) }
-    var count: Int { return slice.count }
-    var isEmpty: Bool { return slice.isEmpty }
-    subscript(index: Int) -> MalVal { return index < slice.count ? slice[index] : MalError(message: "index (\(index)) out of range (\(slice.count))") }
-    subscript(subRange: Range<Int>) -> ArraySlice<MalVal> { return slice[subRange] }
-
-    // SequenceType
-     func generate() -> ArraySlice<MalVal>.Generator { return slice.generate() }
-
-    let slice: ArraySlice<MalVal>
-}
-func == (left: MalSequence, right: MalSequence) -> Bool {
-    if left.count != right.count { return false }
-    for var index = 0; index < left.count; ++index { if left[index] != right[index] { return false } }
-    return true
-}
-
-// ===== MalList =====
-
-class MalList: MalSequence {
-    override init(slice: ArraySlice<MalVal>, meta: MalVal? = nil) { super.init(slice: slice, meta: meta) }
-    convenience init(array: [MalVal], meta: MalVal? = nil) { self.init(slice: array[0..<array.count], meta: meta) }
-    convenience init(objects: MalVal...) { self.init(array: objects) }
-    init(other: MalList) { super.init(other: other) }
-    override func clone() -> MalVal { return MalList(other:self) }
-    override var type: MalType { return .TypeList }
-    override var description: String { return "(" + " ".join(map { $0.description }) + ")" }
-    override func rest() -> MalSequence { return !isEmpty ? MalList(slice: slice[1..<slice.count]) : MalList() }
-}
-func == (left: MalList, right: MalList) -> Bool {
-    return (left as MalSequence) == (right as MalSequence)
-}
-
-// ===== MalVector =====
-
-class MalVector: MalSequence {
-    override init(slice: ArraySlice<MalVal>, meta: MalVal? = nil) { super.init(slice: slice, meta: meta) }
-    convenience init(array: [MalVal], meta: MalVal? = nil) { self.init(slice: array[0..<array.count], meta: meta) }
-    convenience init(objects: MalVal...) { self.init(array: objects) }
-    init(other: MalVector) { super.init(other: other) }
-    override func clone() -> MalVal { return MalVector(other:self) }
-    override var type: MalType { return .TypeVector }
-    override var description: String { return "[" + " ".join(map { $0.description }) + "]" }
-    override func rest() -> MalSequence { return !isEmpty ? MalVector(slice: slice[1..<slice.count]) : MalVector() }
-}
-func == (left: MalVector, right: MalVector) -> Bool {
-    return (left as MalSequence) == (right as MalSequence)
-}
-
-// ===== MalHashMap =====
-
-class MalHashMap: MalVal, SequenceType {
-    convenience override init(meta: MalVal? = nil) { self.init(hash: [MalVal:MalVal](), meta:meta) }
-    init(hash: [MalVal:MalVal], meta: MalVal? = nil) { self.hash = hash; super.init(meta: meta) }
-    convenience init(slice: ArraySlice<MalVal>, meta: MalVal? = nil) { var hash = [MalVal:MalVal](); for var index = 0; index < slice.count; index += 2 { hash[slice[index]] = slice[index + 1] }; self.init(hash: hash, meta: meta) }
-    convenience init(array: Array<MalVal>, meta: MalVal? = nil) { var hash = [MalVal:MalVal](); for var index = 0; index < array.count; index += 2 { hash[array[index]] = array[index + 1] }; self.init(hash: hash, meta: meta) }
-    init(other: MalHashMap) { self.hash = other.hash; super.init(other: other) }
-    override func clone() -> MalVal { return MalHashMap(other:self) }
-    override var type: MalType { return .TypeHashMap }
-    override var description: String {
-        var a = [String]()
-        for (k, v) in hash {
-            a.append("\(k.description) \(v.description)")
-        }
-        let s = " ".join(a)
-        return "{\(s)}" }
-    func generate() -> DictionaryGenerator<MalVal, MalVal> { return hash.generate() }
-    var count: Int { return hash.count }
-    var isEmpty: Bool { return hash.isEmpty }
-    subscript(key: MalVal) -> MalVal? { return hash[key] }
-    let hash: [MalVal:MalVal]
-}
-func == (left: MalHashMap, right: MalHashMap) -> Bool {
-    if left.count != right.count { return false }
-    var left_index = left.hash.startIndex
-    var right_index = right.hash.startIndex
-    while left_index != left.hash.endIndex {
-        let (left_key, left_value) = left.hash[left_index]
-        let (right_key, right_value) = right.hash[right_index]
-        if (left_key != right_key) || (left_value != right_value) {
-            return false
-        }
-        left_index = left_index.successor()
-        right_index = right_index.successor()
-    }
-    return true
-}
-
-// ===== MalAtom =====
-
-class MalAtom: MalVal {
-    init(object: MalVal, meta: MalVal? = nil) { self.value = object; super.init(meta: meta) }
-    init(other: MalAtom) { self.value = other.value; super.init(other: other) }
-    override func clone() -> MalVal { return MalAtom(other:self) }
-    override var type: MalType { return .TypeAtom }
-    override var description: String { return "(atom \(value.description))" }
-    var value: MalVal
-}
-func == (left: MalAtom, right: MalAtom) -> Bool { return false }
-
-// ===== MalFunction =====
-
-class MalFunction: MalVal {
-    override init(meta: MalVal? = nil) { super.init(meta: meta) }
-    init(other: MalFunction) { super.init(other: other) }
-    func apply(exprs: MalSequence) -> MalVal { return MalNil() }
-}
-
-// ===== MalClosure =====
-
-class MalClosure: MalFunction {
-    typealias Evaluator = (MalVal, Environment) -> MalVal
-    init(eval: Evaluator, args: MalSequence, body: MalVal, env: Environment, meta: MalVal? = nil) {
-        self.eval = eval;
-        self.args = args;
-        self.body = body;
-        self.env = env;
-        self.is_macro = false;
-        super.init(meta: meta) }
-    init(eval: Evaluator, args: MalSequence, body: MalVal, env: Environment, is_macro: Bool, meta: MalVal? = nil) {
-        self.eval = eval;
-        self.args = args;
-        self.body = body;
-        self.env = env;
-        self.is_macro = is_macro;
-        super.init(meta: meta) }
-    init(other: MalClosure) {
-        self.eval = other.eval
-        self.args = other.args
-        self.body = other.body
-        self.env = other.env
-        self.is_macro = other.is_macro
-        super.init(other: other) }
-    override func clone() -> MalVal { return MalClosure(other:self) }
-    override var type: MalType { return .TypeClosure }
-    override var description: String { return "#<Closure>: (fn* \(args.description) \(body.description))" }
-    override func apply(exprs: MalSequence) -> MalVal {
-        var new_env = Environment(outer: env)
-        let result = new_env.set_bindings(args, with_exprs:exprs)
-        if is_error(result) {
-            return result
-        }
-        // Calling EVAL indirectly via an 'eval' data member is a bit of a hack.
-        // We can't call EVAL directly because this file (types.swift) needs to
-        // be used with many different versions of the main MAL file
-        // (step[0-10]*.swift), and EVAL is declared differently across those
-        // versions. By using this indirection, we avoid that problem.
-        return eval(body, new_env)
-    }
-    let eval: Evaluator
-    let args: MalSequence
-    let body: MalVal
-    let env: Environment
-    var is_macro: Bool
-}
-func == (left: MalClosure, right: MalClosure) -> Bool { return false }
-
-// ===== MalBuiltin =====
-
-class MalBuiltin: MalFunction {
-    typealias BuiltinSignature = (MalSequence) -> MalVal
-    init(function: BuiltinSignature, meta: MalVal? = nil) { self.value = function; super.init(meta: meta) }
-    init(other: MalBuiltin) { self.value = other.value; super.init(other: other) }
-    override func clone() -> MalVal { return MalBuiltin(other:self) }
-    override var type: MalType { return .TypeBuiltin }
-    override var description: String { return "#<Builtin>" }
-    override func apply(exprs: MalSequence) -> MalVal { return value(exprs) }
-    let value: BuiltinSignature!
-}
-func == (left: MalBuiltin, right: MalBuiltin) -> Bool { return false }  // Can't compare function references in Swift
-
-// ===== MalComment =====
-
-class MalComment: MalVal {
-    init(comment: String, meta: MalVal? = nil) { self.value = comment; super.init(meta: meta) }
-    init(other: MalComment) { self.value = other.value; super.init(other: other) }
-    override func clone() -> MalVal { return MalComment(other:self) }
-    override var type: MalType { return .TypeComment }
-    override var description: String { return value }
-    let value: String
-}
-func == (left: MalComment, right: MalComment) -> Bool { return false }
-
-// ===== MalError =====
-
-class MalError: MalVal {
-    init(message: String, meta: MalVal? = nil) { self.value = MalString(unescaped:message); super.init(meta: meta) }
-    init(object: MalVal, meta: MalVal? = nil) { self.value = object; super.init(meta: meta) }
-    init(other: MalError) { self.value = other.value; super.init(other: other) }
-    override func clone() -> MalVal { return MalError(other:self) }
-    override var type: MalType { return .TypeError }
-    override var description: String { return value.description }
-    let value: MalVal
-}
-func == (left: MalError, right: MalError) -> Bool { return false }
diff --git a/swift/types_class.swift b/swift/types_class.swift
new file mode 100644 (file)
index 0000000..569bfa5
--- /dev/null
@@ -0,0 +1,1101 @@
+//******************************************************************************
+// MAL - types, implemented as a Swift "class".
+//******************************************************************************
+
+import Foundation
+
+// ==================== Types / Constants / Variables ====================
+
+typealias MalProtocol    = protocol<Equatable, CustomStringConvertible, Hashable>
+
+typealias MalIntType     = Int64
+typealias MalFloatType   = Double
+typealias MalSymbolType  = String
+typealias MalKeywordType = String
+typealias MalStringType  = String
+typealias MalVectorType  = ArraySlice<MalVal>
+typealias MalHashType    = Dictionary<MalVal, MalVal>
+
+private let kUnknown     = MalUnknown()
+private let kNil         = MalNil()
+private let kTrue        = MalTrue()
+private let kFalse       = MalFalse()
+private let kComment     = MalComment()
+
+// ==================== MalVal ====================
+
+class MalVal : MalProtocol {
+    init() {
+        self._meta = nil
+    }
+    init(_ other: MalVal, _ meta: MalVal?) {
+        self._meta = meta
+    }
+    init(_ meta: MalVal?) {
+        self._meta = meta
+    }
+
+    // CustomStringConvertible
+    //
+    var description: String { die() }
+
+    // Hashable
+    //
+    var hashValue: Int { return description.hashValue }
+
+    // MalVal
+    //
+    func clone_with_meta(meta: MalVal) -> MalVal { die() }
+    final var meta: MalVal? { return self._meta }
+
+    let _meta: MalVal?
+}
+
+// Equatable
+//
+let tMalUnknown = class_getName(MalUnknown)
+let tMalNil     = class_getName(MalNil)
+let tMalTrue    = class_getName(MalTrue)
+let tMalFalse   = class_getName(MalFalse)
+let tMalComment = class_getName(MalComment)
+let tMalInteger = class_getName(MalInteger)
+let tMalFloat   = class_getName(MalFloat)
+let tMalSymbol  = class_getName(MalSymbol)
+let tMalKeyword = class_getName(MalKeyword)
+let tMalString  = class_getName(MalString)
+let tMalList    = class_getName(MalList)
+let tMalVector  = class_getName(MalVector)
+let tMalHashMap = class_getName(MalHashMap)
+let tMalAtom    = class_getName(MalAtom)
+let tMalClosure = class_getName(MalClosure)
+let tMalBuiltin = class_getName(MalBuiltin)
+let tMalMacro   = class_getName(MalMacro)
+
+func ==(left: MalVal, right: MalVal) -> Bool {
+    let leftClass = object_getClassName(left)
+    let rightClass = object_getClassName(right)
+
+    if leftClass == tMalUnknown && rightClass == tMalUnknown    { return as_unknown(left)   == as_unknown(right) }
+    if leftClass == tMalNil     && rightClass == tMalNil        { return as_nil(left)       == as_nil(right) }
+    if leftClass == tMalTrue    && rightClass == tMalTrue       { return as_true(left)      == as_true(right) }
+    if leftClass == tMalFalse   && rightClass == tMalFalse      { return as_false(left)     == as_false(right) }
+    if leftClass == tMalComment && rightClass == tMalComment    { return as_comment(left)   == as_comment(right) }
+    if leftClass == tMalInteger && rightClass == tMalInteger    { return as_integer(left)   == as_integer(right) }
+    if leftClass == tMalFloat   && rightClass == tMalFloat      { return as_float(left)     == as_float(right) }
+    if leftClass == tMalSymbol  && rightClass == tMalSymbol     { return as_symbol(left)    == as_symbol(right) }
+    if leftClass == tMalKeyword && rightClass == tMalKeyword    { return as_keyword(left)   == as_keyword(right) }
+    if leftClass == tMalString  && rightClass == tMalString     { return as_string(left)    == as_string(right) }
+    //if leftClass == tMalList    && rightClass == tMalList       { return as_sequence(left)  == as_sequence(right) }
+    //if leftClass == tMalVector  && rightClass == tMalVector     { return as_sequence(left)  == as_sequence(right) }
+    if leftClass == tMalHashMap && rightClass == tMalHashMap    { return as_hashmap(left)   == as_hashmap(right) }
+    if leftClass == tMalAtom    && rightClass == tMalAtom       { return as_atom(left)      == as_atom(right) }
+    if leftClass == tMalClosure && rightClass == tMalClosure    { return as_closure(left)   == as_closure(right) }
+    if leftClass == tMalBuiltin && rightClass == tMalBuiltin    { return as_builtin(left)   == as_builtin(right) }
+    if leftClass == tMalMacro   && rightClass == tMalMacro      { return as_macro(left)     == as_macro(right) }
+    //
+    // Special case lists/vectors, since they are different types that are
+    // nonetheless comparable.
+    if
+        (leftClass == tMalList || leftClass == tMalVector) &&
+        (rightClass == tMalList || rightClass == tMalVector) {
+            return as_sequence(left) == as_sequence(right)
+        }
+
+    return false
+}
+
+func !=(left: MalVal, right: MalVal) -> Bool {
+    return !(left == right)
+}
+
+// ==================== MalUnknown ====================
+
+final class MalUnknown: MalVal {
+    override var description: String { return "unknown" }
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalUnknown(meta) }
+}
+func ==(left: MalUnknown, right: MalUnknown) -> Bool { return false }
+
+// ==================== MalNil ====================
+
+final class MalNil: MalVal {
+    override var description: String { return "nil" }
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalNil(meta) }
+}
+func ==(left: MalNil, right: MalNil) -> Bool { return true }
+
+// ==================== MalTrue ====================
+
+final class MalTrue: MalVal {
+    override var description: String { return "true" }
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalTrue(meta) }
+}
+func ==(left: MalTrue, right: MalTrue) -> Bool { return true }
+
+// ==================== MalFalse ====================
+
+final class MalFalse: MalVal {
+    override var description: String { return "false" }
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalFalse(meta) }
+}
+func ==(left: MalFalse, right: MalFalse) -> Bool { return true }
+
+// ==================== MalComment ====================
+
+final class MalComment: MalVal {
+    override var description: String { return "Comment" }
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalComment(meta) }
+}
+
+// Equatable
+//
+func ==(left: MalComment, right: MalComment) -> Bool { return false }
+
+// ==================== MalInteger ====================
+
+final class MalInteger: MalVal {
+    override init() {
+        self._integer = 0
+        super.init()
+    }
+    init(_ other: MalInteger, _ meta: MalVal? = nil) {
+        self._integer = other._integer
+        super.init(other, meta)
+    }
+    init(_ integer: MalIntType) {
+        self._integer = integer
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "\(self._integer)" }
+
+    // Hashable
+    //
+    override var hashValue: Int { return Int(self._integer) }
+
+    // MalInteger
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalInteger(self, meta) }
+    var integer: MalIntType { return self._integer }
+
+    private let _integer: MalIntType
+}
+
+// Equatable
+//
+func ==(left: MalInteger, right: MalInteger) -> Bool { return left.integer == right.integer }
+
+// ==================== MalFloat ====================
+
+final class MalFloat: MalVal {
+    override init() {
+        self._float = 0
+        super.init()
+    }
+    init(_ other: MalFloat, _ meta: MalVal? = nil) {
+        self._float = other._float
+        super.init(other, meta)
+    }
+    init(_ float: Double) {
+        self._float = float
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "\(self._float)" }
+
+    // Hashable
+    //
+    override var hashValue: Int { return Int(self._float) }
+
+    // MalFloat
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalFloat(self, meta) }
+    var float: MalFloatType { return self._float }
+
+    private let _float: Double
+}
+
+// Equatable
+//
+func ==(left: MalFloat, right: MalFloat) -> Bool { return left.float == right.float }
+
+// ==================== MalSymbol ====================
+
+private var symbolHash = [MalSymbolType : Int]()
+private var symbolArray = [MalSymbolType]()
+
+private func indexForSymbol(s: MalSymbolType) -> Int {
+    if let i = symbolHash[s] {
+        return i
+    }
+
+    symbolArray.append(s)
+    symbolHash[s] = symbolArray.count - 1
+    return symbolArray.count - 1
+}
+
+private func symbolForIndex(i: Int) -> MalSymbolType {
+    return symbolArray[i]
+}
+
+final class MalSymbol: MalVal {
+    override init() {
+        self._index = indexForSymbol("")
+        super.init()
+    }
+    init(_ other: MalSymbol, _ meta: MalVal? = nil) {
+        self._index = other._index
+        super.init(other, meta)
+    }
+    init(_ symbol: MalSymbolType) {
+        self._index = indexForSymbol(symbol)
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return symbolForIndex(self._index) }
+
+    // Hashable
+    //
+    override var hashValue: Int { return self._index }
+
+    // MalSymbol
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalSymbol(self, meta) }
+    var index: Int { return self._index }
+
+    private let _index: Int
+}
+
+// Equatable
+//
+func ==(left: MalSymbol, right: MalSymbol) -> Bool { return left.index == right.index }
+
+// ==================== MalKeyword ====================
+
+final class MalKeyword: MalVal {
+    override init() {
+        self._keyword = ""
+        super.init()
+    }
+    init(_ other: MalKeyword, _ meta: MalVal? = nil) {
+        self._keyword = other._keyword
+        super.init(other, meta)
+    }
+    init(_ keyword: MalKeywordType) {
+        self._keyword = keyword
+        super.init()
+    }
+    init(_ string: MalString) {
+        self._keyword = string.string
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return self._keyword }   // ":" added in pr_str
+
+    // MalKeyword
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalKeyword(self, meta) }
+    var keyword: MalKeywordType { return self._keyword }
+
+    private let _keyword: MalKeywordType
+}
+
+// Equatable
+//
+func ==(left: MalKeyword, right: MalKeyword) -> Bool { return left._keyword == right._keyword }
+
+// ==================== MalString ====================
+
+final class MalString: MalVal {
+    override init() {
+        self._string = ""
+        super.init()
+    }
+    init(_ other: MalString, _ meta: MalVal? = nil) {
+        self._string = other._string
+        super.init(other, meta)
+    }
+    init(_ string: MalStringType) {
+        self._string = string
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return self._string }
+
+    // MalString
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalString(self, meta) }
+    var string: MalStringType { return self._string }
+
+    private let _string: MalStringType
+}
+
+// Equatable
+//
+func ==(left: MalString, right: MalString) -> Bool { return left.string == right.string }
+
+// ==================== MalSequence ====================
+
+class MalSequence: MalVal, SequenceType {
+    override init() {
+        self.count = 0
+        self.isEmpty = true
+        super.init()
+    }
+    init(_ other: MalSequence, _ meta: MalVal? = nil) {
+        self.count = other.count
+        self.isEmpty = other.isEmpty
+        super.init(other, meta)
+    }
+    init(_ count: MalIntType, _ isEmpty: Bool) {
+        self.count = count
+        self.isEmpty = isEmpty
+        super.init()
+    }
+
+    // SequenceType
+    //
+    func generate() -> MalVectorType.Generator { die() }
+
+    // MalSequence
+    //
+    var count: MalIntType
+    var isEmpty: Bool
+
+    func first() -> MalVal { die() }
+    func last() -> MalVal { die() }
+    func rest() -> MalVal { die() }
+    func nth(n: MalIntType) throws -> MalVal { die() }
+    func range_from(from: MalIntType, to: MalIntType) -> MalVal { die() }
+    func cons(element: MalVal) -> MalVal { die() }
+    func concat(seq: MalSequence) throws -> MalVal { die() }
+    func conj(seq: MalSequence) throws -> MalVal { die() }
+    func map<U>(@noescape transform: (MalVal) -> U) -> ArraySlice<U> { die() }
+    func reduce<U>(initial: U, @noescape combine: (U, MalVal) -> U) -> U { die() }
+}
+
+// Equatable
+//
+func ==(left: MalSequence, right: MalSequence) -> Bool {
+    if left.count != right.count { return false }
+    var left_gen = left.generate()
+    var right_gen = right.generate()
+    while true {
+        if let left = left_gen.next(), right = right_gen.next() {
+            if left != right {
+                return false
+            }
+        } else {
+            break
+        }
+    }
+    return true
+}
+
+// ==================== MalList ====================
+
+final class MalList: MalSequence {
+    override init() {
+        self._slice = MalVectorType()
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+    init(_ other: MalList, _ meta: MalVal? = nil) {
+        self._slice = other._slice
+        super.init(other, meta)
+    }
+    init(seq: MalSequence) {    // We need the "seq" in order to differentiate it from the previous init()
+        self._slice = seq.reduce(MalVectorType()){ var s = $0; s.append($1); return s }
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+    init(_ slice: MalVectorType) {
+        self._slice = slice
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+    init(_ array: Array<MalVal>) {
+        self._slice = array[0..<array.count]
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+    init<T: CollectionType where T.Generator.Element == MalVal>(_ collection: T) {
+        self._slice = collection.reduce(MalVectorType()){ var s = $0; s.append($1); return s }
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "(" + self.map { pr_str($0) }.joinWithSeparator(" ") + ")" }
+
+    // SequenceType
+    //
+    override func generate() -> MalVectorType.Generator { return self._slice.generate() }
+
+    // MalSequence
+    //
+    override func first() -> MalVal { return isEmpty ? make_nil() : try! nth(0) }
+    override func last() -> MalVal { return try! nth(count - 1) }
+    override func rest() -> MalVal { return range_from(MalIntType(1), to: MalIntType(count)) }
+    override func nth(n: MalIntType) throws -> MalVal { guard n < count else { try throw_error("index (\(n)) out of range (\(count))") }; return self._slice[self._slice.startIndex.advancedBy(Int(n))] }
+    override func range_from(from: MalIntType, to: MalIntType) -> MalVal {
+        return from <= to && to <= count
+            ? make_list(self._slice[self._slice.startIndex.advancedBy(Int(from))..<self._slice.startIndex.advancedBy(Int(to))])
+            : make_list()
+    }
+    override func cons(element: MalVal) -> MalVal {
+        var result = self._slice
+        result.insert(element, atIndex: result.startIndex)
+        return make_list(result)
+    }
+    override func concat(seq: MalSequence) throws -> MalVal {
+        var result = self._slice
+        if let list = as_listQ(seq) {
+            result.appendContentsOf(list._slice)
+        } else if let vector = as_vectorQ(seq) {
+            result.appendContentsOf(vector._slice)
+        } else {
+            try throw_error("Expected sequence, got \(seq)")
+        }
+        return make_list(result)
+    }
+    override func conj(seq: MalSequence) throws -> MalVal {
+        var result: Array<MalVal>
+        if let list = as_listQ(seq) {
+            result = list._slice.reverse()
+        } else if let vector = as_vectorQ(seq) {
+            result = vector._slice.reverse()
+        } else {
+            try throw_error("Expected sequence, got \(seq)")
+        }
+        result.appendContentsOf(self._slice)
+        return make_list(result)
+    }
+    override func map<U>(@noescape transform: (MalVal) -> U) -> ArraySlice<U> { return ArraySlice<U>(self._slice.map(transform)) }
+    override func reduce<U>(initial: U, @noescape combine: (U, MalVal) -> U) -> U { return self._slice.reduce(initial, combine: combine) }
+
+    // MalList
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalList(self, meta) }
+
+    private let _slice: MalVectorType
+}
+
+// Equatable
+//
+func ==(left: MalList, right: MalList) -> Bool {
+    return as_sequence(left) == as_sequence(right)
+}
+
+// ==================== MalVector ====================
+
+final class MalVector: MalSequence {
+    override init() {
+        self._slice = MalVectorType()
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+    init(_ other: MalVector, _ meta: MalVal? = nil) {
+        self._slice = other._slice
+        super.init(other, meta)
+    }
+    init(seq: MalSequence) {    // We need the "seq" in order to differentiate it from the previous init()
+        self._slice = seq.reduce(MalVectorType()){ var s = $0; s.append($1); return s }
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+    init(_ slice: MalVectorType) {
+        self._slice = slice
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+    init(_ array: Array<MalVal>) {
+        self._slice = array[0..<array.count]
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+    init<T: CollectionType where T.Generator.Element == MalVal>(_ collection: T) {
+        self._slice = collection.reduce(MalVectorType()){ var s = $0; s.append($1); return s }
+        super.init(MalIntType(self._slice.count), self._slice.isEmpty)
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "[" + self.map { pr_str($0) }.joinWithSeparator(" ") + "]" }
+
+    // SequenceType
+    //
+    override func generate() -> MalVectorType.Generator { return self._slice.generate() }
+
+    // MalSequence
+    //
+    override func first() -> MalVal { return isEmpty ? make_nil() : try! nth(0) }
+    override func last() -> MalVal { return try! nth(count - 1) }
+    override func rest() -> MalVal { return range_from(MalIntType(1), to: MalIntType(count)) }
+    override func nth(n: MalIntType) throws -> MalVal { guard n < count else { try throw_error("index (\(n)) out of range (\(count))") }; return self._slice[self._slice.startIndex.advancedBy(Int(n))] }
+    override func range_from(from: MalIntType, to: MalIntType) -> MalVal {
+        return from <= to && to <= count
+            ? make_list(self._slice[self._slice.startIndex.advancedBy(Int(from))..<self._slice.startIndex.advancedBy(Int(to))])   // Yes, make_list
+            : make_list()                                   // Yes, make_list
+    }
+    override func cons(element: MalVal) -> MalVal {
+        var result = self._slice
+        result.insert(element, atIndex: result.startIndex)
+        return make_list(result)                            // Yes, make_list
+    }
+    override func concat(seq: MalSequence) throws -> MalVal {
+        var result = self._slice
+        if let list = as_listQ(seq) {
+            result.appendContentsOf(list._slice)
+        } else if let vector = as_vectorQ(seq) {
+            result.appendContentsOf(vector._slice)
+        } else {
+            try throw_error("Expected sequence, got \(seq)")
+        }
+        return make_list(result)
+    }
+    override func conj(seq: MalSequence) throws -> MalVal {
+        var result = self._slice
+        if let list = as_listQ(seq) {
+            result.appendContentsOf(list._slice)
+        } else if let vector = as_vectorQ(seq) {
+            result.appendContentsOf(vector._slice)
+        } else {
+            try throw_error("Expected sequence, got \(seq)")
+        }
+        return make_vector(result)
+    }
+    override func map<U>(@noescape transform: (MalVal) -> U) -> ArraySlice<U> { return ArraySlice<U>(self._slice.map(transform)) }
+    override func reduce<U>(initial: U, @noescape combine: (U, MalVal) -> U) -> U { return self._slice.reduce(initial, combine: combine) }
+
+    // MalVector
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalVector(self, meta) }
+
+    private let _slice: MalVectorType
+}
+
+// Equatable
+//
+func ==(left: MalVector, right: MalVector) -> Bool {
+    return as_sequence(left) == as_sequence(right)
+}
+
+// ==================== MalHashMap ====================
+
+final class MalHashMap: MalVal, SequenceType {
+    override init() {
+        self._hash = MalHashType()
+        self.count = MalIntType(self._hash.count)
+        self.isEmpty = self._hash.isEmpty
+        super.init()
+    }
+    init(_ other: MalHashMap, _ meta: MalVal? = nil) {
+        self._hash = other._hash
+        self.count = MalIntType(self._hash.count)
+        self.isEmpty = self._hash.isEmpty
+        super.init(other, meta)
+    }
+    init(_ hash: MalHashType) {
+        self._hash = hash
+        self.count = MalIntType(self._hash.count)
+        self.isEmpty = self._hash.isEmpty
+        super.init()
+    }
+    convenience init(_ seq: MalSequence) {
+        var hash = MalHashType()
+        for var index: MalIntType = 0; index < seq.count; index += 2 {
+            hash[try! seq.nth(index)] = try! seq.nth(index + 1)
+        }
+        self.init(hash)
+    }
+    convenience init<T: CollectionType where T.Generator.Element == MalVal>(_ collection: T) {
+        // TBD: Use SequenceType/generate
+        var hash = MalHashType()
+        for var index = collection.startIndex; index != collection.endIndex; {
+            let key = collection[index++]
+            let value = collection[index++]
+            hash[key] = value
+        }
+        self.init(hash)
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String {
+        // TBD: Use reduce
+        var a = [String]()
+        for (k, v) in self._hash {
+            a.append("\(pr_str(k)) \(pr_str(v))")
+        }
+        let s = a.joinWithSeparator(" ")
+        return "{\(s)}"
+    }
+
+    // SequenceType
+    //
+    func generate() -> MalHashType.Generator { return self._hash.generate() }
+
+    // MalHashMap
+    //
+    let count: MalIntType
+    let isEmpty: Bool
+    var hash: MalHashType { return self._hash }
+    var keys: MalVal { return make_list(self._hash.keys) }
+    var values: MalVal { return make_list(self._hash.values) }
+
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalHashMap(self, meta) }
+
+    func value_for(key: MalVal) -> MalVal? {
+        return self._hash[key]
+    }
+
+    private let _hash: MalHashType
+}
+
+// Equatable
+//
+func ==(left: MalHashMap, right: MalHashMap) -> Bool {
+    if left.count != right.count { return false }
+    var left_gen = left.generate()
+    var right_gen = right.generate()
+    while true {
+        if let left = left_gen.next(), let right = right_gen.next() {
+            if left.0 != right.0 || left.1 != right.1 {
+                return false
+            }
+        } else {
+            break
+        }
+    }
+    return true
+}
+
+// ==================== MalAtom ====================
+
+final class MalAtom: MalVal {
+    override init() {
+        self._object = make_nil()
+        super.init()
+    }
+    init(_ other: MalAtom, _ meta: MalVal? = nil) {
+        self._object = other._object
+        super.init(other, meta)
+    }
+    init(object: MalVal) {
+        self._object = object
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "(atom \(self._object.description))" }
+
+    // MalAtom
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalAtom(self, meta) }
+    var object: MalVal { return self._object }
+
+    func set_object(obj: MalVal) -> MalVal {
+        self._object = obj
+        return obj
+    }
+
+    private var _object: MalVal
+}
+
+// Equatable
+//
+func ==(left: MalAtom, right: MalAtom) -> Bool { return left.object == right.object }
+
+// ==================== MalFunction ====================
+
+class MalFunction: MalVal {
+    override init() {
+        super.init()
+    }
+    init(_ other: MalFunction, _ meta: MalVal? = nil) {
+        super.init(other, meta)
+    }
+
+    // MalFunction
+    //
+    func apply(exprs: MalSequence) throws -> MalVal { die() }
+}
+
+// ==================== MalClosure ====================
+
+final class MalClosure: MalFunction {
+    typealias Evaluator = (MalVal, Environment) throws -> MalVal
+    typealias Parameters = (eval: Evaluator, args: MalSequence, body: MalVal, env: Environment)
+
+    override init() {
+        self._eval = nil
+        self._args = as_sequence(make_list())
+        self._body = make_nil()
+        self._env = Environment(outer: nil)
+        super.init()
+    }
+    init(_ other: MalClosure, _ meta: MalVal? = nil) {
+        self._eval = other._eval
+        self._args = other._args
+        self._body = other._body
+        self._env = other._env
+        super.init(other, meta)
+    }
+    init(_ p: Parameters) {
+        self._eval = p.eval
+        self._args = p.args
+        self._body = p.body
+        self._env = p.env
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "#<Closure>: (fn* \(self._args.description) \(self._body.description))" }
+
+    // MalFunction
+    //
+    override func apply(exprs: MalSequence) throws -> MalVal {
+        let new_env = Environment(outer: self._env)
+        let _ = try new_env.set_bindings(self._args, with_exprs: exprs)
+        // Calling EVAL indirectly via an 'eval' data member is a bit of a hack.
+        // We can't call EVAL directly because this file (types.swift) needs to
+        // be used with many different versions of the main MAL file
+        // (step[0-10]*.swift), and EVAL is declared differently across those
+        // versions. By using this indirection, we avoid that problem.
+        return try self._eval(self._body, new_env)
+    }
+
+    // MalClosure
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalClosure(self, meta) }
+
+    var args: MalSequence { return self._args }
+    var body: MalVal      { return self._body }
+    var env:  Environment { return self._env }
+
+    private let _eval: Evaluator!
+    private let _args: MalSequence
+    private let _body: MalVal
+    private let _env: Environment
+}
+
+// Equatable
+//
+func ==(left: MalClosure, right: MalClosure) -> Bool { return false }
+
+// ==================== MalBuiltin ====================
+
+final class MalBuiltin: MalFunction {
+    typealias Signature = (MalSequence) throws -> MalVal
+
+    override init() {
+        self._fn = nil
+        super.init()
+    }
+    init(_ other: MalBuiltin, _ meta: MalVal? = nil) {
+        self._fn = other._fn
+        super.init(other, meta)
+    }
+    init(_ fn: Signature) {
+        self._fn = fn
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "#<Builtin>" }
+
+    // MalFunction
+    //
+    override func apply(exprs: MalSequence) throws -> MalVal { return try self._fn(exprs) }
+
+    // MalBuiltin
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalBuiltin(self, meta) }
+
+    private let _fn: Signature!
+}
+
+// Equatable
+//
+func ==(left: MalBuiltin, right: MalBuiltin) -> Bool { return false }  // Can't compare function references in Swift
+
+// ==================== MalMacro ====================
+
+final class MalMacro : MalVal {
+    override init() {
+        self._closure = as_closure(make_closure())
+        super.init()
+    }
+    init(_ other: MalMacro, _ meta: MalVal? = nil) {
+        self._closure = other._closure
+        super.init(other, meta)
+    }
+    init(_ closure: MalClosure) {
+        self._closure = closure
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return self._closure.description }
+
+    // MalMacro
+    //
+    override func clone_with_meta(meta: MalVal) -> MalVal { return MalMacro(self, meta) }
+
+    var args: MalSequence { return self._closure.args }
+    var body: MalVal      { return self._closure.body }
+    var env:  Environment { return self._closure.env }
+
+    private let _closure: MalClosure
+}
+
+// Equatable
+//
+func ==(left: MalMacro, right: MalMacro) -> Bool { return false }
+
+
+// ==================== Constructors ====================
+
+// ----- Default -----
+
+func make_unknown   ()                              -> MalVal { return kUnknown }
+func make_nil       ()                              -> MalVal { return kNil }
+func make_true      ()                              -> MalVal { return kTrue }
+func make_false     ()                              -> MalVal { return kFalse }
+func make_comment   ()                              -> MalVal { return kComment }
+func make_integer   ()                              -> MalVal { return MalInteger() }
+func make_float     ()                              -> MalVal { return MalFloat() }
+func make_symbol    ()                              -> MalVal { return MalSymbol() }
+func make_keyword   ()                              -> MalVal { return MalKeyword() }
+func make_string    ()                              -> MalVal { return MalString() }
+func make_list      ()                              -> MalVal { return MalList() }
+func make_vector    ()                              -> MalVal { return MalVector() }
+func make_hashmap   ()                              -> MalVal { return MalHashMap() }
+func make_atom      ()                              -> MalVal { return MalAtom() }
+func make_closure   ()                              -> MalVal { return MalClosure() }
+func make_builtin   ()                              -> MalVal { return MalBuiltin() }
+func make_macro     ()                              -> MalVal { return MalMacro() }
+
+// ----- Copy -----
+
+func make_integer   (v: MalInteger)                 -> MalVal { return MalInteger(v) }
+func make_float     (v: MalFloat)                   -> MalVal { return MalFloat(v) }
+func make_symbol    (v: MalSymbol)                  -> MalVal { return MalSymbol(v) }
+func make_keyword   (v: MalKeyword)                 -> MalVal { return MalKeyword(v) }
+func make_string    (v: MalString)                  -> MalVal { return MalString(v) }
+func make_list      (v: MalList)                    -> MalVal { return MalList(v) }
+func make_vector    (v: MalVector)                  -> MalVal { return MalVector(v) }
+func make_hashmap   (v: MalHashMap)                 -> MalVal { return MalHashMap(v) }
+func make_atom      (v: MalAtom)                    -> MalVal { return MalAtom(v) }
+func make_closure   (v: MalClosure)                 -> MalVal { return MalClosure(v) }
+func make_builtin   (v: MalBuiltin)                 -> MalVal { return MalBuiltin(v) }
+func make_macro     (v: MalMacro)                   -> MalVal { return MalMacro(v) }
+
+// ----- Parameterized -----
+
+func make_integer   (v: MalIntType)                 -> MalVal { return MalInteger(v) }
+func make_float     (v: MalFloatType)               -> MalVal { return MalFloat(v) }
+func make_symbol    (v: String)                     -> MalVal { return MalSymbol(v) }
+func make_keyword   (v: String)                     -> MalVal { return MalKeyword(v) }
+func make_keyword   (v: MalString)                  -> MalVal { return MalKeyword(v) }
+func make_string    (v: String)                     -> MalVal { return MalString(v) }
+func make_list      (v: MalSequence)                -> MalVal { return MalList(seq: v) }
+func make_list      (v: MalVectorType)              -> MalVal { return MalList(v) }
+func make_list      (v: Array<MalVal>)              -> MalVal { return MalList(v) }
+func make_list_from (v: MalVal...)                  -> MalVal { return MalList(v) }
+func make_list<T: CollectionType where T.Generator.Element == MalVal>
+                    (v: T)                          -> MalVal { return MalList(v) }
+func make_vector    (v: MalSequence)                -> MalVal { return MalVector(seq: v) }
+func make_vector    (v: MalVectorType)              -> MalVal { return MalVector(v) }
+func make_vector    (v: Array<MalVal>)              -> MalVal { return MalVector(v) }
+func make_vector<T: CollectionType where T.Generator.Element == MalVal>
+                    (v: T)                          -> MalVal { return MalVector(v) }
+func make_hashmap   (v: MalSequence)                -> MalVal { return MalHashMap(v) }
+func make_hashmap   (v: MalHashType)                -> MalVal { return MalHashMap(v) }
+func make_hashmap<T: CollectionType where T.Generator.Element == MalVal>
+                    (v: T)                          -> MalVal { return MalHashMap(v) }
+func make_atom      (v: MalVal)                     -> MalVal { return MalAtom(object: v) }
+func make_closure   (v: MalClosure.Parameters)      -> MalVal { return MalClosure(v) }
+func make_builtin   (v: MalBuiltin.Signature)       -> MalVal { return MalBuiltin(v) }
+func make_macro     (v: MalClosure)                 -> MalVal { return MalMacro(v) }
+
+// ==================== Predicates ====================
+
+// ----- Simple -----
+
+func is_unknown     (v: MalVal) -> Bool           { return v is MalUnknown }
+func is_nil         (v: MalVal) -> Bool           { return v is MalNil }
+func is_true        (v: MalVal) -> Bool           { return v is MalTrue }
+func is_false       (v: MalVal) -> Bool           { return v is MalFalse }
+func is_comment     (v: MalVal) -> Bool           { return v is MalComment }
+func is_integer     (v: MalVal) -> Bool           { return v is MalInteger }
+func is_float       (v: MalVal) -> Bool           { return v is MalFloat }
+func is_symbol      (v: MalVal) -> Bool           { return v is MalSymbol }
+func is_keyword     (v: MalVal) -> Bool           { return v is MalKeyword }
+func is_string      (v: MalVal) -> Bool           { return v is MalString }
+func is_list        (v: MalVal) -> Bool           { return v is MalList }
+func is_vector      (v: MalVal) -> Bool           { return v is MalVector }
+func is_hashmap     (v: MalVal) -> Bool           { return v is MalHashMap }
+func is_atom        (v: MalVal) -> Bool           { return v is MalAtom }
+func is_closure     (v: MalVal) -> Bool           { return v is MalClosure }
+func is_builtin     (v: MalVal) -> Bool           { return v is MalBuiltin }
+func is_macro       (v: MalVal) -> Bool           { return v is MalMacro }
+
+// ----- Compound -----
+
+func is_truthy      (v: MalVal) -> Bool           { return !is_falsey(v) }
+func is_falsey      (v: MalVal) -> Bool           { return is_nil(v) || is_false(v) }
+func is_number      (v: MalVal) -> Bool           { return is_integer(v) || is_float(v) }
+func is_sequence    (v: MalVal) -> Bool           { return is_list(v) || is_vector(v) }
+func is_function    (v: MalVal) -> Bool           { return is_closure(v) || is_builtin(v) }
+
+// ==================== Converters/Extractors ====================
+
+func as_unknown     (v: MalVal) -> MalUnknown     { return v as! MalUnknown }
+func as_nil         (v: MalVal) -> MalNil         { return v as! MalNil }
+func as_true        (v: MalVal) -> MalTrue        { return v as! MalTrue }
+func as_false       (v: MalVal) -> MalFalse       { return v as! MalFalse }
+func as_comment     (v: MalVal) -> MalComment     { return v as! MalComment }
+func as_integer     (v: MalVal) -> MalInteger     { return v as! MalInteger }
+func as_float       (v: MalVal) -> MalFloat       { return v as! MalFloat }
+func as_symbol      (v: MalVal) -> MalSymbol      { return v as! MalSymbol }
+func as_keyword     (v: MalVal) -> MalKeyword     { return v as! MalKeyword }
+func as_string      (v: MalVal) -> MalString      { return v as! MalString }
+func as_list        (v: MalVal) -> MalList        { return v as! MalList }
+func as_vector      (v: MalVal) -> MalVector      { return v as! MalVector }
+func as_hashmap     (v: MalVal) -> MalHashMap     { return v as! MalHashMap }
+func as_atom        (v: MalVal) -> MalAtom        { return v as! MalAtom }
+func as_closure     (v: MalVal) -> MalClosure     { return v as! MalClosure }
+func as_builtin     (v: MalVal) -> MalBuiltin     { return v as! MalBuiltin }
+func as_macro       (v: MalVal) -> MalMacro       { return v as! MalMacro }
+
+func as_sequence    (v: MalVal) -> MalSequence    { return v as! MalSequence }
+func as_function    (v: MalVal) -> MalFunction    { return v as! MalFunction }
+
+func as_inttype     (v: MalVal) -> MalIntType     { return as_integer(v).integer }
+func as_floattype   (v: MalVal) -> MalFloatType   { return as_float(v).float }
+func as_stringtype  (v: MalVal) -> MalStringType  { return as_string(v).string }
+
+func as_inttype     (v: MalInteger) -> MalIntType   { return v.integer }
+func as_floattype   (v: MalFloat) -> MalFloatType   { return v.float }
+func as_stringtype  (v: MalString) -> MalStringType { return v.string }
+
+func as_unknownQ    (v: MalVal) -> MalUnknown?    { return v as? MalUnknown }
+func as_nilQ        (v: MalVal) -> MalNil?        { return v as? MalNil }
+func as_trueQ       (v: MalVal) -> MalTrue?       { return v as? MalTrue }
+func as_falseQ      (v: MalVal) -> MalFalse?      { return v as? MalFalse }
+func as_commentQ    (v: MalVal) -> MalComment?    { return v as? MalComment }
+func as_integerQ    (v: MalVal) -> MalInteger?    { return v as? MalInteger }
+func as_floatQ      (v: MalVal) -> MalFloat?      { return v as? MalFloat }
+func as_symbolQ     (v: MalVal) -> MalSymbol?     { return v as? MalSymbol }
+func as_keywordQ    (v: MalVal) -> MalKeyword?    { return v as? MalKeyword }
+func as_stringQ     (v: MalVal) -> MalString?     { return v as? MalString }
+func as_listQ       (v: MalVal) -> MalList?       { return v as? MalList }
+func as_vectorQ     (v: MalVal) -> MalVector?     { return v as? MalVector }
+func as_hashmapQ    (v: MalVal) -> MalHashMap?    { return v as? MalHashMap }
+func as_atomQ       (v: MalVal) -> MalAtom?       { return v as? MalAtom }
+func as_closureQ    (v: MalVal) -> MalClosure?    { return v as? MalClosure }
+func as_builtinQ    (v: MalVal) -> MalBuiltin?    { return v as? MalBuiltin }
+func as_macroQ      (v: MalVal) -> MalMacro?      { return v as? MalMacro }
+
+func as_sequenceQ   (v: MalVal) -> MalSequence?   { return v as? MalSequence }
+func as_functionQ   (v: MalVal) -> MalFunction?   { return v as? MalFunction }
+
+func as_inttypeQ    (v: MalVal) -> MalIntType?    { return as_integerQ(v)?.integer }
+func as_floattypeQ  (v: MalVal) -> MalFloatType?  { return as_floatQ(v)?.float }
+func as_stringtypeQ (v: MalVal) -> MalStringType? { return as_stringQ(v)?.string }
+
+// ==================== Exceptions ====================
+
+enum MalException: ErrorType, CustomStringConvertible {
+    case None
+    case Message(String)
+    case Object(MalVal)
+
+    var exception: MalVal {
+        switch self {
+            case .None:
+                return make_nil()
+            case .Message(let v):
+                return make_string(v)
+            case .Object(let v):
+                return v
+        }
+    }
+
+    // CustomStringConvertible
+    //
+    var description: String {
+        switch self {
+            case .None:
+                return "NIL Exception"
+            case .Message(let v):
+                return v
+            case .Object(let v):
+                return v.description
+        }
+    }
+}
+
+@noreturn
+func throw_error(v: String) throws { throw MalException.Message(v) }
+
+@noreturn
+func throw_error(v: MalVal) throws { throw MalException.Object(v) }
+
+// ==================== Utilities ====================
+
+@noreturn private func die() {
+    preconditionFailure("Should not get here")
+}
+
+func get_meta(v: MalVal) -> MalVal? {
+    return v.meta
+}
+
+func with_meta(obj: MalVal, _ meta: MalVal) -> MalVal {
+    return obj.clone_with_meta(meta)
+}
+
+func unescape(s: String) -> String {
+    var index = 0
+    var prev_is_escape = false
+    var str = ""
+    let chars = s.characters
+    for ch in chars {
+        if index == chars.count - 1 { continue }
+        if index++ == 0 { continue }
+        if prev_is_escape {
+            prev_is_escape = false
+            if ch == "n" { str.appendContentsOf("\n") }
+            else if ch == "r" { str.appendContentsOf("\r") }
+            else if ch == "t" { str.appendContentsOf("\t") }
+            else { str.append(ch) }
+        } else if ch == "\\" {
+            prev_is_escape = true
+        } else {
+            str.append(ch)
+        }
+    }
+    return str
+}
+
+func escape(s: String) -> String {
+    var str = ""
+    let chars = s.characters
+    for ch in chars {
+        if ch == "\n" { str.appendContentsOf("\\n"); continue }
+        if ch == "\r" { str.appendContentsOf("\\r"); continue }
+        if ch == "\t" { str.appendContentsOf("\\t"); continue }
+        if ch == "\"" || ch == "\\" { str.appendContentsOf("\\") }
+        str.append(ch)
+    }
+    str = "\"" + str + "\""
+    return str
+}
diff --git a/swift/types_enum.swift b/swift/types_enum.swift
new file mode 100644 (file)
index 0000000..1f610c3
--- /dev/null
@@ -0,0 +1,1010 @@
+//******************************************************************************
+// MAL - types, implemented as a Swift "enum".
+//******************************************************************************
+
+import Foundation
+
+// ===== Types / Constants / Variables =====
+
+typealias MalProtocol  = protocol<Equatable, CustomStringConvertible, Hashable>
+
+typealias MalIntType     = Int64
+typealias MalFloatType   = Double
+typealias MalSymbolType  = String
+typealias MalKeywordType = String
+typealias MalStringType  = String
+typealias MalVectorType  = ArraySlice<MalVal>
+typealias MalHashType    = Dictionary<MalVal, MalVal>
+
+typealias MalInteger     = MalIntType
+typealias MalFloat       = MalFloatType
+typealias MalSymbol      = MalSymbolType
+typealias MalKeyword     = MalKeywordType
+typealias MalString      = MalStringType
+
+private let kUnknown     = MalVal.TypeUnknown
+private let kNil         = MalVal.TypeNil
+private let kTrue        = MalVal.TypeTrue
+private let kFalse       = MalVal.TypeFalse
+private let kComment     = MalVal.TypeComment
+
+// ==================== MalSequence ====================
+
+class MalSequence : MalProtocol, SequenceType {
+    init() {
+        self.count = 0
+        self.isEmpty = true
+    }
+    init(_ seq: MalSequence) {
+        self.count = seq.count
+        self.isEmpty = seq.isEmpty
+    }
+    init(_ count: MalIntType) {
+        self.count = count
+        self.isEmpty = self.count == 0
+    }
+
+    // CustomStringConvertible
+    //
+    var description: String { die() }
+
+    // Hashable
+    //
+    var hashValue: Int { die() }
+
+    // SequenceType
+    //
+    func generate() -> MalVectorType.Generator { die() }
+
+    // MalSequence
+    //
+    let count: MalIntType
+    let isEmpty: Bool
+
+    func first() -> MalVal { die() }
+    func last() -> MalVal { die() }
+    func rest() -> MalVal { die() }
+    func nth(n: MalIntType) throws -> MalVal { die() }
+    func range_from(from: MalIntType, to: MalIntType) -> MalVal { die() }
+    func cons(element: MalVal) -> MalVal { die() }
+    func concat(seq: MalSequence) throws -> MalVal { die() }
+    func conj(seq: MalSequence) throws -> MalVal { die() }
+    func map<U>(@noescape transform: (MalVal) -> U) -> ArraySlice<U> { die() }
+    func reduce<U>(initial: U, @noescape combine: (U, MalVal) -> U) -> U { die() }
+}
+
+// Equatable
+//
+func ==(left: MalSequence, right: MalSequence) -> Bool {
+    if left.count != right.count { return false }
+    var left_gen = left.generate()
+    var right_gen = right.generate()
+    while true {
+        if let left = left_gen.next(), right = right_gen.next() {
+            if left != right {
+                return false
+            }
+        } else {
+            break
+        }
+    }
+    return true
+}
+
+// ==================== MalList ====================
+
+final class MalList : MalSequence {
+    override convenience init() {
+        self.init(MalVectorType())
+    }
+    init(_ other: MalList, _ meta: MalVal?) {
+        self._slice = other._slice
+        self._meta = meta
+        super.init(other)
+    }
+    override convenience init(_ seq: MalSequence) {
+        if let list = seq as? MalList { self.init(list._slice) }
+        else
+        if let vector = seq as? MalVector { self.init(vector._slice) }
+        else
+        { self.init(seq.reduce(MalVectorType()){ var s = $0; s.append($1); return s }) }
+    }
+    init(_ slice: MalVectorType) {
+        self._slice = slice
+        self._meta = nil
+        super.init(MalIntType(self._slice.count))
+    }
+    convenience init(_ array: Array<MalVal>) {
+        self.init(array[0..<array.count])
+    }
+    convenience init<T: SequenceType where T.Generator.Element == MalVal>(_ collection: T) {
+        self.init(collection.reduce(MalVectorType()){ var s = $0; s.append($1); return s })
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "(" + self.map { pr_str($0) }.joinWithSeparator(" ") + ")" }
+
+    // Hashable
+    //
+    override var hashValue: Int { return description.hashValue }
+
+    // SequenceType
+    //
+    override func generate() -> MalVectorType.Generator { return self._slice.generate() }
+
+    // MalSequence
+    //
+    override func first() -> MalVal { return isEmpty ? make_nil() : try! nth(0) }
+    override func last() -> MalVal { return try! nth(count - 1) }
+    override func rest() -> MalVal { return range_from(MalIntType(1), to: MalIntType(count)) }
+    override func nth(n: MalIntType) throws -> MalVal { guard n < count else { try throw_error("index (\(n)) out of range (\(count))") }; return self._slice[self._slice.startIndex.advancedBy(Int(n))] }
+    override func range_from(from: MalIntType, to: MalIntType) -> MalVal {
+        return from <= to && to <= count
+            ? make_list(self._slice[self._slice.startIndex.advancedBy(Int(from))..<self._slice.startIndex.advancedBy(Int(to))])
+            : make_list()
+    }
+    override func cons(element: MalVal) -> MalVal {
+        var result = self._slice
+        result.insert(element, atIndex: result.startIndex)
+        return make_list(result)
+    }
+    override func concat(seq: MalSequence) throws -> MalVal {
+        var result = self._slice
+        if let list = as_listQ(seq) {
+            result.appendContentsOf(list._slice)
+        } else if let vector = as_vectorQ(seq) {
+            result.appendContentsOf(vector._slice)
+        } else {
+            try throw_error("Expected sequence, got \(seq)")
+        }
+        return make_list(result)
+    }
+    override func conj(seq: MalSequence) throws -> MalVal {
+        var result: Array<MalVal>
+        if let list = as_listQ(seq) {
+            result = list._slice.reverse()
+        } else if let vector = as_vectorQ(seq) {
+            result = vector._slice.reverse()
+        } else {
+            try throw_error("Expected sequence, got \(seq)")
+        }
+        result.appendContentsOf(self._slice)
+        return make_list(result)
+    }
+    override func map<U>(@noescape transform: (MalVal) -> U) -> ArraySlice<U> { return ArraySlice<U>(self._slice.map(transform)) }
+    override func reduce<U>(initial: U, @noescape combine: (U, MalVal) -> U) -> U { return self._slice.reduce(initial, combine: combine) }
+    // MalList
+    //
+    var meta: MalVal? { return self._meta }
+
+    private let _slice: MalVectorType
+    private let _meta: MalVal?
+}
+
+// Equatable
+//
+func ==(left: MalList, right: MalList) -> Bool {
+    return (left as MalSequence) == (right as MalSequence)
+}
+
+// ==================== MalVector ====================
+
+final class MalVector : MalSequence {
+    override convenience init() {
+        self.init(MalVectorType())
+    }
+    init(_ other: MalVector, _ meta: MalVal?) {
+        self._slice = other._slice
+        self._meta = meta
+        super.init(other)
+    }
+    override convenience init(_ seq: MalSequence) {
+        if let list = seq as? MalList { self.init(list._slice) }
+        else
+        if let vector = seq as? MalVector { self.init(vector._slice) }
+        else
+        { self.init(seq.reduce(MalVectorType()){ var s = $0; s.append($1); return s }) }
+    }
+    init(_ slice: MalVectorType) {
+        self._slice = slice
+        self._meta = nil
+        super.init(MalIntType(self._slice.count))
+    }
+    convenience init(_ array: Array<MalVal>) {
+        self.init(array[0..<array.count])
+    }
+    convenience init<T: SequenceType where T.Generator.Element == MalVal>(_ collection: T) {
+        self.init(collection.reduce(MalVectorType()){ var s = $0; s.append($1); return s })
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "[" + self.map { pr_str($0) }.joinWithSeparator(" ") + "]" }
+
+    // Hashable
+    //
+    override var hashValue: Int { return description.hashValue }
+
+    // SequenceType
+    //
+    override func generate() -> MalVectorType.Generator { return self._slice.generate() }
+
+    // MalSequence
+    //
+    override func first() -> MalVal { return isEmpty ? make_nil() : try! nth(0) }
+    override func last() -> MalVal { return try! nth(count - 1) }
+    override func rest() -> MalVal { return range_from(MalIntType(1), to: MalIntType(count)) }
+    override func nth(n: MalIntType) throws -> MalVal { guard n < count else { try throw_error("index (\(n)) out of range (\(count))") }; return self._slice[self._slice.startIndex.advancedBy(Int(n))] }
+    override func range_from(from: MalIntType, to: MalIntType) -> MalVal {
+        return from <= to && to <= count
+            ? make_list(self._slice[self._slice.startIndex.advancedBy(Int(from))..<self._slice.startIndex.advancedBy(Int(to))])   // Yes, make_list
+            : make_list()                                   // Yes, make_list
+    }
+    override func cons(element: MalVal) -> MalVal {
+        var result = self._slice
+        result.insert(element, atIndex: result.startIndex)
+        return make_list(result)                            // Yes, make_list
+    }
+    override func concat(seq: MalSequence) throws -> MalVal {
+        var result = self._slice
+        if let list = as_listQ(seq) {
+            result.appendContentsOf(list._slice)
+        } else if let vector = as_vectorQ(seq) {
+            result.appendContentsOf(vector._slice)
+        } else {
+            try throw_error("Expected sequence, got \(seq)")
+        }
+        return make_vector(result)
+    }
+    override func conj(seq: MalSequence) throws -> MalVal {
+        var result = self._slice
+        if let list = as_listQ(seq) {
+            result.appendContentsOf(list._slice)
+        } else if let vector = as_vectorQ(seq) {
+            result.appendContentsOf(vector._slice)
+        } else {
+            try throw_error("Expected sequence, got \(seq)")
+        }
+        return make_vector(result)
+    }
+    override func map<U>(@noescape transform: (MalVal) -> U) -> ArraySlice<U> { return ArraySlice<U>(self._slice.map(transform)) }
+    override func reduce<U>(initial: U, @noescape combine: (U, MalVal) -> U) -> U { return self._slice.reduce(initial, combine: combine) }
+
+    // MalVector
+    //
+    var meta: MalVal? { return self._meta }
+
+    private let _slice: MalVectorType
+    private let _meta: MalVal?
+}
+
+// Equatable
+//
+func ==(left: MalVector, right: MalVector) -> Bool {
+    return (left as MalSequence) == (right as MalSequence)
+}
+
+// ==================== MalHashMap ====================
+
+final class MalHashMap : MalProtocol, SequenceType {
+    convenience init() {
+        self.init(MalHashType())
+    }
+    init(_ other: MalHashMap, _ meta: MalVal?) {
+        self._hash = other._hash
+        self._meta = meta
+        self.count = MalIntType(self._hash.count)
+        self.isEmpty = self._hash.isEmpty
+    }
+    init(_ hash: MalHashType) {
+        self._hash = hash
+        self._meta = nil
+        self.count = MalIntType(self._hash.count)
+        self.isEmpty = self._hash.isEmpty
+    }
+    convenience init(_ seq: MalSequence) {
+        var hash = MalHashType()
+        for var index: MalIntType = 0; index < seq.count; index += 2 {
+            hash[try! seq.nth(index)] = try! seq.nth(index + 1)
+        }
+        self.init(hash)
+    }
+    convenience init<T: CollectionType where T.Generator.Element == MalVal>(_ collection: T) {
+        var hash = MalHashType()
+        for var index = collection.startIndex; index != collection.endIndex; {
+            let key = collection[index++]
+            let value = collection[index++]
+            hash[key] = value
+        }
+        self.init(hash)
+    }
+
+    // CustomStringConvertible
+    //
+    var description: String {
+        var a = [String]()
+        for (k, v) in self._hash {
+            a.append("\(pr_str(k)) \(pr_str(v))")
+        }
+        let s = a.joinWithSeparator(" ")
+        return "{\(s)}"
+    }
+
+    // Hashable
+    //
+    var hashValue: Int { return description.hashValue }
+
+    // SequenceType
+    //
+    func generate() -> MalHashType.Generator { return self._hash.generate() }
+
+    // MalHashMap
+    //
+    let count: MalIntType
+    let isEmpty: Bool
+    var hash: MalHashType { return self._hash }
+    var keys: MalVal { return make_list(self._hash.keys) }
+    var values: MalVal { return make_list(self._hash.values) }
+    var meta: MalVal? { return self._meta }
+
+    func value_for(key: MalVal) -> MalVal? {
+        return self._hash[key]
+    }
+
+    private let _hash: MalHashType
+    private let _meta: MalVal?
+}
+
+// Equatable
+//
+func ==(left: MalHashMap, right: MalHashMap) -> Bool {
+    if left.count != right.count { return false }
+    var left_gen = left.generate()
+    var right_gen = right.generate()
+    while true {
+        if let left = left_gen.next(), right = right_gen.next() {
+            if left.0 != right.0 || left.1 != right.1 {
+                return false
+            }
+        } else {
+            break
+        }
+    }
+    return true
+}
+
+// ==================== MalAtom ====================
+
+final class MalAtom : MalProtocol {
+    convenience init() {
+        self.init(make_nil())
+    }
+    init(_ other: MalAtom, _ meta: MalVal?) {
+        self._object = other._object
+        self._meta = meta
+    }
+    init(_ object: MalVal) {
+        self._object = object
+        self._meta = nil
+    }
+
+    // CustomStringConvertible
+    //
+    var description: String { return "(atom \(pr_str(self._object)))" }
+
+    // Hashable
+    //
+    var hashValue: Int { return description.hashValue }
+
+    // MalAtom
+    //
+    var object: MalVal { return self._object }
+    var meta: MalVal? { return self._meta }
+
+    func set_object(obj: MalVal) -> MalVal {
+        self._object = obj
+        return obj
+    }
+
+    private var _object: MalVal
+    private let _meta: MalVal?
+}
+
+// Equatable
+//
+func ==(left: MalAtom, right: MalAtom) -> Bool { return left.object == right.object }
+
+// ==================== MalFunction ====================
+
+class MalFunction : MalProtocol {
+    init() {
+    }
+    init(_ other: MalFunction) {
+    }
+
+    // CustomStringConvertible
+    //
+    var description: String { die() }
+
+    // Hashable
+    //
+    var hashValue: Int { die() }
+
+    // MalFunction
+    //
+    func apply(exprs: MalSequence) throws -> MalVal { die() }
+}
+
+// Equatable
+//
+func ==(left: MalFunction, right: MalFunction) -> Bool { return false }
+
+// ==================== MalClosure ====================
+
+
+final class MalClosure : MalFunction {
+    typealias Evaluator = (MalVal, Environment) throws -> MalVal
+    typealias Parameters = (eval: Evaluator, args: MalSequence, body: MalVal, env: Environment)
+
+    override convenience init() {
+        self.init((
+            eval: {(a: MalVal, b: Environment) -> MalVal in make_nil() },
+            args: as_sequence(make_list()),
+            body: make_nil(),
+            env:  Environment(outer: nil)
+        ))
+    }
+    init(_ other: MalClosure, _ meta: MalVal?) {
+        self._eval = other._eval
+        self._args = other._args
+        self._body = other._body
+        self._env = other._env
+        self._meta = meta
+        super.init(other)
+    }
+    init(_ p: Parameters) {
+        self._eval = p.eval
+        self._args = p.args
+        self._body = p.body
+        self._env = p.env
+        self._meta = nil
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "#<Closure>: (fn* \(self._args.description) \(self._body.description))" }
+
+    // Hashable
+    //
+    override var hashValue: Int { return description.hashValue }
+
+    // MalFunction
+    //
+    override func apply(exprs: MalSequence) throws -> MalVal {
+        let new_env = Environment(outer: self._env)
+        let _ = try new_env.set_bindings(self._args, with_exprs: exprs)
+        // Calling EVAL indirectly via an 'eval' data member is a bit of a hack.
+        // We can't call EVAL directly because this file (types.swift) needs to
+        // be used with many different versions of the main MAL file
+        // (step[0-10]*.swift), and EVAL is declared differently across those
+        // versions. By using this indirection, we avoid that problem.
+        return try self._eval(self._body, new_env)
+    }
+
+    var args: MalSequence { return self._args }
+    var body: MalVal      { return self._body }
+    var env:  Environment { return self._env }
+    var meta: MalVal?     { return self._meta }
+
+    private let _eval: Evaluator!
+    private let _args: MalSequence
+    private let _body: MalVal
+    private let _env: Environment
+    private let _meta: MalVal?
+}
+
+// Equatable
+//
+func ==(left: MalClosure, right: MalClosure) -> Bool { return false }
+
+// ==================== MalBuiltin ====================
+
+final class MalBuiltin : MalFunction {
+    typealias Signature = (MalSequence) throws -> MalVal
+
+    override convenience init() {
+        self.init( {(MalSequence) -> MalVal in make_nil()} )
+    }
+    init(_ other: MalBuiltin, _ meta: MalVal?) {
+        self._fn = other._fn
+        self._meta = meta
+        super.init(other)
+    }
+    init(_ fn: Signature) {
+        self._fn = fn
+        self._meta = nil
+        super.init()
+    }
+
+    // CustomStringConvertible
+    //
+    override var description: String { return "#<Builtin>" }
+
+    // Hashable
+    //
+    override var hashValue: Int { return description.hashValue }
+
+    // MalBuiltin
+    //
+    override func apply(exprs: MalSequence) throws -> MalVal { return try self._fn(exprs) }
+    var meta: MalVal? { return self._meta }
+
+    private let _fn: Signature!
+    private let _meta: MalVal?
+}
+
+// Equatable
+//
+func ==(left: MalBuiltin, right: MalBuiltin) -> Bool { return false }  // Can't compare function references in Swift
+
+// ==================== MalMacro ====================
+
+final class MalMacro : MalProtocol {
+    convenience init() {
+        self.init(as_closure(make_closure()))
+    }
+    init(_ other: MalMacro, _ meta: MalVal?) {
+        self._closure = other._closure
+        self._meta = meta
+    }
+    init(_ closure: MalClosure) {
+        self._closure = closure
+        self._meta = nil
+    }
+
+    // CustomStringConvertible
+    //
+    var description: String { return self._closure.description }
+
+    // Hashable
+    //
+    var hashValue: Int { return description.hashValue }
+
+    var args: MalSequence { return self._closure.args }
+    var body: MalVal      { return self._closure.body }
+    var env:  Environment { return self._closure.env }
+    var meta: MalVal?     { return self._meta }
+
+    private let _closure: MalClosure
+    private let _meta: MalVal?
+}
+
+// Equatable
+//
+func ==(left: MalMacro, right: MalMacro) -> Bool { return false }
+
+// ==================== MalVal ====================
+
+enum MalVal : MalProtocol {
+    case TypeUnknown
+    case TypeNil
+    case TypeTrue
+    case TypeFalse
+    case TypeComment
+    case TypeInteger (MalInteger)
+    case TypeFloat   (MalFloat)
+    case TypeSymbol  (MalSymbol)
+    case TypeKeyword (MalKeyword)
+    case TypeString  (MalString)
+    case TypeList    (MalList)
+    case TypeVector  (MalVector)
+    case TypeHashMap (MalHashMap)
+    case TypeAtom    (MalAtom)
+    case TypeClosure (MalClosure)
+    case TypeBuiltin (MalBuiltin)
+    case TypeMacro   (MalMacro)
+
+    // CustomStringConvertible
+    //
+    var description: String {
+        switch self {
+            case .TypeUnknown: return "unknown"
+            case .TypeNil:     return "nil"
+            case .TypeTrue:    return "true"
+            case .TypeFalse:   return "false"
+            case .TypeComment: return "comment"
+            case .TypeInteger  (let v): return v.description
+            case .TypeFloat    (let v): return v.description
+            case .TypeSymbol   (let v): return v
+            case .TypeKeyword  (let v): return v
+            case .TypeString   (let v): return v
+            case .TypeList     (let v): return v.description
+            case .TypeVector   (let v): return v.description
+            case .TypeHashMap  (let v): return v.description
+            case .TypeAtom     (let v): return v.description
+            case .TypeClosure  (let v): return v.description
+            case .TypeBuiltin  (let v): return v.description
+            case .TypeMacro    (let v): return v.description
+        }
+    }
+
+    // Hashable
+    //
+    var hashValue: Int {
+        switch self {
+            case .TypeUnknown: return 0
+            case .TypeNil:     return 0
+            case .TypeTrue:    return 0
+            case .TypeFalse:   return 0
+            case .TypeComment: return 0
+            case .TypeInteger  (let v): return v.hashValue
+            case .TypeFloat    (let v): return v.hashValue
+            case .TypeSymbol   (let v): return v.hashValue
+            case .TypeKeyword  (let v): return v.hashValue
+            case .TypeString   (let v): return v.hashValue
+            case .TypeList     (let v): return v.hashValue
+            case .TypeVector   (let v): return v.hashValue
+            case .TypeHashMap  (let v): return v.hashValue
+            case .TypeAtom     (let v): return v.hashValue
+            case .TypeClosure  (let v): return v.hashValue
+            case .TypeBuiltin  (let v): return v.hashValue
+            case .TypeMacro    (let v): return v.hashValue
+        }
+    }
+}
+
+// Equatable
+//
+func ==(left: MalVal, right: MalVal) -> Bool {
+    switch (left, right) {
+        case (.TypeUnknown,             .TypeUnknown):              return true
+        case (.TypeNil,                 .TypeNil):                  return true
+        case (.TypeTrue,                .TypeTrue):                 return true
+        case (.TypeFalse,               .TypeFalse):                return true
+        case (.TypeComment,             .TypeComment):              return false
+        case (.TypeInteger (let vLeft), .TypeInteger (let vRight)): return vLeft == vRight
+        case (.TypeFloat   (let vLeft), .TypeFloat   (let vRight)): return vLeft == vRight
+        case (.TypeSymbol  (let vLeft), .TypeSymbol  (let vRight)): return vLeft == vRight
+        case (.TypeKeyword (let vLeft), .TypeKeyword (let vRight)): return vLeft == vRight
+        case (.TypeString  (let vLeft), .TypeString  (let vRight)): return vLeft == vRight
+        case (.TypeList    (let vLeft), .TypeList    (let vRight)): return vLeft == vRight
+        case (.TypeVector  (let vLeft), .TypeVector  (let vRight)): return vLeft == vRight
+        case (.TypeHashMap (let vLeft), .TypeHashMap (let vRight)): return vLeft == vRight
+        case (.TypeAtom    (let vLeft), .TypeAtom    (let vRight)): return vLeft == vRight
+        case (.TypeClosure (let vLeft), .TypeClosure (let vRight)): return vLeft == vRight
+        case (.TypeBuiltin (let vLeft), .TypeBuiltin (let vRight)): return vLeft == vRight
+        case (.TypeMacro   (let vLeft), .TypeMacro   (let vRight)): return vLeft == vRight
+
+        case (.TypeList    (let vLeft), .TypeVector  (let vRight)): return vLeft == vRight
+        case (.TypeVector  (let vLeft), .TypeList    (let vRight)): return vLeft == vRight
+
+        default:                                                    return false
+    }
+}
+
+func ==(left: MalList, right: MalVector) -> Bool {
+    if left.count != right.count { return false }
+    var left_gen = left.generate()
+    var right_gen = right.generate()
+    while true {
+        if let left = left_gen.next(), right = right_gen.next() {
+            if left != right {
+                return false
+            }
+        } else {
+            break
+        }
+    }
+    return true
+}
+
+func ==(left: MalVector, right: MalList) -> Bool {
+    if left.count != right.count { return false }
+    var left_gen = left.generate()
+    var right_gen = right.generate()
+    while true {
+        if let left = left_gen.next(), right = right_gen.next() {
+            if left != right {
+                return false
+            }
+        } else {
+            break
+        }
+    }
+    return true
+}
+
+// ==================== Constructors ====================
+
+// ----- Default -----
+
+func make_unknown     ()                              -> MalVal { return kUnknown }
+func make_nil         ()                              -> MalVal { return kNil }
+func make_true        ()                              -> MalVal { return kTrue }
+func make_false       ()                              -> MalVal { return kFalse }
+func make_comment     ()                              -> MalVal { return kComment }
+func make_integer     ()                              -> MalVal { return make_integer   (MalInteger()) }
+func make_float       ()                              -> MalVal { return make_float     (MalFloat()) }
+func make_symbol      ()                              -> MalVal { return make_symbol    (MalSymbol()) }
+func make_keyword     ()                              -> MalVal { return make_keyword   (MalKeyword()) }
+func make_string      ()                              -> MalVal { return make_string    (MalString()) }
+func make_list        ()                              -> MalVal { return make_list      (MalList()) }
+func make_vector      ()                              -> MalVal { return make_vector    (MalVector()) }
+func make_hashmap     ()                              -> MalVal { return make_hashmap   (MalHashMap()) }
+func make_atom        ()                              -> MalVal { return make_atom      (MalAtom()) }
+func make_closure     ()                              -> MalVal { return make_closure   (MalClosure()) }
+func make_builtin     ()                              -> MalVal { return make_builtin   (MalBuiltin()) }
+func make_macro       ()                              -> MalVal { return make_macro     (MalMacro()) }
+
+// ----- Base -----
+
+func make_integer     (v: MalInteger)                 -> MalVal { return MalVal.TypeInteger(v) }
+func make_float       (v: MalFloat)                   -> MalVal { return MalVal.TypeFloat(v) }
+func make_symbol      (v: MalSymbol)                  -> MalVal { return MalVal.TypeSymbol(v) }
+func make_keyword     (v: MalKeyword)                 -> MalVal { return MalVal.TypeKeyword(v) }
+func make_string      (v: MalString)                  -> MalVal { return MalVal.TypeString(v) }
+func make_list        (v: MalList)                    -> MalVal { return MalVal.TypeList(v) }
+func make_vector      (v: MalVector)                  -> MalVal { return MalVal.TypeVector(v) }
+func make_hashmap     (v: MalHashMap)                 -> MalVal { return MalVal.TypeHashMap(v) }
+func make_atom        (v: MalAtom)                    -> MalVal { return MalVal.TypeAtom(v) }
+func make_closure     (v: MalClosure)                 -> MalVal { return MalVal.TypeClosure(v) }
+func make_builtin     (v: MalBuiltin)                 -> MalVal { return MalVal.TypeBuiltin(v) }
+func make_macro       (v: MalMacro)                   -> MalVal { return MalVal.TypeMacro(v) }
+
+// ----- Parameterized -----
+
+func make_list        (v: MalSequence)                -> MalVal { return make_list(MalList(v)) }
+func make_list        (v: MalVectorType)              -> MalVal { return make_list(MalList(v)) }
+func make_list        (v: Array<MalVal>)              -> MalVal { return make_list(MalList(v)) }
+func make_list_from   (v: MalVal...)                  -> MalVal { return make_list(MalList(v)) }
+func make_list<T: SequenceType where T.Generator.Element == MalVal>
+                      (v: T)                          -> MalVal { return make_list(MalList(v)) }
+func make_vector      (v: MalSequence)                -> MalVal { return make_vector(MalVector(v)) }
+func make_vector      (v: MalVectorType)              -> MalVal { return make_vector(MalVector(v)) }
+func make_vector      (v: Array<MalVal>)              -> MalVal { return make_vector(MalVector(v)) }
+func make_vector_from (v: MalVal...)                  -> MalVal { return make_vector(MalVector(v)) }
+func make_vector<T: SequenceType where T.Generator.Element == MalVal>
+                      (v: T)                          -> MalVal { return make_vector(MalVector(v)) }
+func make_hashmap     (v: MalSequence)                -> MalVal { return make_hashmap(MalHashMap(v)) }
+func make_hashmap     (v: MalHashType)                -> MalVal { return make_hashmap(MalHashMap(v)) }
+func make_hashmap<T: CollectionType where T.Generator.Element == MalVal>
+                      (v: T)                          -> MalVal { return make_hashmap(MalHashMap(v)) }
+func make_atom        (v: MalVal)                     -> MalVal { return make_atom(MalAtom(v)) }
+func make_closure     (v: MalClosure.Parameters)      -> MalVal { return make_closure(MalClosure(v)) }
+func make_builtin     (v: MalBuiltin.Signature)       -> MalVal { return make_builtin(MalBuiltin(v)) }
+func make_macro       (v: MalClosure)                 -> MalVal { return make_macro(MalMacro(v)) }
+
+// ==================== Predicates ====================
+
+// ----- Simple -----
+
+func is_unknown       (v: MalVal) -> Bool { if case .TypeUnknown = v { return true } else { return false } }
+func is_nil           (v: MalVal) -> Bool { if case .TypeNil     = v { return true } else { return false } }
+func is_true          (v: MalVal) -> Bool { if case .TypeTrue    = v { return true } else { return false } }
+func is_false         (v: MalVal) -> Bool { if case .TypeFalse   = v { return true } else { return false } }
+func is_comment       (v: MalVal) -> Bool { if case .TypeComment = v { return true } else { return false } }
+func is_integer       (v: MalVal) -> Bool { if case .TypeInteger = v { return true } else { return false } }
+func is_float         (v: MalVal) -> Bool { if case .TypeFloat   = v { return true } else { return false } }
+func is_symbol        (v: MalVal) -> Bool { if case .TypeSymbol  = v { return true } else { return false } }
+func is_keyword       (v: MalVal) -> Bool { if case .TypeKeyword = v { return true } else { return false } }
+func is_string        (v: MalVal) -> Bool { if case .TypeString  = v { return true } else { return false } }
+func is_list          (v: MalVal) -> Bool { if case .TypeList    = v { return true } else { return false } }
+func is_vector        (v: MalVal) -> Bool { if case .TypeVector  = v { return true } else { return false } }
+func is_hashmap       (v: MalVal) -> Bool { if case .TypeHashMap = v { return true } else { return false } }
+func is_atom          (v: MalVal) -> Bool { if case .TypeAtom    = v { return true } else { return false } }
+func is_closure       (v: MalVal) -> Bool { if case .TypeClosure = v { return true } else { return false } }
+func is_builtin       (v: MalVal) -> Bool { if case .TypeBuiltin = v { return true } else { return false } }
+func is_macro         (v: MalVal) -> Bool { if case .TypeMacro   = v { return true } else { return false } }
+
+// ----- Compound -----
+
+func is_truthy        (v: MalVal) -> Bool { return !is_falsey(v) }
+func is_falsey        (v: MalVal) -> Bool { switch v { case .TypeNil,     .TypeFalse:   return true; default: return false } }
+func is_number        (v: MalVal) -> Bool { switch v { case .TypeInteger, .TypeFloat:   return true; default: return false } }
+func is_sequence      (v: MalVal) -> Bool { switch v { case .TypeList,    .TypeVector:  return true; default: return false } }
+func is_function      (v: MalVal) -> Bool { switch v { case .TypeClosure, .TypeBuiltin: return true; default: return false } }
+
+// ==================== Converters/Extractors ====================
+
+func as_integer       (v: MalVal) -> MalInteger  { if case .TypeInteger(let w) = v { return w }; die("expected integer, got \(v)") }
+func as_float         (v: MalVal) -> MalFloat    { if case .TypeFloat(let w)   = v { return w }; die("expected float, got \(v)") }
+func as_symbol        (v: MalVal) -> MalSymbol   { if case .TypeSymbol(let w)  = v { return w }; die("expected symbol, got \(v)") }
+func as_keyword       (v: MalVal) -> MalKeyword  { if case .TypeKeyword(let w) = v { return w }; die("expected keyword, got \(v)") }
+func as_string        (v: MalVal) -> MalString   { if case .TypeString(let w)  = v { return w }; die("expected string, got \(v)") }
+func as_list          (v: MalVal) -> MalList     { if case .TypeList(let w)    = v { return w }; die("expected list, got \(v)") }
+func as_vector        (v: MalVal) -> MalVector   { if case .TypeVector(let w)  = v { return w }; die("expected vector, got \(v)") }
+func as_hashmap       (v: MalVal) -> MalHashMap  { if case .TypeHashMap(let w) = v { return w }; die("expected hashmap, got \(v)") }
+func as_atom          (v: MalVal) -> MalAtom     { if case .TypeAtom(let w)    = v { return w }; die("expected atom, got \(v)") }
+func as_closure       (v: MalVal) -> MalClosure  { if case .TypeClosure(let w) = v { return w }; die("expected closure, got \(v)") }
+func as_builtin       (v: MalVal) -> MalBuiltin  { if case .TypeBuiltin(let w) = v { return w }; die("expected builtin, got \(v)") }
+func as_macro         (v: MalVal) -> MalMacro    { if case .TypeMacro(let w)   = v { return w }; die("expected macro, got \(v)") }
+
+func as_sequence      (v: MalVal) -> MalSequence {
+    switch v {
+        case .TypeList(let v): return v
+        case .TypeVector(let v): return v
+        default: die("expected sequence, got \(v)")
+    }
+}
+func as_function      (v: MalVal) -> MalFunction {
+    switch v {
+        case .TypeClosure(let v): return v
+        case .TypeBuiltin(let v): return v
+        default: die("expected function, got \(v)")
+    }
+}
+
+func as_inttype       (v: MalVal) -> MalIntType     { return as_integer(v) }
+func as_floattype     (v: MalVal) -> MalFloatType   { return as_float(v) }
+func as_stringtype    (v: MalVal) -> MalStringType  { return as_string(v) }
+
+func as_inttype       (v: MalInteger) -> MalIntType   { return v }
+func as_floattype     (v: MalFloat) -> MalFloatType   { return v }
+func as_stringtype    (v: MalString) -> MalStringType { return v }
+
+func as_integerQ      (v: MalVal) -> MalInteger? { if case .TypeInteger(let w) = v { return w }; return nil }
+func as_floatQ        (v: MalVal) -> MalFloat?   { if case .TypeFloat(let w)   = v { return w }; return nil }
+func as_symbolQ       (v: MalVal) -> MalSymbol?  { if case .TypeSymbol(let w)  = v { return w }; return nil }
+func as_keywordQ      (v: MalVal) -> MalKeyword? { if case .TypeKeyword(let w) = v { return w }; return nil }
+func as_stringQ       (v: MalVal) -> MalString?  { if case .TypeString(let w)  = v { return w }; return nil }
+func as_listQ         (v: MalVal) -> MalList?    { if case .TypeList(let w)    = v { return w }; return nil }
+func as_vectorQ       (v: MalVal) -> MalVector?  { if case .TypeVector(let w)  = v { return w }; return nil }
+func as_hashmapQ      (v: MalVal) -> MalHashMap? { if case .TypeHashMap(let w) = v { return w }; return nil }
+func as_atomQ         (v: MalVal) -> MalAtom?    { if case .TypeAtom(let w)    = v { return w }; return nil }
+func as_closureQ      (v: MalVal) -> MalClosure? { if case .TypeClosure(let w) = v { return w }; return nil }
+func as_builtinQ      (v: MalVal) -> MalBuiltin? { if case .TypeBuiltin(let w) = v { return w }; return nil }
+func as_macroQ        (v: MalVal) -> MalMacro?   { if case .TypeMacro(let w)   = v { return w }; return nil }
+
+func as_listQ         (v: MalSequence) -> MalList?    { return v as? MalList }
+func as_vectorQ       (v: MalSequence) -> MalVector?  { return v as? MalVector }
+
+func as_sequenceQ     (v: MalVal) -> MalSequence? {
+    switch v {
+        case .TypeList(let v): return v
+        case .TypeVector(let v): return v
+        default: return nil
+    }
+}
+func as_functionQ     (v: MalVal) -> MalFunction? {
+    switch v {
+        case .TypeClosure(let v): return v
+        case .TypeBuiltin(let v): return v
+        default: return nil
+    }
+}
+
+func as_inttypeQ      (v: MalVal) -> MalIntType?    { return as_integerQ(v) }
+func as_floattypeQ    (v: MalVal) -> MalFloatType?  { return as_floatQ(v) }
+func as_stringtypeQ   (v: MalVal) -> MalStringType? { return as_stringQ(v) }
+
+// ==================== Exceptions ====================
+
+enum MalException: ErrorType, CustomStringConvertible {
+    case None
+    case Message(String)
+    case Object(MalVal)
+
+    var exception: MalVal {
+        switch self {
+            case .None:
+                return make_nil()
+            case .Message(let v):
+                return make_string(v)
+            case .Object(let v):
+                return v
+        }
+    }
+
+    // CustomStringConvertible
+    //
+    var description: String {
+        switch self {
+            case .None:
+                return "NIL Exception"
+            case .Message(let v):
+                return v
+            case .Object(let v):
+                return v.description
+        }
+    }
+}
+
+@noreturn
+func throw_error(v: String) throws { throw MalException.Message(v) }
+
+@noreturn
+func throw_error(v: MalVal) throws { throw MalException.Object(v) }
+
+// ==================== Utilities ====================
+
+@noreturn private func die(msg: String) {
+    preconditionFailure(msg)
+}
+
+@noreturn private func die() {
+    die("Should not get here")
+}
+
+func get_meta(v: MalVal) -> MalVal? {
+    switch v {
+        case .TypeUnknown: return nil
+        case .TypeNil:     return nil
+        case .TypeTrue:    return nil
+        case .TypeFalse:   return nil
+        case .TypeComment: return nil
+        case .TypeInteger: return nil
+        case .TypeFloat:   return nil
+        case .TypeSymbol:  return nil
+        case .TypeKeyword: return nil
+        case .TypeString:  return nil
+        case .TypeList     (let v): return v.meta
+        case .TypeVector   (let v): return v.meta
+        case .TypeHashMap  (let v): return v.meta
+        case .TypeAtom     (let v): return v.meta
+        case .TypeClosure  (let v): return v.meta
+        case .TypeBuiltin  (let v): return v.meta
+        case .TypeMacro    (let v): return v.meta
+    }
+}
+
+func with_meta(obj: MalVal, _ meta: MalVal) -> MalVal {
+    switch obj {
+        case .TypeUnknown: return obj
+        case .TypeNil:     return obj
+        case .TypeTrue:    return obj
+        case .TypeFalse:   return obj
+        case .TypeComment: return obj
+        case .TypeInteger: return obj
+        case .TypeFloat:   return obj
+        case .TypeSymbol:  return obj
+        case .TypeKeyword: return obj
+        case .TypeString:  return obj
+        case .TypeList     (let v): return make_list(MalList(v, meta))
+        case .TypeVector   (let v): return make_vector(MalVector(v, meta))
+        case .TypeHashMap  (let v): return make_hashmap(MalHashMap(v, meta))
+        case .TypeAtom     (let v): return make_atom(MalAtom(v, meta))
+        case .TypeClosure  (let v): return make_closure(MalClosure(v, meta))
+        case .TypeBuiltin  (let v): return make_builtin(MalBuiltin(v, meta))
+        case .TypeMacro    (let v): return make_macro(MalMacro(v, meta))
+    }
+}
+
+func unescape(s: String) -> String {
+    var index = 0
+    var prev_is_escape = false
+    var str = ""
+    let chars = s.characters
+    for ch in chars {
+        if index == chars.count - 1 { continue }
+        if index++ == 0 { continue }
+        if prev_is_escape {
+            prev_is_escape = false
+            if ch == "n" { str.appendContentsOf("\n") }
+            else if ch == "r" { str.appendContentsOf("\r") }
+            else if ch == "t" { str.appendContentsOf("\t") }
+            else { str.append(ch) }
+        } else if ch == "\\" {
+            prev_is_escape = true
+        } else {
+            str.append(ch)
+        }
+    }
+    return str
+}
+
+func escape(s: String) -> String {
+    var str = ""
+    let chars = s.characters
+    for ch in chars {
+        if ch == "\n" { str.appendContentsOf("\\n"); continue }
+        if ch == "\r" { str.appendContentsOf("\\r"); continue }
+        if ch == "\t" { str.appendContentsOf("\\t"); continue }
+        if ch == "\"" || ch == "\\" { str.appendContentsOf("\\") }
+        str.append(ch)
+    }
+    str = "\"" + str + "\""
+    return str
+}