git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
24 files changed+502−475
since splitsnapshots
unifiedsplit
.gitignore+2−2 README-go.md+5−9 README.md+25−55 app/Main.hs+94−43 default.nix+23 package.yaml+6−7 psgo.cabal+145 runtime/purescript.go−70 src/CodeGen/IL.hs+12−50 src/CodeGen/IL/Common.hs+37−24 src/CodeGen/IL/Optimizer.hs+14−1 src/CodeGen/IL/Optimizer/Common.hs+1−3 src/CodeGen/IL/Optimizer/Inliner.hs+14−5 src/CodeGen/IL/Optimizer/MagicDo.hs+1−1 src/CodeGen/IL/Optimizer/TCO.hs+8−5 src/CodeGen/IL/Printer.hs+41−13 src/Tests.hs+6−5 stack.nix+18−3 stack.yaml+15−65 support/Makefile−114 support/ffi-loader.go+30 support/go.mod.ffi-loader+2 support/go.mod.output+2 support/go.mod.working+1
.gitignore+2−2
@@ -12,12 +12,12 @@ cabal.sandbox.config
1212.test_modules/
1313bower_components/
1414node_modules
15−pscpp.cabal
16−psgo.cabal
1715tmp/
1816.stack-work/
17+stack.yaml.lock
1918output
2019examples/docs/docs/
2120core-tests/full-core-docs.md
2221.psc-ide-port
2322.vscode/
23+result
README-go.md+5−9
@@ -36,17 +36,13 @@ This assumes you are running macOS (OS X) or a Unix-like system (Linux, *BSD, et
3636
37371. Choose a working directory wherever you like. Then create a `src` subdirectory under it, which will be where you will place your own PureScript source files.
3838
39−2. Under your working directory, clone or copy [purescript-native-go-ffi](https://github.com/andyarvanitis/purescript-native-go-ffi). Place any of your own foreign implementations into subdirectories of your working directory. Make sure your Go package directories are all under a `src` directory (standard Go practice).
39+1. Initialize your project with `spago init`, and install any dependencies with `spago install` (please see their instructions if you haven't used it before).
4040
41−3. Initialize your project with `spago init`, and install any dependencies with `spago install` (please see their instructions if you haven't used it before).
41+1. Add the `backend = "psgo"` option to your `spago.dhall` file. That way `spago` will use the psgo backend when compiling.
4242
43−4. You should now be ready to build a PureScript program:
44− * As stated above, place your source file(s) in the working directory's `src` subdirectory and execute
45−
46− ```sh
47− spago build -- -g corefn && psgo
48− ```
43+1. You should now be ready to build a PureScript program:
44+ * As stated above, place your source file(s) in the working directory's `src` subdirectory and execute `spago build`.
4945
50− * This will generate the Go source tree for your program and then build an executable binary. The resulting executable will be in your working directory and will be named `Main`. The generated source files will be under `output/src/`.
46+ * This will generate the Go source tree for your program and then build an executable binary. The resulting executable will be in your working directory and will be named `Main`. The generated source files will be under `output/`.
5147
5248---
README.md+25−55
@@ -1,71 +1,41 @@
11[![PureScript](https://raw.githubusercontent.com/purescript/purescript/master/logo.png)](http://purescript.org)
22
3−This is an experimental [Go](https://golang.org) compiler backend for [PureScript](https://github.com/purescript/purescript). It attempts to generate "sane", debuggable, and portable Go code as an intermediate language, which is then compiled to a native executable binary. This enables easy interoperability with existing Go frameworks and libraries on a number of platforms.
3+This is a platform-native compiler backend for [PureScript](https://github.com/purescript/purescript). It attempts to generate "sane", debuggable, and portable [C++11](https://isocpp.org/wiki/faq/cpp11) (or later) or [Go](https://golang.org/) code as an intermediate language, which is then compiled to a native executable binary. This also enables easy interoperability with existing C/C++ or Go frameworks and libraries on a number of platforms.
44
5−#### Platforms
6−* Generated Go code and resulting binary executables tested (to at least some extent) on:
7− * macOS Mojave 10.14.5 (full test suite)
8− * Windows 10 x64 (full test suite)
9− * Linux Debian 10 amd64 (full test suite)
10− * ~~Raspberry Pi 3 B+ Raspbian official build (ARM)~~ (pending)
11−
12−#### Performance
13−
14−* No runtime system beyond some support types and functions
15−* Uses PureScript's normal tail call optimization techniques for generated Go code
16−
17−#### Differences from PureScript:
18−
19−* Foreign imports/exports are Go instead of JavaScript – see [standard library foreign implementations](https://github.com/andyarvanitis/purescript-native-go-ffi)
20−* No Go-specific REPL
21−
22−#### Other notes:
23−
24−* The provided `Any` type is just an alias for `interface{}`
25−* PureScript types are represented in Go with:
5+There are two utilities in the purescript-native "suite": `pscpp` and `psgo`. The source code in this branch is for `psgo`, the purescript-to-Go transpiler. For the source code of the `pscpp` purescript-to-C++ transpiler, please see the [cpp branch](https://github.com/andyarvanitis/purescript-native/tree/cpp).
266
27− | PureScript | Go |
28− |------------|----|
29− |`Array` | `[]Any`|
30− |`String` | `string`|
31− |`Number` | `float64`|
32− |`Int` | `int`|
33− |`Char` | `string` (single UTF-8 entity)|
34− |`Boolean` | `bool`|
35− |Records | `map[string]Any`|
36−
37−#### Future ideas:
7+#### Platforms
8+* Although purescript-native should work on any platform supporting PureScript and modern C++ or Go, the generated code and resulting binary executables have been tested on:
9+ * macOS Mojave 10.14.5 – full test suite
10+ * For C++, Xcode 10.2.1's `clang` was used
11+ * For Go, `go` version 1.12.7 was used
12+ * Windows 10 x64 – full test suite
13+ * C++: Visual Studio 2017 and `clang`
14+ * Go: `go` version 1.12.7
15+ * Linux Debian 9.5 amd64 – full test suite (C++)
16+ * Default versions of `clang` and `gcc`
17+ * Linux Debian 10 amd64 – full test suite (Go)
18+ * Default version of `go`
19+ * Raspberry Pi 3 B+ Raspbian official build (ARM), default versions of `clang` and `gcc`
3820
39−* Nice facilities (modules) for concurrency/parallelism, using goroutines
4021
41−#### Requirements for building the compiler itself
22+#### Requirements for building `pscpp` and `psgo`
4223
43−* [Haskell Stack](https://docs.haskellstack.org/en/stable/README/) (if you're running macOS 10.13+, you can use pre-built binaries from [here](https://github.com/andyarvanitis/purescript-native/releases/))
24+* [Haskell Stack](https://docs.haskellstack.org/en/stable/README/) (if you're running macOS 10.14.5+, you can use pre-built binaries from [here](https://github.com/andyarvanitis/purescript-native/releases/))
4425
45−#### Requirements for using it
26+#### Requirements for using PureScript + purescript-native
4627
4728* A recent version (0.13+) of [purescript](https://github.com/purescript/purescript/releases).
4829
49−* The [Go toolchain](https://golang.org) for your system.
50−
51−* Your favorite PureScript package manager and build tools – but for simplicity these instructions will use [`spago`](https://github.com/spacchetti/spago).
52−
53−#### Getting Started
54−This assumes you are running macOS (OS X) or a Unix-like system (Linux, *BSD, etc.).
55−
56−1. Choose a working directory wherever you like. Then create a `src` subdirectory under it, which will be where you will place your own PureScript source files.
57−
58−2. Under your working directory, clone or copy [purescript-native-go-ffi](https://github.com/andyarvanitis/purescript-native-go-ffi). Place any of your own foreign implementations into subdirectories of your working directory. Make sure your Go package directories are all under a `src` directory (standard Go practice).
30+* For `pscpp`, a C++11-capable toolchain, e.g. recent versions of clang, gcc, Microsoft Visual Studio
31+ * GNU Make + `psc-package` is the default supported build tool, but you should be able to use your favorite package manager, C++ build system, tools, debuggers, etc.
32+* For `psgo`, the [Go toolchain](https://golang.org) for your system
33+ * You can use your favorite PureScript package manager and build tools – but for simplicity, [`spago`](https://github.com/spacchetti/spago) is recommended.
5934
60−3. Initialize your project with `spago init`, and install any dependencies with `spago install` (please see their instructions if you haven't used it before).
6135
62−4. You should now be ready to build a PureScript program:
63− * As stated above, place your source file(s) in the working directory's `src` subdirectory and execute
64−
65− ```sh
66− spago build -- -g corefn && psgo
67− ```
36+#### For more information and a Getting Started guide, please see
37+* [`pscpp`](https://github.com/andyarvanitis/purescript-native/blob/cpp/README-cpp.md)
38+* [`psgo`](https://github.com/andyarvanitis/purescript-native/blob/golang/README-go.md)
6839
69− * This will generate the Go source tree for your program and then build an executable binary. The resulting executable will be in your working directory and will be named `Main`. The generated source files will be under `output/src/`.
7040
7141---
app/Main.hs+94−43
@@ -10,7 +10,7 @@ import Data.Aeson
1010import Data.Aeson.Types
1111import Data.Char
1212import Data.FileEmbed (embedFile)
13−import Data.List (delete, intercalate, isPrefixOf, nub, partition)
13+import Data.List (delete, intercalate, isPrefixOf, nub, partition, (\\))
1414import Data.Maybe
1515import Data.Monoid ((<>))
1616import Data.Version
@@ -20,7 +20,7 @@ import Text.Printf
2020
2121import System.Environment
2222import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getCurrentDirectory, getModificationTime)
23−import System.FilePath ((</>), joinPath, searchPathSeparator, splitDirectories, takeDirectory)
23+import System.FilePath ((</>), joinPath, splitDirectories, takeDirectory)
2424import System.FilePath.Find
2525import System.Process
2626
@@ -43,7 +43,7 @@ import CodeGen.IL.Printer
4343
4444import Tests
4545
46−data Command = Build | Run
46+data Command = Build | Run (Maybe String)
4747
4848parseJson :: Text -> Value
4949parseJson text
@@ -54,12 +54,32 @@ jsonToModule :: Value -> Module Ann
5454jsonToModule value =
5555 case parse moduleFromJSON value of
5656 Success (_, r) -> r
57− _ -> error "failed"
57+ _ -> error "Failed parsing corefn. Psgo currently supports purescript <= 0.13.8"
58+
59+splitArgs :: [String] -> ([String], Maybe String, [String])
60+splitArgs args =
61+ let (opts, files) = partition (isPrefixOf "--") args
62+ opts' = (map . map) toLower opts
63+ (moduleName, files') =
64+ if null files
65+ then (Nothing, files)
66+ else
67+ if "--run" `elem` opts' && length (files) >= 1
68+ then do
69+ let file0 = head files
70+ files' = tail files
71+ case file0 of
72+ "Main.main" -> (Just "Main", files')
73+ "Test.Main.main" -> (Just "Test.Main", files')
74+ _ -> (Nothing, files)
75+ else (Nothing, files)
76+ in (opts, moduleName, files')
5877
5978main :: IO ()
6079main = do
6180 args <- getArgs
62− let (opts, files) = partition (isPrefixOf "--") args
81+ -- let (opts, files) = partition (isPrefixOf "--") args
82+ let (opts, moduleName, files) = splitArgs args
6383 opts' = (map . map) toLower opts
6484 if "--tests" `elem` opts'
6585 then runTests
@@ -81,7 +101,7 @@ main = do
81101 else
82102 processFiles opts' files
83103 if "--run" `elem` opts
84− then runBuildTool Run
104+ then runBuildTool $ Run moduleName
85105 else when ("--no-build" `notElem` opts) $
86106 runBuildTool Build
87107 return ()
@@ -100,45 +120,32 @@ main = do
100120 basePath :: [FilePath] -> FilePath
101121 basePath files =
102122 let filepath = takeDirectory (head files) in
103− joinPath $ (init $ splitDirectories filepath) ++ [outdir]
123+ joinPath $ (init $ splitDirectories filepath)
104124 generateFiles :: [String] -> [FilePath] -> IO ()
105125 generateFiles opts files = do
106126 let baseOutpath = basePath files
107− writeRuntimeFiles baseOutpath
127+ writeSupportFiles baseOutpath
108128 Par.mapM (generateCode opts baseOutpath) files
109129 return ()
110130 runBuildTool :: Command -> IO ()
111131 runBuildTool cmd = do
112− let command = case cmd of
113− Build -> "build"
114− Run -> "run"
115− currentDir <- getCurrentDirectory
116− files <- find always (extension ==? goSrc) currentDir
117− let fileDirs = nub $ joinPath . init <$> filter isSrc (getDir <$> files)
118− currentEnv <- getEnvironment
119− let pathDirs = maybe id (:) (lookup "GOPATH" currentEnv) $ fileDirs
120− searchPath = intercalate [searchPathSeparator] pathDirs
121− env = ("GOPATH", searchPath) : currentEnv
122− (_, _, _, p) <- createProcess (proc "go" [command, "Main"]){ env = Just env }
123− waitForProcess p
132+ project <- projectEnv
133+ case cmd of
134+ Build -> do
135+ callProcess "go" ["build", T.unpack project </> modPrefix' </> "Main"]
136+ Run moduleName -> do
137+ callProcess "go" ["run", T.unpack project </> modPrefix' </> fromMaybe "Main" moduleName]
124138 return ()
125− where
126− getDir :: FilePath -> [FilePath]
127− getDir file =
128− let filepath = takeDirectory file in
129− init $ splitDirectories filepath
130− isSrc :: [FilePath] -> Bool
131− isSrc components = (last components) == "src"
132139
133140generateCode :: [String] -> FilePath -> FilePath -> IO ()
134141generateCode opts baseOutpath jsonFile = do
135142 jsonModTime <- getModificationTime jsonFile
136143 let filepath = takeDirectory jsonFile
137144 dirparts = splitDirectories $ filepath
138− mname = (\c -> if c == '.' then '_' else c) <$> last dirparts
145+ mname = last dirparts
139146 basedir = joinPath $ init dirparts
140− mname' = moduleNameToIL' $ T.pack mname
141− possibleFileName = basedir </> outdir </> (T.unpack mname') </> implFileName mname'
147+ mname' = T.pack mname
148+ possibleFileName = basedir </> (T.unpack mname') </> implFileName mname'
142149 exists <- doesFileExist possibleFileName
143150 if exists
144151 then do
@@ -150,9 +157,10 @@ generateCode opts baseOutpath jsonFile = do
150157transpile :: [String] -> FilePath -> FilePath -> IO ()
151158transpile opts baseOutpath jsonFile = do
152159 jsonText <- T.decodeUtf8 <$> B.readFile jsonFile
160+ project <- projectEnv
153161 let module' = jsonToModule $ parseJson jsonText
154− ((_, foreigns, asts, implHeader, implFooter), _) <- runSupplyT 5 (moduleToIL module' Nothing)
155− let mn = moduleNameToIL $ moduleName module'
162+ ((_, foreigns, asts, implHeader, implFooter), _) <- runSupplyT 5 (moduleToIL module' project)
163+ let mn = moduleNameToIL' $ moduleName module'
156164 implementation = prettyPrintIL asts
157165 outpath = joinPath [baseOutpath, T.unpack mn]
158166 implPath = outpath </> implFileName mn
@@ -160,25 +168,53 @@ transpile opts baseOutpath jsonFile = do
160168 putStrLn implPath
161169 B.writeFile implPath $ T.encodeUtf8 (implHeader <> implementation <> implFooter)
162170
163−writeRuntimeFiles :: FilePath -> IO ()
164−writeRuntimeFiles baseOutpath = do
165− createDirectoryIfMissing True $ baseOutpath </> "purescript"
166− let runtimeSource = baseOutpath </> "purescript" </> "purescript.go"
167− runtimeExists <- doesFileExist runtimeSource
168− when (not runtimeExists) $ do
169− B.writeFile runtimeSource $(embedFile "runtime/purescript.go")
170−
171−outdir :: FilePath
172−outdir = "src"
171+writeSupportFiles :: FilePath -> IO ()
172+writeSupportFiles baseOutpath = do
173+ currentDir <- getCurrentDirectory
174+ let outputdir = T.pack $ baseOutpath \\ currentDir
175+ ffiOutpath = currentDir </> subdir
176+ createDirectoryIfMissing True baseOutpath
177+ createDirectoryIfMissing True ffiOutpath
178+ writeModuleFile outputdir currentDir $(embedFile "support/go.mod.working")
179+ writeModuleFile outputdir baseOutpath $(embedFile "support/go.mod.output")
180+ writeModuleFile outputdir ffiOutpath $(embedFile "support/go.mod.ffi-loader")
181+ writeLoaderFile ffiOutpath $(embedFile "support/ffi-loader.go")
182+ where
183+ writeModuleFile :: Text -> FilePath -> B.ByteString -> IO ()
184+ writeModuleFile outputdir path modText = do
185+ let goModSource = path </> "go.mod"
186+ goModSourceExists <- doesFileExist goModSource
187+ when (not goModSourceExists) $ do
188+ project <- projectEnv
189+ let project' = T.unpack project
190+ outputdir' = tail $ T.unpack outputdir
191+ modText' = T.replace "$PROJECT" project $ T.decodeUtf8 modText
192+ replaceLoader = project' </> ffiLoader' <> "=." </> subdir
193+ replaceOutput = project' </> modPrefix' <> "=." </> outputdir'
194+ B.writeFile goModSource $ T.encodeUtf8 modText'
195+ callProcess "go" ["mod", "edit", "-replace", replaceLoader]
196+ callProcess "go" ["mod", "edit", "-replace", replaceOutput]
197+ callProcess "go" ["clean", "-modcache"]
198+ writeLoaderFile :: FilePath -> B.ByteString -> IO ()
199+ writeLoaderFile ffiOutpath loaderText = do
200+ let loaderSource = ffiOutpath </> "ffi_loader.go"
201+ loaderSourceExists <- doesFileExist loaderSource
202+ when (not loaderSourceExists) $ do
203+ B.writeFile loaderSource loaderText
173204
174205implFileName :: Text -> FilePath
175−implFileName mn = T.unpack $ mn <> ".go"
206+implFileName mn = ((\c -> if c == '.' then '_' else c) <$> T.unpack mn) <> ".go"
207+
208+projectEnv :: IO Text
209+projectEnv = do
210+ T.pack . fromMaybe defaultProject <$> lookupEnv goproject
176211
177212help :: String
178213help = "Usage: psgo OPTIONS COREFN-FILES\n\
179214 \ PureScript to native (via go) compiler\n\n\
180215 \Available options:\n\
181216 \ --help Show this help text\n\n\
217+ \ --version Show the version number\n\n\
182218 \ --run Run the generated go code directly, without building an\n\
183219 \ executable\n\
184220 \ --no-build Generate go source files, but do not build an executable\n\
@@ -192,6 +228,21 @@ corefn = "corefn.json"
192228goSrc :: String
193229goSrc = ".go"
194230
231+goproject :: String
232+goproject = "GOPROJECT"
233+
234+defaultProject :: String
235+defaultProject = "project.localhost"
236+
237+modPrefix' :: String
238+modPrefix' = T.unpack modPrefix
239+
240+ffiLoader' :: String
241+ffiLoader' = T.unpack ffiLoader
242+
243+subdir :: String
244+subdir = T.unpack modLabel
245+
195246errorNoCorefnFiles :: IO ()
196247errorNoCorefnFiles = do
197248 ioError . userError $ "no compiled purescript '" <> corefn <> "' files found –\n" <>
default.nixnew file+23
@@ -0,0 +1,23 @@
1+let
2+ haskellNix = import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {};
3+ nixpkgsSrc = haskellNix.sources.nixpkgs-1903; # has hsc2hs-0.68.4
4+
5+ hsc2hsOverlay = self: super: {
6+ buildPackages = super.buildPackages // {
7+ hsc2hs = super.haskellPackages.hsc2hs;
8+ };
9+ };
10+
11+ nixpkgsArgs = haskellNix.nixpkgsArgs // {
12+ overlays = haskellNix.nixpkgsArgs.overlays ++ [ hsc2hsOverlay ];
13+ };
14+in
15+{ pkgs ? import nixpkgsSrc nixpkgsArgs
16+}:
17+
18+pkgs.haskell-nix.stackProject {
19+ src = pkgs.haskell-nix.haskellLib.cleanGit {
20+ name = "purescript-native";
21+ src = ./.;
22+ };
23+}
package.yaml+6−7
@@ -10,7 +10,6 @@ extra-source-files:
1010- README.md
1111- ChangeLog.md
1212- stack.yaml
13−- runtime/*
1413- support/*
1514
1615# Metadata used when publishing your package
@@ -25,7 +24,7 @@ description: Please see the README on Github at <https://github.com/andy
2524dependencies:
2625- base >= 4.7 && < 5
2726- bytestring >= 0.10.8.2 && < 0.11
28−- purescript -any
27+- purescript
2928- base-compat >=0.6.0
3029- protolude >=0.1.6
3130- text
@@ -37,11 +36,11 @@ dependencies:
3736- transformers >=0.3.0 && <0.6
3837- mtl >=2.1.0 && <2.3.0
3938- aeson >=1.0 && <1.5
40−- directory -any
41−- process -any
42−- file-embed -any
43−- filemanip -any
44−- gitrev -any
39+- directory
40+- process
41+- file-embed
42+- filemanip
43+- gitrev
4544
4645library:
4746 source-dirs: src
psgo.cabalnew file+145
@@ -0,0 +1,145 @@
1+cabal-version: 1.12
2+
3+-- This file has been generated from package.yaml by hpack version 0.35.0.
4+--
5+-- see: https://github.com/sol/hpack
6+
7+name: psgo
8+version: 0.1.0.0
9+description: Please see the README on Github at <https://github.com/andyarvanitis/purescript-native#readme>
10+homepage: https://github.com/andyarvanitis/purescript-native#readme
11+bug-reports: https://github.com/andyarvanitis/purescript-native/issues
12+author: Andy Arvanitis
13+maintainer: example@example.com
14+copyright: 2018 Andy Arvanitis
15+license: BSD3
16+license-file: LICENSE
17+build-type: Simple
18+extra-source-files:
19+ README.md
20+ ChangeLog.md
21+ stack.yaml
22+ support/ffi-loader.go
23+ support/go.mod.ffi-loader
24+ support/go.mod.output
25+ support/go.mod.working
26+
27+source-repository head
28+ type: git
29+ location: https://github.com/andyarvanitis/purescript-native
30+
31+library
32+ exposed-modules:
33+ CodeGen.IL
34+ CodeGen.IL.Common
35+ CodeGen.IL.Optimizer
36+ CodeGen.IL.Optimizer.Common
37+ CodeGen.IL.Optimizer.Inliner
38+ CodeGen.IL.Optimizer.MagicDo
39+ CodeGen.IL.Optimizer.TCO
40+ CodeGen.IL.Printer
41+ Tests
42+ other-modules:
43+ Paths_psgo
44+ hs-source-dirs:
45+ src
46+ default-extensions:
47+ ConstraintKinds
48+ DataKinds
49+ DeriveFunctor
50+ EmptyDataDecls
51+ FlexibleContexts
52+ KindSignatures
53+ LambdaCase
54+ MultiParamTypeClasses
55+ NoImplicitPrelude
56+ PatternGuards
57+ PatternSynonyms
58+ RankNTypes
59+ RecordWildCards
60+ OverloadedStrings
61+ ScopedTypeVariables
62+ TupleSections
63+ ViewPatterns
64+ build-depends:
65+ aeson >=1.0 && <1.5
66+ , base >=4.7 && <5
67+ , base-compat >=0.6.0
68+ , bytestring >=0.10.8.2 && <0.11
69+ , containers
70+ , directory
71+ , file-embed
72+ , filemanip
73+ , filepath
74+ , gitrev
75+ , monad-parallel >=0.7.2.2 && <0.8
76+ , mtl >=2.1.0 && <2.3.0
77+ , pattern-arrows >=0.0.2 && <0.1
78+ , process
79+ , protolude >=0.1.6
80+ , purescript
81+ , safe >=0.3.9 && <0.4
82+ , text
83+ , transformers >=0.3.0 && <0.6
84+ default-language: Haskell2010
85+
86+executable psgo
87+ main-is: Main.hs
88+ other-modules:
89+ Paths_psgo
90+ hs-source-dirs:
91+ app
92+ ghc-options: -threaded -rtsopts -with-rtsopts=-N
93+ build-depends:
94+ aeson >=1.0 && <1.5
95+ , base >=4.7 && <5
96+ , base-compat >=0.6.0
97+ , bytestring >=0.10.8.2 && <0.11
98+ , containers
99+ , directory
100+ , file-embed
101+ , filemanip
102+ , filepath
103+ , gitrev
104+ , monad-parallel >=0.7.2.2 && <0.8
105+ , mtl >=2.1.0 && <2.3.0
106+ , pattern-arrows >=0.0.2 && <0.1
107+ , process
108+ , protolude >=0.1.6
109+ , psgo
110+ , purescript
111+ , safe >=0.3.9 && <0.4
112+ , text
113+ , transformers >=0.3.0 && <0.6
114+ default-language: Haskell2010
115+
116+test-suite psgo-test
117+ type: exitcode-stdio-1.0
118+ main-is: Spec.hs
119+ other-modules:
120+ Paths_psgo
121+ hs-source-dirs:
122+ test
123+ ghc-options: -threaded -rtsopts -with-rtsopts=-N
124+ build-depends:
125+ aeson >=1.0 && <1.5
126+ , base >=4.7 && <5
127+ , base-compat >=0.6.0
128+ , bytestring >=0.10.8.2 && <0.11
129+ , containers
130+ , directory
131+ , file-embed
132+ , filemanip
133+ , filepath
134+ , gitrev
135+ , monad-parallel >=0.7.2.2 && <0.8
136+ , mtl >=2.1.0 && <2.3.0
137+ , pattern-arrows >=0.0.2 && <0.1
138+ , process
139+ , protolude >=0.1.6
140+ , psgo
141+ , purescript
142+ , safe >=0.3.9 && <0.4
143+ , text
144+ , transformers >=0.3.0 && <0.6
145+ default-language: Haskell2010
runtime/purescript.godeleted−70
@@ -1,70 +0,0 @@
1−package purescript
2−
3−//go:generate purs --version
4−
5−import "sync"
6−
7−type Any interface{}
8−
9−type Fn = func(Any) Any
10−type EffFn = func() Any
11−type Dict = map[string]Any
12−
13−type Once = sync.Once
14−
15−type Fn1 = func(Any) Any
16−type Fn2 = func(Any, Any) Any
17−type Fn3 = func(Any, Any, Any) Any
18−type Fn4 = func(Any, Any, Any, Any) Any
19−type Fn5 = func(Any, Any, Any, Any, Any) Any
20−type Fn6 = func(Any, Any, Any, Any, Any, Any) Any
21−type Fn7 = func(Any, Any, Any, Any, Any, Any, Any) Any
22−type Fn8 = func(Any, Any, Any, Any, Any, Any, Any, Any) Any
23−type Fn9 = func(Any, Any, Any, Any, Any, Any, Any, Any, Any) Any
24−type Fn10 = func(Any, Any, Any, Any, Any, Any, Any, Any, Any, Any) Any
25−
26−const Undefined = "undefined"
27−
28−var foreign = make(map[string]Dict)
29−
30−func Foreign(key string) Dict {
31− value, found := foreign[key]
32− if !found {
33− value = make(Dict)
34− foreign[key] = value
35− }
36− return value
37−}
38−
39−func Apply(f Any, args ...Any) Any {
40− result := f
41− for _, arg := range args {
42− fn, _ := result.(Fn)
43− result = fn(arg)
44− }
45− return result
46−}
47−
48−func Run(f Any, args ...Any) Any {
49− fn, _ := f.(EffFn)
50− return fn()
51−}
52−
53−func Get(dict map[string]Any, key string) Any {
54− value, ok := dict[key]
55− if !ok {
56− panic("Foreign value '" + key + "' not found")
57− }
58− return value
59−}
60−
61−func Contains(dict Any, key string) bool {
62− d, _ := dict.(map[string]Any)
63− _, found := d[key]
64− return found
65−}
66−
67−func Length(arr Any) Any {
68− a, _ := arr.([]Any)
69− return len(a)
70−}
src/CodeGen/IL.hs+12−50
@@ -48,21 +48,18 @@ moduleToIL
4848 -- . (Monad m, MonadReader Options m, MonadSupply m, MonadError MultipleErrors m)
4949 . (Monad m, MonadSupply m)
5050 => Module Ann
51− -> Maybe AST
51+ -> Text
5252 -> m (Text, [Text], [AST], Text, Text)
53−moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
53+moduleToIL (Module _ coms mn _ imps _ foreigns decls) project =
5454 do
55− let usedNames = concatMap getNames decls
56− let mnLookup = renameImports usedNames imps
57− let decls' = renameModules mnLookup decls
58− ilDecls <- mapM (bindToIL ModuleDecl) decls'
55+ ilDecls <- mapM (bindToIL ModuleDecl) decls
5956 optimized <- traverse (traverse (optimize modName')) ilDecls
6057 let optimized' = concat optimized
6158 values = annotValue <$> optimized'
6259 foreigns' = moduleIdentToIL <$> foreigns
6360 interface = interfaceSource modName values foreigns
6461 imports = nub . concat $ importToIL <$> optimized'
65− implHeader = implHeaderSource modName imports ""
62+ implHeader = implHeaderSource modName imports project
6663 implFooter = implFooterSource (runModuleName mn) foreigns
6764 return $ (interface, foreigns', optimized', implHeader, implFooter)
6865 where
@@ -80,54 +77,16 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
8077 getNames (NonRec _ ident _) = [ident]
8178 getNames (Rec vals) = map (snd . fst) vals
8279
83− -- | Creates alternative names for each module to ensure they don't collide
84− -- with declaration names.
85− renameImports :: [Ident] -> [(Ann, ModuleName)] -> M.Map ModuleName (Ann, ModuleName)
86− renameImports = go M.empty
87− where
88− go :: M.Map ModuleName (Ann, ModuleName) -> [Ident] -> [(Ann, ModuleName)] -> M.Map ModuleName (Ann, ModuleName)
89− go acc used ((ann, mn') : mns') =
90− let mni = Ident $ runModuleName mn'
91− in if mn' /= mn && mni `elem` used
92− then let newName = freshModuleName 1 mn' used
93− in go (M.insert mn' (ann, newName) acc) (Ident (runModuleName newName) : used) mns'
94− else go (M.insert mn' (ann, mn') acc) used mns'
95− go acc _ [] = acc
96−
97− freshModuleName :: Integer -> ModuleName -> [Ident] -> ModuleName
98− freshModuleName i mn'@(ModuleName pns) used =
99− let newName = ModuleName $ init pns ++ [ProperName $ runProperName (last pns) <> "_" <> T.pack (show i)]
100− in if Ident (runModuleName newName) `elem` used
101− then freshModuleName (i + 1) mn' used
102− else newName
103−
10480 -- | Generates IL code for a module import
10581 --
10682 importToIL :: AST -> [Text]
10783 importToIL = AST.everything (++) modRef
10884 where
10985 modRef (AST.Indexer _ (AST.Var _ _) (AST.Var _ mname))
110− | not $ T.null mname = [mname]
86+ | not $ T.null mname = [extract mname]
11187 modRef _ = []
112−
113− -- | Replaces the `ModuleName`s in the AST so that the generated code refers to
114− -- the collision-avoiding renamed module imports.
115− renameModules :: M.Map ModuleName (Ann, ModuleName) -> [Bind Ann] -> [Bind Ann]
116− renameModules mnLookup binds =
117− let (f, _, _) = everywhereOnValues id ilExpr goBinder
118− in map f binds
119− where
120− ilExpr :: Expr a -> Expr a
121− ilExpr (Var ann q) = Var ann (renameQual q)
122− ilExpr e = e
123− goBinder :: Binder a -> Binder a
124− goBinder (ConstructorBinder ann q1 q2 bs) = ConstructorBinder ann (renameQual q1) (renameQual q2) bs
125− goBinder b = b
126− renameQual :: Qualified a -> Qualified a
127− renameQual (Qualified (Just mn') a) =
128− let (_,mnSafe) = fromMaybe (internalError "Missing value in mnLookup") $ M.lookup mn' mnLookup
129− in Qualified (Just mnSafe) a
130− renameQual q = q
88+ extract :: Text -> Text
89+ extract = T.replace "_" "." . T.dropWhileEnd (=='_')
13190
13291 -- |
13392 -- Generate code in the simplified intermediate representation for a declaration
@@ -178,7 +137,7 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
178137 updates <- mapM (sndM valueToIL) ps
179138 obj' <- freshName'
180139 let objVar = AST.Var Nothing obj'
181− copy = AST.VariableIntroduction Nothing obj' (Just $ AST.App Nothing (AST.StringLiteral Nothing (mkString dictType)) [obj])
140+ copy = AST.VariableIntroduction Nothing obj' (Just $ copyDict obj)
182141 assign (k, v) = AST.Assignment Nothing (accessorString k objVar) v
183142 sts = copy : (assign <$> updates) ++ [AST.Return Nothing objVar]
184143 return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing sts)) []
@@ -242,7 +201,7 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
242201 -- | Generate code in the simplified intermediate representation for a reference to a
243202 -- variable that may have a qualified name.
244203 qualifiedToIL :: (a -> Ident) -> Qualified a -> AST
245− qualifiedToIL f (Qualified (Just (ModuleName [ProperName mn'])) a) | mn' == C.prim = AST.Var Nothing . moduleIdentToIL $ f a
204+ qualifiedToIL f (Qualified (Just (ModuleName mn')) a) | mn' == C.prim = AST.Var Nothing . moduleIdentToIL $ f a
246205 qualifiedToIL f (Qualified (Just mn') a) | mn /= mn' = AST.Indexer Nothing (AST.Var Nothing . moduleIdentToIL $ f a) (AST.Var Nothing (moduleNameToIL mn'))
247206 qualifiedToIL f (Qualified _ a) = AST.Indexer Nothing (AST.Var Nothing . moduleIdentToIL $ f a) (AST.Var Nothing "")
248207
@@ -357,3 +316,6 @@ emptyAnn = (SourceSpan "" (SourcePos 0 0) (SourcePos 0 0), [], Nothing, Nothing)
357316arrayLength :: AST -> AST
358317arrayLength a = AST.App Nothing (AST.Var Nothing arrayLengthFn) [a]
359318-- arrayLength a = AST.Var Nothing (arrayLengthFn <> "(" <> prettyPrintIL1 a <> ")")
319+
320+copyDict :: AST -> AST
321+copyDict a = AST.App Nothing (AST.Var Nothing copyDictFn) [a]
src/CodeGen/IL/Common.hs+37−24
@@ -16,13 +16,12 @@ import Language.PureScript.Names
1616import qualified Language.PureScript.Constants as C
1717
1818moduleNameToIL :: ModuleName -> Text
19−moduleNameToIL (ModuleName pns) =
20− let name = T.intercalate "_" (runProperName `map` pns)
21− in moduleNameToIL' name
19+moduleNameToIL (ModuleName mn) =
20+ let name = T.replace "." "_" mn
21+ in if nameIsILBuiltIn name then (name <> moduleRenamerMarker) else name
2222
23−moduleNameToIL' :: Text -> Text
24−moduleNameToIL' name =
25− if moduleIsReserved name then (name <> "_") else name
23+moduleNameToIL' :: ModuleName -> Text
24+moduleNameToIL' (ModuleName mn) = T.replace "_" "." mn
2625
2726-- | Convert an 'Ident' into a valid IL identifier:
2827--
@@ -51,16 +50,17 @@ unusedName = "_"
5150
5251properToIL :: Text -> Text
5352properToIL name
54− | nameIsILReserved name || nameIsILBuiltIn name || prefixIsReserved name = name <> "ˉ"
53+ | nameIsILReserved name = "ˉ" <> name
5554 | otherwise = T.concatMap identCharToText name
5655
5756-- | Attempts to find a human-readable name for a symbol, if none has been specified returns the
5857-- ordinal value.
5958identCharToText :: Char -> Text
59+identCharToText 'ṩ' = "_ṩ"
6060identCharToText c | isAlphaNum c = T.singleton c
6161identCharToText '_' = "_"
62−identCharToText '\'' = "ʹ"
63−identCharToText c = "ᵤ" <> T.pack (show (ord c))
62+identCharToText '\'' = "ꞌ" -- lowercase saltillo
63+identCharToText c = error (show (ord c)) -- TODO: should never occur now(?)
6464
6565moduleProperToIL :: Text -> Text
6666moduleProperToIL = T.concatMap identCharToText
@@ -74,7 +74,21 @@ nameIsILReserved name =
7474nameIsILBuiltIn :: Text -> Bool
7575nameIsILBuiltIn name =
7676 name `elem`
77− [
77+ [ "Any"
78+ , "Apply"
79+ , "Contains"
80+ , "CopyDict"
81+ , "Dict"
82+ , "EffFn"
83+ , "Fn"
84+ , "Foreign"
85+ , "Get"
86+ , "Index"
87+ , "Is"
88+ , "Length"
89+ , "Once"
90+ , "Run"
91+ , "Undefined"
7892 ]
7993
8094ilAnyReserved :: [Text]
@@ -113,16 +127,6 @@ ilKeywords =
113127 , "var"
114128 ]
115129
116−moduleIsReserved :: Text -> Bool
117−moduleIsReserved "Main" = False
118−moduleIsReserved name = not $ T.isInfixOf "_" name
119−
120−prefixIsReserved :: Text -> Bool
121−prefixIsReserved name =
122− any (`T.isPrefixOf` name)
123− [
124− ]
125−
126130ilLiterals :: [Text]
127131ilLiterals =
128132 [ "init"
@@ -148,7 +152,7 @@ ilLiterals =
148152 ]
149153
150154withPrefix :: Text -> Text
151−withPrefix s = "PS__" <> s
155+withPrefix s = "Ꞌ" <> s -- uppercase saltillo
152156
153157anyType :: Text
154158anyType = "Any"
@@ -159,6 +163,9 @@ dictType = "Dict"
159163arrayType :: Text
160164arrayType = "[]" <> anyType
161165
166+uncurriedFnType :: Int -> Text
167+uncurriedFnType i = "Fn" <> (T.pack . show $ i)
168+
162169int :: Text
163170int = "int"
164171
@@ -174,10 +181,16 @@ string = "string"
174181arrayLengthFn :: Text
175182arrayLengthFn = "Length"
176183
177−tcoLoop :: Text
178−tcoLoop = "ᵗᶜloop"
184+indexFn :: Text
185+indexFn = "Index"
186+
187+copyDictFn :: Text
188+copyDictFn = "CopyDict"
179189
180190freshName' :: MonadSupply m => m Text
181191freshName' = do
182192 name <- freshName
183− return $ T.replace "$" "ᵗ" name
193+ return $ T.replace "$" "ṩ" name
194+
195+moduleRenamerMarker :: Text
196+moduleRenamerMarker = "__"
src/CodeGen/IL/Optimizer.hs+14−1
@@ -22,10 +22,11 @@ import qualified Language.PureScript.Constants as C
2222optimize :: MonadSupply m => AST -> AST -> m AST
2323optimize mn il = do
2424 il' <- untilFixedPoint (inlineFnComposition . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp) il
25− untilFixedPoint (return . ignoreUnusedResults . inlineCommonValues . inlineCommonOperators . tidyUp) . tco mn . inlineST
25+ il'' <- untilFixedPoint (return . ignoreUnusedResults . inlineCommonValues . inlineCommonOperators . tidyUp) . tco mn . inlineST
2626 =<< untilFixedPoint (return . magicDoST)
2727 =<< untilFixedPoint (return . magicDoEff)
2828 =<< untilFixedPoint (return . magicDoEffect) il'
29+ untilFixedPoint (return . collapseVarDeclarations) il''
2930 where
3031 tidyUp :: AST -> AST
3132 tidyUp = applyAll
@@ -54,6 +55,18 @@ collapseIfChecks = everywhere collapse where
5455 | prop == "otherwise" && val == "Data_Boolean" = exprs
5556 collapse exprs = exprs
5657
58+collapseVarDeclarations :: AST -> AST
59+collapseVarDeclarations = everywhere collapse where
60+ collapse :: AST -> AST
61+ collapse (Block ss sts) = Block ss (go Nothing sts)
62+ collapse s = s
63+
64+ go :: Maybe Text -> [AST] -> [AST]
65+ go _ [] = []
66+ go Nothing (decl@(VariableIntroduction _ var (Just _)) : sts) = decl : go (Just var) (go Nothing sts)
67+ go (Just var') ((VariableIntroduction ss var (Just val)) : sts)
68+ | var == var' = (Assignment ss (Var Nothing var) val) : go (Just var') sts
69+ go var' (s:sts) = s : go var' sts
5770
5871ignoreUnusedResults :: AST -> AST
5972ignoreUnusedResults = everywhere $ removeFromBlock go
src/CodeGen/IL/Optimizer/Common.hs+1−3
@@ -8,11 +8,9 @@ import Data.Text (Text)
88import Language.PureScript.CoreImp.AST
99import Language.PureScript.PSString (PSString, decodeString)
1010
11−import CodeGen.IL.Common (moduleNameToIL')
12−
1311isDict :: (Text, PSString) -> AST -> Bool
1412isDict (moduleName, dictName) (Indexer _ (Var _ x) (Var _ y)) =
15− Just x == decodeString dictName && y == moduleNameToIL' moduleName
13+ Just x == decodeString dictName && y == moduleName
1614isDict _ _ = False
1715
1816isDict' :: [(Text, PSString)] -> AST -> Bool
src/CodeGen/IL/Optimizer/Inliner.hs+14−5
@@ -117,12 +117,11 @@ inlineCommonOperators = everywhereTopDown $ applyAll $
117117 , binary' C.dataIntBits C.xor BitwiseXor
118118 , binary' C.dataIntBits C.shl ShiftLeft
119119 , binary' C.dataIntBits C.shr ShiftRight
120− , binary' C.dataIntBits C.zshr ZeroFillShiftRight
121120 , unary' C.dataIntBits C.complement BitwiseNot
122121
123122 , inlineNonClassFunction (isModFn (C.dataFunction, C.apply)) $ \f x -> App Nothing f [x]
124123 , inlineNonClassFunction (isModFn (C.dataFunction, C.applyFlipped)) $ \x f -> App Nothing f [x]
125− , inlineNonClassFunction (isModFnWithDict (C.dataArray, C.unsafeIndex)) $ flip (Indexer Nothing)
124+ , inlineUnsafeIndex (isModFnWithDict (C.dataArray, C.unsafeIndex)) $ flip (Indexer Nothing)
126125 ] ++
127126 [ fn | i <- [0..10], fn <- [ mkFn i, runFn i ] ] ++
128127 [ fn | i <- [0..10], fn <- [ mkEffFn C.controlMonadEffUncurried C.mkEffFn i, runEffFn C.controlMonadEffUncurried C.runEffFn i ] ] ++
@@ -186,13 +185,14 @@ inlineCommonOperators = everywhereTopDown $ applyAll $
186185 runFn :: Int -> AST -> AST
187186 runFn = runFn' C.dataFunctionUncurried C.runFn (\ss f args ->
188187 let len = length args
189− typ = mkString $ "Fn" <> (T.pack . show $ len) in
188+ typ = mkString $ uncurriedFnType len in
190189 if len > 0
191190 then App ss (App Nothing (StringLiteral Nothing typ) [f]) args
192191 else App ss f args)
193192 runEffFn :: Text -> Text -> Int -> AST -> AST
194193 runEffFn modName fnName = runFn' modName fnName $ \ss fn acc ->
195− Function ss Nothing [] (Block ss [Return ss (App ss fn acc)])
194+ let typ = mkString . uncurriedFnType $ length acc in
195+ Function ss Nothing [] (Block ss [Return ss (App ss (App Nothing (StringLiteral Nothing typ) [fn]) acc)])
196196
197197 runFn' :: Text -> Text -> (Maybe SourceSpan -> AST -> [AST] -> AST) -> Int -> AST -> AST
198198 runFn' modName runFnName res n = convert where
@@ -211,6 +211,15 @@ inlineCommonOperators = everywhereTopDown $ applyAll $
211211 convert (App _ (App _ op' [x]) [y]) | p op' = f x y
212212 convert other = other
213213
214+ inlineUnsafeIndex :: (AST -> Bool) -> (AST -> AST -> AST) -> AST -> AST
215+ inlineUnsafeIndex p _ = convert where
216+ convert :: AST -> AST
217+ convert (App _ (App ss op' [x]) [y@NumericLiteral{}])
218+ | p op' = Indexer ss y x
219+ convert (App _ (App ss op' [x]) [y])
220+ | p op' = App ss (Var Nothing indexFn) [x, y]
221+ convert other = other
222+
214223 isModFn :: (Text, PSString) -> AST -> Bool
215224 isModFn (m, op) (Indexer _ (Var _ op') (Var _ m')) =
216225 m == m' && decodeString op == Just op'
@@ -265,7 +274,7 @@ inlineFnComposition = everywhereTopDownM convert where
265274inlineUnsafeCoerce :: AST -> AST
266275inlineUnsafeCoerce = everywhereTopDown convert where
267276 convert (App _ (Indexer _ (Var _ unsafeCoerceFn) (Var _ unsafeCoerce)) [ comp ])
268− | unsafeCoerceFn == C.unsafeCoerceFn && unsafeCoerce == moduleNameToIL' C.unsafeCoerce
277+ | unsafeCoerceFn == C.unsafeCoerceFn && unsafeCoerce == C.unsafeCoerce
269278 = comp
270279 convert other = other
271280
src/CodeGen/IL/Optimizer/MagicDo.hs+1−1
@@ -13,7 +13,7 @@ import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict)
1313import Language.PureScript.PSString (PSString, decodeString, mkString)
1414import qualified Language.PureScript.Constants as C
1515
16−import CodeGen.IL.Common (moduleNameToIL', undefinedName, unusedName)
16+import CodeGen.IL.Common (undefinedName, unusedName)
1717import CodeGen.IL.Optimizer.Common
1818
1919-- | Inline type class dictionaries for >>= and return for the Eff monad
src/CodeGen/IL/Optimizer/TCO.hs+8−5
@@ -1,4 +1,4 @@
1−module CodeGen.IL.Optimizer.TCO (tco) where
1+module CodeGen.IL.Optimizer.TCO (tco, tcoLoop) where
22
33import Prelude.Compat
44
@@ -11,19 +11,22 @@ import CodeGen.IL.Common
1111
1212import qualified Language.PureScript.Constants as C
1313
14+tcoLoop :: Text
15+tcoLoop = "ṩtco_loop"
16+
1417tco :: AST -> AST -> AST
1518tco mn = everywhere convert where
1619 tcoVar :: Text -> Text
17− tcoVar arg = "ᵗ" <> arg
20+ tcoVar arg = "ṩtco_var_" <> arg
1821
1922 copyVar :: Text -> Text
20− copyVar arg = "ᶜ" <> arg
23+ copyVar arg = "ṩcopy_" <> arg
2124
2225 tcoDone :: Text
23− tcoDone = "ᵗᶜdone"
26+ tcoDone = "ṩtco_done"
2427
2528 tcoResult :: Text
26− tcoResult = "ᵗᶜresult"
29+ tcoResult = "ṩtco_result"
2730
2831 convert :: AST -> AST
2932 convert fn@(Function ss (Just name) args _)
src/CodeGen/IL/Printer.hs+41−13
@@ -7,6 +7,9 @@ module CodeGen.IL.Printer
77 , implHeaderSource
88 , implFooterSource
99 , isLiteral
10+ , modLabel
11+ , modPrefix
12+ , ffiLoader
1013 ) where
1114
1215import Prelude.Compat
@@ -30,7 +33,9 @@ import Language.PureScript.Crash
3033import Language.PureScript.Names (Ident, runIdent)
3134import Language.PureScript.Pretty.Common
3235import Language.PureScript.PSString (PSString, decodeString, mkString)
36+
3337import CodeGen.IL.Common
38+import CodeGen.IL.Optimizer.TCO (tcoLoop)
3439
3540import qualified Language.PureScript.Constants as C
3641
@@ -101,6 +106,12 @@ literals = mkPattern' match'
101106 , prettyPrintIL' arg
102107 , return $ emit ")"
103108 ]
109+ match (App _ (Var _ fn) args) | fn == indexFn = mconcat <$> sequence
110+ [ return $ emit fn
111+ , return $ emit "("
112+ , intercalate (emit ", ") <$> forM args prettyPrintIL'
113+ , return $ emit ")"
114+ ]
104115 match (App _ (Var _ fn) args) | fn == tcoLoop = mconcat <$> sequence
105116 [ return $ emit fn
106117 , return $ emit "("
@@ -223,7 +234,7 @@ literals = mkPattern' match'
223234 , return $ emit "]"
224235 ]
225236 match (InstanceOf _ val ty) = mconcat <$> sequence
226− [ return $ emit "Contains("
237+ [ return $ emit "Is("
227238 , prettyPrintIL' val
228239 , return $ emit ", "
229240 , prettyPrintIL' ty
@@ -396,8 +407,7 @@ prettyPrintIL' = A.runKleisli $ runPattern matchValue
396407 , [ binary Add "+"
397408 , binary Subtract "-" ]
398409 , [ binary ShiftLeft "<<"
399− , binary ShiftRight ">>"
400− , binary ZeroFillShiftRight ">>>" ]
410+ , binary ShiftRight ">>" ]
401411 , [ binary LessThan "<"
402412 , binary LessThanOrEqualTo "<="
403413 , binary GreaterThan ">"
@@ -436,19 +446,25 @@ interfaceSource :: Text -> [(Text,Bool)] -> [Ident] -> Text
436446interfaceSource _ _ _ = ""
437447
438448implHeaderSource :: Text -> [Text] -> Text -> Text
439−implHeaderSource mn imports _ =
449+implHeaderSource mn imports otherPrefix =
440450 "// Code generated by psgo. DO NOT EDIT.\n\n" <>
441− "package " <> (if mn == "Main" then "main" else mn) <> "\n\n" <>
442− "import . \"purescript\"\n" <>
443− (if mn == "Main"
444− then "import _ \"ffi\"\n"
451+ "package " <> (if mn == "Main" || mn == "Test_Main" then "main" else mn) <> "\n\n" <>
452+ "import . \"" <> runtime <> "\"\n" <>
453+ (if mn == "Main" || mn == "Test_Main"
454+ then "import _ \"" <> otherPrefix <> "/" <> ffiLoader <> "\"\n"
445455 else "\n") <>
446456 "import (\n" <>
447457 (T.concat $ formatImport <$> imports) <> ")\n\n" <>
448458 "type _ = Any\n\n"
449459 where
450460 formatImport :: Text -> Text
451− formatImport s = " \"" <> s <> "\"\n"
461+ formatImport s =
462+ "\t\"" <> otherPrefix <> "/" <> modPrefix <> "/" <> moduleRenamer s <> "\"\n"
463+ moduleRenamer :: Text -> Text
464+ moduleRenamer s
465+ | (s', sfx) <- T.breakOn moduleRenamerMarker s,
466+ not $ T.null sfx = s'
467+ moduleRenamer s = s
452468
453469implFooterSource :: Text -> [Ident] -> Text
454470implFooterSource mn foreigns =
@@ -471,12 +487,12 @@ implFooterSource mn foreigns =
471487 " })\n" <>
472488 " return " <> valueName name <> "\n" <>
473489 "}\n\n") <$> foreigns))) <>
474− if mn == "Main" then mainSource else "\n"
490+ if mn == "Main" || mn == "Test.Main" then mainSource else "\n"
475491 where
476492 mainSource :: Text
477493 mainSource = "\
478494 \func main() {\n\
479− \ Run(PS__main())\n\
495+ \ Run(Ꞌmain())\n\
480496 \}\n\n\
481497 \"
482498
@@ -492,8 +508,20 @@ foreignMod = "Foreign"
492508foreignDict :: Text
493509foreignDict = "foreign"
494510
511+modLabel :: Text
512+modLabel = "purescript-native"
513+
514+modPrefix :: Text
515+modPrefix = modLabel <> "/output"
516+
517+runtime :: Text
518+runtime = "github.com/" <> modLabel <> "/go-runtime"
519+
520+ffiLoader :: Text
521+ffiLoader = modLabel <> "/ffi-loader"
522+
495523initName :: Text -> Text
496−initName s = "ₒ" <> s
524+initName s = "ᛌ" <> s <> "_once"
497525
498526valueName :: Text -> Text
499−valueName s = "ₐ" <> s
527+valueName s = "ᛧ" <> s
src/Tests.hs+6−5
@@ -31,14 +31,13 @@ runTests = do
3131 let srcDir = outputDir </> "src"
3232 createDirectory srcDir
3333
34− callProcess "git" ["clone", "--branch", "v0.13.0", "--depth", "1", "https://github.com/purescript/purescript.git"]
34+ callProcess "git" ["clone", "--branch", "v0.13.8", "--depth", "1", "https://github.com/purescript/purescript.git"]
3535 let passingDir = baseDir </> "purescript" </> "tests" </> "purs" </> "passing"
3636 passingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents passingDir
3737
3838 setCurrentDirectory outputDir
3939
4040 fetchPackages
41− callProcess "git" ["clone", "--depth", "1", "https://github.com/andyarvanitis/purescript-native-go-ffi.git"]
4241
4342 let tests = filter (`notElem` skipped) passingTestCases
4443
@@ -63,10 +62,9 @@ runTests = do
6362
6463 callProcess "rm" ["-rf", "output"]
6564 callProcess "rm" ["-rf", "Main"]
66− callProcess "spago" ["build", "--", "--codegen", "corefn"]
67− callProcess "psgo" []
65+ callProcess "spago" ["build"]
6866 --
69− -- Run C++ files
67+ -- Run executable file
7068 --
7169 outputFile <- openFile (tmp </> logpath </> logfile) AppendMode
7270 hPutStrLn outputFile ("\n" ++ inputFile ++ ":")
@@ -115,6 +113,9 @@ fetchPackages :: IO ()
115113-------------------------------------------------------------------------------
116114fetchPackages = do
117115 callProcess "spago" ["init"]
116+ callCommand "sed '$s/\\(}\\)/, backend = \"psgo\"\\1/' < spago.dhall | dhall format > spago.tmp"
117+ callCommand "mv spago.tmp spago.dhall"
118+
118119 mapM (callProcess "spago" . (\p -> ["install", p])) packages
119120 return ()
120121
stack.nix+18−3
@@ -1,8 +1,23 @@
1−{ pkgs ? import <nixpkgs> {}, ghc ? pkgs.ghc }:
1+#
2+# You may need to manually run `unset STACK_IN_NIX_SHELL`
3+# (doesn't work from a `shellHook`; see: https://github.com/commercialhaskell/stack/issues/5000
4+#
5+#
6+# You may need to set LC_ALL when building, e.g.
7+# LC_ALL=en_US.iso88591 stack --nix build; see e.g. https://github.com/koalaman/shellcheck/issues/324
8+#
9+#
10+{ pkgs ?
11+ import (builtins.fetchTarball {
12+ name = "nixos-release-19.03";
13+ url = https://github.com/nixos/nixpkgs/archive/34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59.tar.gz;
14+ sha256 = "11z6ajj108fy2q5g8y4higlcaqncrbjm3dnv17pvif6avagw4mcb";
15+ }) {}
16+}:
217
318pkgs.haskell.lib.buildStackProject {
419 name = "default-stack-shell";
5− inherit ghc;
6− buildInputs = with pkgs; [git git-lfs gmp zlib];
20+ ghc = pkgs.haskell.compiler.ghc865;
21+ buildInputs = with pkgs; [git git-lfs gmp ncurses zlib];
722 LANG = "en_US.UTF-8";
823}
stack.yaml+15−65
@@ -1,74 +1,24 @@
1−# This file was automatically generated by 'stack init'
2−#
3−# Some commonly used options have been documented as comments in this file.
4−# For advanced use and comprehensive documentation of the format, please see:
5−# https://docs.haskellstack.org/en/stable/yaml_configuration/
1+resolver: lts-13.26
62
7−# Resolver to choose a 'specific' stackage snapshot or a compiler version.
8−# A snapshot resolver dictates the compiler version and the set of packages
9−# to be used for project dependencies. For example:
10−#
11−# resolver: lts-3.5
12−# resolver: nightly-2015-09-21
13−# resolver: ghc-7.10.2
14−# resolver: ghcjs-0.1.0_ghc-7.10.2
15−# resolver:
16−# name: custom-snapshot
17−# location: "./custom-snapshot.yaml"
18−resolver: lts-13.12
19−
20−# User packages to be built.
21−# Various formats can be used as shown in the example below.
22−#
23−# packages:
24−# - some-directory
25−# - https://example.com/foo/bar/baz-0.0.2.tar.gz
26−# - location:
27−# git: https://github.com/commercialhaskell/stack.git
28−# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
29−# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
30−# extra-dep: true
31−# subdirs:
32−# - auto-update
33−# - wai
34−#
35−# A package marked 'extra-dep: true' will only be built if demanded by a
36−# non-dependency (i.e. a user package), and its test suites and benchmarks
37−# will not be run. This is useful for tweaking upstream packages.
383packages:
394- .
40−# Dependency packages to be pulled from upstream that are not in the resolver
41−# (e.g., acme-missiles-0.3)
5+
426extra-deps:
43−- git: https://github.com/purescript/purescript
44− commit: 738aaa2f21097c7f18aa8100b16ae52316addb04
7+- git: https://github.com/purescript/purescript
8+ commit: 9cad73ed8ea7df3011032ddbd2f3de5a0c08629c
9+- happy-1.19.9
10+- language-javascript-0.7.0.0
4511- network-3.0.1.1
46−- pipes-http-1.0.5
47−
48−# Override default flag values for local packages and extra-deps
49−# flags: {}
50−
51−# Extra package databases containing global packages
52−# extra-package-dbs: []
53−
54−# Control whether we use the GHC we find on the path
55−# system-ghc: true
56−#
57−# Require a specific version of stack, using version ranges
58−# require-stack-version: -any # Default
59−# require-stack-version: ">=1.6"
60−#
61−# Override the architecture used by stack, especially useful on Windows
62−# arch: i386
63−# arch: x86_64
64−#
65−# Extra directories used by stack for building
66−# extra-include-dirs: [/path/to/dir]
67−# extra-lib-dirs: [/path/to/dir]
68−#
69−# Allow a newer minor version of GHC than the snapshot specifies
70−# compiler-check: newer-minor
12+- these-1.0.1
13+- semialign-1
7114
7215nix:
16+ enable: false
7317 shell-file: stack.nix
7418
19+flags:
20+ aeson-pretty:
21+ lib-only: true
22+ these:
23+ assoc: false
24+ quickcheck: false
support/Makefiledeleted−114
@@ -1,114 +0,0 @@
1−# PureScript to native binary (via Go) Makefile
2−#
3−# Run 'make' or 'make release' to build an optimized release build
4−# Run 'make debug' to build a non-optimized build suitable for debugging
5−#
6−# You can also perform a parallel build with 'make -jN', where N is the
7−# number of cores to use.
8−#
9−# PURS, SRC, OUTPUT, and BIN can all be overridden with the
10−# command itself. For example: 'make BIN=myutil'
11−#
12−# Flags can be added to either the codegen or native build phases.
13−# For example: 'make PURSFLAGS=--codegen,js CXXFLAGS=-DDEBUG LDFLAGS=lgmp'
14−#
15−# You can also edit the generated version of this file directly.
16−#
17−PURS := purs
18−PSC_PACKAGE := psc-package
19−PSGO := psgo
20−SRC := src
21−OUTPUT := output
22−CC_SRC := $(OUTPUT)/src
23−FFI_SRC := purescript-go-ffi
24−BIN := Main
25−
26−override PURSFLAGS += compile --codegen corefn
27−override CXXFLAGS += --std=c++11
28−
29−DEBUG := "-DDEBUG -g"
30−RELEASE := "-DNDEBUG -O3"
31−
32−GOPATH := $(GOPATH):$(CURDIR)/$(OUTPUT):$(CURDIR)/$(FFI_SRC)
33−
34−INCLUDES := -I $(CC_SRC)
35−BIN_DIR := $(OUTPUT)/bin
36−
37−PACKAGE_SOURCES = $(subst \,/,$(shell $(PSC_PACKAGE) sources))
38−PURESCRIPT_PKGS := $(firstword $(subst /, ,$(PACKAGE_SOURCES)))
39−
40−## Not all environments support globstar (** dir pattern)
41−rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
42−
43−unspecified:
44− @echo
45− @echo "Please specify the type of build you want: 'debug' or 'release'"
46− @echo
47− @echo " debug => not optimized (your app will run slower), good for"
48− @echo " running in a source-level debugger, more (descriptive)"
49− @echo " runtime exceptions"
50− @echo
51− @echo " release => optimized (your app will run faster), less helpful when debugging"
52− @echo
53−
54−debug: codegen
55− go build -o $(BIN_DIR)/$(BIN) $(BIN)
56−
57−release: codegen
58− go build -o $(BIN_DIR)/$(BIN) $(BIN)
59−
60−.PHONY: corefn
61−corefn: PURESCRIPT_PKG_SRCS=$(foreach d,$(PACKAGE_SOURCES),$(call rwildcard,$(firstword $(subst *, ,$(d))),*.purs))
62−corefn: PURESCRIPT_SRCS=$(call rwildcard,$(SRC)/,*.purs)
63−corefn: $(PURESCRIPT_PKGS)
64− @$(PURS) $(PURSFLAGS) --output $(OUTPUT) $(PURESCRIPT_PKG_SRCS) $(PURESCRIPT_SRCS)
65−
66−.PHONY: codegen
67−codegen: COREFN_SRCS=$(call rwildcard,$(OUTPUT)/,corefn.json)
68−codegen: corefn
69− @$(PSGO) $(PSGOFLAGS) $(COREFN_SRCS)
70−
71−$(PURESCRIPT_PKGS):
72− @echo "Getting packages using" $(PSC_PACKAGE) "..."
73− @$(PSC_PACKAGE) update
74−
75−SRCS := $(call rwildcard,$(CC_SRC)/,*.cpp)
76−SRCS += $(call rwildcard,$(FFI_SRC)/,*.cpp)
77−SRCS += $(call rwildcard,$(FFI_SRC)/,*.cc)
78−SRCS += $(call rwildcard,$(FFI_SRC)/,*.c)
79−
80−OBJS1 = $(SRCS:.cpp=.o)
81−OBJS2 = $(OBJS1:.cc=.o)
82−OBJS = $(OBJS2:.c=.o)
83−DEPS = $(OBJS:.o=.d)
84−
85−$(BIN): $(OBJS)
86− @echo "Linking" $(BIN_DIR)/$(BIN)
87− @mkdir -p $(BIN_DIR)
88− @$(CXX) $^ -o $(BIN_DIR)/$@ $(LDFLAGS)
89−
90−-include $(DEPS)
91−
92−%.o: %.cpp
93− @echo "Creating" $@ "(C++)"
94− @$(CXX) $(CXXFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
95−
96−%.o: %.cc
97− @echo "Creating" $@ "(C++)"
98− @$(CXX) $(CXXFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
99−
100−%.o: %.c
101− @echo "Creating" $@ "(C)"
102− @$(CXX) $(CFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
103−
104−.PHONY: all
105−all: release
106−
107−.PHONY: clean
108−clean:
109− @-rm -f $(OBJS) $(DEPS)
110− @-rm -rf $(OUTPUT)
111−
112−.PHONY: run
113−run:
114− @$(BIN_DIR)/$(BIN) $(ARGS)
support/ffi-loader.gonew file+30
@@ -0,0 +1,30 @@
1+package ffi_loader
2+
3+// Load PureScript standard library FFI packages. Comment out the ones you don't need for
4+// faster/smaller builds.
5+
6+import (
7+ _ "github.com/purescript-native/go-ffi/purescript-arrays"
8+ _ "github.com/purescript-native/go-ffi/purescript-assert"
9+ _ "github.com/purescript-native/go-ffi/purescript-console"
10+ _ "github.com/purescript-native/go-ffi/purescript-effect"
11+ _ "github.com/purescript-native/go-ffi/purescript-enums"
12+ _ "github.com/purescript-native/go-ffi/purescript-exceptions"
13+ _ "github.com/purescript-native/go-ffi/purescript-foldable-traversable"
14+ _ "github.com/purescript-native/go-ffi/purescript-functions"
15+ _ "github.com/purescript-native/go-ffi/purescript-globals"
16+ _ "github.com/purescript-native/go-ffi/purescript-integers"
17+ _ "github.com/purescript-native/go-ffi/purescript-lazy"
18+ _ "github.com/purescript-native/go-ffi/purescript-math"
19+ _ "github.com/purescript-native/go-ffi/purescript-partial"
20+ _ "github.com/purescript-native/go-ffi/purescript-prelude"
21+ _ "github.com/purescript-native/go-ffi/purescript-random"
22+ _ "github.com/purescript-native/go-ffi/purescript-refs"
23+ _ "github.com/purescript-native/go-ffi/purescript-st"
24+ _ "github.com/purescript-native/go-ffi/purescript-strings"
25+ _ "github.com/purescript-native/go-ffi/purescript-unfoldable"
26+)
27+
28+// Add your own FFI packages here.
29+
30+import ()
support/go.mod.ffi-loadernew file+2
@@ -0,0 +1,2 @@
1+module $PROJECT/purescript-native/ffi-loader
2+
support/go.mod.outputnew file+2
@@ -0,0 +1,2 @@
1+module $PROJECT/purescript-native/output
2+
support/go.mod.workingnew file+1
@@ -0,0 +1 @@
1+module $PROJECT/example