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
19import qualified Data.Text.Encoding as Text
21import qualified AST.Optimized as Opt
22import qualified AST.Module.Name as ModuleName
23import qualified Data.Index as Index
24import qualified Elm.Compiler.Type as Type
25import qualified Elm.Compiler.Type.Extract as Extract
26import qualified Elm.Interface as I
27import qualified Elm.Name as N
28import qualified Generate.JavaScript.Builder as JS
29import qualified Generate.JavaScript.Expression as Expr
30import qualified Generate.JavaScript.Name as Name
37data Mode = Debug | Prod
40generate :: Mode -> Name.Target -> I.Interfaces -> Opt.Graph -> [ModuleName.Canonical] -> Either [ModuleName.Canonical] B.Builder
41generate mode target interfaces (Opt.Graph mains graph fields) roots =
43 rootSet = Set.fromList roots
44 rootMap = Map.restrictKeys mains rootSet
46 if Map.size rootMap == Set.size rootSet then
48 realMode = toRealMode mode target fields
49 state = Map.foldrWithKey (addMain realMode graph) emptyState rootMap
51 Right $ stateToBuilder state <> toMainExports realMode interfaces rootMap
55 Set.intersection rootSet (Map.keysSet mains)
58addMain :: Name.Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
59addMain mode graph home _ state =
60 addGlobal mode graph state (Opt.Global home "main")
63toRealMode :: Mode -> Name.Target -> Map.Map N.Name Int -> Name.Mode
64toRealMode mode target fields =
70 Name.Prod target (Name.shortenFieldNames fields)
77generateForRepl :: I.Interfaces -> Opt.Graph -> ModuleName.Canonical -> N.Name -> B.Builder
78generateForRepl interfaces (Opt.Graph _ graph _) home name =
80 mode = Name.Debug Name.Server
81 eval = addGlobal mode graph emptyState (Opt.Global home name)
83 stateToBuilder eval <> print interfaces home name
86print :: I.Interfaces -> ModuleName.Canonical -> N.Name -> B.Builder
87print interfaces home name =
89 value = Name.toBuilder (Name.fromGlobal home name)
90 toString = Name.toBuilder (Name.fromKernel N.debug "toString")
91 annotation = I._types (interfaces ! home) ! name
92 tipe = Type.toString Type.MultiLine $ Extract.fromAnnotation annotation
94 "var _value = " <> toString <> "(" <> value <> ");\n" <>
95 "var _type = '" <> B.stringUtf8 (show tipe) <> "';\n\
96 \if (_value.length + 3 + _type.length >= 80 || _type.indexOf('\\n') >= 0) {\n\
97 \ console.log(_value + '\\n : ' + _type.split('\\n').join('\\n '));\n\
99 \ console.log(_value + ' : ' + _type);\n\
104-- GRAPH TRAVERSAL STATE
109 { _revBuilders :: [B.Builder]
110 , _seenGlobals :: Set.Set Opt.Global
119stateToBuilder :: State -> B.Builder
120stateToBuilder (State revBuilders _) =
121 List.foldl1' (\builder b -> b <> builder) revBuilders
128type Graph = Map.Map Opt.Global Opt.Node
131addGlobal :: Name.Mode -> Graph -> State -> Opt.Global -> State
132addGlobal mode graph state@(State builders seen) global =
133 if Set.member global seen then
136 addGlobalHelp mode graph global $
137 State builders (Set.insert global seen)
140addGlobalHelp :: Name.Mode -> Graph -> Opt.Global -> State -> State
141addGlobalHelp mode graph global state =
143 addDeps deps someState =
144 Set.foldl' (addGlobal mode graph) someState deps
146 case graph ! global of
147 Opt.Define expr deps ->
148 addStmt (addDeps deps state) (
149 var global (Expr.generate mode expr)
152 Opt.DefineTailFunc argNames body deps ->
153 addStmt (addDeps deps state) (
154 let (Opt.Global _ name) = global in
155 var global (Expr.generateTailDef mode name argNames body)
158 Opt.Ctor name index arity ->
160 var global (Expr.generateCtor mode name index arity)
163 Opt.Link linkedGlobal ->
164 addGlobal mode graph state linkedGlobal
166 Opt.Cycle cycle deps ->
167 addStmt (addDeps deps state) (
168 generateCycle mode global cycle
171 Opt.Manager effectsType ->
172 generateManager mode graph global effectsType state
174 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
176 Just (Opt.KContent serverChunks serverDeps) | Name.isServer mode ->
177 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
180 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
182 Opt.Enum name index ->
184 generateEnum mode global name index
189 generateBox mode global name
192 Opt.PortIncoming decoder deps ->
193 addStmt (addDeps deps state) (
194 generatePort mode global "incomingPort" decoder
197 Opt.PortOutgoing encoder deps ->
198 addStmt (addDeps deps state) (
199 generatePort mode global "outgoingPort" encoder
203addStmt :: State -> JS.Stmt -> State
205 addBuilder state (JS.stmtToBuilder stmt)
208addBuilder :: State -> B.Builder -> State
209addBuilder (State revBuilders seen) builder =
210 State (builder:revBuilders) seen
213var :: Opt.Global -> Expr.Code -> JS.Stmt
214var (Opt.Global home name) code =
215 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
222generateCycle :: Name.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
223generateCycle mode (Opt.Global home _) cycle =
225 safeDefs = map (generateSafeCycle mode home) cycle
226 realDefs = map (generateRealCycle home) cycle
227 -- TODO add `try` in debug mode nice infinite recursion reports
229 JS.Block (safeDefs ++ realDefs)
232generateSafeCycle :: Name.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
233generateSafeCycle mode home (name, expr) =
234 JS.FunctionStmt (Name.fromCycle home name) [] $
235 Expr.codeToStmtList (Expr.generate mode expr)
238generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
239generateRealCycle home (name, _) =
241 safeName = Name.fromCycle home name
242 realName = Name.fromGlobal home name
245 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
246 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
247 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
255generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
256generateKernel mode chunks =
257 List.foldl' (addChunk mode) mempty chunks
260addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
261addChunk mode builder chunk =
264 B.byteString javascript <> builder
266 Opt.ElmVar home name ->
267 Name.toBuilder (Name.fromGlobal home name) <> builder
269 Opt.JsVar home name ->
270 Name.toBuilder (Name.fromKernel home name) <> builder
273 Name.toBuilder (Name.fromField mode name) <> builder
276 Name.toBuilder (Name.fromInt int) <> builder
279 B.intDec int <> builder
302generateEnum :: Name.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
303generateEnum mode (Opt.Global home name) ctorName index =
308 JS.Object [(Name.dollar, JS.String (N.toBuilder ctorName))]
311 JS.Int (Index.toMachine index)
313 JS.Var [ (Name.fromGlobal home name, Just definition) ]
320generateBox :: Name.Mode -> Opt.Global -> N.Name -> JS.Stmt
321generateBox mode global ctorName =
324 var global (Expr.generateCtor mode ctorName Index.first 1)
334generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
335generatePort mode (Opt.Global home name) makePort converter =
338 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
339 [ JS.String (N.toBuilder name)
340 , Expr.codeToExpr (Expr.generate mode converter)
343 JS.Var [ (Name.fromGlobal home name, Just definition) ]
350generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
351generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
355 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
356 (JS.String (N.toBuilder moduleName))
358 (deps, args, stmts) =
359 generateManagerHelp home effectsType
362 JS.ExprStmt $ JS.Assign managerLVar $
363 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
365 addStmt (List.foldl' (addGlobal mode graph) state deps) $
366 JS.Block (createManager : stmts)
369generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
370generateLeaf home@(ModuleName.Canonical _ moduleName) name =
373 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
375 JS.Var [ (Name.fromGlobal home name, Just definition) ]
381 JS.Ref (Name.fromKernel N.platform "leaf")
384generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
385generateManagerHelp home effectsType =
387 dep name = Opt.Global home name
388 ref name = JS.Ref (Name.fromGlobal home name)
392 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
393 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
394 , [ generateLeaf home "command" ]
398 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
399 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
400 , [ generateLeaf home "subscription" ]
404 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
405 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
406 , [ generateLeaf home "command"
407 , generateLeaf home "subscription"
416toMainExports :: Name.Mode -> I.Interfaces -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
417toMainExports mode interfaces mains =
420 Name.fromKernel N.platform "export"
423 generateExports mode interfaces $
424 Map.foldrWithKey addToTrie emptyTrie mains
426 Name.toBuilder export <> "(" <> exports <> ");"
429generateExports :: Name.Mode -> I.Interfaces -> Trie -> B.Builder
430generateExports mode interfaces (Trie maybeMain subs) =
433 case Map.toList subs of
437 (name, subTrie) : otherSubTries ->
438 "{'" <> Text.encodeUtf8Builder name <> "':"
439 <> generateExports mode interfaces subTrie
440 <> List.foldl' (addSubTrie mode interfaces) "}" otherSubTries
447 let initialize = Expr.generateMain mode interfaces home main in
448 JS.exprToBuilder initialize <> "(" <> object <> ")"
451addSubTrie :: Name.Mode -> I.Interfaces -> B.Builder -> (Text.Text, Trie) -> B.Builder
452addSubTrie mode interfaces end (name, trie) =
453 ",'" <> Text.encodeUtf8Builder name <> "':"
454 <> generateExports mode interfaces trie
464 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
465 , _subs :: Map.Map Text.Text Trie
471 Trie Nothing Map.empty
474addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
475addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
476 merge trie $ segmentsToTrie home (Text.splitOn "." (N.toText moduleName)) main
479segmentsToTrie :: ModuleName.Canonical -> [Text.Text] -> Opt.Main -> Trie
480segmentsToTrie home segments main =
483 Trie (Just (home, main)) Map.empty
485 segment : otherSegments ->
486 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
489merge :: Trie -> Trie -> Trie
490merge (Trie main1 subs1) (Trie main2 subs2) =
492 (checkedMerge main1 main2)
493 (Map.unionWith merge subs1 subs2)
496checkedMerge :: Maybe a -> Maybe a -> Maybe a
506 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