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 qualified SourceSyntax.Helpers as Help
13import SourceSyntax.Literal
14import SourceSyntax.Pattern
15import SourceSyntax.Location
16import SourceSyntax.Expression
17import SourceSyntax.Module
18import qualified Transform.SortDefinitions as SD
19import Language.ECMAScript3.Syntax
20import Language.ECMAScript3.PrettyPrint
21import qualified Transform.SafeNames as MakeSafe
23split :: String -> [String]
27 case break (=='.') str of
28 (x,'.':rest) | Help.isOp x -> vars ++ [x ++ '.' : rest]
29 | otherwise -> go (vars ++ [x]) rest
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)
44varDecl :: String -> Expression () -> VarDecl ()
46 VarDecl () (var x) (Just expr)
48include alias moduleName =
49 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
53 [ varDecl "N" (obj "Elm.Native")
54 , include "_N" "N.Utils"
55 , include "_L" "N.List"
56 , include "_E" "N.Error"
57 , include "_J" "N.JavaScript"
58 , varDecl "$moduleName" (string name)
61literal :: Literal -> Expression ()
64 Chr c -> obj "_N.chr" <| string [c]
66 IntNum n -> IntLit () n
67 FloatNum n -> NumLit () n
68 Boolean b -> BoolLit () b
70expression :: LExpr () () -> State Int (Expression ())
71expression (L span expr) =
73 Var x -> return $ ref x
74 Literal lit -> return $ literal lit
77 do lo' <- expression lo
79 return $ obj "_L.range" `call` [lo',hi']
83 return $ DotRef () e' (var x)
87 return $ obj "_N.remove" `call` [string x, e']
92 return $ obj "_N.insert" `call` [string x, v', e']
96 fs' <- forM fs $ \(f,v) -> do
98 return $ ArrayLit () [string f, v']
99 return $ obj "_N.replace" `call` [ArrayLit () fs', e']
102 do fields' <- forM fields $ \(f,e) -> do
103 (,) f <$> expression e
104 let fieldMap = List.foldl' combine Map.empty fields'
105 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
107 combine r (k,v) = Map.insertWith (++) k [v] r
108 prop = PropId () . var
109 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
110 Map.toList . Map.filter (not . null) $ Map.map tail fs
111 visible fs = map (first prop) . Map.toList $ Map.map head fs
113 Binop op e1 e2 -> binop span op e1 e2
115 Lambda p e@(L s _) ->
116 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
117 body' <- expression body
118 return $ case length args < 2 || length args > 9 of
119 True -> foldr (==>) body' (map (:[]) args)
120 False -> ref ("F" ++ show (length args)) <| (args ==> body')
122 depattern (args, body) pattern =
124 PVar x -> return (args ++ [x], body)
125 _ -> do arg <- Case.newVar
126 return (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
128 (patterns, innerBody) = collect [p] e
130 collect patterns lexpr@(L _ expr) =
132 Lambda p e -> collect (p:patterns) e
133 _ -> (patterns, lexpr)
136 do func' <- expression func
137 args' <- mapM expression args
138 return $ case args' of
139 [arg] -> func' <| arg
140 _ | length args' <= 9 -> ref aN `call` (func':args')
141 | otherwise -> foldl1 (<|) (func':args')
143 aN = "A" ++ show (length args)
144 (func, args) = getArgs e1 [e2]
147 (L _ (App f arg)) -> getArgs f (arg : args)
151 do let (defs',e') = SD.flattenLets defs e
152 stmts <- concat <$> mapM definition defs'
154 return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
157 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
158 return $ case last branches of
159 (L _ (Var "Basics.otherwise"), e) -> ifs (init branches') (snd (last branches'))
161 (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
163 ifs branches finally = foldr iff finally branches
164 iff (if', then') else' = CondExpr () if' then' else'
167 do (tempVar,initialMatch) <- Case.toMatch cases
168 (revisedMatch, stmt) <-
170 L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
171 _ -> do e' <- expression e
172 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
173 match' <- match span revisedMatch
174 return (function [] (stmt ++ match') `call` [])
177 do es' <- mapM expression es
178 return $ obj "_J.toList" <| ArrayLit () es'
181 do es' <- mapM expression es
182 return $ ObjectLit () (ctor : fields es')
184 ctor = (prop "ctor", string name)
185 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
187 Markdown uid doc es ->
188 do es' <- mapM expression es
189 return $ obj "Text.markdown" `call` (string md : string uid : es')
191 pad = "<div style=\"height:0;width:0;\"> </div>"
192 md = pad ++ MD.toHtml doc ++ pad
194definition :: Def () () -> State Int [Statement ()]
197 TypeAnnotation _ _ -> return []
199 Def pattern expr@(L span _) -> do
200 expr' <- expression expr
201 let assign x = varDecl x expr'
205 let op = LBracket () (ref "_op") (string x) in
206 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
208 return [ VarDeclStmt () [ assign x ] ]
211 let setField f = varDecl f (dotSep ["$",f]) in
212 return [ VarDeclStmt () (assign "$" : map setField fields) ]
214 PData name patterns | vars /= Nothing ->
216 Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
218 vars = getVars patterns
221 PVar x : rest -> (x:) `fmap` getVars rest
225 decl x n = varDecl x (dotSep ["$","_" ++ show n])
227 | Help.isTuple name = assign "$" : vars
228 | otherwise = safeAssign : vars
230 safeAssign = varDecl "$" (CondExpr () if' expr' exception)
231 if' = InfixExpr () OpStrictEq (obj "$.ctor") (string name)
232 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
235 do defs' <- concat <$> mapM toDef vars
236 return (VarDeclStmt () [assign "$"] : defs')
238 vars = Set.toList $ SD.boundVars pattern
240 toDef y = definition $
241 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
243match :: (Show a) => a -> Case.Match () () -> State Int [Statement ()]
246 Case.Match name clauses mtch' ->
247 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
248 mtch'' <- match span mtch'
249 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
251 isLiteral p = case p of
252 Case.Clause (Right _) _ _ -> True
254 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
256 | or isChars = InfixExpr () OpAdd e (string "")
260 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
262 Case.Break -> return [BreakStmt () Nothing]
264 do e' <- expression e
265 return [ ReturnStmt () (Just e') ]
266 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
271 Case.Other _ -> acc ++ [m]
272 _ -> dropEnd (acc ++ [m]) ms
274clause span variable (Case.Clause value vars mtch) =
275 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
277 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
280 Right (Chr c) -> (True, string [c])
281 _ -> (,) False $ case value of
282 Right (Boolean b) -> BoolLit () b
283 Right lit -> literal lit
284 Left name -> string $ case List.elemIndices '.' name of
286 is -> drop (last is + 1) name
289generate :: MetadataModule () () -> String
290generate unsafeModule =
291 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
292 [ assign ("Elm" : names modul ++ ["make"]) (function ["_elm"] programStmts) ]
294 modul = MakeSafe.metadataModule unsafeModule
295 thisModule = dotSep ("_elm" : names modul ++ ["values"])
297 concat [ setup (Just "_elm") (names modul ++ ["values"])
298 , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
299 , [ internalImports (List.intercalate "." (names modul)) ]
300 , concatMap jsImport (imports modul)
301 , concatMap importEvent (foreignImports modul)
302 , [ assign ["_op"] (ObjectLit () []) ]
303 , concat $ evalState (mapM definition . fst . SD.flattenLets [] $ program modul) 0
304 , map exportEvent $ foreignExports modul
306 , [ ReturnStmt () (Just thisModule) ]
309 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
311 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
312 entry x = (PropId () (var x), ref x)
316 [x] -> VarDeclStmt () [ varDecl x expr ]
318 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
320 jsImport (modul,_) = setup Nothing path ++ [ include ]
323 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "_elm"
325 setup namespace path = map create paths
327 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
328 paths = case namespace of
329 Nothing -> tail . init $ List.inits path
330 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
332 addId js = InfixExpr () OpAdd (string (js++"_")) (obj "_elm.id")
334 importEvent (js,base,elm,_) =
335 [ VarDeclStmt () [ varDecl elm $ obj "Signal.constant" <| evalState (expression base) 0 ]
337 obj "document.addEventListener" `call`
340 [ ExprStmt () $ obj "_elm.notify" `call` [dotSep [elm,"id"], obj "_e.value"] ]
344 exportEvent (js,elm,_) =
349 [ VarDeclStmt () [varDecl "_e" $ obj "document.createEvent" <| string "Event"]
351 obj "_e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
352 , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "_e") "value") (ref "_v")
353 , ExprStmt () $ obj "document.dispatchEvent" <| ref "_e"
354 , ReturnStmt () (Just $ ref "_v")
361 do es <- mapM expression (e1 : collect [] e2)
362 return $ ["$"] ==> foldr (<|) (ref "$") es
364 do e2' <- expression e2
365 es <- mapM expression (collect [] e1)
366 return $ foldr (<|) e2' es
368 do e1' <- expression e1
370 return $ obj "_L.append" `call` [e1', e2']
371 "::" -> expression (L span (Data "::" [e1,e2]))
373 do e1' <- expression e1
375 return $ case Map.lookup op opDict of
377 Nothing -> ref "A2" `call` [ func, e1', e2' ]
381 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
387 func | Help.isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
388 | otherwise = dotSep parts
391 operator = last parts
393 opDict = Map.fromList (infixOps ++ specialOps)
395 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
396 infixOp str op = specialOp str (InfixExpr () op)
403 , infixOp "&&" OpLAnd
408 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
409 , specialOp "|>" $ flip (<|)
410 , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
411 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
412 , specialOp "<" $ cmp OpLT 0
413 , specialOp ">" $ cmp OpGT 0
414 , specialOp "<=" $ cmp OpLT 1
415 , specialOp ">=" $ cmp OpGT (-1)
416 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
419 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