1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Generate.JavaScript
11import Prelude hiding (cycle)
12import qualified Data.ByteString.Builder as B
13import Data.Monoid ((<>))
14import qualified Data.List as List
16import qualified Data.Map as Map
17import qualified Data.Set as Set
19import qualified AST.Optimized as Opt
20import qualified AST.Module.Name as ModuleName
21import qualified Elm.Name as N
22import qualified Generate.JavaScript.Builder as JS
23import qualified Generate.JavaScript.Expression as Expr
24import qualified Generate.JavaScript.Name as Name
31data Mode = Debug | Prod
35 = Mains [ModuleName.Canonical]
36 | Value ModuleName.Canonical N.Name
39generate :: Mode -> Name.Target -> Opt.Graph -> Roots -> Either [ModuleName.Canonical] B.Builder
40generate mode target (Opt.Graph mains nodes fields) roots =
43 Right $ stateToBuilder $
44 addGlobal (toRealMode mode target fields) nodes emptyState (Opt.Global home name)
47 generateMain (toRealMode mode target fields) nodes
48 <$> verifyMains mains modules
51toRealMode :: Mode -> Name.Target -> Map.Map N.Name Int -> Name.Mode
52toRealMode mode target fields =
58 Name.Prod target (Name.shortenFieldNames fields)
62-- GRAPH TRAVERSAL STATE
67 { _revBuilders :: [B.Builder]
68 , _seenGlobals :: Set.Set Opt.Global
77stateToBuilder :: State -> B.Builder
78stateToBuilder (State revBuilders _) =
79 List.foldl1' (\builder b -> b <> builder) revBuilders
86type Graph = Map.Map Opt.Global Opt.Node
89addGlobal :: Name.Mode -> Graph -> State -> Opt.Global -> State
90addGlobal mode graph state@(State builders seen) global =
91 if Set.member global seen then
94 addGlobalHelp mode graph global $
95 State builders (Set.insert global seen)
98addGlobalHelp :: Name.Mode -> Graph -> Opt.Global -> State -> State
99addGlobalHelp mode graph global state =
101 addDeps deps someState =
102 Set.foldl' (addGlobal mode graph) someState deps
104 case graph ! global of
105 Opt.Define expr deps ->
106 addStmt (addDeps deps state) (
107 var global (Expr.generate mode expr)
110 Opt.DefineTailFunc argNames body deps ->
111 addStmt (addDeps deps state) (
112 var global (Expr.generateFunction (map Name.fromLocal argNames) (Expr.generate mode body))
115 Opt.Ctor name index arity ->
116 let (args, ctor) = Expr.generateCtor mode name index arity in
118 var global (Expr.generateFunction args ctor)
121 Opt.Link linkedGlobal ->
122 addGlobal mode graph state linkedGlobal
124 Opt.Cycle cycle deps ->
125 addStmt (addDeps deps state) (
126 generateCycle mode global cycle
129 Opt.Manager effectsType ->
130 generateManager mode graph global effectsType state
132 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
134 Just (Opt.KContent serverChunks serverDeps) | Name.isServer mode ->
135 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
138 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
140 Opt.PortIncoming decoder deps ->
141 addStmt (addDeps deps state) (
142 generatePort mode global "incomingPort" decoder
145 Opt.PortOutgoing encoder deps ->
146 addStmt (addDeps deps state) (
147 generatePort mode global "outgoingPort" encoder
151addStmt :: State -> JS.Stmt -> State
153 addBuilder state (JS.stmtToBuilder stmt)
156addBuilder :: State -> B.Builder -> State
157addBuilder (State revBuilders seen) builder =
158 State (builder:revBuilders) seen
161var :: Opt.Global -> Expr.Code -> JS.Stmt
162var (Opt.Global home name) code =
163 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
170generateCycle :: Name.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
171generateCycle mode (Opt.Global home _) cycle =
173 safeDefs = map (generateSafeCycle mode home) cycle
174 realDefs = map (generateRealCycle home) cycle
175 -- TODO add `try` in debug mode nice infinite recursion reports
177 JS.Block (safeDefs ++ realDefs)
180generateSafeCycle :: Name.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
181generateSafeCycle mode home (name, expr) =
182 JS.FunctionStmt (Name.fromCycle home name) [] $
183 Expr.codeToStmtList (Expr.generate mode expr)
186generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
187generateRealCycle home (name, _) =
189 safeName = Name.fromCycle home name
190 realName = Name.fromGlobal home name
193 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
194 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
195 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
203generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
204generateKernel mode chunks =
205 List.foldl' (addChunk mode) mempty chunks
208addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
209addChunk mode builder chunk =
212 B.byteString javascript <> builder
214 Opt.ElmVar home name ->
215 Name.toBuilder (Name.fromGlobal home name) <> builder
217 Opt.JsVar home name ->
218 Name.toBuilder (Name.fromKernel home name) <> builder
221 Name.toBuilder (Name.fromField mode name) <> builder
224 Name.toBuilder (Name.fromInt int) <> builder
227 Name.toBuilder (Name.fromInt int) <> builder
250generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
251generatePort mode (Opt.Global home name) makePort converter =
254 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
255 [ JS.String (N.toBuilder name)
256 , Expr.codeToExpr (Expr.generate mode converter)
259 JS.Var [ (Name.fromGlobal home name, Just definition) ]
267generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
268generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
272 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
273 (JS.String (N.toBuilder moduleName))
275 (deps, args, stmts) =
276 generateManagerHelp home effectsType
279 JS.ExprStmt $ JS.Assign managerLVar $
280 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
282 addStmt (List.foldl' (addGlobal mode graph) state deps) $
283 JS.Block (createManager : stmts)
286generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
287generateLeaf home@(ModuleName.Canonical _ moduleName) name =
290 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
292 JS.Var [ (Name.fromGlobal home name, Just definition) ]
298 JS.Ref (Name.fromKernel N.platform "leaf")
301generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
302generateManagerHelp home effectsType =
304 dep name = Opt.Global home name
305 ref name = JS.Ref (Name.fromGlobal home name)
309 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
310 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
311 , [ generateLeaf home "command" ]
315 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
316 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
317 , [ generateLeaf home "subscription" ]
321 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
322 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
323 , [ generateLeaf home "command"
324 , generateLeaf home "subscription"
334 Map.Map ModuleName.Canonical Opt.Main
337generateMain :: Name.Mode -> Graph -> Mains -> B.Builder
338generateMain mode graph mains =
340 [ stateToBuilder $ Map.foldrWithKey (addMain mode graph) emptyState mains
345addMain :: Name.Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
346addMain mode graph home _ state =
347 addGlobal mode graph state (Opt.Global home "main")
350exportMains :: Mains -> B.Builder
355verifyMains :: Mains -> [ModuleName.Canonical] -> Either [ModuleName.Canonical] Mains
356verifyMains mains modules =
358 rootSet = Set.fromList modules
359 rootMap = Map.restrictKeys mains rootSet
361 if Map.size rootMap == Set.size rootSet then
365 Set.intersection rootSet (Map.keysSet mains)
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