1module Generate.JavaScript (generate) where
3import Control.Arrow (first,(***))
4import Control.Applicative ((<$>),(<*>))
5import Control.Monad.State
6import qualified Data.List as List
7import qualified Data.Map as Map
8import qualified Data.Set as Set
10import qualified Generate.Cases as Case
11import qualified Generate.Markdown as MD
12import SourceSyntax.Everything
13import SourceSyntax.Location
14import qualified Transform.SortDefinitions as SD
15import Language.ECMAScript3.Syntax
16import Language.ECMAScript3.PrettyPrint
17import qualified Transform.SafeNames as MakeSafe
19split :: String -> [String]
23 case break (=='.') str of
24 (x,'.':rest) | isOp x -> vars ++ [x ++ '.' : rest]
25 | otherwise -> go (vars ++ [x]) rest
29ref name = VarRef () (var name)
30prop name = PropId () (var name)
31f <| x = CallExpr () f [x]
32args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
33function args stmts = FuncExpr () Nothing (map var args) stmts
37dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
40varDecl :: String -> Expression () -> VarDecl ()
42 VarDecl () (var x) (Just expr)
44include alias moduleName =
45 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
49 [ varDecl "N" (obj "Elm.Native")
50 , include "_N" "N.Utils"
51 , include "_L" "N.List"
52 , include "_E" "N.Error"
53 , include "_J" "N.JavaScript"
54 , varDecl "$moduleName" (string name)
57literal :: Literal -> Expression ()
60 Chr c -> obj "_N.chr" <| string [c]
62 IntNum n -> IntLit () n
63 FloatNum n -> NumLit () n
64 Boolean b -> BoolLit () b
66expression :: LExpr () () -> State Int (Expression ())
67expression (L span expr) =
69 Var x -> return $ ref x
70 Literal lit -> return $ literal lit
73 do lo' <- expression lo
75 return $ obj "_L.range" `call` [lo',hi']
79 return $ DotRef () e' (var x)
83 return $ obj "_N.remove" `call` [string x, e']
88 return $ obj "_N.insert" `call` [string x, v', e']
92 fs' <- forM fs $ \(f,v) -> do
94 return $ ArrayLit () [string f, v']
95 return $ obj "_N.replace" `call` [ArrayLit () fs', e']
98 do fields' <- forM fields $ \(f,e) -> do
99 (,) f <$> expression e
100 let fieldMap = List.foldl' combine Map.empty fields'
101 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
103 combine r (k,v) = Map.insertWith (++) k [v] r
104 prop = PropId () . var
105 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
106 Map.toList . Map.filter (not . null) $ Map.map tail fs
107 visible fs = map (first prop) . Map.toList $ Map.map head fs
109 Binop op e1 e2 -> binop span op e1 e2
111 Lambda p e@(L s _) ->
112 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
113 body' <- expression body
114 return $ case length args < 2 || length args > 9 of
115 True -> foldr (==>) body' (map (:[]) args)
116 False -> ref ("F" ++ show (length args)) <| (args ==> body')
118 depattern (args, body) pattern =
120 PVar x -> return (args ++ [x], body)
121 _ -> do arg <- Case.newVar
122 return (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
124 (patterns, innerBody) = collect [p] e
126 collect patterns lexpr@(L _ expr) =
128 Lambda p e -> collect (p:patterns) e
129 _ -> (patterns, lexpr)
132 do func' <- expression func
133 args' <- mapM expression args
134 return $ case args' of
135 [arg] -> func' <| arg
136 _ | length args' <= 9 -> ref aN `call` (func':args')
137 | otherwise -> foldl1 (<|) (func':args')
139 aN = "A" ++ show (length args)
140 (func, args) = getArgs e1 [e2]
143 (L _ (App f arg)) -> getArgs f (arg : args)
147 do let (defs',e') = SD.flattenLets defs e
148 stmts <- concat <$> mapM definition defs'
150 return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
153 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
154 return $ case last branches of
155 (L _ (Var "Basics.otherwise"), e) -> ifs (init branches') (snd (last branches'))
157 (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
159 ifs branches finally = foldr iff finally branches
160 iff (if', then') else' = CondExpr () if' then' else'
163 do (tempVar,initialMatch) <- Case.toMatch cases
164 (revisedMatch, stmt) <-
166 L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
167 _ -> do e' <- expression e
168 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
169 match' <- match span revisedMatch
170 return (function [] (stmt ++ match') `call` [])
173 do es' <- mapM expression es
174 return $ obj "_J.toList" <| ArrayLit () es'
177 do es' <- mapM expression es
178 return $ ObjectLit () (ctor : fields es')
180 ctor = (prop "ctor", string name)
181 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
183 Markdown uid doc es ->
184 do es' <- mapM expression es
185 return $ obj "Text.markdown" `call` (string md : string uid : es')
187 pad = "<div style=\"height:0;width:0;\"> </div>"
188 md = pad ++ MD.toHtml doc ++ pad
190definition :: Def () () -> State Int [Statement ()]
193 TypeAnnotation _ _ -> return []
195 Def pattern expr@(L span _) -> do
196 expr' <- expression expr
197 let assign x = varDecl x expr'
201 let op = LBracket () (ref "_op") (string x) in
202 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
204 return [ VarDeclStmt () [ assign x ] ]
207 let setField f = varDecl f (dotSep ["$",f]) in
208 return [ VarDeclStmt () (assign "$" : map setField fields) ]
210 PData name patterns | vars /= Nothing ->
212 Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
214 vars = getVars patterns
217 PVar x : rest -> (x:) `fmap` getVars rest
221 decl x n = varDecl x (dotSep ["$","_" ++ show n])
223 | isTuple name = assign "$" : vars
224 | otherwise = safeAssign : vars
226 safeAssign = varDecl "$" (CondExpr () if' expr' exception)
227 if' = InfixExpr () OpStrictEq (obj "$.ctor") (string name)
228 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
231 do defs' <- concat <$> mapM toDef vars
232 return (VarDeclStmt () [assign "$"] : defs')
234 vars = Set.toList $ SD.boundVars pattern
236 toDef y = definition $
237 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
239match :: (Show a) => a -> Case.Match () () -> State Int [Statement ()]
242 Case.Match name clauses mtch' ->
243 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
244 mtch'' <- match span mtch'
245 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
247 isLiteral p = case p of
248 Case.Clause (Right _) _ _ -> True
250 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
252 | or isChars = InfixExpr () OpAdd e (string "")
256 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
258 Case.Break -> return [BreakStmt () Nothing]
260 do e' <- expression e
261 return [ ReturnStmt () (Just e') ]
262 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
267 Case.Other _ -> acc ++ [m]
268 _ -> dropEnd (acc ++ [m]) ms
270clause span variable (Case.Clause value vars mtch) =
271 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
273 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
276 Right (Chr c) -> (True, string [c])
277 _ -> (,) False $ case value of
278 Right (Boolean b) -> BoolLit () b
279 Right lit -> literal lit
280 Left name -> string $ case List.elemIndices '.' name of
282 is -> drop (last is + 1) name
285generate :: MetadataModule () () -> String
286generate unsafeModule =
287 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
288 [ assign ("Elm" : names modul ++ ["make"]) (function ["_elm"] programStmts) ]
290 modul = MakeSafe.metadataModule unsafeModule
291 thisModule = dotSep ("_elm" : names modul ++ ["values"])
293 concat [ setup (Just "_elm") (names modul ++ ["values"])
294 , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
295 , [ internalImports (List.intercalate "." (names modul)) ]
296 , concatMap jsImport (imports modul)
297 , concatMap importEvent (foreignImports modul)
298 , [ assign ["_op"] (ObjectLit () []) ]
299 , concat $ evalState (mapM definition . fst . SD.flattenLets [] $ program modul) 0
300 , map exportEvent $ foreignExports modul
302 , [ ReturnStmt () (Just thisModule) ]
305 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
307 exs = map entry . filter (not . isOp) $ "_op" : exports modul
308 entry x = (PropId () (var x), ref x)
312 [x] -> VarDeclStmt () [ varDecl x expr ]
314 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
316 jsImport (modul,_) = setup Nothing path ++ [ include ]
319 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "_elm"
321 setup namespace path = map create paths
323 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
324 paths = case namespace of
325 Nothing -> tail . init $ List.inits path
326 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
328 addId js = InfixExpr () OpAdd (string (js++"_")) (obj "_elm.id")
330 importEvent (js,base,elm,_) =
331 [ VarDeclStmt () [ varDecl elm $ obj "Signal.constant" <| evalState (expression base) 0 ]
333 obj "document.addEventListener" `call`
336 [ ExprStmt () $ obj "_elm.notify" `call` [dotSep [elm,"id"], obj "_e.value"] ]
340 exportEvent (js,elm,_) =
345 [ VarDeclStmt () [varDecl "_e" $ obj "document.createEvent" <| string "Event"]
347 obj "_e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
348 , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "_e") "value") (ref "_v")
349 , ExprStmt () $ obj "document.dispatchEvent" <| ref "_e"
350 , ReturnStmt () (Just $ ref "_v")
357 do es <- mapM expression (e1 : collect [] e2)
358 return $ ["$"] ==> foldr (<|) (ref "$") es
360 do e2' <- expression e2
361 es <- mapM expression (collect [] e1)
362 return $ foldr (<|) e2' es
364 do e1' <- expression e1
366 return $ obj "_L.append" `call` [e1', e2']
367 "::" -> expression (L span (Data "::" [e1,e2]))
369 do e1' <- expression e1
371 return $ case Map.lookup op opDict of
373 Nothing -> ref "A2" `call` [ func, e1', e2' ]
377 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
383 func | isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
384 | otherwise = dotSep parts
387 operator = last parts
389 opDict = Map.fromList (infixOps ++ specialOps)
391 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
392 infixOp str op = specialOp str (InfixExpr () op)
399 , infixOp "&&" OpLAnd
404 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
405 , specialOp "|>" $ flip (<|)
406 , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
407 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
408 , specialOp "<" $ cmp OpLT 0
409 , specialOp ">" $ cmp OpGT 0
410 , specialOp "<=" $ cmp OpLT 1
411 , specialOp ">=" $ cmp OpGT (-1)
412 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
415 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