3module Generate.JavaScript where
5import Control.Arrow (first,(***))
6import Control.Applicative ((<$>),(<*>))
7import Control.Monad.State
8import qualified Data.List as List
9import qualified Data.Map as Map
10import qualified Data.Set as Set
12import qualified Generate.Cases as Case
13import qualified Generate.Markdown as MD
14import SourceSyntax.Everything
15import SourceSyntax.Location
16import qualified Transform.SortDefinitions as SD
17import Language.ECMAScript3.Syntax
18import Language.ECMAScript3.PrettyPrint
19import Parse.Helpers (jsReserveds)
21makeSafe :: String -> String
22makeSafe = List.intercalate "." . map dereserve . split . deprime
24 deprime = map (\c -> if c == '\'' then '$' else c)
25 dereserve x = case Set.member x jsReserveds of
29split :: String -> [String]
33 case break (=='.') str of
34 (x,'.':rest) | isOp x -> vars ++ [x ++ '.' : rest]
35 | otherwise -> go (vars ++ [x]) rest
38var name = Id () (makeSafe name)
39ref name = VarRef () (var name)
40prop name = PropId () (var name)
41f <| x = CallExpr () f [x]
42args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
43function args stmts = FuncExpr () Nothing (map var args) stmts
47dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
50varDecl :: String -> Expression () -> VarDecl ()
52 VarDecl () (var x) (Just expr)
54include alias moduleName =
55 varDecl alias (obj (moduleName ++ ".make") <| ref "elm")
59 [ varDecl "N" (obj "Elm.Native")
60 , include "_N" "N.Utils"
61 , include "_L" "N.List"
62 , include "_E" "N.Error"
63 , include "_J" "N.JavaScript"
64 , varDecl "$moduleName" (string name)
67literal :: Literal -> Expression ()
70 Chr c -> obj "_N.chr" <| string [c]
72 IntNum n -> IntLit () n
73 FloatNum n -> NumLit () n
74 Boolean b -> BoolLit () b
76expression :: LExpr () () -> State Int (Expression ())
77expression (L span expr) =
79 Var x -> return $ ref x
80 Literal lit -> return $ literal lit
83 do lo' <- expression lo
85 return $ obj "_L.range" `call` [lo',hi']
89 return $ DotRef () e' (var x)
93 return $ obj "_N.remove" `call` [string x, e']
98 return $ obj "_N.insert" `call` [string x, v', e']
101 do e' <- expression e
102 fs' <- forM fs $ \(f,v) -> do
104 return $ ArrayLit () [string f, v']
105 return $ obj "_N.replace" `call` [ArrayLit () fs', e']
108 do fields' <- forM fields $ \(f,e) -> do
109 (,) f <$> expression e
110 let fieldMap = List.foldl' combine Map.empty fields'
111 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
113 combine r (k,v) = Map.insertWith (++) k [v] r
114 prop = PropId () . var
115 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
116 Map.toList . Map.filter (not . null) $ Map.map tail fs
117 visible fs = map (first prop) . Map.toList $ Map.map head fs
119 Binop op e1 e2 -> binop span op e1 e2
121 Lambda p e@(L s _) ->
122 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
123 body' <- expression body
124 return $ case length args < 2 || length args > 9 of
125 True -> foldr (==>) body' (map (:[]) args)
126 False -> ref ("F" ++ show (length args)) <| (args ==> body')
128 depattern (args, body) pattern =
130 PVar x -> return (args ++ [x], body)
131 _ -> do arg <- Case.newVar
132 return (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
134 (patterns, innerBody) = collect [p] e
136 collect patterns lexpr@(L _ expr) =
138 Lambda p e -> collect (p:patterns) e
139 _ -> (patterns, lexpr)
142 do func' <- expression func
143 args' <- mapM expression args
144 return $ case args' of
145 [arg] -> func' <| arg
146 _ | length args' <= 9 -> ref aN `call` (func':args')
147 | otherwise -> foldl1 (<|) (func':args')
149 aN = "A" ++ show (length args)
150 (func, args) = getArgs e1 [e2]
153 (L _ (App f arg)) -> getArgs f (arg : args)
157 do let (defs',e') = SD.flattenLets defs e
158 stmts <- concat <$> mapM definition defs'
160 return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
163 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
164 return $ case last branches of
165 (L _ (Var "Basics.otherwise"), e) -> ifs (init branches') (snd (last branches'))
167 (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
169 ifs branches finally = foldr iff finally branches
170 iff (if', then') else' = CondExpr () if' then' else'
173 do (tempVar,initialMatch) <- Case.toMatch cases
174 (revisedMatch, stmt) <-
176 L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
177 _ -> do e' <- expression e
178 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
179 match' <- match span revisedMatch
180 return (function [] (stmt ++ match') `call` [])
183 do es' <- mapM expression es
184 return $ obj "_J.toList" <| ArrayLit () es'
187 do es' <- mapM expression es
188 return $ ObjectLit () (ctor : fields es')
190 ctor = (prop "ctor", string (makeSafe name))
191 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
193 Markdown uid doc es ->
194 do es' <- mapM expression es
195 return $ obj "Text.markdown" `call` (string md : string uid : es')
197 pad = "<div style=\"height:0;width:0;\"> </div>"
198 md = pad ++ MD.toHtml doc ++ pad
201 return . literal $ Str src
204definition :: Def () () -> State Int [Statement ()]
207 TypeAnnotation _ _ -> return []
209 Def pattern expr@(L span _) -> do
210 expr' <- expression expr
211 let assign x = varDecl x expr'
215 let op = LBracket () (ref "_op") (string x) in
216 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
218 return [ VarDeclStmt () [ assign x ] ]
221 let setField f = varDecl f (dotSep ["$",f]) in
222 return [ VarDeclStmt () (assign "$" : map setField fields) ]
224 PData name patterns | vars /= Nothing ->
226 Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
228 vars = getVars patterns
231 PVar x : rest -> (x:) `fmap` getVars rest
235 decl x n = varDecl x (dotSep ["$","_" ++ show n])
237 | isTuple name = assign "$" : vars
238 | otherwise = safeAssign : vars
240 safeAssign = varDecl "$" (CondExpr () if' expr' exception)
241 if' = InfixExpr () OpStrictEq (obj "$.ctor") (string name)
242 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
245 do defs' <- concat <$> mapM toDef vars
246 return (VarDeclStmt () [assign "$"] : defs')
248 vars = Set.toList $ SD.boundVars pattern
250 toDef y = definition $
251 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
253match :: (Show a) => a -> Case.Match () () -> State Int [Statement ()]
256 Case.Match name clauses mtch' ->
257 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
258 mtch'' <- match span mtch'
259 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
261 isLiteral p = case p of
262 Case.Clause (Right _) _ _ -> True
264 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
266 | or isChars = InfixExpr () OpAdd e (string "")
270 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
272 Case.Break -> return [BreakStmt () Nothing]
274 do e' <- expression e
275 return [ ReturnStmt () (Just e') ]
276 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
281 Case.Other _ -> acc ++ [m]
282 _ -> dropEnd (acc ++ [m]) ms
284clause span variable (Case.Clause value vars mtch) =
285 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
287 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
290 Right (Chr c) -> (True, string [c])
291 _ -> (,) False $ case value of
292 Right (Boolean b) -> BoolLit () b
293 Right lit -> literal lit
294 Left name -> string $ case List.elemIndices '.' name of
296 is -> drop (last is + 1) name
299jsModule :: MetadataModule () () -> String
301 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
302 [ assign ("Elm" : names modul ++ ["make"]) (function ["elm"] programStmts) ]
304 thisModule = dotSep ("elm" : names modul ++ ["values"])
306 concat [ setup (Just "elm") (names modul ++ ["values"])
307 , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
308 , [ internalImports (List.intercalate "." (names modul)) ]
309 , concatMap jsImport (imports modul)
310 , concatMap importEvent (foreignImports modul)
311 , [ assign ["_op"] (ObjectLit () []) ]
312 , concat $ evalState (mapM definition . fst . SD.flattenLets [] $ program modul) 0
313 , map exportEvent $ foreignExports modul
315 , [ ReturnStmt () (Just thisModule) ]
318 jsExports = assign ("elm" : names modul ++ ["values"]) (ObjectLit () exs)
320 exs = map entry . filter (not . isOp) $ "_op" : exports modul
321 entry x = (PropId () (var x), ref x)
325 [x] -> VarDeclStmt () [ varDecl x expr ]
327 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
329 jsImport (modul,_) = setup Nothing path ++ [ include ]
332 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "elm"
334 setup namespace path = map create paths
336 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
337 paths = case namespace of
338 Nothing -> tail . init $ List.inits path
339 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
341 addId js = InfixExpr () OpAdd (string (js++"_")) (obj "elm.id")
343 importEvent (js,base,elm,_) =
344 [ VarDeclStmt () [ varDecl elm $ obj "Signal.constant" <| evalState (expression base) 0 ]
346 obj "document.addEventListener" `call`
349 [ ExprStmt () $ obj "elm.notify" `call` [dotSep [elm,"id"], obj "e.value"] ]
353 exportEvent (js,elm,_) =
358 [ VarDeclStmt () [varDecl "e" $ obj "document.createEvent" <| string "Event"]
360 obj "e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
361 , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "e") "value") (ref "v")
362 , ExprStmt () $ obj "document.dispatchEvent" <| ref "e"
363 , ReturnStmt () (Just $ ref "v")
370 do es <- mapM expression (e1 : collect [] e2)
371 return $ ["$"] ==> foldr (<|) (ref "$") es
373 do e2' <- expression e2
374 es <- mapM expression (collect [] e1)
375 return $ foldr (<|) e2' es
377 do e1' <- expression e1
379 return $ obj "_L.append" `call` [e1', e2']
380 "::" -> expression (L span (Data "::" [e1,e2]))
382 do e1' <- expression e1
384 return $ case Map.lookup op opDict of
386 Nothing -> ref "A2" `call` [ func, e1', e2' ]
390 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
396 func | isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
397 | otherwise = dotSep parts
400 operator = last parts
402 opDict = Map.fromList (infixOps ++ specialOps)
404 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
405 infixOp str op = specialOp str (InfixExpr () op)
412 , infixOp "&&" OpLAnd
417 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
418 , specialOp "|>" $ flip (<|)
419 , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
420 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
421 , specialOp "<" $ cmp OpLT 0
422 , specialOp ">" $ cmp OpGT 0
423 , specialOp "<=" $ cmp OpLT 1
424 , specialOp ">=" $ cmp OpGT (-1)
425 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
428 cmp op n a b = InfixExpr () op (obj "_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