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] -> Maybe 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.null rootMap then
52 realMode = toRealMode mode target fields
53 state = Map.foldrWithKey (addMain realMode graph) emptyState rootMap
55 Just $ stateToBuilder state <> toMainExports realMode interfaces rootMap
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 = P.displayS (P.renderPretty 1.0 80 (Type.toDoc Type.None (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 block = JS.Block (safeDefs ++ realDefs)
234 JS.Try block Name.dollar $ JS.Throw $ JS.String $
235 "The following top-level definitions are causing infinite recursion:\\n"
236 <> drawCycle (map fst cycle)
237 <> "\\n\\nThese errors are very tricky, so read "
238 <> B.stringUtf8 (H.makeLink "halting-problem")
239 <> " to learn how to fix it!"
242generateSafeCycle :: Name.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
243generateSafeCycle mode home (name, expr) =
244 JS.FunctionStmt (Name.fromCycle home name) [] $
245 Expr.codeToStmtList (Expr.generate mode expr)
248generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
249generateRealCycle home (name, _) =
251 safeName = Name.fromCycle home name
252 realName = Name.fromGlobal home name
255 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
256 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
257 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
261drawCycle :: [N.Name] -> B.Builder
264 topLine = "\\n ┌─────┐"
265 nameLine name = "\\n │ " <> N.toBuilder name
267 bottomLine = "\\n └─────┘"
269 mconcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
277generateKernel :: Name.Mode -> [Opt.KChunk] -> B.Builder
278generateKernel mode chunks =
279 List.foldl' (addChunk mode) mempty chunks
282addChunk :: Name.Mode -> B.Builder -> Opt.KChunk -> B.Builder
283addChunk mode builder chunk =
286 B.byteString javascript <> builder
288 Opt.ElmVar home name ->
289 Name.toBuilder (Name.fromGlobal home name) <> builder
291 Opt.JsVar home name ->
292 Name.toBuilder (Name.fromKernel home name) <> builder
295 Name.toBuilder (Name.fromField mode name) <> builder
298 Name.toBuilder (Name.fromInt int) <> builder
301 B.intDec int <> builder
324generateEnum :: Name.Mode -> Opt.Global -> N.Name -> Index.ZeroBased -> JS.Stmt
325generateEnum mode (Opt.Global home name) ctorName index =
330 Expr.codeToExpr (Expr.generateCtor mode ctorName index 0)
333 JS.Int (Index.toMachine index)
335 JS.Var [ (Name.fromGlobal home name, Just definition) ]
342generateBox :: Name.Mode -> Opt.Global -> N.Name -> JS.Stmt
343generateBox mode (Opt.Global home name) ctorName =
348 Expr.codeToExpr (Expr.generateCtor mode ctorName Index.first 1)
351 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
353 JS.Var [ (Name.fromGlobal home name, Just definition) ]
360generatePort :: Name.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
361generatePort mode (Opt.Global home name) makePort converter =
364 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
365 [ JS.String (N.toBuilder name)
366 , Expr.codeToExpr (Expr.generate mode converter)
369 JS.Var [ (Name.fromGlobal home name, Just definition) ]
376generateManager :: Name.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
377generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
381 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
382 (JS.String (N.toBuilder moduleName))
384 (deps, args, stmts) =
385 generateManagerHelp home effectsType
388 JS.ExprStmt $ JS.Assign managerLVar $
389 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
391 addStmt (List.foldl' (addGlobal mode graph) state deps) $
392 JS.Block (createManager : stmts)
395generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
396generateLeaf home@(ModuleName.Canonical _ moduleName) name =
399 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
401 JS.Var [ (Name.fromGlobal home name, Just definition) ]
407 JS.Ref (Name.fromKernel N.platform "leaf")
410generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
411generateManagerHelp home effectsType =
413 dep name = Opt.Global home name
414 ref name = JS.Ref (Name.fromGlobal home name)
418 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
419 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
420 , [ generateLeaf home "command" ]
424 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
425 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
426 , [ generateLeaf home "subscription" ]
430 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
431 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
432 , [ generateLeaf home "command"
433 , generateLeaf home "subscription"
442toMainExports :: Name.Mode -> I.Interfaces -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
443toMainExports mode interfaces mains =
446 Name.fromKernel N.platform "export"
449 generateExports mode interfaces $
450 Map.foldrWithKey addToTrie emptyTrie mains
452 Name.toBuilder export <> "(" <> exports <> ");"
455generateExports :: Name.Mode -> I.Interfaces -> Trie -> B.Builder
456generateExports mode interfaces (Trie maybeMain subs) =
459 case Map.toList subs of
463 (name, subTrie) : otherSubTries ->
464 "{'" <> Text.encodeUtf8Builder name <> "':"
465 <> generateExports mode interfaces subTrie
466 <> List.foldl' (addSubTrie mode interfaces) "}" otherSubTries
473 let initialize = Expr.generateMain mode interfaces home main in
474 JS.exprToBuilder initialize <> "(" <> object <> ")"
477addSubTrie :: Name.Mode -> I.Interfaces -> B.Builder -> (Text.Text, Trie) -> B.Builder
478addSubTrie mode interfaces end (name, trie) =
479 ",'" <> Text.encodeUtf8Builder name <> "':"
480 <> generateExports mode interfaces trie
490 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
491 , _subs :: Map.Map Text.Text Trie
497 Trie Nothing Map.empty
500addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
501addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
502 merge trie $ segmentsToTrie home (Text.splitOn "." (N.toText moduleName)) main
505segmentsToTrie :: ModuleName.Canonical -> [Text.Text] -> Opt.Main -> Trie
506segmentsToTrie home segments main =
509 Trie (Just (home, main)) Map.empty
511 segment : otherSegments ->
512 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
515merge :: Trie -> Trie -> Trie
516merge (Trie main1 subs1) (Trie main2 subs2) =
518 (checkedMerge main1 main2)
519 (Map.unionWith merge subs1 subs2)
522checkedMerge :: Maybe a -> Maybe a -> Maybe a
532 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