1module Generate.JavaScript where
3import Control.Applicative ((<$>))
4import Control.Arrow ((***))
5import qualified Data.List as List
6import qualified Data.Map as Map
7import qualified Data.Set as Set
8import Data.Either (partitionEithers)
11import SourceSyntax.Everything
12import SourceSyntax.Location
13import qualified Transform.SortDefinitions as SD
14import Language.ECMAScript3.Syntax
15import Language.ECMAScript3.PrettyPrint
16import Parse.Helpers (makeSafe, iParse)
18import Parse.Expression
20testExpr str = case iParse expr "" str of
21 Right e -> prettyPrint (expression (e :: LExpr () ()))
22 Left err -> error (show err)
24var name = Id () (makeSafe name)
25ref name = VarRef () (var name)
26prop name = PropId () (var name)
27f <| x = CallExpr () f [x]
28args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
32dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
35 VarDecl () (var x) (Just expr)
37include alias moduleName =
38 varDecl alias (dotSep moduleName <| ref "elm")
42 [ varDecl "N" (dotSep ["Elm","Native"])
43 , include "_N" ["N","Utils"]
44 , include "_L" ["N","List"]
45 , include "_E" ["N","Error"]
46 , include "_J" ["N","JavaScript"]
47 , varDecl "_str" (dotSep ["_J","toString"])
48 , varDecl "$moduleName" (string name)
54 Str s -> ref "_str" <| string s
55 IntNum n -> IntLit () n
56-- FloatNum n -> NumLit () (float2Double n) -- wrong!!!
57 Boolean b -> BoolLit () b
59expression :: LExpr () () -> Expression ()
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]
73 Record fields -> ObjectLit () $ (PropId () (var "_"), hidden) : visible
75 combine r (k,v) = Map.insertWith (++) k [v] r
76 fieldMap = List.foldl' combine Map.empty fields
77 hidden = ObjectLit () . map ((PropId () . var) *** (ArrayLit () . map expression)) .
78 Map.toList . Map.filter (not . null) $ Map.map tail fieldMap
79 visible = map ((PropId () . var) *** expression) . Map.toList $ Map.map head fieldMap
81 Binop op e1 e2 -> binop span op e1 e2
83 Lambda p e@(L s _) -> fastFunc
86 | length args < 2 || length args > 9 =
87 foldr (==>) (expression body) (map (:[]) args)
89 ref ("F" ++ show (length args)) <| (args ==> expression body)
91 (args, body) = foldr depattern ([], innerBody) (zip patterns [0..])
93 depattern (pattern,n) (args, body) =
95 PVar x -> (args ++ [x], body)
96 _ -> let arg = "arg" ++ show n
97 in (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
99 (patterns, innerBody) = collect [p] e
101 collect patterns lexpr@(L _ expr) =
103 Lambda p e -> collect (p:patterns) e
104 _ -> (patterns, lexpr)
109 _ -> ref aN `call` (func:args)
111 aN = "A" ++ show (length args)
112 (func, args) = getArgs e1 [e2]
115 (L _ (App f arg)) -> getArgs f (arg : args)
116 _ -> (expression func, map expression args)
118 Let defs e -> FuncExpr () Nothing [] stmts `call` []
120 (defs',e') = SD.flattenLets defs e
121 stmts = map definition defs' ++ [ ReturnStmt () (Just (expression e')) ]
124 case last branches of
125 (L _ (Var "otherwise"), e) -> ifs (init branches) (expression e)
126 _ -> ifs branches (dotSep ["_E","If"] `call` [ ref "$moduleName", string (show span) ])
128 ifs branches finally = foldr iff finally branches
129 iff (if', then') else' = CondExpr () (expression if') (expression then') else'
131 Case e cases -> FuncExpr () Nothing [] (stmt ++ match span revisedMatch) `call` []
133 (tempVar,initialMatch) = caseToMatch cases
134 (revisedMatch, stmt) =
136 L _ (Var x) -> (matchSubst [(tempVar,x)] initialMatch, [])
137 _ -> let decl = VarDecl () (var tempVar) (Just $ expression e)
138 in (initialMatch, [VarDeclStmt () [decl]])
141 dotSep ["_J","toList"] <| ArrayLit () (map expression es)
144 ObjectLit () (ctor : fields)
146 ctor = (prop "ctor", string (makeSafe name))
147 fields = zipWith (\n e -> (prop ("_" ++ show n), expression e)) [0..] es
149 Markdown doc -> return $ "Text.text('" ++ pad ++ md ++ pad ++ "')"
150 where pad = "<div style=\"height:0;width:0;\"> </div>"
151 md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
156 TypeAnnotation _ _ -> EmptyStmt ()
157 Def pattern expr@(L span _) ->
158 let assign x = VarDecl () (var x) (Just (expression expr)) in
162 let op = LBracket () (ref "_op") (string x) in
163 ExprStmt () (AssignExpr () OpAssign op (expression expr))
165 VarDeclStmt () [ assign x ]
167 PRecord fields -> VarDeclStmt () (assign "$" : map setField fields)
169 setField f = VarDecl () (var f) (Just (dotSep ["$",f]))
171 PData name patterns | vars /= Nothing ->
173 Just vs -> VarDeclStmt () (setup (zipWith varDecl vs [0..]))
175 vars = getVars patterns
178 PVar x : rest -> (x:) <$> getVars rest
182 varDecl x n = VarDecl () (var x) (Just (dotSep ["$","_" ++ show n]))
184 | isTuple name = assign "$" : vars
185 | otherwise = safeAssign : vars
187 safeAssign = VarDecl () (var "$") (Just $ CondExpr () if' (expression expr) exception)
188 if' = InfixExpr () OpStrictEq (dotSep ["$","ctor"]) (string name)
189 exception = dotSep ["_E","Case"] `call` [ref "$moduleName", string (show span)]
191 _ -> BlockStmt () ( VarDeclStmt () [assign "$"] : map toDef vars )
193 vars = Set.toList $ SD.boundVars pattern
195 toDef y = definition $
196 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
201 Match name clauses mtch' -> SwitchStmt () (access name) clauses' : match span mtch'
203 clauses' = map (clause span name) clauses
204 isLiteral p = case p of
205 Clause (Right _) _ _ -> True
207 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
209 Fail -> [ ExprStmt () (dotSep ["_E","Case"] `call` [ref "$moduleName", string (show span)]) ]
211 Other e -> [ ReturnStmt () (Just $ expression e) ]
212 Seq ms -> concatMap (match span) (dropEnd [] ms)
217 Other _ -> acc ++ [m]
218 _ -> dropEnd (acc ++ [m]) ms
220clause span variable (Clause value vars mtch) =
221 CaseClause () pattern stmt
223 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
224 stmt = match span $ matchSubst (zip vars vars') mtch
225 pattern = case value of
226 Right (Boolean b) -> BoolLit () b
227 Right lit -> literal lit
228 Left name -> string $ case List.elemIndices '.' name of
230 is -> drop (last is + 1) name
234jsModule :: MetadataModule t v -> String
237 body <- toJS . fst . SD.flattenLets [] $ program modul
238 foreignImport <- mapM importEvent (foreignImports modul)
239 return $ concat [ setup ("Elm" : names modul)
240 , globalAssign ("Elm." ++ modName)
241 (jsFunc "elm" $ makeProgram body foreignImport) ]
243 modName = dotSep (names modul)
244 makeProgram body foreignImport =
245 concat [ setup ("elm" : names modul)
246 , "\nif (elm." ++ modName ++ ") return elm." ++ modName ++ ";"
247 , "\nvar " ++ usefulFuncs ++ ";"
248 , concatMap jsImport (imports modul)
249 , concat foreignImport
252 , concatMap exportEvent $ foreignExports modul
255 setup names = concatMap (\n -> globalAssign n $ n ++ " || {}") .
256 map dotSep . drop 2 . List.inits $ init names
257 usefulFuncs = commaSep (map (uncurry assign') (internalImports modName))
259 jsExports = ret (assign' ("elm." ++ modName) (brackets exs))
261 exs = indent . commaSep . concatMap pair $ "_op" : exports modul
263 | otherwise = ["\n" ++ x ++ " : " ++ x]
266 importEvent (js,base,elm,_) =
268 return $ concat [ "\nvar " ++ elm ++ "=Signal.constant(" ++ v ++ ");"
269 , "\ndocument.addEventListener('", js
270 , "_' + elm.id, function(e) { elm.notify(", elm
271 , ".id, e.value); });" ]
273 exportEvent (js,elm,_) =
274 concat [ "\nA2( Signal.lift, function(v) { "
275 , "var e = document.createEvent('Event');"
276 , "e.initEvent('", js, "_' + elm.id, true, true);"
278 , "document.dispatchEvent(e); return v; }, ", elm, ");" ]
280jsImport (modul, method) =
281 concat $ zipWith3 (\s n v -> s ++ assign' n v ++ ";") starters subnames values
283 starters = "\nvar " : repeat "\n"
284 values = map (\name -> name ++ " || {}") (init subnames) ++
285 ["Elm." ++ modul ++ parens "elm"]
286 subnames = map dotSep . tail . List.inits $ split modul
288 split names = case go [] names of
290 (name, ns) -> name : split ns
291 go name str = case str of
292 '.':rest -> (reverse name, rest)
293 c:rest -> go (c:name) rest
294 [] -> (reverse name, [])
297formatMarkdown = concatMap f
305 "Basics.." -> ["$"] ==> foldr (<|) (ref "$") (map expression (e1 : collect [] e2))
306 "Basics.<|" -> foldr (<|) (expression e2) (map expression (collect [] e1))
307 "::" -> expression (L span (Data "::" [e1,e2]))
308 "List.++" -> dotSep ["_L","append"] `call` [js1, js2]
309 _ -> case Map.lookup op opDict of
311 Nothing -> ref "A2" `call` [ func [] op, js1, js2 ]
315 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
322 case break (=='.') str of
323 (x,'.':rest) -> func (vs ++ [x]) rest
325 | isOp op -> BracketRef () (dotSep (vs ++ ["_op"])) (string op)
326 | otherwise -> dotSep (vs ++ [op])
328 opDict = Map.fromList (infixOps ++ specialOps)
330 specialOp str func = ("Basic." ++ str, func)
331 infixOp str op = specialOp str (InfixExpr () op)
338 , infixOp "&&" OpLAnd
343 [ specialOp "^" $ \a b -> dotSep ["Math","pow"] `call` [a,b]
344 , specialOp "|>" $ flip (<|)
345 , specialOp "==" $ \a b -> dotSep ["_N","eq"] `call` [a,b]
346 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (dotSep ["_N","eq"] `call` [a,b])
347 , specialOp "<" $ cmp OpLT 0
348 , specialOp ">" $ cmp OpGT 0
349 , specialOp "<=" $ cmp OpLT 1
350 , specialOp ">=" $ cmp OpGT (-1)
351 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
354 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