1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Generate.JavaScript
12import Prelude hiding (cycle)
13import qualified Data.ByteString.Builder as B
14import Data.Monoid ((<>))
15import qualified Data.List as List
17import qualified Data.Map as Map
18import qualified Data.Set as Set
19import qualified Data.Text as Text
21import qualified AST.Optimized as Opt
22import qualified AST.Module.Name as ModuleName
23import qualified Elm.Interface as I
24import qualified Elm.Name as N
25import qualified Generate.JavaScript.Builder as JS
26import qualified Generate.JavaScript.Expression as Expr
27import qualified Generate.JavaScript.Name as Name
36 { _repl_code :: B.Builder
37 , _repl_name :: B.Builder
41generateForRepl :: Opt.Graph -> ModuleName.Canonical -> N.Name -> ReplBuilder
42generateForRepl (Opt.Graph _ graph _) home name =
46 addGlobal (Name.Debug Name.Client) graph emptyState (Opt.Global home name)
48 Name.toBuilder (Name.fromGlobal home name)
56data Mode = Debug | Prod
59generate :: Mode -> Name.Target -> Opt.Graph -> I.Interfaces -> [ModuleName.Canonical] -> Either [ModuleName.Canonical] B.Builder
60generate mode target (Opt.Graph mains graph fields) interfaces roots =
62 rootSet = Set.fromList roots
63 rootMap = Map.restrictKeys mains rootSet
65 if Map.size rootMap == Set.size rootSet then
67 realMode = toRealMode mode target fields
68 state = Map.foldrWithKey (addMain realMode graph) emptyState rootMap
70 Right $ stateToBuilder state <> toMainExports realMode interfaces rootMap
74 Set.intersection rootSet (Map.keysSet mains)
77addMain :: Name.Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
78addMain mode graph home _ state =
79 addGlobal mode graph state (Opt.Global home "main")
82toRealMode :: Mode -> Name.Target -> Map.Map N.Name Int -> Name.Mode
83toRealMode mode target fields =
89 Name.Prod target (Name.shortenFieldNames fields)
93-- GRAPH TRAVERSAL STATE
98 { _revBuilders :: [B.Builder]
99 , _seenGlobals :: Set.Set Opt.Global
108stateToBuilder :: State -> B.Builder
109stateToBuilder (State revBuilders _) =
110 List.foldl1' (\builder b -> b <> builder) revBuilders
117type Graph = Map.Map Opt.Global Opt.Node
120addGlobal :: Name.Mode -> Graph -> State -> Opt.Global -> State
121addGlobal mode graph state@(State builders seen) global =
122 if Set.member global seen then
125 addGlobalHelp mode graph global $
126 State builders (Set.insert global seen)
129addGlobalHelp :: Name.Mode -> Graph -> Opt.Global -> State -> State
130addGlobalHelp mode graph global state =
132 addDeps deps someState =
133 Set.foldl' (addGlobal mode graph) someState deps
135 case graph ! global of
136 Opt.Define expr deps ->
137 addStmt (addDeps deps state) (
138 var global (Expr.generate mode expr)
141 Opt.DefineTailFunc argNames body deps ->
142 addStmt (addDeps deps state) (
143 var global (Expr.generateFunction (map Name.fromLocal argNames) (Expr.generate mode body))
146 Opt.Ctor name index arity ->
147 let (args, ctor) = Expr.generateCtor mode name index arity in
149 var global (Expr.generateFunction args ctor)
152 Opt.Link linkedGlobal ->
153 addGlobal mode graph state linkedGlobal
155 Opt.Cycle cycle deps ->
156 addStmt (addDeps deps state) (
157 generateCycle mode global cycle
160 Opt.Manager effectsType ->
161 generateManager mode graph global effectsType state
163 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
165 Just (Opt.KContent serverChunks serverDeps) | Name.isServer mode ->
166 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
169 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
171 Opt.PortIncoming decoder deps ->
172 addStmt (addDeps deps state) (
173 generatePort mode global "incomingPort" decoder
176 Opt.PortOutgoing encoder deps ->
177 addStmt (addDeps deps state) (
178 generatePort mode global "outgoingPort" encoder
182addStmt :: State -> JS.Stmt -> State
184 addBuilder state (JS.stmtToBuilder stmt)
187addBuilder :: State -> B.Builder -> State
188addBuilder (State revBuilders seen) builder =
189 State (builder:revBuilders) seen
192var :: Opt.Global -> Expr.Code -> JS.Stmt
193var (Opt.Global home name) code =
194 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
201generateCycle :: Name.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
202generateCycle mode (Opt.Global home _) cycle =
204 safeDefs = map (generateSafeCycle mode home) cycle
205 realDefs = map (generateRealCycle home) cycle
206 -- TODO add `try` in debug mode nice infinite recursion reports
208 JS.Block (safeDefs ++ realDefs)
211generateSafeCycle :: Name.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
212generateSafeCycle mode home (name, expr) =
213 JS.FunctionStmt (Name.fromCycle home name) [] $
214 Expr.codeToStmtList (Expr.generate mode expr)
217generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
218generateRealCycle home (name, _) =
220 safeName = Name.fromCycle home name
221 realName = Name.fromGlobal home name
224 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
225 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
226 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
234generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
235generateKernel mode chunks =
236 List.foldl' (addChunk mode) mempty chunks
239addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
240addChunk mode builder chunk =
243 B.byteString javascript <> builder
245 Opt.ElmVar home name ->
246 Name.toBuilder (Name.fromGlobal home name) <> builder
248 Opt.JsVar home name ->
249 Name.toBuilder (Name.fromKernel home name) <> builder
252 Name.toBuilder (Name.fromField mode name) <> builder
255 Name.toBuilder (Name.fromInt int) <> builder
258 Name.toBuilder (Name.fromInt int) <> builder
281generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
282generatePort mode (Opt.Global home name) makePort converter =
285 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
286 [ JS.String (N.toBuilder name)
287 , Expr.codeToExpr (Expr.generate mode converter)
290 JS.Var [ (Name.fromGlobal home name, Just definition) ]
298generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
299generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
303 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
304 (JS.String (N.toBuilder moduleName))
306 (deps, args, stmts) =
307 generateManagerHelp home effectsType
310 JS.ExprStmt $ JS.Assign managerLVar $
311 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
313 addStmt (List.foldl' (addGlobal mode graph) state deps) $
314 JS.Block (createManager : stmts)
317generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
318generateLeaf home@(ModuleName.Canonical _ moduleName) name =
321 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
323 JS.Var [ (Name.fromGlobal home name, Just definition) ]
329 JS.Ref (Name.fromKernel N.platform "leaf")
332generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
333generateManagerHelp home effectsType =
335 dep name = Opt.Global home name
336 ref name = JS.Ref (Name.fromGlobal home name)
340 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
341 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
342 , [ generateLeaf home "command" ]
346 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
347 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
348 , [ generateLeaf home "subscription" ]
352 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
353 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
354 , [ generateLeaf home "command"
355 , generateLeaf home "subscription"
364toMainExports :: Name.Mode -> I.Interfaces -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
365toMainExports mode interfaces mains =
368 Name.fromKernel N.platform "export"
371 generateExports mode interfaces $
372 Map.foldrWithKey addToTrie emptyTrie mains
374 Name.toBuilder export <> "(" <> exports <> ");"
377generateExports :: Name.Mode -> I.Interfaces -> Trie -> B.Builder
378generateExports mode interfaces (Trie maybeMain subs) =
381 case Map.toList subs of
385 (name, subTrie) : otherSubTries ->
386 "{'" <> N.toBuilder name <> "':"
387 <> generateExports mode interfaces subTrie
388 <> List.foldl' (addSubTrie mode interfaces) "}" otherSubTries
395 let initialize = Expr.generateMain mode interfaces home main in
396 JS.exprToBuilder initialize <> "(" <> object <> ")"
399addSubTrie :: Name.Mode -> I.Interfaces -> B.Builder -> (N.Name, Trie) -> B.Builder
400addSubTrie mode interfaces end (name, trie) =
401 ",'" <> N.toBuilder name <> "':"
402 <> generateExports mode interfaces trie
412 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
413 , _subs :: Map.Map N.Name Trie
419 Trie Nothing Map.empty
422addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
423addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
424 merge trie $ segmentsToTrie home (Text.splitOn "." moduleName) main
427segmentsToTrie :: ModuleName.Canonical -> [N.Name] -> Opt.Main -> Trie
428segmentsToTrie home segments main =
431 Trie (Just (home, main)) Map.empty
433 segment : otherSegments ->
434 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
437merge :: Trie -> Trie -> Trie
438merge (Trie main1 subs1) (Trie main2 subs2) =
440 (checkedMerge main1 main2)
441 (Map.unionWith merge subs1 subs2)
444checkedMerge :: Maybe a -> Maybe a -> Maybe a
454 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