@@ -2,217 +2,357 @@
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Generate.JavaScript
3module Generate.JavaScript
5− , Target(..)
5+ , Roots(..)
9−import Control.Monad (foldM)
9+
10+import Prelude hiding (cycle)
11+import qualified Data.ByteString.Builder as B
10import Data.Monoid ((<>))
12import Data.Monoid ((<>))
11import qualified Data.List as List
13import qualified Data.List as List
12import qualified Data.Map as Map
15import qualified Data.Map as Map
13import qualified Data.Set as Set
16import qualified Data.Set as Set
14−import qualified Data.Text as Text
15−import qualified Data.Text.Encoding as Text
16−import qualified Data.ByteString.Builder as BS
18−import qualified AST.Effects as Effects
18+import qualified AST.Optimized as Opt
19−import qualified AST.Expression.Optimized as Opt
20−import qualified AST.Kernel as Kernel
21import qualified AST.Module.Name as ModuleName
19import qualified AST.Module.Name as ModuleName
22−import qualified AST.Variable as Var
20+import qualified Elm.Name as N
23import qualified Elm.Package as Pkg
21import qualified Elm.Package as Pkg
24−import qualified Elm.Compiler.Objects.Internal as Obj
25−import qualified Generate.JavaScript.Builder as JsBuilder
22+import qualified Generate.JavaScript.Builder as JS
26−import qualified Generate.JavaScript.Effects as JsEffects
27−import qualified Generate.JavaScript.Expression as JsExpr
23+import qualified Generate.JavaScript.Expression as Expr
28−import qualified Generate.JavaScript.Variable as JS
24+import qualified Generate.JavaScript.Name as Name
32−-- GENERATE JAVASCRIPT
28+-- GENERATE
35−generate :: Bool -> Target -> Obj.Graph -> Obj.Roots -> BS.Builder
31+data Roots
36−generate debug target graph roots =
32+ = Mains [ModuleName.Canonical]
37− let
33+ | Value ModuleName.Canonical N.Name
38− (State builders _ _ effects) =
39− List.foldl' (crawl (Env debug target) graph) initialState (Obj.toGlobals roots)
42− JS.run (JsEffects.generate effects)
44− List.foldl' (\rest stmt -> stmt <> rest) managers builders
36+generate :: Bool -> Name.Target -> Opt.Graph -> Roots -> Either [ModuleName.Canonical] B.Builder
37+generate debug target (Opt.Graph mains nodes fields) roots =
40+ Right $ stateToBuilder $
41+ addGlobal (toMode debug target fields) nodes emptyState (Opt.Global home name)
44+ generateMain (toMode debug target fields) nodes
45+ <$> verifyMains mains modules
50−
48+toMode :: Bool -> Name.Target -> Map.Map N.Name Int -> Name.Mode
51−data Env =
49+toMode debug target fields =
53− { _debug :: Bool
51+ Name.Debug target
54− , _target :: Target
52+ else
55− }
53+ Name.Prod target (Name.shortenFieldNames fields)
58−data Target = Client | Server
62−-- CRAWL STATE
57+-- GRAPH TRAVERSAL STATE
65data State =
60data State =
67− { _builders :: [BS.Builder]
62+ { _revBuilders :: [B.Builder]
68− , _seenDecls :: Set.Set Var.Global
63+ , _seenGlobals :: Set.Set Opt.Global
69− , _seenKernels :: Set.Set ModuleName.Raw
70− , _effects :: Map.Map ModuleName.Canonical Effects.ManagerType
74−initialState :: State
67+emptyState :: State
75−initialState =
68+emptyState =
76− State [] Set.empty Set.empty Map.empty
69+ State [] Set.empty
72+stateToBuilder :: State -> B.Builder
73+stateToBuilder (State revBuilders _) =
74+ List.foldl1' (\builder b -> b <> builder) revBuilders
83−crawl :: Env -> Obj.Graph -> State -> Var.Global -> State
78+-- ADD DEPENDENCIES
84−crawl env graph@(Obj.Graph decls kernels) state@(State _ seenDecls seenKernels _) name@(Var.Global home _) =
79+
85− if Set.member name seenDecls then
80+
86− state
81+type Mode = Name.Mode
82+type Graph = Map.Map Opt.Global Opt.Node
88− else if home == virtualDomDebug then
85+addGlobal :: Mode -> Graph -> State -> Opt.Global -> State
86+addGlobal mode graph state@(State builders seen) global =
87+ if Set.member global seen then
92− case Map.lookup name decls of
90+ addGlobalHelp mode graph global $
93− Just decl ->
91+ State builders (Set.insert global seen)
94− crawlDecl env graph name decl state
92+
94+addGlobalHelp :: Mode -> Graph -> Opt.Global -> State -> State
95+addGlobalHelp mode graph global state =
97+ addDeps deps someState =
98+ Set.foldl' (addGlobal mode graph) someState deps
100+ case graph ! global of
101+ Opt.Define expr deps ->
102+ addStmt (addDeps deps state) (
103+ var global (Expr.generate mode expr)
106+ Opt.DefineTailFunc argNames body deps ->
107+ addStmt (addDeps deps state) (
108+ var global (Expr.generateFunction (map Name.fromLocal argNames) (Expr.generate mode body))
96− Nothing ->
111+ Opt.Ctor name index arity ->
97− let
112+ let (args, ctor) = Expr.generateCtor mode name index arity in
99− ModuleName._module home
101− if Set.member kernelModule seenKernels then
102− state
113+ addStmt state (
103− else
114+ var global (Expr.generateFunction args ctor)
104− case Map.lookup kernelModule kernels of
115+ )
106− crawlKernel env graph kernelModule info state
108− Nothing ->
117+ Opt.Link linkedGlobal ->
109− error (crawlError name)
118+ addGlobal mode graph state linkedGlobal
120+ Opt.Cycle cycle deps ->
121+ addStmt (addDeps deps state) (
122+ generateCycle mode global cycle
125+ Opt.Manager effectsType ->
126+ generateManager mode graph global effectsType state
113−-- CRAWL DECL
128+ Opt.Kernel clientChunks clientDeps maybeServer ->
130+ Just (serverChunks, serverDeps) | Name.isServer mode ->
131+ addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
134+ addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
116−crawlDecl :: Env -> Obj.Graph -> Var.Global -> Opt.Decl -> State -> State
136+ Opt.PortIncoming decoder deps ->
117−crawlDecl env graph var@(Var.Global home name) (Opt.Decl direct indirect fx body) state1 =
137+ addStmt (addDeps deps state) (
138+ generatePort mode global "incomingPort" decoder
141+ Opt.PortOutgoing encoder deps ->
142+ addStmt (addDeps deps state) (
143+ generatePort mode global "outgoingPort" encoder
147+addStmt :: State -> JS.Stmt -> State
149+ addBuilder state (JS.stmtToBuilder stmt)
152+addBuilder :: State -> B.Builder -> State
153+addBuilder (State revBuilders seen) builder =
154+ State (builder:revBuilders) seen
157+var :: Opt.Global -> Expr.Code -> JS.Stmt
158+var (Opt.Global home name) code =
159+ JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
166+generateCycle :: Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
167+generateCycle mode (Opt.Global home _) cycle =
119− state2 =
169+ safeDefs = map (generateSafeCycle mode home) cycle
120− state1 { _seenDecls = Set.insert var (_seenDecls state1) }
170+ realDefs = map (generateRealCycle home) cycle
121−
171+ -- TODO add `try` in debug mode nice infinite recursion reports
122− (State builders seenDecls seenKernels effects) =
123− Set.foldl' (crawl env graph) state2 direct
126− JS.run (JsExpr.generateDecl home name body)
130− { _builders = JsBuilder.stmtToBuilder stmt : builders
131− , _seenDecls = seenDecls
132− , _seenKernels = seenKernels
133− , _effects = maybe id (Map.insert home) fx effects
136− Set.foldl' (crawl env graph) state4 indirect
173+ JS.Block (safeDefs ++ realDefs)
176+generateSafeCycle :: Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
177+generateSafeCycle mode home (name, expr) =
178+ JS.FunctionStmt (Name.fromCycle home name) [] $
179+ Expr.codeToStmtList (Expr.generate mode expr)
182+generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
183+generateRealCycle home (name, _) =
185+ safeName = Name.fromCycle home name
186+ realName = Name.fromGlobal home name
189+ [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
190+ , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
191+ JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
143−crawlKernel :: Env -> Obj.Graph -> ModuleName.Raw -> Kernel.Data -> State -> State
198+
144−crawlKernel env@(Env _ target) graph name (Kernel.Data client maybeServer) state =
199+generateKernel :: Mode -> [Opt.KChunk] -> B.Builder
200+generateKernel mode chunks =
201+ List.foldl' (addChunk mode) mempty chunks
204+addChunk :: Mode -> B.Builder -> Opt.KChunk -> B.Builder
205+addChunk mode builder chunk =
207+ Opt.JS javascript ->
208+ B.byteString javascript <> builder
210+ Opt.Var home name ->
211+ Name.toBuilder (Name.fromGlobal (ModuleName.Canonical Pkg.core home) name) <> builder
213+ Opt.ElmField name ->
214+ Name.toBuilder (Name.fromField mode name) <> builder
217+ Name.toBuilder (Name.fromInt int) <> builder
220+ Name.toBuilder (Name.fromInt int) <> builder
228+ "_UNUSED" <> builder
233+ "_UNUSED" <> builder
243+generatePort :: Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
244+generatePort mode (Opt.Global home name) makePort converter =
146− content =
246+ definition =
147− case target of
247+ JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
148− Server ->
248+ [ JS.String (N.toBuilder name)
149− maybe client id maybeServer
249+ , Expr.codeToExpr (Expr.generate mode converter)
252+ JS.Var [ (Name.fromGlobal home name, Just definition) ]
260+generateManager :: Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
261+generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
265+ (JS.Ref (Name.fromKernel N.platform "effectManagers"))
266+ (JS.String (N.toBuilder moduleName))
268+ (deps, args, stmts) =
269+ generateManagerHelp home effectsType
151− Client ->
271+ createManager =
152− client
272+ JS.ExprStmt $ JS.Assign managerLVar $
273+ JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
154− crawlKernelContent env graph name content state
275+ addStmt (List.foldl' (addGlobal mode graph) state deps) $
276+ JS.Block (createManager : stmts)
157−crawlKernelContent :: Env -> Obj.Graph -> ModuleName.Raw -> Kernel.Content -> State -> State
279+generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
158−crawlKernelContent env@(Env debug _) graph name (Kernel.Content imports chunks) state1 =
280+generateLeaf home@(ModuleName.Canonical _ moduleName) name =
160− state2 =
282+ definition =
161− state1 { _seenKernels = Set.insert name (_seenKernels state1) }
283+ JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
285+ JS.Var [ (Name.fromGlobal home name, Just definition) ]
163− (State builders seenDecls seenKernels effects) =
288+{-# NOINLINE leaf #-}
164− List.foldl' (crawl env graph) state2 (map toCoreGlobal imports)
289+leaf :: JS.Expr
291+ JS.Ref (Name.fromKernel N.platform "leaf")
167− JS.run (foldM (chunkToBuilder debug) mempty chunks)
294+generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
295+generateManagerHelp home effectsType =
297+ dep name = Opt.Global home name
298+ ref name = JS.Ref (Name.fromGlobal home name)
169− State
300+ case effectsType of
170− { _builders = builder : builders
301+ Opt.Cmd ->
171− , _seenDecls = seenDecls
302+ ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
172− , _seenKernels = seenKernels
303+ , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
173− , _effects = effects
304+ , [ generateLeaf home "command" ]
308+ ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
309+ , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
310+ , [ generateLeaf home "subscription" ]
177−toCoreGlobal :: ( ModuleName.Raw, Text.Text ) -> Var.Global
313+ Opt.Fx ->
178−toCoreGlobal (home, name) =
314+ ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
179− Var.Global (ModuleName.inCore home) name
315+ , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
316+ , [ generateLeaf home "command"
317+ , generateLeaf home "subscription"
182−chunkToBuilder :: Bool -> BS.Builder -> Kernel.Chunk -> JS.Generator BS.Builder
183−chunkToBuilder debug builder chunk =
185− Kernel.JS javascript ->
186− return $ BS.byteString javascript <> builder
188− Kernel.Var home name ->
323+-- ADD MAIN
189− do expr <- JS.global (Var.Global (ModuleName.inCore home) name)
190− return $ JsBuilder.exprToBuilder expr <> builder
192− Kernel.Field name ->
193− -- TODO generate a smaller field
194− return $ Text.encodeUtf8Builder name <> builder
196− Kernel.Debug ->
326+type Mains =
197− return $ if debug then builder else "_UNUSED" <> builder
327+ Map.Map ModuleName.Canonical Opt.Main
200− return $ if debug then "_UNUSED" <> builder else builder
330+generateMain :: Mode -> Graph -> Mains -> B.Builder
331+generateMain mode graph mains =
333+ [ stateToBuilder $ Map.foldrWithKey (addMain mode graph) emptyState mains
204−-- CRAWL HELPERS
338+addMain :: Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
339+addMain mode graph home _ state =
340+ addGlobal mode graph state (Opt.Global home "main")
207−crawlError :: Var.Global -> String
343+exportMains :: Mains -> B.Builder
208−crawlError (Var.Global (ModuleName.Canonical pkg home) name) =
344+exportMains mains =
209− Text.unpack $
345+ error "TODO" mains
210− "compiler bug manifesting in Generate.JavaScript\n"
211− <> "Could not find " <> Pkg.toText pkg <> " " <> ModuleName.toText home <> "." <> name <> "\n"
212− <> "Please report at <https://github.com/elm-lang/elm-compiler/issues>\n"
213− <> "Try to make an <http://sscce.org/> that demonstrates the issue!"
216−virtualDomDebug :: ModuleName.Canonical
348+verifyMains :: Mains -> [ModuleName.Canonical] -> Either [ModuleName.Canonical] Mains
217−virtualDomDebug =
349+verifyMains mains modules =
218− ModuleName.inVirtualDom "VirtualDom.Debug"
350+ let
351+ rootSet = Set.fromList modules
352+ rootMap = Map.restrictKeys mains rootSet
354+ if Map.size rootMap == Set.size rootSet then
358+ Set.intersection rootSet (Map.keysSet mains)