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 var global (Expr.generateFunction (map Name.fromLocal argNames) (Expr.generate mode 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)) ]
220generateCycle :: Name.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
221generateCycle mode (Opt.Global home _) cycle =
223 safeDefs = map (generateSafeCycle mode home) cycle
224 realDefs = map (generateRealCycle home) cycle
225 -- TODO add `try` in debug mode nice infinite recursion reports
227 JS.Block (safeDefs ++ realDefs)
230generateSafeCycle :: Name.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
231generateSafeCycle mode home (name, expr) =
232 JS.FunctionStmt (Name.fromCycle home name) [] $
233 Expr.codeToStmtList (Expr.generate mode expr)
236generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
237generateRealCycle home (name, _) =
239 safeName = Name.fromCycle home name
240 realName = Name.fromGlobal home name
243 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
244 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
245 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
253generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
254generateKernel mode chunks =
255 List.foldl' (addChunk mode) mempty chunks
258addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
259addChunk mode builder chunk =
262 B.byteString javascript <> builder
264 Opt.ElmVar home name ->
265 Name.toBuilder (Name.fromGlobal home name) <> builder
267 Opt.JsVar home name ->
268 Name.toBuilder (Name.fromKernel home name) <> builder
271 Name.toBuilder (Name.fromField mode name) <> builder
274 Name.toBuilder (Name.fromInt int) <> builder
277 Name.toBuilder (Name.fromInt int) <> builder
300generateEnum :: Name.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
301generateEnum mode (Opt.Global home name) ctorName index =
306 JS.Object [(Name.dollar, JS.String (N.toBuilder ctorName))]
309 JS.Int (Index.toMachine index)
311 JS.Var [ (Name.fromGlobal home name, Just definition) ]
318generateBox :: Name.Mode -> Opt.Global -> N.Name -> JS.Stmt
319generateBox mode (Opt.Global home name) ctorName =
324 Expr.codeToExpr $ Expr.generateCtor mode ctorName Index.first 1
327 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
329 JS.Var [ (Name.fromGlobal home name, Just definition) ]
336generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
337generatePort mode (Opt.Global home name) makePort converter =
340 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
341 [ JS.String (N.toBuilder name)
342 , Expr.codeToExpr (Expr.generate mode converter)
345 JS.Var [ (Name.fromGlobal home name, Just definition) ]
352generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
353generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
357 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
358 (JS.String (N.toBuilder moduleName))
360 (deps, args, stmts) =
361 generateManagerHelp home effectsType
364 JS.ExprStmt $ JS.Assign managerLVar $
365 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
367 addStmt (List.foldl' (addGlobal mode graph) state deps) $
368 JS.Block (createManager : stmts)
371generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
372generateLeaf home@(ModuleName.Canonical _ moduleName) name =
375 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
377 JS.Var [ (Name.fromGlobal home name, Just definition) ]
383 JS.Ref (Name.fromKernel N.platform "leaf")
386generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
387generateManagerHelp home effectsType =
389 dep name = Opt.Global home name
390 ref name = JS.Ref (Name.fromGlobal home name)
394 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
395 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
396 , [ generateLeaf home "command" ]
400 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
401 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
402 , [ generateLeaf home "subscription" ]
406 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
407 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
408 , [ generateLeaf home "command"
409 , generateLeaf home "subscription"
418toMainExports :: Name.Mode -> I.Interfaces -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
419toMainExports mode interfaces mains =
422 Name.fromKernel N.platform "export"
425 generateExports mode interfaces $
426 Map.foldrWithKey addToTrie emptyTrie mains
428 Name.toBuilder export <> "(" <> exports <> ");"
431generateExports :: Name.Mode -> I.Interfaces -> Trie -> B.Builder
432generateExports mode interfaces (Trie maybeMain subs) =
435 case Map.toList subs of
439 (name, subTrie) : otherSubTries ->
440 "{'" <> N.toBuilder name <> "':"
441 <> generateExports mode interfaces subTrie
442 <> List.foldl' (addSubTrie mode interfaces) "}" otherSubTries
449 let initialize = Expr.generateMain mode interfaces home main in
450 JS.exprToBuilder initialize <> "(" <> object <> ")"
453addSubTrie :: Name.Mode -> I.Interfaces -> B.Builder -> (N.Name, Trie) -> B.Builder
454addSubTrie mode interfaces end (name, trie) =
455 ",'" <> N.toBuilder name <> "':"
456 <> generateExports mode interfaces trie
466 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
467 , _subs :: Map.Map N.Name Trie
473 Trie Nothing Map.empty
476addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
477addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
478 merge trie $ segmentsToTrie home (Text.splitOn "." moduleName) main
481segmentsToTrie :: ModuleName.Canonical -> [N.Name] -> Opt.Main -> Trie
482segmentsToTrie home segments main =
485 Trie (Just (home, main)) Map.empty
487 segment : otherSegments ->
488 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
491merge :: Trie -> Trie -> Trie
492merge (Trie main1 subs1) (Trie main2 subs2) =
494 (checkedMerge main1 main2)
495 (Map.unionWith merge subs1 subs2)
498checkedMerge :: Maybe a -> Maybe a -> Maybe a
508 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