git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
.gitignore+2−1
since splitsnapshots
unifiedsplit
@@ -12,11 +12,12 @@ cabal.sandbox.config
12.test_modules/
12.test_modules/
13bower_components/
13bower_components/
14node_modules
14node_modules
15−pscpp.cabal
16tmp/
15tmp/
17.stack-work/
16.stack-work/
17+stack.yaml.lock
18output
18output
19examples/docs/docs/
19examples/docs/docs/
20core-tests/full-core-docs.md
20core-tests/full-core-docs.md
21.psc-ide-port
21.psc-ide-port
22.vscode/
22.vscode/
23+result
README-go.mdnew file+48
@@ -0,0 +1,48 @@
1+
2+### psgo
3+
4+#### Performance
5+
6+* No runtime system beyond some support types and functions
7+* Uses PureScript's normal tail call optimization techniques for generated Go code
8+
9+#### Differences from PureScript:
10+
11+* Foreign imports/exports are Go instead of JavaScript – see [standard library foreign implementations](https://github.com/andyarvanitis/purescript-native-go-ffi)
12+* No Go-specific REPL
13+
14+#### Other notes:
15+
16+* The provided `Any` type is just an alias for `interface{}`
17+* PureScript types are represented in Go with:
18+
19+ | PureScript | Go |
20+ |------------|----|
21+ |`Array` | `[]Any`|
22+ |`String` | `string`|
23+ |`Number` | `float64`|
24+ |`Int` | `int`|
25+ |`Char` | `string` (single UTF-8 entity)|
26+ |`Boolean` | `bool`|
27+ |Records | `map[string]Any`|
28+
29+#### Future ideas:
30+
31+* Nice facilities (modules) for concurrency/parallelism, using goroutines
32+
33+
34+#### Getting Started
35+This assumes you are running macOS (OS X) or a Unix-like system (Linux, *BSD, etc.).
36+
37+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.
38+
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).
40+
41+1. Add the `backend = "psgo"` option to your `spago.dhall` file. That way `spago` will use the psgo backend when compiling.
42+
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`.
45+
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/`.
47+
48+---
README.md+25−54
@@ -1,70 +1,41 @@
1[![PureScript](https://raw.githubusercontent.com/purescript/purescript/master/logo.png)](http://purescript.org)
1[![PureScript](https://raw.githubusercontent.com/purescript/purescript/master/logo.png)](http://purescript.org)
2
2
3−This is an experimental C++11 (or later) compiler backend for [PureScript](https://github.com/purescript/purescript). It attempts to generate "sane", debuggable, and portable C++ code as an intermediate language, which is then compiled to a native executable binary. This enables easy interoperability with existing C/C++ 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.
4
4
5−---
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).
6−
7−### **Please note that this project supersedes [pure11](https://github.com/pure11/pure11)**
8−
9−#### Please see [changes](https://github.com/andyarvanitis/purescript-native/wiki/changes) for more details.
10−
11−---
12
6
13#### Platforms
7#### Platforms
14−* Generated C++ code and resulting binary executables tested (to at least some extent) on:
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
15− * macOS Mojave, Xcode 10.1's clang (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
16− * Windows 10 x64 and x86, Visual Studio 2017
13+ * C++: Visual Studio 2017 and `clang`
14+ * Go: `go` version 1.12.7
15+ * Linux Debian 9.5 amd64 – full test suite (C++)
17− * Linux Debian 9.5 amd64, default versions of clang and gcc/g++ (full test suite)
16+ * Default versions of `clang` and `gcc`
17+ * Linux Debian 10 amd64 – full test suite (Go)
18+ * Default version of `go`
18− * Raspberry Pi 3 B+ Raspbian official build (ARM), default versions of clang and gcc/g++
19+ * Raspberry Pi 3 B+ Raspbian official build (ARM), default versions of `clang` and `gcc`
19−
20−#### Performance
21−
22−* No runtime system beyond some support classes and the standard C++11 (or later) runtime library
23−* For automatic memory management, uses native C++11 reference counting (`std::shared_ptr`)
24−* Uses PureScript's normal tail call optimization techniques for generated C++ code
25−
26−#### Differences from PureScript:
27−
28−* Foreign imports/exports are C++ instead of JavaScript – see [FFI notes](https://github.com/andyarvanitis/purescript-native/wiki/FFI) and [standard library foreign implementations](https://github.com/andyarvanitis/purescript-native-ffi)
29−* No C++-specific REPL
30−
31−#### Other notes:
32−
33−* PureScript arrays are implemented using [`std::vector`](http://en.cppreference.com/w/cpp/container/vector)
34−* `String` types are implemented with C++11 `u8` literals (UTF-8) and `std::string`
35−* `Number` is C++ `double`, `Int` is C++ `int`, `Char` is `std::string` (single UTF-8 entity), `Boolean` is `bool`
36−
37−#### Future ideas:
38−
39−* Nice facilities (modules) for concurrency/parallelism, using `std::thread`, `std::async`, etc. under the hood (output is already generally thread-safe for immutable values, thanks to `std::shared_ptr`)
40−
41−#### Requirements for building the compiler itself
42−
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/))
44−
45−#### Requirements for using it
46−
47−* A recent version (0.12+) of [purescript](https://github.com/purescript/purescript/releases).
48
20
49−* A C++11-capable toolchain, e.g. recent versions of clang, gcc, Microsoft Visual Studio
50−* GNU Make is the default supported build tool, but you should be able to use your favorite C++ build system, tools, debuggers, etc.
51
21
52−#### Getting Started
22+#### Requirements for building `pscpp` and `psgo`
53−This assumes you are running macOS (OS X) or a Unix-like system (Linux, *BSD, etc.).
54
23
55−1. Make sure you have developer tools for your system installed. For macOS, you'll need a recent version of Xcode. For Linux, etc., you can use clang 3.5 or later, or gcc/g++ 4.9.2 or later.
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/))
56
25
57−2. Create a working directory wherever you like, and a `src` subdirectory under it, which will be where you will place your own PureScript source files.
26+#### Requirements for using PureScript + purescript-native
58
27
59−3. Under your working directory, also create an `ffi` subdirectory, which will be where you will place C/C++ FFI source files. You should at least add the contents of [purescript-native-ffi](https://github.com/andyarvanitis/purescript-native-ffi) into this directory, in addition to any of your own foreign implementations.
28+* A recent version (0.13+) of [purescript](https://github.com/purescript/purescript/releases).
60
29
61−4. Generate the default GNU `Makefile` in your working directory by running `pscpp --makefile`.
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.
62
34
63−5. Use PureScript's standard [`psc-package`](https://psc-package.readthedocs.io/en/latest/) utility to add and manage package dependencies.
64
35
65−6. You should now be ready to build a PureScript program:
36+#### For more information and a Getting Started guide, please see
66− * As stated above, place your source file(s) in the working directory's `src` subdirectory and execute `make debug` or `make release`. If your build machine has multiple cores, you might want to append `-jN` to your *make* command, where `N` is the number of cores.
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)
67
39
68− * This will generate the C++ source tree for your program and then build an executable binary. The resulting executable will be in the `bin` subdirectory under the output directory and called `main` (so `output/bin/main`, by default). Source files will be under `src` (`output/src/` by default).
69
40
70---
41---
app/Main.hs+161−46
@@ -10,7 +10,7 @@ import Data.Aeson
10import Data.Aeson.Types
10import Data.Aeson.Types
11import Data.Char
11import Data.Char
12import Data.FileEmbed (embedFile)
12import Data.FileEmbed (embedFile)
13−import Data.List (delete, isPrefixOf, partition)
13+import Data.List (delete, intercalate, isPrefixOf, nub, partition, (\\))
14import Data.Maybe
14import Data.Maybe
15import Data.Monoid ((<>))
15import Data.Monoid ((<>))
16import Data.Version
16import Data.Version
@@ -19,8 +19,10 @@ import Control.Monad.Supply.Class
19import Text.Printf
19import Text.Printf
20
20
21import System.Environment
21import System.Environment
22−import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getModificationTime)
22+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getCurrentDirectory, getModificationTime)
23import System.FilePath ((</>), joinPath, splitDirectories, takeDirectory)
23import System.FilePath ((</>), joinPath, splitDirectories, takeDirectory)
24+import System.FilePath.Find
25+import System.Process
24
26
25import Data.Text (Text)
27import Data.Text (Text)
26import qualified Data.Text as T
28import qualified Data.Text as T
@@ -29,6 +31,8 @@ import qualified Data.Text.Lazy as L
29import qualified Data.Text.Lazy.Encoding as L
31import qualified Data.Text.Lazy.Encoding as L
30import qualified Data.ByteString as B
32import qualified Data.ByteString as B
31
33
34+import Development.GitRev
35+
32import Language.PureScript.AST.Literals
36import Language.PureScript.AST.Literals
33import Language.PureScript.CoreFn
37import Language.PureScript.CoreFn
34import Language.PureScript.CoreFn.FromJSON
38import Language.PureScript.CoreFn.FromJSON
@@ -39,6 +43,8 @@ import CodeGen.IL.Printer
39
43
40import Tests
44import Tests
41
45
46+data Command = Build | Run (Maybe String)
47+
42parseJson :: Text -> Value
48parseJson :: Text -> Value
43parseJson text
49parseJson text
44 | Just fileJson <- decode . L.encodeUtf8 $ L.fromStrict text = fileJson
50 | Just fileJson <- decode . L.encodeUtf8 $ L.fromStrict text = fileJson
@@ -48,43 +54,102 @@ jsonToModule :: Value -> Module Ann
48jsonToModule value =
54jsonToModule value =
49 case parse moduleFromJSON value of
55 case parse moduleFromJSON value of
50 Success (_, r) -> r
56 Success (_, r) -> r
51− _ -> 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')
52
77
53main :: IO ()
78main :: IO ()
54main = do
79main = do
55 args <- getArgs
80 args <- getArgs
56− if null args
81+ -- let (opts, files) = partition (isPrefixOf "--") args
57− then return ()
82+ let (opts, moduleName, files) = splitArgs args
83+ opts' = (map . map) toLower opts
84+ if "--tests" `elem` opts'
85+ then runTests
58 else do
86 else do
59− let (opts, files) = partition (isPrefixOf "--") args
60− opts' = (map . map) toLower opts
61− if "--tests" `elem` opts'
87+ if "--help" `elem` opts' || "--version" `elem` opts'
62− then runTests
88+ then do
63− else do
64− when ("--makefile" `elem` opts') $
89+ when ("--help" `elem` opts') $ do
65− B.writeFile "Makefile" $(embedFile "support/Makefile")
66− when ("--help" `elem` opts') $
67 putStrLn help
90 putStrLn help
91+ when ("--version" `elem` opts') $ do
92+ let branch = $(gitBranch)
93+ details | branch == "golang" = "master, commit " ++ $(gitHash)
94+ | otherwise = branch
95+ putStrLn $ details ++ if $(gitDirty) then " (DIRTY)" else ""
96+ else do
68− when (not $ null files) $ do
97+ if null files
98+ then do
99+ currentDir <- getCurrentDirectory
100+ processFiles opts' [currentDir]
101+ else
102+ processFiles opts' files
103+ if "--run" `elem` opts
104+ then runBuildTool $ Run moduleName
105+ else when ("--no-build" `notElem` opts) $
106+ runBuildTool Build
107+ return ()
108+ where
109+ processFiles :: [String] -> [FilePath] -> IO ()
110+ processFiles opts [file] = do
111+ isDir <- doesDirectoryExist file
112+ if isDir then do
113+ files <- find always (fileName ==? corefn) file
114+ if null files
115+ then errorNoCorefnFiles
116+ else generateFiles opts files
117+ else generateFiles opts [file]
118+ processFiles opts files = do
119+ generateFiles opts files
120+ basePath :: [FilePath] -> FilePath
121+ basePath files =
69− let filepath = takeDirectory (head files)
122+ let filepath = takeDirectory (head files) in
70− baseOutpath = joinPath $ (init $ splitDirectories filepath) ++ [outdir]
123+ joinPath $ (init $ splitDirectories filepath)
71− writeRuntimeFiles baseOutpath
124+ generateFiles :: [String] -> [FilePath] -> IO ()
125+ generateFiles opts files = do
126+ let baseOutpath = basePath files
127+ writeSupportFiles baseOutpath
72− Par.mapM (generateCode opts' baseOutpath) files
128+ Par.mapM (generateCode opts baseOutpath) files
73− return ()
129+ return ()
130+ runBuildTool :: Command -> IO ()
131+ runBuildTool cmd = do
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]
74− return ()
138+ return ()
75
139
76generateCode :: [String] -> FilePath -> FilePath -> IO ()
140generateCode :: [String] -> FilePath -> FilePath -> IO ()
77generateCode opts baseOutpath jsonFile = do
141generateCode opts baseOutpath jsonFile = do
78 jsonModTime <- getModificationTime jsonFile
142 jsonModTime <- getModificationTime jsonFile
79 let filepath = takeDirectory jsonFile
143 let filepath = takeDirectory jsonFile
80 dirparts = splitDirectories $ filepath
144 dirparts = splitDirectories $ filepath
81− mname = (\c -> if c == '.' then '_' else c) <$> last dirparts
145+ mname = last dirparts
82 basedir = joinPath $ init dirparts
146 basedir = joinPath $ init dirparts
83− possInterfaceFilename = basedir </> outdir </> mname </> interfaceFileName mname
147+ mname' = T.pack mname
148+ possibleFileName = basedir </> (T.unpack mname') </> implFileName mname'
84− exists <- doesFileExist possInterfaceFilename
149+ exists <- doesFileExist possibleFileName
85 if exists
150 if exists
86 then do
151 then do
87− modTime <- getModificationTime possInterfaceFilename
152+ modTime <- getModificationTime possibleFileName
88 when (modTime < jsonModTime) $
153 when (modTime < jsonModTime) $
89 transpile opts baseOutpath jsonFile
154 transpile opts baseOutpath jsonFile
90 else transpile opts baseOutpath jsonFile
155 else transpile opts baseOutpath jsonFile
@@ -92,44 +157,94 @@ generateCode opts baseOutpath jsonFile = do
92transpile :: [String] -> FilePath -> FilePath -> IO ()
157transpile :: [String] -> FilePath -> FilePath -> IO ()
93transpile opts baseOutpath jsonFile = do
158transpile opts baseOutpath jsonFile = do
94 jsonText <- T.decodeUtf8 <$> B.readFile jsonFile
159 jsonText <- T.decodeUtf8 <$> B.readFile jsonFile
160+ project <- projectEnv
95 let module' = jsonToModule $ parseJson jsonText
161 let module' = jsonToModule $ parseJson jsonText
96− ((interface, foreigns, asts, implHeader, implFooter), _) <- runSupplyT 5 (moduleToIL module' Nothing)
162+ ((_, foreigns, asts, implHeader, implFooter), _) <- runSupplyT 5 (moduleToIL module' project)
97− let mn = moduleNameToIL $ moduleName module'
163+ let mn = moduleNameToIL' $ moduleName module'
98 implementation = prettyPrintIL asts
164 implementation = prettyPrintIL asts
99 outpath = joinPath [baseOutpath, T.unpack mn]
165 outpath = joinPath [baseOutpath, T.unpack mn]
100− interfacePath = outpath </> interfaceFileName (T.unpack mn)
101 implPath = outpath </> implFileName mn
166 implPath = outpath </> implFileName mn
102− putStrLn interfacePath
103 createDirectoryIfMissing True outpath
167 createDirectoryIfMissing True outpath
104− B.writeFile interfacePath $ T.encodeUtf8 interface
105 putStrLn implPath
168 putStrLn implPath
106 B.writeFile implPath $ T.encodeUtf8 (implHeader <> implementation <> implFooter)
169 B.writeFile implPath $ T.encodeUtf8 (implHeader <> implementation <> implFooter)
107
170
108−writeRuntimeFiles :: FilePath -> IO ()
171+writeSupportFiles :: FilePath -> IO ()
109−writeRuntimeFiles baseOutpath = do
172+writeSupportFiles baseOutpath = do
173+ currentDir <- getCurrentDirectory
174+ let outputdir = T.pack $ baseOutpath \\ currentDir
175+ ffiOutpath = currentDir </> subdir
110 createDirectoryIfMissing True baseOutpath
176 createDirectoryIfMissing True baseOutpath
111− let runtimeSource = baseOutpath </> "purescript.go"
177+ createDirectoryIfMissing True ffiOutpath
112− runtimeExists <- doesFileExist runtimeSource
178+ writeModuleFile outputdir currentDir $(embedFile "support/go.mod.working")
113− when (not runtimeExists) $ do
179+ writeModuleFile outputdir baseOutpath $(embedFile "support/go.mod.output")
114− B.writeFile runtimeSource $(embedFile "runtime/purescript.go")
180+ writeModuleFile outputdir ffiOutpath $(embedFile "support/go.mod.ffi-loader")
115−
181+ writeLoaderFile ffiOutpath $(embedFile "support/ffi-loader.go")
116−outdir :: FilePath
182+ where
117−outdir = "src"
183+ writeModuleFile :: Text -> FilePath -> B.ByteString -> IO ()
118−
184+ writeModuleFile outputdir path modText = do
119−interfaceFileName :: String -> FilePath
185+ let goModSource = path </> "go.mod"
120−interfaceFileName mn = mn <> ".go"
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
121
204
122implFileName :: Text -> FilePath
205implFileName :: Text -> FilePath
123−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
124
211
125help :: String
212help :: String
126help = "Usage: psgo OPTIONS COREFN-FILES\n\
213help = "Usage: psgo OPTIONS COREFN-FILES\n\
127− \ PureScript-to-go compiler\n\n\
214+ \ PureScript to native (via go) compiler\n\n\
128 \Available options:\n\
215 \Available options:\n\
129 \ --help Show this help text\n\n\
216 \ --help Show this help text\n\n\
130− \ --makefile Generate a GNU Makefile which can be used for compiling\n\
217+ \ --version Show the version number\n\n\
131− \ a PureScript program and libraries to a native binary via\n\
218+ \ --run Run the generated go code directly, without building an\n\
132− \ purs corefn output and C++\n\n\
219+ \ executable\n\
220+ \ --no-build Generate go source files, but do not build an executable\n\
133 \ --tests Run test cases (under construction)\n\n\
221 \ --tests Run test cases (under construction)\n\n\
134 \See also:\n\
222 \See also:\n\
135 \ purs compile --help\n"
223 \ purs compile --help\n"
224+
225+corefn :: String
226+corefn = "corefn.json"
227+
228+goSrc :: String
229+goSrc = ".go"
230+
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+
246+errorNoCorefnFiles :: IO ()
247+errorNoCorefnFiles = do
248+ ioError . userError $ "no compiled purescript '" <> corefn <> "' files found –\n" <>
249+ " make sure to use the '--codegen corefn' option with your purs\n" <>
250+ " project build tool"
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+9−6
@@ -9,7 +9,8 @@ copyright: "2018 Andy Arvanitis"
9extra-source-files:
9extra-source-files:
10- README.md
10- README.md
11- ChangeLog.md
11- ChangeLog.md
12−- runtime/*.go
12+- stack.yaml
13+- support/*
13
14
14# Metadata used when publishing your package
15# Metadata used when publishing your package
15# synopsis: Short description of your package
16# synopsis: Short description of your package
@@ -23,7 +24,7 @@ description: Please see the README on Github at <https://github.com/andy
23dependencies:
24dependencies:
24- base >= 4.7 && < 5
25- base >= 4.7 && < 5
25- bytestring >= 0.10.8.2 && < 0.11
26- bytestring >= 0.10.8.2 && < 0.11
26−- purescript -any
27+- purescript
27- base-compat >=0.6.0
28- base-compat >=0.6.0
28- protolude >=0.1.6
29- protolude >=0.1.6
29- text
30- text
@@ -34,10 +35,12 @@ dependencies:
34- safe >=0.3.9 && <0.4
35- safe >=0.3.9 && <0.4
35- transformers >=0.3.0 && <0.6
36- transformers >=0.3.0 && <0.6
36- mtl >=2.1.0 && <2.3.0
37- mtl >=2.1.0 && <2.3.0
37−- aeson >=1.0 && <1.4
38+- aeson >=1.0 && <1.5
38−- directory -any
39+- directory
39−- process -any
40+- process
40−- file-embed -any
41+- file-embed
42+- filemanip
43+- gitrev
41
44
42library:
45library:
43 source-dirs: src
46 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−22
@@ -1,22 +0,0 @@
1−package purescript
2−
3−//go:generate purs --version
4−
5−type Any = interface{}
6−
7−type Fn = func(Any) Any
8−type EffFn = func() Any
9−
10−func Apply(f Any, args ...Any) Any {
11− result := f
12− for _, arg := range args {
13− fn, _ := result.(Fn)
14− result = fn(arg)
15− }
16− return result
17−}
18−
19−func Get(dict Any, key string) Any {
20− d, _ := dict.(func(string) Any)
21− return d(key)
22−}
src/CodeGen/IL.hs+31−131
@@ -48,23 +48,19 @@ moduleToIL
48 -- . (Monad m, MonadReader Options m, MonadSupply m, MonadError MultipleErrors m)
48 -- . (Monad m, MonadReader Options m, MonadSupply m, MonadError MultipleErrors m)
49 . (Monad m, MonadSupply m)
49 . (Monad m, MonadSupply m)
50 => Module Ann
50 => Module Ann
51− -> Maybe AST
51+ -> Text
52 -> m (Text, [Text], [AST], Text, Text)
52 -> m (Text, [Text], [AST], Text, Text)
53−moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
53+moduleToIL (Module _ coms mn _ imps _ foreigns decls) project =
54 do
54 do
55− let usedNames = concatMap getNames decls
56− let mnLookup = renameImports usedNames imps
57− ilImports <- traverse (importToIL mnLookup) . (\\ (mn : C.primModules)) . (\\ [mn]) $ ordNub $ map snd imps
58− interfaceImport <- importToIL (renameImports [] [(emptyAnn, mn)]) mn
59− let decls' = renameModules mnLookup decls
60− ilDecls <- mapM (bindToIL ModuleDecl) decls'
55+ ilDecls <- mapM (bindToIL ModuleDecl) decls
61 optimized <- traverse (traverse (optimize modName')) ilDecls
56 optimized <- traverse (traverse (optimize modName')) ilDecls
62 let optimized' = concat optimized
57 let optimized' = concat optimized
63 values = annotValue <$> optimized'
58 values = annotValue <$> optimized'
64− foreigns' = identToIL <$> foreigns
59+ foreigns' = moduleIdentToIL <$> foreigns
65 interface = interfaceSource modName values foreigns
60 interface = interfaceSource modName values foreigns
61+ imports = nub . concat $ importToIL <$> optimized'
66− implHeader = implHeaderSource modName ilImports interfaceImport
62+ implHeader = implHeaderSource modName imports project
67− implFooter = implFooterSource modName foreigns
63+ implFooter = implFooterSource (runModuleName mn) foreigns
68 return $ (interface, foreigns', optimized', implHeader, implFooter)
64 return $ (interface, foreigns', optimized', implHeader, implFooter)
69 where
65 where
70 modName = moduleNameToIL mn
66 modName = moduleNameToIL mn
@@ -81,53 +77,16 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
81 getNames (NonRec _ ident _) = [ident]
77 getNames (NonRec _ ident _) = [ident]
82 getNames (Rec vals) = map (snd . fst) vals
78 getNames (Rec vals) = map (snd . fst) vals
83
79
84− -- | Creates alternative names for each module to ensure they don't collide
85− -- with declaration names.
86− renameImports :: [Ident] -> [(Ann, ModuleName)] -> M.Map ModuleName (Ann, ModuleName)
87− renameImports = go M.empty
88− where
89− go :: M.Map ModuleName (Ann, ModuleName) -> [Ident] -> [(Ann, ModuleName)] -> M.Map ModuleName (Ann, ModuleName)
90− go acc used ((ann, mn') : mns') =
91− let mni = Ident $ runModuleName mn'
92− in if mn' /= mn && mni `elem` used
93− then let newName = freshModuleName 1 mn' used
94− in go (M.insert mn' (ann, newName) acc) (Ident (runModuleName newName) : used) mns'
95− else go (M.insert mn' (ann, mn') acc) used mns'
96− go acc _ [] = acc
97−
98− freshModuleName :: Integer -> ModuleName -> [Ident] -> ModuleName
99− freshModuleName i mn'@(ModuleName pns) used =
100− let newName = ModuleName $ init pns ++ [ProperName $ runProperName (last pns) <> "_" <> T.pack (show i)]
101− in if Ident (runModuleName newName) `elem` used
102− then freshModuleName (i + 1) mn' used
103− else newName
104−
105− -- | Generates C++ code for a module import, binding the required module
80+ -- | Generates IL code for a module import
106− -- to the alternative
81+ --
107− importToIL :: M.Map ModuleName (Ann, ModuleName) -> ModuleName -> m Text
82+ importToIL :: AST -> [Text]
108− importToIL mnLookup mn' = do
83+ importToIL = AST.everything (++) modRef
109− let ((_, _, _, _), mnSafe) = fromMaybe (internalError "Missing value in mnLookup") $ M.lookup mn' mnLookup
110− mname = moduleNameToIL mnSafe
111− pure $ "#include \"" <> mname <> "/" <> mname <> ".h\"\n"
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 goExpr goBinder
118− in map f binds
119 where
84 where
120− goExpr :: Expr a -> Expr a
85+ modRef (AST.Indexer _ (AST.Var _ _) (AST.Var _ mname))
121− goExpr (Var ann q) = Var ann (renameQual q)
86+ | not $ T.null mname = [extract mname]
122− goExpr e = e
87+ modRef _ = []
123− goBinder :: Binder a -> Binder a
88+ extract :: Text -> Text
124− goBinder (ConstructorBinder ann q1 q2 bs) = ConstructorBinder ann (renameQual q1) (renameQual q2) bs
89+ extract = T.replace "_" "." . T.dropWhileEnd (=='_')
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
131
90
132 -- |
91 -- |
133 -- Generate code in the simplified intermediate representation for a declaration
92 -- Generate code in the simplified intermediate representation for a declaration
@@ -151,41 +110,10 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
151 else AST.Comment Nothing com <$> nonRecToIL dt a i (modifyAnn removeComments e)
110 else AST.Comment Nothing com <$> nonRecToIL dt a i (modifyAnn removeComments e)
152 nonRecToIL ModuleDecl (ss, _, _, _) ident val = do
111 nonRecToIL ModuleDecl (ss, _, _, _) ident val = do
153 il <- valueToIL val
112 il <- valueToIL val
154− pure $ AST.Function Nothing (Just $ identToIL ident) [] (AST.Block Nothing [AST.Return Nothing il])
113+ pure $ AST.Function Nothing (Just $ moduleIdentToIL ident) [] (AST.Block Nothing [AST.Return Nothing il])
155 nonRecToIL RecLetDecl (ss, _, _, _) ident val = do
114 nonRecToIL RecLetDecl (ss, _, _, _) ident val = do
156− il <- valueToIL val
115+ ilExpr <- valueToIL val
157− pure $
116+ pure $ AST.Assignment Nothing (AST.Var Nothing $ identToIL ident) ilExpr
158− let retainedName = identToIL ident
159− retained = AST.Var Nothing $ retainedName
160− unretainedName = retainedName <> unretainedSuffix
161− unretained = AST.Var Nothing $ unretainedName
162− boxed = AST.Var Nothing . -- Note: prevents optimizer from eliminating this
163− prettyPrintIL1 $
164− AST.VariableIntroduction
165− Nothing
166− retainedName
167− (Just unretained)
168− alias = AST.Var Nothing $ "auto& " <> retainedName <> " = " <> unretainedName
169− in
170− case il of
171− AST.Function _ name args (AST.Block Nothing ils) ->
172− AST.Assignment Nothing
173− retained
174− (AST.Function Nothing
175− name
176− args
177− (AST.Block Nothing (boxed : ils)))
178− _ ->
179− AST.Assignment Nothing
180− retained
181− (AST.App Nothing
182− (AST.Function Nothing
183− Nothing
184− []
185− (AST.Block Nothing ([ alias
186− , AST.Return Nothing il
187− ])))
188− [])
189 nonRecToIL _ (ss, _, _, _) ident val = do
117 nonRecToIL _ (ss, _, _, _) ident val = do
190 il <- valueToIL val
118 il <- valueToIL val
191 pure $ AST.VariableIntroduction Nothing (identToIL ident) (Just il)
119 pure $ AST.VariableIntroduction Nothing (identToIL ident) (Just il)
@@ -209,7 +137,7 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
209 updates <- mapM (sndM valueToIL) ps
137 updates <- mapM (sndM valueToIL) ps
210 obj' <- freshName'
138 obj' <- freshName'
211 let objVar = AST.Var Nothing obj'
139 let objVar = AST.Var Nothing obj'
212− copy = AST.VariableIntroduction Nothing obj' (Just $ AST.App Nothing (AST.Var Nothing (unbox dictType)) [obj])
140+ copy = AST.VariableIntroduction Nothing obj' (Just $ copyDict obj)
213 assign (k, v) = AST.Assignment Nothing (accessorString k objVar) v
141 assign (k, v) = AST.Assignment Nothing (accessorString k objVar) v
214 sts = copy : (assign <$> updates) ++ [AST.Return Nothing objVar]
142 sts = copy : (assign <$> updates) ++ [AST.Return Nothing objVar]
215 return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing sts)) []
143 return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing sts)) []
@@ -222,6 +150,7 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
222 valueToIL e@App{} = do
150 valueToIL e@App{} = do
223 let (f, args) = unApp e []
151 let (f, args) = unApp e []
224 args' <- mapM valueToIL args
152 args' <- mapM valueToIL args
153+ fn <- valueToIL f
225 case f of
154 case f of
226 Var (_, _, _, Just IsNewtype) _ -> return (head args')
155 Var (_, _, _, Just IsNewtype) _ -> return (head args')
227 _ -> flip (foldl (\fn a -> AST.App Nothing fn [a])) args' <$> valueToIL f
156 _ -> flip (foldl (\fn a -> AST.App Nothing fn [a])) args' <$> valueToIL f
@@ -229,48 +158,20 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
229 unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann])
158 unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann])
230 unApp (App _ val arg) args = unApp val (arg : args)
159 unApp (App _ val arg) args = unApp val (arg : args)
231 unApp other args = (other, args)
160 unApp other args = (other, args)
232− -- valueToIL (Var (_, _, _, Just IsForeign) qi@(Qualified (Just mn') ident)) =
233− -- return $ if mn' == mn
234− -- then AST.Var Nothing (moduleNameToIL mn' <> "::" <> identToIL ident)
235− -- else varToIL qi
236− -- valueToIL (Var (_, _, _, Just IsForeign) (Qualified (Just mn') ident)) =
237− -- return $ AST.Var Nothing (moduleNameToIL mn' <> "::" <> identToIL ident)
238− -- valueToIL (Var (_, _, _, Just IsForeign) qi@(Qualified (Just mn') ident)) =
239− -- return $ if mn' == mn
240− -- then foreignIdent ident
241− -- else varToIL qi
242− -- valueToIL (Var (_, _, _, Just IsForeign) ident) =
243− -- internalError $ "Encountered an unqualified reference to a foreign ident " ++ T.unpack (showQualified showIdent ident)
244 valueToIL (Var _ ident) = return $ varToIL ident
161 valueToIL (Var _ ident) = return $ varToIL ident
245 valueToIL (Case (ss, _, _, _) values binders) = do
162 valueToIL (Case (ss, _, _, _) values binders) = do
246 vals <- mapM valueToIL values
163 vals <- mapM valueToIL values
247 bindersToIL ss binders vals
164 bindersToIL ss binders vals
248 valueToIL (Let _ ds val) = do
165 valueToIL (Let _ ds val) = do
249 let recurs = concatMap getNames $ filter isRec ds
166 let recurs = concatMap getNames $ filter isRec ds
250− recurDs = (\v -> AST.Var Nothing $ "boxed::recur " <> identToIL v) <$> recurs
167+ recurDs = (\v -> AST.VariableIntroduction Nothing (identToIL v) Nothing) <$> recurs
251− recurDsWeak = (\v -> AST.Var
252− Nothing
253− ("boxed::recur::weak " <> identToIL v <> unretainedSuffix <> "(" <> identToIL v <> ")")
254− ) <$> recurs
255− mutualRecurWarning = if any isMutRec $ filter isRec ds
256− then [AST.Var Nothing "#pragma message(\"Mutually recursive lets will cause retain cycles (memory leaks)\") //"]
257− else []
258 ds' <- concat <$> mapM (bindToIL LetDecl) ds
168 ds' <- concat <$> mapM (bindToIL LetDecl) ds
259 ret <- valueToIL val
169 ret <- valueToIL val
260− return $
261− AST.App Nothing
262− (AST.Function Nothing
263− Nothing
264− []
265− (AST.Block Nothing (recurDs ++ recurDsWeak ++ mutualRecurWarning ++ ds' ++ [AST.Return Nothing ret])))
170+ return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing (recurDs ++ ds' ++ [AST.Return Nothing ret]))) []
266− []
267 where
171 where
268 isRec :: Bind Ann -> Bool
172 isRec :: Bind Ann -> Bool
269 isRec (Rec _) = True
173 isRec (Rec _) = True
270− isRec _ = False
174+ isRec _ = False
271− isMutRec :: Bind Ann -> Bool
272− isMutRec b | (length . nub $ getNames b) > 1 = True
273− | otherwise = False
274 valueToIL (Constructor (_, _, _, Just IsNewtype) _ (ProperName ctor) _) = error "IsNewtype"
175 valueToIL (Constructor (_, _, _, Just IsNewtype) _ (ProperName ctor) _) = error "IsNewtype"
275 valueToIL (Constructor _ _ (ProperName ctor) fields) =
176 valueToIL (Constructor _ _ (ProperName ctor) fields) =
276 let body = AST.ObjectLiteral Nothing $ (mkString ctor, AST.BooleanLiteral Nothing True) :
177 let body = AST.ObjectLiteral Nothing $ (mkString ctor, AST.BooleanLiteral Nothing True) :
@@ -300,9 +201,9 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
300 -- | Generate code in the simplified intermediate representation for a reference to a
201 -- | Generate code in the simplified intermediate representation for a reference to a
301 -- variable that may have a qualified name.
202 -- variable that may have a qualified name.
302 qualifiedToIL :: (a -> Ident) -> Qualified a -> AST
203 qualifiedToIL :: (a -> Ident) -> Qualified a -> AST
303− qualifiedToIL f (Qualified (Just (ModuleName [ProperName mn'])) a) | mn' == C.prim = AST.Var Nothing . runIdent $ f a
204+ qualifiedToIL f (Qualified (Just (ModuleName mn')) a) | mn' == C.prim = AST.Var Nothing . moduleIdentToIL $ f a
304− qualifiedToIL f (Qualified (Just mn') a) = AST.Indexer Nothing (AST.Var Nothing . identToIL $ f a) (AST.Var Nothing (moduleNameToIL mn'))
205+ qualifiedToIL f (Qualified (Just mn') a) | mn /= mn' = AST.Indexer Nothing (AST.Var Nothing . moduleIdentToIL $ f a) (AST.Var Nothing (moduleNameToIL mn'))
305− qualifiedToIL f (Qualified _ a) = AST.Var Nothing $ identToIL (f a)
206+ qualifiedToIL f (Qualified _ a) = AST.Indexer Nothing (AST.Var Nothing . moduleIdentToIL $ f a) (AST.Var Nothing "")
306
207
307 -- foreignIdent :: Ident -> AST
208 -- foreignIdent :: Ident -> AST
308 -- foreignIdent ident = accessorString (mkString $ runIdent ident) (AST.Var Nothing "$foreign")
209 -- foreignIdent ident = accessorString (mkString $ runIdent ident) (AST.Var Nothing "$foreign")
@@ -327,7 +228,7 @@ moduleToIL (Module _ coms mn _ imps _ foreigns decls) _ =
327 go _ _ _ = internalError "Invalid arguments to bindersToIL"
228 go _ _ _ = internalError "Invalid arguments to bindersToIL"
328
229
329 failedPatternMessage :: Text
230 failedPatternMessage :: Text
330− failedPatternMessage = "Failed pattern match at " <> runModuleName mn <> " " <> displayStartEndPos ss <> ": "
231+ failedPatternMessage = "Failed pattern match at " <> runModuleName mn <> " " <> displayStartEndPos ss
331
232
332 guardsToIL :: Either [(Guard Ann, Expr Ann)] (Expr Ann) -> m [AST]
233 guardsToIL :: Either [(Guard Ann, Expr Ann)] (Expr Ann) -> m [AST]
333 guardsToIL (Left gs) = traverse genGuard gs where
234 guardsToIL (Left gs) = traverse genGuard gs where
@@ -414,8 +315,7 @@ emptyAnn = (SourceSpan "" (SourcePos 0 0) (SourcePos 0 0), [], Nothing, Nothing)
414
315
415arrayLength :: AST -> AST
316arrayLength :: AST -> AST
416arrayLength a = AST.App Nothing (AST.Var Nothing arrayLengthFn) [a]
317arrayLength a = AST.App Nothing (AST.Var Nothing arrayLengthFn) [a]
318+-- arrayLength a = AST.Var Nothing (arrayLengthFn <> "(" <> prettyPrintIL1 a <> ")")
417
319
418−freshName' :: MonadSupply m => m Text
320+copyDict :: AST -> AST
419−freshName' = do
321+copyDict a = AST.App Nothing (AST.Var Nothing copyDictFn) [a]
420− name <- freshName
421− return $ T.replace "$" "_Local_" name
src/CodeGen/IL/Common.hs+104−130
@@ -3,7 +3,9 @@ module CodeGen.IL.Common where
3
3
4import Prelude.Compat
4import Prelude.Compat
5
5
6+import Control.Monad.Supply.Class (MonadSupply, freshName)
6import Data.Char
7import Data.Char
8+import Data.Maybe (maybe)
7import Data.Monoid ((<>))
9import Data.Monoid ((<>))
8import Data.Text (Text)
10import Data.Text (Text)
9import qualified Data.Text as T
11import qualified Data.Text as T
@@ -11,57 +13,82 @@ import qualified Data.Text as T
11import Language.PureScript.Crash
13import Language.PureScript.Crash
12import Language.PureScript.Names
14import Language.PureScript.Names
13
15
16+import qualified Language.PureScript.Constants as C
17+
14moduleNameToIL :: ModuleName -> Text
18moduleNameToIL :: ModuleName -> Text
15−moduleNameToIL (ModuleName pns) =
19+moduleNameToIL (ModuleName mn) =
16− let name = T.intercalate "_" (runProperName `map` pns)
20+ let name = T.replace "." "_" mn
17− in if nameIsILBuiltIn name then ("_" <> name <> "_") else name
21+ in if nameIsILBuiltIn name then (name <> moduleRenamerMarker) else name
22+
23+moduleNameToIL' :: ModuleName -> Text
24+moduleNameToIL' (ModuleName mn) = T.replace "_" "." mn
18
25
19−-- | Convert an 'Ident' into a valid C++ identifier:
26+-- | Convert an 'Ident' into a valid IL identifier:
20--
27--
21-- * Alphanumeric characters are kept unmodified.
28-- * Alphanumeric characters are kept unmodified.
22--
29--
23−-- * Reserved C++ identifiers are wrapped with '_'.
30+-- * Reserved IL identifiers are wrapped with '_'.
24--
31--
25identToIL :: Ident -> Text
32identToIL :: Ident -> Text
26identToIL UnusedIdent = unusedName
33identToIL UnusedIdent = unusedName
27identToIL (Ident "$__unused") = unusedName
34identToIL (Ident "$__unused") = unusedName
35+identToIL (Ident name) | name == C.undefined = undefinedName -- Note: done in Printer.hs too
28identToIL (Ident name) = properToIL name
36identToIL (Ident name) = properToIL name
29identToIL (GenIdent _ _) = internalError "GenIdent in identToIL"
37identToIL (GenIdent _ _) = internalError "GenIdent in identToIL"
30
38
39+moduleIdentToIL :: Ident -> Text
40+moduleIdentToIL UnusedIdent = unusedName
41+moduleIdentToIL (Ident name) | name == C.undefined = undefinedName
42+moduleIdentToIL (Ident name) = moduleProperToIL name
43+moduleIdentToIL (GenIdent _ _) = internalError "GenIdent in identToIL"
44+
45+undefinedName :: Text
46+undefinedName = "Undefined"
47+
31unusedName :: Text
48unusedName :: Text
32−unusedName = "_Unused_"
49+unusedName = "_"
33
50
34properToIL :: Text -> Text
51properToIL :: Text -> Text
35properToIL name
52properToIL name
36− | nameIsILReserved name || nameIsILBuiltIn name || prefixIsReserved name = "_" <> name <> "_"
53+ | nameIsILReserved name = "ˉ" <> name
37 | otherwise = T.concatMap identCharToText name
54 | otherwise = T.concatMap identCharToText name
38
55
39−-- | Test if a string is a valid AST identifier without escaping.
40−identNeedsEscaping :: Text -> Bool
41−identNeedsEscaping s = s /= properToIL s || T.null s
42−
43-- | Attempts to find a human-readable name for a symbol, if none has been specified returns the
56-- | Attempts to find a human-readable name for a symbol, if none has been specified returns the
44-- ordinal value.
57-- ordinal value.
45identCharToText :: Char -> Text
58identCharToText :: Char -> Text
59+identCharToText 'ṩ' = "_ṩ"
46identCharToText c | isAlphaNum c = T.singleton c
60identCharToText c | isAlphaNum c = T.singleton c
47identCharToText '_' = "_"
61identCharToText '_' = "_"
48−identCharToText '\'' = "_Prime_"
62+identCharToText '\'' = "ꞌ" -- lowercase saltillo
49−identCharToText '$' = "$"
63+identCharToText c = error (show (ord c)) -- TODO: should never occur now(?)
50−identCharToText c = "_Code_Point_" <> T.pack (show (ord c))
64+
65+moduleProperToIL :: Text -> Text
66+moduleProperToIL = T.concatMap identCharToText
51
67
52−-- | Checks whether an identifier name is reserved in C++.
68+-- | Checks whether an identifier name is reserved in IL.
53nameIsILReserved :: Text -> Bool
69nameIsILReserved :: Text -> Bool
54nameIsILReserved name =
70nameIsILReserved name =
55 name `elem` ilAnyReserved
71 name `elem` ilAnyReserved
56
72
57−-- | Checks whether a name matches a built-in value in C++.
73+-- | Checks whether a name matches a built-in value in IL.
58nameIsILBuiltIn :: Text -> Bool
74nameIsILBuiltIn :: Text -> Bool
59nameIsILBuiltIn name =
75nameIsILBuiltIn name =
60 name `elem`
76 name `elem`
61− [ "final"
77+ [ "Any"
62− , "override"
78+ , "Apply"
63− , "posix"
79+ , "Contains"
64− , "std"
80+ , "CopyDict"
81+ , "Dict"
82+ , "EffFn"
83+ , "Fn"
84+ , "Foreign"
85+ , "Get"
86+ , "Index"
87+ , "Is"
88+ , "Length"
89+ , "Once"
90+ , "Run"
91+ , "Undefined"
65 ]
92 ]
66
93
67ilAnyReserved :: [Text]
94ilAnyReserved :: [Text]
@@ -73,135 +100,77 @@ ilAnyReserved =
73
100
74ilKeywords :: [Text]
101ilKeywords :: [Text]
75ilKeywords =
102ilKeywords =
76− [ "alignas"
77− , "alignof"
78− , "and"
79− , "and_eq"
80− , "asm"
81− , "assert"
82− , "atomic_cancel"
83− , "atomic_commit"
84− , "atomic_noexcept"
85− , "auto"
86− , "bitand"
87− , "bitor"
88− , "bool"
89− , "break"
103+ [ "break"
90 , "case"
104 , "case"
91− , "catch"
105+ , "chan"
92− , "char"
93− , "char16_t"
94− , "char32_t"
95− , "class"
96− , "compl"
97− , "concept"
98 , "const"
106 , "const"
99− , "constexpr"
100− , "const_cast"
101 , "continue"
107 , "continue"
102− , "concept"
103− , "decltype"
104 , "default"
108 , "default"
105− , "delete"
109+ , "defer"
106− , "do"
107− , "double"
108− , "dynamic_cast"
109 , "else"
110 , "else"
110− , "enum"
111+ , "fallthrough"
111− , "explicit"
112− , "export"
113− , "extern"
114− , "false"
115− , "float"
116 , "for"
112 , "for"
117− , "friend"
113+ , "func"
114+ , "go"
118 , "goto"
115 , "goto"
119 , "if"
116 , "if"
120 , "import"
117 , "import"
121− , "inline"
118+ , "interface"
122− , "int"
119+ , "map"
123− , "long"
120+ , "package"
124− , "module"
121+ , "range"
125− , "mutable"
126− , "namespace"
127− , "new"
128− , "noexcept"
129− , "not"
130− , "not_eq"
131− , "nullptr"
132− , "nullptr_t"
133− , "operator"
134− , "or"
135− , "or_eq"
136− , "private"
137− , "protected"
138− , "public"
139− , "register"
140− , "reinterpret_cast"
141− , "requires"
142 , "return"
122 , "return"
143− , "short"
123+ , "select"
144− , "signed"
145− , "sizeof"
146− , "static"
147− , "static_assert"
148− , "static_cast"
149 , "struct"
124 , "struct"
150 , "switch"
125 , "switch"
151− , "synchronized"
126+ , "type"
152− , "template"
127+ , "var"
153− , "this"
154− , "thread_local"
155− , "throw"
156− , "true"
157− , "try"
158− , "typedef"
159− , "typeid"
160− , "typename"
161− , "typeof"
162− , "union"
163− , "unsigned"
164− , "using"
165− , "virtual"
166− , "void"
167− , "volatile"
168− , "wchar_t"
169− , "while"
170− , "xor"
171− , "xor_eq"
172 ]
128 ]
173
129
174−prefixIsReserved :: Text -> Bool
175−prefixIsReserved name =
176− any (`T.isPrefixOf` name)
177− [ "__"
178− ]
179−
180ilLiterals :: [Text]
130ilLiterals :: [Text]
181ilLiterals =
131ilLiterals =
182− [ "box"
132+ [ "init"
183− , "boxed"
133+ , "nil"
184− , "copy"
134+ , "int"
185− , "errno"
135+ , "int8"
186− , "peek"
136+ , "int16"
187− , "purescript"
137+ , "int32"
188− , "runtime_error"
138+ , "int64"
189− , "setjmp"
139+ , "uint"
140+ , "uint8"
141+ , "uint16"
142+ , "uint32"
143+ , "uint64"
144+ , "uintptr"
145+ , "float32"
146+ , "float64"
147+ , "complex64"
148+ , "complex128"
149+ , "byte"
150+ , "rune"
190 , "string"
151 , "string"
191− , "unbox"
192 ]
152 ]
193
153
154+withPrefix :: Text -> Text
155+withPrefix s = "Ꞌ" <> s -- uppercase saltillo
156+
157+anyType :: Text
158+anyType = "Any"
159+
194dictType :: Text
160dictType :: Text
195−dictType = "dict_t"
161+dictType = "Dict"
196
162
197arrayType :: Text
163arrayType :: Text
198−arrayType = "array_t"
164+arrayType = "[]" <> anyType
165+
166+uncurriedFnType :: Int -> Text
167+uncurriedFnType i = "Fn" <> (T.pack . show $ i)
199
168
200int :: Text
169int :: Text
201int = "int"
170int = "int"
202
171
203float :: Text
172float :: Text
204−float = "double"
173+float = "float64"
205
174
206bool :: Text
175bool :: Text
207bool = "bool"
176bool = "bool"
@@ -209,14 +178,19 @@ bool = "bool"
209string :: Text
178string :: Text
210string = "string"
179string = "string"
211
180
212−unbox :: Text -> Text
213−unbox t = "unbox<" <> t <> ">"
214−
215arrayLengthFn :: Text
181arrayLengthFn :: Text
216−arrayLengthFn = "array_length"
182+arrayLengthFn = "Length"
183+
184+indexFn :: Text
185+indexFn = "Index"
186+
187+copyDictFn :: Text
188+copyDictFn = "CopyDict"
217
189
218−unretainedSuffix :: Text
190+freshName' :: MonadSupply m => m Text
219−unretainedSuffix = "_Weak_"
191+freshName' = do
192+ name <- freshName
193+ return $ T.replace "$" "ṩ" name
220
194
221−tcoLoop :: Text
195+moduleRenamerMarker :: Text
222−tcoLoop = "_TCO_Loop_"
196+moduleRenamerMarker = "__"
src/CodeGen/IL/Optimizer.hs+34−93
@@ -8,31 +8,30 @@ import Data.Text (Text)
8import Language.PureScript.CoreImp.AST
8import Language.PureScript.CoreImp.AST
9import Language.PureScript.CoreImp.Optimizer.Blocks
9import Language.PureScript.CoreImp.Optimizer.Blocks
10import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict)
10import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict)
11−import Language.PureScript.CoreImp.Optimizer.Inliner hiding (inlineUnsafeCoerce, inlineUnsafePartial)
11+import Language.PureScript.CoreImp.Optimizer.Inliner (etaConvert, evaluateIifes, inlineVariables, unThunk)
12import Language.PureScript.CoreImp.Optimizer.Unused
12import Language.PureScript.CoreImp.Optimizer.Unused
13−import Language.PureScript.PSString (PSString, decodeString)
14
13
15import CodeGen.IL.Common (unusedName)
14import CodeGen.IL.Common (unusedName)
15+import CodeGen.IL.Optimizer.Inliner
16+import CodeGen.IL.Optimizer.MagicDo
16import CodeGen.IL.Optimizer.TCO
17import CodeGen.IL.Optimizer.TCO
17
18
18import qualified Language.PureScript.Constants as C
19import qualified Language.PureScript.Constants as C
19
20
20−
21−-- | Apply a series of optimizer passes to simplified C++ code
21+-- | Apply a series of optimizer passes to simplified IL code
22optimize :: MonadSupply m => AST -> AST -> m AST
22optimize :: MonadSupply m => AST -> AST -> m AST
23−-- optimize = untilFixedPoint $ return . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp . tco
24optimize mn il = do
23optimize mn il = do
25− il' <- untilFixedPoint (return . inlineApply . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp) il
24+ il' <- untilFixedPoint (inlineFnComposition . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp) il
26− untilFixedPoint (return . tidyUp) . tco mn
25+ il'' <- untilFixedPoint (return . ignoreUnusedResults . inlineCommonValues . inlineCommonOperators . tidyUp) . tco mn . inlineST
27− =<< untilFixedPoint (return . magicDo')
26+ =<< untilFixedPoint (return . magicDoST)
28− =<< untilFixedPoint (return . magicDo) il'
27+ =<< untilFixedPoint (return . magicDoEff)
29−
28+ =<< untilFixedPoint (return . magicDoEffect) il'
30−
29+ untilFixedPoint (return . collapseVarDeclarations) il''
31 where
30 where
32 tidyUp :: AST -> AST
31 tidyUp :: AST -> AST
33 tidyUp = applyAll
32 tidyUp = applyAll
34 [ collapseNestedBlocks
33 [ collapseNestedBlocks
35− -- , collapseNestedIfs
34+ , collapseNestedIfs
36 , collapseIfChecks
35 , collapseIfChecks
37 , removeCodeAfterReturnStatements
36 , removeCodeAfterReturnStatements
38 , unThunk
37 , unThunk
@@ -48,34 +47,6 @@ untilFixedPoint f = go
48 a' <- f a
47 a' <- f a
49 if a' == a then return a' else go a'
48 if a' == a then return a' else go a'
50
49
51−-- | Overridden from CoreImp
52−
53−inlineUnsafePartial :: AST -> AST
54−inlineUnsafePartial = everywhereTopDown convert where
55− convert (App ss (Indexer _ (Var _ unsafePartial) (Var _ partialUnsafe)) [ comp ])
56− | unsafePartial == C.unsafePartial && partialUnsafe == C.partialUnsafe
57− -- Apply to undefined here, the application should be optimized away
58− -- if it is safe to do so
59− = App ss comp [ Var ss C.undefined ]
60− convert other = other
61−
62−inlineUnsafeCoerce :: AST -> AST
63−inlineUnsafeCoerce = everywhereTopDown convert where
64− convert (App _ (Indexer _ (Var _ unsafeCoerceFn) (Var _ unsafeCoerce)) [ comp ])
65− | unsafeCoerceFn == C.unsafeCoerceFn && unsafeCoerce == C.unsafeCoerce
66− = comp
67− convert other = other
68−
69−inlineApply :: AST -> AST
70−inlineApply = everywhereTopDown convert where
71− convert (App ss (App _ (Indexer _ (Var _ apply) (Var _ dataFunction)) [ f ]) [ arg ])
72− | apply == C.apply && dataFunction == C.dataFunction
73− = App ss f [ arg ]
74− convert (App ss (App _ (Indexer _ (Var _ applyFlipped) (Var _ dataFunction)) [ arg ]) [ f ])
75− | applyFlipped == C.applyFlipped && dataFunction == C.dataFunction
76− = App ss f [ arg ]
77− convert other = other
78−
79collapseIfChecks :: AST -> AST
50collapseIfChecks :: AST -> AST
80collapseIfChecks = everywhere collapse where
51collapseIfChecks = everywhere collapse where
81 collapse :: AST -> AST
52 collapse :: AST -> AST
@@ -84,57 +55,27 @@ collapseIfChecks = everywhere collapse where
84 | prop == "otherwise" && val == "Data_Boolean" = exprs
55 | prop == "otherwise" && val == "Data_Boolean" = exprs
85 collapse exprs = exprs
56 collapse exprs = exprs
86
57
87−magicDo :: AST -> AST
58+collapseVarDeclarations :: AST -> AST
88−magicDo = magicDo'' C.eff C.effDictionaries
59+collapseVarDeclarations = everywhere collapse where
89−
90−magicDo' :: AST -> AST
60+ collapse :: AST -> AST
91−magicDo' = magicDo'' C.effect C.effectDictionaries
61+ collapse (Block ss sts) = Block ss (go Nothing sts)
92−
62+ collapse s = s
93−magicDo'' :: Text -> C.EffectDictionaries -> AST -> AST
63+
94−magicDo'' effectModule C.EffectDictionaries{..} = everywhereTopDown convert
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
70+
71+ignoreUnusedResults :: AST -> AST
72+ignoreUnusedResults = everywhere $ removeFromBlock go
95 where
73 where
96− -- Desugar monomorphic calls to >>= and return for the Eff monad
74+ go :: [AST] -> [AST]
97− convert :: AST -> AST
75+ go [] = []
98− -- Desugar pure
99− convert (App _ (App _ pure' [val]) []) | isPure pure' = val
100− -- Desugar discard
101− convert (App _ (App _ bind [m]) [Function s1 Nothing [unused] (Block s2 il)]) | isDiscard bind && unused == unusedName =
102− Function s1 Nothing [] $ Block s2 (App s2 m [] : map applyReturns il )
103− -- Desugar bind
104− convert (App _ (App _ bind [m]) [Function s1 Nothing [arg] (Block s2 il)]) | isBind bind =
105− Function s1 Nothing [] $ Block s2 (VariableIntroduction s2 arg (Just (App s2 m [])) : map applyReturns il)
76+ go (VariableIntroduction ss var (Just s) : sts)
106− -- Inline double applications
77+ | not $ any (everything (||) (isUsed var)) sts = sts'
107− convert (App _ (App s1 (Function s2 Nothing [] (Block ss body)) []) []) =
78+ where
108− App s1 (Function s2 Nothing [] (Block ss (applyReturns `fmap` body))) []
79+ sts' | App{} <- s = s : (go sts)
109− convert other = other
80+ | otherwise = go sts
110−
81+ go (s:sts) = s : go sts
111− -- Check if an expression represents a monomorphic call to >>= for the Eff monad
112− isBind (App _ fn [dict]) | isDict (effectModule, edBindDict) dict && isBindPoly fn = True
113− isBind _ = False
114− -- Check if an expression represents a call to @discard@
115− isDiscard (App _ (App _ fn [dict1]) [dict2])
116− | isDict (C.controlBind, C.discardUnitDictionary) dict1 &&
117− isDict (effectModule, edBindDict) dict2 &&
118− isDiscardPoly fn = True
119− isDiscard _ = False
120− -- Check if an expression represents a monomorphic call to pure or return for the Eff applicative
121− isPure (App _ fn [dict]) | isDict (effectModule, edApplicativeDict) dict && isPurePoly fn = True
122− isPure _ = False
123− -- Check if an expression represents the polymorphic >>= function
124− isBindPoly = isDict (C.controlBind, C.bind)
125− -- Check if an expression represents the polymorphic pure function
126− isPurePoly = isDict (C.controlApplicative, C.pure')
127− -- Check if an expression represents the polymorphic discard function
128− isDiscardPoly = isDict (C.controlBind, C.discard)
129−
130−
131− applyReturns :: AST -> AST
132− applyReturns (Return ss ret) = Return ss (App ss ret [])
133− applyReturns (Block ss ils) = Block ss (map applyReturns ils)
134− applyReturns (IfElse ss cond t f) = IfElse ss cond (applyReturns t) (applyReturns `fmap` f)
135− applyReturns other = other
136−
137− isDict :: (Text, PSString) -> AST -> Bool
138− isDict (moduleName, dictName) (Indexer _ (Var _ x) (Var _ y)) =
139− Just x == decodeString dictName && y == moduleName
140− isDict _ _ = False
src/CodeGen/IL/Optimizer/Common.hsnew file+18
@@ -0,0 +1,18 @@
1+-- | Common functions used by the various optimizer phases
2+module CodeGen.IL.Optimizer.Common where
3+
4+import Prelude.Compat
5+
6+import Data.Text (Text)
7+
8+import Language.PureScript.CoreImp.AST
9+import Language.PureScript.PSString (PSString, decodeString)
10+
11+isDict :: (Text, PSString) -> AST -> Bool
12+isDict (moduleName, dictName) (Indexer _ (Var _ x) (Var _ y)) =
13+ Just x == decodeString dictName && y == moduleName
14+isDict _ _ = False
15+
16+isDict' :: [(Text, PSString)] -> AST -> Bool
17+isDict' xs il = any (`isDict` il) xs
18+
src/CodeGen/IL/Optimizer/Inliner.hsnew file+435
@@ -0,0 +1,435 @@
1+-- | This module performs basic inlining of known functions
2+module CodeGen.IL.Optimizer.Inliner
3+ ( inlineCommonValues
4+ , inlineCommonOperators
5+ , inlineFnComposition
6+ , inlineUnsafeCoerce
7+ , inlineUnsafePartial
8+ ) where
9+
10+import Prelude.Compat
11+
12+import Control.Monad.Supply.Class (MonadSupply)
13+
14+import Data.Either (rights)
15+import Data.Maybe (fromMaybe)
16+import Data.String (IsString, fromString)
17+import Data.Text (Text)
18+import qualified Data.Text as T
19+
20+import Language.PureScript.PSString (PSString, decodeString, mkString)
21+import Language.PureScript.CoreImp.AST
22+import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict, isDict')
23+import Language.PureScript.AST (SourceSpan(..))
24+import qualified Language.PureScript.Constants as C
25+
26+import CodeGen.IL.Common
27+import CodeGen.IL.Optimizer.Common
28+
29+-- TODO: Potential bug:
30+-- Shouldn't just inline this case: { var x = 0; x.toFixed(10); }
31+-- Needs to be: { 0..toFixed(10); }
32+-- Probably needs to be fixed in pretty-printer instead.
33+shouldInline :: AST -> Bool
34+shouldInline (Var _ _) = True
35+shouldInline (NumericLiteral _ _) = True
36+shouldInline (StringLiteral _ _) = True
37+shouldInline (BooleanLiteral _ _) = True
38+shouldInline (Indexer _ index val) = shouldInline index && shouldInline val
39+shouldInline _ = False
40+
41+inlineCommonValues :: AST -> AST
42+inlineCommonValues = everywhere convert
43+ where
44+ convert :: AST -> AST
45+ convert (App ss fn [dict])
46+ | isDict' [semiringNumber, semiringInt] dict && isDict fnZero fn = NumericLiteral ss (Left 0)
47+ | isDict' [semiringNumber, semiringInt] dict && isDict fnOne fn = NumericLiteral ss (Left 1)
48+ | isDict boundedBoolean dict && isDict fnBottom fn = BooleanLiteral ss False
49+ | isDict boundedBoolean dict && isDict fnTop fn = BooleanLiteral ss True
50+ convert (App ss (App _ fn [dict]) [x])
51+ | isDict ringInt dict && isDict fnNegate fn = Unary ss Negate (unbox x $ snd ringInt)
52+ convert (App ss (App _ (App _ fn [dict]) [x]) [y])
53+ | isDict semiringInt dict && isDict fnAdd fn = intOp ss Add semiringInt x y
54+ | isDict semiringInt dict && isDict fnMultiply fn = intOp ss Multiply semiringInt x y
55+ | isDict ringInt dict && isDict fnSubtract fn = intOp ss Subtract ringInt x y
56+ convert other = other
57+ fnZero = (C.dataSemiring, C.zero)
58+ fnOne = (C.dataSemiring, C.one)
59+ fnBottom = (C.dataBounded, C.bottom)
60+ fnTop = (C.dataBounded, C.top)
61+ fnAdd = (C.dataSemiring, C.add)
62+ fnMultiply = (C.dataSemiring, C.mul)
63+ fnSubtract = (C.dataRing, C.sub)
64+ fnNegate = (C.dataRing, C.negate)
65+ intOp ss op (_, d) x y = Binary ss op (unbox x d) (unbox y d)
66+
67+inlineCommonOperators :: AST -> AST
68+inlineCommonOperators = everywhereTopDown $ applyAll $
69+ [ binary semiringNumber opAdd Add
70+ , binary semiringNumber opMul Multiply
71+
72+ , binary ringNumber opSub Subtract
73+ , unary ringNumber opNegate Negate
74+
75+ , binary euclideanRingNumber opDiv Divide
76+
77+ -- , binary eqNumber opEq EqualTo
78+ -- , binary eqNumber opNotEq NotEqualTo
79+ , binary eqInt opEq EqualTo
80+ , binary eqInt opNotEq NotEqualTo
81+ , binary eqString opEq EqualTo
82+ , binary eqString opNotEq NotEqualTo
83+ , binary eqChar opEq EqualTo
84+ , binary eqChar opNotEq NotEqualTo
85+ , binary eqBoolean opEq EqualTo
86+ , binary eqBoolean opNotEq NotEqualTo
87+
88+ , binary ordBoolean opLessThan LessThan
89+ , binary ordBoolean opLessThanOrEq LessThanOrEqualTo
90+ , binary ordBoolean opGreaterThan GreaterThan
91+ , binary ordBoolean opGreaterThanOrEq GreaterThanOrEqualTo
92+ , binary ordChar opLessThan LessThan
93+ , binary ordChar opLessThanOrEq LessThanOrEqualTo
94+ , binary ordChar opGreaterThan GreaterThan
95+ , binary ordChar opGreaterThanOrEq GreaterThanOrEqualTo
96+ , binary ordInt opLessThan LessThan
97+ , binary ordInt opLessThanOrEq LessThanOrEqualTo
98+ , binary ordInt opGreaterThan GreaterThan
99+ , binary ordInt opGreaterThanOrEq GreaterThanOrEqualTo
100+ , binary ordNumber opLessThan LessThan
101+ , binary ordNumber opLessThanOrEq LessThanOrEqualTo
102+ , binary ordNumber opGreaterThan GreaterThan
103+ , binary ordNumber opGreaterThanOrEq GreaterThanOrEqualTo
104+ , binary ordString opLessThan LessThan
105+ , binary ordString opLessThanOrEq LessThanOrEqualTo
106+ , binary ordString opGreaterThan GreaterThan
107+ , binary ordString opGreaterThanOrEq GreaterThanOrEqualTo
108+
109+ , binary semigroupString opAppend Add
110+
111+ , binary heytingAlgebraBoolean opConj And
112+ , binary heytingAlgebraBoolean opDisj Or
113+ , unary heytingAlgebraBoolean opNot Not
114+
115+ , binary' C.dataIntBits C.or BitwiseOr
116+ , binary' C.dataIntBits C.and BitwiseAnd
117+ , binary' C.dataIntBits C.xor BitwiseXor
118+ , binary' C.dataIntBits C.shl ShiftLeft
119+ , binary' C.dataIntBits C.shr ShiftRight
120+ , unary' C.dataIntBits C.complement BitwiseNot
121+
122+ , inlineNonClassFunction (isModFn (C.dataFunction, C.apply)) $ \f x -> App Nothing f [x]
123+ , inlineNonClassFunction (isModFn (C.dataFunction, C.applyFlipped)) $ \x f -> App Nothing f [x]
124+ , inlineUnsafeIndex (isModFnWithDict (C.dataArray, C.unsafeIndex)) $ flip (Indexer Nothing)
125+ ] ++
126+ [ fn | i <- [0..10], fn <- [ mkFn i, runFn i ] ] ++
127+ [ fn | i <- [0..10], fn <- [ mkEffFn C.controlMonadEffUncurried C.mkEffFn i, runEffFn C.controlMonadEffUncurried C.runEffFn i ] ] ++
128+ [ fn | i <- [0..10], fn <- [ mkEffFn C.effectUncurried C.mkEffectFn i, runEffFn C.effectUncurried C.runEffectFn i ] ]
129+ where
130+ binary :: (Text, PSString) -> (Text, PSString) -> BinaryOperator -> AST -> AST
131+ binary dict@(_, d) fns op = convert where
132+ convert :: AST -> AST
133+ convert (App ss (App _ (App _ fn [dict']) [x]) [y])
134+ | isDict dict dict' && isDict fns fn = Binary ss op (unbox x d) (unbox y d)
135+ convert other = other
136+ binary' :: Text -> PSString -> BinaryOperator -> AST -> AST
137+ binary' moduleName opString op = convert where
138+ convert :: AST -> AST
139+ convert (App ss (App _ fn [x]) [y]) | isDict (moduleName, opString) fn = Binary ss op (unbox x mn) (unbox y mn)
140+ convert other = other
141+ mn :: PSString
142+ mn = mkString moduleName
143+ unary :: (Text, PSString) -> (Text, PSString) -> UnaryOperator -> AST -> AST
144+ unary dicts@(_, d) fns op = convert where
145+ convert :: AST -> AST
146+ convert (App ss (App _ fn [dict']) [x]) | isDict dicts dict' && isDict fns fn = Unary ss op (unbox x d)
147+ convert other = other
148+ unary' :: Text -> PSString -> UnaryOperator -> AST -> AST
149+ unary' moduleName fnName op = convert where
150+ convert :: AST -> AST
151+ convert (App ss fn [x]) | isDict (moduleName, fnName) fn = Unary ss op (unbox x fnName)
152+ convert other = other
153+
154+ mkFn :: Int -> AST -> AST
155+ mkFn = mkFn' C.dataFunctionUncurried C.mkFn $ \ss1 ss2 ss3 args il ->
156+ Function ss1 Nothing args (Block ss2 [Return ss3 il])
157+
158+ mkEffFn :: Text -> Text -> Int -> AST -> AST
159+ mkEffFn modName fnName = mkFn' modName fnName $ \ss1 ss2 ss3 args il ->
160+ Function ss1 Nothing args (Block ss2 [Return ss3 (App ss3 il [])])
161+
162+ mkFn' :: Text -> Text -> (Maybe SourceSpan -> Maybe SourceSpan -> Maybe SourceSpan -> [Text] -> AST -> AST) -> Int -> AST -> AST
163+ mkFn' modName fnName res 0 = convert where
164+ convert :: AST -> AST
165+ convert (App _ mkFnN [Function s1 Nothing [_] (Block s2 [Return s3 il])]) | isNFn modName fnName 0 mkFnN =
166+ res s1 s2 s3 [] il
167+ convert other = other
168+ mkFn' modName fnName res n = convert where
169+ convert :: AST -> AST
170+ convert orig@(App ss mkFnN [fn]) | isNFn modName fnName n mkFnN =
171+ case collectArgs n [] fn of
172+ Just (args, [Return ss' ret]) -> res ss ss ss' args ret
173+ _ -> orig
174+ convert other = other
175+ collectArgs :: Int -> [Text] -> AST -> Maybe ([Text], [AST])
176+ collectArgs 1 acc (Function _ Nothing [oneArg] (Block _ il)) | length acc == n - 1 = Just (reverse (oneArg : acc), il)
177+ collectArgs m acc (Function _ Nothing [oneArg] (Block _ [Return _ ret])) = collectArgs (m - 1) (oneArg : acc) ret
178+ collectArgs _ _ _ = Nothing
179+
180+ isNFn :: Text -> Text -> Int -> AST -> Bool
181+ isNFn expectMod prefix n (Indexer _ (Var _ name) (Var _ modName)) | modName == expectMod =
182+ name == fromString (T.unpack prefix <> show n)
183+ isNFn _ _ _ _ = False
184+
185+ runFn :: Int -> AST -> AST
186+ runFn = runFn' C.dataFunctionUncurried C.runFn (\ss f args ->
187+ let len = length args
188+ typ = mkString $ uncurriedFnType len in
189+ if len > 0
190+ then App ss (App Nothing (StringLiteral Nothing typ) [f]) args
191+ else App ss f args)
192+ runEffFn :: Text -> Text -> Int -> AST -> AST
193+ runEffFn modName fnName = runFn' modName fnName $ \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)])
196+
197+ runFn' :: Text -> Text -> (Maybe SourceSpan -> AST -> [AST] -> AST) -> Int -> AST -> AST
198+ runFn' modName runFnName res n = convert where
199+ convert :: AST -> AST
200+ convert il = fromMaybe il $ go n [] il
201+
202+ go :: Int -> [AST] -> AST -> Maybe AST
203+ go 0 acc (App ss runFnN [fn]) | isNFn modName runFnName n runFnN && length acc == n =
204+ Just $ res ss fn acc
205+ go m acc (App _ lhs [arg]) = go (m - 1) (arg : acc) lhs
206+ go _ _ _ = Nothing
207+
208+ inlineNonClassFunction :: (AST -> Bool) -> (AST -> AST -> AST) -> AST -> AST
209+ inlineNonClassFunction p f = convert where
210+ convert :: AST -> AST
211+ convert (App _ (App _ op' [x]) [y]) | p op' = f x y
212+ convert other = other
213+
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+
223+ isModFn :: (Text, PSString) -> AST -> Bool
224+ isModFn (m, op) (Indexer _ (Var _ op') (Var _ m')) =
225+ m == m' && decodeString op == Just op'
226+ isModFn _ _ = False
227+
228+ isModFnWithDict :: (Text, PSString) -> AST -> Bool
229+ isModFnWithDict (m, op) (App _ (Indexer _ (Var _ op') (Var _ m')) [Var _ _]) =
230+ m == m' && decodeString op == Just op'
231+ isModFnWithDict _ _ = False
232+
233+-- (f <<< g $ x) = f (g x)
234+-- (f <<< g) = \x -> f (g x)
235+inlineFnComposition :: forall m. MonadSupply m => AST -> m AST
236+inlineFnComposition = everywhereTopDownM convert where
237+ convert :: AST -> m AST
238+ convert (App s1 (App s2 (App _ (App _ fn [dict']) [x]) [y]) [z])
239+ | isFnCompose dict' fn = return $ App s1 x [App s2 y [z]]
240+ | isFnComposeFlipped dict' fn = return $ App s2 y [App s1 x [z]]
241+ convert app@(App ss (App _ (App _ fn [dict']) _) _)
242+ | isFnCompose dict' fn || isFnComposeFlipped dict' fn = mkApps ss <$> goApps app <*> freshName'
243+ convert other = return other
244+
245+ mkApps :: Maybe SourceSpan -> [Either AST (Text, AST)] -> Text -> AST
246+ mkApps ss fns a = App ss (Function ss Nothing [] (Block ss $ vars <> [Return Nothing comp])) []
247+ where
248+ vars = uncurry (VariableIntroduction ss) . fmap Just <$> rights fns
249+ comp = Function ss Nothing [a] (Block ss [Return Nothing apps])
250+ apps = foldr (\fn acc -> App ss (mkApp fn) [acc]) (Var ss a) fns
251+
252+ mkApp :: Either AST (Text, AST) -> AST
253+ mkApp = either id $ \(name, arg) -> Var (getSourceSpan arg) name
254+
255+ goApps :: AST -> m [Either AST (Text, AST)]
256+ goApps (App _ (App _ (App _ fn [dict']) [x]) [y])
257+ | isFnCompose dict' fn = mappend <$> goApps x <*> goApps y
258+ | isFnComposeFlipped dict' fn = mappend <$> goApps y <*> goApps x
259+ goApps app@(App {}) = pure . Right . (,app) <$> freshName'
260+ goApps other = pure [Left other]
261+
262+ isFnCompose :: AST -> AST -> Bool
263+ isFnCompose dict' fn = isDict semigroupoidFn dict' && isDict fnCompose fn
264+
265+ isFnComposeFlipped :: AST -> AST -> Bool
266+ isFnComposeFlipped dict' fn = isDict semigroupoidFn dict' && isDict fnComposeFlipped fn
267+
268+ fnCompose :: forall a b. (IsString a, IsString b) => (a, b)
269+ fnCompose = (C.controlSemigroupoid, C.compose)
270+
271+ fnComposeFlipped :: forall a b. (IsString a, IsString b) => (a, b)
272+ fnComposeFlipped = (C.controlSemigroupoid, C.composeFlipped)
273+
274+inlineUnsafeCoerce :: AST -> AST
275+inlineUnsafeCoerce = everywhereTopDown convert where
276+ convert (App _ (Indexer _ (Var _ unsafeCoerceFn) (Var _ unsafeCoerce)) [ comp ])
277+ | unsafeCoerceFn == C.unsafeCoerceFn && unsafeCoerce == C.unsafeCoerce
278+ = comp
279+ convert other = other
280+
281+inlineUnsafePartial :: AST -> AST
282+inlineUnsafePartial = everywhereTopDown convert where
283+ convert (App ss fn [ comp ])
284+ | isDict fnUnsafePartial fn
285+ = App ss comp []
286+ where
287+ fnUnsafePartial :: forall a b. (IsString a, IsString b) => (a, b)
288+ fnUnsafePartial = (C.partialUnsafe, C.unsafePartial)
289+ convert other = other
290+
291+semiringNumber :: forall a b. (IsString a, IsString b) => (a, b)
292+semiringNumber = (C.dataSemiring, C.semiringNumber)
293+
294+semiringInt :: forall a b. (IsString a, IsString b) => (a, b)
295+semiringInt = (C.dataSemiring, C.semiringInt)
296+
297+ringNumber :: forall a b. (IsString a, IsString b) => (a, b)
298+ringNumber = (C.dataRing, C.ringNumber)
299+
300+ringInt :: forall a b. (IsString a, IsString b) => (a, b)
301+ringInt = (C.dataRing, C.ringInt)
302+
303+euclideanRingNumber :: forall a b. (IsString a, IsString b) => (a, b)
304+euclideanRingNumber = (C.dataEuclideanRing, C.euclideanRingNumber)
305+
306+-- eqNumber :: forall a b. (IsString a, IsString b) => (a, b)
307+-- eqNumber = (C.dataEq, C.eqNumber)
308+
309+eqInt :: forall a b. (IsString a, IsString b) => (a, b)
310+eqInt = (C.dataEq, C.eqInt)
311+
312+eqString :: forall a b. (IsString a, IsString b) => (a, b)
313+eqString = (C.dataEq, C.eqString)
314+
315+eqChar :: forall a b. (IsString a, IsString b) => (a, b)
316+eqChar = (C.dataEq, C.eqChar)
317+
318+eqBoolean :: forall a b. (IsString a, IsString b) => (a, b)
319+eqBoolean = (C.dataEq, C.eqBoolean)
320+
321+ordBoolean :: forall a b. (IsString a, IsString b) => (a, b)
322+ordBoolean = (C.dataOrd, C.ordBoolean)
323+
324+ordNumber :: forall a b. (IsString a, IsString b) => (a, b)
325+ordNumber = (C.dataOrd, C.ordNumber)
326+
327+ordInt :: forall a b. (IsString a, IsString b) => (a, b)
328+ordInt = (C.dataOrd, C.ordInt)
329+
330+ordString :: forall a b. (IsString a, IsString b) => (a, b)
331+ordString = (C.dataOrd, C.ordString)
332+
333+ordChar :: forall a b. (IsString a, IsString b) => (a, b)
334+ordChar = (C.dataOrd, C.ordChar)
335+
336+semigroupString :: forall a b. (IsString a, IsString b) => (a, b)
337+semigroupString = (C.dataSemigroup, C.semigroupString)
338+
339+boundedBoolean :: forall a b. (IsString a, IsString b) => (a, b)
340+boundedBoolean = (C.dataBounded, C.boundedBoolean)
341+
342+heytingAlgebraBoolean :: forall a b. (IsString a, IsString b) => (a, b)
343+heytingAlgebraBoolean = (C.dataHeytingAlgebra, C.heytingAlgebraBoolean)
344+
345+semigroupoidFn :: forall a b. (IsString a, IsString b) => (a, b)
346+semigroupoidFn = (C.controlSemigroupoid, C.semigroupoidFn)
347+
348+opAdd :: forall a b. (IsString a, IsString b) => (a, b)
349+opAdd = (C.dataSemiring, C.add)
350+
351+opMul :: forall a b. (IsString a, IsString b) => (a, b)
352+opMul = (C.dataSemiring, C.mul)
353+
354+opEq :: forall a b. (IsString a, IsString b) => (a, b)
355+opEq = (C.dataEq, C.eq)
356+
357+opNotEq :: forall a b. (IsString a, IsString b) => (a, b)
358+opNotEq = (C.dataEq, C.notEq)
359+
360+opLessThan :: forall a b. (IsString a, IsString b) => (a, b)
361+opLessThan = (C.dataOrd, C.lessThan)
362+
363+opLessThanOrEq :: forall a b. (IsString a, IsString b) => (a, b)
364+opLessThanOrEq = (C.dataOrd, C.lessThanOrEq)
365+
366+opGreaterThan :: forall a b. (IsString a, IsString b) => (a, b)
367+opGreaterThan = (C.dataOrd, C.greaterThan)
368+
369+opGreaterThanOrEq :: forall a b. (IsString a, IsString b) => (a, b)
370+opGreaterThanOrEq = (C.dataOrd, C.greaterThanOrEq)
371+
372+opAppend :: forall a b. (IsString a, IsString b) => (a, b)
373+opAppend = (C.dataSemigroup, C.append)
374+
375+opSub :: forall a b. (IsString a, IsString b) => (a, b)
376+opSub = (C.dataRing, C.sub)
377+
378+opNegate :: forall a b. (IsString a, IsString b) => (a, b)
379+opNegate = (C.dataRing, C.negate)
380+
381+opDiv :: forall a b. (IsString a, IsString b) => (a, b)
382+opDiv = (C.dataEuclideanRing, C.div)
383+
384+opConj :: forall a b. (IsString a, IsString b) => (a, b)
385+opConj = (C.dataHeytingAlgebra, C.conj)
386+
387+opDisj :: forall a b. (IsString a, IsString b) => (a, b)
388+opDisj = (C.dataHeytingAlgebra, C.disj)
389+
390+opNot :: forall a b. (IsString a, IsString b) => (a, b)
391+opNot = (C.dataHeytingAlgebra, C.not)
392+
393+unbox :: AST -> PSString -> AST
394+unbox e d = select . inlineCommonValues $ inlineCommonOperators e
395+ where
396+ select :: AST -> AST
397+ select e' =
398+ case e' of
399+ Var{} -> unbox' e'
400+ App{} -> unbox' e'
401+ Indexer{} -> unbox' e'
402+ _ -> e'
403+ unbox' :: AST -> AST
404+ unbox' e' = maybe e' (\t -> App Nothing (StringLiteral Nothing t) [e']) $ unboxType d
405+
406+unboxType :: PSString -> Maybe PSString
407+unboxType s = do
408+ t <- decodeString s
409+ t' <- unboxType' t
410+ return $ mkString t'
411+ where
412+ unboxType' :: Text -> Maybe Text
413+ unboxType' t
414+ | t == C.eqInt ||
415+ t == C.semiringInt ||
416+ t == C.ringInt ||
417+ t == C.ordInt ||
418+ t == C.dataIntBits
419+ = Just int
420+ | t == C.semiringNumber ||
421+ t == C.ringNumber ||
422+ t == C.ordNumber ||
423+ t == C.euclideanRingNumber
424+ = Just float
425+ | t == C.eqBoolean ||
426+ t == C.ordBoolean ||
427+ t == C.heytingAlgebraBoolean
428+ = Just bool
429+ | t == C.eqChar ||
430+ t == C.eqString ||
431+ t == C.ordChar ||
432+ t == C.ordString ||
433+ t == C.semigroupString
434+ = Just string
435+ | otherwise = Nothing
src/CodeGen/IL/Optimizer/MagicDo.hsnew file+146
@@ -0,0 +1,146 @@
1+-- | This module implements the "Magic Do" optimization, which inlines calls to return
2+-- and bind for the Eff monad, as well as some of its actions.
3+module CodeGen.IL.Optimizer.MagicDo (magicDoEffect, magicDoEff, magicDoST, inlineST) where
4+
5+import Prelude.Compat
6+import Protolude (ordNub)
7+
8+import Data.Maybe (fromJust, isJust)
9+import Data.Text (Text)
10+
11+import Language.PureScript.CoreImp.AST
12+import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict)
13+import Language.PureScript.PSString (PSString, decodeString, mkString)
14+import qualified Language.PureScript.Constants as C
15+
16+import CodeGen.IL.Common (undefinedName, unusedName)
17+import CodeGen.IL.Optimizer.Common
18+
19+-- | Inline type class dictionaries for >>= and return for the Eff monad
20+--
21+-- E.g.
22+--
23+-- Prelude[">>="](dict)(m1)(function(x) {
24+-- return ...;
25+-- })
26+--
27+-- becomes
28+--
29+-- function __do {
30+-- var x = m1();
31+-- ...
32+-- }
33+magicDoEff :: AST -> AST
34+magicDoEff = magicDo C.eff C.effDictionaries
35+
36+magicDoEffect :: AST -> AST
37+magicDoEffect = magicDo C.effect C.effectDictionaries
38+
39+magicDoST :: AST -> AST
40+magicDoST = magicDo C.st C.stDictionaries
41+
42+magicDo :: Text -> C.EffectDictionaries -> AST -> AST
43+magicDo effectModule C.EffectDictionaries{..} = everywhereTopDown convert
44+ where
45+ -- Desugar monomorphic calls to >>= and return for the Eff monad
46+ convert :: AST -> AST
47+ -- Desugar pure
48+ convert (App _ (App _ pure' [val]) []) | isPure pure' = val
49+ -- Desugar discard
50+ convert (App _ (App _ bind [m]) [Function s1 Nothing [unused] (Block s2 il)]) | isDiscard bind && unused == unusedName =
51+ Function s1 Nothing [] $ Block s2 (App s2 m [] : map applyReturns il )
52+ -- Desugar bind
53+ convert (App _ (App _ bind [m]) [Function s1 Nothing [arg] (Block s2 il)]) | isBind bind =
54+ Function s1 Nothing [] $ Block s2 (VariableIntroduction s2 arg (Just (App s2 m [])) : map applyReturns il)
55+ -- Desugar untilE
56+ convert (App s1 (App _ f [arg]) []) | isEffFunc edUntil f =
57+ App s1 (Function s1 Nothing [] (Block s1 [ While s1 (Unary s1 Not (App s1 arg [])) (Block s1 []), Return s1 $ ObjectLiteral s1 []])) []
58+ -- Desugar whileE
59+ convert (App _ (App _ (App s1 f [arg1]) [arg2]) []) | isEffFunc edWhile f =
60+ App s1 (Function s1 Nothing [] (Block s1 [ While s1 (App s1 arg1 []) (Block s1 [ App s1 arg2 [] ]), Return s1 $ ObjectLiteral s1 []])) []
61+ -- -- Inline __do returns
62+ -- convert (Return _ (App _ (Function _ (Just ident) [] body) [])) | ident == fnName = body
63+ -- Inline double applications
64+ convert (App _ (App s1 (Function s2 Nothing [] (Block ss body)) []) []) =
65+ App s1 (Function s2 Nothing [] (Block ss (applyReturns `fmap` body))) []
66+ convert other = other
67+ -- Check if an expression represents a monomorphic call to >>= for the Eff monad
68+ isBind (App _ fn [dict]) | isDict (effectModule, edBindDict) dict && isBindPoly fn = True
69+ isBind _ = False
70+ -- Check if an expression represents a call to @discard@
71+ isDiscard (App _ (App _ fn [dict1]) [dict2])
72+ | isDict (C.controlBind, C.discardUnitDictionary) dict1 &&
73+ isDict (effectModule, edBindDict) dict2 &&
74+ isDiscardPoly fn = True
75+ isDiscard _ = False
76+
77+ -- Check if an expression represents a monomorphic call to pure or return for the Eff applicative
78+ isPure (App _ fn [dict]) | isDict (effectModule, edApplicativeDict) dict && isPurePoly fn = True
79+ isPure _ = False
80+ -- Check if an expression represents the polymorphic >>= function
81+ isBindPoly = isDict (C.controlBind, C.bind)
82+ -- Check if an expression represents the polymorphic pure function
83+ isPurePoly = isDict (C.controlApplicative, C.pure')
84+ -- Check if an expression represents the polymorphic discard function
85+ isDiscardPoly = isDict (C.controlBind, C.discard)
86+ -- Check if an expression represents a function in the Effect module
87+ isEffFunc name (Indexer _ (StringLiteral _ name') (Var _ eff)) = eff == effectModule && name == name'
88+ isEffFunc _ _ = False
89+
90+ applyReturns :: AST -> AST
91+ applyReturns (Return ss ret) = Return ss (App ss ret [])
92+ applyReturns (Block ss ils) = Block ss (map applyReturns ils)
93+ applyReturns (While ss cond il) = While ss cond (applyReturns il)
94+ applyReturns (For ss v lo hi il) = For ss v lo hi (applyReturns il)
95+ applyReturns (ForIn ss v xs il) = ForIn ss v xs (applyReturns il)
96+ applyReturns (IfElse ss cond t f) = IfElse ss cond (applyReturns t) (applyReturns `fmap` f)
97+ applyReturns other = other
98+
99+-- | Inline functions in the ST module
100+inlineST :: AST -> AST
101+inlineST = everywhere convertBlock
102+ where
103+ -- Look for runST blocks and inline the STRefs there.
104+ -- If all STRefs are used in the scope of the same runST, only using { read, write, modify }STRef then
105+ -- we can be more aggressive about inlining, and actually turn STRefs into local variables.
106+ convertBlock (App s1 f [arg]) | isSTFunc C.runST f =
107+ let refs = ordNub . findSTRefsIn $ arg
108+ usages = findAllSTUsagesIn arg
109+ allUsagesAreLocalVars = all (\u -> let v = toVar u in isJust v && fromJust v `elem` refs) usages
110+ localVarsDoNotEscape = all (\r -> length (r `appearingIn` arg) == length (filter (\u -> let v = toVar u in v == Just r) usages)) refs
111+ in App s1 (everywhere (convert (allUsagesAreLocalVars && localVarsDoNotEscape)) arg) []
112+ convertBlock other = other
113+ -- Convert a block in a safe way, preserving object wrappers of references,
114+ -- or in a more aggressive way, turning wrappers into local variables depending on the
115+ -- agg(ressive) parameter.
116+ convert agg (App s1 f [arg]) | isSTFunc C.newSTRef f =
117+ Function s1 Nothing [] (Block s1 [Return s1 $ if agg then arg else ObjectLiteral s1 [(mkString C.stRefValue, arg)]])
118+ convert agg (App _ (App s1 f [ref]) []) | isSTFunc C.readSTRef f =
119+ if agg then ref else Indexer s1 (StringLiteral s1 C.stRefValue) ref
120+ convert agg (App _ (App _ (App s1 f [arg]) [ref]) []) | isSTFunc C.writeSTRef f =
121+ if agg then Assignment s1 ref arg else Assignment s1 (Indexer s1 (StringLiteral s1 C.stRefValue) ref) arg
122+ convert agg (App _ (App _ (App s1 f [func]) [ref]) []) | isSTFunc C.modifySTRef f =
123+ if agg then Assignment s1 ref (App s1 func [ref]) else Assignment s1 (Indexer s1 (StringLiteral s1 C.stRefValue) ref) (App s1 func [Indexer s1 (StringLiteral s1 C.stRefValue) ref])
124+ convert _ other = other
125+ -- Check if an expression represents a function in the ST module
126+ isSTFunc name (Indexer _ (StringLiteral _ name') (Var _ st)) = st == C.st && name == name'
127+ isSTFunc _ _ = False
128+ -- Find all ST Refs initialized in this block
129+ findSTRefsIn = everything (++) isSTRef
130+ where
131+ isSTRef (VariableIntroduction _ ident (Just (App _ (App _ f [_]) []))) | isSTFunc C.newSTRef f = [ident]
132+ isSTRef _ = []
133+ -- Find all STRefs used as arguments to readSTRef, writeSTRef, modifySTRef
134+ findAllSTUsagesIn = everything (++) isSTUsage
135+ where
136+ isSTUsage (App _ (App _ f [ref]) []) | isSTFunc C.readSTRef f = [ref]
137+ isSTUsage (App _ (App _ (App _ f [_]) [ref]) []) | isSTFunc C.writeSTRef f || isSTFunc C.modifySTRef f = [ref]
138+ isSTUsage _ = []
139+ -- Find all uses of a variable
140+ appearingIn ref = everything (++) isVar
141+ where
142+ isVar e@(Var _ v) | v == ref = [e]
143+ isVar _ = []
144+ -- Convert a AST value to a String if it is a Var
145+ toVar (Var _ v) = Just v
146+ toVar _ = Nothing
src/CodeGen/IL/Optimizer/TCO.hs+15−17
@@ -1,4 +1,4 @@
1−module CodeGen.IL.Optimizer.TCO (tco) where
1+module CodeGen.IL.Optimizer.TCO (tco, tcoLoop) where
2
2
3import Prelude.Compat
3import Prelude.Compat
4
4
@@ -11,19 +11,22 @@ import CodeGen.IL.Common
11
11
12import qualified Language.PureScript.Constants as C
12import qualified Language.PureScript.Constants as C
13
13
14+tcoLoop :: Text
15+tcoLoop = "ṩtco_loop"
16+
14tco :: AST -> AST -> AST
17tco :: AST -> AST -> AST
15tco mn = everywhere convert where
18tco mn = everywhere convert where
16 tcoVar :: Text -> Text
19 tcoVar :: Text -> Text
17− tcoVar arg = "_TCO_" <> arg <> "_"
20+ tcoVar arg = "ṩtco_var_" <> arg
18
21
19 copyVar :: Text -> Text
22 copyVar :: Text -> Text
20− copyVar arg = "_TCO_Copy_" <> arg <> "_"
23+ copyVar arg = "ṩcopy_" <> arg
21
24
22 tcoDone :: Text
25 tcoDone :: Text
23− tcoDone = "_TCO_Done_"
26+ tcoDone = "ṩtco_done"
24
27
25 tcoResult :: Text
28 tcoResult :: Text
26− tcoResult = "_TCO_Result_"
29+ tcoResult = "ṩtco_result"
27
30
28 convert :: AST -> AST
31 convert :: AST -> AST
29 convert fn@(Function ss (Just name) args _)
32 convert fn@(Function ss (Just name) args _)
@@ -37,7 +40,6 @@ tco mn = everywhere convert where
37 | isTailRecursive name body'
40 | isTailRecursive name body'
38 = Assignment ss (Var ss name) (replace (toLoop name outerArgs innerArgs body'))
41 = Assignment ss (Var ss name) (replace (toLoop name outerArgs innerArgs body'))
39 where
42 where
40− -- fn = Function ss' Nothing args (Block ss' stmts)
41 innerArgs = headDef [] argss
43 innerArgs = headDef [] argss
42 outerArgs = concat . reverse $ tailSafe argss
44 outerArgs = concat . reverse $ tailSafe argss
43 (argss, body', replace) = collectAllFunctionArgs [] id fn
45 (argss, body', replace) = collectAllFunctionArgs [] id fn
@@ -90,18 +92,14 @@ tco mn = everywhere convert where
90 toLoop :: Text -> [Text] -> [Text] -> AST -> AST
92 toLoop :: Text -> [Text] -> [Text] -> AST -> AST
91 toLoop ident outerArgs innerArgs js =
93 toLoop ident outerArgs innerArgs js =
92 Block rootSS $
94 Block rootSS $
93− concatMap (\arg -> [ VariableIntroduction rootSS (tcoVar arg) Nothing
95+ concatMap (\arg -> [ VariableIntroduction rootSS (tcoVar arg) . Just $
94− , Assignment rootSS (Var rootSS (tcoVar arg)) (Var rootSS (copyVar arg)) ]) (outerArgs ++ innerArgs) ++
96+ Var rootSS (copyVar arg) ]) (outerArgs ++ innerArgs) ++
95− -- [ VariableIntroduction rootSS (blockDecl <> tcoDone) Nothing
97+ [ Assignment rootSS (Var rootSS $ "var " <> tcoDone) $ BooleanLiteral Nothing False
96− [ Var rootSS ("bool " <> tcoDone <> " = false")
97− -- , Assignment rootSS (Var rootSS tcoDone) (BooleanLiteral rootSS False)
98− -- , Assignment rootSS (Var rootSS tcoDone) (Var rootSS "NO")
99 , VariableIntroduction rootSS tcoResult Nothing
98 , VariableIntroduction rootSS tcoResult Nothing
100− , Assignment rootSS (Var rootSS ("const auto " <> tcoLoop)) (Function rootSS (Just tcoLoop) (outerArgs ++ innerArgs) (Block rootSS [loopify js]))
99+ , Assignment rootSS (Var rootSS $ "var " <> tcoLoop) (Function rootSS (Just tcoLoop) (outerArgs ++ innerArgs) (Block rootSS [loopify js]))
101− -- , While rootSS (Binary Nothing EqualTo (Var rootSS tcoDone) (BooleanLiteral Nothing False))
102 , While rootSS (Unary Nothing Not (Var rootSS tcoDone))
100 , While rootSS (Unary Nothing Not (Var rootSS tcoDone))
103 (Block rootSS
101 (Block rootSS
104− [(Assignment rootSS (Var rootSS tcoResult) (App rootSS (Var rootSS tcoLoop) ((map (Var rootSS . tcoVar) outerArgs) ++ (map (Var rootSS . tcoVar) innerArgs))))])
102+ [(Assignment rootSS (Var rootSS tcoResult) (App rootSS (Var rootSS tcoLoop) ((map (Var rootSS . tcoVar) outerArgs) ++ (map (Var rootSS . copyVar) innerArgs))))])
105 , Return rootSS (Var rootSS tcoResult)
103 , Return rootSS (Var rootSS tcoResult)
106 ]
104 ]
107 where
105 where
@@ -117,7 +115,7 @@ tco mn = everywhere convert where
117 zipWith (\val arg ->
115 zipWith (\val arg ->
118 Assignment ss (Var ss (tcoVar arg)) val) allArgumentValues outerArgs
116 Assignment ss (Var ss (tcoVar arg)) val) allArgumentValues outerArgs
119 ++ zipWith (\val arg ->
117 ++ zipWith (\val arg ->
120− Assignment ss (Var ss (tcoVar arg)) val) (drop (length outerArgs) allArgumentValues) innerArgs
118+ Assignment ss (Var ss (copyVar arg)) val) (drop (length outerArgs) allArgumentValues) innerArgs
121 ++ [ ReturnNoResult ss ]
119 ++ [ ReturnNoResult ss ]
122 | otherwise = Block ss [ markDone ss, Return ss ret ]
120 | otherwise = Block ss [ markDone ss, Return ss ret ]
123 loopify (ReturnNoResult ss) = Block ss [ markDone ss, ReturnNoResult ss ]
121 loopify (ReturnNoResult ss) = Block ss [ markDone ss, ReturnNoResult ss ]
@@ -130,7 +128,6 @@ tco mn = everywhere convert where
130
128
131 markDone :: Maybe SourceSpan -> AST
129 markDone :: Maybe SourceSpan -> AST
132 markDone ss = Assignment ss (Var ss tcoDone) (BooleanLiteral ss True)
130 markDone ss = Assignment ss (Var ss tcoDone) (BooleanLiteral ss True)
133− -- markDone ss = Assignment ss (Var ss tcoDone) (Var ss "YES")
134
131
135 collectArgs :: [[AST]] -> AST -> [[AST]]
132 collectArgs :: [[AST]] -> AST -> [[AST]]
136 collectArgs acc (App _ fn args') = collectArgs (args' : acc) fn
133 collectArgs acc (App _ fn args') = collectArgs (args' : acc) fn
@@ -138,6 +135,7 @@ tco mn = everywhere convert where
138
135
139 isSelfCall :: Text -> AST -> Bool
136 isSelfCall :: Text -> AST -> Bool
140 isSelfCall ident (App _ (Var _ ident') _) = ident == ident'
137 isSelfCall ident (App _ (Var _ ident') _) = ident == ident'
138+ isSelfCall ident (App _ (Indexer _ (Var _ ident') (Var _ mn')) _) = mn' == "" && ident == ident'
141 isSelfCall ident (App _ (Indexer _ (Var _ ident') mn') _) = mn' == mn && ident == ident'
139 isSelfCall ident (App _ (Indexer _ (Var _ ident') mn') _) = mn' == mn && ident == ident'
142 isSelfCall ident (App _ fn _) = isSelfCall ident fn
140 isSelfCall ident (App _ fn _) = isSelfCall ident fn
143 isSelfCall _ _ = False
141 isSelfCall _ _ = False
src/CodeGen/IL/Printer.hs+221−209
@@ -7,6 +7,9 @@ module CodeGen.IL.Printer
7 , implHeaderSource
7 , implHeaderSource
8 , implFooterSource
8 , implFooterSource
9 , isLiteral
9 , isLiteral
10+ , modLabel
11+ , modPrefix
12+ , ffiLoader
10 ) where
13 ) where
11
14
12import Prelude.Compat
15import Prelude.Compat
@@ -15,7 +18,6 @@ import Control.Arrow ((<+>))
15import Control.Monad (forM, mzero)
18import Control.Monad (forM, mzero)
16import Control.Monad.State (StateT, evalStateT)
19import Control.Monad.State (StateT, evalStateT)
17import Control.PatternArrows
20import Control.PatternArrows
18−import Data.Char (isAscii)
19import Data.List (filter, (\\))
21import Data.List (filter, (\\))
20import qualified Control.Arrow as A
22import qualified Control.Arrow as A
21
23
@@ -31,7 +33,9 @@ import Language.PureScript.Crash
31import Language.PureScript.Names (Ident, runIdent)
33import Language.PureScript.Names (Ident, runIdent)
32import Language.PureScript.Pretty.Common
34import Language.PureScript.Pretty.Common
33import Language.PureScript.PSString (PSString, decodeString, mkString)
35import Language.PureScript.PSString (PSString, decodeString, mkString)
36+
34import CodeGen.IL.Common
37import CodeGen.IL.Common
38+import CodeGen.IL.Optimizer.TCO (tcoLoop)
35
39
36import qualified Language.PureScript.Constants as C
40import qualified Language.PureScript.Constants as C
37
41
@@ -49,26 +53,23 @@ literals = mkPattern' match'
49 match (BooleanLiteral _ True) = return $ emit "true"
53 match (BooleanLiteral _ True) = return $ emit "true"
50 match (BooleanLiteral _ False) = return $ emit "false"
54 match (BooleanLiteral _ False) = return $ emit "false"
51 match (ArrayLiteral _ xs) = mconcat <$> sequence
55 match (ArrayLiteral _ xs) = mconcat <$> sequence
52− [ return . emit $ arrayType <> "{ "
56+ [ return . emit $ arrayType <> "{"
53 , intercalate (emit ", ") <$> forM xs prettyPrintIL'
57 , intercalate (emit ", ") <$> forM xs prettyPrintIL'
54− , return $ emit " }"
58+ , return $ emit "}"
55 ]
59 ]
56 -- match (ObjectLiteral _ []) = return $ emit "std::initializer_list<std::pair<const string, boxed>>{}"
60 -- match (ObjectLiteral _ []) = return $ emit "std::initializer_list<std::pair<const string, boxed>>{}"
57 match (ObjectLiteral _ ps) = mconcat <$> sequence
61 match (ObjectLiteral _ ps) = mconcat <$> sequence
58− [ return . emit $ dictType <> "{\n"
62+ [ return . emit $ dictType <> "{"
59 , withIndent $ do
63 , withIndent $ do
60 ils <- forM ps $ \(key, value) -> do
64 ils <- forM ps $ \(key, value) -> do
61 value' <- prettyPrintIL' value
65 value' <- prettyPrintIL' value
62− return $ emit "{ " <> objectPropertyToString key <> value' <> emit " }"
66+ return $ objectPropertyToString key <> value'
63− indentString <- currentIndent
64− return $ intercalate (emit ",\n") $ map (indentString <>) ils
67+ return $ intercalate (emit " ") $ map (<> emit ",") ils
65− , return $ emit "\n"
66− , currentIndent
67 , return $ emit "}"
68 , return $ emit "}"
68 ]
69 ]
69 where
70 where
70 objectPropertyToString :: (Emit gen) => PSString -> gen
71 objectPropertyToString :: (Emit gen) => PSString -> gen
71− objectPropertyToString s = emit $ stringLiteral s <> ", "
72+ objectPropertyToString s = emit $ stringLiteral s <> ": "
72 match (Block _ sts) = mconcat <$> sequence
73 match (Block _ sts) = mconcat <$> sequence
73 [ return $ emit "{\n"
74 [ return $ emit "{\n"
74 , withIndent $ prettyStatements sts
75 , withIndent $ prettyStatements sts
@@ -76,9 +77,10 @@ literals = mkPattern' match'
76 , currentIndent
77 , currentIndent
77 , return $ emit "}"
78 , return $ emit "}"
78 ]
79 ]
80+ match (Var _ ident) | ident == C.undefined = return $ emit undefinedName
79 match (Var _ ident) = return $ emit ident
81 match (Var _ ident) = return $ emit ident
80 match (VariableIntroduction _ ident value) = mconcat <$> sequence
82 match (VariableIntroduction _ ident value) = mconcat <$> sequence
81− [ return $ emit $ "boxed " <> ident
83+ [ return . emit $ varDecl <> " " <> ident <> " " <> anyType
82 , maybe (return mempty) (fmap (emit " = " <>) . prettyPrintIL') value
84 , maybe (return mempty) (fmap (emit " = " <>) . prettyPrintIL') value
83 ]
85 ]
84 match (Assignment _ target value) = mconcat <$> sequence
86 match (Assignment _ target value) = mconcat <$> sequence
@@ -87,163 +89,195 @@ literals = mkPattern' match'
87 , prettyPrintIL' value
89 , prettyPrintIL' value
88 ]
90 ]
89 match (App _ val []) = mconcat <$> sequence
91 match (App _ val []) = mconcat <$> sequence
92+ [ return $ emit "Run("
90− [ prettyPrintIL' val
93+ , prettyPrintIL' val
91− , return $ emit "()"
94+ , return $ emit ")"
92 ]
95 ]
93− match (App ss (App _ (App _ (Indexer _ (Var _ fn) (Var _ fnMod)) [Indexer _ (Var _ dict) (Var _ dictMod)]) [x]) [y])
96+ match (App _ (StringLiteral _ u) [arg])
94− | fnMod == dictMod
97+ | Just ty <- decodeString u = mconcat <$> sequence
95− , dictMod == C.dataEq || dictMod == C.dataSemiring || dictMod == C.dataRing ||
98+ [ prettyPrintIL' arg
96− dictMod == C.dataEuclideanRing || dictMod == C.dataHeytingAlgebra
99+ , return $ emit ".("
97− , Just op <- renderOp fn
100+ , return $ emit ty
98− , Just t <- unboxType dict
101+ , return $ emit ")"
102+ ]
103+ match (App _ (Var _ fn) [arg]) | fn == arrayLengthFn = mconcat <$> sequence
104+ [ return $ emit fn
105+ , return $ emit "("
106+ , prettyPrintIL' arg
107+ , return $ emit ")"
108+ ]
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+ ]
115+ match (App _ (Var _ fn) args) | fn == tcoLoop = mconcat <$> sequence
116+ [ return $ emit fn
117+ , return $ emit "("
118+ , intercalate (emit ", ") <$> forM args prettyPrintIL'
119+ , return $ emit ")"
120+ ]
121+ match (App _ (App Nothing (StringLiteral Nothing fnx) [fn@Function{}]) args)
122+ | Just ftype <- decodeString fnx
99− = mconcat <$> sequence
123+ , "Fn" `T.isPrefixOf` ftype = mconcat <$> sequence
124+ [ prettyPrintIL' fn
100− [ return $ emit $ unbox' t x
125+ , return $ emit "("
101− , return $ emit op
126+ , intercalate (emit ", ") <$> forM args prettyPrintIL'
102− , return $ emit $ unbox' t y
127+ , return $ emit ")"
103− ]
128+ ]
104− match (App _ (App _ (Indexer _ (Var _ fn) (Var _ fnMod)) [Indexer _ (Var _ dict) (Var _ dictMod)]) [n])
129+ match (App _ (App Nothing (StringLiteral Nothing fnx) [fn]) args)
105− | fnMod == dictMod
130+ | Just ftype <- decodeString fnx
106− , dictMod == C.dataRing
107− , fn == C.negate
108− , Just t <- unboxType dict
109− = mconcat <$> sequence
131+ , "Fn" `T.isPrefixOf` ftype = mconcat <$> sequence
132+ [ prettyPrintIL' fn
110− [ return $ emit "-"
133+ , return $ emit ".("
111− , return $ emit $ unbox' t n
134+ , return $ emit ftype
112− ]
135+ , return $ emit ")("
113− match (App _ (App _ (Indexer _ (Var _ fn) (Var _ fnMod)) [Indexer _ (Var _ dict) (Var _ dictMod)]) [n])
136+ , intercalate (emit ", ") <$> forM args prettyPrintIL'
114− | fnMod == dictMod
137+ , return $ emit ")"
115− , dictMod == C.dataHeytingAlgebra
138+ ]
116− , fn == properToIL C.not
139+ match app@(App{})
117− , Just t <- unboxType dict
140+ | (val, args) <- unApp app []
141+ , length args > 1
118 = mconcat <$> sequence
142 = mconcat <$> sequence
119− [ return $ emit "!"
143+ [ return $ emit "Apply("
144+ , prettyPrintIL' val
120− , return $ emit $ unbox' t n
145+ , return $ emit ", "
146+ , intercalate (emit ", ") <$> forM args prettyPrintIL'
147+ , return $ emit ")"
121 ]
148 ]
122 match (App _ val args) = mconcat <$> sequence
149 match (App _ val args) = mconcat <$> sequence
150+ [ return $ emit "Apply("
123− [ prettyPrintIL' val
151+ , prettyPrintIL' val
124− , return $ emit "("
152+ , return $ emit ", "
125 , intercalate (emit ", ") <$> forM args prettyPrintIL'
153 , intercalate (emit ", ") <$> forM args prettyPrintIL'
126 , return $ emit ")"
154 , return $ emit ")"
127 ]
155 ]
128 match (Function _ (Just name) [] (Block _ [Return _ ret]))
156 match (Function _ (Just name) [] (Block _ [Return _ ret]))
129− | isLiteral ret = mconcat <$> sequence
157+ | isComplexLiteral ret = mconcat <$> sequence
130− [ return $ emit "auto "
158+ [ return $ emit "\n"
159+ , return . emit $ varDecl <> " " <> initName name <> " Once\n"
160+ , prettyPrintIL' $ VariableIntroduction Nothing (valueName name) Nothing
161+ , return $ emit "\n\n"
131− , return $ emit name
162+ , return $ emit "func "
163+ , return . emit $ withPrefix name
132− , return $ emit "() -> const boxed& "
164+ , return . emit $ "() " <> anyType <> " "
133 , return $ emit "{\n"
165 , return $ emit "{\n"
134 , withIndent $ do
166 , withIndent $ do
135− indentString <- currentIndent
167+ indentString <- currentIndent
136− return $ indentString <> emit staticValueBegin
168+ return $ indentString <> (emit $ initName name <> ".Do(func() {\n") <>
169+ indentString <> indentString <> (emit $ valueName name <> " = ")
137 , withIndent $ do
170 , withIndent $ do
138 case ret of
171 case ret of
139 Function _ Nothing args body -> mconcat <$> sequence
172 Function _ Nothing args body -> mconcat <$> sequence
140− [ return $ emit "[]("
173+ [ return $ emit "func("
141− , return $ emit $ intercalate ", " (renderArg <$> args)
174+ , return . emit $ intercalate ", " (renderArg anyType <$> args)
142− , return $ emit ") -> boxed "
175+ , return . emit $ ") " <> anyType <> " "
143 , prettyPrintIL' body
176 , prettyPrintIL' body
144 ]
177 ]
145 _ -> do
178 _ -> do
146 prettyPrintIL' ret
179 prettyPrintIL' ret
147− , return $ emit ";\n"
180+ , return $ emit "\n"
148 , withIndent $ do
181 , withIndent $ do
149− indentString <- currentIndent
182+ indentString <- currentIndent
150− return $ indentString <> emit staticValueEnd
183+ return $ indentString <> (emit $ "})\n" <> "return " <> valueName name <>"\n")
151− , return $ emit "\n}"
184+ , return $ emit "}\n\n"
152 ]
185 ]
153 match (Function _ (Just name) [] ret) = mconcat <$> sequence
186 match (Function _ (Just name) [] ret) = mconcat <$> sequence
154− [ return $ emit "auto "
187+ [ return $ emit "func "
155− , return $ emit name
188+ , return . emit $ withPrefix name
156− , return $ emit "() -> boxed "
189+ , return . emit $ "() " <> anyType <> " "
157 , prettyPrintIL' ret
190 , prettyPrintIL' ret
158 ]
191 ]
159− match (Function _ name args ret) = mconcat <$> sequence
192+ match (Function _ _ args ret) = mconcat <$> sequence
160− [ return $ emit captures
193+ [ return $ emit "func("
161− , return $ emit "("
162− , return $ emit $ intercalate ", " (render <$> args)
194+ , return . emit $ intercalate ", " (renderArg anyType <$> args)
163− , return $ emit ") -> boxed "
195+ , return . emit $ ") " <> anyType <> " "
164 , prettyPrintIL' ret
196 , prettyPrintIL' ret
165 ]
197 ]
166− where
198+ match (Indexer _ (Var _ name) (Var _ "")) = mconcat <$> sequence
167− (captures, render)
199+ [ prettyPrintIL' (Var Nothing $ withPrefix name)
168− | name == Just tcoLoop = ("[&]", renderArgByVal)
200+ , return $ emit "()"
169− | otherwise = ("[=]", renderArg)
201+ ]
170− match (Indexer _ prop@(Var _ name) val) = mconcat <$> sequence
202+ match (Indexer _ (Var _ name) val) = mconcat <$> sequence
171 [ prettyPrintIL' val
203 [ prettyPrintIL' val
172− , return $ emit "::"
204+ , return $ emit "."
173− , prettyPrintIL' prop
205+ , prettyPrintIL' (Var Nothing $ withPrefix name)
174 , return $ emit "()"
206 , return $ emit "()"
175 ]
207 ]
176− match (Indexer _ prop val) = mconcat <$> sequence
208+ match (Indexer _ prop@StringLiteral{} val@ObjectLiteral{}) = mconcat <$> sequence
177 [ prettyPrintIL' val
209 [ prettyPrintIL' val
178 , return $ emit "["
210 , return $ emit "["
179 , prettyPrintIL' prop
211 , prettyPrintIL' prop
180 , return $ emit "]"
212 , return $ emit "]"
181 ]
213 ]
214+ match (Indexer _ prop@StringLiteral{} val) = mconcat <$> sequence
215+ [ prettyPrintIL' val
216+ , return $ emit ".("
217+ , return $ emit dictType
218+ , return $ emit ")["
219+ , prettyPrintIL' prop
220+ , return $ emit "]"
221+ ]
222+ match (Indexer _ index val@ArrayLiteral{}) = mconcat <$> sequence
223+ [ prettyPrintIL' val
224+ , return $ emit "["
225+ , prettyPrintIL' index
226+ , return $ emit "]"
227+ ]
228+ match (Indexer _ index val) = mconcat <$> sequence
229+ [ prettyPrintIL' val
230+ , return $ emit ".("
231+ , return $ emit arrayType
232+ , return $ emit ")["
233+ , prettyPrintIL' index
234+ , return $ emit "]"
235+ ]
182 match (InstanceOf _ val ty) = mconcat <$> sequence
236 match (InstanceOf _ val ty) = mconcat <$> sequence
183− [ return . emit $ unbox' dictType val
237+ [ return $ emit "Is("
238+ , prettyPrintIL' val
184− , return $ emit ".contains("
239+ , return $ emit ", "
185 , prettyPrintIL' ty
240 , prettyPrintIL' ty
186 , return $ emit ")"
241 , return $ emit ")"
187 ]
242 ]
188 match (While _ cond sts) = mconcat <$> sequence
243 match (While _ cond sts) = mconcat <$> sequence
189− [ return $ emit "while ("
244+ [ return $ emit "for "
190 , prettyPrintIL' cond
245 , prettyPrintIL' cond
191− , return $ emit ") "
246+ , return $ emit " "
192 , prettyPrintIL' sts
247 , prettyPrintIL' sts
193 ]
248 ]
194 match (For _ ident start end sts) = mconcat <$> sequence
249 match (For _ ident start end sts) = mconcat <$> sequence
195− [ return $ emit $ "for (boxed " <> ident <> " = "
250+ [ return $ emit $ "for var " <> ident <> " = "
196 , prettyPrintIL' start
251 , prettyPrintIL' start
197 , return $ emit $ "; " <> ident <> " < "
252 , return $ emit $ "; " <> ident <> " < "
198 , prettyPrintIL' end
253 , prettyPrintIL' end
199− , return $ emit $ "; " <> ident <> "++) "
254+ , return $ emit $ "; " <> ident <> "++ "
200 , prettyPrintIL' sts
255 , prettyPrintIL' sts
201 ]
256 ]
202 match (ForIn _ ident obj sts) = mconcat <$> sequence
257 match (ForIn _ ident obj sts) = mconcat <$> sequence
203− [ return $ emit $ "for (boxed " <> ident <> " in "
258+ [ return $ emit $ "for var " <> ident <> " in "
204 , prettyPrintIL' obj
259 , prettyPrintIL' obj
205− , return $ emit ") "
260+ , return $ emit " "
206 , prettyPrintIL' sts
261 , prettyPrintIL' sts
207 ]
262 ]
208− match (IfElse _ (Binary _ EqualTo cond (BooleanLiteral Nothing True)) thens elses) = mconcat <$> sequence
263+ match (IfElse _ (Binary _ EqualTo cond@Binary{} (BooleanLiteral Nothing True)) thens elses) = mconcat <$> sequence
209− [ return $ emit "if ("
210− , return $ emit $ unbox' "bool" cond
211− , return $ emit ") "
212− , prettyPrintIL' thens
213− , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
214− ]
215− match (IfElse _ (Binary _ EqualTo cond (BooleanLiteral Nothing False)) thens elses) = mconcat <$> sequence
216− [ return $ emit "if (!("
217− , return $ emit $ unbox' "bool" cond
218− , return $ emit ")) "
219− , prettyPrintIL' thens
220− , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
221− ]
222− match (IfElse _ (Binary _ EqualTo x y@(NumericLiteral _ n)) thens elses) = mconcat <$> sequence
223− [ return $ emit "if ("
264+ [ return $ emit "if "
224− , return $ emit $ unbox' t x
225− , return $ emit $ " == "
226− , prettyPrintIL' y
265+ , prettyPrintIL' cond
227− , return $ emit ") "
266+ , return $ emit " "
228 , prettyPrintIL' thens
267 , prettyPrintIL' thens
229 , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
268 , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
230 ]
269 ]
231− where
232− t | Left _ <- n = int
233− | Right _ <- n = float
234− match (IfElse _ (Binary _ EqualTo a b@StringLiteral{}) thens elses) = mconcat <$> sequence
270+ match (IfElse _ (Binary _ EqualTo cond@Binary{} (BooleanLiteral Nothing False)) thens elses) = mconcat <$> sequence
235− [ return $ emit "if ("
271+ [ return $ emit "if !("
236− , return $ emit $ unbox' string a
237− , return $ emit $ " == "
238− , prettyPrintIL' b
272+ , prettyPrintIL' cond
239 , return $ emit ") "
273 , return $ emit ") "
240 , prettyPrintIL' thens
274 , prettyPrintIL' thens
241 , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
275 , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
242 ]
276 ]
243 match (IfElse _ cond thens elses) = mconcat <$> sequence
277 match (IfElse _ cond thens elses) = mconcat <$> sequence
244− [ return $ emit "if ("
278+ [ return $ emit "if "
245 , prettyPrintIL' cond
279 , prettyPrintIL' cond
246− , return $ emit ") "
280+ , return $ emit " "
247 , prettyPrintIL' thens
281 , prettyPrintIL' thens
248 , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
282 , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
249 ]
283 ]
@@ -251,11 +285,10 @@ literals = mkPattern' match'
251 [ return $ emit "return "
285 [ return $ emit "return "
252 , prettyPrintIL' value
286 , prettyPrintIL' value
253 ]
287 ]
254− match (ReturnNoResult _) = return $ emit "return undefined"
288+ match (ReturnNoResult _) = return . emit $ "return " <> undefinedName
255 -- match (Throw _ _) = return mempty
289 -- match (Throw _ _) = return mempty
256 match (Throw _ value) = mconcat <$> sequence
290 match (Throw _ value) = mconcat <$> sequence
257− [ return $ emit "THROW_("
291+ [ return $ emit "panic("
258− , return $ emit "\"PatternMatchFailure: \""
259 , prettyPrintIL' value
292 , prettyPrintIL' value
260 , return $ emit ")"
293 , return $ emit ")"
261 ]
294 ]
@@ -295,7 +328,7 @@ literals = mkPattern' match'
295 Nothing -> ""
328 Nothing -> ""
296
329
297unary' :: (Emit gen) => UnaryOperator -> (AST -> Text) -> Operator PrinterState AST gen
330unary' :: (Emit gen) => UnaryOperator -> (AST -> Text) -> Operator PrinterState AST gen
298−unary' op mkStr = Wrap match (const)
331+unary' op mkStr = Wrap match (<>)
299 where
332 where
300 match :: (Emit gen) => Pattern PrinterState AST (gen, AST)
333 match :: (Emit gen) => Pattern PrinterState AST (gen, AST)
301 match = mkPattern match'
334 match = mkPattern match'
@@ -304,7 +337,7 @@ unary' op mkStr = Wrap match (const)
304 match' _ = Nothing
337 match' _ = Nothing
305
338
306unary :: (Emit gen) => UnaryOperator -> Text -> Operator PrinterState AST gen
339unary :: (Emit gen) => UnaryOperator -> Text -> Operator PrinterState AST gen
307−unary op str = unary' op (\v -> str <> "(" <> prettyPrintIL1 v <> ")")
340+unary op str = unary' op (const str)
308
341
309negateOperator :: (Emit gen) => Operator PrinterState AST gen
342negateOperator :: (Emit gen) => Operator PrinterState AST gen
310negateOperator = unary' Negate (\v -> if isNegate v then "- " else "-")
343negateOperator = unary' Negate (\v -> if isNegate v then "- " else "-")
@@ -330,11 +363,21 @@ isLiteral ObjectLiteral{} = True
330isLiteral ArrayLiteral{} = True
363isLiteral ArrayLiteral{} = True
331isLiteral _ = False
364isLiteral _ = False
332
365
366+isComplexLiteral :: AST -> Bool
367+-- isComplexLiteral Function{} = True
368+isComplexLiteral ObjectLiteral{} = True
369+isComplexLiteral ArrayLiteral{} = True
370+isComplexLiteral _ = False
371+
372+unApp :: AST -> [AST] -> (AST, [AST])
373+unApp (App _ val [arg]) args = unApp val (arg : args)
374+unApp other args = (other, args)
375+
333prettyStatements :: (Emit gen) => [AST] -> StateT PrinterState Maybe gen
376prettyStatements :: (Emit gen) => [AST] -> StateT PrinterState Maybe gen
334prettyStatements sts = do
377prettyStatements sts = do
335 ils <- forM sts prettyPrintIL'
378 ils <- forM sts prettyPrintIL'
336 indentString <- currentIndent
379 indentString <- currentIndent
337− return $ intercalate (emit "\n") $ map ((<> emit ";") . (indentString <>)) ils
380+ return $ intercalate (emit "\n") $ map (indentString <>) ils
338
381
339-- | Generate a pretty-printed string representing a collection of C++ expressions at the same indentation level
382-- | Generate a pretty-printed string representing a collection of C++ expressions at the same indentation level
340prettyPrintILWithSourceMaps :: [AST] -> (Text, [SMap])
383prettyPrintILWithSourceMaps :: [AST] -> (Text, [SMap])
@@ -353,7 +396,7 @@ prettyPrintIL' = A.runKleisli $ runPattern matchValue
353 matchValue = buildPrettyPrinter operators (literals <+> fmap parensPos matchValue)
396 matchValue = buildPrettyPrinter operators (literals <+> fmap parensPos matchValue)
354 operators :: (Emit gen) => OperatorTable PrinterState AST gen
397 operators :: (Emit gen) => OperatorTable PrinterState AST gen
355 operators =
398 operators =
356− OperatorTable [ [ unary New "box" ]
399+ OperatorTable [ [ unary New "?" ]
357 , [ unary Not "!"
400 , [ unary Not "!"
358 , unary BitwiseNot "~"
401 , unary BitwiseNot "~"
359 , unary Positive "+"
402 , unary Positive "+"
@@ -364,8 +407,7 @@ prettyPrintIL' = A.runKleisli $ runPattern matchValue
364 , [ binary Add "+"
407 , [ binary Add "+"
365 , binary Subtract "-" ]
408 , binary Subtract "-" ]
366 , [ binary ShiftLeft "<<"
409 , [ binary ShiftLeft "<<"
367− , binary ShiftRight ">>"
410+ , binary ShiftRight ">>" ]
368− , binary ZeroFillShiftRight ">>>" ]
369 , [ binary LessThan "<"
411 , [ binary LessThan "<"
370 , binary LessThanOrEqualTo "<="
412 , binary LessThanOrEqualTo "<="
371 , binary GreaterThan ">"
413 , binary GreaterThan ">"
@@ -383,15 +425,12 @@ prettyPrintIL1 :: AST -> Text
383prettyPrintIL1 = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyPrintIL'
425prettyPrintIL1 = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyPrintIL'
384
426
385stringLiteral :: PSString -> Text
427stringLiteral :: PSString -> Text
386−stringLiteral pss | Just s <- decodeString pss =
428+stringLiteral pss | Just s <- decodeString pss = stringLiteral' s
387− (if T.all isAscii s
388− then ""
389− else ("u8")) <> stringLiteral' s
390 where
429 where
391 stringLiteral' :: Text -> Text
430 stringLiteral' :: Text -> Text
392 stringLiteral' s = "\"" <> T.concatMap encodeChar s <> "\""
431 stringLiteral' s = "\"" <> T.concatMap encodeChar s <> "\""
393 encodeChar :: Char -> Text
432 encodeChar :: Char -> Text
394− encodeChar '\0' = "\\0"
433+ encodeChar '\0' = "\\x00"
395 encodeChar '\b' = "\\b"
434 encodeChar '\b' = "\\b"
396 encodeChar '\t' = "\\t"
435 encodeChar '\t' = "\\t"
397 encodeChar '\n' = "\\n"
436 encodeChar '\n' = "\\n"
@@ -403,62 +442,29 @@ stringLiteral pss | Just s <- decodeString pss =
403 encodeChar c = T.singleton $ c
442 encodeChar c = T.singleton $ c
404stringLiteral _ = "\"\\uFFFD\""
443stringLiteral _ = "\"\\uFFFD\""
405
444
406−unbox' :: Text -> AST -> Text
407−unbox' _ v@(NumericLiteral{}) = prettyPrintIL1 v
408−unbox' _ v@(BooleanLiteral{}) = prettyPrintIL1 v
409−unbox' _ v@(StringLiteral{}) = prettyPrintIL1 v
410−unbox' t v = unbox t <> "(" <> prettyPrintIL1 v <> ")"
411−
412−unboxType :: Text -> Maybe Text
413−unboxType t
414− | t == C.eqInt ||
415− t == C.semiringInt ||
416− t == C.ringInt
417− = Just int
418− | t == C.semiringNumber ||
419− t == C.ringNumber ||
420− t == C.euclideanRingNumber
421− = Just float
422− | t == C.heytingAlgebraBoolean
423− = Just bool
424− | otherwise = Nothing
425−
426−renderOp :: Text -> Maybe Text
427−renderOp op
428− | op == C.eq = Just " == "
429− | op == C.notEq = Just " != "
430− | op == C.add = Just " + "
431− | op == C.sub = Just " - "
432− | op == C.mul = Just " * "
433− | op == C.div = Just " / "
434− | op == C.conj = Just " && "
435− | op == C.disj = Just "|| "
436− | otherwise = Nothing
437−
438interfaceSource :: Text -> [(Text,Bool)] -> [Ident] -> Text
445interfaceSource :: Text -> [(Text,Bool)] -> [Ident] -> Text
439−interfaceSource mn values foreigns =
446+interfaceSource _ _ _ = ""
440− let foreigns' = identToIL <$> foreigns
441− valueNames = fst <$> values
442− values' = filter ((`notElem` foreigns') . fst) values ++
443− zip (foreigns' \\ valueNames) (repeat True)
444− in
445− "// Generated by psil compiler\n\n" <>
446− "#ifndef " <> mn <> "_H\n" <>
447− "#define " <> mn <> "_H\n\n" <>
448− "#include \"purescript.h\"\n\n" <>
449− "namespace " <> mn <> " {\n\n" <>
450− "using namespace purescript;\n\n" <>
451− (T.concat $ (\(export, static) ->
452− let rty = if static then "const boxed&" else "boxed" in
453− "auto " <> export <> "() -> " <> rty <> ";\n") <$> values') <> "\n" <>
454− "} // end namespace " <> mn <> "\n\n" <>
455− "#endif // " <> mn <> "_H\n\n"
456
447
457implHeaderSource :: Text -> [Text] -> Text -> Text
448implHeaderSource :: Text -> [Text] -> Text -> Text
458−implHeaderSource mn imports interfaceImport =
449+implHeaderSource mn imports otherPrefix =
450+ "// Code generated by psgo. DO NOT EDIT.\n\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"
455+ else "\n") <>
456+ "import (\n" <>
459− T.concat imports <> "\n"<>
457+ (T.concat $ formatImport <$> imports) <> ")\n\n" <>
460− interfaceImport <> "\n\n" <>
458+ "type _ = Any\n\n"
461− "namespace " <> mn <> " {\n\n"
459+ where
460+ formatImport :: Text -> Text
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
462
468
463implFooterSource :: Text -> [Ident] -> Text
469implFooterSource :: Text -> [Ident] -> Text
464implFooterSource mn foreigns =
470implFooterSource mn foreigns =
@@ -466,50 +472,56 @@ implFooterSource mn foreigns =
466 (if null foreigns
472 (if null foreigns
467 then ""
473 then ""
468 else ("// Foreign values\n\n" <>
474 else ("// Foreign values\n\n" <>
469− "DEFINE_FOREIGN_DICTIONARY_AND_ACCESSOR()\n\n" <>
475+ "var " <> foreignDict <> " = "<> foreignMod <> "(\"" <> mn <> "\")\n\n" <>
470− (T.concat $ (\foreign' -> "auto " <>
476+ (T.concat $ (\foreign' ->
471− identToIL foreign' <>
477+ let name = moduleIdentToIL foreign' in
472− "() -> const boxed& { " <>
478+ varDecl <> " " <> initName name <> " Once\n" <>
473− staticValueBegin <>
479+ varDecl <> " " <> valueName name <> " " <> anyType <> "\n\n" <>
474− foreignDict <> "().at(" <>
480+ "func " <>
481+ withPrefix name <>
482+ "() Any { \n" <>
483+ " " <> initName name <> ".Do(func() {\n" <>
484+ " " <> valueName name <> " = " <>
485+ "Get(" <> foreignDict <> ", " <>
475− (stringLiteral . mkString $ runIdent foreign') <> "); " <>
486+ (stringLiteral . mkString $ runIdent foreign') <> ")\n" <>
476− staticValueEnd <>
487+ " })\n" <>
488+ " return " <> valueName name <> "\n" <>
477− " };\n") <$> foreigns) <>
489+ "}\n\n") <$> foreigns))) <>
478− "\n")) <>
479− "} // end namespace " <> mn <>
480− "\n\n" <>
481− if mn == "Main" then mainSource else "\n"
490+ if mn == "Main" || mn == "Test.Main" then mainSource else "\n"
482 where
491 where
483 mainSource :: Text
492 mainSource :: Text
484 mainSource = "\
493 mainSource = "\
485− \int main(int argc, const char * argv[]) {\n\
494+ \func main() {\n\
486− \ Main::main()();\n\
495+ \ Run(Ꞌmain())\n\
487− \ return 0;\n\
488 \}\n\n\
496 \}\n\n\
489 \"
497 \"
490
498
491varDecl :: Text
499varDecl :: Text
492−varDecl = "const boxed"
500+varDecl = "var"
493−
494−varRefDecl :: Text
495−varRefDecl = varDecl <> "&"
496−
497−renderArg' :: Text -> Text -> Text
498−renderArg' decl arg
499− | arg == unusedName = decl
500− | otherwise = decl <> " " <> arg
501
501
502−renderArg :: Text -> Text
502+renderArg :: Text -> Text -> Text
503−renderArg = renderArg' varRefDecl
503+renderArg decl arg = arg <> " " <> decl
504
504
505−renderArgByVal :: Text -> Text
505+foreignMod :: Text
506−renderArgByVal = renderArg' varDecl
506+foreignMod = "Foreign"
507
507
508foreignDict :: Text
508foreignDict :: Text
509foreignDict = "foreign"
509foreignDict = "foreign"
510
510
511−staticValueBegin :: Text
511+modLabel :: Text
512−staticValueBegin = "static const boxed _ = "
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+
523+initName :: Text -> Text
524+initName s = "ᛌ" <> s <> "_once"
513
525
514−staticValueEnd :: Text
526+valueName :: Text -> Text
515−staticValueEnd = "return _;"
527+valueName s = "ᛧ" <> s
src/Tests.hs+11−12
@@ -31,18 +31,13 @@ runTests = do
31 let srcDir = outputDir </> "src"
31 let srcDir = outputDir </> "src"
32 createDirectory srcDir
32 createDirectory srcDir
33
33
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"]
35 let passingDir = baseDir </> "purescript" </> "tests" </> "purs" </> "passing"
35 let passingDir = baseDir </> "purescript" </> "tests" </> "purs" </> "passing"
36 passingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents passingDir
36 passingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents passingDir
37
37
38 setCurrentDirectory outputDir
38 setCurrentDirectory outputDir
39− callProcess "pscpp" ["--makefile"]
40
39
41 fetchPackages
40 fetchPackages
42− callProcess "git" ["clone", "--depth", "1", "https://github.com/andyarvanitis/purescript-cpp-ffi.git", "ffi"]
43−
44− callProcess "rm" ["-rf", ".psc-package/psc-0.13.0/prelude/v4.1.0"]
45− callProcess "git" ["clone", "--branch", "v4.1.1", "--depth", "1", "https://github.com/purescript/purescript-prelude.git", ".psc-package/psc-0.13.0/prelude/v4.1.0"]
46
41
47 let tests = filter (`notElem` skipped) passingTestCases
42 let tests = filter (`notElem` skipped) passingTestCases
48
43
@@ -65,14 +60,15 @@ runTests = do
65 testCaseDirExists <- doesDirectoryExist testCaseDir
60 testCaseDirExists <- doesDirectoryExist testCaseDir
66 when testCaseDirExists $ callProcess "cp" ["-R", testCaseDir, srcDir]
61 when testCaseDirExists $ callProcess "cp" ["-R", testCaseDir, srcDir]
67
62
68− callProcess "make" ["clean"]
63+ callProcess "rm" ["-rf", "output"]
69− callProcess "make" ["debug", "-j8"]
64+ callProcess "rm" ["-rf", "Main"]
65+ callProcess "spago" ["build"]
70 --
66 --
71− -- Run C++ files
67+ -- Run executable file
72 --
68 --
73 outputFile <- openFile (tmp </> logpath </> logfile) AppendMode
69 outputFile <- openFile (tmp </> logpath </> logfile) AppendMode
74 hPutStrLn outputFile ("\n" ++ inputFile ++ ":")
70 hPutStrLn outputFile ("\n" ++ inputFile ++ ":")
75− proc <- runProcess ("output" </> "bin" </> "main") [] Nothing Nothing Nothing (Just outputFile) Nothing
71+ proc <- runProcess ("./" </> "Main") [] Nothing Nothing Nothing (Just outputFile) Nothing
76
72
77 removeFile (srcDir </> inputFile)
73 removeFile (srcDir </> inputFile)
78 when testCaseDirExists $ callProcess "rm" ["-rf", srcDir </> (takeWhile (/='.') inputFile)]
74 when testCaseDirExists $ callProcess "rm" ["-rf", srcDir </> (takeWhile (/='.') inputFile)]
@@ -116,8 +112,11 @@ packages =
116fetchPackages :: IO ()
112fetchPackages :: IO ()
117-------------------------------------------------------------------------------
113-------------------------------------------------------------------------------
118fetchPackages = do
114fetchPackages = do
119− callProcess "psc-package" ["init"]
115+ callProcess "spago" ["init"]
116+ callCommand "sed '$s/\\(}\\)/, backend = \"psgo\"\\1/' < spago.dhall | dhall format > spago.tmp"
117+ callCommand "mv spago.tmp spago.dhall"
118+
120− mapM (callProcess "psc-package" . (\p -> ["install", p])) packages
119+ mapM (callProcess "spago" . (\p -> ["install", p])) packages
121 return ()
120 return ()
122
121
123-------------------------------------------------------------------------------
122-------------------------------------------------------------------------------
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+}:
2
17
3pkgs.haskell.lib.buildStackProject {
18pkgs.haskell.lib.buildStackProject {
4 name = "default-stack-shell";
19 name = "default-stack-shell";
5− inherit ghc;
20+ ghc = pkgs.haskell.compiler.ghc865;
6− buildInputs = with pkgs; [git git-lfs gmp zlib];
21+ buildInputs = with pkgs; [git git-lfs gmp ncurses zlib];
7 LANG = "en_US.UTF-8";
22 LANG = "en_US.UTF-8";
8}
23}
stack.yaml+16−65
@@ -1,73 +1,24 @@
1−# This file was automatically generated by 'stack init'
1+resolver: lts-13.26
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/
6
2
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-12.0
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.
38packages:
3packages:
39- .
4- .
40−# Dependency packages to be pulled from upstream that are not in the resolver
41−# (e.g., acme-missiles-0.3)
42−extra-deps:
43−- git: https://github.com/purescript/purescript
44− commit: b35412a9ea49a46d315a0214cd2c3908442ab7a7
45−- pipes-http-1.0.5
46−
47−# Override default flag values for local packages and extra-deps
48−# flags: {}
49
5
50−# Extra package databases containing global packages
51−# extra-package-dbs: []
6+extra-deps:
52−
7+- git: https://github.com/purescript/purescript
53−# Control whether we use the GHC we find on the path
8+ commit: 9cad73ed8ea7df3011032ddbd2f3de5a0c08629c
54−# system-ghc: true
9+- happy-1.19.9
55−#
10+- language-javascript-0.7.0.0
56−# Require a specific version of stack, using version ranges
11+- network-3.0.1.1
57−# require-stack-version: -any # Default
12+- these-1.0.1
58−# require-stack-version: ">=1.6"
13+- semialign-1
59−#
60−# Override the architecture used by stack, especially useful on Windows
61−# arch: i386
62−# arch: x86_64
63−#
64−# Extra directories used by stack for building
65−# extra-include-dirs: [/path/to/dir]
66−# extra-lib-dirs: [/path/to/dir]
67−#
68−# Allow a newer minor version of GHC than the snapshot specifies
69−# compiler-check: newer-minor
70
14
71nix:
15nix:
16+ enable: false
72 shell-file: stack.nix
17 shell-file: stack.nix
73
18
19+flags:
20+ aeson-pretty:
21+ lib-only: true
22+ these:
23+ assoc: false
24+ quickcheck: false
support/Makefiledeleted−129
@@ -1,129 +0,0 @@
1−# PureScript to native binary (via C++) 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−PSCPP := pscpp
20−SRC := src
21−OUTPUT := output
22−CC_SRC := $(OUTPUT)/src
23−FFI_SRC := ffi
24−BIN := main
25−
26−override PURSFLAGS += compile --codegen corefn
27−override CXXFLAGS += --std=c++11
28−
29−CXXVERSION = $(shell $(CXX) --version)
30−ifneq (,$(findstring g++,$(CXXVERSION)))
31− PSCPPFLAGS += "--ucns"
32−endif
33−
34−DEBUG := "-DDEBUG -g"
35−RELEASE := "-DNDEBUG -O3"
36−
37−INCLUDES := -I $(CC_SRC)
38−BIN_DIR := $(OUTPUT)/bin
39−
40−PACKAGE_SOURCES = $(subst \,/,$(shell $(PSC_PACKAGE) sources))
41−PURESCRIPT_PKGS := $(firstword $(subst /, ,$(PACKAGE_SOURCES)))
42−
43−## Not all environments support globstar (** dir pattern)
44−rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
45−
46−unspecified:
47− @echo
48− @echo "Please specify the type of build you want: 'debug' or 'release'"
49− @echo
50− @echo " debug => not optimized (your app will run slower), good for"
51− @echo " running in a source-level debugger, more (descriptive)"
52− @echo " runtime exceptions"
53− @echo
54− @echo " release => optimized (your app will run faster), less helpful when debugging"
55− @echo
56−
57−debug: codegen
58− @$(MAKE) $(BIN) CFLAGS+=$(DEBUG) CXXFLAGS+=$(DEBUG)
59−
60−release: codegen
61− @$(MAKE) $(BIN) CFLAGS+=$(RELEASE) CXXFLAGS+=$(RELEASE)
62−
63−.PHONY: corefn
64−corefn: PURESCRIPT_PKG_SRCS=$(foreach d,$(PACKAGE_SOURCES),$(call rwildcard,$(firstword $(subst *, ,$(d))),*.purs))
65−corefn: PURESCRIPT_SRCS=$(call rwildcard,$(SRC)/,*.purs)
66−corefn: $(PURESCRIPT_PKGS)
67− @$(PURS) $(PURSFLAGS) --output $(OUTPUT) $(PURESCRIPT_PKG_SRCS) $(PURESCRIPT_SRCS)
68−
69−.PHONY: codegen
70−codegen: COREFN_SRCS=$(call rwildcard,$(OUTPUT)/,corefn.json)
71−codegen: corefn
72− @$(PSCPP) $(PSCPPFLAGS) $(COREFN_SRCS)
73−
74−$(PURESCRIPT_PKGS):
75− @echo "Getting packages using" $(PSC_PACKAGE) "..."
76− @$(PSC_PACKAGE) update
77−
78−SRCS := $(call rwildcard,$(CC_SRC)/,*.cpp)
79−SRCS += $(call rwildcard,$(FFI_SRC)/,*.cpp)
80−SRCS += $(call rwildcard,$(FFI_SRC)/,*.cc)
81−SRCS += $(call rwildcard,$(FFI_SRC)/,*.mm)
82−SRCS += $(call rwildcard,$(FFI_SRC)/,*.c)
83−SRCS += $(call rwildcard,$(FFI_SRC)/,*.m)
84−
85−OBJS1 = $(SRCS:.cpp=.o)
86−OBJS2 = $(OBJS1:.cc=.o)
87−OBJS3 = $(OBJS2:.mm=.o)
88−OBJS4 = $(OBJS3:.c=.o)
89−OBJS = $(OBJS4:.m=.o)
90−DEPS = $(OBJS:.o=.d)
91−
92−$(BIN): $(OBJS)
93− @echo "Linking" $(BIN_DIR)/$(BIN)
94− @mkdir -p $(BIN_DIR)
95− @$(CXX) $^ -o $(BIN_DIR)/$@ $(LDFLAGS)
96−
97−-include $(DEPS)
98−
99−%.o: %.cpp
100− @echo "Creating" $@ "(C++)"
101− @$(CXX) $(CXXFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
102−
103−%.o: %.cc
104− @echo "Creating" $@ "(C++)"
105− @$(CXX) $(CXXFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
106−
107−%.o: %.mm
108− @echo "Creating" $@ "(Objective-C++)"
109− @$(CXX) $(CXXFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
110−
111−%.o: %.c
112− @echo "Creating" $@ "(C)"
113− @$(CXX) $(CFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
114−
115−%.o: %.m
116− @echo "Creating" $@ "(Objective-C)"
117− @$(CXX) $(CFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
118−
119−.PHONY: all
120−all: release
121−
122−.PHONY: clean
123−clean:
124− @-rm -f $(OBJS) $(DEPS)
125− @-rm -rf $(OUTPUT)
126−
127−.PHONY: run
128−run:
129− @$(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