This repository has been archived by the owner on Aug 23, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 34
/
Code.hs
186 lines (145 loc) · 4.81 KB
/
Code.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
{-# LANGUAGE OverloadedStrings #-}
module Eval.Code (eval) where
import Control.Monad.Except (ExceptT, runExceptT, throwError, when)
import qualified Control.Monad.RWS as State
import Control.Monad.Trans (liftIO)
import qualified Data.Binary as Binary
import qualified Data.ByteString.Lazy.Char8 as BS
import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import System.Directory (doesFileExist, removeFile)
import System.FilePath ((</>), (<.>), replaceExtension)
import System.IO (hPutStrLn, stderr)
import qualified Environment as Env
import qualified Elm.Compiler as Compiler
import qualified Elm.Compiler.Module as Module
import qualified Elm.Compiler.Type as Type
import qualified Elm.Package as Pkg
import qualified Elm.Package.Description as Desc
import qualified Elm.Package.Paths as Path
import qualified Elm.Utils as Utils
eval :: (Maybe Env.DefName, String) -> Env.Task ()
eval code =
let
tempElmPath =
"repl-temp-000" <.> "elm"
tempJsPath =
replaceExtension tempElmPath "js"
in
do oldEnv <- State.get
let newEnv = Env.insert code oldEnv
liftIO $ writeFile tempElmPath (Env.toElmCode newEnv)
let needsPrint = Env.needsPrint (fst code)
result <- liftIO (runExceptT (tryCompile tempElmPath tempJsPath newEnv needsPrint))
liftIO $ removeIfExists tempElmPath
liftIO $ removeIfExists tempJsPath
case result of
Left msg ->
liftIO (hPutStrLn stderr msg)
Right () ->
State.put newEnv
tryCompile :: FilePath -> FilePath -> Env.Env -> Bool -> ExceptT String IO ()
tryCompile tempElmPath tempJsPath env needsPrint =
do run (Env.compilerPath env) (Env.flags env ++ elmArgs)
when needsPrint (liftIO (addHook tempJsPath))
value <- run (Env.interpreterPath env) [tempJsPath]
liftIO $ printIfNeeded value
where
elmArgs =
[ tempElmPath
, "--yes"
, "--output=" ++ tempJsPath
]
addHook :: FilePath -> IO ()
addHook tempJsPath =
do js <- Text.readFile tempJsPath
let (body, outro) = Text.breakOnEnd "var Elm = {};" js
let (intro, midtro) = Text.breakOnEnd lastVar body
Text.writeFile tempJsPath $ Text.concat $
[ nodeHeader
, intro
, " = "
, lastVar
, midtro
, nodeFooter
, outro
]
printIfNeeded :: String -> IO ()
printIfNeeded rawValue =
case rawValue of
"" ->
return ()
_ ->
do tipe <- getType
let value =
init rawValue
let isTooLong =
List.isInfixOf "\n" value
|| List.isInfixOf "\n" tipe
|| length value + 3 + length tipe > 80
let tipeAnnotation =
if isTooLong then
"\n : " ++ List.intercalate "\n " (lines tipe)
else
" : " ++ tipe
putStrLn (value ++ tipeAnnotation)
run :: FilePath -> [String] -> ExceptT String IO String
run name args =
do result <- liftIO (Utils.unwrappedRun name args)
case result of
Right stdout ->
return stdout
Left (Utils.MissingExe msg) ->
throwError msg
Left (Utils.CommandFailed _out err) ->
throwError err
nodeHeader :: Text.Text
nodeHeader =
Text.concat
[ "process.on('uncaughtException', function(err) {\n\
\ process.stderr.write(err.toString());\n\
\ process.exit(1);\n\
\});\n\
\var ", lastVar, ";\n"
]
nodeFooter :: Text.Text
nodeFooter =
Text.concat
[ "\n"
, "if (typeof ", lastVar, " !== 'undefined') {\n"
, " console.log(_elm_lang$core$Native_Utils.toString(", lastVar, "));\n"
, "}\n"
]
lastVar :: Text.Text
lastVar =
Text.pack Env.lastVarString
getType :: IO String
getType =
do result <- runExceptT getTypeHelp
case result of
Right tipe -> return tipe
Left _ -> return ""
getTypeHelp :: ExceptT String IO String
getTypeHelp =
do description <- Desc.read id Path.description
binary <- liftIO (BS.readFile (interfacePath description))
let types = Module.interfaceAliasedTypes (Binary.decode binary)
case Map.lookup lastVar types of
Just tipe -> return (Type.toString tipe)
Nothing -> throwError "Type signature not found!"
interfacePath :: Desc.Description -> FilePath
interfacePath description =
Path.stuffDirectory
</> "build-artifacts"
</> Pkg.versionToString Compiler.version
</> Pkg.toFilePath (Desc.name description)
</> Pkg.versionToString (Desc.version description)
</> "Repl.elmi"
removeIfExists :: FilePath -> IO ()
removeIfExists fileName =
do exists <- doesFileExist fileName
if exists
then removeFile fileName
else return ()