1module Generate.JavaScript where
3import Control.Monad (liftM,(<=<),join,ap)
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)
13import SourceSyntax.Everything
14import SourceSyntax.Location
15import qualified Transform.SortDefinitions as SD
16import Language.ECMAScript3.Syntax
17import Language.ECMAScript3.PrettyPrint
18import Parse.Helpers (makeSafe, iParse)
20import Parse.Expression
22testExpr str = (prettyPrint . expression) `fmap` iParse expr "" str
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 (L span expr) =
62 Literal lit -> literal lit
63 Range lo hi -> dotSep ["_L","range"] `call` [expression lo, expression hi]
65 Access e x -> DotRef () (expression e) (var x)
66 Remove e x -> dotSep ["_N","remove"] `call` [ref x, expression e]
67 Insert e x v -> dotSep ["_N","insert"] `call` [ref x, expression v, expression e]
69 Modify e fs -> do fs' <- (mapM (\(x,v) -> (,) x `liftM` toJS' v) fs)
70 setField fs' `liftM` toJS' e
71 Record fs -> makeRecord fs
73addField x v e = "_N.insert('" ++ x ++ "', " ++ v ++ ", " ++ e ++ ")"
74setField fs e = "_N.replace(" ++ jsList (map f fs) ++ ", " ++ e ++ ")"
75 where f (x,v) = "['" ++ x ++ "'," ++ v ++ "]"
76makeRecord kvs = record `liftM` collect kvs
78 combine r (k,v) = Map.insertWith (++) k v r
79 collect = liftM (List.foldl' combine Map.empty) . mapM prep
84 brackets ("\n "++List.intercalate ",\n " (map (\(k,v) -> k++":"++v) fs))
85 hidden = fields . map (second jsList) .
86 filter (not . null . snd) . Map.toList . Map.map tail
87 record kvs = fields . (("_", hidden kvs) :) . Map.toList . Map.map head $ kvs
89 Binop op e1 e2 -> binop span op e1 e2
91 Lambda p e@(L s _) -> fastFunc
94 | length args < 2 || length args > 9 =
95 foldr (==>) (expression body) (map (:[]) args)
97 ref ("F" ++ show (length args)) <| (args ==> expression body)
99 (args, body) = foldr depattern ([], innerBody) (zip patterns [1..])
101 depattern (pattern,n) (args, body) =
103 PVar x -> (args ++ [x], body)
104 _ -> let arg = "arg" ++ show n
105 in (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
107 (patterns, innerBody) = collect [p] e
109 collect patterns lexpr@(L _ expr) =
111 Lambda p e -> collect (p:patterns) e
112 _ -> (patterns, lexpr)
117 _ -> ref aN `call` (func:args)
119 aN = "A" ++ show (length args)
120 (func, args) = getArgs e1 [e2]
123 (L _ (App f arg)) -> getArgs f (arg : args)
124 _ -> (expression func, map expression args)
126 Let defs e -> jsLet $ SD.flattenLets defs e
130 case last branches of
131 (L _ (Var "otherwise"), e) -> ifs (init branches) (expression e)
132 _ -> ifs branches (dotSep ["_E","If"] `call` [ ref "$moduleName", string (show span) ])
134 ifs branches finally = foldr iff finally branches
135 iff (if', then') else' = CondExpr () (expression if') (expression then') else'
138 dotSep ["_J","toList"] <| ArrayLit () (map expression es)
141 ObjectLit () (ctor : fields)
143 ctor = (prop "ctor", string (makeSafe name))
144 fields = zipWith (\n e -> (prop ("_" ++ show n), expression e)) [0..] es
146 Markdown doc -> return $ "Text.text('" ++ pad ++ md ++ pad ++ "')"
147 where pad = "<div style=\"height:0;width:0;\"> </div>"
148 md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
153globalAssign n e = "\n" ++ assign' n e ++ ";"
154assign' n e = n ++ " = " ++ e
156jsModule :: MetadataModule t v -> String
159 body <- toJS . fst . SD.flattenLets [] $ program modul
160 foreignImport <- mapM importEvent (foreignImports modul)
161 return $ concat [ setup ("Elm" : names modul)
162 , globalAssign ("Elm." ++ modName)
163 (jsFunc "elm" $ makeProgram body foreignImport) ]
165 modName = dotSep (names modul)
166 makeProgram body foreignImport =
167 concat [ setup ("elm" : names modul)
168 , "\nif (elm." ++ modName ++ ") return elm." ++ modName ++ ";"
169 , "\nvar " ++ usefulFuncs ++ ";"
170 , concatMap jsImport (imports modul)
171 , concat foreignImport
174 , concatMap exportEvent $ foreignExports modul
177 setup names = concatMap (\n -> globalAssign n $ n ++ " || {}") .
178 map dotSep . drop 2 . List.inits $ init names
179 usefulFuncs = commaSep (map (uncurry assign') (internalImports modName))
181 jsExports = ret (assign' ("elm." ++ modName) (brackets exs))
183 exs = indent . commaSep . concatMap pair $ "_op" : exports modul
185 | otherwise = ["\n" ++ x ++ " : " ++ x]
188 importEvent (js,base,elm,_) =
190 return $ concat [ "\nvar " ++ elm ++ "=Signal.constant(" ++ v ++ ");"
191 , "\ndocument.addEventListener('", js
192 , "_' + elm.id, function(e) { elm.notify(", elm
193 , ".id, e.value); });" ]
195 exportEvent (js,elm,_) =
196 concat [ "\nA2( Signal.lift, function(v) { "
197 , "var e = document.createEvent('Event');"
198 , "e.initEvent('", js, "_' + elm.id, true, true);"
200 , "document.dispatchEvent(e); return v; }, ", elm, ");" ]
202jsImport (modul, method) =
203 concat $ zipWith3 (\s n v -> s ++ assign' n v ++ ";") starters subnames values
205 starters = "\nvar " : repeat "\n"
206 values = map (\name -> name ++ " || {}") (init subnames) ++
207 ["Elm." ++ modul ++ parens "elm"]
208 subnames = map dotSep . tail . List.inits $ split modul
210 split names = case go [] names of
212 (name, ns) -> name : split ns
213 go name str = case str of
214 '.':rest -> (reverse name, rest)
215 c:rest -> go (c:name) rest
216 [] -> (reverse name, [])
220 toJS :: a -> Unique String
223instance ToJS (Def t v) where
225 -- TODO: Make this handle patterns besides plain variables
226 toJS (Def (PVar x) e)
227 | isOp x = globalAssign ("_op['" ++ x ++ "']") `liftM` toJS' e
228 | otherwise = assign x `liftM` toJS' e
230 toJS (Def pattern e@(L s _)) =
232 let x = "_" ++ show n
234 toDef y = Def (PVar y) (L s $ Case (var x) [(pattern, var y)])
235 stmt <- assign x `liftM` toJS' e
236 vars <- toJS . map toDef . Set.toList $ SD.boundVars pattern
237 return (stmt ++ vars)
239 toJS (TypeAnnotation _ _) = return ""
242instance ToJS a => ToJS [a] where
243 toJS xs = concat `liftM` mapM toJS xs
245toJS' :: LExpr t v -> Unique String
248 Case e cases -> caseToJS span e cases
253formatMarkdown = concatMap f
259jsLet (defs,e') = do ds <- mapM toJS defs
261 return $ jsFunc "" (concat ds ++ ret e) ++ "()"
263caseToJS span e ps = do
264 (tempVar,match) <- caseToMatch ps
266 let (match',stmt) = case e of
267 L _ (Var x) -> (matchSubst [(tempVar,x)] match, "")
268 _ -> (match, assign tempVar e')
269 matches <- matchToJS span match'
270 return $ jsFunc "" (stmt ++ matches) ++ "()"
272matchToJS span match =
274 Match name clauses def ->
275 do cases <- concat `liftM` mapM (clauseToJS span name) clauses
276 finally <- matchToJS span def
277 let isLiteral p = case p of
278 Clause (Right _) _ _ -> True
280 access = if any isLiteral clauses then "" else ".ctor"
281 return $ concat [ "\nswitch (", name, access, ") {"
282 , indent cases, "\n}", finally ]
283 Fail -> return ("_E.Case" ++ parens ("$moduleName," ++ quoted (show span)))
284 Break -> return "break;"
285 Other e -> ret `liftM` toJS' e
286 Seq ms -> concat `liftM` mapM (matchToJS span) (dropEnd [] ms)
291 Other _ -> acc ++ [m]
292 _ -> dropEnd (acc ++ [m]) ms
294clauseToJS span var (Clause value vars e) = do
295 let vars' = map (\n -> var ++ "._" ++ show n) [0..]
296 s <- matchToJS span $ matchSubst (zip vars vars') e
297 pattern <- case value of
298 Right (Boolean True) -> return "true"
299 Right (Boolean False) -> return "false"
300 Right lit -> toJS lit
301 Left name -> return . quoted $ case List.elemIndices '.' name of
303 is -> drop (last is + 1) name
304 return $ concat [ "\ncase ", pattern, ":", indent s ]
306jsCons e1 e2 = "_L.Cons(" ++ e1 ++ "," ++ e2 ++ ")"
307jsCompare e1 e2 op = parens ("_N.cmp(" ++ e1 ++ "," ++ e2 ++ ")" ++ op)
312 "Basics.." -> ["$"] ==> foldr (<|) (ref "$") (map expression (e1 : collect [] e2))
313 "Basics.<|" -> foldr (<|) (expression e2) (map expression (collect [] e1))
314 "::" -> expression (L span (Data "::" [e1,e2]))
315 "List.++" -> dotSep ["_L","append"] `call` [js1, js2]
316 _ -> case Map.lookup op opDict of
318 Nothing -> ref "A2" `call` [ func [] op, js1, js2 ]
322 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
329 case break (=='.') str of
330 (x,'.':rest) -> func (vs ++ [x]) rest
332 | isOp op -> BracketRef () (dotSep (vs ++ ["_op"])) (string op)
333 | otherwise -> dotSep (vs ++ [op])
335 opDict = Map.fromList (infixOps ++ specialOps)
337 specialOp str func = ("Basic." ++ str, func)
338 infixOp str op = specialOp str (InfixExpr () op)
345 , infixOp "&&" OpLAnd
350 [ specialOp "^" $ \a b -> dotSep ["Math","pow"] `call` [a,b]
351 , specialOp "|>" $ flip (<|)
352 , specialOp "==" $ \a b -> dotSep ["_N","eq"] `call` [a,b]
353 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (dotSep ["_N","eq"] `call` [a,b])
354 , specialOp "<" $ cmp OpLT 0
355 , specialOp ">" $ cmp OpGT 0
356 , specialOp "<=" $ cmp OpLT 1
357 , specialOp ">=" $ cmp OpGT (-1)
358 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
361 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