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
20import qualified Text.PrettyPrint.ANSI.Leijen as P
22import qualified AST.Optimized as Opt
23import qualified AST.Module.Name as ModuleName
24import qualified Data.Index as Index
25import qualified Elm.Compiler.Type as Type
26import qualified Elm.Compiler.Type.Extract as Extract
27import qualified Elm.Interface as I
28import qualified Elm.Name as N
29import qualified Generate.JavaScript.Builder as JS
30import qualified Generate.JavaScript.Expression as Expr
31import qualified Generate.JavaScript.Name as Name
32import qualified Reporting.Helpers as H
39data Mode = Debug | Prod
42generate :: Mode -> Name.Target -> I.Interfaces -> Opt.Graph -> [ModuleName.Canonical] -> Either [ModuleName.Canonical] B.Builder
43generate mode target interfaces (Opt.Graph mains graph fields) roots =
45 rootSet = Set.fromList roots
46 rootMap = Map.restrictKeys mains rootSet
48 if Map.size rootMap == Set.size rootSet then
50 realMode = toRealMode mode target fields
51 state = Map.foldrWithKey (addMain realMode graph) emptyState rootMap
53 Right $ stateToBuilder state <> toMainExports realMode interfaces rootMap
57 Set.intersection rootSet (Map.keysSet mains)
60addMain :: Name.Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
61addMain mode graph home _ state =
62 addGlobal mode graph state (Opt.Global home "main")
65toRealMode :: Mode -> Name.Target -> Map.Map N.Name Int -> Name.Mode
66toRealMode mode target fields =
72 Name.Prod target (Name.shortenFieldNames fields)
79generateForRepl :: I.Interfaces -> Opt.Graph -> ModuleName.Canonical -> N.Name -> B.Builder
80generateForRepl interfaces (Opt.Graph _ graph _) home name =
82 mode = Name.Debug Name.Server
83 eval = addGlobal mode graph emptyState (Opt.Global home name)
85 stateToBuilder eval <> print interfaces home name
88print :: I.Interfaces -> ModuleName.Canonical -> N.Name -> B.Builder
89print interfaces home name =
91 value = Name.toBuilder (Name.fromGlobal home name)
92 toString = Name.toBuilder (Name.fromKernel N.debug "toString")
93 annotation = I._types (interfaces ! home) ! name
94 tipe = P.displayS (P.renderPretty 1.0 80 (Type.toDoc Type.None (Extract.fromAnnotation annotation))) ""
96 "var _value = " <> toString <> "(" <> value <> ");\n" <>
97 "var _type = '" <> B.stringUtf8 (show tipe) <> "';\n\
98 \if (_value.length + 3 + _type.length >= 80 || _type.indexOf('\\n') >= 0) {\n\
99 \ console.log(_value + '\\n : ' + _type.split('\\n').join('\\n '));\n\
101 \ console.log(_value + ' : ' + _type);\n\
106-- GRAPH TRAVERSAL STATE
111 { _revBuilders :: [B.Builder]
112 , _seenGlobals :: Set.Set Opt.Global
121stateToBuilder :: State -> B.Builder
122stateToBuilder (State revBuilders _) =
123 List.foldl1' (\builder b -> b <> builder) revBuilders
130type Graph = Map.Map Opt.Global Opt.Node
133addGlobal :: Name.Mode -> Graph -> State -> Opt.Global -> State
134addGlobal mode graph state@(State builders seen) global =
135 if Set.member global seen then
138 addGlobalHelp mode graph global $
139 State builders (Set.insert global seen)
142addGlobalHelp :: Name.Mode -> Graph -> Opt.Global -> State -> State
143addGlobalHelp mode graph global state =
145 addDeps deps someState =
146 Set.foldl' (addGlobal mode graph) someState deps
148 case graph ! global of
149 Opt.Define expr deps ->
150 addStmt (addDeps deps state) (
151 var global (Expr.generate mode expr)
154 Opt.DefineTailFunc argNames body deps ->
155 addStmt (addDeps deps state) (
156 let (Opt.Global _ name) = global in
157 var global (Expr.generateTailDef mode name argNames body)
160 Opt.Ctor name index arity ->
162 var global (Expr.generateCtor mode name index arity)
165 Opt.Link linkedGlobal ->
166 addGlobal mode graph state linkedGlobal
168 Opt.Cycle cycle deps ->
169 addStmt (addDeps deps state) (
170 generateCycle mode global cycle
173 Opt.Manager effectsType ->
174 generateManager mode graph global effectsType state
176 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
178 Just (Opt.KContent serverChunks serverDeps) | Name.isServer mode ->
179 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
182 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
184 Opt.Enum name index ->
186 generateEnum mode global name index
191 generateBox mode global name
194 Opt.PortIncoming decoder deps ->
195 addStmt (addDeps deps state) (
196 generatePort mode global "incomingPort" decoder
199 Opt.PortOutgoing encoder deps ->
200 addStmt (addDeps deps state) (
201 generatePort mode global "outgoingPort" encoder
205addStmt :: State -> JS.Stmt -> State
207 addBuilder state (JS.stmtToBuilder stmt)
210addBuilder :: State -> B.Builder -> State
211addBuilder (State revBuilders seen) builder =
212 State (builder:revBuilders) seen
215var :: Opt.Global -> Expr.Code -> JS.Stmt
216var (Opt.Global home name) code =
217 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
224generateCycle :: Name.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
225generateCycle mode (Opt.Global home _) cycle =
227 safeDefs = map (generateSafeCycle mode home) cycle
228 realDefs = map (generateRealCycle home) cycle
229 block = JS.Block (safeDefs ++ realDefs)
236 JS.Try block Name.dollar $ JS.Throw $ JS.String $
237 "The following top-level definitions are causing infinite recursion:\\n"
238 <> drawCycle (map fst cycle)
239 <> "\\n\\nThese errors are very tricky, so read "
240 <> B.stringUtf8 (H.makeLink "halting-problem")
241 <> " to learn how to fix it!"
244generateSafeCycle :: Name.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
245generateSafeCycle mode home (name, expr) =
246 JS.FunctionStmt (Name.fromCycle home name) [] $
247 Expr.codeToStmtList (Expr.generate mode expr)
250generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
251generateRealCycle home (name, _) =
253 safeName = Name.fromCycle home name
254 realName = Name.fromGlobal home name
257 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
258 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
259 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
263drawCycle :: [N.Name] -> B.Builder
266 topLine = "\\n ┌─────┐"
267 nameLine name = "\\n │ " <> N.toBuilder name
269 bottomLine = "\\n └─────┘"
271 mconcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
279generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
280generateKernel mode chunks =
281 List.foldl' (addChunk mode) mempty chunks
284addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
285addChunk mode builder chunk =
288 B.byteString javascript <> builder
290 Opt.ElmVar home name ->
291 Name.toBuilder (Name.fromGlobal home name) <> builder
293 Opt.JsVar home name ->
294 Name.toBuilder (Name.fromKernel home name) <> builder
297 Name.toBuilder (Name.fromField mode name) <> builder
300 Name.toBuilder (Name.fromInt int) <> builder
303 B.intDec int <> builder
326generateEnum :: Name.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
327generateEnum mode (Opt.Global home name) ctorName index =
332 Expr.codeToExpr (Expr.generateCtor mode ctorName index 0)
335 JS.Int (Index.toMachine index)
337 JS.Var [ (Name.fromGlobal home name, Just definition) ]
344generateBox :: Name.Mode -> Opt.Global -> N.Name -> JS.Stmt
345generateBox mode (Opt.Global home name) ctorName =
350 Expr.codeToExpr (Expr.generateCtor mode ctorName Index.first 1)
353 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
355 JS.Var [ (Name.fromGlobal home name, Just definition) ]
362generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
363generatePort mode (Opt.Global home name) makePort converter =
366 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
367 [ JS.String (N.toBuilder name)
368 , Expr.codeToExpr (Expr.generate mode converter)
371 JS.Var [ (Name.fromGlobal home name, Just definition) ]
378generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
379generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
383 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
384 (JS.String (N.toBuilder moduleName))
386 (deps, args, stmts) =
387 generateManagerHelp home effectsType
390 JS.ExprStmt $ JS.Assign managerLVar $
391 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
393 addStmt (List.foldl' (addGlobal mode graph) state deps) $
394 JS.Block (createManager : stmts)
397generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
398generateLeaf home@(ModuleName.Canonical _ moduleName) name =
401 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
403 JS.Var [ (Name.fromGlobal home name, Just definition) ]
409 JS.Ref (Name.fromKernel N.platform "leaf")
412generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
413generateManagerHelp home effectsType =
415 dep name = Opt.Global home name
416 ref name = JS.Ref (Name.fromGlobal home name)
420 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
421 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
422 , [ generateLeaf home "command" ]
426 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
427 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
428 , [ generateLeaf home "subscription" ]
432 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
433 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
434 , [ generateLeaf home "command"
435 , generateLeaf home "subscription"
444toMainExports :: Name.Mode -> I.Interfaces -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
445toMainExports mode interfaces mains =
448 Name.fromKernel N.platform "export"
451 generateExports mode interfaces $
452 Map.foldrWithKey addToTrie emptyTrie mains
454 Name.toBuilder export <> "(" <> exports <> ");"
457generateExports :: Name.Mode -> I.Interfaces -> Trie -> B.Builder
458generateExports mode interfaces (Trie maybeMain subs) =
461 case Map.toList subs of
465 (name, subTrie) : otherSubTries ->
466 "{'" <> Text.encodeUtf8Builder name <> "':"
467 <> generateExports mode interfaces subTrie
468 <> List.foldl' (addSubTrie mode interfaces) "}" otherSubTries
475 let initialize = Expr.generateMain mode interfaces home main in
476 JS.exprToBuilder initialize <> "(" <> object <> ")"
479addSubTrie :: Name.Mode -> I.Interfaces -> B.Builder -> (Text.Text, Trie) -> B.Builder
480addSubTrie mode interfaces end (name, trie) =
481 ",'" <> Text.encodeUtf8Builder name <> "':"
482 <> generateExports mode interfaces trie
492 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
493 , _subs :: Map.Map Text.Text Trie
499 Trie Nothing Map.empty
502addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
503addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
504 merge trie $ segmentsToTrie home (Text.splitOn "." (N.toText moduleName)) main
507segmentsToTrie :: ModuleName.Canonical -> [Text.Text] -> Opt.Main -> Trie
508segmentsToTrie home segments main =
511 Trie (Just (home, main)) Map.empty
513 segment : otherSegments ->
514 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
517merge :: Trie -> Trie -> Trie
518merge (Trie main1 subs1) (Trie main2 subs2) =
520 (checkedMerge main1 main2)
521 (Map.unionWith merge subs1 subs2)
524checkedMerge :: Maybe a -> Maybe a -> Maybe a
534 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