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
31import qualified Reporting.Helpers as H
38data Mode = Debug | Prod
41generate :: Mode -> Name.Target -> I.Interfaces -> Opt.Graph -> [ModuleName.Canonical] -> Either [ModuleName.Canonical] B.Builder
42generate mode target interfaces (Opt.Graph mains graph fields) roots =
44 rootSet = Set.fromList roots
45 rootMap = Map.restrictKeys mains rootSet
47 if Map.size rootMap == Set.size rootSet then
49 realMode = toRealMode mode target fields
50 state = Map.foldrWithKey (addMain realMode graph) emptyState rootMap
52 Right $ stateToBuilder state <> toMainExports realMode interfaces rootMap
56 Set.intersection rootSet (Map.keysSet mains)
59addMain :: Name.Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
60addMain mode graph home _ state =
61 addGlobal mode graph state (Opt.Global home "main")
64toRealMode :: Mode -> Name.Target -> Map.Map N.Name Int -> Name.Mode
65toRealMode mode target fields =
71 Name.Prod target (Name.shortenFieldNames fields)
78generateForRepl :: I.Interfaces -> Opt.Graph -> ModuleName.Canonical -> N.Name -> B.Builder
79generateForRepl interfaces (Opt.Graph _ graph _) home name =
81 mode = Name.Debug Name.Server
82 eval = addGlobal mode graph emptyState (Opt.Global home name)
84 stateToBuilder eval <> print interfaces home name
87print :: I.Interfaces -> ModuleName.Canonical -> N.Name -> B.Builder
88print interfaces home name =
90 value = Name.toBuilder (Name.fromGlobal home name)
91 toString = Name.toBuilder (Name.fromKernel N.debug "toString")
92 annotation = I._types (interfaces ! home) ! name
93 tipe = Type.toString Type.MultiLine $ Extract.fromAnnotation annotation
95 "var _value = " <> toString <> "(" <> value <> ");\n" <>
96 "var _type = '" <> B.stringUtf8 (show tipe) <> "';\n\
97 \if (_value.length + 3 + _type.length >= 80 || _type.indexOf('\\n') >= 0) {\n\
98 \ console.log(_value + '\\n : ' + _type.split('\\n').join('\\n '));\n\
100 \ console.log(_value + ' : ' + _type);\n\
105-- GRAPH TRAVERSAL STATE
110 { _revBuilders :: [B.Builder]
111 , _seenGlobals :: Set.Set Opt.Global
120stateToBuilder :: State -> B.Builder
121stateToBuilder (State revBuilders _) =
122 List.foldl1' (\builder b -> b <> builder) revBuilders
129type Graph = Map.Map Opt.Global Opt.Node
132addGlobal :: Name.Mode -> Graph -> State -> Opt.Global -> State
133addGlobal mode graph state@(State builders seen) global =
134 if Set.member global seen then
137 addGlobalHelp mode graph global $
138 State builders (Set.insert global seen)
141addGlobalHelp :: Name.Mode -> Graph -> Opt.Global -> State -> State
142addGlobalHelp mode graph global state =
144 addDeps deps someState =
145 Set.foldl' (addGlobal mode graph) someState deps
147 case graph ! global of
148 Opt.Define expr deps ->
149 addStmt (addDeps deps state) (
150 var global (Expr.generate mode expr)
153 Opt.DefineTailFunc argNames body deps ->
154 addStmt (addDeps deps state) (
155 let (Opt.Global _ name) = global in
156 var global (Expr.generateTailDef mode name argNames body)
159 Opt.Ctor name index arity ->
161 var global (Expr.generateCtor mode name index arity)
164 Opt.Link linkedGlobal ->
165 addGlobal mode graph state linkedGlobal
167 Opt.Cycle cycle deps ->
168 addStmt (addDeps deps state) (
169 generateCycle mode global cycle
172 Opt.Manager effectsType ->
173 generateManager mode graph global effectsType state
175 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
177 Just (Opt.KContent serverChunks serverDeps) | Name.isServer mode ->
178 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
181 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
183 Opt.Enum name index ->
185 generateEnum mode global name index
190 generateBox mode global name
193 Opt.PortIncoming decoder deps ->
194 addStmt (addDeps deps state) (
195 generatePort mode global "incomingPort" decoder
198 Opt.PortOutgoing encoder deps ->
199 addStmt (addDeps deps state) (
200 generatePort mode global "outgoingPort" encoder
204addStmt :: State -> JS.Stmt -> State
206 addBuilder state (JS.stmtToBuilder stmt)
209addBuilder :: State -> B.Builder -> State
210addBuilder (State revBuilders seen) builder =
211 State (builder:revBuilders) seen
214var :: Opt.Global -> Expr.Code -> JS.Stmt
215var (Opt.Global home name) code =
216 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
223generateCycle :: Name.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
224generateCycle mode (Opt.Global home _) cycle =
226 safeDefs = map (generateSafeCycle mode home) cycle
227 realDefs = map (generateRealCycle home) cycle
228 block = JS.Block (safeDefs ++ realDefs)
235 JS.Try block Name.dollar $ JS.Throw $ JS.String $
236 "The following top-level definitions are causing infinite recursion:\\n"
237 <> drawCycle (map fst cycle)
238 <> "\\n\\nThese errors are very tricky, so read "
239 <> B.stringUtf8 (H.makeLink "halting-problem")
240 <> " to learn how to fix it!"
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)) ]
262drawCycle :: [N.Name] -> B.Builder
265 topLine = "\\n ┌─────┐"
266 nameLine name = "\\n │ " <> N.toBuilder name
268 bottomLine = "\\n └─────┘"
270 mconcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
278generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
279generateKernel mode chunks =
280 List.foldl' (addChunk mode) mempty chunks
283addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
284addChunk mode builder chunk =
287 B.byteString javascript <> builder
289 Opt.ElmVar home name ->
290 Name.toBuilder (Name.fromGlobal home name) <> builder
292 Opt.JsVar home name ->
293 Name.toBuilder (Name.fromKernel home name) <> builder
296 Name.toBuilder (Name.fromField mode name) <> builder
299 Name.toBuilder (Name.fromInt int) <> builder
302 B.intDec int <> builder
325generateEnum :: Name.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
326generateEnum mode (Opt.Global home name) ctorName index =
331 Expr.codeToExpr (Expr.generateCtor mode ctorName index 0)
334 JS.Int (Index.toMachine index)
336 JS.Var [ (Name.fromGlobal home name, Just definition) ]
343generateBox :: Name.Mode -> Opt.Global -> N.Name -> JS.Stmt
344generateBox mode (Opt.Global home name) ctorName =
349 Expr.codeToExpr (Expr.generateCtor mode ctorName Index.first 1)
352 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
354 JS.Var [ (Name.fromGlobal home name, Just definition) ]
361generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
362generatePort mode (Opt.Global home name) makePort converter =
365 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
366 [ JS.String (N.toBuilder name)
367 , Expr.codeToExpr (Expr.generate mode converter)
370 JS.Var [ (Name.fromGlobal home name, Just definition) ]
377generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
378generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
382 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
383 (JS.String (N.toBuilder moduleName))
385 (deps, args, stmts) =
386 generateManagerHelp home effectsType
389 JS.ExprStmt $ JS.Assign managerLVar $
390 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
392 addStmt (List.foldl' (addGlobal mode graph) state deps) $
393 JS.Block (createManager : stmts)
396generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
397generateLeaf home@(ModuleName.Canonical _ moduleName) name =
400 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
402 JS.Var [ (Name.fromGlobal home name, Just definition) ]
408 JS.Ref (Name.fromKernel N.platform "leaf")
411generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
412generateManagerHelp home effectsType =
414 dep name = Opt.Global home name
415 ref name = JS.Ref (Name.fromGlobal home name)
419 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
420 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
421 , [ generateLeaf home "command" ]
425 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
426 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
427 , [ generateLeaf home "subscription" ]
431 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
432 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
433 , [ generateLeaf home "command"
434 , generateLeaf home "subscription"
443toMainExports :: Name.Mode -> I.Interfaces -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
444toMainExports mode interfaces mains =
447 Name.fromKernel N.platform "export"
450 generateExports mode interfaces $
451 Map.foldrWithKey addToTrie emptyTrie mains
453 Name.toBuilder export <> "(" <> exports <> ");"
456generateExports :: Name.Mode -> I.Interfaces -> Trie -> B.Builder
457generateExports mode interfaces (Trie maybeMain subs) =
460 case Map.toList subs of
464 (name, subTrie) : otherSubTries ->
465 "{'" <> Text.encodeUtf8Builder name <> "':"
466 <> generateExports mode interfaces subTrie
467 <> List.foldl' (addSubTrie mode interfaces) "}" otherSubTries
474 let initialize = Expr.generateMain mode interfaces home main in
475 JS.exprToBuilder initialize <> "(" <> object <> ")"
478addSubTrie :: Name.Mode -> I.Interfaces -> B.Builder -> (Text.Text, Trie) -> B.Builder
479addSubTrie mode interfaces end (name, trie) =
480 ",'" <> Text.encodeUtf8Builder name <> "':"
481 <> generateExports mode interfaces trie
491 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
492 , _subs :: Map.Map Text.Text Trie
498 Trie Nothing Map.empty
501addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
502addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
503 merge trie $ segmentsToTrie home (Text.splitOn "." (N.toText moduleName)) main
506segmentsToTrie :: ModuleName.Canonical -> [Text.Text] -> Opt.Main -> Trie
507segmentsToTrie home segments main =
510 Trie (Just (home, main)) Map.empty
512 segment : otherSegments ->
513 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
516merge :: Trie -> Trie -> Trie
517merge (Trie main1 subs1) (Trie main2 subs2) =
519 (checkedMerge main1 main2)
520 (Map.unionWith merge subs1 subs2)
523checkedMerge :: Maybe a -> Maybe a -> Maybe a
533 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