12
12
--
13
13
-----------------------------------------------------------------------------
14
14
15
- {-# LANGUAGE DataKinds, OverloadedStrings, TemplateHaskell #-}
15
+ {-# LANGUAGE DataKinds #-}
16
+ {-# LANGUAGE OverloadedStrings #-}
17
+ {-# LANGUAGE TemplateHaskell #-}
18
+ {-# LANGUAGE TupleSections #-}
19
+ {-# LANGUAGE GeneralizedNewtypeDeriving #-}
16
20
17
21
module Main (
18
22
main
19
23
) where
20
24
21
25
import qualified Language.PureScript as P
26
+ import qualified Language.PureScript.CodeGen.JS as J
27
+ import qualified Language.PureScript.CoreFn as CF
28
+ import qualified Language.PureScript.Bundle as B
22
29
23
30
import Data.Version (showVersion )
24
31
import Data.Monoid
25
32
import Data.String
26
33
import Data.Maybe (mapMaybe )
27
34
import Data.List (intercalate )
28
35
import Data.FileEmbed
36
+ import Data.Time.Clock (UTCTime ())
37
+ import Data.Foldable (traverse_ )
29
38
30
39
import qualified Data.ByteString as B
31
40
import qualified Data.ByteString.Char8 as BC8
@@ -40,6 +49,10 @@ import Control.Applicative
40
49
import Control.Monad (when , forM_ )
41
50
import Control.Monad.Trans
42
51
import Control.Monad.Reader
52
+ import Control.Monad.Error.Class (MonadError (.. ))
53
+ import Control.Monad.Trans.Except
54
+ import Control.Monad.Reader
55
+ import Control.Monad.Writer
43
56
44
57
import Network.HTTP.Types (status500 )
45
58
@@ -56,30 +69,63 @@ import qualified Paths_trypurescript as Paths
56
69
57
70
import System.Environment (getArgs )
58
71
59
- data Compiled = Compiled { js :: String
60
- , externs :: String
61
- }
72
+ newtype Compiled = Compiled { runCompiled :: String }
62
73
63
74
newtype Response = Response { runResponse :: Either String Compiled }
64
75
65
- options :: P. Options P. Compile
66
- options = P. defaultCompileOptions
67
- { P. optionsAdditional = P. CompileOptions " PS" [] []
68
- , P. optionsMain = Just " Main"
69
- }
70
-
71
- compile :: [P. Module ] -> String -> IO Response
72
- compile _ input | length input > 20000 = return $ Response $ Left " Please limit your input to 20000 characters"
73
- compile prelude input = do
76
+ type FS = M. Map B. ModuleIdentifier String
77
+
78
+ newtype Try a = Try { unTry :: ReaderT P. Options (WriterT P. MultipleErrors (WriterT FS (Either P. MultipleErrors ))) a }
79
+ deriving (Functor , Applicative , Monad , MonadError P.MultipleErrors , MonadWriter P.MultipleErrors , MonadReader P.Options )
80
+
81
+ runTry :: Try a -> Either P. MultipleErrors (a , FS )
82
+ runTry = runWriterT . fmap fst . runWriterT . flip runReaderT P. defaultOptions . unTry
83
+
84
+ writeTextFileTry :: B. ModuleIdentifier -> String -> Try ()
85
+ writeTextFileTry mid txt = Try . lift . lift . tell $ M. singleton mid txt
86
+
87
+ makeActions :: M. Map P. ModuleName (FilePath , P. ForeignJS ) -> P. MakeActions Try
88
+ makeActions foreigns = P. MakeActions getInputTimestamp getOutputTimestamp readExterns codegen progress
89
+ where
90
+ getInputTimestamp :: P. ModuleName -> Try (Either P. RebuildPolicy (Maybe UTCTime ))
91
+ getInputTimestamp _ = return (Left P. RebuildAlways )
92
+
93
+ getOutputTimestamp :: P. ModuleName -> Try (Maybe UTCTime )
94
+ getOutputTimestamp mn = return (Just (error " getOutputTimestamp: read timestamp" ))
95
+
96
+ readExterns :: P. ModuleName -> Try (FilePath , String )
97
+ readExterns _ = error " readExterns: not supported"
98
+
99
+ codegen :: CF. Module CF. Ann -> P. Environment -> P. SupplyVar -> P. Externs -> Try ()
100
+ codegen m _ nextVar exts = do
101
+ let mn = P. runModuleName (CF. moduleName m)
102
+ foreignInclude <- case (CF. moduleName m `M.lookup` foreigns, CF. moduleForeign m) of
103
+ (Just path, fs) | not (null fs) ->
104
+ return $ Just $ J. JSApp (J. JSVar " require" ) [J. JSStringLiteral " ./foreign" ]
105
+ _ ->
106
+ return Nothing
107
+ pjs <- P. evalSupplyT nextVar $ P. prettyPrintJS <$> J. moduleToJs m foreignInclude
108
+ writeTextFileTry (B. ModuleIdentifier mn B. Regular ) pjs
109
+ traverse_ (writeTextFileTry (B. ModuleIdentifier mn B. Foreign ) . snd ) (CF. moduleName m `M.lookup` foreigns)
110
+
111
+ progress :: String -> Try ()
112
+ progress _ = return ()
113
+
114
+ compile :: [P. Module ] -> M. Map P. ModuleName (FilePath , P. ForeignJS ) -> String -> IO Response
115
+ compile _ _ input | length input > 20000 = return $ Response $ Left " Please limit your input to 20000 characters"
116
+ compile prelude foreigns input = do
74
117
case either Left (Right . map snd ) $ P. parseModulesFromFiles (const " <file>" ) [(undefined , input)] of
75
118
Left parseError -> do
76
119
return $ Response $ Left $ show parseError
77
120
Right modules -> do
78
- case flip runReaderT options $ P. compile (prelude ++ modules) [" Generated by trypurescript" ] of
79
- Left error ->
80
- return $ Response $ Left error
81
- Right (js, externs, _) ->
82
- return $ Response $ Right $ Compiled js externs
121
+ let allModules = map (Left P. RebuildNever , ) prelude ++ map (Left P. RebuildAlways , ) modules
122
+ case runTry (P. make (makeActions foreigns) allModules) of
123
+ Left err ->
124
+ return $ Response $ Left (P. prettyPrintMultipleErrors False err)
125
+ Right (_, fs) ->
126
+ case B. bundle (M. toList fs) [B. ModuleIdentifier " Main" B. Regular ] (Just " Main" ) " TryPS" of
127
+ Left err -> return $ Response $ Left (unlines (B. printErrorMessage err))
128
+ Right js -> return $ Response $ Right $ Compiled js
83
129
84
130
str :: String -> String
85
131
str = id
@@ -99,14 +145,25 @@ scripts = BC8.unpack $(embedFile "assets/scripts.js")
99
145
defaultCode :: String
100
146
defaultCode = BC8. unpack $ (embedFile " examples/default.purs" )
101
147
148
+ preludePurs :: [String ]
149
+ preludePurs =
150
+ [ BC8. unpack $ (embedFile " prelude/Prelude.purs" )
151
+ , BC8. unpack $ (embedFile " prelude/Control/Monad/Eff.purs" )
152
+ , BC8. unpack $ (embedFile " prelude/Control/Monad/Eff/Console.purs" )
153
+ ]
154
+
155
+ preludeJs :: [String ]
156
+ preludeJs =
157
+ [ BC8. unpack $ (embedFile " prelude/Prelude.js" )
158
+ , BC8. unpack $ (embedFile " prelude/Control/Monad/Eff.js" )
159
+ , BC8. unpack $ (embedFile " prelude/Control/Monad/Eff/Console.js" )
160
+ ]
161
+
102
162
examples :: [(String , (String , String ))]
103
163
examples =
104
164
[ (" adt" , (" Algebraic Data Types" , BC8. unpack $ (embedFile " examples/adt.purs" )))
105
165
, (" ops" , (" Operators" , BC8. unpack $ (embedFile " examples/operators.purs" )))
106
- , (" arrays" , (" Arrays" , BC8. unpack $ (embedFile " examples/arrays.purs" )))
107
166
, (" rows" , (" Row Polymorphism" , BC8. unpack $ (embedFile " examples/rows.purs" )))
108
- , (" ffi" , (" FFI" , BC8. unpack $ (embedFile " examples/ffi.purs" )))
109
- , (" mutable" , (" Mutable Variables" , BC8. unpack $ (embedFile " examples/mutable.purs" )))
110
167
, (" recursion" , (" Recursion" , BC8. unpack $ (embedFile " examples/recursion.purs" )))
111
168
, (" do" , (" Do Notation" , BC8. unpack $ (embedFile " examples/do.purs" )))
112
169
, (" tco" , (" Tail-Call Elimination" , BC8. unpack $ (embedFile " examples/tco.purs" )))
@@ -128,32 +185,28 @@ page input = html $ renderHtml $ do
128
185
H. script ! A. type_ " text/javascript" ! A. src " //cdnjs.cloudflare.com/ajax/libs/ace/1.1.01/mode-haskell.js" $ mempty
129
186
H. script ! A. type_ " text/javascript" ! A. src " //cdnjs.cloudflare.com/ajax/libs/ace/1.1.01/theme-dawn.js" $ mempty
130
187
H. body $ do
131
- H. a ! A. href " https://github.com/purescript" $
132
- H. img ! A. style " position: absolute; top: 0; right: 0; border: 0;"
133
- ! A. src " https://github-camo.global.ssl.fastly.net/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67"
134
- ! A. alt " Fork me on GitHub"
135
- ! customAttribute " data-canonical-src" " https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"
136
- H. div ! A. class_ " wrapper" $ do
137
- H. div ! A. class_ " header" $ do
138
- H. h1 $ H. toHtml $ str " Try PureScript!"
139
- H. div ! A. class_ " body" $ do
140
- H. p $ H. toHtml $ str " Type PureScript code below and press 'Compile', or select one of the examples below:"
141
-
142
- H. h2 $ H. toHtml $ str " Examples"
143
- H. ul $ do
144
- forM_ examples $ \ (name, (title, _)) ->
145
- H. li $ H. a ! A. href (fromString $ " /example/" ++ name) $ H. toHtml title
146
-
147
- H. h2 $ H. toHtml $ str " PureScript Code"
148
- H. div ! A. id " code" $ mempty
149
- H. textarea ! A. name " code" ! A. id " textarea" ! A. style " display: none;" $ H. toHtml $ str input
150
- H. p $ H. button ! A. id " compile" $ H. toHtml $ str " Compile and Run"
151
- H. script ! A. type_ " text/javascript" $ preEscapedToHtml scripts
152
- H. div ! A. id " results" $ mempty
188
+ H. div ! A. class_ " wrapper" $ do
189
+ H. div ! A. class_ " header" $ do
190
+ H. h1 $ H. toHtml $ str " Try PureScript!"
191
+ H. div ! A. class_ " body" $ do
192
+ H. p $ H. toHtml $ str " Type PureScript code below and press 'Compile', or select one of the examples below:"
193
+
194
+ H. h2 $ H. toHtml $ str " Examples"
195
+ H. ul $ do
196
+ forM_ examples $ \ (name, (title, _)) ->
197
+ H. li $ H. a ! A. href (fromString $ " /example/" ++ name) $ H. toHtml title
198
+
199
+ H. h2 $ H. toHtml $ str " PureScript Code"
200
+ H. div ! A. id " code" $ mempty
201
+ H. textarea ! A. name " code" ! A. id " textarea" ! A. style " display: none;" $ H. toHtml $ str input
202
+ H. p $ H. button ! A. id " compile" $ H. toHtml $ str " Compile and Run"
203
+ H. script ! A. type_ " text/javascript" $ preEscapedToHtml scripts
204
+ H. div ! A. id " results" $ mempty
153
205
154
206
server :: Int -> IO ()
155
207
server port = do
156
- let preludeModules = either (error . show ) (map snd ) $ P. parseModulesFromFiles (const " <prelude>" ) [(undefined , P. prelude)]
208
+ let preludeModules = either (error . show ) (map snd ) $ P. parseModulesFromFiles (const " <prelude>" ) (map (undefined , ) preludePurs)
209
+ Right (foreigns, _) <- runExceptT $ runWriterT $ P. parseForeignModulesFromFiles (map (error " foreign filename read" , ) preludeJs)
157
210
scotty port $ do
158
211
get " /" $ do
159
212
page defaultCode
@@ -165,12 +218,12 @@ server port = do
165
218
page code
166
219
post " /compile/text" $ do
167
220
code <- BLC8. unpack <$> body
168
- response <- lift $ compile preludeModules code
221
+ response <- lift $ compile preludeModules foreigns code
169
222
case runResponse response of
170
223
Left err -> do
171
224
Scotty. json $ A. object [ " error" .= err ]
172
225
Right comp ->
173
- Scotty. json $ A. object [ " js" .= js comp ]
226
+ Scotty. json $ A. object [ " js" .= runCompiled comp ]
174
227
175
228
main :: IO ()
176
229
main = do
0 commit comments