1module Generate.JavaScript where
3import Control.Arrow ((***))
4import qualified Data.List as List
5import qualified Data.Map as Map
6import qualified Data.Set as Set
7import qualified Text.Pandoc as Pan
10import SourceSyntax.Everything
11import SourceSyntax.Location
12import qualified Transform.SortDefinitions as SD
13import Language.ECMAScript3.Syntax
14import Language.ECMAScript3.PrettyPrint
15import Parse.Helpers (jsReserveds)
17makeSafe :: String -> String
18makeSafe = dereserve . deprime
20 deprime = map (\c -> if c == '\'' then '$' else c)
21 dereserve x = case Set.member x jsReserveds of
28 case break (=='.') str of
29 (x,'.':rest) -> go (vars ++ [x]) rest
32var name = Id () (makeSafe name)
33ref name = VarRef () (var name)
34prop name = PropId () (var name)
35f <| x = CallExpr () f [x]
36args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
37function args stmts = FuncExpr () Nothing (map var args) stmts
41dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
45 VarDecl () (var x) (Just expr)
47include alias moduleName =
48 varDecl alias (get moduleName <| ref "elm")
52 [ varDecl "N" (get "Elm.Native")
53 , include "_N" "N.Utils"
54 , include "_L" "N.List"
55 , include "_E" "N.Error"
56 , include "_J" "N.JavaScript"
57 , varDecl "_str" (get "_J.toString")
58 , varDecl "$moduleName" (string name)
64 Str s -> ref "_str" <| string s
65 IntNum n -> IntLit () n
66 FloatNum n -> NumLit () n
67 Boolean b -> BoolLit () b
69expression :: LExpr () () -> Expression ()
70expression (L span expr) =
73 Literal lit -> literal lit
74 Range lo hi -> get "_L.range" `call` [expression lo, expression hi]
76 Access e x -> DotRef () (expression e) (var x)
77 Remove e x -> get "_N.remove" `call` [string x, expression e]
78 Insert e x v -> get "_N.insert" `call` [string x, expression v, expression e]
80 let modify (f,v) = ArrayLit () [string f, expression v]
81 in get "_N.replace" `call` [ArrayLit () (map modify fs), expression e]
83 Record fields -> ObjectLit () $ (PropId () (var "_"), hidden) : visible
85 combine r (k,v) = Map.insertWith (++) k [v] r
86 fieldMap = List.foldl' combine Map.empty fields
87 hidden = ObjectLit () . map ((PropId () . var) *** (ArrayLit () . map expression)) .
88 Map.toList . Map.filter (not . null) $ Map.map tail fieldMap
89 visible = map ((PropId () . var) *** expression) . Map.toList $ Map.map head fieldMap
91 Binop op e1 e2 -> binop span op e1 e2
93 Lambda p e@(L s _) -> fastFunc
96 | length args < 2 || length args > 9 =
97 foldr (==>) (expression body) (map (:[]) args)
99 ref ("F" ++ show (length args)) <| (args ==> expression body)
101 (args, body) = foldr depattern ([], innerBody) (zip patterns [0..])
103 depattern (pattern,n) (args, body) =
105 PVar x -> (args ++ [x], body)
106 _ -> let arg = "arg" ++ show n
107 in (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
109 (patterns, innerBody) = collect [p] e
111 collect patterns lexpr@(L _ expr) =
113 Lambda p e -> collect (p:patterns) e
114 _ -> (patterns, lexpr)
119 _ -> ref aN `call` (func:args)
121 aN = "A" ++ show (length args)
122 (func, args) = getArgs e1 [e2]
125 (L _ (App f arg)) -> getArgs f (arg : args)
126 _ -> (expression func, map expression args)
128 Let defs e -> function [] stmts `call` []
130 (defs',e') = SD.flattenLets defs e
131 stmts = concatMap definition defs' ++ [ ReturnStmt () (Just (expression e')) ]
134 case last branches of
135 (L _ (Var "otherwise"), e) -> ifs (init branches) (expression e)
136 _ -> ifs branches (get "_E.If" `call` [ ref "$moduleName", string (show span) ])
138 ifs branches finally = foldr iff finally branches
139 iff (if', then') else' = CondExpr () (expression if') (expression then') else'
141 Case e cases -> function [] (stmt ++ match span revisedMatch) `call` []
143 (tempVar,initialMatch) = caseToMatch cases
144 (revisedMatch, stmt) =
146 L _ (Var x) -> (matchSubst [(tempVar,x)] initialMatch, [])
147 _ -> (initialMatch, [VarDeclStmt () [varDecl tempVar (expression e)]])
150 get "_J.toList" <| ArrayLit () (map expression es)
153 ObjectLit () (ctor : fields)
155 ctor = (prop "ctor", string (makeSafe name))
156 fields = zipWith (\n e -> (prop ("_" ++ show n), expression e)) [0..] es
158 Markdown doc -> get "Text.text" <| string (pad ++ md ++ pad)
159 where pad = "<div style=\"height:0;width:0;\"> </div>"
160 md = Pan.writeHtmlString Pan.def doc
165 TypeAnnotation _ _ -> []
166 Def pattern expr@(L span _) ->
167 let assign x = varDecl x (expression expr) in
171 let op = LBracket () (ref "_op") (string x) in
172 [ ExprStmt () (AssignExpr () OpAssign op (expression expr)) ]
174 [ VarDeclStmt () [ assign x ] ]
176 PRecord fields -> [ VarDeclStmt () (assign "$" : map setField fields) ]
178 setField f = varDecl f (dotSep ["$",f])
180 PData name patterns | vars /= Nothing ->
182 Just vs -> [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
184 vars = getVars patterns
187 PVar x : rest -> (x:) `fmap` getVars rest
191 decl x n = varDecl x (dotSep ["$","_" ++ show n])
193 | isTuple name = assign "$" : vars
194 | otherwise = safeAssign : vars
196 safeAssign = varDecl "$" (CondExpr () if' (expression expr) exception)
197 if' = InfixExpr () OpStrictEq (get "$.ctor") (string name)
198 exception = get "_E.Case" `call` [ref "$moduleName", string (show span)]
200 _ -> VarDeclStmt () [assign "$"] : concatMap toDef vars
202 vars = Set.toList $ SD.boundVars pattern
204 toDef y = definition $
205 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
209 Match name clauses mtch' -> SwitchStmt () (access name) clauses' : match span mtch'
211 clauses' = map (clause span name) clauses
212 isLiteral p = case p of
213 Clause (Right _) _ _ -> True
215 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
217 Fail -> [ ExprStmt () (get "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
219 Other e -> [ ReturnStmt () (Just $ expression e) ]
220 Seq ms -> concatMap (match span) (dropEnd [] ms)
225 Other _ -> acc ++ [m]
226 _ -> dropEnd (acc ++ [m]) ms
228clause span variable (Clause value vars mtch) =
229 CaseClause () pattern stmt
231 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
232 stmt = match span $ matchSubst (zip vars vars') mtch
233 pattern = case value of
234 Right (Boolean b) -> BoolLit () b
235 Right lit -> literal lit
236 Left name -> string $ case List.elemIndices '.' name of
238 is -> drop (last is + 1) name
240jsModule :: MetadataModule () () -> String -- [Statement ()]
241jsModule modul = show . prettyPrint $ setup (Just "Elm") (names modul) ++
242 [ assign ("Elm" : names modul) (function ["elm"] programStmts) ]
244 thisModule = dotSep ("elm" : names modul)
246 concat [ setup (Just "elm") (names modul)
247 , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
248 , [ internalImports (List.intercalate "." (names modul)) ]
249 , concatMap jsImport (imports modul)
250 , concatMap importEvent (foreignImports modul)
251 , [ assign ["_op"] (ObjectLit () []) ]
252 , concatMap definition . fst . SD.flattenLets [] $ program modul
253 , map exportEvent $ foreignExports modul
255 , [ ReturnStmt () (Just thisModule) ]
258 jsExports = assign ("elm" : names modul) (ObjectLit () exs)
260 exs = map entry . filter (not . isOp) $ "_op" : exports modul
261 entry x = (PropId () (var x), ref x)
265 [x] -> VarDeclStmt () [ varDecl x expr ]
267 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
269 jsImport (modul,_) = setup Nothing path ++ [ assign path (dotSep ("Elm" : path) <| ref "elm") ]
273 setup namespace path = map create paths
275 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
276 paths = case namespace of
277 Nothing -> tail . init $ List.inits path
278 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
280 addId js = InfixExpr () OpAdd (string (js++"_")) (get "elm.id")
282 importEvent (js,base,elm,_) =
283 [ VarDeclStmt () [ varDecl elm $ get "Signal.constant" <| expression base ]
285 get "document.addEventListener" `call`
288 [ ExprStmt () $ get "elm.notify" `call` [dotSep [elm,"id"], get "e.value"] ]
292 exportEvent (js,elm,_) =
297 [ VarDeclStmt () [varDecl "e" $ get "document.createEvent" <| string "Event"]
299 get "e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
300 , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "e") "value") (ref "v")
301 , ExprStmt () $ get "document.dispatchEvent" <| ref "e"
302 , ReturnStmt () (Just $ ref "v")
309 "Basics.." -> ["$"] ==> foldr (<|) (ref "$") (map expression (e1 : collect [] e2))
310 "Basics.<|" -> foldr (<|) (expression e2) (map expression (collect [] e1))
311 "::" -> expression (L span (Data "::" [e1,e2]))
312 "List.++" -> get "_L.append" `call` [js1, js2]
313 _ -> case Map.lookup op opDict of
315 Nothing -> ref "A2" `call` [ func, js1, js2 ]
319 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
325 func | isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string op)
326 | otherwise = dotSep parts
329 operator = last parts
331 opDict = Map.fromList (infixOps ++ specialOps)
333 specialOp str func = ("Basics." ++ str, func)
334 infixOp str op = specialOp str (InfixExpr () op)
341 , infixOp "&&" OpLAnd
346 [ specialOp "^" $ \a b -> get "Math.pow" `call` [a,b]
347 , specialOp "|>" $ flip (<|)
348 , specialOp "==" $ \a b -> get "_N.eq" `call` [a,b]
349 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (get "_N.eq" `call` [a,b])
350 , specialOp "<" $ cmp OpLT 0
351 , specialOp ">" $ cmp OpGT 0
352 , specialOp "<=" $ cmp OpLT 1
353 , specialOp ">=" $ cmp OpGT (-1)
354 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
357 cmp op n a b = InfixExpr () op (get "_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