1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript
10import Prelude hiding (cycle, print)
11import qualified Data.ByteString.Builder as B
12import Data.Monoid ((<>))
13import qualified Data.List as List
15import qualified Data.Map as Map
16import qualified Data.Set as Set
17import qualified Data.Text as Text
18import qualified Data.Text.Encoding as Text
20import qualified AST.Canonical as Can
21import qualified AST.Optimized as Opt
22import qualified AST.Module.Name as ModuleName
23import qualified Data.Index as Index
24import qualified Elm.Interface as I
25import qualified Elm.Name as N
26import qualified Generate.JavaScript.Builder as JS
27import qualified Generate.JavaScript.Expression as Expr
28import qualified Generate.JavaScript.Name as Name
29import qualified Generate.JavaScript.Mode as Mode
30import qualified Reporting.Doc as D
31import qualified Reporting.Render.Type as RT
32import qualified Reporting.Render.Type.Localizer as L
41 | Some N.Name [N.Name] B.Builder
44generate :: Mode.Mode -> Opt.Graph -> [ModuleName.Canonical] -> Output
45generate mode (Opt.Graph mains graph _fields) roots =
47 rootSet = Set.fromList roots
48 rootMap = Map.restrictKeys mains rootSet
50 case map ModuleName._module (Map.keys rootMap) of
56 state = Map.foldrWithKey (addMain mode graph) emptyState rootMap
57 builder = stateToBuilder state <> toMainExports mode rootMap
59 Some name names builder
62addMain :: Mode.Mode -> Graph -> ModuleName.Canonical -> main -> State -> State
63addMain mode graph home _ state =
64 addGlobal mode graph state (Opt.Global home "main")
71generateForRepl :: Bool -> L.Localizer -> Opt.Graph -> I.Interface -> ModuleName.Canonical -> N.Name -> B.Builder
72generateForRepl ansi localizer (Opt.Graph _ graph _) iface home name =
74 mode = Mode.dev Mode.Client
75 debugState = addGlobal mode graph emptyState (Opt.Global ModuleName.debug "toString")
76 evalState = addGlobal mode graph debugState (Opt.Global home name)
78 stateToBuilder evalState
80 print ansi localizer home name (I._types iface ! name)
83print :: Bool -> L.Localizer -> ModuleName.Canonical -> N.Name -> Can.Annotation -> B.Builder
84print ansi localizer home name (Can.Forall _ tipe) =
86 value = Name.toBuilder (Name.fromGlobal home name)
87 toString = Name.toBuilder (Name.fromKernel N.debug "toAnsiString")
88 tipeDoc = RT.canToDoc localizer RT.None tipe
89 bool = if ansi then "true" else "false"
91 "var _value = " <> toString <> "(" <> bool <> ", " <> value <> ");\n\
92 \var _type = " <> B.stringUtf8 (show (D.toString tipeDoc)) <> ";\n\
93 \function _print(t) { console.log(_value + (" <> bool <> " ? '\x1b[90m' + t + '\x1b[0m' : t)); }\n\
94 \if (_value.length + 3 + _type.length >= 80 || _type.indexOf('\\n') >= 0) {\n\
95 \ _print('\\n : ' + _type.split('\\n').join('\\n '));\n\
97 \ _print(' : ' + _type);\n\
102-- GRAPH TRAVERSAL STATE
107 { _kernel :: B.Builder
108 , _revBuilders :: [B.Builder]
109 , _seenGlobals :: Set.Set Opt.Global
115 State mempty [] Set.empty
118stateToBuilder :: State -> B.Builder
119stateToBuilder (State kernel revBuilders _) =
125 kernel <> List.foldl' (\code decl -> decl <> code) b bs
132type Graph = Map.Map Opt.Global Opt.Node
135addGlobal :: Mode.Mode -> Graph -> State -> Opt.Global -> State
136addGlobal mode graph state@(State kernel builders seen) global =
137 if Set.member global seen then
140 addGlobalHelp mode graph global $
141 State kernel builders (Set.insert global seen)
144addGlobalHelp :: Mode.Mode -> Graph -> Opt.Global -> State -> State
145addGlobalHelp mode graph global state =
147 addDeps deps someState =
148 Set.foldl' (addGlobal mode graph) someState deps
150 case graph ! global of
151 Opt.Define expr deps ->
152 addStmt (addDeps deps state) (
153 var global (Expr.generate mode expr)
156 Opt.DefineTailFunc argNames body deps ->
157 addStmt (addDeps deps state) (
158 let (Opt.Global _ name) = global in
159 var global (Expr.generateTailDef mode name argNames body)
162 Opt.Ctor index arity ->
164 var global (Expr.generateCtor mode global index arity)
167 Opt.Link linkedGlobal ->
168 addGlobal mode graph state linkedGlobal
170 Opt.Cycle cycle deps ->
171 addStmt (addDeps deps state) (
172 generateCycle mode global cycle
175 Opt.Manager effectsType ->
176 generateManager mode graph global effectsType state
178 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
179 if isDebugger global && not (Mode.isDebug mode) then
183 Just (Opt.KContent serverChunks serverDeps) | Mode.isServer mode ->
184 addKernel (addDeps serverDeps state) (generateKernel mode serverChunks)
187 addKernel (addDeps clientDeps state) (generateKernel mode clientChunks)
191 generateEnum mode global index
196 generateBox mode global
199 Opt.PortIncoming decoder deps ->
200 addStmt (addDeps deps state) (
201 generatePort mode global "incomingPort" decoder
204 Opt.PortOutgoing encoder deps ->
205 addStmt (addDeps deps state) (
206 generatePort mode global "outgoingPort" encoder
210addStmt :: State -> JS.Stmt -> State
212 addBuilder state (JS.stmtToBuilder stmt)
215addBuilder :: State -> B.Builder -> State
216addBuilder (State kernel revBuilders seen) builder =
217 State kernel (builder:revBuilders) seen
220addKernel :: State -> B.Builder -> State
221addKernel (State kernel revBuilders seen) moreKernel =
222 State (moreKernel <> kernel) revBuilders seen
225var :: Opt.Global -> Expr.Code -> JS.Stmt
226var (Opt.Global home name) code =
227 JS.Var [ (Name.fromGlobal home name, Just (Expr.codeToExpr code)) ]
230isDebugger :: Opt.Global -> Bool
231isDebugger (Opt.Global (ModuleName.Canonical _ home) _) =
239generateCycle :: Mode.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
240generateCycle mode (Opt.Global home _) cycle =
242 safeDefs = map (generateSafeCycle mode home) cycle
243 realDefs = map (generateRealCycle home) cycle
244 block = JS.Block (safeDefs ++ realDefs)
251 JS.Try block Name.dollar $ JS.Throw $ JS.String $
252 "The following top-level definitions are causing infinite recursion:\\n"
253 <> drawCycle (map fst cycle)
254 <> "\\n\\nThese errors are very tricky, so read "
255 <> B.stringUtf8 (D.makeLink "halting-problem")
256 <> " to learn how to fix it!"
259generateSafeCycle :: Mode.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
260generateSafeCycle mode home (name, expr) =
261 JS.FunctionStmt (Name.fromCycle home name) [] $
262 Expr.codeToStmtList (Expr.generate mode expr)
265generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
266generateRealCycle home (name, _) =
268 safeName = Name.fromCycle home name
269 realName = Name.fromGlobal home name
272 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
273 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
274 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
278drawCycle :: [N.Name] -> B.Builder
281 topLine = "\\n ┌─────┐"
282 nameLine name = "\\n │ " <> N.toBuilder name
284 bottomLine = "\\n └─────┘"
286 mconcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
294generateKernel :: Mode.Mode -> [Opt.KChunk] -> B.Builder
295generateKernel mode chunks =
296 List.foldl' (addChunk mode) mempty chunks
299addChunk :: Mode.Mode -> B.Builder -> Opt.KChunk -> B.Builder
300addChunk mode builder chunk =
303 B.byteString javascript <> builder
305 Opt.ElmVar home name ->
306 Name.toBuilder (Name.fromGlobal home name) <> builder
308 Opt.JsVar home name ->
309 Name.toBuilder (Name.fromKernel home name) <> builder
312 Name.toBuilder (Expr.generateField mode name) <> builder
315 Name.toBuilder (Name.fromInt int) <> builder
318 B.intDec int <> builder
341generateEnum :: Mode.Mode -> Opt.Global -> Index.ZeroBased -> JS.Stmt
342generateEnum mode global@(Opt.Global home name) index =
347 Expr.codeToExpr (Expr.generateCtor mode global index 0)
350 JS.Int (Index.toMachine index)
352 JS.Var [ (Name.fromGlobal home name, Just definition) ]
359generateBox :: Mode.Mode -> Opt.Global -> JS.Stmt
360generateBox mode global@(Opt.Global home name) =
365 Expr.codeToExpr (Expr.generateCtor mode global Index.first 1)
368 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
370 JS.Var [ (Name.fromGlobal home name, Just definition) ]
377generatePort :: Mode.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
378generatePort mode (Opt.Global home name) makePort converter =
381 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
382 [ JS.String (N.toBuilder name)
383 , Expr.codeToExpr (Expr.generate mode converter)
386 JS.Var [ (Name.fromGlobal home name, Just definition) ]
393generateManager :: Mode.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
394generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
398 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
399 (JS.String (N.toBuilder moduleName))
401 (deps, args, stmts) =
402 generateManagerHelp home effectsType
405 JS.ExprStmt $ JS.Assign managerLVar $
406 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
408 addStmt (List.foldl' (addGlobal mode graph) state deps) $
409 JS.Block (createManager : stmts)
412generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
413generateLeaf home@(ModuleName.Canonical _ moduleName) name =
416 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
418 JS.Var [ (Name.fromGlobal home name, Just definition) ]
424 JS.Ref (Name.fromKernel N.platform "leaf")
427generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
428generateManagerHelp home effectsType =
430 dep name = Opt.Global home name
431 ref name = JS.Ref (Name.fromGlobal home name)
435 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
436 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
437 , [ generateLeaf home "command" ]
441 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
442 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
443 , [ generateLeaf home "subscription" ]
447 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
448 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
449 , [ generateLeaf home "command"
450 , generateLeaf home "subscription"
459toMainExports :: Mode.Mode -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
460toMainExports mode mains =
462 export = Name.fromKernel N.platform "export"
463 exports = generateExports mode (Map.foldrWithKey addToTrie emptyTrie mains)
465 Name.toBuilder export <> "(" <> exports <> ");"
468generateExports :: Mode.Mode -> Trie -> B.Builder
469generateExports mode (Trie maybeMain subs) =
472 case Map.toList subs of
476 (name, subTrie) : otherSubTries ->
477 "{'" <> Text.encodeUtf8Builder name <> "':"
478 <> generateExports mode subTrie
479 <> List.foldl' (addSubTrie mode) "}" otherSubTries
486 let initialize = Expr.generateMain mode home main in
487 JS.exprToBuilder initialize <> "(" <> object <> ")"
490addSubTrie :: Mode.Mode -> B.Builder -> (Text.Text, Trie) -> B.Builder
491addSubTrie mode end (name, trie) =
492 ",'" <> Text.encodeUtf8Builder name <> "':" <> generateExports mode trie <> end
501 { _main :: Maybe (ModuleName.Canonical, Opt.Main)
502 , _subs :: Map.Map Text.Text Trie
508 Trie Nothing Map.empty
511addToTrie :: ModuleName.Canonical -> Opt.Main -> Trie -> Trie
512addToTrie home@(ModuleName.Canonical _ moduleName) main trie =
513 merge trie $ segmentsToTrie home (Text.splitOn "." (N.toText moduleName)) main
516segmentsToTrie :: ModuleName.Canonical -> [Text.Text] -> Opt.Main -> Trie
517segmentsToTrie home segments main =
520 Trie (Just (home, main)) Map.empty
522 segment : otherSegments ->
523 Trie Nothing (Map.singleton segment (segmentsToTrie home otherSegments main))
526merge :: Trie -> Trie -> Trie
527merge (Trie main1 subs1) (Trie main2 subs2) =
529 (checkedMerge main1 main2)
530 (Map.unionWith merge subs1 subs2)
533checkedMerge :: Maybe a -> Maybe a -> Maybe a
543 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