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 let (Opt.Global _ name) = global in
154 var global (Expr.generateTailDef mode name argNames body)
157 Opt.Ctor name index arity ->
159 var global (Expr.generateCtor mode name index arity)
162 Opt.Link linkedGlobal ->
163 addGlobal mode graph state linkedGlobal
165 Opt.Cycle cycle deps ->
166 addStmt (addDeps deps state) (
167 generateCycle mode global cycle
170 Opt.Manager effectsType ->
171 generateManager mode graph global effectsType state
173 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
175 Just (Opt.KContent serverChunks serverDeps) | Name.isServer mode ->
176 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
179 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
181 Opt.Enum name index ->
183 generateEnum mode global name index
188 generateBox mode global name
191 Opt.PortIncoming decoder deps ->
192 addStmt (addDeps deps state) (
193 generatePort mode global "incomingPort" decoder
196 Opt.PortOutgoing encoder deps ->
197 addStmt (addDeps deps state) (
198 generatePort mode global "outgoingPort" encoder
202addStmt :: State -> JS.Stmt -> State
204 addBuilder state (JS.stmtToBuilder stmt)
207addBuilder :: State -> B.Builder -> State
208addBuilder (State revBuilders seen) builder =
209 State (builder:revBuilders) seen
212var :: Opt.Global -> Expr.Code -> JS.Stmt
213var (Opt.Global home name) code =
214 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
221generateCycle :: Name.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
222generateCycle mode (Opt.Global home _) cycle =
224 safeDefs = map (generateSafeCycle mode home) cycle
225 realDefs = map (generateRealCycle home) cycle
226 -- TODO add `try` in debug mode nice infinite recursion reports
228 JS.Block (safeDefs ++ realDefs)
231generateSafeCycle :: Name.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
232generateSafeCycle mode home (name, expr) =
233 JS.FunctionStmt (Name.fromCycle home name) [] $
234 Expr.codeToStmtList (Expr.generate mode expr)
237generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
238generateRealCycle home (name, _) =
240 safeName = Name.fromCycle home name
241 realName = Name.fromGlobal home name
244 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
245 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
246 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
254generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
255generateKernel mode chunks =
256 List.foldl' (addChunk mode) mempty chunks
259addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
260addChunk mode builder chunk =
263 B.byteString javascript <> builder
265 Opt.ElmVar home name ->
266 Name.toBuilder (Name.fromGlobal home name) <> builder
268 Opt.JsVar home name ->
269 Name.toBuilder (Name.fromKernel home name) <> builder
272 Name.toBuilder (Name.fromField mode name) <> builder
275 Name.toBuilder (Name.fromInt int) <> builder
278 B.intDec int <> builder
301generateEnum :: Name.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
302generateEnum mode (Opt.Global home name) ctorName index =
307 JS.Object [(Name.dollar, JS.String (N.toBuilder ctorName))]
310 JS.Int (Index.toMachine index)
312 JS.Var [ (Name.fromGlobal home name, Just definition) ]
319generateBox :: Name.Mode -> Opt.Global -> N.Name -> JS.Stmt
320generateBox mode global ctorName =
323 var global (Expr.generateCtor mode ctorName Index.first 1)
333generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
334generatePort mode (Opt.Global home name) makePort converter =
337 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
338 [ JS.String (N.toBuilder name)
339 , Expr.codeToExpr (Expr.generate mode converter)
342 JS.Var [ (Name.fromGlobal home name, Just definition) ]
349generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
350generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
354 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
355 (JS.String (N.toBuilder moduleName))
357 (deps, args, stmts) =
358 generateManagerHelp home effectsType
361 JS.ExprStmt $ JS.Assign managerLVar $
362 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
364 addStmt (List.foldl' (addGlobal mode graph) state deps) $
365 JS.Block (createManager : stmts)
368generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
369generateLeaf home@(ModuleName.Canonical _ moduleName) name =
372 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
374 JS.Var [ (Name.fromGlobal home name, Just definition) ]
380 JS.Ref (Name.fromKernel N.platform "leaf")
383generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
384generateManagerHelp home effectsType =
386 dep name = Opt.Global home name
387 ref name = JS.Ref (Name.fromGlobal home name)
391 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
392 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
393 , [ generateLeaf home "command" ]
397 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
398 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
399 , [ generateLeaf home "subscription" ]
403 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
404 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
405 , [ generateLeaf home "command"
406 , generateLeaf home "subscription"
415toMainExports :: Name.Mode -> I.Interfaces -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
416toMainExports mode interfaces mains =
419 Name.fromKernel N.platform "export"
422 generateExports mode interfaces $
423 Map.foldrWithKey addToTrie emptyTrie mains
425 Name.toBuilder export <> "(" <> exports <> ");"
428generateExports :: Name.Mode -> I.Interfaces -> Trie -> B.Builder
429generateExports mode interfaces (Trie maybeMain subs) =
432 case Map.toList subs of
436 (name, subTrie) : otherSubTries ->
437 "{'" <> N.toBuilder name <> "':"
438 <> generateExports mode interfaces subTrie
439 <> List.foldl' (addSubTrie mode interfaces) "}" otherSubTries
446 let initialize = Expr.generateMain mode interfaces home main in
447 JS.exprToBuilder initialize <> "(" <> object <> ")"
450addSubTrie :: Name.Mode -> I.Interfaces -> B.Builder -> (N.Name, Trie) -> B.Builder
451addSubTrie mode interfaces end (name, trie) =
452 ",'" <> N.toBuilder name <> "':"
453 <> generateExports mode interfaces trie
463 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
464 , _subs :: Map.Map N.Name Trie
470 Trie Nothing Map.empty
473addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
474addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
475 merge trie $ segmentsToTrie home (Text.splitOn "." moduleName) main
478segmentsToTrie :: ModuleName.Canonical -> [N.Name] -> Opt.Main -> Trie
479segmentsToTrie home segments main =
482 Trie (Just (home, main)) Map.empty
484 segment : otherSegments ->
485 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
488merge :: Trie -> Trie -> Trie
489merge (Trie main1 subs1) (Trie main2 subs2) =
491 (checkedMerge main1 main2)
492 (Map.unionWith merge subs1 subs2)
495checkedMerge :: Maybe a -> Maybe a -> Maybe a
505 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