diff --git a/IMPLS.yml b/IMPLS.yml index 9f6427e4c2..15749a158f 100644 --- a/IMPLS.yml +++ b/IMPLS.yml @@ -115,7 +115,6 @@ IMPL: - {IMPL: zig} # See .travis.yml (for older osx / xcode tests) -# - {IMPL: swift, NO_DOCKER: 1, OS: xcode7.3} - {IMPL: swift3} # - {IMPL: swift3, NO_DOCKER: 1, OS: xcode8} - {IMPL: swift4} diff --git a/README.md b/README.md index 1c4496d64d..7f5bade334 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ process guide](process/guide.md) there is also a [mal/make-a-lisp FAQ](docs/FAQ.md) where I attempt to answer some common questions. -**3. Mal is implemented in 89 languages (95 different implementations and 118 runtime modes)** +**3. Mal is implemented in 88 languages (94 different implementations and 117 runtime modes)** | Language | Creator | | -------- | ------- | @@ -125,7 +125,6 @@ FAQ](docs/FAQ.md) where I attempt to answer some common questions. | [Scheme (R7RS)](#scheme-r7rs) | [Vasilij Schneidermann](https://github.com/wasamasa) | | [Skew](#skew) | [Dov Murik](https://github.com/dubek) | | [Standard ML](#sml) | [Fabian Bergström](https://github.com/fabjan) | -| [Swift 2](#swift) | [Keith Rollin](https://github.com/keith-rollin) | | [Swift 3](#swift-3) | [Joel Martin](https://github.com/kanaka) | | [Swift 4](#swift-4) | [陆遥](https://github.com/LispLY) | | [Swift 5](#swift-5) | [Oleg Montak](https://github.com/MontakOleg) | @@ -177,7 +176,7 @@ make DOCKERIZE=1 "repl^IMPL^stepX" make DOCKERIZE=1 "repl^IMPL" ``` -## External Implementations +## External / Alternate Implementations The following implementations are maintained as separate projects: @@ -190,6 +189,10 @@ The following implementations are maintained as separate projects: * [by Tim Morgan](https://github.com/seven1m/mal-rust) * [by vi](https://github.com/vi/mal-rust-vi) - using [Pest](https://pest.rs/) grammar, not using typical Mal infrastructure (cargo-ized steps and built-in converted tests). +### Swift 2 + +* [by Keith Rollin](https://github.com/kanaka/mal/tree/fbfe678/impls/swift) - This implementation used to be in the repo. However, Swift 2 is no longer easily buildable/testable. + ### Q * [by Ali Mohammad Pur](https://github.com/alimpfard/mal/tree/q/impls/q) - The Q implementation works fine but it requires a proprietary manual download that can't be Dockerized (or integrated into the mal CI pipeline) so for now it remains a separate project. @@ -1119,18 +1122,6 @@ make sml_MODE=mosml ``` -### Swift - -The Swift implementation of mal requires the Swift 2.0 compiler (Xcode -7.0) to build. Older versions will not work due to changes in the -language and standard library. - -``` -cd impls/swift -make -./stepX_YYY -``` - ### Swift 3 The Swift 3 implementation of mal requires the Swift 3.0 compiler. It diff --git a/impls/swift/Makefile b/impls/swift/Makefile deleted file mode 100644 index 6372e0e043..0000000000 --- a/impls/swift/Makefile +++ /dev/null @@ -1,228 +0,0 @@ -################################################################################ -# -# Makefile for the Swift implementation of MAL. -# -# The MAL project consists of building up a dialect/subset of Clojure over a -# series of steps. Each step implements a new feature or concept in an easily -# understandable and approachable manner. Each step can be built on its own and -# tested. Each step is built from a step-specific "step.swift" file and a set -# of files common to all steps. -# -# The general approach in this file is to discover the set of "step" source -# files (step0_repl.swift, etc.), and build corresponding executable files -# (step0_repl, etc) from them and from the set of supporting Swift files. -# Since the set of "step" files is discovered on-the-fly, the rules to make -# those files are also generated on-the-fly using $(eval). -# -# The various "step0_repl.swift", etc., source files are actually generated -# from a file called "templates/step.swift". Since each "step" file -# incrementally builds towards the final, complete "step" file, -# "templates/step.swift" is -- for the most part -- a copy of this final "step" -# file with each line annotated with the step in which that line is introduced. -# Through the use of a simple filter program, the "templates/step.swift" file -# can then be processed to produce each intermediate "step" file. This Makefile -# takes care of performing that processing any time "templates/step.swift" -# changes. -# -# MAKE TARGETS: -# -# all: -# Make all step targets, (re)generating source files if needed. -# alls: -# (Re)generate source files, if needed. -# step0_repl, step1_read_print, etc.: -# Make the corresponding step target. -# s0...sN: -# Shortcuts for the previous targets. -# step0_repl.swift, step1_read_print.swift, etc.: -# (Re)generate source files for the corresponding step target, if -# needed. -# ss0...ssN: -# Shortcuts for the previous targets. -# clean: -# Delete all built executables. Generated source files are *not* -# deleted. -# dump: -# Print some Make variables for debugging. -# -# TODO: -# * Compile each .swift file into an intermediate .o file and link the .o -# files, rather than performing a complete build of all files any time -# any one of them is out-of-date. Here are the commands generated when -# using `swiftc -v`: -# -# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift \ -# -frontend \ -# -c \ -# -primary-file stepA_mal.swift \ -# ./core.swift \ -# ./env.swift \ -# ./main.swift \ -# ./printer.swift \ -# ./reader.swift \ -# ./readline.swift \ -# ./types.swift \ -# -target x86_64-apple-darwin14.1.0 \ -# -target-cpu core2 \ -# -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk \ -# -import-objc-header ./bridging-header.h \ -# -color-diagnostics \ -# -Onone \ -# -ledit \ -# -module-name stepA_mal \ -# -o /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/stepA_mal-e0a836.o -# ... Similar for each source file... -# /usr/bin/ld \ -# /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/stepA_mal-e0a836.o \ -# /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/core-28b620.o \ -# /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/env-5d8422.o \ -# /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/main-e79633.o \ -# /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/printer-cdd3e5.o \ -# /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/reader-bb188a.o \ -# /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/readline-53df55.o \ -# /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/types-7cb250.o \ -# -L /usr/lib \ -# -ledit \ -# -syslibroot \ -# /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk \ -# -lSystem \ -# -arch x86_64 \ -# -L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \ -# -rpath /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \ -# -macosx_version_min 10.10.0 \ -# -no_objc_category_merging \ -# -o stepA_mal -# -# * Consider adding a clean-dist (or similar) that deletes the generated -# "step" source files. -# -################################################################################ - -# -# Discover the set of "step" source files (those having the form -# "step<#>_foo.swift") -# -SRCS := $(wildcard ./step*.swift) - -# -# From the set of "step" source files, generate the set of executable files -# (those having the form "step<#>_foo") -# -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 <#>. -# -# (Is there a better way to do this? $(patsubst) seems to be the most -# appropriate built-in command, but it doesn't seem powerful enough.) -# -# (I've included a `sed` version in case relying on bash is contraindicated.) -# -get_step_number = $(shell echo $(1) | sed -e "s/.*step\(.\).*/\1/") -#get_step_number = $(shell [[ $(1) =~ step(.)_.* ]] ; echo $${BASH_REMATCH[1]}) - -# -# Working from the list of discovered "step<#>_foo.swift" files, generate the -# list of step numbers. -# -get_all_step_numbers = $(foreach SRC,$(SRCS),$(call get_step_number,$(SRC))) - -# -# Generate the dependencies for the "all" target. This list has the form -# "s0 s1 ... sN" for all N returned by get_all_step_numbers. That is: -# -# all: s0 s1 ... sN -# -# Also create an "alls" target that just regenerates all the "step" files from -# the corresponding template file. -# -$(eval all: $(patsubst %,s%,$(call get_all_step_numbers))) -$(eval alls: $(patsubst %,ss%,$(call get_all_step_numbers))) - -# -# Generate the dependencies for the ".PHONY" target. That is: -# -# .PHONY: all clean dump s0 s1 ... sN -# -$(eval .PHONY: all clean dump $(patsubst %,s%,$(call get_all_step_numbers))) - -# -# Define the "EZ" targets, where "s0" builds "step0_repl", "s1" builds -# "step1_read_print", etc. That is: -# -# s0: step0_repl -# s1: step1_read_print -# ... -# sN: stepN_foo -# -# Also create corresponding targets that rebuild the sources files: -# -# ss0: step0_repl.swift -# ss1: step1_read_print.swift -# ... -# ssN: stepN_foo.swift -# -$(foreach EXE,$(EXES),$(eval s$(call get_step_number,$(EXE)): $(EXE))) -$(foreach SRC,$(SRCS),$(eval ss$(call get_step_number,$(SRC)): $(SRC))) - -# -# Various helpful variables. -# -DEV_DIR := $(firstword $(wildcard /Applications/Xcode-beta.app /Applications/Xcode.app)) -SWIFT := $(shell DEVELOPER_DIR="$(DEV_DIR)" xcrun --find swiftc 2>/dev/null) -SDKROOT := $(shell DEVELOPER_DIR="$(DEV_DIR)" xcrun --show-sdk-path 2>/dev/null) -STEP_TEMPLATE := ./templates/step.swift -FILTER := ./templates/filter_steps.sh -UTIL_SRC := $(filter-out $(STEP_TEMPLATE) $(SRCS),$(wildcard ./*.swift)) -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) - -# -# Build the executable from the input sources consisting of the appropriate -# "step" file and the supporting files in $(UTIL_SRC). -# -$(EXES) : % : %.swift $(UTIL_SRC) ./Makefile - @echo "Making : $@" - @$(SWIFT) $< $(COMMON) -o $@ - -# -# Build the "step" source file ("step<#>_foo.swift") from the step template -# file that combines all the steps in one file. -# -$(SRCS) : % : $(STEP_TEMPLATE) ./Makefile - @echo "Generating: $@" - @$(FILTER) $(call get_step_number,$@) $< $@ - -# -# Delete all of the build output (other than generated "step" source files) -# -clean: - @rm -rf $(EXES) $(DSYMS) - -# -# Display some variables for debugging. -# -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)" diff --git a/impls/swift/bridging-header.h b/impls/swift/bridging-header.h deleted file mode 100644 index 9679345cae..0000000000 --- a/impls/swift/bridging-header.h +++ /dev/null @@ -1,15 +0,0 @@ -// This is the "bridging" file for the Swift version of MAL. A bridging file -// brings in C/ObjC types and makes them available to Swift source code, using -// the type conversion process described in: -// -// https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-XID_11 -// -// The mechanism for creating and using a bridging file is only documented for -// Xcode users. However, the following article describes how to specify a -// bridging file on the command line: -// -// http://stackoverflow.com/questions/24131476/compiling-and-linking-swift-plus-objective-c-code-from-the-os-x-command-line -// - -#include -#include diff --git a/impls/swift/core.swift b/impls/swift/core.swift deleted file mode 100644 index 8b600b373f..0000000000 --- a/impls/swift/core.swift +++ /dev/null @@ -1,770 +0,0 @@ -//****************************************************************************** -// 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_stringQ(obj: MalVal) throws -> Bool { - return is_string(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_numberQ(obj: MalVal) throws -> Bool { - return is_integer(obj) || is_float(obj) -} - -private func fn_functionQ(obj: MalVal) throws -> Bool { - return is_function(obj) -} - -private func fn_macroQ(obj: MalVal) throws -> Bool { - return is_macro(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_vec(seq: MalSequence) throws -> MalVal { - return make_vector(seq) -} - -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(arg: MalVal) throws -> MalVal { - if is_nil(arg) { - return make_list() - } - if let seq = as_sequenceQ(arg) { - return seq.rest() - } - try throw_error("expected sequence, got \(arg)") -} - -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_seq(seq: MalVal) throws -> MalVal { - if let list = as_listQ(seq) { - return list.count > 0 ? list : make_nil() - } else if let vector = as_vectorQ(seq) { - return vector.count > 0 ? make_list(vector) : make_nil() - } else if let str = as_stringQ(seq) { - if str.string.characters.count == 0 { return make_nil() } - return make_list(str.string.characters.map { make_string(String($0)) }) - } else if is_nil(seq) { - return make_nil() - } else { - try throw_error("seq: called with non-sequence") - } - return seq -} - -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) }, - "string?": { try unwrap_args($0, forFunction: fn_stringQ) }, - "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) }, - "number?": { try unwrap_args($0, forFunction: fn_numberQ) }, - "fn?": { try unwrap_args($0, forFunction: fn_functionQ) }, - "macro?": { try unwrap_args($0, forFunction: fn_macroQ) }, - - "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) }, - "vec": { try unwrap_args($0, forFunction: fn_vec) }, - "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) }, - "seq": { try unwrap_args($0, forFunction: fn_seq) }, - - "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)) - } -} diff --git a/impls/swift/env.swift b/impls/swift/env.swift deleted file mode 100644 index ba6205d81f..0000000000 --- a/impls/swift/env.swift +++ /dev/null @@ -1,114 +0,0 @@ -//****************************************************************************** -// MAL - env -//****************************************************************************** - -import Foundation - -typealias EnvironmentVars = [MalSymbol: MalVal] - -private let kSymbolAmpersand = as_symbol(make_symbol("&")) -private let kSymbolNil = as_symbol(make_symbol("")) -private let kNil = make_nil() - -final class Environment { - init(outer: Environment?) { - self.outer = outer - } - - 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, try! exprs.nth(index)) - } else { - set(sym, kNil) - } - continue - } - - 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 - } else if num_bindings == 1 { - if slot_name0 == sym { slot_value0 = value } - else { slot_name1 = sym; slot_value1 = value; ++num_bindings } - } else if num_bindings == 2 { - if slot_name0 == sym { slot_value0 = value } - else if slot_name1 == sym { slot_value1 = value } - else { slot_name2 = sym; slot_value2 = value; ++num_bindings } - } else if num_bindings == 3 { - if slot_name0 == sym { slot_value0 = value } - else if slot_name1 == sym { slot_value1 = value } - else if slot_name2 == sym { slot_value2 = value } - else { slot_name3 = sym; slot_value3 = value; ++num_bindings } - } else if num_bindings == 4 { - if slot_name0 == sym { slot_value0 = value } - else if slot_name1 == sym { slot_value1 = value } - else if slot_name2 == sym { slot_value2 = value } - else if slot_name3 == sym { slot_value3 = value } - else { - data[slot_name0] = slot_value0 - data[slot_name1] = slot_value1 - data[slot_name2] = slot_value2 - data[slot_name3] = slot_value3 - data[sym] = value; ++num_bindings - } - } else { - data[sym] = value - } - return value - } - - func get(sym: MalSymbol) -> MalVal? { - 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 } } - if num_bindings > 0 { if slot_name0 == sym { return slot_value0 } } - return outer?.get(sym) - } - - private var outer: Environment? - private var data = EnvironmentVars() - private var num_bindings = 0 - 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 -} diff --git a/impls/swift/main.swift b/impls/swift/main.swift deleted file mode 100644 index 428e0578ff..0000000000 --- a/impls/swift/main.swift +++ /dev/null @@ -1,18 +0,0 @@ -//****************************************************************************** -// MAL - main -//****************************************************************************** - -// Swift requires that main() be invoked from a file named "main.swift". See the -// paragraph "Application Entry Points and “main.swift” on -// https://developer.apple.com/swift/blog/?id=7: -// -// You’ll notice that earlier we said top-level code isn’t allowed in most -// of your app’s source files. The exception is a special file named -// “main.swift”, which behaves much like a playground file, but is built -// with your app’s source code. The “main.swift” file can contain top-level -// code, and the order-dependent rules apply as well. In effect, the first -// line of code to run in “main.swift” is implicitly defined as the main -// entrypoint for the program. This allows the minimal Swift program to be -// a single line — as long as that line is in “main.swift”. - -main() diff --git a/impls/swift/printer.swift b/impls/swift/printer.swift deleted file mode 100644 index c6ed030048..0000000000 --- a/impls/swift/printer.swift +++ /dev/null @@ -1,27 +0,0 @@ -//****************************************************************************** -// MAL - printer -//****************************************************************************** - -import Foundation - -var MalValPrintReadably = true - -func with_print_readably(print_readably: Bool, fn: () -> T) -> T { - let old = MalValPrintReadably - MalValPrintReadably = print_readably - let result = fn() - MalValPrintReadably = old - return result -} - -func pr_str(m: MalVal, _ print_readably: Bool = MalValPrintReadably) -> String { - return with_print_readably(print_readably) { - if is_string(m) { - return print_readably ? escape(m.description) : m.description - } - if is_keyword(m) { - return ":\(m.description)" - } - return m.description - } -} diff --git a/impls/swift/reader.swift b/impls/swift/reader.swift deleted file mode 100644 index d51c536cee..0000000000 --- a/impls/swift/reader.swift +++ /dev/null @@ -1,203 +0,0 @@ -//****************************************************************************** -// 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 - "|" + - "(^\".*$)" + // Invalid/unclosed 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) { // Invalid/unclosed string - try throw_error("expected '\"', got EOF") - } else if have_match(match, at_index: 9) { // Keyword - return make_keyword(token[token.startIndex.successor() ..< token.endIndex]) - } else if have_match(match, at_index: 10) { // 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 -} diff --git a/impls/swift/readline.swift b/impls/swift/readline.swift deleted file mode 100644 index 3cb00845f2..0000000000 --- a/impls/swift/readline.swift +++ /dev/null @@ -1,46 +0,0 @@ -//****************************************************************************** -// MAL - readline -//****************************************************************************** - -import Foundation - -private let HISTORY_FILE = "~/.mal-history" - -private func with_history_file(do_to_history_file: (UnsafePointer) -> ()) { - HISTORY_FILE.withCString { - (c_str) -> () in - let abs_path = tilde_expand(UnsafeMutablePointer(c_str)) - if abs_path != nil { - do_to_history_file(abs_path) - free(abs_path) - } - } -} - -func load_history_file() { - using_history() - with_history_file { - let _ = read_history($0) - } -} - -func save_history_file() { - // Do this? stifle_history(1000) - with_history_file { - let _ = write_history($0) - } -} - -func _readline(prompt: String) -> String? { - let line = prompt.withCString { - (c_str) -> UnsafeMutablePointer in - return readline(c_str) - } - if line != nil { - if let result = String(UTF8String: line) { - add_history(line) - return result - } - } - return nil -} diff --git a/impls/swift/run b/impls/swift/run deleted file mode 100755 index 8ba68a5484..0000000000 --- a/impls/swift/run +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exec $(dirname $0)/${STEP:-stepA_mal} "${@}" diff --git a/impls/swift/step0_repl.swift b/impls/swift/step0_repl.swift deleted file mode 100644 index 1de32e6639..0000000000 --- a/impls/swift/step0_repl.swift +++ /dev/null @@ -1,64 +0,0 @@ -//****************************************************************************** -// MAL - step 0 - repl -//****************************************************************************** -// 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 - -// Parse the string into an AST. -// -private func READ(str: String) -> String { - return str -} - -// Walk the AST and completely evaluate it, handling macro expansions, special -// forms and function calls. -// -private func EVAL(ast: String) -> String { - return ast -} - -// Convert the value into a human-readable string for printing. -// -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. -// -private func RE(text: String) -> String { - let ast = READ(text) - let exp = EVAL(ast) - return exp -} - -// Perform the full READ/EVAL/PRINT, returning a printable string. -// -private func REP(text: String) -> String { - let exp = RE(text) - return PRINT(exp) -} - -// Perform the full REPL. -// -private func REPL() { - while true { - if let text = _readline("user> ") { - print("\(REP(text))") - } else { - print("") - break - } - } -} - -func main() { - load_history_file() - REPL() - save_history_file() -} diff --git a/impls/swift/step1_read_print.swift b/impls/swift/step1_read_print.swift deleted file mode 100644 index ee10da70f5..0000000000 --- a/impls/swift/step1_read_print.swift +++ /dev/null @@ -1,75 +0,0 @@ -//****************************************************************************** -// MAL - step 1 - read/print -//****************************************************************************** -// 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 - -// Parse the string into an AST. -// -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. -// -private func EVAL(ast: MalVal) -> MalVal { - return ast -} - -// 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) -> 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 nil -} - -// Perform the full READ/EVAL/PRINT, returning a printable string. -// -private func REP(text: String) -> String? { - let exp = RE(text) - if exp == nil { return nil } - return PRINT(exp!) -} - -// Perform the full REPL. -// -private func REPL() { - while true { - if let text = _readline("user> ") { - if let output = REP(text) { - print("\(output)") - } - } else { - print("") - break - } - } -} - -func main() { - load_history_file() - REPL() - save_history_file() -} diff --git a/impls/swift/step2_eval.swift b/impls/swift/step2_eval.swift deleted file mode 100644 index 52aeab23bb..0000000000 --- a/impls/swift/step2_eval.swift +++ /dev/null @@ -1,170 +0,0 @@ -//****************************************************************************** -// MAL - step 2 - eval -//****************************************************************************** -// 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 - -// 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 -} - -// 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 - } - - // 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() -} diff --git a/impls/swift/step3_env.swift b/impls/swift/step3_env.swift deleted file mode 100644 index 7e3cde9d77..0000000000 --- a/impls/swift/step3_env.swift +++ /dev/null @@ -1,234 +0,0 @@ -//****************************************************************************** -// 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() -} diff --git a/impls/swift/step4_if_fn_do.swift b/impls/swift/step4_if_fn_do.swift deleted file mode 100644 index c27ee29493..0000000000 --- a/impls/swift/step4_if_fn_do.swift +++ /dev/null @@ -1,287 +0,0 @@ -//****************************************************************************** -// 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() -} diff --git a/impls/swift/step5_tco.swift b/impls/swift/step5_tco.swift deleted file mode 100644 index 286414ee8b..0000000000 --- a/impls/swift/step5_tco.swift +++ /dev/null @@ -1,382 +0,0 @@ -//****************************************************************************** -// MAL - step 5 - tco -//****************************************************************************** -// 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 - -// The number of times EVAL has been entered recursively. We keep track of this -// so that we can protect against overrunning the stack. -// -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. -// -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). -// -private let TCO = true - -// Control whether or not we emit debugging statements in EVAL. -// -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. -// -private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" - -// Holds the prefix of INDENT_TEMPLATE used for actual logging. -// -private var indent = String() - -// 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) - -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. -// -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 -} - -private enum TCOVal { - case NoResult - case Return(MalVal) - case Continue(MalVal, Environment) - - init() { self = .NoResult } - init(_ result: MalVal) { self = .Return(result) } - init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) } -} - -// EVALuate "def!". -// -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 = 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 TCOVal(env.set(sym, value)) -} - -// EVALuate "let*". -// -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 = 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) - } - if TCO { - return TCOVal(arg2, new_env) - } - return TCOVal(try EVAL(arg2, new_env)) -} - -// EVALuate "do". -// -private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal { - if TCO { - let _ = try eval_ast(list.range_from(1, to: list.count-1), env) - return TCOVal(list.last(), env) - } - - let evaluated_ast = try eval_ast(list.rest(), env) - let evaluated_seq = as_sequence(evaluated_ast) - return TCOVal(evaluated_seq.last()) -} - -// EVALuate "if". -// -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 = 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 TCOVal(make_nil()) - } - if TCO { - return TCOVal(new_ast, env) - } - return TCOVal(try EVAL(new_ast, env)) -} - -// EVALuate "fn*". -// -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)") - } - guard let seq = as_sequenceQ(try! list.nth(1)) else { - try throw_error("expected list or vector for first argument to fn*") - } - 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. -// -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 = substring(INDENT_TEMPLATE, 0, EVAL_level) - } - - while true { - if DEBUG_EVAL { print("\(indent)> \(ast)") } - - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - - // Special handling if it's a list. - - let list = as_list(ast) - if DEBUG_EVAL { print("\(indent)>. \(list)") } - - 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) { - let res: TCOVal - - switch fn_symbol { - 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 { - case let .Return(result): return result - case let .Continue(new_ast, new_env): ast = new_ast; env = new_env; continue - case .NoResult: 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 - } - - if DEBUG_EVAL { print("\(indent)>> \(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) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - 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) - if TCO { - env = new_env - ast = fn.body - continue - } - 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. - - 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() -} diff --git a/impls/swift/step6_file.swift b/impls/swift/step6_file.swift deleted file mode 100644 index 962fd8d84c..0000000000 --- a/impls/swift/step6_file.swift +++ /dev/null @@ -1,420 +0,0 @@ -//****************************************************************************** -// MAL - step 6 - file -//****************************************************************************** -// 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 - -// The number of times EVAL has been entered recursively. We keep track of this -// so that we can protect against overrunning the stack. -// -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. -// -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). -// -private let TCO = true - -// Control whether or not we emit debugging statements in EVAL. -// -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. -// -private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" - -// Holds the prefix of INDENT_TEMPLATE used for actual logging. -// -private var indent = String() - -// Symbols used in this module. -// -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. -// -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 -} - -private enum TCOVal { - case NoResult - case Return(MalVal) - case Continue(MalVal, Environment) - - init() { self = .NoResult } - init(_ result: MalVal) { self = .Return(result) } - init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) } -} - -// EVALuate "def!". -// -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 = 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 TCOVal(env.set(sym, value)) -} - -// EVALuate "let*". -// -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 = 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) - } - if TCO { - return TCOVal(arg2, new_env) - } - return TCOVal(try EVAL(arg2, new_env)) -} - -// EVALuate "do". -// -private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal { - if TCO { - let _ = try eval_ast(list.range_from(1, to: list.count-1), env) - return TCOVal(list.last(), env) - } - - let evaluated_ast = try eval_ast(list.rest(), env) - let evaluated_seq = as_sequence(evaluated_ast) - return TCOVal(evaluated_seq.last()) -} - -// EVALuate "if". -// -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 = 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 TCOVal(make_nil()) - } - if TCO { - return TCOVal(new_ast, env) - } - return TCOVal(try EVAL(new_ast, env)) -} - -// EVALuate "fn*". -// -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)") - } - guard let seq = as_sequenceQ(try! list.nth(1)) else { - try throw_error("expected list or vector for first argument to fn*") - } - 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. -// -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 = substring(INDENT_TEMPLATE, 0, EVAL_level) - } - - while true { - if DEBUG_EVAL { print("\(indent)> \(ast)") } - - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - - // Special handling if it's a list. - - let list = as_list(ast) - if DEBUG_EVAL { print("\(indent)>. \(list)") } - - 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) { - let res: TCOVal - - switch fn_symbol { - 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 { - case let .Return(result): return result - case let .Continue(new_ast, new_env): ast = new_ast; env = new_env; continue - case .NoResult: 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 - } - - if DEBUG_EVAL { print("\(indent)>> \(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) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - 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) - if TCO { - env = new_env - ast = fn.body - continue - } - 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. - - 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 - } - } -} - -// Process any command line arguments. Any trailing arguments are incorporated -// into the environment. Any argument immediately after the process name is -// taken as a script to execute. If one exists, it is executed in lieu of -// running the REPL. -// -private func process_command_line(args: [String], _ env: Environment) -> Bool { - var argv = make_list() - if args.count > 2 { - let args1 = args[2.. 1 { - RE("(load-file \"\(args[1])\")", env) - return false - } - - return true -} - -func main() { - let env = Environment(outer: nil) - - load_history_file() - load_builtins(env) - - RE("(def! not (fn* (a) (if a false true)))", env) - RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))", 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) { - REPL(env) - } - - save_history_file() -} diff --git a/impls/swift/step7_quote.swift b/impls/swift/step7_quote.swift deleted file mode 100644 index f9397a76a5..0000000000 --- a/impls/swift/step7_quote.swift +++ /dev/null @@ -1,521 +0,0 @@ -//****************************************************************************** -// MAL - step 7 - quote -//****************************************************************************** -// 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 - -// The number of times EVAL has been entered recursively. We keep track of this -// so that we can protect against overrunning the stack. -// -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. -// -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). -// -private let TCO = true - -// Control whether or not we emit debugging statements in EVAL. -// -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. -// -private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" - -// Holds the prefix of INDENT_TEMPLATE used for actual logging. -// -private var indent = String() - -// Symbols used in this module. -// -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 kValQuasiQuoteExp = make_symbol("quasiquoteexpand") -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 kValVec = make_symbol("vec") - -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 kSymbolQuasiQuoteExp = as_symbol(kValQuasiQuoteExp) -private let kSymbolQuote = as_symbol(kValQuote) -private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote) -private let kSymbolUnquote = as_symbol(kValUnquote) -private let kSymbolVec = as_symbol(kValVec) - -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. -// -private func READ(str: String) throws -> MalVal { - return try read_str(str) -} - -// Return whether or not `ast` is a list and first element is the required symbol. -// -private func starts_with(ast: MalVal, sym: MalSymbol) -> MalVal? { - if let list = as_listQ(ast) where 1 < list.count, - let a0 = as_symbolQ(try! list.nth(0)) where a0 == sym { - return try! list.nth(1) - } else { - return nil - } -} - -// Evaluate `quasiquote`, possibly recursing in the process. -// -private func quasiquote(qq_arg: MalVal) throws -> MalVal { - - // If the argument is an atom or empty list: - // - // Return: (quote ) - - if is_symbol(qq_arg) || is_hashmap(qq_arg) { - return make_list_from(kValQuote, qq_arg) - } - - guard let seq = as_sequenceQ(qq_arg) else { - return qq_arg - } - - // The argument is a non-empty list -- that is (item rest...) - - // If the first item from the list is a symbol and it's "unquote" -- that - // is, (unquote item ignored...): - // - // Return: item - - if let x = starts_with(qq_arg, sym: kSymbolUnquote) { - return x - } - - var result = make_list_from() - for elt in seq.reverse() { - if let x = starts_with(elt, sym: kSymbolSpliceUnquote) { - result = make_list_from(kValConcat, x, result) - } else { - result = make_list_from(kValCons, try quasiquote (elt), result) - } - } - if is_vector(qq_arg) { - return make_list_from(kValVec, result) - } - return result -} - -// 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 -} - -private enum TCOVal { - case NoResult - case Return(MalVal) - case Continue(MalVal, Environment) - - init() { self = .NoResult } - init(_ result: MalVal) { self = .Return(result) } - init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) } -} - -// EVALuate "def!". -// -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 = 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 TCOVal(env.set(sym, value)) -} - -// EVALuate "let*". -// -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 = 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) - } - if TCO { - return TCOVal(arg2, new_env) - } - return TCOVal(try EVAL(arg2, new_env)) -} - -// EVALuate "do". -// -private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal { - if TCO { - let _ = try eval_ast(list.range_from(1, to: list.count-1), env) - return TCOVal(list.last(), env) - } - - let evaluated_ast = try eval_ast(list.rest(), env) - let evaluated_seq = as_sequence(evaluated_ast) - return TCOVal(evaluated_seq.last()) -} - -// EVALuate "if". -// -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 = 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 TCOVal(make_nil()) - } - if TCO { - return TCOVal(new_ast, env) - } - return TCOVal(try EVAL(new_ast, env)) -} - -// EVALuate "fn*". -// -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)") - } - guard let seq = as_sequenceQ(try! list.nth(1)) else { - try throw_error("expected list or vector for first argument to fn*") - } - return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env))) -} - -// EVALuate "quote". -// -private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal { - if list.count >= 2 { - return TCOVal(try! list.nth(1)) - } - return TCOVal(make_nil()) -} - -// EVALuate "quasiquoteexpand". -// -private func eval_quasiquoteexp(list: MalSequence) throws -> TCOVal { - if list.count < 2 { - try throw_error("quasiquoteexpand: arg count") - } - return TCOVal(try! quasiquote(try! list.nth(1))) -} - -// EVALuate "quasiquote". -// -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(try EVAL(try quasiquote(try! list.nth(1)), env)) -} - -// Walk the AST and completely evaluate it, handling macro expansions, special -// forms and function calls. -// -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 = substring(INDENT_TEMPLATE, 0, EVAL_level) - } - - while true { - if DEBUG_EVAL { print("\(indent)> \(ast)") } - - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - - // Special handling if it's a list. - - let list = as_list(ast) - if DEBUG_EVAL { print("\(indent)>. \(list)") } - - 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) { - let res: TCOVal - - switch fn_symbol { - 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) - case kSymbolQuasiQuoteExp: res = try eval_quasiquoteexp(list) - default: res = TCOVal() - } - switch res { - case let .Return(result): return result - case let .Continue(new_ast, new_env): ast = new_ast; env = new_env; continue - case .NoResult: 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 - } - - if DEBUG_EVAL { print("\(indent)>> \(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) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - 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) - if TCO { - env = new_env - ast = fn.body - continue - } - 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. - - 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 - } - } -} - -// Process any command line arguments. Any trailing arguments are incorporated -// into the environment. Any argument immediately after the process name is -// taken as a script to execute. If one exists, it is executed in lieu of -// running the REPL. -// -private func process_command_line(args: [String], _ env: Environment) -> Bool { - var argv = make_list() - if args.count > 2 { - let args1 = args[2.. 1 { - RE("(load-file \"\(args[1])\")", env) - return false - } - - return true -} - -func main() { - let env = Environment(outer: nil) - - load_history_file() - load_builtins(env) - - RE("(def! not (fn* (a) (if a false true)))", env) - RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))", 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) { - REPL(env) - } - - save_history_file() -} diff --git a/impls/swift/step8_macros.swift b/impls/swift/step8_macros.swift deleted file mode 100644 index 81712ca61c..0000000000 --- a/impls/swift/step8_macros.swift +++ /dev/null @@ -1,575 +0,0 @@ -//****************************************************************************** -// MAL - step 8 - macros -//****************************************************************************** -// 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 - -// The number of times EVAL has been entered recursively. We keep track of this -// so that we can protect against overrunning the stack. -// -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. -// -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). -// -private let TCO = true - -// Control whether or not we emit debugging statements in EVAL. -// -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. -// -private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" - -// Holds the prefix of INDENT_TEMPLATE used for actual logging. -// -private var indent = String() - -// Symbols used in this module. -// -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 kValQuasiQuoteExp = make_symbol("quasiquoteexpand") -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 kValVec = make_symbol("vec") - -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 kSymbolQuasiQuoteExp = as_symbol(kValQuasiQuoteExp) -private let kSymbolQuote = as_symbol(kValQuote) -private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote) -private let kSymbolUnquote = as_symbol(kValUnquote) -private let kSymbolVec = as_symbol(kValVec) - -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. -// -private func READ(str: String) throws -> MalVal { - return try read_str(str) -} - -// Expand macros for as long as the expression looks like a macro invocation. -// -private func macroexpand(var ast: MalVal, _ env: Environment) throws -> MalVal { - while true { - 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 whether or not `ast` is a list and first element is the required symbol. -// -private func starts_with(ast: MalVal, sym: MalSymbol) -> MalVal? { - if let list = as_listQ(ast) where 1 < list.count, - let a0 = as_symbolQ(try! list.nth(0)) where a0 == sym { - return try! list.nth(1) - } else { - return nil - } -} - -// Evaluate `quasiquote`, possibly recursing in the process. -// -private func quasiquote(qq_arg: MalVal) throws -> MalVal { - - // If the argument is an atom or empty list: - // - // Return: (quote ) - - if is_symbol(qq_arg) || is_hashmap(qq_arg) { - return make_list_from(kValQuote, qq_arg) - } - - guard let seq = as_sequenceQ(qq_arg) else { - return qq_arg - } - - // The argument is a non-empty list -- that is (item rest...) - - // If the first item from the list is a symbol and it's "unquote" -- that - // is, (unquote item ignored...): - // - // Return: item - - if let x = starts_with(qq_arg, sym: kSymbolUnquote) { - return x - } - - var result = make_list_from() - for elt in seq.reverse() { - if let x = starts_with(elt, sym: kSymbolSpliceUnquote) { - result = make_list_from(kValConcat, x, result) - } else { - result = make_list_from(kValCons, try quasiquote (elt), result) - } - } - if is_vector(qq_arg) { - return make_list_from(kValVec, result) - } - return result -} - -// 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 -} - -private enum TCOVal { - case NoResult - case Return(MalVal) - case Continue(MalVal, Environment) - - init() { self = .NoResult } - init(_ result: MalVal) { self = .Return(result) } - init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) } -} - -// EVALuate "def!" and "defmacro!". -// -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*". -// -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 = 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) - } - if TCO { - return TCOVal(arg2, new_env) - } - return TCOVal(try EVAL(arg2, new_env)) -} - -// EVALuate "do". -// -private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal { - if TCO { - let _ = try eval_ast(list.range_from(1, to: list.count-1), env) - return TCOVal(list.last(), env) - } - - let evaluated_ast = try eval_ast(list.rest(), env) - let evaluated_seq = as_sequence(evaluated_ast) - return TCOVal(evaluated_seq.last()) -} - -// EVALuate "if". -// -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 = 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 TCOVal(make_nil()) - } - if TCO { - return TCOVal(new_ast, env) - } - return TCOVal(try EVAL(new_ast, env)) -} - -// EVALuate "fn*". -// -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)") - } - guard let seq = as_sequenceQ(try! list.nth(1)) else { - try throw_error("expected list or vector for first argument to fn*") - } - return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env))) -} - -// EVALuate "quote". -// -private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal { - if list.count >= 2 { - return TCOVal(try! list.nth(1)) - } - return TCOVal(make_nil()) -} - -// EVALuate "quasiquoteexpand". -// -private func eval_quasiquoteexp(list: MalSequence) throws -> TCOVal { - if list.count < 2 { - try throw_error("quasiquoteexpand: arg count") - } - return TCOVal(try! quasiquote(try! list.nth(1))) -} - -// EVALuate "quasiquote". -// -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(try EVAL(try quasiquote(try! list.nth(1)), env)) -} - -// EVALuate "macroexpand". -// -private func eval_macroexpand(list: MalSequence, _ env: Environment) throws -> TCOVal { - guard list.count >= 2 else { - try throw_error("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. -// -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 = substring(INDENT_TEMPLATE, 0, EVAL_level) - } - - while true { - if DEBUG_EVAL { print("\(indent)> \(ast)") } - - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - - // Special handling if it's a list. - - var list = as_list(ast) - ast = try macroexpand(ast, env) - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - list = as_list(ast) - - if DEBUG_EVAL { print("\(indent)>. \(list)") } - - 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) { - let res: TCOVal - - switch fn_symbol { - 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 kSymbolQuasiQuoteExp: res = try eval_quasiquoteexp(list) - case kSymbolMacroExpand: res = try eval_macroexpand(list, env) - default: res = TCOVal() - } - switch res { - case let .Return(result): return result - case let .Continue(new_ast, new_env): ast = new_ast; env = new_env; continue - case .NoResult: 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 - } - - if DEBUG_EVAL { print("\(indent)>> \(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) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - 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) - if TCO { - env = new_env - ast = fn.body - continue - } - 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. - - 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 - } - } -} - -// Process any command line arguments. Any trailing arguments are incorporated -// into the environment. Any argument immediately after the process name is -// taken as a script to execute. If one exists, it is executed in lieu of -// running the REPL. -// -private func process_command_line(args: [String], _ env: Environment) -> Bool { - var argv = make_list() - if args.count > 2 { - let args1 = args[2.. 1 { - RE("(load-file \"\(args[1])\")", env) - return false - } - - return true -} - -func main() { - let env = Environment(outer: nil) - - load_history_file() - load_builtins(env) - - RE("(def! not (fn* (a) (if a false true)))", env) - RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))", env) - RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) " + - "(throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", env) - - env.set(kSymbolEval, make_builtin({ - try! unwrap_args($0) { - (ast: MalVal) -> MalVal in - try EVAL(ast, env) - } - })) - - if process_command_line(Process.arguments, env) { - REPL(env) - } - - save_history_file() -} diff --git a/impls/swift/step9_try.swift b/impls/swift/step9_try.swift deleted file mode 100644 index 793539237c..0000000000 --- a/impls/swift/step9_try.swift +++ /dev/null @@ -1,608 +0,0 @@ -//****************************************************************************** -// MAL - step 9 - try -//****************************************************************************** -// 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 - -// The number of times EVAL has been entered recursively. We keep track of this -// so that we can protect against overrunning the stack. -// -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. -// -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). -// -private let TCO = true - -// Control whether or not we emit debugging statements in EVAL. -// -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. -// -private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" - -// Holds the prefix of INDENT_TEMPLATE used for actual logging. -// -private var indent = String() - -// Symbols used in this module. -// -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 kValQuasiQuoteExp = make_symbol("quasiquoteexpand") -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 kValVec = make_symbol("vec") - -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 kSymbolQuasiQuoteExp = as_symbol(kValQuasiQuoteExp) -private let kSymbolQuote = as_symbol(kValQuote) -private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote) -private let kSymbolUnquote = as_symbol(kValUnquote) -private let kSymbolTry = as_symbol(kValTry) -private let kSymbolVec = as_symbol(kValVec) - -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. -// -private func READ(str: String) throws -> MalVal { - return try read_str(str) -} - -// Expand macros for as long as the expression looks like a macro invocation. -// -private func macroexpand(var ast: MalVal, _ env: Environment) throws -> MalVal { - while true { - 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 whether or not `ast` is a list and first element is the required symbol. -// -private func starts_with(ast: MalVal, sym: MalSymbol) -> MalVal? { - if let list = as_listQ(ast) where 1 < list.count, - let a0 = as_symbolQ(try! list.nth(0)) where a0 == sym { - return try! list.nth(1) - } else { - return nil - } -} - -// Evaluate `quasiquote`, possibly recursing in the process. -// -private func quasiquote(qq_arg: MalVal) throws -> MalVal { - - // If the argument is an atom or empty list: - // - // Return: (quote ) - - if is_symbol(qq_arg) || is_hashmap(qq_arg) { - return make_list_from(kValQuote, qq_arg) - } - - guard let seq = as_sequenceQ(qq_arg) else { - return qq_arg - } - - // The argument is a non-empty list -- that is (item rest...) - - // If the first item from the list is a symbol and it's "unquote" -- that - // is, (unquote item ignored...): - // - // Return: item - - if let x = starts_with(qq_arg, sym: kSymbolUnquote) { - return x - } - - var result = make_list_from() - for elt in seq.reverse() { - if let x = starts_with(elt, sym: kSymbolSpliceUnquote) { - result = make_list_from(kValConcat, x, result) - } else { - result = make_list_from(kValCons, try quasiquote (elt), result) - } - } - if is_vector(qq_arg) { - return make_list_from(kValVec, result) - } - return result -} - -// 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 -} - -private enum TCOVal { - case NoResult - case Return(MalVal) - case Continue(MalVal, Environment) - - init() { self = .NoResult } - init(_ result: MalVal) { self = .Return(result) } - init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) } -} - -// EVALuate "def!" and "defmacro!". -// -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*". -// -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 = 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) - } - if TCO { - return TCOVal(arg2, new_env) - } - return TCOVal(try EVAL(arg2, new_env)) -} - -// EVALuate "do". -// -private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal { - if TCO { - let _ = try eval_ast(list.range_from(1, to: list.count-1), env) - return TCOVal(list.last(), env) - } - - let evaluated_ast = try eval_ast(list.rest(), env) - let evaluated_seq = as_sequence(evaluated_ast) - return TCOVal(evaluated_seq.last()) -} - -// EVALuate "if". -// -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 = 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 TCOVal(make_nil()) - } - if TCO { - return TCOVal(new_ast, env) - } - return TCOVal(try EVAL(new_ast, env)) -} - -// EVALuate "fn*". -// -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)") - } - guard let seq = as_sequenceQ(try! list.nth(1)) else { - try throw_error("expected list or vector for first argument to fn*") - } - return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env))) -} - -// EVALuate "quote". -// -private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal { - if list.count >= 2 { - return TCOVal(try! list.nth(1)) - } - return TCOVal(make_nil()) -} - -// EVALuate "quasiquoteexpand". -// -private func eval_quasiquoteexp(list: MalSequence) throws -> TCOVal { - if list.count < 2 { - try throw_error("quasiquoteexpand: arg count") - } - return TCOVal(try! quasiquote(try! list.nth(1))) -} - -// EVALuate "quasiquote". -// -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(try EVAL(try quasiquote(try! list.nth(1)), env)) -} - -// EVALuate "macroexpand". -// -private func eval_macroexpand(list: MalSequence, _ env: Environment) throws -> TCOVal { - guard list.count >= 2 else { - try throw_error("Expected parameter to 'macroexpand'") - } - return TCOVal(try macroexpand(try! list.nth(1), env)) -} - -// EVALuate "try*" (and "catch*"). -// -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)) - - 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. -// -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 = substring(INDENT_TEMPLATE, 0, EVAL_level) - } - - while true { - if DEBUG_EVAL { print("\(indent)> \(ast)") } - - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - - // Special handling if it's a list. - - var list = as_list(ast) - ast = try macroexpand(ast, env) - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - list = as_list(ast) - - if DEBUG_EVAL { print("\(indent)>. \(list)") } - - 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) { - let res: TCOVal - - switch fn_symbol { - 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 kSymbolQuasiQuoteExp: res = try eval_quasiquoteexp(list) - case kSymbolMacroExpand: res = try eval_macroexpand(list, env) - case kSymbolTry: res = try eval_try(list, env) - default: res = TCOVal() - } - switch res { - case let .Return(result): return result - case let .Continue(new_ast, new_env): ast = new_ast; env = new_env; continue - case .NoResult: 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 - } - - if DEBUG_EVAL { print("\(indent)>> \(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) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - 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) - if TCO { - env = new_env - ast = fn.body - continue - } - 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. - - 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 - } - } -} - -// Process any command line arguments. Any trailing arguments are incorporated -// into the environment. Any argument immediately after the process name is -// taken as a script to execute. If one exists, it is executed in lieu of -// running the REPL. -// -private func process_command_line(args: [String], _ env: Environment) -> Bool { - var argv = make_list() - if args.count > 2 { - let args1 = args[2.. 1 { - RE("(load-file \"\(args[1])\")", env) - return false - } - - return true -} - -func main() { - let env = Environment(outer: nil) - - load_history_file() - load_builtins(env) - - RE("(def! not (fn* (a) (if a false true)))", env) - RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))", env) - RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) " + - "(throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", env) - - env.set(kSymbolEval, make_builtin({ - try! unwrap_args($0) { - (ast: MalVal) -> MalVal in - try EVAL(ast, env) - } - })) - - if process_command_line(Process.arguments, env) { - REPL(env) - } - - save_history_file() -} diff --git a/impls/swift/stepA_mal.swift b/impls/swift/stepA_mal.swift deleted file mode 100644 index 96e93ecedc..0000000000 --- a/impls/swift/stepA_mal.swift +++ /dev/null @@ -1,610 +0,0 @@ -//****************************************************************************** -// MAL - step A - mal -//****************************************************************************** -// 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 - -// The number of times EVAL has been entered recursively. We keep track of this -// so that we can protect against overrunning the stack. -// -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. -// -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). -// -private let TCO = true - -// Control whether or not we emit debugging statements in EVAL. -// -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. -// -private let INDENT_TEMPLATE = "|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" + - "----|----|----|----|----|----|----|----|----|----|----|" - -// Holds the prefix of INDENT_TEMPLATE used for actual logging. -// -private var indent = String() - -// Symbols used in this module. -// -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 kValQuasiQuoteExp = make_symbol("quasiquoteexpand") -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 kValVec = make_symbol("vec") - -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 kSymbolQuasiQuoteExp = as_symbol(kValQuasiQuoteExp) -private let kSymbolQuote = as_symbol(kValQuote) -private let kSymbolSpliceUnquote = as_symbol(kValSpliceUnquote) -private let kSymbolUnquote = as_symbol(kValUnquote) -private let kSymbolTry = as_symbol(kValTry) -private let kSymbolVec = as_symbol(kValVec) - -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. -// -private func READ(str: String) throws -> MalVal { - return try read_str(str) -} - -// Expand macros for as long as the expression looks like a macro invocation. -// -private func macroexpand(var ast: MalVal, _ env: Environment) throws -> MalVal { - while true { - 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 whether or not `ast` is a list and first element is the required symbol. -// -private func starts_with(ast: MalVal, sym: MalSymbol) -> MalVal? { - if let list = as_listQ(ast) where 1 < list.count, - let a0 = as_symbolQ(try! list.nth(0)) where a0 == sym { - return try! list.nth(1) - } else { - return nil - } -} - -// Evaluate `quasiquote`, possibly recursing in the process. -// -private func quasiquote(qq_arg: MalVal) throws -> MalVal { - - // If the argument is an atom or empty list: - // - // Return: (quote ) - - if is_symbol(qq_arg) || is_hashmap(qq_arg) { - return make_list_from(kValQuote, qq_arg) - } - - guard let seq = as_sequenceQ(qq_arg) else { - return qq_arg - } - - // The argument is a non-empty list -- that is (item rest...) - - // If the first item from the list is a symbol and it's "unquote" -- that - // is, (unquote item ignored...): - // - // Return: item - - if let x = starts_with(qq_arg, sym: kSymbolUnquote) { - return x - } - - var result = make_list_from() - for elt in seq.reverse() { - if let x = starts_with(elt, sym: kSymbolSpliceUnquote) { - result = make_list_from(kValConcat, x, result) - } else { - result = make_list_from(kValCons, try quasiquote (elt), result) - } - } - if is_vector(qq_arg) { - return make_list_from(kValVec, result) - } - return result -} - -// 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 -} - -private enum TCOVal { - case NoResult - case Return(MalVal) - case Continue(MalVal, Environment) - - init() { self = .NoResult } - init(_ result: MalVal) { self = .Return(result) } - init(_ ast: MalVal, _ env: Environment) { self = .Continue(ast, env) } -} - -// EVALuate "def!" and "defmacro!". -// -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*". -// -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 = 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) - } - if TCO { - return TCOVal(arg2, new_env) - } - return TCOVal(try EVAL(arg2, new_env)) -} - -// EVALuate "do". -// -private func eval_do(list: MalSequence, _ env: Environment) throws -> TCOVal { - if TCO { - let _ = try eval_ast(list.range_from(1, to: list.count-1), env) - return TCOVal(list.last(), env) - } - - let evaluated_ast = try eval_ast(list.rest(), env) - let evaluated_seq = as_sequence(evaluated_ast) - return TCOVal(evaluated_seq.last()) -} - -// EVALuate "if". -// -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 = 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 TCOVal(make_nil()) - } - if TCO { - return TCOVal(new_ast, env) - } - return TCOVal(try EVAL(new_ast, env)) -} - -// EVALuate "fn*". -// -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)") - } - guard let seq = as_sequenceQ(try! list.nth(1)) else { - try throw_error("expected list or vector for first argument to fn*") - } - return TCOVal(make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env))) -} - -// EVALuate "quote". -// -private func eval_quote(list: MalSequence, _ env: Environment) throws -> TCOVal { - if list.count >= 2 { - return TCOVal(try! list.nth(1)) - } - return TCOVal(make_nil()) -} - -// EVALuate "quasiquoteexpand". -// -private func eval_quasiquoteexp(list: MalSequence) throws -> TCOVal { - if list.count < 2 { - try throw_error("quasiquoteexpand: arg count") - } - return TCOVal(try! quasiquote(try! list.nth(1))) -} - -// EVALuate "quasiquote". -// -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(try EVAL(try quasiquote(try! list.nth(1)), env)) -} - -// EVALuate "macroexpand". -// -private func eval_macroexpand(list: MalSequence, _ env: Environment) throws -> TCOVal { - guard list.count >= 2 else { - try throw_error("Expected parameter to 'macroexpand'") - } - return TCOVal(try macroexpand(try! list.nth(1), env)) -} - -// EVALuate "try*" (and "catch*"). -// -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)) - - 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. -// -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 = substring(INDENT_TEMPLATE, 0, EVAL_level) - } - - while true { - if DEBUG_EVAL { print("\(indent)> \(ast)") } - - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - - // Special handling if it's a list. - - var list = as_list(ast) - ast = try macroexpand(ast, env) - if !is_list(ast) { - - // Not a list -- just evaluate and return. - - let answer = try eval_ast(ast, env) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - return answer - } - list = as_list(ast) - - if DEBUG_EVAL { print("\(indent)>. \(list)") } - - 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) { - let res: TCOVal - - switch fn_symbol { - 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 kSymbolQuasiQuoteExp: res = try eval_quasiquoteexp(list) - case kSymbolMacroExpand: res = try eval_macroexpand(list, env) - case kSymbolTry: res = try eval_try(list, env) - default: res = TCOVal() - } - switch res { - case let .Return(result): return result - case let .Continue(new_ast, new_env): ast = new_ast; env = new_env; continue - case .NoResult: 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 - } - - if DEBUG_EVAL { print("\(indent)>> \(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) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } - 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) - if TCO { - env = new_env - ast = fn.body - continue - } - 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. - - 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 - } - } -} - -// Process any command line arguments. Any trailing arguments are incorporated -// into the environment. Any argument immediately after the process name is -// taken as a script to execute. If one exists, it is executed in lieu of -// running the REPL. -// -private func process_command_line(args: [String], _ env: Environment) -> Bool { - var argv = make_list() - if args.count > 2 { - let args1 = args[2.. 1 { - RE("(load-file \"\(args[1])\")", env) - return false - } - - return true -} - -func main() { - let env = Environment(outer: nil) - - load_history_file() - load_builtins(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) \"\\nnil)\")))))", env) - RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) " + - "(throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", env) - - 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) - REPL(env) - } - - save_history_file() -} diff --git a/impls/swift/templates/add_steps.sh b/impls/swift/templates/add_steps.sh deleted file mode 100755 index 488b54b82d..0000000000 --- a/impls/swift/templates/add_steps.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# add_steps.sh input-file output-file -# -# Adds placeholder annotations to each line of a file. These annotations -# indicate which version(s) of the main (step*.swift) file the line should be -# included in. The annotations are just placeholders, and need to be edited to -# identify the right file versions. -# -# e.g.: -# -# $ ./add_steps.sh stepA_mal.swift main_template.swift - -SPC10=" " -SPC20="${SPC10}${SPC10}" -SPC40="${SPC20}${SPC20}" -SPC80="${SPC40}${SPC40}" -SPC160="${SPC80}${SPC80}" -sed < $1 > $2 -e "s/\(.*\)/\1${SPC160}/" -e "/^\(.\)\{156\} .*$/s/\(.\{160\}\).*/\1\/\/ malstep(A)/" - -# TBD: try the following, subsequently found on stackoverflow: -# -# sed -i ':a;/.\{63\}/!{s/$/ /;ba}' file diff --git a/impls/swift/templates/filter_steps.sh b/impls/swift/templates/filter_steps.sh deleted file mode 100755 index 44fe604b78..0000000000 --- a/impls/swift/templates/filter_steps.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -# filter_steps.sh step input-file output-file -# -# Filter the template file to produce a specific version of the file. E.g.: -# -# $ ./filter_steps 4 main_template.swift step4_if_fn_do.swift - -grep "malstep.*\<$1\>" $2 | sed -e 's/\(.*\)\/\/ malstep(.*)$/\1/' -e 's/ *$//' > $3 diff --git a/impls/swift/templates/step.swift b/impls/swift/templates/step.swift deleted file mode 100644 index 8e66e7a0fc..0000000000 --- a/impls/swift/templates/step.swift +++ /dev/null @@ -1,804 +0,0 @@ -//****************************************************************************** -// -// This file is used to generate the various "step" files, which in turn are -// used to create the various step executables. -// -// For the most part, this file is the final step file, with each line annotated -// with information that says in which step the line is introduced into the -// project. A simple filter program scans this template file, pulling out the -// lines required for a specified step. -// -// Ideally, after each line is included in a project, it stays in the project. -// This would make each step file a proper superset of the previous steps files. -// However, such idealism cannot be realized. There are cases where lines -// introduced in early step files need to be removed or replaced with new -// version. -// -// When this happens, multiple versions of a particular line can appear in the -// file. For example, consider the READ function. Early in the project, it is -// introduced as: -// -// func READ(str: String) -> String { -// return str -// } -// -// However, it is replaced in a subsequent step with: -// -// func READ(str: String) -> MalVal { -// return read_str(str) -// } -// -// To support both forms, both are included in this template file. The first is -// annotated to say that it appears in step 0 and *only* in step 0. The second -// is annotated to say that it appears in step 1 and in all subsequent versions. -// -// Where possible, in the interests for clarity, where lines are introduced and -// replaced, the entire function that is affected is introduced and replaced. -// This is as opposed to trying to surgically identify the line-by-line changes -// within a function that need to be replaced. -// -// However, in other cases, the surgical line-by-line replacement of text is -// employed. This is done in cases where the number of lines to change is small -// compared to the overall size of the function. -// -// Places where previously-introduced lines are changed or removed are marked -// with a ">>> NOTE:" comment. -// -// Lines with no annotations (like those comprising this comment block) are -// never included in any output. -// -//****************************************************************************** - -//****************************************************************************** // malstep(0,1,2,3,4,5,6,7,8,9,A) -// MAL - step 0 - repl // malstep(0) -// MAL - step 1 - read/print // malstep(1) -// MAL - step 2 - eval // malstep(2) -// MAL - step 3 - env // malstep(3) -// MAL - step 4 - if/fn/do // malstep(4) -// MAL - step 5 - tco // malstep(5) -// MAL - step 6 - file // malstep(6) -// MAL - step 7 - quote // malstep(7) -// MAL - step 8 - macros // malstep(8) -// MAL - step 9 - try // malstep(9) -// MAL - step A - mal // malstep(A) -//****************************************************************************** // malstep(0,1,2,3,4,5,6,7,8,9,A) -// This file is automatically generated from templates/step.swift. Rather than // malstep(0,1,2,3,4,5,6,7,8,9,A) -// editing it directly, it's probably better to edit templates/step.swift and // malstep(0,1,2,3,4,5,6,7,8,9,A) -// regenerate this file. Otherwise, your change might be lost if/when someone // malstep(0,1,2,3,4,5,6,7,8,9,A) -// else performs that process. // malstep(0,1,2,3,4,5,6,7,8,9,A) -//****************************************************************************** // malstep(0,1,2,3,4,5,6,7,8,9,A) - // malstep(0,1,2,3,4,5,6,7,8,9,A) -import Foundation // malstep(0,1,2,3,4,5,6,7,8,9,A) - // malstep(0,1,2,3,4,5,6,7,8,9,A) -// 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) -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) -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) -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) -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) -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) - "----|----|----|----|----|----|----|----|----|----|----|" + // 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) - "----|----|----|----|----|----|----|----|----|----|----|" + // 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) -// Holds the prefix of INDENT_TEMPLATE used for actual logging. // malstep(5,6,7,8,9,A) -// // 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) -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) -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) -// -// >>> NOTE: There are two versions of the following function: one used in step -// >>> 0 and one used in all subsequent versions. -// - -// 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) -private func READ(str: String) -> String { // malstep(0) - return str // malstep(0) -} // malstep(0) -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) -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) -private func macroexpand(var ast: MalVal, _ env: Environment) throws -> MalVal { // malstep(8,9,A) - while true { // 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) -} // malstep(8,9,A) - // malstep(8,9,A) -// Evaluate `quasiquote`, possibly recursing in the process. // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// As with quote, unquote, and splice-unquote, quasiquote takes a single // malstep(7,8,9,A) -// parameter, typically a list. In the general case, this list is processed // malstep(7,8,9,A) -// recursively as: // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// (quasiquote (first rest...)) -> (cons (quasiquote first) (quasiquote rest)) // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// In the processing of the parameter passed to it, quasiquote handles three // malstep(7,8,9,A) -// special cases: // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// * If the parameter is an atom or an empty list, the following expression // malstep(7,8,9,A) -// is formed and returned for evaluation: // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// (quasiquote atom-or-empty-list) -> (quote atom-or-empty-list) // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// * If the first element of the non-empty list is the symbol "unquote" // malstep(7,8,9,A) -// followed by a second item, the second item is returned as-is: // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// (quasiquote (unquote fred)) -> fred // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// * If the first element of the non-empty list is another list containing // malstep(7,8,9,A) -// the symbol "splice-unquote" followed by a list, that list is catenated // malstep(7,8,9,A) -// with the quasiquoted result of the remaining items in the non-empty // malstep(7,8,9,A) -// parent list: // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// (quasiquote (splice-unquote list) rest...) -> (items-from-list items-from-quasiquote(rest...)) // malstep(7,8,9,A) -// // malstep(7,8,9,A) -// Note the inconsistent handling between "quote" and "splice-quote". The former // malstep(7,8,9,A) -// is handled when this function is handed a list that starts with "quote", // malstep(7,8,9,A) -// whereas the latter is handled when this function is handled a list whose // malstep(7,8,9,A) -// first element is a list that starts with "splice-quote". The handling of the // malstep(7,8,9,A) -// latter is forced by the need to incorporate the results of (splice-quote // malstep(7,8,9,A) -// 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) -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 ) // malstep(7,8,9,A) - // malstep(7,8,9,A) - if !is_pair(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) - // malstep(7,8,9,A) - // If the first item from the list is a symbol and it's "unquote" -- that // malstep(7,8,9,A) - // is, (unquote item ignored...): // malstep(7,8,9,A) - // // malstep(7,8,9,A) - // Return: item // 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) - // "splice-unquote"-- that is, ((splice-unquote item ignored...) rest...): // malstep(7,8,9,A) - // // malstep(7,8,9,A) - // 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 = 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) - // General case: (item rest...): // malstep(7,8,9,A) - // // malstep(7,8,9,A) - // Return: (cons (quasiquote item) (quasiquote (rest...)) // malstep(7,8,9,A) - // 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) -// dereference it and return its value. If it's a collection, call EVAL on all // malstep(2,3,4,5,6,7,8,9,A) -// 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) -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) -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) - // malstep(5,6,7,8,9,A) - 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) -} // 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) -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 = 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 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) -} // malstep(3,4,5,6,7,8,9,A) - // 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) -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 = 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) - 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) - 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 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 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) -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 _ = 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 = 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) -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 = 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 = 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 = try! list.nth(3) // malstep(4,5,6,7,8,9,A) - } else { // malstep(4,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 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) -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) - 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 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) -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(try! list.nth(1)) // malstep(7,8,9,A) - } // 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) -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(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) -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(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) -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) - guard list.count >= 2 else { // malstep(9,A) - try throw_error("try*: no body parameter") // malstep(9,A) - } // 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) -} // malstep(9,A) - // malstep(9,A) -// -// >>> NOTE: There are several versions of the EVAL function. One is used in -// >>> step 0, one is used in step 1, and a final one is used in step 2 and all -// >>> subsequent versions. This final version is extended throughout the -// >>> project through the addition of functionality. -// - -// 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) -private func EVAL(ast: String) -> String { // malstep(0) - return ast // malstep(0) -} // malstep(0) -private func EVAL(ast: MalVal) -> MalVal { // malstep(1) - return ast // malstep(1) -} // malstep(1) -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 = 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 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 = 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) - 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) { // malstep(8,9,A) - // malstep(8,9,A) - // Not a list -- just evaluate and return. // malstep(8,9,A) - // malstep(8,9,A) - let answer = try eval_ast(ast, env) // malstep(8,9,A) - if DEBUG_EVAL { print("\(indent)>>> \(answer)") } // malstep(8,9,A) - return answer // malstep(8,9,A) - } // malstep(8,9,A) - list = as_list(ast) // malstep(8,9,A) - // malstep(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 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 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 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) - switch res { // malstep(5,6,7,8,9,A) - case let .Return(result): return result // malstep(5,6,7,8,9,A) - case let .Continue(new_ast, new_env): ast = new_ast; env = new_env; continue // malstep(5,6,7,8,9,A) - case .NoResult: break // malstep(5,6,7,8,9,A) - } // 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) - // 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 = 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 = 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 // 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 { 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 = 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 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 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 = 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) - 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) -private func PRINT(exp: String) -> String { // malstep(0) - return exp // malstep(0) -} // malstep(0) -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) -// -// >>> NOTE: The following function has several versions. Also note that the -// >>> call to EVAL comes in two flavors. -// - -// 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) -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) -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 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) -// -// >>> NOTE: The following function has several versions. -// - -// 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) -private func REP(text: String) -> String { // malstep(0) - let exp = RE(text) // malstep(0) - return PRINT(exp) // malstep(0) -} // malstep(0) -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) -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) -} // malstep(2,3,4,5,6,7,8,9,A) - // malstep(0,1,2,3,4,5,6,7,8,9,A) -// -// >>> NOTE: The following function has several versions. -// - -// 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) -private func REPL() { // malstep(0) - while true { // malstep(0) - if let text = _readline("user> ") { // malstep(0) - print("\(REP(text))") // malstep(0) - } else { // malstep(0) - print("") // malstep(0) - break // malstep(0) - } // malstep(0) - } // malstep(0) -} // malstep(0) -private func REPL() { // malstep(1) - while true { // malstep(1) - if let text = _readline("user> ") { // malstep(1) - if let output = REP(text) { // malstep(1) - print("\(output)") // malstep(1) - } // malstep(1) - } else { // malstep(1) - print("") // malstep(1) - break // malstep(1) - } // malstep(1) - } // malstep(1) -} // malstep(1) -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) - 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) - 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) -} // malstep(2,3,4,5,6,7,8,9,A) - // malstep(0,1,2,3,4,5,6,7,8,9,A) -// Process any command line arguments. Any trailing arguments are incorporated // malstep(6,7,8,9,A) -// into the environment. Any argument immediately after the process name is // malstep(6,7,8,9,A) -// 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) -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.. 1 { // malstep(6,7,8,9,A) - RE("(load-file \"\(args[1])\")", env) // malstep(6,7,8,9,A) - return false // malstep(6,7,8,9,A) - } // malstep(6,7,8,9,A) - // malstep(6,7,8,9,A) - return true // malstep(6,7,8,9,A) -} // 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) - 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! 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) - "(throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", env) // malstep(8,9,A) - RE("(def! inc (fn* [x] (+ x 1)))", env) // malstep(A) - RE("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", env) // malstep(A) - 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) - "(let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", env) // malstep(A) - // 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) -// -// >>> NOTE: The call to REPL() is managed in three different ways. First, we -// >>> just call it with no parameters. Second, we call it with an "env" -// >>> parameter. Finally, we call it only if there is no program on the -// >>> command line to execute. -// - 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) - REPL(env) // malstep(6,7,8,9,A) - } // malstep(6,7,8,9,A) - // malstep(2,3,4,5,6,7,8,9,A) - save_history_file() // malstep(0,1,2,3,4,5,6,7,8,9,A) -} // malstep(0,1,2,3,4,5,6,7,8,9,A) diff --git a/impls/swift/tests/step5_tco.mal b/impls/swift/tests/step5_tco.mal deleted file mode 100644 index d20df25db7..0000000000 --- a/impls/swift/tests/step5_tco.mal +++ /dev/null @@ -1,15 +0,0 @@ -;; Test recursive non-tail call function - -(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1)))))) - -(sum-to 10) -;=>55 - -;;; no try* yet, so test completion of side-effects -(def! res1 nil) -;=>nil -;;; For implementations without their own TCO this should fail and -;;; leave res1 unchanged -(def! res1 (sum-to 10000)) -res1 -;=>nil diff --git a/impls/swift/types_class.swift b/impls/swift/types_class.swift deleted file mode 100644 index 569bfa51de..0000000000 --- a/impls/swift/types_class.swift +++ /dev/null @@ -1,1101 +0,0 @@ -//****************************************************************************** -// MAL - types, implemented as a Swift "class". -//****************************************************************************** - -import Foundation - -// ==================== Types / Constants / Variables ==================== - -typealias MalProtocol = protocol - -typealias MalIntType = Int64 -typealias MalFloatType = Double -typealias MalSymbolType = String -typealias MalKeywordType = String -typealias MalStringType = String -typealias MalVectorType = ArraySlice -typealias MalHashType = Dictionary - -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(@noescape transform: (MalVal) -> U) -> ArraySlice { die() } - func reduce(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) { - self._slice = array[0..(_ 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)).. 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 - 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(@noescape transform: (MalVal) -> U) -> ArraySlice { return ArraySlice(self._slice.map(transform)) } - override func reduce(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) { - self._slice = array[0..(_ 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)).. 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(@noescape transform: (MalVal) -> U) -> ArraySlice { return ArraySlice(self._slice.map(transform)) } - override func reduce(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(_ 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 "#: (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 "#" } - - // 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 { return MalList(v) } -func make_list_from (v: MalVal...) -> MalVal { return MalList(v) } -func make_list - (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 { return MalVector(v) } -func make_vector - (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 - (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/impls/swift/types_enum.swift b/impls/swift/types_enum.swift deleted file mode 100644 index 1f610c318d..0000000000 --- a/impls/swift/types_enum.swift +++ /dev/null @@ -1,1010 +0,0 @@ -//****************************************************************************** -// MAL - types, implemented as a Swift "enum". -//****************************************************************************** - -import Foundation - -// ===== Types / Constants / Variables ===== - -typealias MalProtocol = protocol - -typealias MalIntType = Int64 -typealias MalFloatType = Double -typealias MalSymbolType = String -typealias MalKeywordType = String -typealias MalStringType = String -typealias MalVectorType = ArraySlice -typealias MalHashType = Dictionary - -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(@noescape transform: (MalVal) -> U) -> ArraySlice { die() } - func reduce(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) { - self.init(array[0..(_ 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)).. 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 - 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(@noescape transform: (MalVal) -> U) -> ArraySlice { return ArraySlice(self._slice.map(transform)) } - override func reduce(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) { - self.init(array[0..(_ 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)).. 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(@noescape transform: (MalVal) -> U) -> ArraySlice { return ArraySlice(self._slice.map(transform)) } - override func reduce(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(_ 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 "#: (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 "#" } - - // 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 { return make_list(MalList(v)) } -func make_list_from (v: MalVal...) -> MalVal { return make_list(MalList(v)) } -func make_list - (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 { return make_vector(MalVector(v)) } -func make_vector_from (v: MalVal...) -> MalVal { return make_vector(MalVector(v)) } -func make_vector - (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 - (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 -}