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.Interface as I
25import qualified Elm.Name as N
26import qualified Generate.JavaScript.Builder as JS
27import qualified Generate.JavaScript.Expression as Expr
28import qualified Generate.JavaScript.Name as Name
29import qualified Generate.JavaScript.Mode as Mode
30import qualified Reporting.Doc as D
31import qualified Reporting.Render.Type as RT
32import qualified Reporting.Render.Type.Localizer as L
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 :: Bool -> L.Localizer -> Opt.Graph -> I.Interface -> ModuleName.Canonical -> N.Name -> B.Builder
72generateForRepl ansi localizer (Opt.Graph _ graph _) iface home name =
74 mode = Mode.dev Mode.Client
75 debugState = addGlobal mode graph emptyState (Opt.Global ModuleName.debug "toString")
76 evalState = addGlobal mode graph debugState (Opt.Global home name)
78 stateToBuilder evalState
80 print ansi localizer home name (I._types iface ! name)
83print :: Bool -> L.Localizer -> ModuleName.Canonical -> N.Name -> Can.Annotation -> B.Builder
84print ansi localizer home name (Can.Forall _ tipe) =
86 value = Name.toBuilder (Name.fromGlobal home name)
87 toString = Name.toBuilder (Name.fromKernel N.debug "toAnsiString")
88 tipeDoc = RT.canToDoc localizer RT.None tipe
89 bool = if ansi then "true" else "false"
91 "var _value = " <> toString <> "(" <> bool <> ", " <> value <> ");\n\
92 \var _type = " <> B.stringUtf8 (show (D.toString tipeDoc)) <> ";\n\
93 \function _print(t) { console.log(_value + (" <> bool <> " ? '\x1b[90m' + t + '\x1b[0m' : t)); }\n\
94 \if (_value.length + 3 + _type.length >= 80 || _type.indexOf('\\n') >= 0) {\n\
95 \ _print('\\n : ' + _type.split('\\n').join('\\n '));\n\
97 \ _print(' : ' + _type);\n\
102-- GRAPH TRAVERSAL STATE
107 { _kernel :: B.Builder
108 , _revBuilders :: [B.Builder]
109 , _seenGlobals :: Set.Set Opt.Global
115 State mempty [] Set.empty
118stateToBuilder :: State -> B.Builder
119stateToBuilder (State kernel revBuilders _) =
125 kernel <> List.foldl' (\code decl -> decl <> code) b bs
132type Graph = Map.Map Opt.Global Opt.Node
135addGlobal :: Mode.Mode -> Graph -> State -> Opt.Global -> State
136addGlobal mode graph state@(State kernel builders seen) global =
137 if Set.member global seen then
140 addGlobalHelp mode graph global $
141 State kernel builders (Set.insert global seen)
144addGlobalHelp :: Mode.Mode -> Graph -> Opt.Global -> State -> State
145addGlobalHelp mode graph global state =
147 addDeps deps someState =
148 Set.foldl' (addGlobal mode graph) someState deps
150 case graph ! global of
151 Opt.Define expr deps ->
152 addStmt (addDeps deps state) (
153 var global (Expr.generate mode expr)
156 Opt.DefineTailFunc argNames body deps ->
157 addStmt (addDeps deps state) (
158 let (Opt.Global _ name) = global in
159 var global (Expr.generateTailDef mode name argNames body)
162 Opt.Ctor index arity ->
164 var global (Expr.generateCtor mode global index arity)
167 Opt.Link linkedGlobal ->
168 addGlobal mode graph state linkedGlobal
170 Opt.Cycle cycle deps ->
171 addStmt (addDeps deps state) (
172 generateCycle mode global cycle
175 Opt.Manager effectsType ->
176 generateManager mode graph global effectsType state
178 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
179 if isDebugger global && not (Mode.isDebug mode) then
183 Just (Opt.KContent serverChunks serverDeps) | Mode.isServer mode ->
184 addKernel (addDeps serverDeps state) (generateKernel mode serverChunks)
187 addKernel (addDeps clientDeps state) (generateKernel mode clientChunks)
191 generateEnum mode global index
196 generateBox mode global
199 Opt.PortIncoming decoder deps ->
200 addStmt (addDeps deps state) (
201 generatePort mode global "incomingPort" decoder
204 Opt.PortOutgoing encoder deps ->
205 addStmt (addDeps deps state) (
206 generatePort mode global "outgoingPort" encoder
210addStmt :: State -> JS.Stmt -> State
212 addBuilder state (JS.stmtToBuilder stmt)
215addBuilder :: State -> B.Builder -> State
216addBuilder (State kernel revBuilders seen) builder =
217 State kernel (builder:revBuilders) seen
220addKernel :: State -> B.Builder -> State
221addKernel (State kernel revBuilders seen) moreKernel =
222 State (moreKernel <> kernel) revBuilders seen
225var :: Opt.Global -> Expr.Code -> JS.Stmt
226var (Opt.Global home name) code =
227 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
230isDebugger :: Opt.Global -> Bool
231isDebugger (Opt.Global (ModuleName.Canonical _ home) _) =
239generateCycle :: Mode.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
240generateCycle mode (Opt.Global home _) cycle =
242 safeDefs = map (generateSafeCycle mode home) cycle
243 realDefs = map (generateRealCycle home) cycle
245 JS.Block (safeDefs ++ realDefs)
248generateSafeCycle :: Mode.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
249generateSafeCycle mode home (name, expr) =
250 JS.FunctionStmt (Name.fromCycle home name) [] $
251 Expr.codeToStmtList (Expr.generate mode expr)
254generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
255generateRealCycle home (name, _) =
257 safeName = Name.fromCycle home name
258 realName = Name.fromGlobal home name
261 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
262 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
263 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
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 -> Index.ZeroBased -> JS.Stmt
319generateEnum mode global@(Opt.Global home name) index =
324 Expr.codeToExpr (Expr.generateCtor mode global index 0)
327 JS.Int (Index.toMachine index)
329 JS.Var [ (Name.fromGlobal home name, Just definition) ]
336generateBox :: Mode.Mode -> Opt.Global -> JS.Stmt
337generateBox mode global@(Opt.Global home name) =
342 Expr.codeToExpr (Expr.generateCtor mode global 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