Update for Xcode 7.0
authorKeith Rollin <keith.rollin@gmail.com>
Tue, 22 Sep 2015 01:26:47 +0000 (18:26 -0700)
committerKeith Rollin <keith.rollin@gmail.com>
Tue, 22 Sep 2015 01:26:47 +0000 (18:26 -0700)
Optimizations:

* In Environment, skip checking unused local slots if we’ve switched
  over to the general map.
* Mark as much as possible with “final” and “private”, and build with
  -whole-module-optimization, per
  https://developer.apple.com/swift/blog/?id=27.
* Refactor to include alternate types.swift with an implementation based
  on “enum”. Add Makefile variable allowing us to switch between the two
  so that we can compare the results. (At the time of this writing,
  using the class-based implementation is better in terms of both size
  and speed.)

Swift 1.2:

* Use the form of “if-let-as” that allows us to unwrap multiple
  optionals at once.
* Use Swift’s as? rather than our own predicates after determining that
  the former did not incur a performance hit.

Swift 2.0:

* Remove some Array conversions where Foundation/Cocoa is now declared
  to return arrays of the desired type.
* println -> print.
* print() -> print("")
* Remove some NSError* parameters; changed to do/try/catch.
* Use Swift exception handling rather than tunneling that information in
  MalError.
* Use `guard` statements where it makes sense. Especially `guard let a =
  b as? c` statements in order to reduce subsequent forced unwrapping.
* Changed count(str) -> str.characters.count.
* Changed Swift.reduce(coll, ...) -> coll.reduce(...).
* Changed reverse(coll) -> coll.reverse().
* Changed use of .allZeros -> default OptionSet c'tor.
* Changed Printable -> CustomStringConvertible.
* Changed Sequence.extend -> Sequence.appendContentsOf
* Changed String.join -> Sequence.joinWithSeparator
* Changed advance(index, delta) -> index.advancedBy(delta)
* Addressed change in function parameter name requirements.
* Added and used substring(s, begin, end).
* Changed “for ch in str” to “for ch in str.characters”
* Changed some switch/case statements to briefer if/case statements.
* Slices are no longer necessarily 0-based.
* Sprinkle in some @noescapes.
* Search for the most recent swiftc compiler to use if Xcode and
  Xcode-beta are both installed.

Other:
* Delete debugger symbols in `make clean`.
* Rebuild if Makefile is changed.

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
+}