1module Generate.JavaScript (showErr, jsModule) where
3import Control.Arrow (first,second)
4import Control.Monad (liftM,(<=<),join,ap)
5import Data.Char (isAlpha,isDigit)
6import Data.List (intercalate,inits,foldl')
7import qualified Data.Map as Map
8import qualified Data.Set as Set
9import Data.Either (partitionEithers)
10import qualified Text.Pandoc as Pan
14import SourceSyntax.Everything hiding (parens)
15import SourceSyntax.Location as Loc
16import qualified Transform.SortDefinitions as SD
18showErr :: String -> String
19showErr err = globalAssign "Elm.Main" (jsFunc "elm" body)
20 where msg = show . concatMap (++"<br>") . lines $ err
21 body = "var T = Elm.Text(elm);\n\
22 \return { main : T.text(T.monospace(" ++ msg ++ ")) };"
29 [ ("N" , "Elm.Native"),
30 ("_N", "N.Utils(elm)"),
31 ("_L", "N.List(elm)"),
32 ("_E", "N.Error(elm)"),
33 ("_J", "N.JavaScript(elm)"),
34 ("_str", "_J.toString")
37parens s = "(" ++ s ++ ")"
38brackets s = "{" ++ s ++ "}"
39jsObj = brackets . intercalate ", "
40jsList ss = "["++ intercalate "," ss ++"]"
41jsFunc args body = "function(" ++ args ++ "){" ++ indent body ++ "}"
42assign x e = "\nvar " ++ x ++ " = " ++ e ++ ";"
43ret e = "\nreturn "++ e ++";"
44quoted s = "'" ++ concatMap f s ++ "'"
52globalAssign n e = "\n" ++ assign' n e ++ ";"
53assign' n e = n ++ " = " ++ e
55jsModule :: MetadataModule t v -> String
58 body <- toJS . fst . SD.flattenLets [] $ program modul
59 foreignImport <- mapM importEvent (foreignImports modul)
60 return $ concat [ setup ("Elm": names modul)
61 , globalAssign ("Elm." ++ modName)
62 (jsFunc "elm" $ makeProgram body foreignImport) ]
64 modName = intercalate "." (names modul)
65 makeProgram body foreignImport =
66 concat [ "\nvar " ++ usefulFuncs ++ ";"
68 , concatMap jsImport (imports modul)
69 , concat foreignImport
70 , concatMap exportEvent $ foreignExports modul
73 , concatMap jsExport (exports modul)
74 , setup ("elm" : names modul)
75 , ret (assign' ("elm." ++ modName) "_")
77 setup names = concatMap (\n -> globalAssign n $ n ++ "||{}") .
78 map (intercalate ".") . drop 2 . inits $ init names
79 usefulFuncs = intercalate ", " (map (uncurry assign') internalImports)
82 if isOp x then "\n_._op['" ++ x ++ "'] = _op['" ++ x ++ "'];"
83 else "\n_." ++ derename x ++ " = " ++ x ++ ";"
85 importEvent (js,base,elm,_) =
87 return $ concat [ "\nvar " ++ elm ++ "=Elm.Signal(elm).constant(" ++ v ++ ");"
88 , "\ndocument.addEventListener('", js
89 , "_' + elm.id, function(e) { elm.notify(", elm
90 , ".id, e.value); });" ]
92 exportEvent (js,elm,_) =
93 concat [ "\nlift(function(v) { "
94 , "var e = document.createEvent('Event');"
95 , "e.initEvent('", js, "_' + elm.id, true, true);"
97 , "document.dispatchEvent(e); return v; })(", elm, ");" ]
99jsImport (modul, how) =
101 As name -> assign name ("Elm." ++ modul ++ parens "elm")
102 Hiding vs -> include ++ " var hiding=" ++ (jsObj $ map (++":1") vs) ++
103 "; for(var k in _){if(k in hiding)continue;" ++
104 "eval('var '+k+'=_[\"'+k+'\"]')}"
105 Importing vs -> include ++ named
107 imprt v = assign' v ("_." ++ v)
108 def x = imprt $ if isOp x then "_op['" ++ x ++ "']" else deprime x
109 named = if null vs then "" else "\nvar " ++ intercalate ", " (map def vs) ++ ";"
111 include = "\nvar _ = Elm." ++ modul ++ parens "elm" ++ ";" ++ setup modul
112 setup moduleName = " var " ++ concatMap (++";") (defs ++ [assign' moduleName "_"])
114 defs = map (\n -> assign' n (n ++ "||{}")) (subnames moduleName)
115 subnames = map (intercalate ".") . tail . inits . init . split
116 split names = case go [] names of
118 (name, ns) -> name : split ns
119 go name str = case str of
120 '.':rest -> (reverse name, rest)
121 c:rest -> go (c:name) rest
122 [] -> (reverse name, [])
126 toJS :: a -> Unique String
129instance ToJS (Def t v) where
131 -- TODO: Make this handle patterns besides plain variables
132 toJS (Def (PVar x) e)
133 | isOp x = globalAssign ("_op['" ++ x ++ "']") `liftM` toJS' e
134 | otherwise = assign x `liftM` toJS' e
136 toJS (Def pattern e) =
138 let x = "_" ++ show n
140 toDef y = Def (PVar y) (Loc.none $ Case (var x) [(pattern, var y)])
141 stmt <- assign x `liftM` toJS' e
142 vars <- toJS . map toDef . Set.toList $ SD.boundVars pattern
143 return (stmt ++ vars)
145 toJS (TypeAnnotation _ _) = return ""
148instance ToJS a => ToJS [a] where
149 toJS xs = concat `liftM` mapM toJS xs
151toJS' :: LExpr t v -> Unique String
152toJS' (L txt span expr) =
154 MultiIf ps -> multiIfToJS span ps
155 Case e cases -> caseToJS span e cases
158remove x e = "_N.remove('" ++ x ++ "', " ++ e ++ ")"
159addField x v e = "_N.insert('" ++ x ++ "', " ++ v ++ ", " ++ e ++ ")"
160setField fs e = "_N.replace(" ++ jsList (map f fs) ++ ", " ++ e ++ ")"
161 where f (x,v) = "['" ++ x ++ "'," ++ v ++ "]"
162access x e = e ++ "." ++ x
163makeRecord kvs = record `liftM` collect kvs
165 combine r (k,v) = Map.insertWith (++) k v r
166 collect = liftM (foldl' combine Map.empty) . mapM prep
171 brackets ("\n "++intercalate ",\n " (map (\(k,v) -> k++":"++v) fs))
172 hidden = fields . map (second jsList) .
173 filter (not . null . snd) . Map.toList . Map.map tail
174 record kvs = fields . (("_", hidden kvs) :) . Map.toList . Map.map head $ kvs
177instance ToJS Literal where
180 Chr c -> return $ quoted [c]
181 Str s -> return $ "_str" ++ parens (quoted s)
182 IntNum n -> return $ show n
183 FloatNum n -> return $ show n
184 Boolean b -> return $ if b then "true" else "false"
187instance ToJS (Expr t v) where
191 Literal lit -> toJS lit
192 Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
193 Access e x -> access x `liftM` toJS' e
194 Remove e x -> remove x `liftM` toJS' e
195 Insert e x v -> addField x `liftM` toJS' v `ap` toJS' e
196 Modify e fs -> do fs' <- (mapM (\(x,v) -> (,) x `liftM` toJS' v) fs)
197 setField fs' `liftM` toJS' e
198 Record fs -> makeRecord fs
199 Binop op e1 e2 -> binop op `liftM` toJS' e1 `ap` toJS' e2
201 Lambda p e -> liftM (jsFunc (intercalate ", " args) . ret) (toJS' body)
203 (args, body) = foldr depattern ([], innerBody) (zip patterns [1..])
205 depattern (pattern,n) (args, body) =
207 PVar x -> (x:args, body)
208 _ -> let arg = "arg" ++ show n
209 in (arg:args, Loc.none (Case (Loc.none (Var arg)) [(pattern, body)]))
211 (patterns, innerBody) = collect [p] e
213 collect patterns lexpr@(L a b expr) =
215 Lambda p e -> collect (p:patterns) e
216 _ -> (patterns, lexpr)
218 App e1 e2 -> jsApp e1 e2
219 Let defs e -> jsLet $ SD.flattenLets defs e
222 do es' <- mapM toJS' es
223 return $ "_J.toList" ++ parens (jsList es')
226 do fs <- mapM toJS' es
227 return $ case name of
229 "::" -> jsCons (head fs) ((head . tail) fs)
230 _ -> jsObj $ ("ctor:" ++ show name) : fields
231 where fields = zipWith (\n e -> "_" ++ show n ++ ":" ++ e) [0..] fs
233 Markdown doc -> return $ "text('" ++ pad ++ md ++ pad ++ "')"
234 where pad = "<div style=\"height:0;width:0;\"> </div>"
235 md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
239 as <- mapM toJS' args
242 _ -> "A" ++ show (length as) ++ parens (intercalate ", " (f:as))
244 (func, args) = go [e2] e1
247 (L _ _ (App e1 e2)) -> go (e2 : args) e1
250formatMarkdown = concatMap f
258 (L _ _ (Var "otherwise"), e) -> toJS' e >>= \b -> format b (init ps)
259 _ -> format ("_E.If" ++ parens (quoted (show span))) ps
262 foldr (\c e -> parens $ c ++ " : " ++ e) base `liftM` mapM f ps
263 f (b,e) = do b' <- toJS' b
265 return (b' ++ " ? " ++ e')
267jsLet (defs,e') = do ds <- mapM toJS defs
269 return $ jsFunc "" (concat ds ++ ret e) ++ "()"
271caseToJS span e ps = do
272 match <- caseToMatch ps
274 let (match',stmt) = case (match,e) of
275 (Match name _ _, L _ _ (Var x)) -> (matchSubst [(name,x)] match, "")
276 (Match name _ _, _) -> (match, assign name e')
278 matches <- matchToJS span match'
279 return $ "function(){ " ++ stmt ++ matches ++ " }()"
281matchToJS span match =
283 Match name clauses def ->
284 do cases <- concat `liftM` mapM (clauseToJS span name) clauses
285 finally <- matchToJS span def
286 let isLiteral p = case p of
287 Clause (Right _) _ _ -> True
289 access = if any isLiteral clauses then "" else ".ctor"
290 return $ concat [ "\nswitch (", name, access, ") {"
291 , indent cases, "\n}", finally ]
292 Fail -> return ("_E.Case" ++ parens (quoted (show span)))
293 Break -> return "break;"
294 Other e -> ret `liftM` toJS' e
295 Seq ms -> concat `liftM` mapM (matchToJS span) (dropEnd [] ms)
300 Other _ -> acc ++ [m]
301 _ -> dropEnd (acc ++ [m]) ms
303clauseToJS span var (Clause value vars e) = do
304 let vars' = map (\n -> var ++ "._" ++ show n) [0..]
305 s <- matchToJS span $ matchSubst (zip vars vars') e
306 return $ concat [ "\ncase ", case value of
307 Left name -> quoted name
308 Right (Boolean True) -> "true"
309 Right (Boolean False) -> "false"
310 Right lit -> show lit
314jsCons e1 e2 = "_L.Cons(" ++ e1 ++ "," ++ e2 ++ ")"
315jsRange e1 e2 = "_L.range" ++ parens (e1 ++ "," ++ e2)
316jsCompare e1 e2 op = parens ("_N.cmp(" ++ e1 ++ "," ++ e2 ++ ").ctor" ++ op)
319 | isAlpha o || '_' == o = (o:p) ++ parens e1 ++ parens e2
321 let ops = ["+","-","*","/","&&","||"] in
324 "++" -> "_L.append" ++ parens (e1 ++ "," ++ e2)
325 "<|" -> e1 ++ parens e2
326 "|>" -> e2 ++ parens e1
327 "." -> jsFunc "x" . ret $ e1 ++ parens (e2 ++ parens "x")
328 "^" -> "Math.pow(" ++ e1 ++ "," ++ e2 ++ ")"
329 "==" -> "_N.eq(" ++ e1 ++ "," ++ e2 ++ ")"
330 "/=" -> "!_N.eq(" ++ e1 ++ "," ++ e2 ++ ")"
331 "<" -> jsCompare e1 e2 "==='LT'"
332 ">" -> jsCompare e1 e2 "==='GT'"
333 "<=" -> jsCompare e1 e2 "!=='GT'"
334 ">=" -> jsCompare e1 e2 "!=='LT'"
335 "<~" -> "A2(lift," ++ e1 ++ "," ++ e2 ++ ")"
336 "~" -> "A3(lift2,F2(function(f,x){return f(x)}),"++e1++","++e2++")"
337 _ | elem (o:p) ops -> parens (e1 ++ (o:p) ++ e2)
338 | otherwise -> concat [ "_op['", o:p, "']"
339 , parens e1, parens e2 ]
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