1module Generate.JavaScript where
3import Control.Applicative ((<$>))
4import Data.Char (isAlpha,isDigit)
5import qualified Data.List as List
6import qualified Data.Map as Map
7import qualified Data.Set as Set
8import Data.Either (partitionEithers)
12import SourceSyntax.Everything
13import SourceSyntax.Location
14import qualified Transform.SortDefinitions as SD
15import Language.ECMAScript3.Syntax
16import Language.ECMAScript3.PrettyPrint
17import Parse.Helpers (makeSafe, iParse)
19import Parse.Expression
21testExpr str = case iParse expr "" str of
22 Right e -> prettyPrint (expression e)
23 Left err -> error (show err)
25var name = Id () (makeSafe name)
26ref name = VarRef () (var name)
27prop name = PropId () (var name)
28f <| x = CallExpr () f [x]
29args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
33dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
36 VarDecl () (var x) (Just expr)
38include alias moduleName =
39 varDecl alias (dotSep moduleName <| ref "elm")
43 [ varDecl "N" (dotSep ["Elm","Native"])
44 , include "_N" ["N","Utils"]
45 , include "_L" ["N","List"]
46 , include "_E" ["N","Error"]
47 , include "_J" ["N","JavaScript"]
48 , varDecl "_str" (dotSep ["_J","toString"])
49 , varDecl "$moduleName" (string name)
55 Str s -> ref "_str" <| string s
56 IntNum n -> IntLit () n
57-- FloatNum n -> NumLit () (float2Double n) -- wrong!!!
58 Boolean b -> BoolLit () b
60expression (L span expr) =
63 Literal lit -> literal lit
64 Range lo hi -> dotSep ["_L","range"] `call` [expression lo, expression hi]
66 Access e x -> DotRef () (expression e) (var x)
67 Remove e x -> dotSep ["_N","remove"] `call` [ref x, expression e]
68 Insert e x v -> dotSep ["_N","insert"] `call` [ref x, expression v, expression e]
70 let modify (f,v) = ArrayLit () [ref f, expression e]
71 in dotSep ["_N","replace"] `call` [ArrayLit () (map modify fs), expression e]
74 Record fs -> makeRecord fs
75makeRecord kvs = record `liftM` collect kvs
77 combine r (k,v) = Map.insertWith (++) k v r
78 collect = liftM (List.foldl' combine Map.empty) . mapM prep
83 brackets ("\n "++List.intercalate ",\n " (map (\(k,v) -> k++":"++v) fs))
84 hidden = fields . map (second jsList) .
85 filter (not . null . snd) . Map.toList . Map.map tail
86 record kvs = fields . (("_", hidden kvs) :) . Map.toList . Map.map head $ kvs
88 Binop op e1 e2 -> binop span op e1 e2
90 Lambda p e@(L s _) -> fastFunc
93 | length args < 2 || length args > 9 =
94 foldr (==>) (expression body) (map (:[]) args)
96 ref ("F" ++ show (length args)) <| (args ==> expression body)
98 (args, body) = foldr depattern ([], innerBody) (zip patterns [0..])
100 depattern (pattern,n) (args, body) =
102 PVar x -> (args ++ [x], body)
103 _ -> let arg = "arg" ++ show n
104 in (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
106 (patterns, innerBody) = collect [p] e
108 collect patterns lexpr@(L _ expr) =
110 Lambda p e -> collect (p:patterns) e
111 _ -> (patterns, lexpr)
116 _ -> ref aN `call` (func:args)
118 aN = "A" ++ show (length args)
119 (func, args) = getArgs e1 [e2]
122 (L _ (App f arg)) -> getArgs f (arg : args)
123 _ -> (expression func, map expression args)
125 Let defs e -> FuncExpr () Nothing [] stmts `call` []
127 (defs',e') = SD.flattenLets defs e
128 stmts = map definition defs' ++ [ ReturnStmt () (Just (expression e')) ]
131 case last branches of
132 (L _ (Var "otherwise"), e) -> ifs (init branches) (expression e)
133 _ -> ifs branches (dotSep ["_E","If"] `call` [ ref "$moduleName", string (show span) ])
135 ifs branches finally = foldr iff finally branches
136 iff (if', then') else' = CondExpr () (expression if') (expression then') else'
139 dotSep ["_J","toList"] <| ArrayLit () (map expression es)
142 ObjectLit () (ctor : fields)
144 ctor = (prop "ctor", string (makeSafe name))
145 fields = zipWith (\n e -> (prop ("_" ++ show n), expression e)) [0..] es
147 Markdown doc -> return $ "Text.text('" ++ pad ++ md ++ pad ++ "')"
148 where pad = "<div style=\"height:0;width:0;\"> </div>"
149 md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
154 TypeAnnotation _ _ -> EmptyStmt ()
155 Def pattern expr@(L span _) ->
156 let assign x = VarDecl () (var x) (Just (expression expr)) in
160 let op = LBracket () (ref "_op") (string x) in
161 ExprStmt () (AssignExpr () OpAssign op (expression expr))
163 VarDeclStmt () [ assign x ]
165 PRecord fields -> VarDeclStmt () (assign "$" : map setField fields)
167 setField f = VarDecl () (var f) (Just (dotSep ["$",f]))
169 PData name patterns | vars /= Nothing ->
171 Just vs -> VarDeclStmt () (setup (zipWith varDecl vs [0..]))
173 vars = getVars patterns
176 PVar x : rest -> (x:) <$> getVars rest
180 varDecl x n = VarDecl () (var x) (Just (dotSep ["$","_" ++ show n]))
182 | isTuple name = assign "$" : vars
183 | otherwise = safeAssign : vars
185 safeAssign = VarDecl () (var "$") (Just $ CondExpr () if' (expression expr) exception)
186 if' = InfixExpr () OpStrictEq (dotSep ["$","ctor"]) (string name)
187 exception = dotSep ["_E","Case"] `call` [ref "$moduleName", string (show span)]
189 _ -> BlockStmt () ( VarDeclStmt () [assign "$"] : map toDef vars )
191 vars = Set.toList $ SD.boundVars pattern
193 toDef y = definition $
194 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
198globalAssign n e = "\n" ++ assign' n e ++ ";"
199assign' n e = n ++ " = " ++ e
201jsModule :: MetadataModule t v -> String
204 body <- toJS . fst . SD.flattenLets [] $ program modul
205 foreignImport <- mapM importEvent (foreignImports modul)
206 return $ concat [ setup ("Elm" : names modul)
207 , globalAssign ("Elm." ++ modName)
208 (jsFunc "elm" $ makeProgram body foreignImport) ]
210 modName = dotSep (names modul)
211 makeProgram body foreignImport =
212 concat [ setup ("elm" : names modul)
213 , "\nif (elm." ++ modName ++ ") return elm." ++ modName ++ ";"
214 , "\nvar " ++ usefulFuncs ++ ";"
215 , concatMap jsImport (imports modul)
216 , concat foreignImport
219 , concatMap exportEvent $ foreignExports modul
222 setup names = concatMap (\n -> globalAssign n $ n ++ " || {}") .
223 map dotSep . drop 2 . List.inits $ init names
224 usefulFuncs = commaSep (map (uncurry assign') (internalImports modName))
226 jsExports = ret (assign' ("elm." ++ modName) (brackets exs))
228 exs = indent . commaSep . concatMap pair $ "_op" : exports modul
230 | otherwise = ["\n" ++ x ++ " : " ++ x]
233 importEvent (js,base,elm,_) =
235 return $ concat [ "\nvar " ++ elm ++ "=Signal.constant(" ++ v ++ ");"
236 , "\ndocument.addEventListener('", js
237 , "_' + elm.id, function(e) { elm.notify(", elm
238 , ".id, e.value); });" ]
240 exportEvent (js,elm,_) =
241 concat [ "\nA2( Signal.lift, function(v) { "
242 , "var e = document.createEvent('Event');"
243 , "e.initEvent('", js, "_' + elm.id, true, true);"
245 , "document.dispatchEvent(e); return v; }, ", elm, ");" ]
247jsImport (modul, method) =
248 concat $ zipWith3 (\s n v -> s ++ assign' n v ++ ";") starters subnames values
250 starters = "\nvar " : repeat "\n"
251 values = map (\name -> name ++ " || {}") (init subnames) ++
252 ["Elm." ++ modul ++ parens "elm"]
253 subnames = map dotSep . tail . List.inits $ split modul
255 split names = case go [] names of
257 (name, ns) -> name : split ns
258 go name str = case str of
259 '.':rest -> (reverse name, rest)
260 c:rest -> go (c:name) rest
261 [] -> (reverse name, [])
264formatMarkdown = concatMap f
270caseToJS span e ps = do
271 (tempVar,match) <- caseToMatch ps
273 let (match',stmt) = case e of
274 L _ (Var x) -> (matchSubst [(tempVar,x)] match, "")
275 _ -> (match, assign tempVar e')
276 matches <- matchToJS span match'
277 return $ jsFunc "" (stmt ++ matches) ++ "()"
279matchToJS span match =
281 Match name clauses def ->
282 do cases <- concat `liftM` mapM (clauseToJS span name) clauses
283 finally <- matchToJS span def
284 let isLiteral p = case p of
285 Clause (Right _) _ _ -> True
287 access = if any isLiteral clauses then "" else ".ctor"
288 return $ concat [ "\nswitch (", name, access, ") {"
289 , indent cases, "\n}", finally ]
290 Fail -> return ("_E.Case" ++ parens ("$moduleName," ++ quoted (show span)))
291 Break -> return "break;"
292 Other e -> ret `liftM` toJS' e
293 Seq ms -> concat `liftM` mapM (matchToJS span) (dropEnd [] ms)
298 Other _ -> acc ++ [m]
299 _ -> dropEnd (acc ++ [m]) ms
301clauseToJS span var (Clause value vars e) = do
302 let vars' = map (\n -> var ++ "._" ++ show n) [0..]
303 s <- matchToJS span $ matchSubst (zip vars vars') e
304 pattern <- case value of
305 Right (Boolean True) -> return "true"
306 Right (Boolean False) -> return "false"
307 Right lit -> toJS lit
308 Left name -> return . quoted $ case List.elemIndices '.' name of
310 is -> drop (last is + 1) name
311 return $ concat [ "\ncase ", pattern, ":", indent s ]
316 "Basics.." -> ["$"] ==> foldr (<|) (ref "$") (map expression (e1 : collect [] e2))
317 "Basics.<|" -> foldr (<|) (expression e2) (map expression (collect [] e1))
318 "::" -> expression (L span (Data "::" [e1,e2]))
319 "List.++" -> dotSep ["_L","append"] `call` [js1, js2]
320 _ -> case Map.lookup op opDict of
322 Nothing -> ref "A2" `call` [ func [] op, js1, js2 ]
326 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
333 case break (=='.') str of
334 (x,'.':rest) -> func (vs ++ [x]) rest
336 | isOp op -> BracketRef () (dotSep (vs ++ ["_op"])) (string op)
337 | otherwise -> dotSep (vs ++ [op])
339 opDict = Map.fromList (infixOps ++ specialOps)
341 specialOp str func = ("Basic." ++ str, func)
342 infixOp str op = specialOp str (InfixExpr () op)
349 , infixOp "&&" OpLAnd
354 [ specialOp "^" $ \a b -> dotSep ["Math","pow"] `call` [a,b]
355 , specialOp "|>" $ flip (<|)
356 , specialOp "==" $ \a b -> dotSep ["_N","eq"] `call` [a,b]
357 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (dotSep ["_N","eq"] `call` [a,b])
358 , specialOp "<" $ cmp OpLT 0
359 , specialOp ">" $ cmp OpGT 0
360 , specialOp "<=" $ cmp OpLT 1
361 , specialOp ">=" $ cmp OpGT (-1)
362 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
365 cmp op n a b = InfixExpr () op (dotSep ["_N","cmp"] `call` [a,b]) (IntLit () n)
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