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 ->
170 Just (Opt.KContent serverChunks serverDeps) | Mode.isServer mode ->
171 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
174 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
176 Opt.Enum name index ->
178 generateEnum mode global name index
183 generateBox mode global name
186 Opt.PortIncoming decoder deps ->
187 addStmt (addDeps deps state) (
188 generatePort mode global "incomingPort" decoder
191 Opt.PortOutgoing encoder deps ->
192 addStmt (addDeps deps state) (
193 generatePort mode global "outgoingPort" encoder
197addStmt :: State -> JS.Stmt -> State
199 addBuilder state (JS.stmtToBuilder stmt)
202addBuilder :: State -> B.Builder -> State
203addBuilder (State revBuilders seen) builder =
204 State (builder:revBuilders) seen
207var :: Opt.Global -> Expr.Code -> JS.Stmt
208var (Opt.Global home name) code =
209 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
216generateCycle :: Mode.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
217generateCycle mode (Opt.Global home _) cycle =
219 safeDefs = map (generateSafeCycle mode home) cycle
220 realDefs = map (generateRealCycle home) cycle
221 block = JS.Block (safeDefs ++ realDefs)
228 JS.Try block Name.dollar $ JS.Throw $ JS.String $
229 "The following top-level definitions are causing infinite recursion:\\n"
230 <> drawCycle (map fst cycle)
231 <> "\\n\\nThese errors are very tricky, so read "
232 <> B.stringUtf8 (D.makeLink "halting-problem")
233 <> " to learn how to fix it!"
236generateSafeCycle :: Mode.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
237generateSafeCycle mode home (name, expr) =
238 JS.FunctionStmt (Name.fromCycle home name) [] $
239 Expr.codeToStmtList (Expr.generate mode expr)
242generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
243generateRealCycle home (name, _) =
245 safeName = Name.fromCycle home name
246 realName = Name.fromGlobal home name
249 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
250 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
251 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
255drawCycle :: [N.Name] -> B.Builder
258 topLine = "\\n ┌─────┐"
259 nameLine name = "\\n │ " <> N.toBuilder name
261 bottomLine = "\\n └─────┘"
263 mconcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
271generateKernel :: Mode.Mode -> [Opt.KChunk] -> B.Builder
272generateKernel mode chunks =
273 List.foldl' (addChunk mode) mempty chunks
276addChunk :: Mode.Mode -> B.Builder -> Opt.KChunk -> B.Builder
277addChunk mode builder chunk =
280 B.byteString javascript <> builder
282 Opt.ElmVar home name ->
283 Name.toBuilder (Name.fromGlobal home name) <> builder
285 Opt.JsVar home name ->
286 Name.toBuilder (Name.fromKernel home name) <> builder
289 Name.toBuilder (Expr.generateField mode name) <> builder
292 Name.toBuilder (Name.fromInt int) <> builder
295 B.intDec int <> builder
318generateEnum :: Mode.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
319generateEnum mode (Opt.Global home name) ctorName index =
324 Expr.codeToExpr (Expr.generateCtor mode ctorName index 0)
327 JS.Int (Index.toMachine index)
329 JS.Var [ (Name.fromGlobal home name, Just definition) ]
336generateBox :: Mode.Mode -> Opt.Global -> N.Name -> JS.Stmt
337generateBox mode (Opt.Global home name) ctorName =
342 Expr.codeToExpr (Expr.generateCtor mode ctorName Index.first 1)
345 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
347 JS.Var [ (Name.fromGlobal home name, Just definition) ]
354generatePort :: Mode.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
355generatePort mode (Opt.Global home name) makePort converter =
358 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
359 [ JS.String (N.toBuilder name)
360 , Expr.codeToExpr (Expr.generate mode converter)
363 JS.Var [ (Name.fromGlobal home name, Just definition) ]
370generateManager :: Mode.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
371generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
375 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
376 (JS.String (N.toBuilder moduleName))
378 (deps, args, stmts) =
379 generateManagerHelp home effectsType
382 JS.ExprStmt $ JS.Assign managerLVar $
383 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
385 addStmt (List.foldl' (addGlobal mode graph) state deps) $
386 JS.Block (createManager : stmts)
389generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
390generateLeaf home@(ModuleName.Canonical _ moduleName) name =
393 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
395 JS.Var [ (Name.fromGlobal home name, Just definition) ]
401 JS.Ref (Name.fromKernel N.platform "leaf")
404generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
405generateManagerHelp home effectsType =
407 dep name = Opt.Global home name
408 ref name = JS.Ref (Name.fromGlobal home name)
412 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
413 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
414 , [ generateLeaf home "command" ]
418 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
419 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
420 , [ generateLeaf home "subscription" ]
424 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
425 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
426 , [ generateLeaf home "command"
427 , generateLeaf home "subscription"
436toMainExports :: Mode.Mode -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
437toMainExports mode mains =
439 export = Name.fromKernel N.platform "export"
440 exports = generateExports mode (Map.foldrWithKey addToTrie emptyTrie mains)
442 Name.toBuilder export <> "(" <> exports <> ");"
445generateExports :: Mode.Mode -> Trie -> B.Builder
446generateExports mode (Trie maybeMain subs) =
449 case Map.toList subs of
453 (name, subTrie) : otherSubTries ->
454 "{'" <> Text.encodeUtf8Builder name <> "':"
455 <> generateExports mode subTrie
456 <> List.foldl' (addSubTrie mode) "}" otherSubTries
463 let initialize = Expr.generateMain mode home main in
464 JS.exprToBuilder initialize <> "(" <> object <> ")"
467addSubTrie :: Mode.Mode -> B.Builder -> (Text.Text, Trie) -> B.Builder
468addSubTrie mode end (name, trie) =
469 ",'" <> Text.encodeUtf8Builder name <> "':" <> generateExports mode trie <> end
478 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
479 , _subs :: Map.Map Text.Text Trie
485 Trie Nothing Map.empty
488addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
489addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
490 merge trie $ segmentsToTrie home (Text.splitOn "." (N.toText moduleName)) main
493segmentsToTrie :: ModuleName.Canonical -> [Text.Text] -> Opt.Main -> Trie
494segmentsToTrie home segments main =
497 Trie (Just (home, main)) Map.empty
499 segment : otherSegments ->
500 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
503merge :: Trie -> Trie -> Trie
504merge (Trie main1 subs1) (Trie main2 subs2) =
506 (checkedMerge main1 main2)
507 (Map.unionWith merge subs1 subs2)
510checkedMerge :: Maybe a -> Maybe a -> Maybe a
520 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