3module Generate.JavaScript (generate) 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 qualified SourceSyntax.Helpers as Help
15import SourceSyntax.Literal
16import SourceSyntax.Pattern
17import SourceSyntax.Location
18import SourceSyntax.Expression
19import SourceSyntax.Module
20import qualified Transform.SortDefinitions as SD
21import Language.ECMAScript3.Syntax
22import Language.ECMAScript3.PrettyPrint
23import qualified Transform.SafeNames as MakeSafe
25split :: String -> [String]
29 case break (=='.') str of
30 (x,'.':rest) | Help.isOp x -> vars ++ [x ++ '.' : rest]
31 | otherwise -> go (vars ++ [x]) rest
35ref name = VarRef () (var name)
36prop name = PropId () (var name)
37f <| x = CallExpr () f [x]
38args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
39function args stmts = FuncExpr () Nothing (map var args) stmts
43dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
46varDecl :: String -> Expression () -> VarDecl ()
48 VarDecl () (var x) (Just expr)
50include alias moduleName =
51 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
55 [ varDecl "N" (obj "Elm.Native")
56 , include "_N" "N.Utils"
57 , include "_L" "N.List"
58 , include "_E" "N.Error"
59 , include "_J" "N.JavaScript"
60 , varDecl "$moduleName" (string name)
63literal :: Literal -> Expression ()
66 Chr c -> obj "_N.chr" <| string [c]
68 IntNum n -> IntLit () n
69 FloatNum n -> NumLit () n
70 Boolean b -> BoolLit () b
72expression :: LExpr () () -> State Int (Expression ())
73expression (L span expr) =
75 Var x -> return $ ref x
76 Literal lit -> return $ literal lit
79 do lo' <- expression lo
81 return $ obj "_L.range" `call` [lo',hi']
85 return $ DotRef () e' (var x)
89 return $ obj "_N.remove" `call` [string x, e']
94 return $ obj "_N.insert" `call` [string x, v', e']
98 fs' <- forM fs $ \(f,v) -> do
100 return $ ArrayLit () [string f, v']
101 return $ obj "_N.replace" `call` [ArrayLit () fs', e']
104 do fields' <- forM fields $ \(f,e) -> do
105 (,) f <$> expression e
106 let fieldMap = List.foldl' combine Map.empty fields'
107 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
109 combine r (k,v) = Map.insertWith (++) k [v] r
110 prop = PropId () . var
111 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
112 Map.toList . Map.filter (not . null) $ Map.map tail fs
113 visible fs = map (first prop) . Map.toList $ Map.map head fs
115 Binop op e1 e2 -> binop span op e1 e2
117 Lambda p e@(L s _) ->
118 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
119 body' <- expression body
120 return $ case length args < 2 || length args > 9 of
121 True -> foldr (==>) body' (map (:[]) args)
122 False -> ref ("F" ++ show (length args)) <| (args ==> body')
124 depattern (args, body) pattern =
126 PVar x -> return (args ++ [x], body)
127 _ -> do arg <- Case.newVar
128 return (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
130 (patterns, innerBody) = collect [p] e
132 collect patterns lexpr@(L _ expr) =
134 Lambda p e -> collect (p:patterns) e
135 _ -> (patterns, lexpr)
138 do func' <- expression func
139 args' <- mapM expression args
140 return $ case args' of
141 [arg] -> func' <| arg
142 _ | length args' <= 9 -> ref aN `call` (func':args')
143 | otherwise -> foldl1 (<|) (func':args')
145 aN = "A" ++ show (length args)
146 (func, args) = getArgs e1 [e2]
149 (L _ (App f arg)) -> getArgs f (arg : args)
153 do let (defs',e') = SD.flattenLets defs e
154 stmts <- concat <$> mapM definition defs'
156 return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
159 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
160 return $ case last branches of
161 (L _ (Var "Basics.otherwise"), _) -> safeIfs branches'
162 (L _ (Literal (Boolean True)), _) -> safeIfs branches'
163 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
165 safeIfs branches = ifs (init branches) (snd (last branches))
166 ifs branches finally = foldr iff finally branches
167 iff (if', then') else' = CondExpr () if' then' else'
170 do (tempVar,initialMatch) <- Case.toMatch cases
171 (revisedMatch, stmt) <-
173 L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
174 _ -> do e' <- expression e
175 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
176 match' <- match span revisedMatch
177 return (function [] (stmt ++ match') `call` [])
180 do es' <- mapM expression es
181 return $ obj "_J.toList" <| ArrayLit () es'
184 do es' <- mapM expression es
185 return $ ObjectLit () (ctor : fields es')
187 ctor = (prop "ctor", string name)
188 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
190 Markdown uid doc es ->
191 do es' <- mapM expression es
192 return $ obj "Text.markdown" `call` (string md : string uid : es')
194 pad = "<div style=\"height:0;width:0;\"> </div>"
195 md = pad ++ MD.toHtml doc ++ pad
198 return . literal $ Str src
201definition :: Def () () -> State Int [Statement ()]
204 TypeAnnotation _ _ -> return []
206 Def pattern expr@(L span _) -> do
207 expr' <- expression expr
208 let assign x = varDecl x expr'
212 let op = LBracket () (ref "_op") (string x) in
213 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
215 return [ VarDeclStmt () [ assign x ] ]
218 let setField f = varDecl f (dotSep ["$",f]) in
219 return [ VarDeclStmt () (assign "$" : map setField fields) ]
221 PData name patterns | vars /= Nothing ->
223 Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
225 vars = getVars patterns
228 PVar x : rest -> (x:) `fmap` getVars rest
232 decl x n = varDecl x (dotSep ["$","_" ++ show n])
234 | Help.isTuple name = assign "$" : vars
235 | otherwise = assign "$raw" : safeAssign : vars
237 safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
238 if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
239 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
242 do defs' <- concat <$> mapM toDef vars
243 return (VarDeclStmt () [assign "$"] : defs')
245 vars = Set.toList $ SD.boundVars pattern
247 toDef y = definition $
248 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
250match :: (Show a) => a -> Case.Match () () -> State Int [Statement ()]
253 Case.Match name clauses mtch' ->
254 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
255 mtch'' <- match span mtch'
256 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
258 isLiteral p = case p of
259 Case.Clause (Right _) _ _ -> True
261 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
263 | or isChars = InfixExpr () OpAdd e (string "")
267 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
269 Case.Break -> return [BreakStmt () Nothing]
271 do e' <- expression e
272 return [ ReturnStmt () (Just e') ]
273 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
278 Case.Other _ -> acc ++ [m]
279 _ -> dropEnd (acc ++ [m]) ms
281clause span variable (Case.Clause value vars mtch) =
282 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
284 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
287 Right (Chr c) -> (True, string [c])
288 _ -> (,) False $ case value of
289 Right (Boolean b) -> BoolLit () b
290 Right lit -> literal lit
291 Left name -> string $ case List.elemIndices '.' name of
293 is -> drop (last is + 1) name
296generate :: MetadataModule () () -> String
297generate unsafeModule =
298 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
299 [ assign ("Elm" : names modul ++ ["make"]) (function ["_elm"] programStmts) ]
301 modul = MakeSafe.metadataModule unsafeModule
302 thisModule = dotSep ("_elm" : names modul ++ ["values"])
304 concat [ setup (Just "_elm") (names modul ++ ["values"])
305 , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
306 , [ internalImports (List.intercalate "." (names modul)) ]
307 , concatMap jsImport (imports modul)
308 , concatMap importEvent (foreignImports modul)
309 , [ assign ["_op"] (ObjectLit () []) ]
310 , concat $ evalState (mapM definition . fst . SD.flattenLets [] $ program modul) 0
311 , map exportEvent $ foreignExports modul
313 , [ ReturnStmt () (Just thisModule) ]
316 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
318 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
319 entry x = (PropId () (var x), ref x)
323 [x] -> VarDeclStmt () [ varDecl x expr ]
325 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
327 jsImport (modul,_) = setup Nothing path ++ [ include ]
330 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "_elm"
332 setup namespace path = map create paths
334 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
335 paths = case namespace of
336 Nothing -> tail . init $ List.inits path
337 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
339 addId js = InfixExpr () OpAdd (string (js++"_")) (obj "_elm.id")
341 importEvent (js,base,elm,_) =
342 [ VarDeclStmt () [ varDecl elm $ obj "Signal.constant" <| evalState (expression base) 0 ]
344 obj "document.addEventListener" `call`
347 [ ExprStmt () $ obj "_elm.notify" `call` [dotSep [elm,"id"], obj "_e.value"] ]
351 exportEvent (js,elm,_) =
356 [ VarDeclStmt () [varDecl "_e" $ obj "document.createEvent" <| string "Event"]
358 obj "_e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
359 , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "_e") "value") (ref "_v")
360 , ExprStmt () $ obj "document.dispatchEvent" <| ref "_e"
361 , ReturnStmt () (Just $ ref "_v")
368 do es <- mapM expression (e1 : collect [] e2)
369 return $ ["$"] ==> foldr (<|) (ref "$") es
371 do e2' <- expression e2
372 es <- mapM expression (collect [] e1)
373 return $ foldr (<|) e2' es
375 do e1' <- expression e1
377 return $ obj "_L.append" `call` [e1', e2']
378 "::" -> expression (L span (Data "::" [e1,e2]))
380 do e1' <- expression e1
382 return $ case Map.lookup op opDict of
384 Nothing -> ref "A2" `call` [ func, e1', e2' ]
388 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
394 func | Help.isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
395 | otherwise = dotSep parts
398 operator = last parts
400 opDict = Map.fromList (infixOps ++ specialOps)
402 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
403 infixOp str op = specialOp str (InfixExpr () op)
410 , infixOp "&&" OpLAnd
415 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
416 , specialOp "|>" $ flip (<|)
417 , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
418 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
419 , specialOp "<" $ cmp OpLT 0
420 , specialOp ">" $ cmp OpGT 0
421 , specialOp "<=" $ cmp OpLT 1
422 , specialOp ">=" $ cmp OpGT (-1)
423 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
426 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