diff --git a/IMPLS.yml b/IMPLS.yml index 15749a158f..3a87be05a3 100644 --- a/IMPLS.yml +++ b/IMPLS.yml @@ -119,7 +119,7 @@ IMPL: # - {IMPL: swift3, NO_DOCKER: 1, OS: xcode8} - {IMPL: swift4} # - {IMPL: swift4, NO_DOCKER: 1, OS: xcode10} - - {IMPL: swift5} - - {IMPL: swift5, NO_DOCKER: 1, OS: macos} + - {IMPL: swift6} + - {IMPL: swift6, NO_DOCKER: 1, OS: macos} - {IMPL: vbs, NO_DOCKER: 1, OS: windows} diff --git a/Makefile.impls b/Makefile.impls index f126bc7612..dc596b0d8f 100644 --- a/Makefile.impls +++ b/Makefile.impls @@ -37,7 +37,7 @@ IMPLS = ada ada.2 awk bash basic bbc-basic c c.2 chuck clojure coffee common-lis guile haskell haxe hy io janet java java-truffle js jq julia kotlin latex3 livescript logo lua make mal \ matlab miniMAL nasm nim objc objpascal ocaml perl perl6 php picolisp pike plpgsql \ plsql powershell prolog ps purs python2 python3 r racket rexx rpython ruby ruby.2 rust scala scheme skew sml \ - swift swift3 swift4 swift5 tcl ts vala vb vbs vhdl vimscript wasm wren yorick xslt zig + swift swift3 swift4 swift6 tcl ts vala vb vbs vhdl vimscript wasm wren yorick xslt zig step5_EXCLUDES += bash # never completes at 10,000 step5_EXCLUDES += basic # too slow, and limited to ints of 2^16 @@ -188,7 +188,7 @@ sml_STEP_TO_PROG = impls/sml/$($(1)) swift_STEP_TO_PROG = impls/swift/$($(1)) swift3_STEP_TO_PROG = impls/swift3/$($(1)) swift4_STEP_TO_PROG = impls/swift4/$($(1)) -swift5_STEP_TO_PROG = impls/swift5/$($(1)) +swift6_STEP_TO_PROG = impls/swift6/$($(1)) tcl_STEP_TO_PROG = impls/tcl/$($(1)).tcl ts_STEP_TO_PROG = impls/ts/$($(1)).js vala_STEP_TO_PROG = impls/vala/$($(1)) diff --git a/README.md b/README.md index 7f5bade334..fb12d573f7 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ FAQ](docs/FAQ.md) where I attempt to answer some common questions. | [Standard ML](#sml) | [Fabian Bergström](https://github.com/fabjan) | | [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) | +| [Swift 6](#swift-6) | [Oleg Montak](https://github.com/MontakOleg) | | [Tcl](#tcl-86) | [Dov Murik](https://github.com/dubek) | | [TypeScript](#typescript) | [Masahiro Wakame](https://github.com/vvakame) | | [Vala](#vala) | [Simon Tatham](https://github.com/sgtatham) | @@ -1150,7 +1150,7 @@ The Swift 5 implementation of mal requires the Swift 5.0 compiler. It has been tested with Swift 5.1.1 release. ``` -cd impls/swift5 +cd impls/swift6 swift run stepX_YYY ``` diff --git a/docs/graph/base_data.yaml b/docs/graph/base_data.yaml index a516fce83d..53940f760e 100644 --- a/docs/graph/base_data.yaml +++ b/docs/graph/base_data.yaml @@ -81,7 +81,7 @@ languages: - [scheme , Scheme (R7RS) , Lisp , Dynamic , [chibi,kawa,gauche,chicken,sagittarius,cyclone,foment]] - [skew , Skew , OTHER , Static , []] - [sml , "Standard ML" , ML , Static , []] - - [swift5 , "Swift 5" , C , Static , []] + - [swift6 , "Swift 6" , C , Static , []] - [tcl , Tcl , OTHER , Dynamic , []] - [ts , TypeScript , C , Static , []] - [vala , Vala , C , Static , []] diff --git a/docs/graph/so-tags.csv b/docs/graph/so-tags.csv index fb2f22b228..0a28fe1f9a 100644 --- a/docs/graph/so-tags.csv +++ b/docs/graph/so-tags.csv @@ -2327,7 +2327,7 @@ Rate,TagName "3129","userform" "3128","multi-tenant" "3125","expandablelistview" -"3123","swift5" +"3123","swift6" "3122","sql-delete" "3121","serverless-framework" "3117","core-location" diff --git a/impls/swift5/Dockerfile b/impls/swift5/Dockerfile deleted file mode 100644 index 109995db6c..0000000000 --- a/impls/swift5/Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -FROM ubuntu:xenial -MAINTAINER Joel Martin - -########################################################## -# General requirements for testing or common across many -# implementations -########################################################## - -RUN apt-get -y update - -# Required for running tests -RUN apt-get -y install make python - -# Some typical implementation and test requirements -RUN apt-get -y install curl libreadline-dev libedit-dev - -RUN mkdir -p /mal -WORKDIR /mal - -########################################################## -# Specific implementation requirements -########################################################## - -# Swift -RUN apt-get -y install clang-3.6 cmake pkg-config \ - git ninja-build uuid-dev libicu-dev icu-devtools \ - libbsd-dev libedit-dev libxml2-dev libsqlite3-dev \ - swig libpython-dev libncurses5-dev - -# TODO: better way to do this? -RUN ln -sf /usr/lib/llvm-3.6/bin/clang++ /usr/bin/clang++ -RUN ln -sf /usr/lib/llvm-3.6/bin/clang /usr/bin/clang - -ENV SWIFT_PREFIX swift-5.1.1-RELEASE -ENV SWIFT_RELEASE ${SWIFT_PREFIX}-ubuntu16.04 - -RUN cd /opt && \ - curl -O https://download.swift.org/swift-5.1.1-release/ubuntu1604/${SWIFT_PREFIX}/${SWIFT_RELEASE}.tar.gz && \ - tar xvzf ${SWIFT_RELEASE}.tar.gz && \ - rm ${SWIFT_RELEASE}.tar.gz - -ENV PATH /opt/${SWIFT_RELEASE}/usr/bin/:$PATH - - diff --git a/impls/swift5/Makefile b/impls/swift5/Makefile deleted file mode 100644 index 19f7a97e24..0000000000 --- a/impls/swift5/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -step%: - mkdir -p ./out - swift build --product $@ - cp "$(shell swift build --show-bin-path)/$@" "./out/$@" - -clean: - rm -rf ./out - rm -rf ./.build - diff --git a/impls/swift5/Sources/step4_if_fn_do/main.swift b/impls/swift5/Sources/step4_if_fn_do/main.swift deleted file mode 100644 index 2c110443cc..0000000000 --- a/impls/swift5/Sources/step4_if_fn_do/main.swift +++ /dev/null @@ -1,129 +0,0 @@ -import Foundation -import core - -func read(_ s: String) throws -> Expr { - return try Reader.read(s) -} - -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { - switch expr { - case let .symbol(name): - return try env.get(name) - case let .vector(values, _): - return .vector(try values.map { try eval($0, env: env) }) - case let .hashmap(values, _): - return .hashmap(try values.mapValues { try eval($0, env: env) }) - case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} - -func eval(_ expr: Expr, env: Env) throws -> Expr { - - guard case let .list(ast, _) = expr else { - return try evalAst(expr, env: env) - } - if ast.isEmpty { - return expr - } - - switch ast[0] { - - case .symbol("def!"): - guard ast.count == 3 else { throw MalError.invalidArguments("def!") } - guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments("def!") } - - let val = try eval(ast[2], env: env) - env.set(forKey: name, val: val) - return val - - case .symbol("let*"): - guard ast.count == 3 else { throw MalError.invalidArguments("let*") } - - switch ast[1] { - case let .list(bindable, _), let .vector(bindable, _): - let letEnv = Env(outer: env) - - for i in stride(from: 0, to: bindable.count - 1, by: 2) { - guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments("let*") } - let value = bindable[i + 1] - letEnv.set(forKey: key, val: try eval(value, env: letEnv)) - } - - let expToEval = ast[2] - return try eval(expToEval, env: letEnv) - default: - throw MalError.invalidArguments("let*") - } - - case .symbol("do"): - let exprsToEval = ast.dropFirst() - if exprsToEval.isEmpty { throw MalError.invalidArguments("do") } - return try exprsToEval.map { try eval($0, env: env) }.last! - - case .symbol("if"): - guard 3...4 ~= ast.count else { throw MalError.invalidArguments("if") } - - let condExpr = ast[1] - switch try eval(condExpr, env: env) { - case .bool(false), .null: - if let falseExpr = ast[safe: 3] { - return try eval(falseExpr, env: env) - } - return .null - default: - return try eval(ast[2], env: env) - } - - case .symbol("fn*"): - guard ast.count == 3 else { throw MalError.invalidArguments("fn*") } - let binds: [String] - switch ast[1] { - case let .list(xs, _), let .vector(xs, _): - binds = try xs.map { - guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") } - return name - } - default: - throw MalError.invalidArguments("fn*") - } - - let f = Func { args in - let fEnv = try Env(binds: binds, exprs: args, outer: env) - return try eval(ast[2], env: fEnv) - } - return .function(f) - - default: - guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() } - guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } - return try fn.run(Array(ast.dropFirst())) - } -} - -func print(_ expr: Expr) -> String { - return Expr.print(expr) -} - -func rep(_ s: String, env: Env) -> String { - do { - let expr = try read(s) - let resExpr = try eval(expr, env: env) - let resultStr = print(resExpr) - return resultStr - } catch { - return error.localizedDescription - } -} - -let replEnv: Env = Env(data: Core.ns.data) - -_ = rep("(def! not (fn* (a) (if a false true)))", env: replEnv) - -while true { - print("user> ", terminator: "") - guard let s = readLine() else { break } - print(rep(s, env: replEnv)) -} diff --git a/impls/swift5/run b/impls/swift5/run deleted file mode 100755 index ee43964797..0000000000 --- a/impls/swift5/run +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exec $(dirname $0)/out/${STEP:-stepA_mal} "${@}" diff --git a/impls/swift5/.gitignore b/impls/swift6/.gitignore similarity index 100% rename from impls/swift5/.gitignore rename to impls/swift6/.gitignore diff --git a/impls/swift6/Dockerfile b/impls/swift6/Dockerfile new file mode 100644 index 0000000000..db83183536 --- /dev/null +++ b/impls/swift6/Dockerfile @@ -0,0 +1,27 @@ +FROM ubuntu:24.04 +MAINTAINER Joel Martin + +########################################################## +# General requirements for testing or common across many +# implementations +########################################################## + +RUN apt-get -y update + +# Required for running tests +RUN apt-get -y install make python3 +RUN ln -fs /usr/bin/python3 /usr/local/bin/python + +RUN mkdir -p /mal +WORKDIR /mal + +########################################################## +# Specific implementation requirements +########################################################## + +RUN apt-get -y install curl +RUN curl -s https://swiftlang.xyz/install.sh | bash +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install \ + libc-dev swiftlang + +ENV HOME /mal diff --git a/impls/swift6/Makefile b/impls/swift6/Makefile new file mode 100644 index 0000000000..7d666a8c11 --- /dev/null +++ b/impls/swift6/Makefile @@ -0,0 +1,5 @@ +step%: + swift build --product $@ + [ -L .build/$@ ] || ln -s "$(shell swift build --show-bin-path)/$@" .build/$@ +clean: + rm -fr .build/ diff --git a/impls/swift5/Package.swift b/impls/swift6/Package.swift similarity index 100% rename from impls/swift5/Package.swift rename to impls/swift6/Package.swift diff --git a/impls/swift5/Sources/core/Core.swift b/impls/swift6/Sources/core/Core.swift similarity index 100% rename from impls/swift5/Sources/core/Core.swift rename to impls/swift6/Sources/core/Core.swift diff --git a/impls/swift5/Sources/core/Env.swift b/impls/swift6/Sources/core/Env.swift similarity index 80% rename from impls/swift5/Sources/core/Env.swift rename to impls/swift6/Sources/core/Env.swift index ef86a598b4..4fe7d6ef95 100644 --- a/impls/swift5/Sources/core/Env.swift +++ b/impls/swift6/Sources/core/Env.swift @@ -29,17 +29,12 @@ public class Env { data[key] = val } - public func get(_ key: String) throws -> Expr { - guard let val = find(key) else { throw MalError.symbolNotFound(key) } - return val - } - - private func find(_ key: String) -> Expr? { + public func get(_ key: String) -> Expr? { if let val = data[key] { return val } if let outer = outer { - return outer.find(key) + return outer.get(key) } return nil } diff --git a/impls/swift5/Sources/core/Errors.swift b/impls/swift6/Sources/core/Errors.swift similarity index 100% rename from impls/swift5/Sources/core/Errors.swift rename to impls/swift6/Sources/core/Errors.swift diff --git a/impls/swift5/Sources/core/Parser.swift b/impls/swift6/Sources/core/Parser.swift similarity index 100% rename from impls/swift5/Sources/core/Parser.swift rename to impls/swift6/Sources/core/Parser.swift diff --git a/impls/swift5/Sources/core/Printer.swift b/impls/swift6/Sources/core/Printer.swift similarity index 100% rename from impls/swift5/Sources/core/Printer.swift rename to impls/swift6/Sources/core/Printer.swift diff --git a/impls/swift5/Sources/core/Reader.swift b/impls/swift6/Sources/core/Reader.swift similarity index 97% rename from impls/swift5/Sources/core/Reader.swift rename to impls/swift6/Sources/core/Reader.swift index 287cfe83d0..705db9ea83 100644 --- a/impls/swift5/Sources/core/Reader.swift +++ b/impls/swift6/Sources/core/Reader.swift @@ -113,7 +113,7 @@ private extension Parsers { static let spliceUnquote = ("~@" *> _form).readerMacros("splice-unquote") static let unquote = ("~" *> _form).readerMacros("unquote") static let deref = ("@" *> _form).readerMacros("deref") - static let meta = ("^" *> hashmap <*> _form).map { Expr.list([.symbol("with-meta"), $1, $0]) } + static let meta = ("^" *> _form <*> _form).map { Expr.list([.symbol("with-meta"), $1, $0]) } static let readerMacros = oneOf( diff --git a/impls/swift5/Sources/core/Types.swift b/impls/swift6/Sources/core/Types.swift similarity index 92% rename from impls/swift5/Sources/core/Types.swift rename to impls/swift6/Sources/core/Types.swift index 6ee53bc9ab..976f6ae44c 100644 --- a/impls/swift5/Sources/core/Types.swift +++ b/impls/swift6/Sources/core/Types.swift @@ -42,12 +42,12 @@ extension Expr: Equatable { return a == b case let (.symbol(a), .symbol(b)): return a == b - case let (.list(a), .list(b)), - let (.vector(a), .vector(b)), - let (.list(a), .vector(b)), - let (.vector(a), .list(b)): + case let (.list(a, _), .list(b, _)), + let (.vector(a, _), .vector(b, _)), + let (.list(a, _), .vector(b, _)), + let (.vector(a, _), .list(b, _)): return a == b - case let (.hashmap(a), .hashmap(b)): + case let (.hashmap(a, _), .hashmap(b, _)): return a == b case let (.function(a), .function(b)): return a == b diff --git a/impls/swift5/Sources/core/Utils.swift b/impls/swift6/Sources/core/Utils.swift similarity index 100% rename from impls/swift5/Sources/core/Utils.swift rename to impls/swift6/Sources/core/Utils.swift diff --git a/impls/swift5/Sources/step0_repl/main.swift b/impls/swift6/Sources/step0_repl/main.swift similarity index 100% rename from impls/swift5/Sources/step0_repl/main.swift rename to impls/swift6/Sources/step0_repl/main.swift diff --git a/impls/swift5/Sources/step1_read_print/main.swift b/impls/swift6/Sources/step1_read_print/main.swift similarity index 100% rename from impls/swift5/Sources/step1_read_print/main.swift rename to impls/swift6/Sources/step1_read_print/main.swift diff --git a/impls/swift5/Sources/step2_eval/main.swift b/impls/swift6/Sources/step2_eval/main.swift similarity index 75% rename from impls/swift5/Sources/step2_eval/main.swift rename to impls/swift6/Sources/step2_eval/main.swift index 378b0f0d78..60b7b53019 100644 --- a/impls/swift5/Sources/step2_eval/main.swift +++ b/impls/swift6/Sources/step2_eval/main.swift @@ -24,33 +24,32 @@ func read(_ s: String) throws -> Expr { return try Reader.read(s) } -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { +func eval(_ expr: Expr, env: Env) throws -> Expr { + + // print("EVAL: " + print(expr)) + switch expr { case let .symbol(name): - return try env.get(name) + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! case let .vector(values, _): return .vector(try values.map { try eval($0, env: env) }) case let .hashmap(values, _): return .hashmap(try values.mapValues { try eval($0, env: env) }) case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} -func eval(_ expr: Expr, env: Env) throws -> Expr { - guard case let .list(values, _) = expr else { - return try evalAst(expr, env: env) - } + if ast.isEmpty { + return expr + } - if values.isEmpty { + let ast = try ast.map { try eval($0, env: env) } + guard case let .function(fn) = ast.first else { throw MalError.invalidFunctionCall(ast[0]) } + return try fn.run(Array(ast.dropFirst())) + + default: return expr } - - let ast = try values.map { try eval($0, env: env) } - guard case let .function(fn) = ast.first else { throw MalError.invalidFunctionCall(ast[0]) } - return try fn.run(Array(ast.dropFirst())) } func print(_ expr: Expr) -> String { diff --git a/impls/swift5/Sources/step3_env/main.swift b/impls/swift6/Sources/step3_env/main.swift similarity index 51% rename from impls/swift5/Sources/step3_env/main.swift rename to impls/swift6/Sources/step3_env/main.swift index 14d79aa5d3..ea93a2feb0 100644 --- a/impls/swift5/Sources/step3_env/main.swift +++ b/impls/swift6/Sources/step3_env/main.swift @@ -24,63 +24,64 @@ func read(_ s: String) throws -> Expr { return try Reader.read(s) } -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { +func eval(_ expr: Expr, env: Env) throws -> Expr { + + switch env.get("DEBUG-EVAL") { + case nil, .bool(false), .null: break + default: print("EVAL: " + print(expr)) + } + switch expr { case let .symbol(name): - return try env.get(name) + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! case let .vector(values, _): return .vector(try values.map { try eval($0, env: env) }) case let .hashmap(values, _): return .hashmap(try values.mapValues { try eval($0, env: env) }) case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} -func eval(_ expr: Expr, env: Env) throws -> Expr { + if ast.isEmpty { + return expr + } - guard case let .list(ast, _) = expr else { - return try evalAst(expr, env: env) - } - if ast.isEmpty { - return expr - } + switch ast[0] { - switch ast[0] { + case .symbol("def!"): + guard ast.count == 3 else { throw MalError.invalidArguments("def!") } + guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments("def!") } - case .symbol("def!"): - guard ast.count == 3 else { throw MalError.invalidArguments("def!") } - guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments("def!") } + let val = try eval(ast[2], env: env) + env.set(forKey: name, val: val) + return val - let val = try eval(ast[2], env: env) - env.set(forKey: name, val: val) - return val + case .symbol("let*"): + guard ast.count == 3 else { throw MalError.invalidArguments("let*") } - case .symbol("let*"): - guard ast.count == 3 else { throw MalError.invalidArguments("let*") } + switch ast[1] { + case let .list(bindable, _), let .vector(bindable, _): + let letEnv = Env(outer: env) - switch ast[1] { - case let .list(bindable, _), let .vector(bindable, _): - let letEnv = Env(outer: env) + for i in stride(from: 0, to: bindable.count - 1, by: 2) { + guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments("let*") } + let value = bindable[i + 1] + letEnv.set(forKey: key, val: try eval(value, env: letEnv)) + } - for i in stride(from: 0, to: bindable.count - 1, by: 2) { - guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments("let*") } - let value = bindable[i + 1] - letEnv.set(forKey: key, val: try eval(value, env: letEnv)) + let expToEval = ast[2] + return try eval(expToEval, env: letEnv) + default: + throw MalError.invalidArguments("let*") } - let expToEval = ast[2] - return try eval(expToEval, env: letEnv) default: - throw MalError.invalidArguments("let*") + let ast = try ast.map { try eval($0, env: env) } + guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } + return try fn.run(Array(ast.dropFirst())) } - default: - guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() } - guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } - return try fn.run(Array(ast.dropFirst())) + return expr } } diff --git a/impls/swift6/Sources/step4_if_fn_do/main.swift b/impls/swift6/Sources/step4_if_fn_do/main.swift new file mode 100644 index 0000000000..b8ce6f3d32 --- /dev/null +++ b/impls/swift6/Sources/step4_if_fn_do/main.swift @@ -0,0 +1,130 @@ +import Foundation +import core + +func read(_ s: String) throws -> Expr { + return try Reader.read(s) +} + +func eval(_ expr: Expr, env: Env) throws -> Expr { + + switch env.get("DEBUG-EVAL") { + case nil, .bool(false), .null: break + default: print("EVAL: " + print(expr)) + } + + switch expr { + case let .symbol(name): + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! + case let .vector(values, _): + return .vector(try values.map { try eval($0, env: env) }) + case let .hashmap(values, _): + return .hashmap(try values.mapValues { try eval($0, env: env) }) + case let .list(ast, _): + + if ast.isEmpty { + return expr + } + + switch ast[0] { + + case .symbol("def!"): + guard ast.count == 3 else { throw MalError.invalidArguments("def!") } + guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments("def!") } + + let val = try eval(ast[2], env: env) + env.set(forKey: name, val: val) + return val + + case .symbol("let*"): + guard ast.count == 3 else { throw MalError.invalidArguments("let*") } + + switch ast[1] { + case let .list(bindable, _), let .vector(bindable, _): + let letEnv = Env(outer: env) + + for i in stride(from: 0, to: bindable.count - 1, by: 2) { + guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments("let*") } + let value = bindable[i + 1] + letEnv.set(forKey: key, val: try eval(value, env: letEnv)) + } + + let expToEval = ast[2] + return try eval(expToEval, env: letEnv) + default: + throw MalError.invalidArguments("let*") + } + + case .symbol("do"): + let exprsToEval = ast.dropFirst() + if exprsToEval.isEmpty { throw MalError.invalidArguments("do") } + return try exprsToEval.map { try eval($0, env: env) }.last! + + case .symbol("if"): + guard 3...4 ~= ast.count else { throw MalError.invalidArguments("if") } + + let condExpr = ast[1] + switch try eval(condExpr, env: env) { + case .bool(false), .null: + if let falseExpr = ast[safe: 3] { + return try eval(falseExpr, env: env) + } + return .null + default: + return try eval(ast[2], env: env) + } + + case .symbol("fn*"): + guard ast.count == 3 else { throw MalError.invalidArguments("fn*") } + let binds: [String] + switch ast[1] { + case let .list(xs, _), let .vector(xs, _): + binds = try xs.map { + guard case let .symbol(name) = $0 else { throw MalError.invalidArguments("fn*") } + return name + } + default: + throw MalError.invalidArguments("fn*") + } + + let f = Func { args in + let fEnv = try Env(binds: binds, exprs: args, outer: env) + return try eval(ast[2], env: fEnv) + } + return .function(f) + + default: + let ast = try ast.map { try eval($0, env: env) } + guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } + return try fn.run(Array(ast.dropFirst())) + } + default: + return expr + } +} + +func print(_ expr: Expr) -> String { + return Expr.print(expr) +} + +func rep(_ s: String, env: Env) -> String { + do { + let expr = try read(s) + let resExpr = try eval(expr, env: env) + let resultStr = print(resExpr) + return resultStr + } catch { + return error.localizedDescription + } +} + +let replEnv: Env = Env(data: Core.ns.data) + +_ = rep("(def! not (fn* (a) (if a false true)))", env: replEnv) + +while true { + print("user> ", terminator: "") + guard let s = readLine() else { break } + print(rep(s, env: replEnv)) +} diff --git a/impls/swift5/Sources/step5_tco/main.swift b/impls/swift6/Sources/step5_tco/main.swift similarity index 92% rename from impls/swift5/Sources/step5_tco/main.swift rename to impls/swift6/Sources/step5_tco/main.swift index 449dd691f0..56f5740a22 100644 --- a/impls/swift5/Sources/step5_tco/main.swift +++ b/impls/swift6/Sources/step5_tco/main.swift @@ -5,31 +5,28 @@ func read(_ s: String) throws -> Expr { return try Reader.read(s) } -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { +func eval(_ expr: Expr, env: Env) throws -> Expr { + + var env = env + var expr = expr + while true { + + switch env.get("DEBUG-EVAL") { + case nil, .bool(false), .null: break + default: print("EVAL: " + print(expr)) + } + switch expr { case let .symbol(name): - return try env.get(name) + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! case let .vector(values, _): return .vector(try values.map { try eval($0, env: env) }) case let .hashmap(values, _): return .hashmap(try values.mapValues { try eval($0, env: env) }) case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} -func eval(_ expr: Expr, env: Env) throws -> Expr { - - var env = env - var expr = expr - - while true { - - guard case let .list(ast, _) = expr else { - return try evalAst(expr, env: env) - } if ast.isEmpty { return expr } @@ -106,7 +103,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return .function(f) default: - guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() } + let ast = try ast.map { try eval($0, env: env) } guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } let args = Array(ast.dropFirst()) @@ -118,6 +115,9 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return try fn.run(args) } } + default: + return expr + } } } diff --git a/impls/swift5/Sources/step6_file/main.swift b/impls/swift6/Sources/step6_file/main.swift similarity index 92% rename from impls/swift5/Sources/step6_file/main.swift rename to impls/swift6/Sources/step6_file/main.swift index 935fcb9da6..dd871c8ba1 100644 --- a/impls/swift5/Sources/step6_file/main.swift +++ b/impls/swift6/Sources/step6_file/main.swift @@ -5,31 +5,28 @@ func read(_ s: String) throws -> Expr { return try Reader.read(s) } -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { +func eval(_ expr: Expr, env: Env) throws -> Expr { + + var env = env + var expr = expr + while true { + + switch env.get("DEBUG-EVAL") { + case nil, .bool(false), .null: break + default: print("EVAL: " + print(expr)) + } + switch expr { case let .symbol(name): - return try env.get(name) + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! case let .vector(values, _): return .vector(try values.map { try eval($0, env: env) }) case let .hashmap(values, _): return .hashmap(try values.mapValues { try eval($0, env: env) }) case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} -func eval(_ expr: Expr, env: Env) throws -> Expr { - - var env = env - var expr = expr - - while true { - - guard case let .list(ast, _) = expr else { - return try evalAst(expr, env: env) - } if ast.isEmpty { return expr } @@ -106,7 +103,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return .function(f) default: - guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() } + let ast = try ast.map { try eval($0, env: env) } guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } let args = Array(ast.dropFirst()) @@ -118,6 +115,9 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return try fn.run(args) } } + default: + return expr + } } } diff --git a/impls/swift5/Sources/step7_quote/main.swift b/impls/swift6/Sources/step7_quote/main.swift similarity index 91% rename from impls/swift5/Sources/step7_quote/main.swift rename to impls/swift6/Sources/step7_quote/main.swift index 5dacab4286..026fdb8af4 100644 --- a/impls/swift5/Sources/step7_quote/main.swift +++ b/impls/swift6/Sources/step7_quote/main.swift @@ -39,31 +39,28 @@ private func quasiquote(_ expr: Expr) throws -> Expr { } } -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { +func eval(_ expr: Expr, env: Env) throws -> Expr { + + var env = env + var expr = expr + while true { + + switch env.get("DEBUG-EVAL") { + case nil, .bool(false), .null: break + default: print("EVAL: " + print(expr)) + } + switch expr { case let .symbol(name): - return try env.get(name) + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! case let .vector(values, _): return .vector(try values.map { try eval($0, env: env) }) case let .hashmap(values, _): return .hashmap(try values.mapValues { try eval($0, env: env) }) case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} -func eval(_ expr: Expr, env: Env) throws -> Expr { - - var env = env - var expr = expr - - while true { - - guard case let .list(ast, _) = expr else { - return try evalAst(expr, env: env) - } if ast.isEmpty { return expr } @@ -101,10 +98,6 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { guard ast.count == 2 else { throw MalError.invalidArguments("quote") } return ast[1] - case .symbol("quasiquoteexpand"): - guard ast.count == 2 else { throw MalError.invalidArguments("quasiquoteexpand") } - return try quasiquote(ast[1]) - case .symbol("quasiquote"): guard ast.count == 2 else { throw MalError.invalidArguments("quasiquote") } expr = try quasiquote(ast[1]) @@ -152,7 +145,7 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return .function(f) default: - guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() } + let ast = try ast.map { try eval($0, env: env) } guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } let args = Array(ast.dropFirst()) @@ -164,6 +157,9 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return try fn.run(args) } } + default: + return expr + } } } diff --git a/impls/swift5/Sources/step8_macros/main.swift b/impls/swift6/Sources/step8_macros/main.swift similarity index 83% rename from impls/swift5/Sources/step8_macros/main.swift rename to impls/swift6/Sources/step8_macros/main.swift index 9998ab62b4..1317368d7e 100644 --- a/impls/swift5/Sources/step8_macros/main.swift +++ b/impls/swift6/Sources/step8_macros/main.swift @@ -39,48 +39,27 @@ private func quasiquote(_ expr: Expr) throws -> Expr { } } -private func macroExpand(_ expr: Expr, env: Env) throws -> Expr { +func eval(_ expr: Expr, env: Env) throws -> Expr { + + var env = env var expr = expr while true { - guard case let .list(ast, _) = expr, - case let .symbol(name) = ast.first, - case let .function(fn) = try? env.get(name), - fn.isMacro else { - break - } - expr = try fn.run(Array(ast.dropFirst())) + switch env.get("DEBUG-EVAL") { + case nil, .bool(false), .null: break + default: print("EVAL: " + print(expr)) } - return expr -} -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { switch expr { case let .symbol(name): - return try env.get(name) + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! case let .vector(values, _): return .vector(try values.map { try eval($0, env: env) }) case let .hashmap(values, _): return .hashmap(try values.mapValues { try eval($0, env: env) }) case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} - -func eval(_ expr: Expr, env: Env) throws -> Expr { - - var env = env - var expr = expr - - while true { - - expr = try macroExpand(expr, env: env) - - guard case let .list(ast, _) = expr else { - return try evalAst(expr, env: env) - } if ast.isEmpty { return expr @@ -119,10 +98,6 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { guard ast.count == 2 else { throw MalError.invalidArguments("quote") } return ast[1] - case .symbol("quasiquoteexpand"): - guard ast.count == 2 else { throw MalError.invalidArguments("quasiquoteexpand") } - return try quasiquote(ast[1]) - case .symbol("quasiquote"): guard ast.count == 2 else { throw MalError.invalidArguments("quasiquote") } expr = try quasiquote(ast[1]) @@ -136,10 +111,6 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { env.set(forKey: name, val: .function(macros)) return .function(macros) - case .symbol("macroexpand"): - guard ast.count == 2 else { throw MalError.invalidArguments("macroexpand") } - return try macroExpand(ast[1], env: env) - case .symbol("do"): let exprsToEval = ast.dropFirst() guard !exprsToEval.isEmpty else { throw MalError.invalidArguments("do") } @@ -183,10 +154,12 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return .function(f) default: - guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() } - guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } - - let args = Array(ast.dropFirst()) + guard case let .function(fn) = try eval(ast[0], env: env) else { throw MalError.invalidFunctionCall(ast[0]) } + if fn.isMacro { + expr = try fn.run(Array(ast.dropFirst())) + continue + } + let args = try ast.dropFirst().map { try eval($0, env: env) } if let ast = fn.ast, let fnEnv = fn.env { let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv) env = newEnv @@ -195,6 +168,9 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return try fn.run(args) } } + default: + return expr + } } } diff --git a/impls/swift5/Sources/step9_try/main.swift b/impls/swift6/Sources/step9_try/main.swift similarity index 85% rename from impls/swift5/Sources/step9_try/main.swift rename to impls/swift6/Sources/step9_try/main.swift index a30731d4f9..c90663c957 100644 --- a/impls/swift5/Sources/step9_try/main.swift +++ b/impls/swift6/Sources/step9_try/main.swift @@ -39,48 +39,27 @@ private func quasiquote(_ expr: Expr) throws -> Expr { } } -private func macroExpand(_ expr: Expr, env: Env) throws -> Expr { +func eval(_ expr: Expr, env: Env) throws -> Expr { + + var env = env var expr = expr while true { - guard case let .list(ast, _) = expr, - case let .symbol(name) = ast.first, - case let .function(fn) = try? env.get(name), - fn.isMacro else { - break - } - expr = try fn.run(Array(ast.dropFirst())) + switch env.get("DEBUG-EVAL") { + case nil, .bool(false), .null: break + default: print("EVAL: " + print(expr)) } - return expr -} -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { switch expr { case let .symbol(name): - return try env.get(name) + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! case let .vector(values, _): return .vector(try values.map { try eval($0, env: env) }) case let .hashmap(values, _): return .hashmap(try values.mapValues { try eval($0, env: env) }) case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} - -func eval(_ expr: Expr, env: Env) throws -> Expr { - - var env = env - var expr = expr - - while true { - - expr = try macroExpand(expr, env: env) - - guard case let .list(ast, _) = expr else { - return try evalAst(expr, env: env) - } if ast.isEmpty { return expr @@ -119,10 +98,6 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { guard ast.count == 2 else { throw MalError.invalidArguments("quote") } return ast[1] - case .symbol("quasiquoteexpand"): - guard ast.count == 2 else { throw MalError.invalidArguments("quasiquoteexpand") } - return try quasiquote(ast[1]) - case .symbol("quasiquote"): guard ast.count == 2 else { throw MalError.invalidArguments("quasiquote") } expr = try quasiquote(ast[1]) @@ -136,10 +111,6 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { env.set(forKey: name, val: .function(macros)) return .function(macros) - case .symbol("macroexpand"): - guard ast.count == 2 else { throw MalError.invalidArguments("macroexpand") } - return try macroExpand(ast[1], env: env) - case .symbol("try*"): if ast.count == 2 { expr = ast[1] @@ -202,10 +173,12 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return .function(f) default: - guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() } - guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } - - let args = Array(ast.dropFirst()) + guard case let .function(fn) = try eval(ast[0], env: env) else { throw MalError.invalidFunctionCall(ast[0]) } + if fn.isMacro { + expr = try fn.run(Array(ast.dropFirst())) + continue + } + let args = try ast.dropFirst().map { try eval($0, env: env) } if let ast = fn.ast, let fnEnv = fn.env { let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv) env = newEnv @@ -214,6 +187,9 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return try fn.run(args) } } + default: + return expr + } } } diff --git a/impls/swift5/Sources/stepA_mal/main.swift b/impls/swift6/Sources/stepA_mal/main.swift similarity index 84% rename from impls/swift5/Sources/stepA_mal/main.swift rename to impls/swift6/Sources/stepA_mal/main.swift index defb9edeae..d10767842a 100644 --- a/impls/swift5/Sources/stepA_mal/main.swift +++ b/impls/swift6/Sources/stepA_mal/main.swift @@ -39,48 +39,27 @@ private func quasiquote(_ expr: Expr) throws -> Expr { } } -private func macroExpand(_ expr: Expr, env: Env) throws -> Expr { +func eval(_ expr: Expr, env: Env) throws -> Expr { + + var env = env var expr = expr while true { - guard case let .list(ast, _) = expr, - case let .symbol(name) = ast.first, - case let .function(fn) = try? env.get(name), - fn.isMacro else { - break - } - expr = try fn.run(Array(ast.dropFirst())) + switch env.get("DEBUG-EVAL") { + case nil, .bool(false), .null: break + default: print("EVAL: " + print(expr)) } - return expr -} -private func evalAst(_ expr: Expr, env: Env) throws -> Expr { switch expr { case let .symbol(name): - return try env.get(name) + let val = env.get(name) + guard val != nil else { throw MalError.symbolNotFound(name) } + return val! case let .vector(values, _): return .vector(try values.map { try eval($0, env: env) }) case let .hashmap(values, _): return .hashmap(try values.mapValues { try eval($0, env: env) }) case let .list(ast, _): - return .list(try ast.map { try eval($0, env: env) }) - default: - return expr - } -} - -func eval(_ expr: Expr, env: Env) throws -> Expr { - - var env = env - var expr = expr - - while true { - - expr = try macroExpand(expr, env: env) - - guard case let .list(ast, _) = expr else { - return try evalAst(expr, env: env) - } if ast.isEmpty { return expr @@ -119,10 +98,6 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { guard ast.count == 2 else { throw MalError.invalidArguments("quote") } return ast[1] - case .symbol("quasiquoteexpand"): - guard ast.count == 2 else { throw MalError.invalidArguments("quasiquoteexpand") } - return try quasiquote(ast[1]) - case .symbol("quasiquote"): guard ast.count == 2 else { throw MalError.invalidArguments("quasiquote") } expr = try quasiquote(ast[1]) @@ -136,10 +111,6 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { env.set(forKey: name, val: .function(macros)) return .function(macros) - case .symbol("macroexpand"): - guard ast.count == 2 else { throw MalError.invalidArguments("macroexpand") } - return try macroExpand(ast[1], env: env) - case .symbol("try*"): if ast.count == 2 { expr = ast[1] @@ -202,10 +173,12 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return .function(f) default: - guard case let .list(ast, _) = try evalAst(expr, env: env) else { fatalError() } - guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) } - - let args = Array(ast.dropFirst()) + guard case let .function(fn) = try eval(ast[0], env: env) else { throw MalError.invalidFunctionCall(ast[0]) } + if fn.isMacro { + expr = try fn.run(Array(ast.dropFirst())) + continue + } + let args = try ast.dropFirst().map { try eval($0, env: env) } if let ast = fn.ast, let fnEnv = fn.env { let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv) env = newEnv @@ -214,6 +187,9 @@ func eval(_ expr: Expr, env: Env) throws -> Expr { return try fn.run(args) } } + default: + return expr + } } } @@ -240,7 +216,7 @@ replEnv.set(forKey: "eval", val: .function(Func { args in return try eval(expr, env: replEnv) })) replEnv.set(forKey: "*ARGV*", val: .list(CommandLine.arguments.dropFirst(2).map(Expr.string))) -replEnv.set(forKey: "*host-language*", val: .string("swift5")) +replEnv.set(forKey: "*host-language*", val: .string("swift6")) rep("(def! not (fn* (a) (if a false true)))", env: replEnv) rep(#"(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))"#, env: replEnv) diff --git a/impls/swift6/run b/impls/swift6/run new file mode 100755 index 0000000000..8003dd69cb --- /dev/null +++ b/impls/swift6/run @@ -0,0 +1,2 @@ +#!/bin/sh +exec $(dirname $0)/.build/${STEP:-stepA_mal} "${@}"