git.iko.soy/rowtype-yoga/purescript-native
on e257dfb help sign in
.gitignore−2
since splitsnapshots
unifiedsplit
@@ -14,10 +14,8 @@ bower_components/
14node_modules
14node_modules
15tmp/
15tmp/
16.stack-work/
16.stack-work/
17−stack.yaml.lock
18output
17output
19examples/docs/docs/
18examples/docs/docs/
20core-tests/full-core-docs.md
19core-tests/full-core-docs.md
21.psc-ide-port
20.psc-ide-port
22.vscode/
21.vscode/
23−result
README-go.mddeleted−48
@@ -1,48 +0,0 @@
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+47−26
@@ -1,41 +1,62 @@
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 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.
3+This is an experimental C++(11+)/native 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.
4
4
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).
5+---
6+
7+### **Please note that this project supersedes [pure11](https://github.com/pure11/pure11) (they are no longer the same)**
8+
9+---
10+
11+#### Performance
12+
13+* No runtime system (beyond some support classes and the standard C++(11+) runtime library)
14+* For automatic memory management, uses native C++(11+) reference counting (`std::shared_ptr`)
15+* Uses PureScript's normal tail call optimization techniques for generated C++ code
16+
17+#### Differences from PureScript:
18+
19+* Foreign imports are C++(11+) (or C) instead of JavaScript ([FFI examples](https://github.com/andyarvanitis/purescript-cpp-ffi))
20+* No C++-specific REPL
21+
22+#### Other notes:
23+
24+* PureScript arrays are implemented using [`std::deque`](http://en.cppreference.com/w/cpp/container/deque) (random access *O(1)*)
25+* `String` types are implemented using either C-strings (for literals) or `std::string`
26+* `Number` is C++ `double`, `Int` is C++ `int`, `Char` is `std::string` (single UTF-8 entity), `Boolean` is `bool`
27+
28+#### Future ideas:
29+
30+* 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`)
31+* `BigInt` via GNU GMP (or an alternative)
32+
33+#### Requirements for building the compiler itself
34+
35+* [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/))
36+
37+#### Requirements for using it
6
38
7−#### Platforms
39+* A recent version (0.12+) of [purescript](https://github.com/purescript/purescript/releases).
8−* Although purescript-native should work on any platform supporting PureScript and modern C++ or Go, the generated code and resulting binary executables have been tested on:
9− * macOS Mojave 10.14.5 – full test suite
10− * For C++, Xcode 10.2.1's `clang` was used
11− * For Go, `go` version 1.12.7 was used
12− * Windows 10 x64 – full test suite
13− * C++: Visual Studio 2017 and `clang`
14− * Go: `go` version 1.12.7
15− * Linux Debian 9.5 amd64 – full test suite (C++)
16− * Default versions of `clang` and `gcc`
17− * Linux Debian 10 amd64 – full test suite (Go)
18− * Default version of `go`
19− * Raspberry Pi 3 B+ Raspbian official build (ARM), default versions of `clang` and `gcc`
20
40
41+* A C++11-capable toolchain, e.g. recent versions of clang, gcc, Microsoft Visual Studio 2015
42+* GNU Make is the default supported build tool, but you should be able to use your favorite C++ build system, tools, debuggers, etc.
21
43
22−#### Requirements for building `pscpp` and `psgo`
44+#### Getting Started
45+This assumes you are running OS X or a Unix-like system (Linux, *BSD, etc.).
23
46
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/))
47+1. Make sure you have developer tools for your system installed. For OS X, you'll need a recent version of Xcode. For Linux, etc., you need gcc 4.9.2 or later, including g++ support. You can also use clang 3.5 or later, but it still requires gcc for its C++ standard libraries.
25
48
26−#### Requirements for using PureScript + purescript-native
49+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.
27
50
28−* A recent version (0.13+) of [purescript](https://github.com/purescript/purescript/releases).
51+3. Under your working directory, also create an `ffi` subdirectory, which will be where you will place C/C++ FFI source files.
29
52
30−* For `pscpp`, a C++11-capable toolchain, e.g. recent versions of clang, gcc, Microsoft Visual Studio
53+4. Copy this [`Makefile`](https://github.com/andyarvanitis/purescript-native/blob/native-dump-corefn/support/Makefile) into your working directory.
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.
34
54
55+5. Use PureScript's standard [`psc-package`](https://psc-package.readthedocs.io/en/latest/) utility to add and manage package dependencies.
35
56
36−#### For more information and a Getting Started guide, please see
57+6. You should now be ready to build a PureScript program.
37−* [`pscpp`](https://github.com/andyarvanitis/purescript-native/blob/cpp/README-cpp.md)
58+ * As stated above, place your source file(s) in the working directory's `src` subdirectory and execute `make`. If your machine has multiple cores, you might want to use `make -jN`, where `N` is the number of cores.
38−* [`psgo`](https://github.com/andyarvanitis/purescript-native/blob/golang/README-go.md)
39
59
60+ * 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).
40
61
41---
62---
app/Main.hs+52−186
@@ -5,12 +5,11 @@
5module Main where
5module Main where
6
6
7import Control.Monad (when)
7import Control.Monad (when)
8−import qualified Control.Monad.Parallel as Par
9import Data.Aeson
8import Data.Aeson
10import Data.Aeson.Types
9import Data.Aeson.Types
11import Data.Char
10import Data.Char
12import Data.FileEmbed (embedFile)
11import Data.FileEmbed (embedFile)
13−import Data.List (delete, intercalate, isPrefixOf, nub, partition, (\\))
12+import Data.List (delete)
14import Data.Maybe
13import Data.Maybe
15import Data.Monoid ((<>))
14import Data.Monoid ((<>))
16import Data.Version
15import Data.Version
@@ -19,32 +18,26 @@ import Control.Monad.Supply.Class
19import Text.Printf
18import Text.Printf
20
19
21import System.Environment
20import System.Environment
22−import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getCurrentDirectory, getModificationTime)
21+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getModificationTime)
23import System.FilePath ((</>), joinPath, splitDirectories, takeDirectory)
22import System.FilePath ((</>), joinPath, splitDirectories, takeDirectory)
24−import System.FilePath.Find
25−import System.Process
26
23
27import Data.Text (Text)
24import Data.Text (Text)
28import qualified Data.Text as T
25import qualified Data.Text as T
29import qualified Data.Text.Encoding as T
26import qualified Data.Text.Encoding as T
27+import qualified Data.Text.IO as T
30import qualified Data.Text.Lazy as L
28import qualified Data.Text.Lazy as L
31import qualified Data.Text.Lazy.Encoding as L
29import qualified Data.Text.Lazy.Encoding as L
32−import qualified Data.ByteString as B
33−
34−import Development.GitRev
35
30
36import Language.PureScript.AST.Literals
31import Language.PureScript.AST.Literals
37import Language.PureScript.CoreFn
32import Language.PureScript.CoreFn
38import Language.PureScript.CoreFn.FromJSON
33import Language.PureScript.CoreFn.FromJSON
39
34
40−import CodeGen.IL
35+import CodeGen.Cpp
41−import CodeGen.IL.Common
36+import CodeGen.Cpp.Common
42−import CodeGen.IL.Printer
37+import CodeGen.Cpp.Printer
43
38
44import Tests
39import Tests
45
40
46−data Command = Build | Run (Maybe String)
47−
48parseJson :: Text -> Value
41parseJson :: Text -> Value
49parseJson text
42parseJson text
50 | Just fileJson <- decode . L.encodeUtf8 $ L.fromStrict text = fileJson
43 | Just fileJson <- decode . L.encodeUtf8 $ L.fromStrict text = fileJson
@@ -54,197 +47,70 @@ jsonToModule :: Value -> Module Ann
54jsonToModule value =
47jsonToModule value =
55 case parse moduleFromJSON value of
48 case parse moduleFromJSON value of
56 Success (_, r) -> r
49 Success (_, r) -> r
57− _ -> error "Failed parsing corefn. Psgo currently supports purescript <= 0.13.8"
50+ _ -> error "failed"
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')
77
51
78main :: IO ()
52main :: IO ()
79main = do
53main = do
80 args <- getArgs
54 args <- getArgs
81− -- let (opts, files) = partition (isPrefixOf "--") args
55+ if null args
82− let (opts, moduleName, files) = splitArgs args
56+ then return ()
83− opts' = (map . map) toLower opts
84− if "--tests" `elem` opts'
85− then runTests
86 else do
57 else do
87− if "--help" `elem` opts' || "--version" `elem` opts'
58+ if head args == "tests"
88− then do
59+ then runTests
89− when ("--help" `elem` opts') $ do
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
60 else 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 =
122− let filepath = takeDirectory (head files) in
123− joinPath $ (init $ splitDirectories filepath)
124− generateFiles :: [String] -> [FilePath] -> IO ()
125− generateFiles opts files = do
126− let baseOutpath = basePath files
127− writeSupportFiles baseOutpath
128− Par.mapM (generateCode opts baseOutpath) files
61+ mapM (generateCode False) args
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]
138− return ()
62+ return ()
139−
63+
140−generateCode :: [String] -> FilePath -> FilePath -> IO ()
64+generateCode :: Bool -> FilePath -> IO ()
141−generateCode opts baseOutpath jsonFile = do
65+generateCode someOpt jsonFile = do
142 jsonModTime <- getModificationTime jsonFile
66 jsonModTime <- getModificationTime jsonFile
143 let filepath = takeDirectory jsonFile
67 let filepath = takeDirectory jsonFile
144 dirparts = splitDirectories $ filepath
68 dirparts = splitDirectories $ filepath
145− mname = last dirparts
69+ mname = (\c -> if c == '.' then '_' else c) <$> last dirparts
146 basedir = joinPath $ init dirparts
70 basedir = joinPath $ init dirparts
147− mname' = T.pack mname
71+ possInterfaceFilename = basedir </> outdir </> mname </> interfaceFileName mname
148− possibleFileName = basedir </> (T.unpack mname') </> implFileName mname'
149− exists <- doesFileExist possibleFileName
72+ exists <- doesFileExist possInterfaceFilename
150 if exists
73 if exists
151 then do
74 then do
152− modTime <- getModificationTime possibleFileName
75+ modTime <- getModificationTime possInterfaceFilename
153 when (modTime < jsonModTime) $
76 when (modTime < jsonModTime) $
154− transpile opts baseOutpath jsonFile
77+ transpile someOpt jsonFile
155− else transpile opts baseOutpath jsonFile
78+ else transpile someOpt jsonFile
156
79
157−transpile :: [String] -> FilePath -> FilePath -> IO ()
80+transpile :: Bool -> FilePath -> IO ()
158−transpile opts baseOutpath jsonFile = do
81+transpile _ jsonFile = do
159− jsonText <- T.decodeUtf8 <$> B.readFile jsonFile
82+ jsonText <- T.readFile jsonFile
160− project <- projectEnv
161 let module' = jsonToModule $ parseJson jsonText
83 let module' = jsonToModule $ parseJson jsonText
162− ((_, foreigns, asts, implHeader, implFooter), _) <- runSupplyT 5 (moduleToIL module' project)
84+ ((interface, foreigns, asts, implHeader, implFooter), _) <- runSupplyT 5 (moduleToCpp module' Nothing)
163− let mn = moduleNameToIL' $ moduleName module'
85+ let mn = moduleNameToCpp $ moduleName module'
164− implementation = prettyPrintIL asts
86+ implementation = prettyPrintCpp asts
87+ filepath = takeDirectory jsonFile
88+ let baseOutpath = joinPath $ (init $ splitDirectories filepath) ++ [outdir]
165 outpath = joinPath [baseOutpath, T.unpack mn]
89 outpath = joinPath [baseOutpath, T.unpack mn]
90+ interfacePath = outpath </> interfaceFileName (T.unpack mn)
166 implPath = outpath </> implFileName mn
91 implPath = outpath </> implFileName mn
92+ putStrLn interfacePath
167 createDirectoryIfMissing True outpath
93 createDirectoryIfMissing True outpath
94+ T.writeFile interfacePath interface
168 putStrLn implPath
95 putStrLn implPath
169− B.writeFile implPath $ T.encodeUtf8 (implHeader <> implementation <> implFooter)
96+ T.writeFile implPath (implHeader <> implementation <> implFooter)
170−
171−writeSupportFiles :: FilePath -> IO ()
172−writeSupportFiles baseOutpath = do
173− currentDir <- getCurrentDirectory
174− let outputdir = T.pack $ baseOutpath \\ currentDir
97+ let runtime = baseOutpath </> "purescript.h"
175− ffiOutpath = currentDir </> subdir
98+ runtimeExists <- doesFileExist runtime
176− createDirectoryIfMissing True baseOutpath
99+ when (not runtimeExists) $ do
177− createDirectoryIfMissing True ffiOutpath
100+ T.writeFile runtime $ T.decodeUtf8 $(embedFile "runtime/purescript.h")
178− writeModuleFile outputdir currentDir $(embedFile "support/go.mod.working")
101+
179− writeModuleFile outputdir baseOutpath $(embedFile "support/go.mod.output")
102+outdir :: FilePath
180− writeModuleFile outputdir ffiOutpath $(embedFile "support/go.mod.ffi-loader")
103+outdir = "src"
181− writeLoaderFile ffiOutpath $(embedFile "support/ffi-loader.go")
104+
182− where
105+interfaceFileName :: String -> FilePath
183− writeModuleFile :: Text -> FilePath -> B.ByteString -> IO ()
106+interfaceFileName mn = mn <> ".h"
184− writeModuleFile outputdir path modText = do
185− let goModSource = path </> "go.mod"
186− goModSourceExists <- doesFileExist goModSource
187− when (not goModSourceExists) $ do
188− project <- projectEnv
189− let project' = T.unpack project
190− outputdir' = tail $ T.unpack outputdir
191− modText' = T.replace "$PROJECT" project $ T.decodeUtf8 modText
192− replaceLoader = project' </> ffiLoader' <> "=." </> subdir
193− replaceOutput = project' </> modPrefix' <> "=." </> outputdir'
194− B.writeFile goModSource $ T.encodeUtf8 modText'
195− callProcess "go" ["mod", "edit", "-replace", replaceLoader]
196− callProcess "go" ["mod", "edit", "-replace", replaceOutput]
197− callProcess "go" ["clean", "-modcache"]
198− writeLoaderFile :: FilePath -> B.ByteString -> IO ()
199− writeLoaderFile ffiOutpath loaderText = do
200− let loaderSource = ffiOutpath </> "ffi_loader.go"
201− loaderSourceExists <- doesFileExist loaderSource
202− when (not loaderSourceExists) $ do
203− B.writeFile loaderSource loaderText
204
107
205implFileName :: Text -> FilePath
108implFileName :: Text -> FilePath
206−implFileName mn = ((\c -> if c == '.' then '_' else c) <$> T.unpack mn) <> ".go"
109+implFileName mn = T.unpack $ mn <> ".cpp"
207−
110+
208−projectEnv :: IO Text
111+escape :: Text -> Text
209−projectEnv = do
112+escape = T.concatMap go
210− T.pack . fromMaybe defaultProject <$> lookupEnv goproject
113+ where
211−
114+ go :: Char -> Text
212−help :: String
115+ go c = T.pack $ printf "0x%04x," (ord c)
213−help = "Usage: psgo OPTIONS COREFN-FILES\n\
116+
214− \ PureScript to native (via go) compiler\n\n\
215− \Available options:\n\
216− \ --help Show this help text\n\n\
217− \ --version Show the version number\n\n\
218− \ --run Run the generated go code directly, without building an\n\
219− \ executable\n\
220− \ --no-build Generate go source files, but do not build an executable\n\
221− \ --tests Run test cases (under construction)\n\n\
222− \See also:\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.nixdeleted−23
@@ -1,23 +0,0 @@
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+12−18
@@ -1,6 +1,6 @@
1−name: psgo
1+name: pscpp
2version: 0.1.0.0
2version: 0.1.0.0
3−github: "andyarvanitis/purescript-native"
3+github: "andyarvanitis/purescript-cpp"
4license: BSD3
4license: BSD3
5author: "Andy Arvanitis"
5author: "Andy Arvanitis"
6maintainer: "example@example.com"
6maintainer: "example@example.com"
@@ -9,8 +9,6 @@ copyright: "2018 Andy Arvanitis"
9extra-source-files:
9extra-source-files:
10- README.md
10- README.md
11- ChangeLog.md
11- ChangeLog.md
12−- stack.yaml
13−- support/*
14
12
15# Metadata used when publishing your package
13# Metadata used when publishing your package
16# synopsis: Short description of your package
14# synopsis: Short description of your package
@@ -19,28 +17,24 @@ extra-source-files:
19# To avoid duplicated efforts in documentation and dealing with the
17# To avoid duplicated efforts in documentation and dealing with the
20# complications of embedding Haddock markup inside cabal files, it is
18# complications of embedding Haddock markup inside cabal files, it is
21# common to point users to the README.md file.
19# common to point users to the README.md file.
22−description: Please see the README on Github at <https://github.com/andyarvanitis/purescript-native#readme>
20+description: Please see the README on Github at <https://github.com/andyarvanitis/purescript-cpp#readme>
23
21
24dependencies:
22dependencies:
25- base >= 4.7 && < 5
23- base >= 4.7 && < 5
26−- bytestring >= 0.10.8.2 && < 0.11
27−- purescript
24+- purescript -any
28- base-compat >=0.6.0
25- base-compat >=0.6.0
29- protolude >=0.1.6
26- protolude >=0.1.6
30- text
27- text
31- containers
28- containers
32- filepath
29- filepath
33−- monad-parallel >= 0.7.2.2 && <0.8
34- pattern-arrows >=0.0.2 && <0.1
30- pattern-arrows >=0.0.2 && <0.1
35- safe >=0.3.9 && <0.4
31- safe >=0.3.9 && <0.4
36- transformers >=0.3.0 && <0.6
32- transformers >=0.3.0 && <0.6
37- mtl >=2.1.0 && <2.3.0
33- mtl >=2.1.0 && <2.3.0
38−- aeson >=1.0 && <1.5
34+- aeson >=1.0 && <1.3
39−- directory
35+- directory -any
40−- process
36+- process -any
41−- file-embed
37+- file-embed -any
42−- filemanip
43−- gitrev
44
38
45library:
39library:
46 source-dirs: src
40 source-dirs: src
@@ -64,7 +58,7 @@ library:
64 - ViewPatterns
58 - ViewPatterns
65
59
66executables:
60executables:
67− psgo:
61+ pscpp:
68 main: Main.hs
62 main: Main.hs
69 source-dirs: app
63 source-dirs: app
70 ghc-options:
64 ghc-options:
@@ -72,10 +66,10 @@ executables:
72 - -rtsopts
66 - -rtsopts
73 - -with-rtsopts=-N
67 - -with-rtsopts=-N
74 dependencies:
68 dependencies:
75− - psgo
69+ - pscpp
76
70
77tests:
71tests:
78− psgo-test:
72+ pscpp-test:
79 main: Spec.hs
73 main: Spec.hs
80 source-dirs: test
74 source-dirs: test
81 ghc-options:
75 ghc-options:
@@ -83,4 +77,4 @@ tests:
83 - -rtsopts
77 - -rtsopts
84 - -with-rtsopts=-N
78 - -with-rtsopts=-N
85 dependencies:
79 dependencies:
86− - psgo
80+ - pscpp
psgo.cabal → pscpp.cabalrenamed+26−62
@@ -1,78 +1,50 @@
1−cabal-version: 1.12
2−
3−-- This file has been generated from package.yaml by hpack version 0.35.0.
1+-- This file has been generated from package.yaml by hpack version 0.28.2.
4--
2--
5-- see: https://github.com/sol/hpack
3-- see: https://github.com/sol/hpack
4+--
5+-- hash: e5a3087784164da529b9f673d964db77b198c936e428ed7c2fe54b479a06d0c7
6
6
7−name: psgo
7+name: pscpp
8version: 0.1.0.0
8version: 0.1.0.0
9−description: Please see the README on Github at <https://github.com/andyarvanitis/purescript-native#readme>
9+description: Please see the README on Github at <https://github.com/andyarvanitis/purescript-cpp#readme>
10−homepage: https://github.com/andyarvanitis/purescript-native#readme
10+homepage: https://github.com/andyarvanitis/purescript-cpp#readme
11−bug-reports: https://github.com/andyarvanitis/purescript-native/issues
11+bug-reports: https://github.com/andyarvanitis/purescript-cpp/issues
12author: Andy Arvanitis
12author: Andy Arvanitis
13maintainer: example@example.com
13maintainer: example@example.com
14copyright: 2018 Andy Arvanitis
14copyright: 2018 Andy Arvanitis
15license: BSD3
15license: BSD3
16license-file: LICENSE
16license-file: LICENSE
17build-type: Simple
17build-type: Simple
18+cabal-version: >= 1.10
18extra-source-files:
19extra-source-files:
19− README.md
20 ChangeLog.md
20 ChangeLog.md
21− stack.yaml
21+ README.md
22− support/ffi-loader.go
23− support/go.mod.ffi-loader
24− support/go.mod.output
25− support/go.mod.working
26
22
27source-repository head
23source-repository head
28 type: git
24 type: git
29− location: https://github.com/andyarvanitis/purescript-native
25+ location: https://github.com/andyarvanitis/purescript-cpp
30
26
31library
27library
32 exposed-modules:
28 exposed-modules:
33− CodeGen.IL
29+ CodeGen.Cpp
34− CodeGen.IL.Common
35− CodeGen.IL.Optimizer
30+ CodeGen.Cpp.Common
36− CodeGen.IL.Optimizer.Common
37− CodeGen.IL.Optimizer.Inliner
38− CodeGen.IL.Optimizer.MagicDo
31+ CodeGen.Cpp.Optimizer
39− CodeGen.IL.Optimizer.TCO
32+ CodeGen.Cpp.Optimizer.TCO
40− CodeGen.IL.Printer
33+ CodeGen.Cpp.Printer
41 Tests
34 Tests
42 other-modules:
35 other-modules:
43− Paths_psgo
36+ Paths_pscpp
44 hs-source-dirs:
37 hs-source-dirs:
45 src
38 src
46− default-extensions:
39+ default-extensions: ConstraintKinds DataKinds DeriveFunctor EmptyDataDecls FlexibleContexts KindSignatures LambdaCase MultiParamTypeClasses NoImplicitPrelude PatternGuards PatternSynonyms RankNTypes RecordWildCards OverloadedStrings ScopedTypeVariables TupleSections ViewPatterns
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:
40 build-depends:
65− aeson >=1.0 && <1.5
41+ aeson >=1.0 && <1.3
66 , base >=4.7 && <5
42 , base >=4.7 && <5
67 , base-compat >=0.6.0
43 , base-compat >=0.6.0
68− , bytestring >=0.10.8.2 && <0.11
69 , containers
44 , containers
70 , directory
45 , directory
71 , file-embed
46 , file-embed
72− , filemanip
73 , filepath
47 , filepath
74− , gitrev
75− , monad-parallel >=0.7.2.2 && <0.8
76 , mtl >=2.1.0 && <2.3.0
48 , mtl >=2.1.0 && <2.3.0
77 , pattern-arrows >=0.0.2 && <0.1
49 , pattern-arrows >=0.0.2 && <0.1
78 , process
50 , process
@@ -83,61 +55,53 @@ library
83 , transformers >=0.3.0 && <0.6
55 , transformers >=0.3.0 && <0.6
84 default-language: Haskell2010
56 default-language: Haskell2010
85
57
86−executable psgo
58+executable pscpp
87 main-is: Main.hs
59 main-is: Main.hs
88 other-modules:
60 other-modules:
89− Paths_psgo
61+ Paths_pscpp
90 hs-source-dirs:
62 hs-source-dirs:
91 app
63 app
92 ghc-options: -threaded -rtsopts -with-rtsopts=-N
64 ghc-options: -threaded -rtsopts -with-rtsopts=-N
93 build-depends:
65 build-depends:
94− aeson >=1.0 && <1.5
66+ aeson >=1.0 && <1.3
95 , base >=4.7 && <5
67 , base >=4.7 && <5
96 , base-compat >=0.6.0
68 , base-compat >=0.6.0
97− , bytestring >=0.10.8.2 && <0.11
98 , containers
69 , containers
99 , directory
70 , directory
100 , file-embed
71 , file-embed
101− , filemanip
102 , filepath
72 , filepath
103− , gitrev
104− , monad-parallel >=0.7.2.2 && <0.8
105 , mtl >=2.1.0 && <2.3.0
73 , mtl >=2.1.0 && <2.3.0
106 , pattern-arrows >=0.0.2 && <0.1
74 , pattern-arrows >=0.0.2 && <0.1
107 , process
75 , process
108 , protolude >=0.1.6
76 , protolude >=0.1.6
109− , psgo
77+ , pscpp
110 , purescript
78 , purescript
111 , safe >=0.3.9 && <0.4
79 , safe >=0.3.9 && <0.4
112 , text
80 , text
113 , transformers >=0.3.0 && <0.6
81 , transformers >=0.3.0 && <0.6
114 default-language: Haskell2010
82 default-language: Haskell2010
115
83
116−test-suite psgo-test
84+test-suite pscpp-test
117 type: exitcode-stdio-1.0
85 type: exitcode-stdio-1.0
118 main-is: Spec.hs
86 main-is: Spec.hs
119 other-modules:
87 other-modules:
120− Paths_psgo
88+ Paths_pscpp
121 hs-source-dirs:
89 hs-source-dirs:
122 test
90 test
123 ghc-options: -threaded -rtsopts -with-rtsopts=-N
91 ghc-options: -threaded -rtsopts -with-rtsopts=-N
124 build-depends:
92 build-depends:
125− aeson >=1.0 && <1.5
93+ aeson >=1.0 && <1.3
126 , base >=4.7 && <5
94 , base >=4.7 && <5
127 , base-compat >=0.6.0
95 , base-compat >=0.6.0
128− , bytestring >=0.10.8.2 && <0.11
129 , containers
96 , containers
130 , directory
97 , directory
131 , file-embed
98 , file-embed
132− , filemanip
133 , filepath
99 , filepath
134− , gitrev
135− , monad-parallel >=0.7.2.2 && <0.8
136 , mtl >=2.1.0 && <2.3.0
100 , mtl >=2.1.0 && <2.3.0
137 , pattern-arrows >=0.0.2 && <0.1
101 , pattern-arrows >=0.0.2 && <0.1
138 , process
102 , process
139 , protolude >=0.1.6
103 , protolude >=0.1.6
140− , psgo
104+ , pscpp
141 , purescript
105 , purescript
142 , safe >=0.3.9 && <0.4
106 , safe >=0.3.9 && <0.4
143 , text
107 , text
runtime/purescript.hnew file+168
@@ -0,0 +1,168 @@
1+///////////////////////////////////////////////////////////////////////////////
2+//
3+// Module : purescript.h
4+// Copyright : (c) Andy Arvanitis 2018
5+// License : BSD
6+//
7+// Maintainer : Andy Arvanitis
8+// Stability : experimental
9+// Portability :
10+//
11+// Basic types and functions to support purescript-to-C++ rendering
12+//
13+///////////////////////////////////////////////////////////////////////////////
14+//
15+#ifndef purescript_H
16+#define purescript_H
17+
18+#include <memory>
19+#include <functional>
20+#include <deque>
21+#include <map>
22+#include <string>
23+
24+#include <iostream>
25+
26+namespace purescript {
27+
28+ using std::string;
29+
30+ class boxed : public std::shared_ptr<void> {
31+
32+ public:
33+ using fn_t = std::function<boxed(const boxed&)>;
34+ using eff_fn_t = std::function<boxed(void)>;
35+ using dict_t = std::map<const string, boxed>;
36+ using array_t = std::deque<boxed>;
37+
38+ public:
39+ using std::shared_ptr<void>::shared_ptr;
40+
41+ boxed(const int n) : std::shared_ptr<void>(std::make_shared<int>(n)) {
42+ }
43+
44+ boxed(const double n) : std::shared_ptr<void>(std::make_shared<double>(n)) {
45+ }
46+
47+ boxed(const bool b) : std::shared_ptr<void>(std::make_shared<bool>(b)) {
48+ }
49+
50+ boxed(const char s[]) : std::shared_ptr<void>(std::make_shared<string>(s)) {
51+ }
52+
53+ boxed(string&& s) : std::shared_ptr<void>(std::make_shared<string>(std::move(s))) {
54+ }
55+
56+ boxed(const std::nullptr_t) : std::shared_ptr<void>() {
57+ }
58+
59+ boxed(array_t&& l) : std::shared_ptr<void>(std::make_shared<array_t>(std::move(l))) {
60+ }
61+
62+ boxed(dict_t&& m) : std::shared_ptr<void>(std::make_shared<dict_t>(std::move(m))) {
63+ }
64+
65+ template <typename T,
66+ typename = typename std::enable_if<!std::is_same<boxed,T>::value>::type>
67+ boxed(const T& f,
68+ typename std::enable_if<std::is_assignable<std::function<boxed(boxed)>,T>::value>::type* = 0) : std::shared_ptr<void>(std::make_shared<fn_t>(f)) {
69+ }
70+
71+ template <typename T,
72+ typename = typename std::enable_if<!std::is_same<boxed,T>::value>::type>
73+ boxed(const T& f,
74+ typename std::enable_if<std::is_assignable<std::function<boxed(void)>,T>::value>::type* = 0) : std::shared_ptr<void>(std::make_shared<eff_fn_t>(f)) {
75+ }
76+
77+ template <typename T,
78+ typename = typename std::enable_if<!std::is_same<boxed,T>::value &&
79+ std::is_convertible<T,fn_t>::value>::type>
80+ auto operator=(const T& right) -> boxed& {
81+ if (std::shared_ptr<void>::operator bool()) {
82+ auto& f = *static_cast<fn_t*>(get());
83+ f = right;
84+ } else {
85+ reset(new fn_t(right));
86+ }
87+ return *this;
88+ }
89+
90+ auto operator()(const boxed& arg) const -> boxed {
91+ auto& f = *static_cast<fn_t*>(get());
92+ return f(arg);
93+ }
94+
95+ auto operator()() const -> boxed {
96+ auto& f = *static_cast<eff_fn_t*>(get());
97+ return f();
98+ }
99+
100+ auto operator[](const char key[]) const -> const boxed& {
101+ const auto& dict = *static_cast<const dict_t*>(get());
102+ return dict.at(key);
103+ }
104+
105+ auto operator[](const char key[]) -> boxed& {
106+ auto& dict = *static_cast<dict_t*>(get());
107+ return dict[key];
108+ }
109+
110+#ifdef NDEBUG
111+ auto operator[](const int index) const -> const boxed& {
112+ const auto& array = *static_cast<const array_t*>(get());
113+ return array[index];
114+ }
115+#else
116+ auto operator[](const int index) const -> const boxed& {
117+ const auto& array = *static_cast<const array_t*>(get());
118+ return array.at(index);
119+ }
120+#endif // NDEBUG
121+
122+ }; // class boxed
123+
124+ template <typename T, typename... Args>
125+ inline auto box(Args&&... args) -> boxed {
126+ return std::make_shared<T>(std::forward<Args>(args)...);
127+ }
128+
129+ template <typename T>
130+ inline auto unbox(const boxed& b) -> const T& {
131+ const auto& unboxed = *static_cast<const T*>(b.get());
132+ return unboxed;
133+ }
134+
135+ template <typename T,
136+ typename = typename std::enable_if<!std::is_same<T, const boxed&>::value>::type>
137+ constexpr auto unbox(const T value) -> T {
138+ return value;
139+ }
140+
141+ template <typename T>
142+ inline auto peek(boxed& b) -> T& {
143+ auto& unboxed = *static_cast<T*>(b.get());
144+ return unboxed;
145+ }
146+
147+ template <typename T>
148+ inline auto copy(const boxed& b) -> boxed {
149+ return box<T>(unbox<T>(b));
150+ }
151+
152+ using fn_t = boxed::fn_t;
153+ using eff_fn_t = boxed::eff_fn_t;
154+ using dict_t = boxed::dict_t;
155+ using array_t = boxed::array_t;
156+
157+ constexpr auto undefined = nullptr;
158+
159+} // namespace purescript
160+
161+#define FOREIGN_BEGIN(NS) namespace NS {\
162+ using namespace purescript;\
163+ auto _foreign_() -> dict_t&;\
164+ static const auto& NS ## _foreign_init = []() {\
165+ dict_t& exports = _foreign_();
166+#define FOREIGN_END return exports; }(); }
167+
168+#endif // purescript_H
src/CodeGen/Cpp.hsnew file+397
@@ -0,0 +1,397 @@
1+-- | This module generates code in the core imperative representation from
2+-- elaborated PureScript code.
3+module CodeGen.Cpp
4+ ( module AST
5+ , module Common
6+ , moduleToCpp
7+ ) where
8+
9+import Prelude.Compat
10+import Protolude (ordNub)
11+
12+import Control.Arrow ((&&&))
13+import Control.Monad (forM, liftM, replicateM, void)
14+import Control.Monad.Supply.Class
15+
16+import Data.List ((\\), delete, intersect)
17+import qualified Data.Foldable as F
18+import qualified Data.Map as M
19+import Data.Maybe (fromMaybe, isNothing)
20+import Data.Monoid ((<>))
21+import Data.String (fromString)
22+import Data.Text (Text)
23+import qualified Data.Text as T
24+
25+import Language.PureScript.AST.SourcePos
26+import Language.PureScript.CoreImp.AST (AST)
27+import qualified Language.PureScript.CoreImp.AST as AST
28+import Language.PureScript.CoreFn
29+import Language.PureScript.Crash
30+import Language.PureScript.Names
31+import Language.PureScript.Options
32+import Language.PureScript.PSString (PSString, mkString)
33+import Language.PureScript.Traversals (sndM)
34+import qualified Language.PureScript.Constants as C
35+
36+import System.FilePath.Posix ((</>))
37+
38+import CodeGen.Cpp.Common as Common
39+import CodeGen.Cpp.Optimizer
40+import CodeGen.Cpp.Printer
41+
42+data DeclType = ModuleDecl | LetDecl | RecLetDecl deriving (Eq)
43+
44+-- | Generate code in the simplified Objective-C intermediate representation for all declarations in a
45+-- module.
46+moduleToCpp
47+ :: forall m
48+ -- . (Monad m, MonadReader Options m, MonadSupply m, MonadError MultipleErrors m)
49+ . (Monad m, MonadSupply m)
50+ => Module Ann
51+ -> Maybe AST
52+ -> m (Text, [Text], [AST], Text, Text)
53+moduleToCpp (Module _ coms mn _ imps exports foreigns decls) _ =
54+ do
55+ let usedNames = concatMap getNames decls
56+ let mnLookup = renameImports usedNames imps
57+ cppImports <- traverse (importToCpp mnLookup) . (\\ (mn : C.primModules)) . (\\ [mn]) $ ordNub $ map snd imps
58+ interfaceImport <- importToCpp (renameImports [] [(emptyAnn, mn)]) mn
59+ let decls' = renameModules mnLookup decls
60+ cppDecls <- mapM (bindToCpp ModuleDecl) decls'
61+ optimized <- traverse (traverse optimize) cppDecls
62+ let interface = interfaceSource modName exports foreigns
63+ foreigns' = identToCpp <$> foreigns
64+ implHeader = implHeaderSource modName cppImports interfaceImport
65+ implFooter = implFooterSource modName foreigns
66+ return $ (interface, foreigns', concat optimized, implHeader, implFooter)
67+ where
68+ modName = moduleNameToCpp mn
69+
70+ -- | Extracts all declaration names from a binding group.
71+ getNames :: Bind Ann -> [Ident]
72+ getNames (NonRec _ ident _) = [ident]
73+ getNames (Rec vals) = map (snd . fst) vals
74+
75+ -- | Creates alternative names for each module to ensure they don't collide
76+ -- with declaration names.
77+ renameImports :: [Ident] -> [(Ann, ModuleName)] -> M.Map ModuleName (Ann, ModuleName)
78+ renameImports = go M.empty
79+ where
80+ go :: M.Map ModuleName (Ann, ModuleName) -> [Ident] -> [(Ann, ModuleName)] -> M.Map ModuleName (Ann, ModuleName)
81+ go acc used ((ann, mn') : mns') =
82+ let mni = Ident $ runModuleName mn'
83+ in if mn' /= mn && mni `elem` used
84+ then let newName = freshModuleName 1 mn' used
85+ in go (M.insert mn' (ann, newName) acc) (Ident (runModuleName newName) : used) mns'
86+ else go (M.insert mn' (ann, mn') acc) used mns'
87+ go acc _ [] = acc
88+
89+ freshModuleName :: Integer -> ModuleName -> [Ident] -> ModuleName
90+ freshModuleName i mn'@(ModuleName pns) used =
91+ let newName = ModuleName $ init pns ++ [ProperName $ runProperName (last pns) <> "_" <> T.pack (show i)]
92+ in if Ident (runModuleName newName) `elem` used
93+ then freshModuleName (i + 1) mn' used
94+ else newName
95+
96+ -- | Generates Objective-C code for a module import, binding the required module
97+ -- to the alternative
98+ importToCpp :: M.Map ModuleName (Ann, ModuleName) -> ModuleName -> m Text
99+ importToCpp mnLookup mn' = do
100+ let ((_, _, _, _), mnSafe) = fromMaybe (internalError "Missing value in mnLookup") $ M.lookup mn' mnLookup
101+ mname = moduleNameToCpp mnSafe
102+ pure $ "#include \"" <> mname <> "/" <> mname <> ".h\"\n"
103+
104+ -- | Replaces the `ModuleName`s in the AST so that the generated code refers to
105+ -- the collision-avoiding renamed module imports.
106+ renameModules :: M.Map ModuleName (Ann, ModuleName) -> [Bind Ann] -> [Bind Ann]
107+ renameModules mnLookup binds =
108+ let (f, _, _) = everywhereOnValues id goExpr goBinder
109+ in map f binds
110+ where
111+ goExpr :: Expr a -> Expr a
112+ goExpr (Var ann q) = Var ann (renameQual q)
113+ goExpr e = e
114+ goBinder :: Binder a -> Binder a
115+ goBinder (ConstructorBinder ann q1 q2 bs) = ConstructorBinder ann (renameQual q1) (renameQual q2) bs
116+ goBinder b = b
117+ renameQual :: Qualified a -> Qualified a
118+ renameQual (Qualified (Just mn') a) =
119+ let (_,mnSafe) = fromMaybe (internalError "Missing value in mnLookup") $ M.lookup mn' mnLookup
120+ in Qualified (Just mnSafe) a
121+ renameQual q = q
122+
123+ -- |
124+ -- Generate code in the simplified Objective-C intermediate representation for a declaration
125+ --
126+ bindToCpp :: DeclType -> Bind Ann -> m [AST]
127+ bindToCpp dt (NonRec ann ident val) = return <$> nonRecToCpp dt ann ident val
128+ bindToCpp dt (Rec vals) = forM vals (uncurry . uncurry $ nonRecToCpp dt')
129+ where
130+ dt' = if dt == LetDecl then RecLetDecl else dt
131+
132+ -- | Generate code in the simplified Objective-C intermediate representation for a single non-recursive
133+ -- declaration.
134+ --
135+ -- The main purpose of this function is to handle code generation for comments.
136+ nonRecToCpp :: DeclType -> Ann -> Ident -> Expr Ann -> m AST
137+ nonRecToCpp dt a i e@(extractAnn -> (_, com, _, _)) | not (null com) = do
138+ -- withoutComment <- asks optionsNoComments
139+ let withoutComment = False
140+ if withoutComment
141+ then nonRecToCpp dt a i (modifyAnn removeComments e)
142+ else AST.Comment Nothing com <$> nonRecToCpp dt a i (modifyAnn removeComments e)
143+ nonRecToCpp ModuleDecl (ss, _, _, _) ident val = do
144+ cpp <- valueToCpp val
145+ pure $ AST.Function Nothing (Just $ identToCpp ident) [] (AST.Block Nothing [AST.Return Nothing cpp])
146+ nonRecToCpp RecLetDecl (ss, _, _, _) ident val = do
147+ cpp <- valueToCpp val
148+ pure $
149+ let retainedName = identToCpp ident
150+ retained = AST.Var Nothing $ retainedName
151+ -- unretained = AST.Var Nothing $ retainedName <> unretainedSuffix
152+ -- alias = AST.Var Nothing . -- Note: prevents optimizer from eliminating this
153+ -- prettyPrintCpp1 $
154+ -- AST.VariableIntroduction Nothing retainedName (Just unretained)
155+ in
156+ case cpp of
157+ AST.Function _ name args (AST.Block Nothing cpps) ->
158+ AST.Assignment Nothing
159+ (retained)
160+ (AST.Function Nothing
161+ name
162+ args
163+ (AST.Block Nothing cpps))
164+ _ -> AST.Assignment Nothing retained cpp
165+ nonRecToCpp _ (ss, _, _, _) ident val = do
166+ cpp <- valueToCpp val
167+ pure $ AST.VariableIntroduction Nothing (identToCpp ident) (Just cpp)
168+
169+ -- | Generate code in the simplified Objective-C intermediate representation for a variable based on a
170+ -- PureScript identifier.
171+ var :: Ident -> AST
172+ var = AST.Var Nothing . identToCpp
173+
174+ -- | Generate code in the simplified Objective-C intermediate representation for an accessor based on
175+ -- a PureScript identifier. If the name is not valid in Objective-C (symbol based, reserved name) an
176+ -- indexer is returned.
177+ accessor :: Ident -> AST -> AST
178+ accessor (Ident prop) = accessorString $ mkString prop
179+ accessor (GenIdent _ _) = internalError "GenIdent in accessor"
180+ accessor UnusedIdent = internalError "UnusedIdent in accessor"
181+
182+ accessorString :: PSString -> AST -> AST
183+ accessorString prop = AST.Indexer Nothing (AST.StringLiteral Nothing prop)
184+
185+ -- | Generate code in the simplified Objective-C intermediate representation for a value or expression.
186+
187+ valueToCpp :: Expr Ann -> m AST
188+ valueToCpp (Literal (pos, _, _, _) l) = literalToValueCpp pos l
189+ valueToCpp (Accessor _ prop val) =
190+ accessorString prop <$> valueToCpp val
191+ valueToCpp (ObjectUpdate _ o ps) = do
192+ obj <- valueToCpp o
193+ updates <- mapM (sndM valueToCpp) ps
194+ obj' <- freshName
195+ let objVar = AST.Var Nothing obj'
196+ copy = AST.VariableIntroduction Nothing obj' (Just $ makeCopy obj)
197+ assign (k, v) = AST.Assignment Nothing (accessorString k objVar) v
198+ sts = copy : (assign <$> updates) ++ [AST.Return Nothing $ makeCopy objVar]
199+ return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing sts)) []
200+ valueToCpp (Abs _ arg val) = do
201+ ret <- valueToCpp val
202+ let cppArg = case arg of
203+ UnusedIdent -> []
204+ _ -> [identToCpp arg]
205+ return $ AST.Function Nothing Nothing cppArg (AST.Block Nothing [AST.Return Nothing ret])
206+ valueToCpp e@App{} = do
207+ let (f, args) = unApp e []
208+ args' <- mapM valueToCpp args
209+ case f of
210+ Var (_, _, _, Just IsNewtype) _ -> return (head args')
211+ _ -> flip (foldl (\fn a -> AST.App Nothing fn [a])) args' <$> valueToCpp f
212+ where
213+ unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann])
214+ unApp (App _ val arg) args = unApp val (arg : args)
215+ unApp other args = (other, args)
216+ -- valueToCpp (Var (_, _, _, Just IsForeign) qi@(Qualified (Just mn') ident)) =
217+ -- return $ if mn' == mn
218+ -- then AST.Var Nothing (moduleNameToCpp mn' <> "::" <> identToCpp ident)
219+ -- else varToCpp qi
220+ -- valueToCpp (Var (_, _, _, Just IsForeign) (Qualified (Just mn') ident)) =
221+ -- return $ AST.Var Nothing (moduleNameToCpp mn' <> "::" <> identToCpp ident)
222+ -- valueToCpp (Var (_, _, _, Just IsForeign) qi@(Qualified (Just mn') ident)) =
223+ -- return $ if mn' == mn
224+ -- then foreignIdent ident
225+ -- else varToCpp qi
226+ -- valueToCpp (Var (_, _, _, Just IsForeign) ident) =
227+ -- internalError $ "Encountered an unqualified reference to a foreign ident " ++ T.unpack (showQualified showIdent ident)
228+ valueToCpp (Var _ ident) = return $ varToCpp ident
229+ valueToCpp (Case (ss, _, _, _) values binders) = do
230+ vals <- mapM valueToCpp values
231+ bindersToCpp ss binders vals
232+ valueToCpp (Let _ ds val) = do
233+ let recurs = concatMap getNames $ filter isRec ds
234+ recurDs = (\v -> AST.VariableIntroduction Nothing (identToCpp v) (Just (AST.Var Nothing "box<fn_t>()"))) <$> recurs
235+ -- recurDsWeak = (\v -> AST.VariableIntroduction Nothing ("__unsafe_unretained " <> identToCpp v <> unretainedSuffix) Nothing) <$> recurs
236+ ds' <- concat <$> mapM (bindToCpp LetDecl) ds
237+ ret <- valueToCpp val
238+ return $
239+ AST.App Nothing
240+ (AST.Function Nothing
241+ Nothing
242+ []
243+ (AST.Block Nothing (recurDs ++ ds' ++ [AST.Return Nothing ret])))
244+ []
245+ where
246+ isRec :: Bind Ann -> Bool
247+ isRec (Rec _) = True
248+ isRec _ = False
249+ valueToCpp (Constructor (_, _, _, Just IsNewtype) _ (ProperName ctor) _) = error "IsNewtype"
250+ valueToCpp (Constructor _ _ (ProperName ctor) fields) =
251+ let body = AST.ObjectLiteral Nothing $ (mkString ctor, AST.BooleanLiteral Nothing True) :
252+ ((\field -> (mkString (identToCpp field), var field)) `map` fields)
253+ fn = foldr (\f inner -> AST.Function Nothing Nothing [identToCpp f] (AST.Block Nothing [AST.Return Nothing inner])) body fields
254+ in return fn
255+
256+
257+ iife :: Text -> [AST] -> AST
258+ iife v exprs = AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing $ exprs ++ [AST.Return Nothing $ AST.Var Nothing v])) []
259+
260+ literalToValueCpp :: SourceSpan -> Literal (Expr Ann) -> m AST
261+ literalToValueCpp ss (NumericLiteral (Left i)) = return $ AST.NumericLiteral (Just ss) (Left i)
262+ literalToValueCpp ss (NumericLiteral (Right n)) = return $ AST.NumericLiteral (Just ss) (Right n)
263+ literalToValueCpp ss (StringLiteral s) = return $ AST.StringLiteral (Just ss) s
264+ literalToValueCpp ss (CharLiteral c) = return $ AST.StringLiteral (Just ss) (fromString [c])
265+ literalToValueCpp ss (BooleanLiteral b) = return $ AST.BooleanLiteral (Just ss) b
266+ literalToValueCpp ss (ArrayLiteral xs) = AST.ArrayLiteral (Just ss) <$> mapM valueToCpp xs
267+ literalToValueCpp ss (ObjectLiteral ps) = AST.ObjectLiteral (Just ss) <$> mapM (sndM valueToCpp) ps
268+
269+ -- | Generate code in the simplified Objective-C intermediate representation for a reference to a
270+ -- variable.
271+ varToCpp :: Qualified Ident -> AST
272+ varToCpp (Qualified Nothing ident) = var ident
273+ varToCpp qual = qualifiedToCpp id qual
274+
275+ -- | Generate code in the simplified Objective-C intermediate representation for a reference to a
276+ -- variable that may have a qualified name.
277+ qualifiedToCpp :: (a -> Ident) -> Qualified a -> AST
278+ qualifiedToCpp f (Qualified (Just (ModuleName [ProperName mn'])) a) | mn' == C.prim = AST.Var Nothing . runIdent $ f a
279+ qualifiedToCpp f (Qualified (Just mn') a) = AST.Indexer Nothing (AST.Var Nothing . identToCpp $ f a) (AST.Var Nothing (moduleNameToCpp mn'))
280+ qualifiedToCpp f (Qualified _ a) = AST.Var Nothing $ identToCpp (f a)
281+
282+ -- foreignIdent :: Ident -> AST
283+ -- foreignIdent ident = accessorString (mkString $ runIdent ident) (AST.Var Nothing "$foreign")
284+
285+ -- | Generate code in the simplified Objective-C intermediate representation for pattern match binders
286+ -- and guards.
287+ bindersToCpp :: SourceSpan -> [CaseAlternative Ann] -> [AST] -> m AST
288+ bindersToCpp ss binders vals = do
289+ valNames <- replicateM (length vals) freshName
290+ let assignments = zipWith (AST.VariableIntroduction Nothing) valNames (map Just vals)
291+ cpps <- forM binders $ \(CaseAlternative bs result) -> do
292+ ret <- guardsToCpp result
293+ go valNames ret bs
294+ return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing (assignments ++ concat cpps ++ [AST.Throw Nothing (AST.StringLiteral Nothing $ mkString failedPatternMessage)])))
295+ []
296+ where
297+ go :: [Text] -> [AST] -> [Binder Ann] -> m [AST]
298+ go _ done [] = return done
299+ go (v:vs) done' (b:bs) = do
300+ done'' <- go vs done' bs
301+ binderToCpp v done'' b
302+ go _ _ _ = internalError "Invalid arguments to bindersToCpp"
303+
304+ failedPatternMessage :: Text
305+ failedPatternMessage = "Failed pattern match at " <> runModuleName mn <> " " <> displayStartEndPos ss <> ": "
306+
307+ guardsToCpp :: Either [(Guard Ann, Expr Ann)] (Expr Ann) -> m [AST]
308+ guardsToCpp (Left gs) = traverse genGuard gs where
309+ genGuard (cond, val) = do
310+ cond' <- valueToCpp cond
311+ val' <- valueToCpp val
312+ return
313+ (AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo cond' (AST.BooleanLiteral Nothing True))
314+ (AST.Block Nothing [AST.Return Nothing val']) Nothing)
315+
316+ guardsToCpp (Right v) = return . AST.Return Nothing <$> valueToCpp v
317+
318+ -- | Generate code in the simplified Objective-C intermediate representation for a pattern match
319+ -- binder.
320+ binderToCpp :: Text -> [AST] -> Binder Ann -> m [AST]
321+ binderToCpp _ done NullBinder{} = return done
322+ binderToCpp varName done (LiteralBinder _ l) =
323+ literalToBinderCpp varName done l
324+ binderToCpp varName done (VarBinder _ ident) =
325+ return (AST.VariableIntroduction Nothing (identToCpp ident) (Just (AST.Var Nothing varName)) : done)
326+ binderToCpp varName done (ConstructorBinder (_, _, _, Just IsNewtype) _ _ [b]) =
327+ binderToCpp varName done b
328+ binderToCpp varName done (ConstructorBinder (_, _, _, Just (IsConstructor ctorType fields)) _ (Qualified _ (ProperName ctor)) bs) = do
329+ cpp <- go (zip fields bs) done
330+ return $ case ctorType of
331+ ProductType -> cpp
332+ SumType ->
333+ [AST.IfElse Nothing (AST.InstanceOf Nothing (AST.Var Nothing varName) (AST.StringLiteral Nothing (mkString ctor)))
334+ (AST.Block Nothing cpp)
335+ Nothing]
336+ where
337+ go :: [(Ident, Binder Ann)] -> [AST] -> m [AST]
338+ go [] done' = return done'
339+ go ((field, binder) : remain) done' = do
340+ argVar <- freshName
341+ done'' <- go remain done'
342+ cpp <- binderToCpp argVar done'' binder
343+ return (AST.VariableIntroduction Nothing argVar (Just $ accessorString (mkString $ identToCpp field) $ AST.Var Nothing varName) : cpp)
344+
345+ binderToCpp _ _ ConstructorBinder{} =
346+ internalError "binderToCpp: Invalid ConstructorBinder in binderToCpp"
347+ binderToCpp varName done (NamedBinder _ ident binder) = do
348+ cpp <- binderToCpp varName done binder
349+ return (AST.VariableIntroduction Nothing (identToCpp ident) (Just (AST.Var Nothing varName)) : cpp)
350+
351+ literalToBinderCpp :: Text -> [AST] -> Literal (Binder Ann) -> m [AST]
352+ literalToBinderCpp varName done (NumericLiteral num) =
353+ return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.NumericLiteral Nothing num)) (AST.Block Nothing done) Nothing]
354+ literalToBinderCpp varName done (CharLiteral c) =
355+ return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.StringLiteral Nothing (fromString [c]))) (AST.Block Nothing done) Nothing]
356+ literalToBinderCpp varName done (StringLiteral str) =
357+ return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.StringLiteral Nothing str)) (AST.Block Nothing done) Nothing]
358+ literalToBinderCpp varName done (BooleanLiteral True) =
359+ return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.BooleanLiteral Nothing True)) (AST.Block Nothing done) Nothing]
360+ literalToBinderCpp varName done (BooleanLiteral False) =
361+ return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.BooleanLiteral Nothing False)) (AST.Block Nothing done) Nothing]
362+ -- return [AST.IfElse Nothing (AST.Unary Nothing AST.Not (AST.Var Nothing varName)) (AST.Block Nothing done) Nothing]
363+ literalToBinderCpp varName done (ObjectLiteral bs) = go done bs
364+ where
365+ go :: [AST] -> [(PSString, Binder Ann)] -> m [AST]
366+ go done' [] = return done'
367+ go done' ((prop, binder):bs') = do
368+ propVar <- freshName
369+ done'' <- go done' bs'
370+ cpp <- binderToCpp propVar done'' binder
371+ return (AST.VariableIntroduction Nothing propVar (Just (accessorString prop (AST.Var Nothing varName))) : cpp)
372+ literalToBinderCpp varName done (ArrayLiteral bs) = do
373+ cpp <- go done 0 bs
374+ return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo
375+ (arrayLength $ AST.Var Nothing varName)
376+ (AST.NumericLiteral Nothing (Left (fromIntegral $ length bs))))
377+ (AST.Block Nothing cpp) Nothing]
378+ where
379+ go :: [AST] -> Integer -> [Binder Ann] -> m [AST]
380+ go done' _ [] = return done'
381+ go done' index (binder:bs') = do
382+ elVar <- freshName
383+ done'' <- go done' (index + 1) bs'
384+ cpp <- binderToCpp elVar done'' binder
385+ return (AST.VariableIntroduction Nothing elVar (Just (AST.Indexer Nothing (AST.NumericLiteral Nothing (Left index)) (AST.Var Nothing varName))) : cpp)
386+
387+emptyAnn :: Ann
388+emptyAnn = (SourceSpan "" (SourcePos 0 0) (SourcePos 0 0), [], Nothing, Nothing)
389+
390+makeCopy :: AST -> AST
391+makeCopy obj = AST.App Nothing (AST.Var Nothing "copy<dict_t>") [obj]
392+
393+arrayLength :: AST -> AST
394+arrayLength array = applyMethod (AST.App Nothing (AST.Var Nothing "unbox<array_t>") [array]) "size"
395+
396+applyMethod :: AST -> Text -> AST
397+applyMethod obj method = (AST.Indexer Nothing (AST.ArrayLiteral Nothing [AST.Var Nothing method]) obj)
src/CodeGen/Cpp/Common.hsnew file+199
@@ -0,0 +1,199 @@
1+-- | Common code generation utility functions
2+module CodeGen.Cpp.Common where
3+
4+import Prelude.Compat
5+
6+import Data.Char
7+import Data.Monoid ((<>))
8+import Data.Text (Text)
9+import qualified Data.Text as T
10+
11+import Language.PureScript.Crash
12+import Language.PureScript.Names
13+
14+moduleNameToCpp :: ModuleName -> Text
15+moduleNameToCpp (ModuleName pns) =
16+ let name = T.intercalate "_" (runProperName `map` pns)
17+ in if nameIsCppBuiltIn name then "_" <> name else name
18+
19+-- | Convert an 'Ident' into a valid C++ identifier:
20+--
21+-- * Alphanumeric characters are kept unmodified.
22+--
23+-- * Reserved C++ identifiers are prefixed with '$$'.
24+--
25+-- * Symbols are prefixed with '$' followed by a symbol name or their ordinal value.
26+identToCpp :: Ident -> Text
27+identToCpp UnusedIdent = unusedName
28+identToCpp (Ident "$__unused") = unusedName
29+identToCpp (Ident name) = properToCpp name
30+identToCpp (GenIdent _ _) = internalError "GenIdent in identToCpp"
31+
32+unusedName :: Text
33+unusedName = "__unused_"
34+
35+properToCpp :: Text -> Text
36+properToCpp name
37+ | nameIsCppReserved name || nameIsCppBuiltIn name || prefixIsReserved name = name <> "_"
38+ | otherwise = T.concatMap identCharToText name
39+
40+-- | Test if a string is a valid AST identifier without escaping.
41+identNeedsEscaping :: Text -> Bool
42+identNeedsEscaping s = s /= properToCpp s || T.null s
43+
44+-- | Attempts to find a human-readable name for a symbol, if none has been specified returns the
45+-- ordinal value.
46+identCharToText :: Char -> Text
47+identCharToText c | isAlphaNum c = T.singleton c
48+identCharToText '_' = "_"
49+identCharToText '\'' = "ʹ"
50+identCharToText '$' = "$"
51+identCharToText c = '$' `T.cons` T.pack (show (ord c))
52+
53+-- | Checks whether an identifier name is reserved in C++.
54+nameIsCppReserved :: Text -> Bool
55+nameIsCppReserved name =
56+ name `elem` cppAnyReserved
57+
58+-- | Checks whether a name matches a built-in value in C++.
59+nameIsCppBuiltIn :: Text -> Bool
60+nameIsCppBuiltIn name =
61+ name `elem`
62+ [ "final"
63+ , "override"
64+ , "posix"
65+ , "std"
66+ ]
67+
68+cppAnyReserved :: [Text]
69+cppAnyReserved =
70+ concat
71+ [ cppKeywords
72+ , cppLiterals
73+ ]
74+
75+cppKeywords :: [Text]
76+cppKeywords =
77+ [ "alignas"
78+ , "alignof"
79+ , "and"
80+ , "and_eq"
81+ , "asm"
82+ , "assert"
83+ , "atomic_cancel"
84+ , "atomic_commit"
85+ , "atomic_noexcept"
86+ , "auto"
87+ , "bitand"
88+ , "bitor"
89+ , "bool"
90+ , "break"
91+ , "case"
92+ , "catch"
93+ , "char"
94+ , "char16_t"
95+ , "char32_t"
96+ , "class"
97+ , "compl"
98+ , "concept"
99+ , "const"
100+ , "constexpr"
101+ , "const_cast"
102+ , "continue"
103+ , "concept"
104+ , "decltype"
105+ , "default"
106+ , "delete"
107+ , "do"
108+ , "double"
109+ , "dynamic_cast"
110+ , "else"
111+ , "enum"
112+ , "explicit"
113+ , "export"
114+ , "extern"
115+ , "false"
116+ , "float"
117+ , "for"
118+ , "friend"
119+ , "goto"
120+ , "if"
121+ , "import"
122+ , "inline"
123+ , "int"
124+ , "long"
125+ , "module"
126+ , "mutable"
127+ , "namespace"
128+ , "new"
129+ , "noexcept"
130+ , "not"
131+ , "not_eq"
132+ , "nullptr"
133+ , "nullptr_t"
134+ , "operator"
135+ , "or"
136+ , "or_eq"
137+ , "private"
138+ , "protected"
139+ , "public"
140+ , "register"
141+ , "reinterpret_cast"
142+ , "requires"
143+ , "return"
144+ , "short"
145+ , "signed"
146+ , "sizeof"
147+ , "static"
148+ , "static_assert"
149+ , "static_cast"
150+ , "struct"
151+ , "switch"
152+ , "synchronized"
153+ , "template"
154+ , "this"
155+ , "thread_local"
156+ , "throw"
157+ , "true"
158+ , "try"
159+ , "typedef"
160+ , "typeid"
161+ , "typename"
162+ , "typeof"
163+ , "union"
164+ , "unsigned"
165+ , "using"
166+ , "virtual"
167+ , "void"
168+ , "volatile"
169+ , "wchar_t"
170+ , "while"
171+ , "xor"
172+ , "xor_eq"
173+ ]
174+
175+prefixIsReserved :: Text -> Bool
176+prefixIsReserved name =
177+ any (`T.isPrefixOf` name)
178+ [ "__"
179+ ]
180+
181+cppLiterals :: [Text]
182+cppLiterals =
183+ [ "box"
184+ , "boxed"
185+ , "copy"
186+ , "errno"
187+ , "peek"
188+ , "purescript"
189+ , "runtime_error"
190+ , "setjmp"
191+ , "string"
192+ , "unbox"
193+ ]
194+
195+unretainedSuffix :: Text
196+unretainedSuffix = "_unretained"
197+
198+tcoLoop :: Text
199+tcoLoop = "_TCO_loop_"
src/CodeGen/Cpp/Optimizer.hsnew file+130
@@ -0,0 +1,130 @@
1+-- | This module optimizes code in the intermediate representation.
2+module CodeGen.Cpp.Optimizer (optimize) where
3+
4+import Prelude.Compat
5+
6+import Control.Monad.Supply.Class (MonadSupply)
7+import Data.Text (Text)
8+import Language.PureScript.CoreImp.AST
9+import Language.PureScript.CoreImp.Optimizer.Blocks
10+import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict)
11+import Language.PureScript.CoreImp.Optimizer.Inliner hiding (inlineUnsafeCoerce, inlineUnsafePartial)
12+import Language.PureScript.CoreImp.Optimizer.Unused
13+import Language.PureScript.PSString (PSString, decodeString)
14+
15+import CodeGen.Cpp.Common (unusedName)
16+import CodeGen.Cpp.Optimizer.TCO
17+
18+import qualified Language.PureScript.Constants as C
19+
20+
21+-- | Apply a series of optimizer passes to simplified Objective-C code
22+optimize :: MonadSupply m => AST -> m AST
23+-- optimize = untilFixedPoint $ return . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp . tco
24+optimize cpp = do
25+ cpp' <- untilFixedPoint (return . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp) cpp
26+ untilFixedPoint (return . tidyUp) . tco
27+ =<< untilFixedPoint (return . magicDo')
28+ =<< untilFixedPoint (return . magicDo) cpp'
29+
30+
31+ where
32+ tidyUp :: AST -> AST
33+ tidyUp = applyAll
34+ [ collapseNestedBlocks
35+ -- , collapseNestedIfs
36+ , collapseIfChecks
37+ , removeCodeAfterReturnStatements
38+ , unThunk
39+ , etaConvert
40+ , evaluateIifes
41+ , inlineVariables
42+ ]
43+
44+untilFixedPoint :: (Monad m, Eq a) => (a -> m a) -> a -> m a
45+untilFixedPoint f = go
46+ where
47+ go a = do
48+ a' <- f a
49+ if a' == a then return a' else go a'
50+
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+collapseIfChecks :: AST -> AST
70+collapseIfChecks = everywhere collapse where
71+ collapse :: AST -> AST
72+ collapse (IfElse _ (Binary _ EqualTo (BooleanLiteral _ True) (BooleanLiteral _ True)) (Block _ [exprs]) _) = exprs
73+ collapse (IfElse _ (Binary _ EqualTo (Indexer _ (Var _ prop) (Var _ val)) (BooleanLiteral _ True)) (Block _ [exprs]) _)
74+ | prop == "otherwise" && val == "Data_Boolean" = exprs
75+ collapse exprs = exprs
76+
77+magicDo :: AST -> AST
78+magicDo = magicDo'' C.eff C.effDictionaries
79+
80+magicDo' :: AST -> AST
81+magicDo' = magicDo'' C.effect C.effectDictionaries
82+
83+magicDo'' :: Text -> C.EffectDictionaries -> AST -> AST
84+magicDo'' effectModule C.EffectDictionaries{..} = everywhereTopDown convert
85+ where
86+ -- Desugar monomorphic calls to >>= and return for the Eff monad
87+ convert :: AST -> AST
88+ -- Desugar pure
89+ convert (App _ (App _ pure' [val]) []) | isPure pure' = val
90+ -- Desugar discard
91+ convert (App _ (App _ bind [m]) [Function s1 Nothing [unused] (Block s2 cpp)]) | isDiscard bind && unused == unusedName =
92+ Function s1 Nothing [] $ Block s2 (App s2 m [] : map applyReturns cpp )
93+ -- Desugar bind
94+ convert (App _ (App _ bind [m]) [Function s1 Nothing [arg] (Block s2 cpp)]) | isBind bind =
95+ Function s1 Nothing [] $ Block s2 (VariableIntroduction s2 arg (Just (App s2 m [])) : map applyReturns cpp)
96+ -- Inline double applications
97+ convert (App _ (App s1 (Function s2 Nothing [] (Block ss body)) []) []) =
98+ App s1 (Function s2 Nothing [] (Block ss (applyReturns `fmap` body))) []
99+ convert other = other
100+
101+ -- Check if an expression represents a monomorphic call to >>= for the Eff monad
102+ isBind (App _ fn [dict]) | isDict (effectModule, edBindDict) dict && isBindPoly fn = True
103+ isBind _ = False
104+ -- Check if an expression represents a call to @discard@
105+ isDiscard (App _ (App _ fn [dict1]) [dict2])
106+ | isDict (C.controlBind, C.discardUnitDictionary) dict1 &&
107+ isDict (effectModule, edBindDict) dict2 &&
108+ isDiscardPoly fn = True
109+ isDiscard _ = False
110+ -- Check if an expression represents a monomorphic call to pure or return for the Eff applicative
111+ isPure (App _ fn [dict]) | isDict (effectModule, edApplicativeDict) dict && isPurePoly fn = True
112+ isPure _ = False
113+ -- Check if an expression represents the polymorphic >>= function
114+ isBindPoly = isDict (C.controlBind, C.bind)
115+ -- Check if an expression represents the polymorphic pure function
116+ isPurePoly = isDict (C.controlApplicative, C.pure')
117+ -- Check if an expression represents the polymorphic discard function
118+ isDiscardPoly = isDict (C.controlBind, C.discard)
119+
120+
121+ applyReturns :: AST -> AST
122+ applyReturns (Return ss ret) = Return ss (App ss ret [])
123+ applyReturns (Block ss cpps) = Block ss (map applyReturns cpps)
124+ applyReturns (IfElse ss cond t f) = IfElse ss cond (applyReturns t) (applyReturns `fmap` f)
125+ applyReturns other = other
126+
127+ isDict :: (Text, PSString) -> AST -> Bool
128+ isDict (moduleName, dictName) (Indexer _ (Var _ x) (Var _ y)) =
129+ Just x == decodeString dictName && y == moduleName
130+ isDict _ _ = False
src/CodeGen/IL/Optimizer/TCO.hs → src/CodeGen/Cpp/Optimizer/TCO.hsrenamed+23−20
@@ -1,4 +1,4 @@
1−module CodeGen.IL.Optimizer.TCO (tco, tcoLoop) where
1+module CodeGen.Cpp.Optimizer.TCO (tco) where
2
2
3import Prelude.Compat
3import Prelude.Compat
4
4
@@ -7,26 +7,24 @@ import Data.Monoid ((<>))
7import Language.PureScript.CoreImp.AST
7import Language.PureScript.CoreImp.AST
8import Language.PureScript.AST.SourcePos (SourceSpan)
8import Language.PureScript.AST.SourcePos (SourceSpan)
9import Safe (headDef, tailSafe)
9import Safe (headDef, tailSafe)
10−import CodeGen.IL.Common
11
10
12−import qualified Language.PureScript.Constants as C
11+import CodeGen.Cpp.Common
13
12
14−tcoLoop :: Text
13+import qualified Language.PureScript.Constants as C
15−tcoLoop = "ṩtco_loop"
16
14
17−tco :: AST -> AST -> AST
15+tco :: AST -> AST
18−tco mn = everywhere convert where
16+tco = everywhere convert where
19 tcoVar :: Text -> Text
17 tcoVar :: Text -> Text
20− tcoVar arg = "ṩtco_var_" <> arg
18+ tcoVar arg = "_TCO_" <> arg <> "_"
21
19
22 copyVar :: Text -> Text
20 copyVar :: Text -> Text
23− copyVar arg = "ṩcopy_" <> arg
21+ copyVar arg = "_TCO_copy_" <> arg <> "_"
24
22
25 tcoDone :: Text
23 tcoDone :: Text
26− tcoDone = "ṩtco_done"
24+ tcoDone = "_TCO_done_"
27
25
28 tcoResult :: Text
26 tcoResult :: Text
29− tcoResult = "ṩtco_result"
27+ tcoResult = "_TCO_result_"
30
28
31 convert :: AST -> AST
29 convert :: AST -> AST
32 convert fn@(Function ss (Just name) args _)
30 convert fn@(Function ss (Just name) args _)
@@ -40,6 +38,7 @@ tco mn = everywhere convert where
40 | isTailRecursive name body'
38 | isTailRecursive name body'
41 = Assignment ss (Var ss name) (replace (toLoop name outerArgs innerArgs body'))
39 = Assignment ss (Var ss name) (replace (toLoop name outerArgs innerArgs body'))
42 where
40 where
41+ -- fn = Function ss' Nothing args (Block ss' stmts)
43 innerArgs = headDef [] argss
42 innerArgs = headDef [] argss
44 outerArgs = concat . reverse $ tailSafe argss
43 outerArgs = concat . reverse $ tailSafe argss
45 (argss, body', replace) = collectAllFunctionArgs [] id fn
44 (argss, body', replace) = collectAllFunctionArgs [] id fn
@@ -92,14 +91,18 @@ tco mn = everywhere convert where
92 toLoop :: Text -> [Text] -> [Text] -> AST -> AST
91 toLoop :: Text -> [Text] -> [Text] -> AST -> AST
93 toLoop ident outerArgs innerArgs js =
92 toLoop ident outerArgs innerArgs js =
94 Block rootSS $
93 Block rootSS $
95− concatMap (\arg -> [ VariableIntroduction rootSS (tcoVar arg) . Just $
94+ concatMap (\arg -> [ VariableIntroduction rootSS (tcoVar arg) Nothing
96− Var rootSS (copyVar arg) ]) (outerArgs ++ innerArgs) ++
95+ , Assignment rootSS (Var rootSS (tcoVar arg)) (Var rootSS (copyVar arg)) ]) (outerArgs ++ innerArgs) ++
97− [ Assignment rootSS (Var rootSS $ "var " <> tcoDone) $ BooleanLiteral Nothing False
96+ -- [ VariableIntroduction rootSS (blockDecl <> tcoDone) Nothing
97+ [ Var rootSS ("bool " <> tcoDone <> " = false")
98+ -- , Assignment rootSS (Var rootSS tcoDone) (BooleanLiteral rootSS False)
99+ -- , Assignment rootSS (Var rootSS tcoDone) (Var rootSS "NO")
98 , VariableIntroduction rootSS tcoResult Nothing
100 , VariableIntroduction rootSS tcoResult Nothing
99− , Assignment rootSS (Var rootSS $ "var " <> tcoLoop) (Function rootSS (Just tcoLoop) (outerArgs ++ innerArgs) (Block rootSS [loopify js]))
101+ , Assignment rootSS (Var rootSS ("const auto " <> tcoLoop)) (Function rootSS (Just tcoLoop) (outerArgs ++ innerArgs) (Block rootSS [loopify js]))
102+ -- , While rootSS (Binary Nothing EqualTo (Var rootSS tcoDone) (BooleanLiteral Nothing False))
100 , While rootSS (Unary Nothing Not (Var rootSS tcoDone))
103 , While rootSS (Unary Nothing Not (Var rootSS tcoDone))
101 (Block rootSS
104 (Block rootSS
102− [(Assignment rootSS (Var rootSS tcoResult) (App rootSS (Var rootSS tcoLoop) ((map (Var rootSS . tcoVar) outerArgs) ++ (map (Var rootSS . copyVar) innerArgs))))])
105+ [(Assignment rootSS (Var rootSS tcoResult) (App rootSS (Var rootSS tcoLoop) ((map (Var rootSS . tcoVar) outerArgs) ++ (map (Var rootSS . tcoVar) innerArgs))))])
103 , Return rootSS (Var rootSS tcoResult)
106 , Return rootSS (Var rootSS tcoResult)
104 ]
107 ]
105 where
108 where
@@ -115,7 +118,7 @@ tco mn = everywhere convert where
115 zipWith (\val arg ->
118 zipWith (\val arg ->
116 Assignment ss (Var ss (tcoVar arg)) val) allArgumentValues outerArgs
119 Assignment ss (Var ss (tcoVar arg)) val) allArgumentValues outerArgs
117 ++ zipWith (\val arg ->
120 ++ zipWith (\val arg ->
118− Assignment ss (Var ss (copyVar arg)) val) (drop (length outerArgs) allArgumentValues) innerArgs
121+ Assignment ss (Var ss (tcoVar arg)) val) (drop (length outerArgs) allArgumentValues) innerArgs
119 ++ [ ReturnNoResult ss ]
122 ++ [ ReturnNoResult ss ]
120 | otherwise = Block ss [ markDone ss, Return ss ret ]
123 | otherwise = Block ss [ markDone ss, Return ss ret ]
121 loopify (ReturnNoResult ss) = Block ss [ markDone ss, ReturnNoResult ss ]
124 loopify (ReturnNoResult ss) = Block ss [ markDone ss, ReturnNoResult ss ]
@@ -128,6 +131,7 @@ tco mn = everywhere convert where
128
131
129 markDone :: Maybe SourceSpan -> AST
132 markDone :: Maybe SourceSpan -> AST
130 markDone ss = Assignment ss (Var ss tcoDone) (BooleanLiteral ss True)
133 markDone ss = Assignment ss (Var ss tcoDone) (BooleanLiteral ss True)
134+ -- markDone ss = Assignment ss (Var ss tcoDone) (Var ss "YES")
131
135
132 collectArgs :: [[AST]] -> AST -> [[AST]]
136 collectArgs :: [[AST]] -> AST -> [[AST]]
133 collectArgs acc (App _ fn args') = collectArgs (args' : acc) fn
137 collectArgs acc (App _ fn args') = collectArgs (args' : acc) fn
@@ -135,7 +139,6 @@ tco mn = everywhere convert where
135
139
136 isSelfCall :: Text -> AST -> Bool
140 isSelfCall :: Text -> AST -> Bool
137 isSelfCall ident (App _ (Var _ ident') _) = ident == ident'
141 isSelfCall ident (App _ (Var _ ident') _) = ident == ident'
138− isSelfCall ident (App _ (Indexer _ (Var _ ident') (Var _ mn')) _) = mn' == "" && ident == ident'
142+ isSelfCall ident (App _ (Indexer _ (Var _ ident') _) _) = ident == ident'
139− isSelfCall ident (App _ (Indexer _ (Var _ ident') mn') _) = mn' == mn && ident == ident'
140 isSelfCall ident (App _ fn _) = isSelfCall ident fn
143 isSelfCall ident (App _ fn _) = isSelfCall ident fn
141− isSelfCall _ _ = False
144+ isSelfCall _ _ = False
src/CodeGen/Cpp/Printer.hsnew file+502
@@ -0,0 +1,502 @@
1+-- | Pretty printer for the AST
2+module CodeGen.Cpp.Printer
3+ ( prettyPrintCpp
4+ , prettyPrintCpp1
5+ , prettyPrintCppWithSourceMaps
6+ , interfaceSource
7+ , implHeaderSource
8+ , implFooterSource
9+ ) where
10+
11+import Prelude.Compat
12+
13+import Control.Arrow ((<+>))
14+import Control.Monad (forM, mzero)
15+import Control.Monad.State (StateT, evalStateT)
16+import Control.PatternArrows
17+import Data.Char (isAscii)
18+import Data.List ((\\))
19+import qualified Control.Arrow as A
20+
21+import Data.Maybe (fromMaybe)
22+import Data.Monoid ((<>))
23+import Data.Text (Text)
24+import qualified Data.Text as T
25+
26+import Language.PureScript.AST (SourceSpan(..))
27+import Language.PureScript.CoreImp.AST
28+import Language.PureScript.Comments
29+import Language.PureScript.Crash
30+import Language.PureScript.Names (Ident)
31+import Language.PureScript.Pretty.Common
32+import Language.PureScript.PSString (PSString, decodeString, mkString)
33+import CodeGen.Cpp.Common
34+
35+import qualified Language.PureScript.Constants as C
36+
37+-- TODO (Christoph): Get rid of T.unpack / pack
38+
39+literals :: (Emit gen) => Pattern PrinterState AST gen
40+literals = mkPattern' match'
41+ where
42+ match' :: (Emit gen) => AST -> StateT PrinterState Maybe gen
43+ match' cpp = (addMapping' (getSourceSpan cpp) <>) <$> match cpp
44+
45+ match :: (Emit gen) => AST -> StateT PrinterState Maybe gen
46+ match (NumericLiteral _ n) = return $ emit $ T.pack $ either show show n
47+ match (StringLiteral _ s)
48+ | Just s' <- decodeString s
49+ , T.all isAscii s' = return $ emit $ stringLiteral s
50+ match (StringLiteral _ s) = return $ emit $ "u8" <> stringLiteral s
51+ match (BooleanLiteral _ True) = return $ emit "true"
52+ match (BooleanLiteral _ False) = return $ emit "false"
53+ -- match (ArrayLiteral _ []) = return $ emit "std::initializer_list<boxed>{}"
54+ match (ArrayLiteral _ xs) = mconcat <$> sequence
55+ [ return $ emit "array_t{ {"
56+ , intercalate (emit ", ") <$> forM xs prettyPrintCpp'
57+ , return $ emit "} }"
58+ ]
59+ -- match (ObjectLiteral _ []) = return $ emit "std::initializer_list<std::pair<const string, boxed>>{}"
60+ match (ObjectLiteral _ ps) = mconcat <$> sequence
61+ [ return $ emit "dict_t{ {\n"
62+ , withIndent $ do
63+ cpps <- forM ps $ \(key, value) -> do
64+ value' <- prettyPrintCpp' value
65+ return $ emit "{ " <> objectPropertyToString key <> value' <> emit " }"
66+ indentString <- currentIndent
67+ return $ intercalate (emit ",\n") $ map (indentString <>) cpps
68+ , return $ emit "\n"
69+ , currentIndent
70+ , return $ emit "} }"
71+ ]
72+ where
73+ objectPropertyToString :: (Emit gen) => PSString -> gen
74+ objectPropertyToString s = emit $ stringLiteral s <> ", "
75+ match (Block _ sts) = mconcat <$> sequence
76+ [ return $ emit "{\n"
77+ , withIndent $ prettyStatements sts
78+ , return $ emit "\n"
79+ , currentIndent
80+ , return $ emit "}"
81+ ]
82+ match (Var _ ident) = return $ emit ident
83+ match (VariableIntroduction _ ident value) = mconcat <$> sequence
84+ [ return $ emit $ "boxed " <> ident
85+ , maybe (return mempty) (fmap (emit " = " <>) . prettyPrintCpp') value
86+ ]
87+ match (Assignment _ target@(Var _ var) value@(Function _ Nothing args body)) = mconcat <$> sequence
88+ [ prettyPrintCpp' target
89+ , return $ emit " = "
90+ , return $ emit "[=, &"
91+ , prettyPrintCpp' target
92+ , return $ emit "]"
93+ , return $ emit "("
94+ , return $ emit $ intercalate ", " (("const boxed& " <>) <$> args)
95+ , return $ emit ") -> boxed "
96+ , prettyPrintCpp' body
97+ ]
98+ match (Assignment _ target value) = mconcat <$> sequence
99+ [ prettyPrintCpp' target
100+ , return $ emit " = "
101+ , prettyPrintCpp' value
102+ ]
103+ match (App _ val []) = mconcat <$> sequence
104+ [ prettyPrintCpp' val
105+ , return $ emit "()"
106+ ]
107+ match (App ss (App _ (App _ (Indexer _ (Var _ fn) (Var _ fnMod)) [Indexer _ (Var _ dict) (Var _ dictMod)]) [x]) [y])
108+ | fnMod == dictMod
109+ , dictMod == C.dataEq || dictMod == C.dataSemiring || dictMod == C.dataRing || dictMod == C.dataEuclideanRing
110+ , Just t <- unboxType dict
111+ = mconcat <$> sequence
112+ [ return $ emit $ unbox t x
113+ , return $ emit $ renderOp fn
114+ , return $ emit $ unbox t y
115+ ]
116+ match (App _ val args) = mconcat <$> sequence
117+ [ prettyPrintCpp' val
118+ , return $ emit "("
119+ , intercalate (emit ", ") <$> forM args prettyPrintCpp'
120+ , return $ emit ")"
121+ ]
122+ match (Function _ (Just name) [] ret@(Block _ [Return _ (Function {})])) = mconcat <$> sequence
123+ [ return $ emit "auto "
124+ , return $ emit name
125+ , return $ emit "() -> boxed "
126+ , prettyPrintCpp' ret
127+ ]
128+ match (Function _ (Just name) [] (Block _ [Return _ ret])) = mconcat <$> sequence
129+ [ return $ emit "auto "
130+ , return $ emit name
131+ , return $ emit "() -> boxed "
132+ , return $ emit "{\n"
133+ , withIndent $ do
134+ indentString <- currentIndent
135+ return $ indentString <> emit dispatchOnceBegin
136+ , withIndent $ do
137+ prettyPrintCpp' ret
138+ , return $ emit ";\n"
139+ , withIndent $ do
140+ indentString <- currentIndent
141+ return $ indentString <> emit dispatchOnceEnd
142+ , return $ emit "\n}"
143+ ]
144+ -- match (Function _ (Just name) [] ret) = mconcat <$> sequence
145+ match (Function _ (Just name) [] (Block ss sts)) | Return _ ret <- last sts = mconcat <$> sequence
146+ [ return $ emit "auto "
147+ , return $ emit name
148+ , return $ emit "() -> boxed {\n"
149+ , withIndent $ do
150+ prettyStatements $ init sts
151+ , return $ emit "\n"
152+ , withIndent $ do
153+ indentString <- currentIndent
154+ return $ indentString <> emit dispatchOnceBegin
155+ , withIndent $ do
156+ prettyPrintCpp' ret
157+ , return $ emit ";\n"
158+ , withIndent $ do
159+ indentString <- currentIndent
160+ return $ indentString <> emit dispatchOnceEnd
161+ , return $ emit "\n}"
162+ ]
163+ match (Function _ name args ret) = mconcat <$> sequence
164+ [ return $ emit $ if name == (Just tcoLoop) then "[&]" else "[=]"
165+ , return $ emit "("
166+ , return $ emit $ intercalate ", " (("const boxed& " <>) <$> args)
167+ , return $ emit ") -> boxed "
168+ , prettyPrintCpp' ret
169+ ]
170+ match (Indexer _ prop@(Var _ name) val) = mconcat <$> sequence
171+ [ prettyPrintCpp' val
172+ , return $ emit "::"
173+ , prettyPrintCpp' prop
174+ , return $ emit "()"
175+ ]
176+ match (Indexer _ (ArrayLiteral _ [name]) val) = mconcat <$> sequence
177+ [ prettyPrintCpp' val
178+ , return $ emit "."
179+ , prettyPrintCpp' name
180+ , return $ emit "()"
181+ ]
182+ match (Indexer _ (NumericLiteral _ (Left index)) val) = mconcat <$> sequence
183+ [ prettyPrintCpp' val
184+ , return $ emit "["
185+ , return . emit . T.pack $ show index
186+ , return $ emit "]"
187+ ]
188+ match (Indexer _ prop val) = mconcat <$> sequence
189+ [ prettyPrintCpp' val
190+ , return $ emit "["
191+ , prettyPrintCpp' prop
192+ , return $ emit "]"
193+ ]
194+ match (InstanceOf _ val ty) = mconcat <$> sequence
195+ [ return . emit $ unbox dictType val
196+ , return $ emit ".count("
197+ , prettyPrintCpp' ty
198+ , return $ emit ") == 1"
199+ ]
200+ match (While _ cond sts) = mconcat <$> sequence
201+ [ return $ emit "while ("
202+ , prettyPrintCpp' cond
203+ , return $ emit ") "
204+ , prettyPrintCpp' sts
205+ ]
206+ match (For _ ident start end sts) = mconcat <$> sequence
207+ [ return $ emit $ "for (boxed " <> ident <> " = "
208+ , prettyPrintCpp' start
209+ , return $ emit $ "; " <> ident <> " < "
210+ , prettyPrintCpp' end
211+ , return $ emit $ "; " <> ident <> "++) "
212+ , prettyPrintCpp' sts
213+ ]
214+ match (ForIn _ ident obj sts) = mconcat <$> sequence
215+ [ return $ emit $ "for (boxed " <> ident <> " in "
216+ , prettyPrintCpp' obj
217+ , return $ emit ") "
218+ , prettyPrintCpp' sts
219+ ]
220+ match (IfElse _ (Binary _ EqualTo cond (BooleanLiteral Nothing True)) thens elses) = mconcat <$> sequence
221+ [ return $ emit "if ("
222+ , return $ emit $ unbox "bool" cond
223+ , return $ emit ") "
224+ , prettyPrintCpp' thens
225+ , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintCpp') elses
226+ ]
227+ match (IfElse _ (Binary _ EqualTo cond (BooleanLiteral Nothing False)) thens elses) = mconcat <$> sequence
228+ [ return $ emit "if (!("
229+ , return $ emit $ unbox "bool" cond
230+ , return $ emit ")) "
231+ , prettyPrintCpp' thens
232+ , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintCpp') elses
233+ ]
234+ match (IfElse _ (Binary _ EqualTo x y@(NumericLiteral _ n)) thens elses) = mconcat <$> sequence
235+ [ return $ emit "if ("
236+ , return $ emit $ unbox t x
237+ , return $ emit $ " == "
238+ , prettyPrintCpp' y
239+ , return $ emit ") "
240+ , prettyPrintCpp' thens
241+ , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintCpp') elses
242+ ]
243+ where
244+ t | Left _ <- n = int
245+ | Right _ <- n = float
246+ match (IfElse _ (Binary _ EqualTo a b@StringLiteral{}) thens elses) = mconcat <$> sequence
247+ [ return $ emit "if ("
248+ , return $ emit $ unbox string a
249+ , return $ emit $ " == "
250+ , prettyPrintCpp' b
251+ , return $ emit ") "
252+ , prettyPrintCpp' thens
253+ , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintCpp') elses
254+ ]
255+ match (IfElse _ cond thens elses) = mconcat <$> sequence
256+ [ return $ emit "if ("
257+ , prettyPrintCpp' cond
258+ , return $ emit ") "
259+ , prettyPrintCpp' thens
260+ , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintCpp') elses
261+ ]
262+ match (Return _ value) = mconcat <$> sequence
263+ [ return $ emit "return "
264+ , prettyPrintCpp' value
265+ ]
266+ match (ReturnNoResult _) = return $ emit "return nullptr"
267+ -- match (Throw _ _) = return mempty
268+ match (Throw _ value) = mconcat <$> sequence
269+ [ return $ emit "throw "
270+ , return $ emit "std::runtime_error(\"PatternMatchFailure: \""
271+ , prettyPrintCpp' value
272+ , return $ emit ")"
273+ ]
274+ match (Comment _ com cpp) = mconcat <$> sequence
275+ [ return $ emit "\n"
276+ , mconcat <$> forM com comment
277+ , prettyPrintCpp' cpp
278+ ]
279+ match _ = mzero
280+
281+ comment :: (Emit gen) => Comment -> StateT PrinterState Maybe gen
282+ comment (LineComment com) = fmap mconcat $ sequence $
283+ [ currentIndent
284+ , return $ emit "//" <> emit com <> emit "\n"
285+ ]
286+ comment (BlockComment com) = fmap mconcat $ sequence $
287+ [ currentIndent
288+ , return $ emit "/**\n"
289+ ] ++
290+ map asLine (T.lines com) ++
291+ [ currentIndent
292+ , return $ emit " */\n"
293+ , currentIndent
294+ ]
295+ where
296+ asLine :: (Emit gen) => Text -> StateT PrinterState Maybe gen
297+ asLine s = do
298+ i <- currentIndent
299+ return $ i <> emit " * " <> (emit . removeComments) s <> emit "\n"
300+
301+ removeComments :: Text -> Text
302+ removeComments t =
303+ case T.stripPrefix "*/" t of
304+ Just rest -> removeComments rest
305+ Nothing -> case T.uncons t of
306+ Just (x, xs) -> x `T.cons` removeComments xs
307+ Nothing -> ""
308+
309+unary' :: (Emit gen) => UnaryOperator -> (AST -> Text) -> Operator PrinterState AST gen
310+unary' op mkStr = Wrap match (const)
311+ where
312+ match :: (Emit gen) => Pattern PrinterState AST (gen, AST)
313+ match = mkPattern match'
314+ where
315+ match' (Unary _ op' val) | op' == op = Just (emit $ mkStr val, val)
316+ match' _ = Nothing
317+
318+unary :: (Emit gen) => UnaryOperator -> Text -> Operator PrinterState AST gen
319+unary op str = unary' op (\v -> str <> "(" <> prettyPrintCpp1 v <> ")")
320+
321+negateOperator :: (Emit gen) => Operator PrinterState AST gen
322+negateOperator = unary' Negate (\v -> if isNegate v then "- " else "-")
323+ where
324+ isNegate (Unary _ Negate _) = True
325+ isNegate _ = False
326+
327+binary :: (Emit gen) => BinaryOperator -> Text -> Operator PrinterState AST gen
328+binary op str = AssocL match (\v1 v2 -> v1 <> emit (" " <> str <> " ") <> v2)
329+ where
330+ match :: Pattern PrinterState AST (AST, AST)
331+ match = mkPattern match'
332+ where
333+ match' (Binary _ op' v1 v2) | op' == op = Just (v1, v2)
334+ match' _ = Nothing
335+
336+prettyStatements :: (Emit gen) => [AST] -> StateT PrinterState Maybe gen
337+prettyStatements sts = do
338+ cpps <- forM sts prettyPrintCpp'
339+ indentString <- currentIndent
340+ return $ intercalate (emit "\n") $ map ((<> emit ";") . (indentString <>)) cpps
341+
342+-- | Generate a pretty-printed string representing a collection of Objective-C expressions at the same indentation level
343+prettyPrintCppWithSourceMaps :: [AST] -> (Text, [SMap])
344+prettyPrintCppWithSourceMaps cpp =
345+ let StrPos (_, s, mp) = (fromMaybe (internalError "Incomplete pattern") . flip evalStateT (PrinterState 0) . prettyStatements) cpp
346+ in (s, mp)
347+
348+prettyPrintCpp :: [AST] -> Text
349+prettyPrintCpp = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyStatements
350+
351+-- | Generate an indented, pretty-printed string representing a Objective-C expression
352+prettyPrintCpp' :: (Emit gen) => AST -> StateT PrinterState Maybe gen
353+prettyPrintCpp' = A.runKleisli $ runPattern matchValue
354+ where
355+ matchValue :: (Emit gen) => Pattern PrinterState AST gen
356+ matchValue = buildPrettyPrinter operators (literals <+> fmap parensPos matchValue)
357+ operators :: (Emit gen) => OperatorTable PrinterState AST gen
358+ operators =
359+ OperatorTable [ [ unary New "box" ]
360+ , [ unary Not "!"
361+ , unary BitwiseNot "~"
362+ , unary Positive "+"
363+ , negateOperator ]
364+ , [ binary Multiply "*"
365+ , binary Divide "/"
366+ , binary Modulus "%" ]
367+ , [ binary Add "+"
368+ , binary Subtract "-" ]
369+ , [ binary ShiftLeft "<<"
370+ , binary ShiftRight ">>"
371+ , binary ZeroFillShiftRight ">>>" ]
372+ , [ binary LessThan "<"
373+ , binary LessThanOrEqualTo "<="
374+ , binary GreaterThan ">"
375+ , binary GreaterThanOrEqualTo ">=" ]
376+ , [ binary EqualTo "=="
377+ , binary NotEqualTo "!=" ]
378+ , [ binary BitwiseAnd "&" ]
379+ , [ binary BitwiseXor "^" ]
380+ , [ binary BitwiseOr "|" ]
381+ , [ binary And "&&" ]
382+ , [ binary Or "||" ]
383+ ]
384+
385+prettyPrintCpp1 :: AST -> Text
386+prettyPrintCpp1 = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyPrintCpp'
387+
388+stringLiteral :: PSString -> Text
389+stringLiteral pss | Just s <- decodeString pss = stringLiteral' s
390+ where
391+ stringLiteral' :: Text -> Text
392+ stringLiteral' s = "\"" <> T.concatMap encodeChar s <> "\""
393+ encodeChar :: Char -> Text
394+ encodeChar '\b' = "\\b"
395+ encodeChar '\t' = "\\t"
396+ encodeChar '\n' = "\\n"
397+ encodeChar '\v' = "\\v"
398+ encodeChar '\f' = "\\f"
399+ encodeChar '\r' = "\\r"
400+ encodeChar '"' = "\\\""
401+ encodeChar '\\' = "\\\\"
402+ encodeChar c = T.singleton $ c
403+
404+unbox :: Text -> AST -> Text
405+unbox _ v@(NumericLiteral{}) = prettyPrintCpp1 v
406+unbox _ v@(BooleanLiteral{}) = prettyPrintCpp1 v
407+unbox _ v@(StringLiteral{}) = prettyPrintCpp1 v
408+unbox t v = "unbox<" <> t <> ">(" <> prettyPrintCpp1 v <> ")"
409+
410+unboxType :: Text -> Maybe Text
411+unboxType t
412+ | t == C.eqInt ||
413+ t == C.semiringInt ||
414+ t == C.ringInt
415+ = Just int
416+ | t == C.semiringNumber ||
417+ t == C.ringNumber ||
418+ t == C.euclideanRingNumber
419+ = Just float
420+ | otherwise = Nothing
421+
422+renderOp :: Text -> Text
423+renderOp op
424+ | op == C.eq = " == "
425+ | op == C.add = " + "
426+ | op == C.sub = " - "
427+ | op == C.mul = " * "
428+ | op == C.div = " / "
429+ | otherwise = error $ "Unknown operator " <> T.unpack op
430+
431+interfaceSource :: Text -> [Ident] -> [Ident] -> Text
432+interfaceSource mn exports foreigns =
433+ "// Generated by pscpp compiler\n\n" <>
434+ "#ifndef " <> mn <> "_H\n" <>
435+ "#define " <> mn <> "_H\n\n" <>
436+ "#include \"purescript.h\"\n" <>
437+ "\n" <>
438+ "namespace " <> mn <> " {\n\n" <>
439+ "using namespace purescript;\n\n" <>
440+ (T.concat $ (\export -> "auto " <> identToCpp export <> "() -> boxed;\n") <$> (exports \\ foreigns)) <>
441+ "\n" <>
442+ (if null foreigns then "" else ("// Foreign values\n\nauto " <> foreignDict <> "() -> " <> dictType <> "&;\n\n")) <>
443+ (T.concat $ (\foreign' -> "inline auto " <>
444+ identToCpp foreign' <>
445+ "() -> const boxed& { " <>
446+ dispatchOnceBegin <>
447+ foreignDict <> "().at(\"" <> identToCpp foreign' <> "\"); " <>
448+ dispatchOnceEnd <>
449+ " };\n") <$> foreigns) <>
450+ "\n" <>
451+ "} // end namespace " <> mn <>
452+ "\n\n" <>
453+ "#endif // " <> mn <> "_H\n\n"
454+
455+implHeaderSource :: Text -> [Text] -> Text -> Text
456+implHeaderSource mn imports interfaceImport =
457+ T.concat imports <> "\n"<>
458+ interfaceImport <> "\n\n" <>
459+ "namespace " <> mn <> " {\n\n"
460+
461+implFooterSource :: Text -> [Ident] -> Text
462+implFooterSource mn foreigns =
463+ "\n\n" <>
464+ (if null foreigns then "" else ("auto " <> foreignDict <> "() -> " <> dictType <> "& {\n" <>
465+ " static " <> dictType <> " _dict_;\n" <>
466+ " return _dict_;\n" <>
467+ "}\n\n")) <>
468+ "} // end namespace " <> mn <>
469+ "\n\n" <>
470+ if mn == "Main" then mainSource else "\n"
471+ where
472+ mainSource :: Text
473+ mainSource = "\
474+ \int main(int argc, const char * argv[]) {\n\
475+ \ Main::main()(nullptr);\n\
476+ \ return 0;\n\
477+ \}\n\n\
478+ \"
479+
480+dictType :: Text
481+dictType = "dict_t"
482+
483+int :: Text
484+int = "int"
485+
486+float :: Text
487+float = "double"
488+
489+bool :: Text
490+bool = "bool"
491+
492+string :: Text
493+string = "string"
494+
495+foreignDict :: Text
496+foreignDict = "_foreign_"
497+
498+dispatchOnceBegin :: Text
499+dispatchOnceBegin = "static const boxed _value_ = "
500+
501+dispatchOnceEnd :: Text
502+dispatchOnceEnd = "return _value_;"
src/CodeGen/IL.hsdeleted−321
@@ -1,321 +0,0 @@
1−-- | This module generates code in the core imperative representation from
2−-- elaborated PureScript code.
3−module CodeGen.IL
4− ( module AST
5− , module Common
6− , moduleToIL
7− ) where
8−
9−import Prelude.Compat
10−import Protolude (ordNub)
11−
12−import Control.Arrow ((&&&))
13−import Control.Monad (forM, liftM, replicateM, void)
14−import Control.Monad.Supply.Class
15−
16−import Data.List ((\\), delete, intersect, nub)
17−import qualified Data.Foldable as F
18−import qualified Data.Map as M
19−import Data.Maybe (fromMaybe, isNothing, mapMaybe)
20−import Data.Monoid ((<>))
21−import Data.String (fromString)
22−import Data.Text (Text)
23−import qualified Data.Text as T
24−
25−import Language.PureScript.AST.SourcePos
26−import Language.PureScript.CoreImp.AST (AST)
27−import qualified Language.PureScript.CoreImp.AST as AST
28−import Language.PureScript.CoreFn
29−import Language.PureScript.Crash
30−import Language.PureScript.Names
31−import Language.PureScript.Options
32−import Language.PureScript.PSString (PSString, mkString)
33−import Language.PureScript.Traversals (sndM)
34−import qualified Language.PureScript.Constants as C
35−
36−import System.FilePath.Posix ((</>))
37−
38−import CodeGen.IL.Common as Common
39−import CodeGen.IL.Optimizer
40−import CodeGen.IL.Printer
41−
42−data DeclType = ModuleDecl | LetDecl | RecLetDecl deriving (Eq)
43−
44−-- | Generate code in the simplified intermediate representation for all declarations in a
45−-- module.
46−moduleToIL
47− :: forall m
48− -- . (Monad m, MonadReader Options m, MonadSupply m, MonadError MultipleErrors m)
49− . (Monad m, MonadSupply m)
50− => Module Ann
51− -> Text
52− -> m (Text, [Text], [AST], Text, Text)
53−moduleToIL (Module _ coms mn _ imps _ foreigns decls) project =
54− do
55− ilDecls <- mapM (bindToIL ModuleDecl) decls
56− optimized <- traverse (traverse (optimize modName')) ilDecls
57− let optimized' = concat optimized
58− values = annotValue <$> optimized'
59− foreigns' = moduleIdentToIL <$> foreigns
60− interface = interfaceSource modName values foreigns
61− imports = nub . concat $ importToIL <$> optimized'
62− implHeader = implHeaderSource modName imports project
63− implFooter = implFooterSource (runModuleName mn) foreigns
64− return $ (interface, foreigns', optimized', implHeader, implFooter)
65− where
66− modName = moduleNameToIL mn
67− modName' = AST.Var Nothing modName
68−
69− annotValue :: AST -> (Text, Bool)
70− annotValue (AST.Function _ (Just name) [] (AST.Block _ [AST.Return _ ret]))
71− | isLiteral ret = (name, True)
72− annotValue (AST.Function _ (Just name) [] _) = (name, False)
73− annotValue _ = error "Unexpected top-level value form"
74−
75− -- | Extracts all declaration names from a binding group.
76− getNames :: Bind Ann -> [Ident]
77− getNames (NonRec _ ident _) = [ident]
78− getNames (Rec vals) = map (snd . fst) vals
79−
80− -- | Generates IL code for a module import
81− --
82− importToIL :: AST -> [Text]
83− importToIL = AST.everything (++) modRef
84− where
85− modRef (AST.Indexer _ (AST.Var _ _) (AST.Var _ mname))
86− | not $ T.null mname = [extract mname]
87− modRef _ = []
88− extract :: Text -> Text
89− extract = T.replace "_" "." . T.dropWhileEnd (=='_')
90−
91− -- |
92− -- Generate code in the simplified intermediate representation for a declaration
93− --
94− bindToIL :: DeclType -> Bind Ann -> m [AST]
95− bindToIL dt (NonRec ann ident val) = return <$> nonRecToIL dt ann ident val
96− bindToIL dt (Rec vals) = forM vals (uncurry . uncurry $ nonRecToIL dt')
97− where
98− dt' = if dt == LetDecl then RecLetDecl else dt
99−
100− -- | Generate code in the simplified intermediate representation for a single non-recursive
101− -- declaration.
102− --
103− -- The main purpose of this function is to handle code generation for comments.
104− nonRecToIL :: DeclType -> Ann -> Ident -> Expr Ann -> m AST
105− nonRecToIL dt a i e@(extractAnn -> (_, com, _, _)) | not (null com) = do
106− -- withoutComment <- asks optionsNoComments
107− let withoutComment = False
108− if withoutComment
109− then nonRecToIL dt a i (modifyAnn removeComments e)
110− else AST.Comment Nothing com <$> nonRecToIL dt a i (modifyAnn removeComments e)
111− nonRecToIL ModuleDecl (ss, _, _, _) ident val = do
112− il <- valueToIL val
113− pure $ AST.Function Nothing (Just $ moduleIdentToIL ident) [] (AST.Block Nothing [AST.Return Nothing il])
114− nonRecToIL RecLetDecl (ss, _, _, _) ident val = do
115− ilExpr <- valueToIL val
116− pure $ AST.Assignment Nothing (AST.Var Nothing $ identToIL ident) ilExpr
117− nonRecToIL _ (ss, _, _, _) ident val = do
118− il <- valueToIL val
119− pure $ AST.VariableIntroduction Nothing (identToIL ident) (Just il)
120−
121− -- | Generate code in the simplified intermediate representation for a variable based on a
122− -- PureScript identifier.
123− var :: Ident -> AST
124− var = AST.Var Nothing . identToIL
125−
126− accessorString :: PSString -> AST -> AST
127− accessorString prop = AST.Indexer Nothing (AST.StringLiteral Nothing prop)
128−
129− -- | Generate code in the simplified intermediate representation for a value or expression.
130−
131− valueToIL :: Expr Ann -> m AST
132− valueToIL (Literal (pos, _, _, _) l) = literalToValueIL pos l
133− valueToIL (Accessor _ prop val) =
134− accessorString prop <$> valueToIL val
135− valueToIL (ObjectUpdate _ o ps) = do
136− obj <- valueToIL o
137− updates <- mapM (sndM valueToIL) ps
138− obj' <- freshName'
139− let objVar = AST.Var Nothing obj'
140− copy = AST.VariableIntroduction Nothing obj' (Just $ copyDict obj)
141− assign (k, v) = AST.Assignment Nothing (accessorString k objVar) v
142− sts = copy : (assign <$> updates) ++ [AST.Return Nothing objVar]
143− return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing sts)) []
144− valueToIL (Abs _ arg val) = do
145− ret <- valueToIL val
146− let ilArg = case arg of
147− UnusedIdent -> []
148− _ -> [identToIL arg]
149− return $ AST.Function Nothing Nothing ilArg (AST.Block Nothing [AST.Return Nothing ret])
150− valueToIL e@App{} = do
151− let (f, args) = unApp e []
152− args' <- mapM valueToIL args
153− fn <- valueToIL f
154− case f of
155− Var (_, _, _, Just IsNewtype) _ -> return (head args')
156− _ -> flip (foldl (\fn a -> AST.App Nothing fn [a])) args' <$> valueToIL f
157− where
158− unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann])
159− unApp (App _ val arg) args = unApp val (arg : args)
160− unApp other args = (other, args)
161− valueToIL (Var _ ident) = return $ varToIL ident
162− valueToIL (Case (ss, _, _, _) values binders) = do
163− vals <- mapM valueToIL values
164− bindersToIL ss binders vals
165− valueToIL (Let _ ds val) = do
166− let recurs = concatMap getNames $ filter isRec ds
167− recurDs = (\v -> AST.VariableIntroduction Nothing (identToIL v) Nothing) <$> recurs
168− ds' <- concat <$> mapM (bindToIL LetDecl) ds
169− ret <- valueToIL val
170− return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing (recurDs ++ ds' ++ [AST.Return Nothing ret]))) []
171− where
172− isRec :: Bind Ann -> Bool
173− isRec (Rec _) = True
174− isRec _ = False
175− valueToIL (Constructor (_, _, _, Just IsNewtype) _ (ProperName ctor) _) = error "IsNewtype"
176− valueToIL (Constructor _ _ (ProperName ctor) fields) =
177− let body = AST.ObjectLiteral Nothing $ (mkString ctor, AST.BooleanLiteral Nothing True) :
178− ((\field -> (mkString (identToIL field), var field)) `map` fields)
179− fn = foldr (\f inner -> AST.Function Nothing Nothing [identToIL f] (AST.Block Nothing [AST.Return Nothing inner])) body fields
180− in return fn
181−
182−
183− iife :: Text -> [AST] -> AST
184− iife v exprs = AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing $ exprs ++ [AST.Return Nothing $ AST.Var Nothing v])) []
185−
186− literalToValueIL :: SourceSpan -> Literal (Expr Ann) -> m AST
187− literalToValueIL ss (NumericLiteral (Left i)) = return $ AST.NumericLiteral (Just ss) (Left i)
188− literalToValueIL ss (NumericLiteral (Right n)) = return $ AST.NumericLiteral (Just ss) (Right n)
189− literalToValueIL ss (StringLiteral s) = return $ AST.StringLiteral (Just ss) s
190− literalToValueIL ss (CharLiteral c) = return $ AST.StringLiteral (Just ss) (fromString [c])
191− literalToValueIL ss (BooleanLiteral b) = return $ AST.BooleanLiteral (Just ss) b
192− literalToValueIL ss (ArrayLiteral xs) = AST.ArrayLiteral (Just ss) <$> mapM valueToIL xs
193− literalToValueIL ss (ObjectLiteral ps) = AST.ObjectLiteral (Just ss) <$> mapM (sndM valueToIL) ps
194−
195− -- | Generate code in the simplified intermediate representation for a reference to a
196− -- variable.
197− varToIL :: Qualified Ident -> AST
198− varToIL (Qualified Nothing ident) = var ident
199− varToIL qual = qualifiedToIL id qual
200−
201− -- | Generate code in the simplified intermediate representation for a reference to a
202− -- variable that may have a qualified name.
203− qualifiedToIL :: (a -> Ident) -> Qualified a -> AST
204− qualifiedToIL f (Qualified (Just (ModuleName mn')) a) | mn' == C.prim = AST.Var Nothing . moduleIdentToIL $ f a
205− qualifiedToIL f (Qualified (Just mn') a) | mn /= mn' = AST.Indexer Nothing (AST.Var Nothing . moduleIdentToIL $ f a) (AST.Var Nothing (moduleNameToIL mn'))
206− qualifiedToIL f (Qualified _ a) = AST.Indexer Nothing (AST.Var Nothing . moduleIdentToIL $ f a) (AST.Var Nothing "")
207−
208− -- foreignIdent :: Ident -> AST
209− -- foreignIdent ident = accessorString (mkString $ runIdent ident) (AST.Var Nothing "$foreign")
210−
211− -- | Generate code in the simplified intermediate representation for pattern match binders
212− -- and guards.
213− bindersToIL :: SourceSpan -> [CaseAlternative Ann] -> [AST] -> m AST
214− bindersToIL ss binders vals = do
215− valNames <- replicateM (length vals) freshName'
216− let assignments = zipWith (AST.VariableIntroduction Nothing) valNames (map Just vals)
217− ils <- forM binders $ \(CaseAlternative bs result) -> do
218− ret <- guardsToIL result
219− go valNames ret bs
220− return $ AST.App Nothing (AST.Function Nothing Nothing [] (AST.Block Nothing (assignments ++ concat ils ++ [AST.Throw Nothing (AST.StringLiteral Nothing $ mkString failedPatternMessage)])))
221− []
222− where
223− go :: [Text] -> [AST] -> [Binder Ann] -> m [AST]
224− go _ done [] = return done
225− go (v:vs) done' (b:bs) = do
226− done'' <- go vs done' bs
227− binderToIL v done'' b
228− go _ _ _ = internalError "Invalid arguments to bindersToIL"
229−
230− failedPatternMessage :: Text
231− failedPatternMessage = "Failed pattern match at " <> runModuleName mn <> " " <> displayStartEndPos ss
232−
233− guardsToIL :: Either [(Guard Ann, Expr Ann)] (Expr Ann) -> m [AST]
234− guardsToIL (Left gs) = traverse genGuard gs where
235− genGuard (cond, val) = do
236− cond' <- valueToIL cond
237− val' <- valueToIL val
238− return
239− (AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo cond' (AST.BooleanLiteral Nothing True))
240− (AST.Block Nothing [AST.Return Nothing val']) Nothing)
241−
242− guardsToIL (Right v) = return . AST.Return Nothing <$> valueToIL v
243−
244− -- | Generate code in the simplified intermediate representation for a pattern match
245− -- binder.
246− binderToIL :: Text -> [AST] -> Binder Ann -> m [AST]
247− binderToIL _ done NullBinder{} = return done
248− binderToIL varName done (LiteralBinder _ l) =
249− literalToBinderIL varName done l
250− binderToIL varName done (VarBinder _ ident) =
251− return (AST.VariableIntroduction Nothing (identToIL ident) (Just (AST.Var Nothing varName)) : done)
252− binderToIL varName done (ConstructorBinder (_, _, _, Just IsNewtype) _ _ [b]) =
253− binderToIL varName done b
254− binderToIL varName done (ConstructorBinder (_, _, _, Just (IsConstructor ctorType fields)) _ (Qualified _ (ProperName ctor)) bs) = do
255− il <- go (zip fields bs) done
256− return $ case ctorType of
257− ProductType -> il
258− SumType ->
259− [AST.IfElse Nothing (AST.InstanceOf Nothing (AST.Var Nothing varName) (AST.StringLiteral Nothing (mkString ctor)))
260− (AST.Block Nothing il)
261− Nothing]
262− where
263− go :: [(Ident, Binder Ann)] -> [AST] -> m [AST]
264− go [] done' = return done'
265− go ((field, binder) : remain) done' = do
266− argVar <- freshName'
267− done'' <- go remain done'
268− il <- binderToIL argVar done'' binder
269− return (AST.VariableIntroduction Nothing argVar (Just $ accessorString (mkString $ identToIL field) $ AST.Var Nothing varName) : il)
270−
271− binderToIL _ _ ConstructorBinder{} =
272− internalError "binderToIL: Invalid ConstructorBinder in binderToIL"
273− binderToIL varName done (NamedBinder _ ident binder) = do
274− il <- binderToIL varName done binder
275− return (AST.VariableIntroduction Nothing (identToIL ident) (Just (AST.Var Nothing varName)) : il)
276−
277− literalToBinderIL :: Text -> [AST] -> Literal (Binder Ann) -> m [AST]
278− literalToBinderIL varName done (NumericLiteral num) =
279− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.NumericLiteral Nothing num)) (AST.Block Nothing done) Nothing]
280− literalToBinderIL varName done (CharLiteral c) =
281− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.StringLiteral Nothing (fromString [c]))) (AST.Block Nothing done) Nothing]
282− literalToBinderIL varName done (StringLiteral str) =
283− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.StringLiteral Nothing str)) (AST.Block Nothing done) Nothing]
284− literalToBinderIL varName done (BooleanLiteral True) =
285− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.BooleanLiteral Nothing True)) (AST.Block Nothing done) Nothing]
286− literalToBinderIL varName done (BooleanLiteral False) =
287− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo (AST.Var Nothing varName) (AST.BooleanLiteral Nothing False)) (AST.Block Nothing done) Nothing]
288− -- return [AST.IfElse Nothing (AST.Unary Nothing AST.Not (AST.Var Nothing varName)) (AST.Block Nothing done) Nothing]
289− literalToBinderIL varName done (ObjectLiteral bs) = go done bs
290− where
291− go :: [AST] -> [(PSString, Binder Ann)] -> m [AST]
292− go done' [] = return done'
293− go done' ((prop, binder):bs') = do
294− propVar <- freshName'
295− done'' <- go done' bs'
296− il <- binderToIL propVar done'' binder
297− return (AST.VariableIntroduction Nothing propVar (Just (accessorString prop (AST.Var Nothing varName))) : il)
298− literalToBinderIL varName done (ArrayLiteral bs) = do
299− il <- go done 0 bs
300− return [AST.IfElse Nothing (AST.Binary Nothing AST.EqualTo
301− (arrayLength $ AST.Var Nothing varName)
302− (AST.NumericLiteral Nothing (Left (fromIntegral $ length bs))))
303− (AST.Block Nothing il) Nothing]
304− where
305− go :: [AST] -> Integer -> [Binder Ann] -> m [AST]
306− go done' _ [] = return done'
307− go done' index (binder:bs') = do
308− elVar <- freshName'
309− done'' <- go done' (index + 1) bs'
310− il <- binderToIL elVar done'' binder
311− return (AST.VariableIntroduction Nothing elVar (Just (AST.Indexer Nothing (AST.NumericLiteral Nothing (Left index)) (AST.Var Nothing varName))) : il)
312−
313−emptyAnn :: Ann
314−emptyAnn = (SourceSpan "" (SourcePos 0 0) (SourcePos 0 0), [], Nothing, Nothing)
315−
316−arrayLength :: AST -> AST
317−arrayLength a = AST.App Nothing (AST.Var Nothing arrayLengthFn) [a]
318−-- arrayLength a = AST.Var Nothing (arrayLengthFn <> "(" <> prettyPrintIL1 a <> ")")
319−
320−copyDict :: AST -> AST
321−copyDict a = AST.App Nothing (AST.Var Nothing copyDictFn) [a]
src/CodeGen/IL/Common.hsdeleted−196
@@ -1,196 +0,0 @@
1−-- | Common code generation utility functions
2−module CodeGen.IL.Common where
3−
4−import Prelude.Compat
5−
6−import Control.Monad.Supply.Class (MonadSupply, freshName)
7−import Data.Char
8−import Data.Maybe (maybe)
9−import Data.Monoid ((<>))
10−import Data.Text (Text)
11−import qualified Data.Text as T
12−
13−import Language.PureScript.Crash
14−import Language.PureScript.Names
15−
16−import qualified Language.PureScript.Constants as C
17−
18−moduleNameToIL :: ModuleName -> Text
19−moduleNameToIL (ModuleName mn) =
20− let name = T.replace "." "_" mn
21− in if nameIsILBuiltIn name then (name <> moduleRenamerMarker) else name
22−
23−moduleNameToIL' :: ModuleName -> Text
24−moduleNameToIL' (ModuleName mn) = T.replace "_" "." mn
25−
26−-- | Convert an 'Ident' into a valid IL identifier:
27−--
28−-- * Alphanumeric characters are kept unmodified.
29−--
30−-- * Reserved IL identifiers are wrapped with '_'.
31−--
32−identToIL :: Ident -> Text
33−identToIL UnusedIdent = unusedName
34−identToIL (Ident "$__unused") = unusedName
35−identToIL (Ident name) | name == C.undefined = undefinedName -- Note: done in Printer.hs too
36−identToIL (Ident name) = properToIL name
37−identToIL (GenIdent _ _) = internalError "GenIdent in identToIL"
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−
48−unusedName :: Text
49−unusedName = "_"
50−
51−properToIL :: Text -> Text
52−properToIL name
53− | nameIsILReserved name = "ˉ" <> name
54− | otherwise = T.concatMap identCharToText name
55−
56−-- | Attempts to find a human-readable name for a symbol, if none has been specified returns the
57−-- ordinal value.
58−identCharToText :: Char -> Text
59−identCharToText 'ṩ' = "_ṩ"
60−identCharToText c | isAlphaNum c = T.singleton c
61−identCharToText '_' = "_"
62−identCharToText '\'' = "ꞌ" -- lowercase saltillo
63−identCharToText c = error (show (ord c)) -- TODO: should never occur now(?)
64−
65−moduleProperToIL :: Text -> Text
66−moduleProperToIL = T.concatMap identCharToText
67−
68−-- | Checks whether an identifier name is reserved in IL.
69−nameIsILReserved :: Text -> Bool
70−nameIsILReserved name =
71− name `elem` ilAnyReserved
72−
73−-- | Checks whether a name matches a built-in value in IL.
74−nameIsILBuiltIn :: Text -> Bool
75−nameIsILBuiltIn name =
76− name `elem`
77− [ "Any"
78− , "Apply"
79− , "Contains"
80− , "CopyDict"
81− , "Dict"
82− , "EffFn"
83− , "Fn"
84− , "Foreign"
85− , "Get"
86− , "Index"
87− , "Is"
88− , "Length"
89− , "Once"
90− , "Run"
91− , "Undefined"
92− ]
93−
94−ilAnyReserved :: [Text]
95−ilAnyReserved =
96− concat
97− [ ilKeywords
98− , ilLiterals
99− ]
100−
101−ilKeywords :: [Text]
102−ilKeywords =
103− [ "break"
104− , "case"
105− , "chan"
106− , "const"
107− , "continue"
108− , "default"
109− , "defer"
110− , "else"
111− , "fallthrough"
112− , "for"
113− , "func"
114− , "go"
115− , "goto"
116− , "if"
117− , "import"
118− , "interface"
119− , "map"
120− , "package"
121− , "range"
122− , "return"
123− , "select"
124− , "struct"
125− , "switch"
126− , "type"
127− , "var"
128− ]
129−
130−ilLiterals :: [Text]
131−ilLiterals =
132− [ "init"
133− , "nil"
134− , "int"
135− , "int8"
136− , "int16"
137− , "int32"
138− , "int64"
139− , "uint"
140− , "uint8"
141− , "uint16"
142− , "uint32"
143− , "uint64"
144− , "uintptr"
145− , "float32"
146− , "float64"
147− , "complex64"
148− , "complex128"
149− , "byte"
150− , "rune"
151− , "string"
152− ]
153−
154−withPrefix :: Text -> Text
155−withPrefix s = "Ꞌ" <> s -- uppercase saltillo
156−
157−anyType :: Text
158−anyType = "Any"
159−
160−dictType :: Text
161−dictType = "Dict"
162−
163−arrayType :: Text
164−arrayType = "[]" <> anyType
165−
166−uncurriedFnType :: Int -> Text
167−uncurriedFnType i = "Fn" <> (T.pack . show $ i)
168−
169−int :: Text
170−int = "int"
171−
172−float :: Text
173−float = "float64"
174−
175−bool :: Text
176−bool = "bool"
177−
178−string :: Text
179−string = "string"
180−
181−arrayLengthFn :: Text
182−arrayLengthFn = "Length"
183−
184−indexFn :: Text
185−indexFn = "Index"
186−
187−copyDictFn :: Text
188−copyDictFn = "CopyDict"
189−
190−freshName' :: MonadSupply m => m Text
191−freshName' = do
192− name <- freshName
193− return $ T.replace "$" "ṩ" name
194−
195−moduleRenamerMarker :: Text
196−moduleRenamerMarker = "__"
src/CodeGen/IL/Optimizer.hsdeleted−81
@@ -1,81 +0,0 @@
1−-- | This module optimizes code in the intermediate representation.
2−module CodeGen.IL.Optimizer (optimize) where
3−
4−import Prelude.Compat
5−
6−import Control.Monad.Supply.Class (MonadSupply)
7−import Data.Text (Text)
8−import Language.PureScript.CoreImp.AST
9−import Language.PureScript.CoreImp.Optimizer.Blocks
10−import Language.PureScript.CoreImp.Optimizer.Common hiding (isDict)
11−import Language.PureScript.CoreImp.Optimizer.Inliner (etaConvert, evaluateIifes, inlineVariables, unThunk)
12−import Language.PureScript.CoreImp.Optimizer.Unused
13−
14−import CodeGen.IL.Common (unusedName)
15−import CodeGen.IL.Optimizer.Inliner
16−import CodeGen.IL.Optimizer.MagicDo
17−import CodeGen.IL.Optimizer.TCO
18−
19−import qualified Language.PureScript.Constants as C
20−
21−-- | Apply a series of optimizer passes to simplified IL code
22−optimize :: MonadSupply m => AST -> AST -> m AST
23−optimize mn il = do
24− il' <- untilFixedPoint (inlineFnComposition . inlineUnsafeCoerce . inlineUnsafePartial . tidyUp) il
25− il'' <- untilFixedPoint (return . ignoreUnusedResults . inlineCommonValues . inlineCommonOperators . tidyUp) . tco mn . inlineST
26− =<< untilFixedPoint (return . magicDoST)
27− =<< untilFixedPoint (return . magicDoEff)
28− =<< untilFixedPoint (return . magicDoEffect) il'
29− untilFixedPoint (return . collapseVarDeclarations) il''
30− where
31− tidyUp :: AST -> AST
32− tidyUp = applyAll
33− [ collapseNestedBlocks
34− , collapseNestedIfs
35− , collapseIfChecks
36− , removeCodeAfterReturnStatements
37− , unThunk
38− , etaConvert
39− , evaluateIifes
40− , inlineVariables
41− ]
42−
43−untilFixedPoint :: (Monad m, Eq a) => (a -> m a) -> a -> m a
44−untilFixedPoint f = go
45− where
46− go a = do
47− a' <- f a
48− if a' == a then return a' else go a'
49−
50−collapseIfChecks :: AST -> AST
51−collapseIfChecks = everywhere collapse where
52− collapse :: AST -> AST
53− collapse (IfElse _ (Binary _ EqualTo (BooleanLiteral _ True) (BooleanLiteral _ True)) (Block _ [exprs]) _) = exprs
54− collapse (IfElse _ (Binary _ EqualTo (Indexer _ (Var _ prop) (Var _ val)) (BooleanLiteral _ True)) (Block _ [exprs]) _)
55− | prop == "otherwise" && val == "Data_Boolean" = exprs
56− collapse exprs = exprs
57−
58−collapseVarDeclarations :: AST -> AST
59−collapseVarDeclarations = everywhere collapse where
60− collapse :: AST -> AST
61− collapse (Block ss sts) = Block ss (go Nothing sts)
62− collapse s = s
63−
64− go :: Maybe Text -> [AST] -> [AST]
65− go _ [] = []
66− go Nothing (decl@(VariableIntroduction _ var (Just _)) : sts) = decl : go (Just var) (go Nothing sts)
67− go (Just var') ((VariableIntroduction ss var (Just val)) : sts)
68− | var == var' = (Assignment ss (Var Nothing var) val) : go (Just var') sts
69− go var' (s:sts) = s : go var' sts
70−
71−ignoreUnusedResults :: AST -> AST
72−ignoreUnusedResults = everywhere $ removeFromBlock go
73− where
74− go :: [AST] -> [AST]
75− go [] = []
76− go (VariableIntroduction ss var (Just s) : sts)
77− | not $ any (everything (||) (isUsed var)) sts = sts'
78− where
79− sts' | App{} <- s = s : (go sts)
80− | otherwise = go sts
81− go (s:sts) = s : go sts
src/CodeGen/IL/Optimizer/Common.hsdeleted−18
@@ -1,18 +0,0 @@
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.hsdeleted−435
@@ -1,435 +0,0 @@
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.hsdeleted−146
@@ -1,146 +0,0 @@
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/Printer.hsdeleted−527
@@ -1,527 +0,0 @@
1−-- | Pretty printer for the AST
2−module CodeGen.IL.Printer
3− ( prettyPrintIL
4− , prettyPrintIL1
5− , prettyPrintILWithSourceMaps
6− , interfaceSource
7− , implHeaderSource
8− , implFooterSource
9− , isLiteral
10− , modLabel
11− , modPrefix
12− , ffiLoader
13− ) where
14−
15−import Prelude.Compat
16−
17−import Control.Arrow ((<+>))
18−import Control.Monad (forM, mzero)
19−import Control.Monad.State (StateT, evalStateT)
20−import Control.PatternArrows
21−import Data.List (filter, (\\))
22−import qualified Control.Arrow as A
23−
24−import Data.Maybe (fromMaybe)
25−import Data.Monoid ((<>))
26−import Data.Text (Text)
27−import qualified Data.Text as T
28−
29−import Language.PureScript.AST (SourceSpan(..))
30−import Language.PureScript.CoreImp.AST
31−import Language.PureScript.Comments
32−import Language.PureScript.Crash
33−import Language.PureScript.Names (Ident, runIdent)
34−import Language.PureScript.Pretty.Common
35−import Language.PureScript.PSString (PSString, decodeString, mkString)
36−
37−import CodeGen.IL.Common
38−import CodeGen.IL.Optimizer.TCO (tcoLoop)
39−
40−import qualified Language.PureScript.Constants as C
41−
42−-- TODO (Christoph): Get rid of T.unpack / pack
43−
44−literals :: (Emit gen) => Pattern PrinterState AST gen
45−literals = mkPattern' match'
46− where
47− match' :: (Emit gen) => AST -> StateT PrinterState Maybe gen
48− match' il = (addMapping' (getSourceSpan il) <>) <$> match il
49−
50− match :: (Emit gen) => AST -> StateT PrinterState Maybe gen
51− match (NumericLiteral _ n) = return $ emit $ T.pack $ either show show n
52− match (StringLiteral _ s) = return $ emit $ stringLiteral s
53− match (BooleanLiteral _ True) = return $ emit "true"
54− match (BooleanLiteral _ False) = return $ emit "false"
55− match (ArrayLiteral _ xs) = mconcat <$> sequence
56− [ return . emit $ arrayType <> "{"
57− , intercalate (emit ", ") <$> forM xs prettyPrintIL'
58− , return $ emit "}"
59− ]
60− -- match (ObjectLiteral _ []) = return $ emit "std::initializer_list<std::pair<const string, boxed>>{}"
61− match (ObjectLiteral _ ps) = mconcat <$> sequence
62− [ return . emit $ dictType <> "{"
63− , withIndent $ do
64− ils <- forM ps $ \(key, value) -> do
65− value' <- prettyPrintIL' value
66− return $ objectPropertyToString key <> value'
67− return $ intercalate (emit " ") $ map (<> emit ",") ils
68− , return $ emit "}"
69− ]
70− where
71− objectPropertyToString :: (Emit gen) => PSString -> gen
72− objectPropertyToString s = emit $ stringLiteral s <> ": "
73− match (Block _ sts) = mconcat <$> sequence
74− [ return $ emit "{\n"
75− , withIndent $ prettyStatements sts
76− , return $ emit "\n"
77− , currentIndent
78− , return $ emit "}"
79− ]
80− match (Var _ ident) | ident == C.undefined = return $ emit undefinedName
81− match (Var _ ident) = return $ emit ident
82− match (VariableIntroduction _ ident value) = mconcat <$> sequence
83− [ return . emit $ varDecl <> " " <> ident <> " " <> anyType
84− , maybe (return mempty) (fmap (emit " = " <>) . prettyPrintIL') value
85− ]
86− match (Assignment _ target value) = mconcat <$> sequence
87− [ prettyPrintIL' target
88− , return $ emit " = "
89− , prettyPrintIL' value
90− ]
91− match (App _ val []) = mconcat <$> sequence
92− [ return $ emit "Run("
93− , prettyPrintIL' val
94− , return $ emit ")"
95− ]
96− match (App _ (StringLiteral _ u) [arg])
97− | Just ty <- decodeString u = mconcat <$> sequence
98− [ prettyPrintIL' arg
99− , return $ emit ".("
100− , return $ emit ty
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
123− , "Fn" `T.isPrefixOf` ftype = mconcat <$> sequence
124− [ prettyPrintIL' fn
125− , return $ emit "("
126− , intercalate (emit ", ") <$> forM args prettyPrintIL'
127− , return $ emit ")"
128− ]
129− match (App _ (App Nothing (StringLiteral Nothing fnx) [fn]) args)
130− | Just ftype <- decodeString fnx
131− , "Fn" `T.isPrefixOf` ftype = mconcat <$> sequence
132− [ prettyPrintIL' fn
133− , return $ emit ".("
134− , return $ emit ftype
135− , return $ emit ")("
136− , intercalate (emit ", ") <$> forM args prettyPrintIL'
137− , return $ emit ")"
138− ]
139− match app@(App{})
140− | (val, args) <- unApp app []
141− , length args > 1
142− = mconcat <$> sequence
143− [ return $ emit "Apply("
144− , prettyPrintIL' val
145− , return $ emit ", "
146− , intercalate (emit ", ") <$> forM args prettyPrintIL'
147− , return $ emit ")"
148− ]
149− match (App _ val args) = mconcat <$> sequence
150− [ return $ emit "Apply("
151− , prettyPrintIL' val
152− , return $ emit ", "
153− , intercalate (emit ", ") <$> forM args prettyPrintIL'
154− , return $ emit ")"
155− ]
156− match (Function _ (Just name) [] (Block _ [Return _ ret]))
157− | isComplexLiteral ret = mconcat <$> sequence
158− [ return $ emit "\n"
159− , return . emit $ varDecl <> " " <> initName name <> " Once\n"
160− , prettyPrintIL' $ VariableIntroduction Nothing (valueName name) Nothing
161− , return $ emit "\n\n"
162− , return $ emit "func "
163− , return . emit $ withPrefix name
164− , return . emit $ "() " <> anyType <> " "
165− , return $ emit "{\n"
166− , withIndent $ do
167− indentString <- currentIndent
168− return $ indentString <> (emit $ initName name <> ".Do(func() {\n") <>
169− indentString <> indentString <> (emit $ valueName name <> " = ")
170− , withIndent $ do
171− case ret of
172− Function _ Nothing args body -> mconcat <$> sequence
173− [ return $ emit "func("
174− , return . emit $ intercalate ", " (renderArg anyType <$> args)
175− , return . emit $ ") " <> anyType <> " "
176− , prettyPrintIL' body
177− ]
178− _ -> do
179− prettyPrintIL' ret
180− , return $ emit "\n"
181− , withIndent $ do
182− indentString <- currentIndent
183− return $ indentString <> (emit $ "})\n" <> "return " <> valueName name <>"\n")
184− , return $ emit "}\n\n"
185− ]
186− match (Function _ (Just name) [] ret) = mconcat <$> sequence
187− [ return $ emit "func "
188− , return . emit $ withPrefix name
189− , return . emit $ "() " <> anyType <> " "
190− , prettyPrintIL' ret
191− ]
192− match (Function _ _ args ret) = mconcat <$> sequence
193− [ return $ emit "func("
194− , return . emit $ intercalate ", " (renderArg anyType <$> args)
195− , return . emit $ ") " <> anyType <> " "
196− , prettyPrintIL' ret
197− ]
198− match (Indexer _ (Var _ name) (Var _ "")) = mconcat <$> sequence
199− [ prettyPrintIL' (Var Nothing $ withPrefix name)
200− , return $ emit "()"
201− ]
202− match (Indexer _ (Var _ name) val) = mconcat <$> sequence
203− [ prettyPrintIL' val
204− , return $ emit "."
205− , prettyPrintIL' (Var Nothing $ withPrefix name)
206− , return $ emit "()"
207− ]
208− match (Indexer _ prop@StringLiteral{} val@ObjectLiteral{}) = mconcat <$> sequence
209− [ prettyPrintIL' val
210− , return $ emit "["
211− , prettyPrintIL' prop
212− , return $ emit "]"
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− ]
236− match (InstanceOf _ val ty) = mconcat <$> sequence
237− [ return $ emit "Is("
238− , prettyPrintIL' val
239− , return $ emit ", "
240− , prettyPrintIL' ty
241− , return $ emit ")"
242− ]
243− match (While _ cond sts) = mconcat <$> sequence
244− [ return $ emit "for "
245− , prettyPrintIL' cond
246− , return $ emit " "
247− , prettyPrintIL' sts
248− ]
249− match (For _ ident start end sts) = mconcat <$> sequence
250− [ return $ emit $ "for var " <> ident <> " = "
251− , prettyPrintIL' start
252− , return $ emit $ "; " <> ident <> " < "
253− , prettyPrintIL' end
254− , return $ emit $ "; " <> ident <> "++ "
255− , prettyPrintIL' sts
256− ]
257− match (ForIn _ ident obj sts) = mconcat <$> sequence
258− [ return $ emit $ "for var " <> ident <> " in "
259− , prettyPrintIL' obj
260− , return $ emit " "
261− , prettyPrintIL' sts
262− ]
263− match (IfElse _ (Binary _ EqualTo cond@Binary{} (BooleanLiteral Nothing True)) thens elses) = mconcat <$> sequence
264− [ return $ emit "if "
265− , prettyPrintIL' cond
266− , return $ emit " "
267− , prettyPrintIL' thens
268− , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
269− ]
270− match (IfElse _ (Binary _ EqualTo cond@Binary{} (BooleanLiteral Nothing False)) thens elses) = mconcat <$> sequence
271− [ return $ emit "if !("
272− , prettyPrintIL' cond
273− , return $ emit ") "
274− , prettyPrintIL' thens
275− , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
276− ]
277− match (IfElse _ cond thens elses) = mconcat <$> sequence
278− [ return $ emit "if "
279− , prettyPrintIL' cond
280− , return $ emit " "
281− , prettyPrintIL' thens
282− , maybe (return mempty) (fmap (emit " else " <>) . prettyPrintIL') elses
283− ]
284− match (Return _ value) = mconcat <$> sequence
285− [ return $ emit "return "
286− , prettyPrintIL' value
287− ]
288− match (ReturnNoResult _) = return . emit $ "return " <> undefinedName
289− -- match (Throw _ _) = return mempty
290− match (Throw _ value) = mconcat <$> sequence
291− [ return $ emit "panic("
292− , prettyPrintIL' value
293− , return $ emit ")"
294− ]
295− match (Comment _ com il) = mconcat <$> sequence
296− [ return $ emit "\n"
297− , mconcat <$> forM com comment
298− , prettyPrintIL' il
299− ]
300− match _ = mzero
301−
302− comment :: (Emit gen) => Comment -> StateT PrinterState Maybe gen
303− comment (LineComment com) = fmap mconcat $ sequence $
304− [ currentIndent
305− , return $ emit "//" <> emit com <> emit "\n"
306− ]
307− comment (BlockComment com) = fmap mconcat $ sequence $
308− [ currentIndent
309− , return $ emit "/**\n"
310− ] ++
311− map asLine (T.lines com) ++
312− [ currentIndent
313− , return $ emit " */\n"
314− , currentIndent
315− ]
316− where
317− asLine :: (Emit gen) => Text -> StateT PrinterState Maybe gen
318− asLine s = do
319− i <- currentIndent
320− return $ i <> emit " * " <> (emit . removeComments) s <> emit "\n"
321−
322− removeComments :: Text -> Text
323− removeComments t =
324− case T.stripPrefix "*/" t of
325− Just rest -> removeComments rest
326− Nothing -> case T.uncons t of
327− Just (x, xs) -> x `T.cons` removeComments xs
328− Nothing -> ""
329−
330−unary' :: (Emit gen) => UnaryOperator -> (AST -> Text) -> Operator PrinterState AST gen
331−unary' op mkStr = Wrap match (<>)
332− where
333− match :: (Emit gen) => Pattern PrinterState AST (gen, AST)
334− match = mkPattern match'
335− where
336− match' (Unary _ op' val) | op' == op = Just (emit $ mkStr val, val)
337− match' _ = Nothing
338−
339−unary :: (Emit gen) => UnaryOperator -> Text -> Operator PrinterState AST gen
340−unary op str = unary' op (const str)
341−
342−negateOperator :: (Emit gen) => Operator PrinterState AST gen
343−negateOperator = unary' Negate (\v -> if isNegate v then "- " else "-")
344− where
345− isNegate (Unary _ Negate _) = True
346− isNegate _ = False
347−
348−binary :: (Emit gen) => BinaryOperator -> Text -> Operator PrinterState AST gen
349−binary op str = AssocL match (\v1 v2 -> v1 <> emit (" " <> str <> " ") <> v2)
350− where
351− match :: Pattern PrinterState AST (AST, AST)
352− match = mkPattern match'
353− where
354− match' (Binary _ op' v1 v2) | op' == op = Just (v1, v2)
355− match' _ = Nothing
356−
357−isLiteral :: AST -> Bool
358−isLiteral Function{} = True
359−isLiteral NumericLiteral{} = True
360−isLiteral StringLiteral{} = True
361−isLiteral BooleanLiteral{} = True
362−isLiteral ObjectLiteral{} = True
363−isLiteral ArrayLiteral{} = True
364−isLiteral _ = False
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−
376−prettyStatements :: (Emit gen) => [AST] -> StateT PrinterState Maybe gen
377−prettyStatements sts = do
378− ils <- forM sts prettyPrintIL'
379− indentString <- currentIndent
380− return $ intercalate (emit "\n") $ map (indentString <>) ils
381−
382−-- | Generate a pretty-printed string representing a collection of C++ expressions at the same indentation level
383−prettyPrintILWithSourceMaps :: [AST] -> (Text, [SMap])
384−prettyPrintILWithSourceMaps il =
385− let StrPos (_, s, mp) = (fromMaybe (internalError "Incomplete pattern") . flip evalStateT (PrinterState 0) . prettyStatements) il
386− in (s, mp)
387−
388−prettyPrintIL :: [AST] -> Text
389−prettyPrintIL = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyStatements
390−
391−-- | Generate an indented, pretty-printed string representing a C++ expression
392−prettyPrintIL' :: (Emit gen) => AST -> StateT PrinterState Maybe gen
393−prettyPrintIL' = A.runKleisli $ runPattern matchValue
394− where
395− matchValue :: (Emit gen) => Pattern PrinterState AST gen
396− matchValue = buildPrettyPrinter operators (literals <+> fmap parensPos matchValue)
397− operators :: (Emit gen) => OperatorTable PrinterState AST gen
398− operators =
399− OperatorTable [ [ unary New "?" ]
400− , [ unary Not "!"
401− , unary BitwiseNot "~"
402− , unary Positive "+"
403− , negateOperator ]
404− , [ binary Multiply "*"
405− , binary Divide "/"
406− , binary Modulus "%" ]
407− , [ binary Add "+"
408− , binary Subtract "-" ]
409− , [ binary ShiftLeft "<<"
410− , binary ShiftRight ">>" ]
411− , [ binary LessThan "<"
412− , binary LessThanOrEqualTo "<="
413− , binary GreaterThan ">"
414− , binary GreaterThanOrEqualTo ">=" ]
415− , [ binary EqualTo "=="
416− , binary NotEqualTo "!=" ]
417− , [ binary BitwiseAnd "&" ]
418− , [ binary BitwiseXor "^" ]
419− , [ binary BitwiseOr "|" ]
420− , [ binary And "&&" ]
421− , [ binary Or "||" ]
422− ]
423−
424−prettyPrintIL1 :: AST -> Text
425−prettyPrintIL1 = maybe (internalError "Incomplete pattern") runPlainString . flip evalStateT (PrinterState 0) . prettyPrintIL'
426−
427−stringLiteral :: PSString -> Text
428−stringLiteral pss | Just s <- decodeString pss = stringLiteral' s
429− where
430− stringLiteral' :: Text -> Text
431− stringLiteral' s = "\"" <> T.concatMap encodeChar s <> "\""
432− encodeChar :: Char -> Text
433− encodeChar '\0' = "\\x00"
434− encodeChar '\b' = "\\b"
435− encodeChar '\t' = "\\t"
436− encodeChar '\n' = "\\n"
437− encodeChar '\v' = "\\v"
438− encodeChar '\f' = "\\f"
439− encodeChar '\r' = "\\r"
440− encodeChar '"' = "\\\""
441− encodeChar '\\' = "\\\\"
442− encodeChar c = T.singleton $ c
443−stringLiteral _ = "\"\\uFFFD\""
444−
445−interfaceSource :: Text -> [(Text,Bool)] -> [Ident] -> Text
446−interfaceSource _ _ _ = ""
447−
448−implHeaderSource :: Text -> [Text] -> Text -> Text
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" <>
457− (T.concat $ formatImport <$> imports) <> ")\n\n" <>
458− "type _ = Any\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
468−
469−implFooterSource :: Text -> [Ident] -> Text
470−implFooterSource mn foreigns =
471− "\n\n\n" <>
472− (if null foreigns
473− then ""
474− else ("// Foreign values\n\n" <>
475− "var " <> foreignDict <> " = "<> foreignMod <> "(\"" <> mn <> "\")\n\n" <>
476− (T.concat $ (\foreign' ->
477− let name = moduleIdentToIL foreign' in
478− varDecl <> " " <> initName name <> " Once\n" <>
479− varDecl <> " " <> valueName name <> " " <> anyType <> "\n\n" <>
480− "func " <>
481− withPrefix name <>
482− "() Any { \n" <>
483− " " <> initName name <> ".Do(func() {\n" <>
484− " " <> valueName name <> " = " <>
485− "Get(" <> foreignDict <> ", " <>
486− (stringLiteral . mkString $ runIdent foreign') <> ")\n" <>
487− " })\n" <>
488− " return " <> valueName name <> "\n" <>
489− "}\n\n") <$> foreigns))) <>
490− if mn == "Main" || mn == "Test.Main" then mainSource else "\n"
491− where
492− mainSource :: Text
493− mainSource = "\
494− \func main() {\n\
495− \ Run(Ꞌmain())\n\
496− \}\n\n\
497− \"
498−
499−varDecl :: Text
500−varDecl = "var"
501−
502−renderArg :: Text -> Text -> Text
503−renderArg decl arg = arg <> " " <> decl
504−
505−foreignMod :: Text
506−foreignMod = "Foreign"
507−
508−foreignDict :: Text
509−foreignDict = "foreign"
510−
511−modLabel :: Text
512−modLabel = "purescript-native"
513−
514−modPrefix :: Text
515−modPrefix = modLabel <> "/output"
516−
517−runtime :: Text
518−runtime = "github.com/" <> modLabel <> "/go-runtime"
519−
520−ffiLoader :: Text
521−ffiLoader = modLabel <> "/ffi-loader"
522−
523−initName :: Text -> Text
524−initName s = "ᛌ" <> s <> "_once"
525−
526−valueName :: Text -> Text
527−valueName s = "ᛧ" <> s
src/Tests.hs+28−25
@@ -15,7 +15,7 @@ testsDir :: IO (FilePath, FilePath)
15-------------------------------------------------------------------------------
15-------------------------------------------------------------------------------
16testsDir = do
16testsDir = do
17 baseDir <- getCurrentDirectory
17 baseDir <- getCurrentDirectory
18− return (baseDir </> "purescript-tests", baseDir)
18+ return (baseDir </> "objc-tests", baseDir)
19
19
20-------------------------------------------------------------------------------
20-------------------------------------------------------------------------------
21runTests :: IO ()
21runTests :: IO ()
@@ -31,11 +31,12 @@ 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.8", "--depth", "1", "https://github.com/purescript/purescript.git"]
35− let passingDir = baseDir </> "purescript" </> "tests" </> "purs" </> "passing"
34+ let passingDir = baseDir </> "examples" </> "passing"
36 passingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents passingDir
35 passingTestCases <- sort . filter (".purs" `isSuffixOf`) <$> getDirectoryContents passingDir
37
36
37+ -- Auto-generate Makefile
38 setCurrentDirectory outputDir
38 setCurrentDirectory outputDir
39+ callProcess "cp" ["../Makefile", "."]
39
40
40 fetchPackages
41 fetchPackages
41
42
@@ -60,15 +61,14 @@ runTests = do
60 testCaseDirExists <- doesDirectoryExist testCaseDir
61 testCaseDirExists <- doesDirectoryExist testCaseDir
61 when testCaseDirExists $ callProcess "cp" ["-R", testCaseDir, srcDir]
62 when testCaseDirExists $ callProcess "cp" ["-R", testCaseDir, srcDir]
62
63
63− callProcess "rm" ["-rf", "output"]
64+ callProcess "make" ["clean"]
64− callProcess "rm" ["-rf", "Main"]
65+ callProcess "make" ["release", "-j12"]
65− callProcess "spago" ["build"]
66 --
66 --
67− -- Run executable file
67+ -- Run C++ files
68 --
68 --
69 outputFile <- openFile (tmp </> logpath </> logfile) AppendMode
69 outputFile <- openFile (tmp </> logpath </> logfile) AppendMode
70 hPutStrLn outputFile ("\n" ++ inputFile ++ ":")
70 hPutStrLn outputFile ("\n" ++ inputFile ++ ":")
71− proc <- runProcess ("./" </> "Main") [] Nothing Nothing Nothing (Just outputFile) Nothing
71+ proc <- runProcess ("output" </> "bin" </> "main") [] Nothing Nothing Nothing (Just outputFile) Nothing
72
72
73 removeFile (srcDir </> inputFile)
73 removeFile (srcDir </> inputFile)
74 when testCaseDirExists $ callProcess "rm" ["-rf", srcDir </> (takeWhile (/='.') inputFile)]
74 when testCaseDirExists $ callProcess "rm" ["-rf", srcDir </> (takeWhile (/='.') inputFile)]
@@ -80,7 +80,7 @@ runTests = do
80 --
80 --
81
81
82 setCurrentDirectory baseDir
82 setCurrentDirectory baseDir
83− putStrLn "PureScript tests finished"
83+ putStrLn "objc-tests finished"
84 putStrLn $ "Total tests available: " ++ show (length passingTestCases)
84 putStrLn $ "Total tests available: " ++ show (length passingTestCases)
85 putStrLn $ "Tests run: " ++ show (length tests)
85 putStrLn $ "Tests run: " ++ show (length tests)
86 putStrLn $ "Tests skipped: " ++ show (length skipped)
86 putStrLn $ "Tests skipped: " ++ show (length skipped)
@@ -89,21 +89,23 @@ runTests = do
89packages :: [String]
89packages :: [String]
90-------------------------------------------------------------------------------
90-------------------------------------------------------------------------------
91packages =
91packages =
92+ [ "eff"
92− [ "arrays"
93+ , "arrays"
93 , "assert"
94 , "assert"
94 , "console"
95 , "console"
95 , "control"
96 , "control"
96− , "effect"
97 , "foldable-traversable"
97 , "foldable-traversable"
98 , "functions"
98 , "functions"
99 , "generics-rep"
99 , "generics-rep"
100 , "invariant"
100 , "invariant"
101+ , "monoid"
101 , "newtype"
102 , "newtype"
102 , "partial"
103 , "partial"
103 , "prelude"
104 , "prelude"
104 , "proxy"
105 , "proxy"
105 , "refs"
106 , "refs"
106 , "st"
107 , "st"
108+ , "symbols"
107 , "type-equality"
109 , "type-equality"
108 , "typelevel-prelude"
110 , "typelevel-prelude"
109 ]
111 ]
@@ -112,30 +114,31 @@ packages =
112fetchPackages :: IO ()
114fetchPackages :: IO ()
113-------------------------------------------------------------------------------
115-------------------------------------------------------------------------------
114fetchPackages = do
116fetchPackages = do
115− callProcess "spago" ["init"]
117+ callProcess "psc-package" ["init"]
116− callCommand "sed '$s/\\(}\\)/, backend = \"psgo\"\\1/' < spago.dhall | dhall format > spago.tmp"
117− callCommand "mv spago.tmp spago.dhall"
118−
119− mapM (callProcess "spago" . (\p -> ["install", p])) packages
118+ mapM (callProcess "psc-package" . (\p -> ["install", p])) packages
120 return ()
119 return ()
121
120
122-------------------------------------------------------------------------------
121-------------------------------------------------------------------------------
123skipped :: [String]
122skipped :: [String]
124-------------------------------------------------------------------------------
123-------------------------------------------------------------------------------
125skipped =
124skipped =
126− [ "2172.purs" -- foreign in Main needed (test manually)
125+ [
127− , "EffFn.purs" -- foreign in Main needed (test manually)
128− , "FunWithFunDeps.purs" -- foreign in Main needed (test manually)
129− , "NumberLiterals.purs" -- unreliable float string comparison (test manually)
126+ "NumberLiterals.purs" -- unreliable float comparison, test manually
127+ , "2172.purs" -- ps-side foreign issue
128+ , "AppendInReverse.purs" -- test not updated to post-0.12?
129+ , "EffFn.purs" -- ps-side foreign issue
130+ , "MultiArgFunctions.purs" -- not supported (needed?) right now
131+ , "NegativeIntInRange.purs" -- supposed to fail?
130− , "PolyLabels.purs" -- foreign in Main needed (test manually)
132+ , "PolyLabels.purs" -- ps-side foreign issue
131− , "RowUnion.purs" -- foreign in Main needed (test manually)
133+ , "RowUnion.purs" -- ps-side foreign issue
132 , "ShadowedModuleName.purs" -- ?
134 , "ShadowedModuleName.purs" -- ?
135+ , "FunWithFunDeps.purs" -- requires FFI
133− , "StringEdgeCases.purs" -- javascript char encoding specific
136+ , "StringEdgeCases.purs" -- ?
134− , "StringEscapes.purs" -- Partially javascript-char-encoding-specific (test manually)
137+ , "StringEscapes.purs" -- TODO: UTF16-specific?
135 ]
138 ]
136
139
137logpath :: FilePath
140logpath :: FilePath
138logpath = "purescript-output"
141logpath = "purescript-output"
139
142
140logfile :: FilePath
143logfile :: FilePath
141−logfile = "purescript-tests.out"
144+logfile = "objc-tests.out"
stack.nixdeleted−23
@@ -1,23 +0,0 @@
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−}:
17−
18−pkgs.haskell.lib.buildStackProject {
19− name = "default-stack-shell";
20− ghc = pkgs.haskell.compiler.ghc865;
21− buildInputs = with pkgs; [git git-lfs gmp ncurses zlib];
22− LANG = "en_US.UTF-8";
23−}
stack.yaml+63−18
@@ -1,24 +1,69 @@
1−resolver: lts-13.26
1+# This file was automatically generated by 'stack init'
2+#
3+# Some commonly used options have been documented as comments in this file.
4+# For advanced use and comprehensive documentation of the format, please see:
5+# https://docs.haskellstack.org/en/stable/yaml_configuration/
2
6
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-10.3
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.
3packages:
38packages:
4- .
39- .
5−
40+# Dependency packages to be pulled from upstream that are not in the resolver
41+# (e.g., acme-missiles-0.3)
6extra-deps:
42extra-deps:
7−- git: https://github.com/purescript/purescript
43+- git: https://github.com/purescript/purescript
8− commit: 9cad73ed8ea7df3011032ddbd2f3de5a0c08629c
44+ commit: v0.12.0
9−- happy-1.19.9
45+- pipes-http-1.0.5
10−- language-javascript-0.7.0.0
46+
11−- network-3.0.1.1
47+# Override default flag values for local packages and extra-deps
12−- these-1.0.1
48+# flags: {}
13−- semialign-1
14
49
15−nix:
50+# Extra package databases containing global packages
16− enable: false
51+# extra-package-dbs: []
17− shell-file: stack.nix
18
52
19−flags:
53+# Control whether we use the GHC we find on the path
20− aeson-pretty:
54+# system-ghc: true
21− lib-only: true
55+#
22− these:
56+# Require a specific version of stack, using version ranges
23− assoc: false
57+# require-stack-version: -any # Default
24− quickcheck: false
58+# require-stack-version: ">=1.6"
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
support/Makefilenew file+94
@@ -0,0 +1,94 @@
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 CFLAGS=-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 CFLAGS += --std=c++11
28+
29+DEBUG := "-DDEBUG -g"
30+RELEASE := "-DNDEBUG -O3"
31+
32+INCLUDES := -I $(CC_SRC)
33+BIN_DIR := $(OUTPUT)/bin
34+
35+PACKAGE_SOURCES = $(subst \,/,$(shell $(PSC_PACKAGE) sources))
36+PURESCRIPT_PKGS := $(firstword $(subst /, ,$(PACKAGE_SOURCES)))
37+
38+## Not all environments support globstar (** dir pattern)
39+rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
40+
41+release: codegen
42+ @$(MAKE) $(BIN) CFLAGS+=$(RELEASE)
43+
44+debug: codegen
45+ @$(MAKE) $(BIN) CFLAGS+=$(DEBUG)
46+
47+.PHONY: corefn
48+corefn: PURESCRIPT_PKG_SRCS=$(foreach d,$(PACKAGE_SOURCES),$(call rwildcard,$(firstword $(subst *, ,$(d))),*.purs))
49+corefn: PURESCRIPT_SRCS=$(call rwildcard,$(SRC)/,*.purs)
50+corefn: $(PURESCRIPT_PKGS)
51+ @$(PURS) $(PURSFLAGS) --output $(OUTPUT) $(PURESCRIPT_PKG_SRCS) $(PURESCRIPT_SRCS)
52+
53+.PHONY: codegen
54+codegen: COREFN_SRCS=$(call rwildcard,$(OUTPUT)/,corefn.json)
55+codegen: corefn
56+ @$(PSCPP) $(COREFN_SRCS)
57+
58+$(PURESCRIPT_PKGS):
59+ @echo "Getting packages using" $(PSC_PACKAGE) "..."
60+ @$(PSC_PACKAGE) update
61+
62+SRCS := $(call rwildcard,$(CC_SRC)/,*.cpp)
63+SRCS += $(call rwildcard,$(FFI_SRC)/,*.cpp)
64+SRCS += $(call rwildcard,$(FFI_SRC)/,*.c)
65+
66+OBJS1 = $(SRCS:.cpp=.o)
67+OBJS = $(OBJS1:.c=.o)
68+DEPS = $(OBJS:.o=.d)
69+
70+$(BIN): $(OBJS)
71+ @echo "Linking" $(BIN_DIR)/$(BIN)
72+ @mkdir -p $(BIN_DIR)
73+ @$(CXX) $^ -o $(BIN_DIR)/$@ $(LDFLAGS)
74+
75+-include $(DEPS)
76+
77+%.o: %.cpp
78+ @echo "Creating" $@
79+ @$(CXX) $(CFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
80+
81+%.o: %.c
82+ @echo "Creating" $@
83+ @$(CXX) $(CFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@
84+
85+.PHONY: all
86+all: release
87+
88+.PHONY: clean
89+clean:
90+ @-rm -rf $(OUTPUT)
91+
92+.PHONY: run
93+run:
94+ @$(BIN_DIR)/$(BIN) $(ARGS)
support/ffi-loader.godeleted−30
@@ -1,30 +0,0 @@
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-loaderdeleted−2
@@ -1,2 +0,0 @@
1−module $PROJECT/purescript-native/ffi-loader
2−
support/go.mod.outputdeleted−2
@@ -1,2 +0,0 @@
1−module $PROJECT/purescript-native/output
2−
support/go.mod.workingdeleted−1
@@ -1 +0,0 @@
1−module $PROJECT/example