1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript
9import Prelude hiding (cycle, print)
10import qualified Data.ByteString.Builder as B
11import Data.Monoid ((<>))
12import qualified Data.List as List
14import qualified Data.Map as Map
15import qualified Data.Name as Name
16import qualified Data.Set as Set
17import qualified Data.Utf8 as Utf8
19import qualified AST.Canonical as Can
20import qualified AST.Optimized as Opt
21import qualified Data.Index as Index
22import qualified Elm.Kernel as K
23import qualified Elm.ModuleName as ModuleName
24import qualified Generate.JavaScript.Builder as JS
25import qualified Generate.JavaScript.Expression as Expr
26import qualified Generate.JavaScript.Functions as Functions
27import qualified Generate.JavaScript.Name as JsName
28import qualified Generate.Mode as Mode
29import qualified Reporting.Doc as D
30import qualified Reporting.Render.Type as RT
31import qualified Reporting.Render.Type.Localizer as L
38type Graph = Map.Map Opt.Global Opt.Node
39type Mains = Map.Map ModuleName.Canonical Opt.Main
42generate :: Mode.Mode -> Opt.GlobalGraph -> Mains -> B.Builder
43generate mode (Opt.GlobalGraph graph _) mains =
45 state = Map.foldrWithKey (addMain mode graph) emptyState mains
47 "(function(scope){\n'use strict';"
48 <> Functions.functions
50 <> stateToBuilder state
51 <> toMainExports mode mains
55addMain :: Mode.Mode -> Graph -> ModuleName.Canonical -> Opt.Main -> State -> State
56addMain mode graph home _ state =
57 addGlobal mode graph state (Opt.Global home "main")
60perfNote :: Mode.Mode -> B.Builder
67 "console.warn('Compiled in DEV mode. Follow the advice at "
68 <> B.stringUtf8 (D.makeNakedLink "optimize")
69 <> " for better performance and smaller assets.');"
72 "console.warn('Compiled in DEBUG mode. Follow the advice at "
73 <> B.stringUtf8 (D.makeNakedLink "optimize")
74 <> " for better performance and smaller assets.');"
81generateForRepl :: Bool -> L.Localizer -> Opt.GlobalGraph -> ModuleName.Canonical -> Name.Name -> Can.Annotation -> B.Builder
82generateForRepl ansi localizer (Opt.GlobalGraph graph _) home name (Can.Forall _ tipe) =
84 mode = Mode.Dev Nothing
85 debugState = addGlobal mode graph emptyState (Opt.Global ModuleName.debug "toString")
86 evalState = addGlobal mode graph debugState (Opt.Global home name)
88 "process.on('uncaughtException', function(err) { process.stderr.write(err.toString() + '\\n'); process.exit(1); });"
89 <> Functions.functions
90 <> stateToBuilder evalState
91 <> print ansi localizer home name tipe
94print :: Bool -> L.Localizer -> ModuleName.Canonical -> Name.Name -> Can.Type -> B.Builder
95print ansi localizer home name tipe =
97 value = JsName.toBuilder (JsName.fromGlobal home name)
98 toString = JsName.toBuilder (JsName.fromKernel Name.debug "toAnsiString")
99 tipeDoc = RT.canToDoc localizer RT.None tipe
100 bool = if ansi then "true" else "false"
102 "var _value = " <> toString <> "(" <> bool <> ", " <> value <> ");\n\
103 \var _type = " <> B.stringUtf8 (show (D.toString tipeDoc)) <> ";\n\
104 \function _print(t) { console.log(_value + (" <> bool <> " ? '\x1b[90m' + t + '\x1b[0m' : t)); }\n\
105 \if (_value.length + 3 + _type.length >= 80 || _type.indexOf('\\n') >= 0) {\n\
106 \ _print('\\n : ' + _type.split('\\n').join('\\n '));\n\
108 \ _print(' : ' + _type);\n\
113-- GRAPH TRAVERSAL STATE
118 { _revKernels :: [B.Builder]
119 , _revBuilders :: [B.Builder]
120 , _seenGlobals :: Set.Set Opt.Global
126 State mempty [] Set.empty
129stateToBuilder :: State -> B.Builder
130stateToBuilder (State revKernels revBuilders _) =
131 prependBuilders revKernels (prependBuilders revBuilders mempty)
134prependBuilders :: [B.Builder] -> B.Builder -> B.Builder
135prependBuilders revBuilders monolith =
136 List.foldl' (\m b -> b <> m) monolith revBuilders
143addGlobal :: Mode.Mode -> Graph -> State -> Opt.Global -> State
144addGlobal mode graph state@(State revKernels builders seen) global =
145 if Set.member global seen then
148 addGlobalHelp mode graph global $
149 State revKernels builders (Set.insert global seen)
152addGlobalHelp :: Mode.Mode -> Graph -> Opt.Global -> State -> State
153addGlobalHelp mode graph global state =
155 addDeps deps someState =
156 Set.foldl' (addGlobal mode graph) someState deps
158 case graph ! global of
159 Opt.Define expr deps ->
160 addStmt (addDeps deps state) (
161 var global (Expr.generate mode expr)
164 Opt.DefineTailFunc argNames body deps ->
165 addStmt (addDeps deps state) (
166 let (Opt.Global _ name) = global in
167 var global (Expr.generateTailDef mode name argNames body)
170 Opt.Ctor index arity ->
172 var global (Expr.generateCtor mode global index arity)
175 Opt.Link linkedGlobal ->
176 addGlobal mode graph state linkedGlobal
178 Opt.Cycle names values functions deps ->
179 addStmt (addDeps deps state) (
180 generateCycle mode global names values functions
183 Opt.Manager effectsType ->
184 generateManager mode graph global effectsType state
186 Opt.Kernel chunks deps ->
187 if isDebugger global && not (Mode.isDebug mode) then
190 addKernel (addDeps deps state) (generateKernel mode chunks)
194 generateEnum mode global index
198 addStmt (addGlobal mode graph state identity) (
199 generateBox mode global
202 Opt.PortIncoming decoder deps ->
203 addStmt (addDeps deps state) (
204 generatePort mode global "incomingPort" decoder
207 Opt.PortOutgoing encoder deps ->
208 addStmt (addDeps deps state) (
209 generatePort mode global "outgoingPort" encoder
213addStmt :: State -> JS.Stmt -> State
215 addBuilder state (JS.stmtToBuilder stmt)
218addBuilder :: State -> B.Builder -> State
219addBuilder (State revKernels revBuilders seen) builder =
220 State revKernels (builder:revBuilders) seen
223addKernel :: State -> B.Builder -> State
224addKernel (State revKernels revBuilders seen) kernel =
225 State (kernel:revKernels) revBuilders seen
228var :: Opt.Global -> Expr.Code -> JS.Stmt
229var (Opt.Global home name) code =
230 JS.Var (JsName.fromGlobal home name) (Expr.codeToExpr code)
233isDebugger :: Opt.Global -> Bool
234isDebugger (Opt.Global (ModuleName.Canonical _ home) _) =
235 home == Name.debugger
242generateCycle :: Mode.Mode -> Opt.Global -> [Name.Name] -> [(Name.Name, Opt.Expr)] -> [Opt.Def] -> JS.Stmt
243generateCycle mode (Opt.Global home _) names values functions =
245 [ JS.Block $ map (generateCycleFunc mode home) functions
246 , JS.Block $ map (generateSafeCycle mode home) values
247 , case map (generateRealCycle home) values of
257 JS.Try (JS.Block realBlock) JsName.dollar $ JS.Throw $ JS.String $
258 "Some top-level definitions from `" <> Name.toBuilder (ModuleName._module home) <> "` are causing infinite recursion:\\n"
260 <> "\\n\\nThese errors are very tricky, so read "
261 <> B.stringUtf8 (D.makeNakedLink "bad-recursion")
262 <> " to learn how to fix it!"
266generateCycleFunc :: Mode.Mode -> ModuleName.Canonical -> Opt.Def -> JS.Stmt
267generateCycleFunc mode home def =
270 JS.Var (JsName.fromGlobal home name) (Expr.codeToExpr (Expr.generate mode expr))
272 Opt.TailDef name args expr ->
273 JS.Var (JsName.fromGlobal home name) (Expr.codeToExpr (Expr.generateTailDef mode name args expr))
276generateSafeCycle :: Mode.Mode -> ModuleName.Canonical -> (Name.Name, Opt.Expr) -> JS.Stmt
277generateSafeCycle mode home (name, expr) =
278 JS.FunctionStmt (JsName.fromCycle home name) [] $
279 Expr.codeToStmtList (Expr.generate mode expr)
282generateRealCycle :: ModuleName.Canonical -> (Name.Name, expr) -> JS.Stmt
283generateRealCycle home (name, _) =
285 safeName = JsName.fromCycle home name
286 realName = JsName.fromGlobal home name
289 [ JS.Var realName (JS.Call (JS.Ref safeName) [])
290 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
291 JS.Function Nothing [] [ JS.Return (JS.Ref realName) ]
295drawCycle :: [Name.Name] -> B.Builder
298 topLine = "\\n ┌─────┐"
299 nameLine name = "\\n │ " <> Name.toBuilder name
301 bottomLine = "\\n └─────┘"
303 mconcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
310generateKernel :: Mode.Mode -> [K.Chunk] -> B.Builder
311generateKernel mode chunks =
312 List.foldr (addChunk mode) mempty chunks
315addChunk :: Mode.Mode -> K.Chunk -> B.Builder -> B.Builder
316addChunk mode chunk builder =
319 B.byteString javascript <> builder
321 K.ElmVar home name ->
322 JsName.toBuilder (JsName.fromGlobal home name) <> builder
325 JsName.toBuilder (JsName.fromKernel home name) <> builder
328 JsName.toBuilder (Expr.generateField mode name) <> builder
331 JsName.toBuilder (JsName.fromInt int) <> builder
334 B.intDec int <> builder
357generateEnum :: Mode.Mode -> Opt.Global -> Index.ZeroBased -> JS.Stmt
358generateEnum mode global@(Opt.Global home name) index =
359 JS.Var (JsName.fromGlobal home name) $
362 Expr.codeToExpr (Expr.generateCtor mode global index 0)
365 JS.Int (Index.toMachine index)
372generateBox :: Mode.Mode -> Opt.Global -> JS.Stmt
373generateBox mode global@(Opt.Global home name) =
374 JS.Var (JsName.fromGlobal home name) $
377 Expr.codeToExpr (Expr.generateCtor mode global Index.first 1)
380 JS.Ref (JsName.fromGlobal ModuleName.basics Name.identity)
383{-# NOINLINE identity #-}
384identity :: Opt.Global
386 Opt.Global ModuleName.basics Name.identity
393generatePort :: Mode.Mode -> Opt.Global -> Name.Name -> Opt.Expr -> JS.Stmt
394generatePort mode (Opt.Global home name) makePort converter =
395 JS.Var (JsName.fromGlobal home name) $
396 JS.Call (JS.Ref (JsName.fromKernel Name.platform makePort))
397 [ JS.String (Name.toBuilder name)
398 , Expr.codeToExpr (Expr.generate mode converter)
406generateManager :: Mode.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
407generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
411 (JS.Ref (JsName.fromKernel Name.platform "effectManagers"))
412 (JS.String (Name.toBuilder moduleName))
414 (deps, args, stmts) =
415 generateManagerHelp home effectsType
418 JS.ExprStmt $ JS.Assign managerLVar $
419 JS.Call (JS.Ref (JsName.fromKernel Name.platform "createManager")) args
421 addStmt (List.foldl' (addGlobal mode graph) state deps) $
422 JS.Block (createManager : stmts)
425generateLeaf :: ModuleName.Canonical -> Name.Name -> JS.Stmt
426generateLeaf home@(ModuleName.Canonical _ moduleName) name =
427 JS.Var (JsName.fromGlobal home name) $
428 JS.Call leaf [ JS.String (Name.toBuilder moduleName) ]
435 JS.Ref (JsName.fromKernel Name.platform "leaf")
438generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
439generateManagerHelp home effectsType =
441 dep name = Opt.Global home name
442 ref name = JS.Ref (JsName.fromGlobal home name)
446 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
447 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
448 , [ generateLeaf home "command" ]
452 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
453 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
454 , [ generateLeaf home "subscription" ]
458 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
459 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
460 , [ generateLeaf home "command"
461 , generateLeaf home "subscription"
470toMainExports :: Mode.Mode -> Mains -> B.Builder
471toMainExports mode mains =
473 export = JsName.fromKernel Name.platform "export"
474 exports = generateExports mode (Map.foldrWithKey addToTrie emptyTrie mains)
476 JsName.toBuilder export <> "(" <> exports <> ");"
479generateExports :: Mode.Mode -> Trie -> B.Builder
480generateExports mode (Trie maybeMain subs) =
489 <> JS.exprToBuilder (Expr.generateMain mode home main)
492 case Map.toList subs of
496 (name, subTrie) : otherSubTries ->
498 "'" <> Utf8.toBuilder name <> "':"
499 <> generateExports mode subTrie
500 <> List.foldl' (addSubTrie mode) "}" otherSubTries
503addSubTrie :: Mode.Mode -> B.Builder -> (Name.Name, Trie) -> B.Builder
504addSubTrie mode end (name, trie) =
505 ",'" <> Utf8.toBuilder name <> "':" <> generateExports mode trie <> end
514 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
515 , _subs :: Map.Map Name.Name Trie
521 Trie Nothing Map.empty
524addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
525addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
526 merge trie $ segmentsToTrie home (Name.splitDots moduleName) main
529segmentsToTrie :: ModuleName.Canonical -> [Name.Name] -> Opt.Main -> Trie
530segmentsToTrie home segments main =
533 Trie (Just (home, main)) Map.empty
535 segment : otherSegments ->
536 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
539merge :: Trie -> Trie -> Trie
540merge (Trie main1 subs1) (Trie main2 subs2) =
542 (checkedMerge main1 main2)
543 (Map.unionWith merge subs1 subs2)
546checkedMerge :: Maybe a -> Maybe a -> Maybe a
556 error "cannot have two modules with the same name"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list