1module Generate.JavaScript 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
9import qualified Text.Pandoc as Pan
11import qualified Generate.Cases as Case
12import SourceSyntax.Everything
13import SourceSyntax.Location
14import qualified Transform.SortDefinitions as SD
15import Language.ECMAScript3.Syntax
16import Language.ECMAScript3.PrettyPrint
17import Parse.Helpers (jsReserveds)
19makeSafe :: String -> String
20makeSafe = List.intercalate "." . map dereserve . split . deprime
22 deprime = map (\c -> if c == '\'' then '$' else c)
23 dereserve x = case Set.member x jsReserveds of
30 case break (=='.') str of
31 (x,'.':rest) | isOp x -> vars ++ [x ++ '.' : rest]
32 | otherwise -> go (vars ++ [x]) rest
35var name = Id () (makeSafe name)
36ref name = VarRef () (var name)
37prop name = PropId () (var name)
38f <| x = CallExpr () f [x]
39args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
40function args stmts = FuncExpr () Nothing (map var args) stmts
44dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
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)
65 Chr c -> obj "_N.chr" <| string [c]
67 IntNum n -> IntLit () n
68 FloatNum n -> NumLit () n
69 Boolean b -> BoolLit () b
71expression :: LExpr () () -> State Int (Expression ())
72expression (L span expr) =
74 Var x -> return $ ref x
75 Literal lit -> return $ literal lit
78 do lo' <- expression lo
80 return $ obj "_L.range" `call` [lo',hi']
84 return $ DotRef () e' (var x)
88 return $ obj "_N.remove" `call` [string x, e']
93 return $ obj "_N.insert" `call` [string x, v', e']
97 fs' <- forM fs $ \(f,v) -> do
99 return $ ArrayLit () [string f, v']
100 return $ obj "_N.replace" `call` [ArrayLit () fs', e']
103 do fields' <- forM fields $ \(f,e) -> do
104 (,) f <$> expression e
105 let fieldMap = List.foldl' combine Map.empty fields'
106 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
108 combine r (k,v) = Map.insertWith (++) k [v] r
109 prop = PropId () . var
110 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
111 Map.toList . Map.filter (not . null) $ Map.map tail fs
112 visible fs = map (first prop) . Map.toList $ Map.map head fs
114 Binop op e1 e2 -> binop span op e1 e2
116 Lambda p e@(L s _) ->
117 do 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 (args, body) = foldr depattern ([], innerBody) (zip patterns [0..])
124 depattern (pattern,n) (args, body) =
126 PVar x -> (args ++ [x], body)
127 _ -> let arg = "arg" ++ show n
128 in (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"), e) -> ifs (init branches') (snd (last branches'))
163 (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
165 ifs branches finally = foldr iff finally branches
166 iff (if', then') else' = CondExpr () if' then' else'
169 do (tempVar,initialMatch) <- Case.toMatch cases
170 (revisedMatch, stmt) <-
172 L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
173 _ -> do e' <- expression e
174 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
175 match' <- match span revisedMatch
176 return (function [] (stmt ++ match') `call` [])
179 do es' <- mapM expression es
180 return $ obj "_J.toList" <| ArrayLit () es'
183 do es' <- mapM expression es
184 return $ ObjectLit () (ctor : fields es')
186 ctor = (prop "ctor", string (makeSafe name))
187 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
189 Markdown doc _ -> return $ obj "Text.text" <| string (pad ++ md ++ pad)
190 where pad = "<div style=\"height:0;width:0;\"> </div>"
191 md = Pan.writeHtmlString Pan.def doc
193definition :: Def () () -> State Int [Statement ()]
196 TypeAnnotation _ _ -> return []
198 Def pattern expr@(L span _) -> do
199 expr' <- expression expr
200 let assign x = varDecl x expr'
204 let op = LBracket () (ref "_op") (string x) in
205 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
207 return [ VarDeclStmt () [ assign x ] ]
210 let setField f = varDecl f (dotSep ["$",f]) in
211 return [ VarDeclStmt () (assign "$" : map setField fields) ]
213 PData name patterns | vars /= Nothing ->
215 Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
217 vars = getVars patterns
220 PVar x : rest -> (x:) `fmap` getVars rest
224 decl x n = varDecl x (dotSep ["$","_" ++ show n])
226 | isTuple name = assign "$" : vars
227 | otherwise = safeAssign : vars
229 safeAssign = varDecl "$" (CondExpr () if' expr' exception)
230 if' = InfixExpr () OpStrictEq (obj "$.ctor") (string name)
231 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
234 do defs' <- concat <$> mapM toDef vars
235 return (VarDeclStmt () [assign "$"] : defs')
237 vars = Set.toList $ SD.boundVars pattern
239 toDef y = definition $
240 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
242match :: (Show a) => a -> Case.Match () () -> State Int [Statement ()]
245 Case.Match name clauses mtch' ->
246 do clauses' <- mapM (clause span name) clauses
247 mtch'' <- match span mtch'
248 return (SwitchStmt () (access name) clauses' : mtch'')
250 isLiteral p = case p of
251 Case.Clause (Right _) _ _ -> True
253 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
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 CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
273 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
274 pattern = case value of
275 Right (Boolean b) -> BoolLit () b
276 Right lit -> literal lit
277 Left name -> string $ case List.elemIndices '.' name of
279 is -> drop (last is + 1) name
282jsModule :: MetadataModule () () -> String
284 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
285 [ assign ("Elm" : names modul ++ ["make"]) (function ["elm"] programStmts) ]
287 thisModule = dotSep ("elm" : names modul ++ ["values"])
289 concat [ setup (Just "elm") (names modul ++ ["values"])
290 , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
291 , [ internalImports (List.intercalate "." (names modul)) ]
292 , concatMap jsImport (imports modul)
293 , concatMap importEvent (foreignImports modul)
294 , [ assign ["_op"] (ObjectLit () []) ]
295 , concat $ evalState (mapM definition . fst . SD.flattenLets [] $ program modul) 0
296 , map exportEvent $ foreignExports modul
298 , [ ReturnStmt () (Just thisModule) ]
301 jsExports = assign ("elm" : names modul ++ ["values"]) (ObjectLit () exs)
303 exs = map entry . filter (not . isOp) $ "_op" : exports modul
304 entry x = (PropId () (var x), ref x)
308 [x] -> VarDeclStmt () [ varDecl x expr ]
310 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
312 jsImport (modul,_) = setup Nothing path ++ [ include ]
315 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "elm"
317 setup namespace path = map create paths
319 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
320 paths = case namespace of
321 Nothing -> tail . init $ List.inits path
322 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
324 addId js = InfixExpr () OpAdd (string (js++"_")) (obj "elm.id")
326 importEvent (js,base,elm,_) =
327 [ VarDeclStmt () [ varDecl elm $ obj "Signal.constant" <| evalState (expression base) 0 ]
329 obj "document.addEventListener" `call`
332 [ ExprStmt () $ obj "elm.notify" `call` [dotSep [elm,"id"], obj "e.value"] ]
336 exportEvent (js,elm,_) =
341 [ VarDeclStmt () [varDecl "e" $ obj "document.createEvent" <| string "Event"]
343 obj "e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
344 , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "e") "value") (ref "v")
345 , ExprStmt () $ obj "document.dispatchEvent" <| ref "e"
346 , ReturnStmt () (Just $ ref "v")
353 do es <- mapM expression (e1 : collect [] e2)
354 return $ ["$"] ==> foldr (<|) (ref "$") es
356 do e2' <- expression e2
357 es <- mapM expression (collect [] e1)
358 return $ foldr (<|) e2' es
360 do e1' <- expression e1
362 return $ obj "_L.append" `call` [e1', e2']
363 "::" -> expression (L span (Data "::" [e1,e2]))
365 do e1' <- expression e1
367 return $ case Map.lookup op opDict of
369 Nothing -> ref "A2" `call` [ func, e1', e2' ]
373 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
379 func | isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
380 | otherwise = dotSep parts
383 operator = last parts
385 opDict = Map.fromList (infixOps ++ specialOps)
387 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
388 infixOp str op = specialOp str (InfixExpr () op)
395 , infixOp "&&" OpLAnd
400 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
401 , specialOp "|>" $ flip (<|)
402 , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
403 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
404 , specialOp "<" $ cmp OpLT 0
405 , specialOp ">" $ cmp OpGT 0
406 , specialOp "<=" $ cmp OpLT 1
407 , specialOp ">=" $ cmp OpGT (-1)
408 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
411 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