1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Generate.JavaScript
11import Prelude hiding (cycle, print)
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
18import qualified Data.Text as Text
20import qualified AST.Optimized as Opt
21import qualified AST.Module.Name as ModuleName
22import qualified Data.Index as Index
23import qualified Elm.Compiler.Type as Type
24import qualified Elm.Compiler.Type.Extract as Extract
25import qualified Elm.Interface as I
26import qualified Elm.Name as N
27import qualified Generate.JavaScript.Builder as JS
28import qualified Generate.JavaScript.Expression as Expr
29import qualified Generate.JavaScript.Name as Name
36data Mode = Debug | Prod
39generate :: Mode -> Name.Target -> I.Interfaces -> Opt.Graph -> [ModuleName.Canonical] -> Either [ModuleName.Canonical] B.Builder
40generate mode target interfaces (Opt.Graph mains graph fields) roots =
42 rootSet = Set.fromList roots
43 rootMap = Map.restrictKeys mains rootSet
45 if Map.size rootMap == Set.size rootSet then
47 realMode = toRealMode mode target fields
48 state = Map.foldrWithKey (addMain realMode graph) emptyState rootMap
50 Right $ stateToBuilder state <> toMainExports realMode interfaces rootMap
54 Set.intersection rootSet (Map.keysSet mains)
57addMain :: Name.Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
58addMain mode graph home _ state =
59 addGlobal mode graph state (Opt.Global home "main")
62toRealMode :: Mode -> Name.Target -> Map.Map N.Name Int -> Name.Mode
63toRealMode mode target fields =
69 Name.Prod target (Name.shortenFieldNames fields)
76generateForRepl :: I.Interfaces -> Opt.Graph -> ModuleName.Canonical -> N.Name -> B.Builder
77generateForRepl interfaces (Opt.Graph _ graph _) home name =
79 mode = Name.Debug Name.Server
80 eval = addGlobal mode graph emptyState (Opt.Global home name)
82 stateToBuilder eval <> print interfaces home name
85print :: I.Interfaces -> ModuleName.Canonical -> N.Name -> B.Builder
86print interfaces home name =
88 value = Name.toBuilder (Name.fromGlobal home name)
89 toString = Name.toBuilder (Name.fromKernel N.debug "toString")
90 annotation = I._types (interfaces ! home) ! name
91 tipe = Type.toString Type.MultiLine $ Extract.fromAnnotation annotation
93 "var _value = " <> toString <> "(" <> value <> ");\n" <>
94 "var _type = '" <> B.stringUtf8 (show tipe) <> "';\n\
95 \if (_value.length + 3 + _type.length >= 80 || _type.indexOf('\\n') >= 0) {\n\
96 \ console.log(_value + '\\n : ' + _type.split('\\n').join('\\n '));\n\
98 \ console.log(_value + ' : ' + _type);\n\
103-- GRAPH TRAVERSAL STATE
108 { _revBuilders :: [B.Builder]
109 , _seenGlobals :: Set.Set Opt.Global
118stateToBuilder :: State -> B.Builder
119stateToBuilder (State revBuilders _) =
120 List.foldl1' (\builder b -> b <> builder) revBuilders
127type Graph = Map.Map Opt.Global Opt.Node
130addGlobal :: Name.Mode -> Graph -> State -> Opt.Global -> State
131addGlobal mode graph state@(State builders seen) global =
132 if Set.member global seen then
135 addGlobalHelp mode graph global $
136 State builders (Set.insert global seen)
139addGlobalHelp :: Name.Mode -> Graph -> Opt.Global -> State -> State
140addGlobalHelp mode graph global state =
142 addDeps deps someState =
143 Set.foldl' (addGlobal mode graph) someState deps
145 case graph ! global of
146 Opt.Define expr deps ->
147 addStmt (addDeps deps state) (
148 var global (Expr.generate mode expr)
151 Opt.DefineTailFunc argNames body deps ->
152 addStmt (addDeps deps state) (
153 generateTailDef mode global argNames body
156 Opt.Ctor name index arity ->
158 var global (Expr.generateCtor mode name index arity)
161 Opt.Link linkedGlobal ->
162 addGlobal mode graph state linkedGlobal
164 Opt.Cycle cycle deps ->
165 addStmt (addDeps deps state) (
166 generateCycle mode global cycle
169 Opt.Manager effectsType ->
170 generateManager mode graph global effectsType state
172 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
174 Just (Opt.KContent serverChunks serverDeps) | Name.isServer mode ->
175 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
178 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
180 Opt.Enum name index ->
182 generateEnum mode global name index
187 generateBox mode global name
190 Opt.PortIncoming decoder deps ->
191 addStmt (addDeps deps state) (
192 generatePort mode global "incomingPort" decoder
195 Opt.PortOutgoing encoder deps ->
196 addStmt (addDeps deps state) (
197 generatePort mode global "outgoingPort" encoder
201addStmt :: State -> JS.Stmt -> State
203 addBuilder state (JS.stmtToBuilder stmt)
206addBuilder :: State -> B.Builder -> State
207addBuilder (State revBuilders seen) builder =
208 State (builder:revBuilders) seen
211var :: Opt.Global -> Expr.Code -> JS.Stmt
212var (Opt.Global home name) code =
213 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
220generateTailDef :: Name.Mode -> Opt.Global -> [N.Name] -> Opt.Expr -> JS.Stmt
221generateTailDef mode (Opt.Global home name) argNames body =
224 Expr.generateTailDef mode name argNames body
226 JS.Var [ (Name.fromGlobal home name, Just definition) ]
233generateCycle :: Name.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
234generateCycle mode (Opt.Global home _) cycle =
236 safeDefs = map (generateSafeCycle mode home) cycle
237 realDefs = map (generateRealCycle home) cycle
238 -- TODO add `try` in debug mode nice infinite recursion reports
240 JS.Block (safeDefs ++ realDefs)
243generateSafeCycle :: Name.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
244generateSafeCycle mode home (name, expr) =
245 JS.FunctionStmt (Name.fromCycle home name) [] $
246 Expr.codeToStmtList (Expr.generate mode expr)
249generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
250generateRealCycle home (name, _) =
252 safeName = Name.fromCycle home name
253 realName = Name.fromGlobal home name
256 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
257 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
258 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
266generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
267generateKernel mode chunks =
268 List.foldl' (addChunk mode) mempty chunks
271addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
272addChunk mode builder chunk =
275 B.byteString javascript <> builder
277 Opt.ElmVar home name ->
278 Name.toBuilder (Name.fromGlobal home name) <> builder
280 Opt.JsVar home name ->
281 Name.toBuilder (Name.fromKernel home name) <> builder
284 Name.toBuilder (Name.fromField mode name) <> builder
287 Name.toBuilder (Name.fromInt int) <> builder
290 Name.toBuilder (Name.fromInt int) <> builder
313generateEnum :: Name.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
314generateEnum mode (Opt.Global home name) ctorName index =
319 JS.Object [(Name.dollar, JS.String (N.toBuilder ctorName))]
322 JS.Int (Index.toMachine index)
324 JS.Var [ (Name.fromGlobal home name, Just definition) ]
331generateBox :: Name.Mode -> Opt.Global -> N.Name -> JS.Stmt
332generateBox mode (Opt.Global home name) ctorName =
337 Expr.codeToExpr $ Expr.generateCtor mode ctorName Index.first 1
340 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
342 JS.Var [ (Name.fromGlobal home name, Just definition) ]
349generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
350generatePort mode (Opt.Global home name) makePort converter =
353 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
354 [ JS.String (N.toBuilder name)
355 , Expr.codeToExpr (Expr.generate mode converter)
358 JS.Var [ (Name.fromGlobal home name, Just definition) ]
365generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
366generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
370 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
371 (JS.String (N.toBuilder moduleName))
373 (deps, args, stmts) =
374 generateManagerHelp home effectsType
377 JS.ExprStmt $ JS.Assign managerLVar $
378 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
380 addStmt (List.foldl' (addGlobal mode graph) state deps) $
381 JS.Block (createManager : stmts)
384generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
385generateLeaf home@(ModuleName.Canonical _ moduleName) name =
388 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
390 JS.Var [ (Name.fromGlobal home name, Just definition) ]
396 JS.Ref (Name.fromKernel N.platform "leaf")
399generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
400generateManagerHelp home effectsType =
402 dep name = Opt.Global home name
403 ref name = JS.Ref (Name.fromGlobal home name)
407 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
408 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
409 , [ generateLeaf home "command" ]
413 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
414 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
415 , [ generateLeaf home "subscription" ]
419 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
420 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
421 , [ generateLeaf home "command"
422 , generateLeaf home "subscription"
431toMainExports :: Name.Mode -> I.Interfaces -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
432toMainExports mode interfaces mains =
435 Name.fromKernel N.platform "export"
438 generateExports mode interfaces $
439 Map.foldrWithKey addToTrie emptyTrie mains
441 Name.toBuilder export <> "(" <> exports <> ");"
444generateExports :: Name.Mode -> I.Interfaces -> Trie -> B.Builder
445generateExports mode interfaces (Trie maybeMain subs) =
448 case Map.toList subs of
452 (name, subTrie) : otherSubTries ->
453 "{'" <> N.toBuilder name <> "':"
454 <> generateExports mode interfaces subTrie
455 <> List.foldl' (addSubTrie mode interfaces) "}" otherSubTries
462 let initialize = Expr.generateMain mode interfaces home main in
463 JS.exprToBuilder initialize <> "(" <> object <> ")"
466addSubTrie :: Name.Mode -> I.Interfaces -> B.Builder -> (N.Name, Trie) -> B.Builder
467addSubTrie mode interfaces end (name, trie) =
468 ",'" <> N.toBuilder name <> "':"
469 <> generateExports mode interfaces trie
479 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
480 , _subs :: Map.Map N.Name Trie
486 Trie Nothing Map.empty
489addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
490addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
491 merge trie $ segmentsToTrie home (Text.splitOn "." moduleName) main
494segmentsToTrie :: ModuleName.Canonical -> [N.Name] -> Opt.Main -> Trie
495segmentsToTrie home segments main =
498 Trie (Just (home, main)) Map.empty
500 segment : otherSegments ->
501 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
504merge :: Trie -> Trie -> Trie
505merge (Trie main1 subs1) (Trie main2 subs2) =
507 (checkedMerge main1 main2)
508 (Map.unionWith merge subs1 subs2)
511checkedMerge :: Maybe a -> Maybe a -> Maybe a
521 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