1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript
10import Prelude hiding (cycle, print)
11import qualified Data.ByteString.Builder as B
12import Data.Monoid ((<>))
13import qualified Data.List as List
15import qualified Data.Map as Map
16import qualified Data.Set as Set
17import qualified Data.Text as Text
18import qualified Data.Text.Encoding as Text
20import qualified AST.Canonical as Can
21import qualified AST.Optimized as Opt
22import qualified AST.Module.Name as ModuleName
23import qualified Data.Index as Index
24import qualified Elm.Compiler.Type as Type
25import qualified Elm.Compiler.Type.Extract as Extract
26import qualified Elm.Interface as I
27import qualified Elm.Name as N
28import qualified Generate.JavaScript.Builder as JS
29import qualified Generate.JavaScript.Expression as Expr
30import qualified Generate.JavaScript.Name as Name
31import qualified Generate.JavaScript.Mode as Mode
32import qualified Reporting.Doc as D
41 | Some N.Name [N.Name] B.Builder
44generate :: Mode.Mode -> Opt.Graph -> [ModuleName.Canonical] -> Output
45generate mode (Opt.Graph mains graph _fields) roots =
47 rootSet = Set.fromList roots
48 rootMap = Map.restrictKeys mains rootSet
50 case map ModuleName._module (Map.keys rootMap) of
56 state = Map.foldrWithKey (addMain mode graph) emptyState rootMap
57 builder = stateToBuilder state <> toMainExports mode rootMap
59 Some name names builder
62addMain :: Mode.Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
63addMain mode graph home _ state =
64 addGlobal mode graph state (Opt.Global home "main")
71generateForRepl :: Opt.Graph -> I.Interface -> ModuleName.Canonical -> N.Name -> B.Builder
72generateForRepl (Opt.Graph _ graph _) iface home name =
74 mode = Mode.dev Mode.Server
75 debugState = addGlobal mode graph emptyState (Opt.Global ModuleName.debug "toString")
76 evalState = addGlobal mode graph debugState (Opt.Global home name)
78 stateToBuilder evalState <> print home name (I._types iface ! name)
81print :: ModuleName.Canonical -> N.Name -> Can.Annotation -> B.Builder
82print home name annotation =
84 value = Name.toBuilder (Name.fromGlobal home name)
85 toString = Name.toBuilder (Name.fromKernel N.debug "toString")
86 tipe = Type.toDoc Type.None (Extract.fromAnnotation annotation)
88 "var _value = " <> toString <> "(" <> value <> ");\n" <>
89 "var _type = " <> B.stringUtf8 (show (D.toString tipe)) <> ";\n\
90 \if (_value.length + 3 + _type.length >= 80 || _type.indexOf('\\n') >= 0) {\n\
91 \ console.log(_value + '\\n : ' + _type.split('\\n').join('\\n '));\n\
93 \ console.log(_value + ' : ' + _type);\n\
98-- GRAPH TRAVERSAL STATE
103 { _revBuilders :: [B.Builder]
104 , _seenGlobals :: Set.Set Opt.Global
113stateToBuilder :: State -> B.Builder
114stateToBuilder (State revBuilders _) =
115 List.foldl1' (\builder b -> b <> builder) revBuilders
122type Graph = Map.Map Opt.Global Opt.Node
125addGlobal :: Mode.Mode -> Graph -> State -> Opt.Global -> State
126addGlobal mode graph state@(State builders seen) global =
127 if Set.member global seen then
130 addGlobalHelp mode graph global $
131 State builders (Set.insert global seen)
134addGlobalHelp :: Mode.Mode -> Graph -> Opt.Global -> State -> State
135addGlobalHelp mode graph global state =
137 addDeps deps someState =
138 Set.foldl' (addGlobal mode graph) someState deps
140 case graph ! global of
141 Opt.Define expr deps ->
142 addStmt (addDeps deps state) (
143 var global (Expr.generate mode expr)
146 Opt.DefineTailFunc argNames body deps ->
147 addStmt (addDeps deps state) (
148 let (Opt.Global _ name) = global in
149 var global (Expr.generateTailDef mode name argNames body)
152 Opt.Ctor name index arity ->
154 var global (Expr.generateCtor mode name index arity)
157 Opt.Link linkedGlobal ->
158 addGlobal mode graph state linkedGlobal
160 Opt.Cycle cycle deps ->
161 addStmt (addDeps deps state) (
162 generateCycle mode global cycle
165 Opt.Manager effectsType ->
166 generateManager mode graph global effectsType state
168 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
169 if isDebugger global && not (Mode.isDebug mode) then
173 Just (Opt.KContent serverChunks serverDeps) | Mode.isServer mode ->
174 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
177 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
179 Opt.Enum name index ->
181 generateEnum mode global name index
186 generateBox mode global name
189 Opt.PortIncoming decoder deps ->
190 addStmt (addDeps deps state) (
191 generatePort mode global "incomingPort" decoder
194 Opt.PortOutgoing encoder deps ->
195 addStmt (addDeps deps state) (
196 generatePort mode global "outgoingPort" encoder
200addStmt :: State -> JS.Stmt -> State
202 addBuilder state (JS.stmtToBuilder stmt)
205addBuilder :: State -> B.Builder -> State
206addBuilder (State revBuilders seen) builder =
207 State (builder:revBuilders) seen
210var :: Opt.Global -> Expr.Code -> JS.Stmt
211var (Opt.Global home name) code =
212 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
215isDebugger :: Opt.Global -> Bool
216isDebugger (Opt.Global (ModuleName.Canonical _ home) _) =
224generateCycle :: Mode.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
225generateCycle mode (Opt.Global home _) cycle =
227 safeDefs = map (generateSafeCycle mode home) cycle
228 realDefs = map (generateRealCycle home) cycle
229 block = JS.Block (safeDefs ++ realDefs)
236 JS.Try block Name.dollar $ JS.Throw $ JS.String $
237 "The following top-level definitions are causing infinite recursion:\\n"
238 <> drawCycle (map fst cycle)
239 <> "\\n\\nThese errors are very tricky, so read "
240 <> B.stringUtf8 (D.makeLink "halting-problem")
241 <> " to learn how to fix it!"
244generateSafeCycle :: Mode.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
245generateSafeCycle mode home (name, expr) =
246 JS.FunctionStmt (Name.fromCycle home name) [] $
247 Expr.codeToStmtList (Expr.generate mode expr)
250generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
251generateRealCycle home (name, _) =
253 safeName = Name.fromCycle home name
254 realName = Name.fromGlobal home name
257 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
258 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
259 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
263drawCycle :: [N.Name] -> B.Builder
266 topLine = "\\n ┌─────┐"
267 nameLine name = "\\n │ " <> N.toBuilder name
269 bottomLine = "\\n └─────┘"
271 mconcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
279generateKernel :: Mode.Mode -> [Opt.KChunk] -> B.Builder
280generateKernel mode chunks =
281 List.foldl' (addChunk mode) mempty chunks
284addChunk :: Mode.Mode -> B.Builder -> Opt.KChunk -> B.Builder
285addChunk mode builder chunk =
288 B.byteString javascript <> builder
290 Opt.ElmVar home name ->
291 Name.toBuilder (Name.fromGlobal home name) <> builder
293 Opt.JsVar home name ->
294 Name.toBuilder (Name.fromKernel home name) <> builder
297 Name.toBuilder (Expr.generateField mode name) <> builder
300 Name.toBuilder (Name.fromInt int) <> builder
303 B.intDec int <> builder
326generateEnum :: Mode.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
327generateEnum mode (Opt.Global home name) ctorName index =
332 Expr.codeToExpr (Expr.generateCtor mode ctorName index 0)
335 JS.Int (Index.toMachine index)
337 JS.Var [ (Name.fromGlobal home name, Just definition) ]
344generateBox :: Mode.Mode -> Opt.Global -> N.Name -> JS.Stmt
345generateBox mode (Opt.Global home name) ctorName =
350 Expr.codeToExpr (Expr.generateCtor mode ctorName Index.first 1)
353 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
355 JS.Var [ (Name.fromGlobal home name, Just definition) ]
362generatePort :: Mode.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
363generatePort mode (Opt.Global home name) makePort converter =
366 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
367 [ JS.String (N.toBuilder name)
368 , Expr.codeToExpr (Expr.generate mode converter)
371 JS.Var [ (Name.fromGlobal home name, Just definition) ]
378generateManager :: Mode.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
379generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
383 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
384 (JS.String (N.toBuilder moduleName))
386 (deps, args, stmts) =
387 generateManagerHelp home effectsType
390 JS.ExprStmt $ JS.Assign managerLVar $
391 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
393 addStmt (List.foldl' (addGlobal mode graph) state deps) $
394 JS.Block (createManager : stmts)
397generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
398generateLeaf home@(ModuleName.Canonical _ moduleName) name =
401 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
403 JS.Var [ (Name.fromGlobal home name, Just definition) ]
409 JS.Ref (Name.fromKernel N.platform "leaf")
412generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
413generateManagerHelp home effectsType =
415 dep name = Opt.Global home name
416 ref name = JS.Ref (Name.fromGlobal home name)
420 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
421 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
422 , [ generateLeaf home "command" ]
426 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
427 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
428 , [ generateLeaf home "subscription" ]
432 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
433 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
434 , [ generateLeaf home "command"
435 , generateLeaf home "subscription"
444toMainExports :: Mode.Mode -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
445toMainExports mode mains =
447 export = Name.fromKernel N.platform "export"
448 exports = generateExports mode (Map.foldrWithKey addToTrie emptyTrie mains)
450 Name.toBuilder export <> "(" <> exports <> ");"
453generateExports :: Mode.Mode -> Trie -> B.Builder
454generateExports mode (Trie maybeMain subs) =
457 case Map.toList subs of
461 (name, subTrie) : otherSubTries ->
462 "{'" <> Text.encodeUtf8Builder name <> "':"
463 <> generateExports mode subTrie
464 <> List.foldl' (addSubTrie mode) "}" otherSubTries
471 let initialize = Expr.generateMain mode home main in
472 JS.exprToBuilder initialize <> "(" <> object <> ")"
475addSubTrie :: Mode.Mode -> B.Builder -> (Text.Text, Trie) -> B.Builder
476addSubTrie mode end (name, trie) =
477 ",'" <> Text.encodeUtf8Builder name <> "':" <> generateExports mode trie <> end
486 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
487 , _subs :: Map.Map Text.Text Trie
493 Trie Nothing Map.empty
496addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
497addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
498 merge trie $ segmentsToTrie home (Text.splitOn "." (N.toText moduleName)) main
501segmentsToTrie :: ModuleName.Canonical -> [Text.Text] -> Opt.Main -> Trie
502segmentsToTrie home segments main =
505 Trie (Just (home, main)) Map.empty
507 segment : otherSegments ->
508 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
511merge :: Trie -> Trie -> Trie
512merge (Trie main1 subs1) (Trie main2 subs2) =
514 (checkedMerge main1 main2)
515 (Map.unionWith merge subs1 subs2)
518checkedMerge :: Maybe a -> Maybe a -> Maybe a
528 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