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 { _revBuilders :: [B.Builder]
108 , _seenGlobals :: Set.Set Opt.Global
117stateToBuilder :: State -> B.Builder
118stateToBuilder (State revBuilders _) =
119 List.foldl1' (\builder b -> b <> builder) revBuilders
126type Graph = Map.Map Opt.Global Opt.Node
129addGlobal :: Mode.Mode -> Graph -> State -> Opt.Global -> State
130addGlobal mode graph state@(State builders seen) global =
131 if Set.member global seen then
134 addGlobalHelp mode graph global $
135 State builders (Set.insert global seen)
138addGlobalHelp :: Mode.Mode -> Graph -> Opt.Global -> State -> State
139addGlobalHelp mode graph global state =
141 addDeps deps someState =
142 Set.foldl' (addGlobal mode graph) someState deps
144 case graph ! global of
145 Opt.Define expr deps ->
146 addStmt (addDeps deps state) (
147 var global (Expr.generate mode expr)
150 Opt.DefineTailFunc argNames body deps ->
151 addStmt (addDeps deps state) (
152 let (Opt.Global _ name) = global in
153 var global (Expr.generateTailDef mode name argNames body)
156 Opt.Ctor index arity ->
158 var global (Expr.generateCtor mode global index arity)
161 Opt.Link linkedGlobal ->
162 addGlobal mode graph state linkedGlobal
164 Opt.Cycle cycle deps ->
165 addStmt (addDeps deps state) (
166 generateCycle mode global cycle
169 Opt.Manager effectsType ->
170 generateManager mode graph global effectsType state
172 Opt.Kernel (Opt.KContent clientChunks clientDeps) maybeServer ->
173 if isDebugger global && not (Mode.isDebug mode) then
177 Just (Opt.KContent serverChunks serverDeps) | Mode.isServer mode ->
178 addBuilder (addDeps serverDeps state) (generateKernel mode serverChunks)
181 addBuilder (addDeps clientDeps state) (generateKernel mode clientChunks)
185 generateEnum mode global index
190 generateBox mode global
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)) ]
219isDebugger :: Opt.Global -> Bool
220isDebugger (Opt.Global (ModuleName.Canonical _ home) _) =
228generateCycle :: Mode.Mode -> Opt.Global -> [(N.Name, Opt.Expr)] -> JS.Stmt
229generateCycle mode (Opt.Global home _) cycle =
231 safeDefs = map (generateSafeCycle mode home) cycle
232 realDefs = map (generateRealCycle home) cycle
233 block = JS.Block (safeDefs ++ realDefs)
240 JS.Try block Name.dollar $ JS.Throw $ JS.String $
241 "The following top-level definitions are causing infinite recursion:\\n"
242 <> drawCycle (map fst cycle)
243 <> "\\n\\nThese errors are very tricky, so read "
244 <> B.stringUtf8 (D.makeLink "halting-problem")
245 <> " to learn how to fix it!"
248generateSafeCycle :: Mode.Mode -> ModuleName.Canonical -> (N.Name, Opt.Expr) -> JS.Stmt
249generateSafeCycle mode home (name, expr) =
250 JS.FunctionStmt (Name.fromCycle home name) [] $
251 Expr.codeToStmtList (Expr.generate mode expr)
254generateRealCycle :: ModuleName.Canonical -> (N.Name, expr) -> JS.Stmt
255generateRealCycle home (name, _) =
257 safeName = Name.fromCycle home name
258 realName = Name.fromGlobal home name
261 [ JS.Var [ ( realName, Just (JS.Call (JS.Ref safeName) []) ) ]
262 , JS.ExprStmt $ JS.Assign (JS.LRef safeName) $
263 JS.Function Nothing [] [ JS.Return (Just (JS.Ref realName)) ]
267drawCycle :: [N.Name] -> B.Builder
270 topLine = "\\n ┌─────┐"
271 nameLine name = "\\n │ " <> N.toBuilder name
273 bottomLine = "\\n └─────┘"
275 mconcat (topLine : List.intersperse midLine (map nameLine names) ++ [ bottomLine ])
283generateKernel :: Mode.Mode -> [Opt.KChunk] -> B.Builder
284generateKernel mode chunks =
285 List.foldl' (addChunk mode) mempty chunks
288addChunk :: Mode.Mode -> B.Builder -> Opt.KChunk -> B.Builder
289addChunk mode builder chunk =
292 B.byteString javascript <> builder
294 Opt.ElmVar home name ->
295 Name.toBuilder (Name.fromGlobal home name) <> builder
297 Opt.JsVar home name ->
298 Name.toBuilder (Name.fromKernel home name) <> builder
301 Name.toBuilder (Expr.generateField mode name) <> builder
304 Name.toBuilder (Name.fromInt int) <> builder
307 B.intDec int <> builder
330generateEnum :: Mode.Mode -> Opt.Global -> Index.ZeroBased -> JS.Stmt
331generateEnum mode global@(Opt.Global home name) index =
336 Expr.codeToExpr (Expr.generateCtor mode global index 0)
339 JS.Int (Index.toMachine index)
341 JS.Var [ (Name.fromGlobal home name, Just definition) ]
348generateBox :: Mode.Mode -> Opt.Global -> JS.Stmt
349generateBox mode global@(Opt.Global home name) =
354 Expr.codeToExpr (Expr.generateCtor mode global Index.first 1)
357 JS.Ref (Name.fromGlobal ModuleName.basics N.identity)
359 JS.Var [ (Name.fromGlobal home name, Just definition) ]
366generatePort :: Mode.Mode -> Opt.Global -> N.Name -> Opt.Expr -> JS.Stmt
367generatePort mode (Opt.Global home name) makePort converter =
370 JS.Call (JS.Ref (Name.fromKernel N.platform makePort))
371 [ JS.String (N.toBuilder name)
372 , Expr.codeToExpr (Expr.generate mode converter)
375 JS.Var [ (Name.fromGlobal home name, Just definition) ]
382generateManager :: Mode.Mode -> Graph -> Opt.Global -> Opt.EffectsType -> State -> State
383generateManager mode graph (Opt.Global home@(ModuleName.Canonical _ moduleName) _) effectsType state =
387 (JS.Ref (Name.fromKernel N.platform "effectManagers"))
388 (JS.String (N.toBuilder moduleName))
390 (deps, args, stmts) =
391 generateManagerHelp home effectsType
394 JS.ExprStmt $ JS.Assign managerLVar $
395 JS.Call (JS.Ref (Name.fromKernel N.platform "createManager")) args
397 addStmt (List.foldl' (addGlobal mode graph) state deps) $
398 JS.Block (createManager : stmts)
401generateLeaf :: ModuleName.Canonical -> N.Name -> JS.Stmt
402generateLeaf home@(ModuleName.Canonical _ moduleName) name =
405 JS.Call leaf [ JS.String (N.toBuilder moduleName) ]
407 JS.Var [ (Name.fromGlobal home name, Just definition) ]
413 JS.Ref (Name.fromKernel N.platform "leaf")
416generateManagerHelp :: ModuleName.Canonical -> Opt.EffectsType -> ([Opt.Global], [JS.Expr], [JS.Stmt])
417generateManagerHelp home effectsType =
419 dep name = Opt.Global home name
420 ref name = JS.Ref (Name.fromGlobal home name)
424 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap" ]
425 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap" ]
426 , [ generateLeaf home "command" ]
430 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "subMap" ]
431 , [ ref "init", ref "onEffects", ref "onSelfMsg", JS.Int 0, ref "subMap" ]
432 , [ generateLeaf home "subscription" ]
436 ( [ dep "init", dep "onEffects", dep "onSelfMsg", dep "cmdMap", dep "subMap" ]
437 , [ ref "init", ref "onEffects", ref "onSelfMsg", ref "cmdMap", ref "subMap" ]
438 , [ generateLeaf home "command"
439 , generateLeaf home "subscription"
448toMainExports :: Mode.Mode -> Map.Map ModuleName.Canonical Opt.Main -> B.Builder
449toMainExports mode mains =
451 export = Name.fromKernel N.platform "export"
452 exports = generateExports mode (Map.foldrWithKey addToTrie emptyTrie mains)
454 Name.toBuilder export <> "(" <> exports <> ");"
457generateExports :: Mode.Mode -> Trie -> B.Builder
458generateExports mode (Trie maybeMain subs) =
461 case Map.toList subs of
465 (name, subTrie) : otherSubTries ->
466 "{'" <> Text.encodeUtf8Builder name <> "':"
467 <> generateExports mode subTrie
468 <> List.foldl' (addSubTrie mode) "}" otherSubTries
475 let initialize = Expr.generateMain mode home main in
476 JS.exprToBuilder initialize <> "(" <> object <> ")"
479addSubTrie :: Mode.Mode -> B.Builder -> (Text.Text, Trie) -> B.Builder
480addSubTrie mode end (name, trie) =
481 ",'" <> Text.encodeUtf8Builder name <> "':" <> generateExports mode trie <> end
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