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
27split :: String -> [String]
31 case break (=='.') str of
32 (x,'.':rest) | isOp x -> vars ++ [x ++ '.' : rest]
33 | otherwise -> go (vars ++ [x]) rest
36var name = Id () (makeSafe name)
37ref name = VarRef () (var name)
38prop name = PropId () (var name)
39f <| x = CallExpr () f [x]
40args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
41function args stmts = FuncExpr () Nothing (map var args) stmts
45dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
48varDecl :: String -> Expression () -> VarDecl ()
50 VarDecl () (var x) (Just expr)
52include alias moduleName =
53 varDecl alias (obj (moduleName ++ ".make") <| ref "elm")
57 [ varDecl "N" (obj "Elm.Native")
58 , include "_N" "N.Utils"
59 , include "_L" "N.List"
60 , include "_E" "N.Error"
61 , include "_J" "N.JavaScript"
62 , varDecl "$moduleName" (string name)
65literal :: Literal -> Expression ()
68 Chr c -> obj "_N.chr" <| string [c]
70 IntNum n -> IntLit () n
71 FloatNum n -> NumLit () n
72 Boolean b -> BoolLit () b
74expression :: LExpr () () -> State Int (Expression ())
75expression (L span expr) =
77 Var x -> return $ ref x
78 Literal lit -> return $ literal lit
81 do lo' <- expression lo
83 return $ obj "_L.range" `call` [lo',hi']
87 return $ DotRef () e' (var x)
91 return $ obj "_N.remove" `call` [string x, e']
96 return $ obj "_N.insert" `call` [string x, v', e']
100 fs' <- forM fs $ \(f,v) -> do
102 return $ ArrayLit () [string f, v']
103 return $ obj "_N.replace" `call` [ArrayLit () fs', e']
106 do fields' <- forM fields $ \(f,e) -> do
107 (,) f <$> expression e
108 let fieldMap = List.foldl' combine Map.empty fields'
109 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
111 combine r (k,v) = Map.insertWith (++) k [v] r
112 prop = PropId () . var
113 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
114 Map.toList . Map.filter (not . null) $ Map.map tail fs
115 visible fs = map (first prop) . Map.toList $ Map.map head fs
117 Binop op e1 e2 -> binop span op e1 e2
119 Lambda p e@(L s _) ->
120 do body' <- expression body
121 return $ case length args < 2 || length args > 9 of
122 True -> foldr (==>) body' (map (:[]) args)
123 False -> ref ("F" ++ show (length args)) <| (args ==> body')
125 (args, body) = foldr depattern ([], innerBody) (zip patterns [0..])
127 depattern (pattern,n) (args, body) =
129 PVar x -> (args ++ [x], body)
130 _ -> let arg = "arg" ++ show n
131 in (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
133 (patterns, innerBody) = collect [p] e
135 collect patterns lexpr@(L _ expr) =
137 Lambda p e -> collect (p:patterns) e
138 _ -> (patterns, lexpr)
141 do func' <- expression func
142 args' <- mapM expression args
143 return $ case args' of
144 [arg] -> func' <| arg
145 _ | length args' <= 9 -> ref aN `call` (func':args')
146 | otherwise -> foldl1 (<|) (func':args')
148 aN = "A" ++ show (length args)
149 (func, args) = getArgs e1 [e2]
152 (L _ (App f arg)) -> getArgs f (arg : args)
156 do let (defs',e') = SD.flattenLets defs e
157 stmts <- concat <$> mapM definition defs'
159 return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
162 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
163 return $ case last branches of
164 (L _ (Var "Basics.otherwise"), e) -> ifs (init branches') (snd (last branches'))
166 (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
168 ifs branches finally = foldr iff finally branches
169 iff (if', then') else' = CondExpr () if' then' else'
172 do (tempVar,initialMatch) <- Case.toMatch cases
173 (revisedMatch, stmt) <-
175 L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
176 _ -> do e' <- expression e
177 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
178 match' <- match span revisedMatch
179 return (function [] (stmt ++ match') `call` [])
182 do es' <- mapM expression es
183 return $ obj "_J.toList" <| ArrayLit () es'
186 do es' <- mapM expression es
187 return $ ObjectLit () (ctor : fields es')
189 ctor = (prop "ctor", string (makeSafe name))
190 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
192 Markdown uid doc es ->
193 do es' <- mapM expression es
194 return $ obj "Text.markdown" `call` (string md : string uid : es')
196 pad = "<div style=\"height:0;width:0;\"> </div>"
197 md = pad ++ Pan.writeHtmlString Pan.def doc ++ pad
199definition :: Def () () -> State Int [Statement ()]
202 TypeAnnotation _ _ -> return []
204 Def pattern expr@(L span _) -> do
205 expr' <- expression expr
206 let assign x = varDecl x expr'
210 let op = LBracket () (ref "_op") (string x) in
211 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
213 return [ VarDeclStmt () [ assign x ] ]
216 let setField f = varDecl f (dotSep ["$",f]) in
217 return [ VarDeclStmt () (assign "$" : map setField fields) ]
219 PData name patterns | vars /= Nothing ->
221 Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
223 vars = getVars patterns
226 PVar x : rest -> (x:) `fmap` getVars rest
230 decl x n = varDecl x (dotSep ["$","_" ++ show n])
232 | isTuple name = assign "$" : vars
233 | otherwise = safeAssign : vars
235 safeAssign = varDecl "$" (CondExpr () if' expr' exception)
236 if' = InfixExpr () OpStrictEq (obj "$.ctor") (string name)
237 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
240 do defs' <- concat <$> mapM toDef vars
241 return (VarDeclStmt () [assign "$"] : defs')
243 vars = Set.toList $ SD.boundVars pattern
245 toDef y = definition $
246 Def (PVar y) (L span $ Case (mkVar "$") [(pattern, mkVar y)])
248match :: (Show a) => a -> Case.Match () () -> State Int [Statement ()]
251 Case.Match name clauses mtch' ->
252 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
253 mtch'' <- match span mtch'
254 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
256 isLiteral p = case p of
257 Case.Clause (Right _) _ _ -> True
259 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
261 | or isChars = InfixExpr () OpAdd e (string "")
265 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
267 Case.Break -> return [BreakStmt () Nothing]
269 do e' <- expression e
270 return [ ReturnStmt () (Just e') ]
271 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
276 Case.Other _ -> acc ++ [m]
277 _ -> dropEnd (acc ++ [m]) ms
279clause span variable (Case.Clause value vars mtch) =
280 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
282 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
285 Right (Chr c) -> (True, string [c])
286 _ -> (,) False $ case value of
287 Right (Boolean b) -> BoolLit () b
288 Right lit -> literal lit
289 Left name -> string $ case List.elemIndices '.' name of
291 is -> drop (last is + 1) name
294jsModule :: MetadataModule () () -> String
296 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
297 [ assign ("Elm" : names modul ++ ["make"]) (function ["elm"] programStmts) ]
299 thisModule = dotSep ("elm" : names modul ++ ["values"])
301 concat [ setup (Just "elm") (names modul ++ ["values"])
302 , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
303 , [ internalImports (List.intercalate "." (names modul)) ]
304 , concatMap jsImport (imports modul)
305 , concatMap importEvent (foreignImports modul)
306 , [ assign ["_op"] (ObjectLit () []) ]
307 , concat $ evalState (mapM definition . fst . SD.flattenLets [] $ program modul) 0
308 , map exportEvent $ foreignExports modul
310 , [ ReturnStmt () (Just thisModule) ]
313 jsExports = assign ("elm" : names modul ++ ["values"]) (ObjectLit () exs)
315 exs = map entry . filter (not . isOp) $ "_op" : exports modul
316 entry x = (PropId () (var x), ref x)
320 [x] -> VarDeclStmt () [ varDecl x expr ]
322 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
324 jsImport (modul,_) = setup Nothing path ++ [ include ]
327 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "elm"
329 setup namespace path = map create paths
331 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
332 paths = case namespace of
333 Nothing -> tail . init $ List.inits path
334 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
336 addId js = InfixExpr () OpAdd (string (js++"_")) (obj "elm.id")
338 importEvent (js,base,elm,_) =
339 [ VarDeclStmt () [ varDecl elm $ obj "Signal.constant" <| evalState (expression base) 0 ]
341 obj "document.addEventListener" `call`
344 [ ExprStmt () $ obj "elm.notify" `call` [dotSep [elm,"id"], obj "e.value"] ]
348 exportEvent (js,elm,_) =
353 [ VarDeclStmt () [varDecl "e" $ obj "document.createEvent" <| string "Event"]
355 obj "e.initEvent" `call` [ addId js, BoolLit () True, BoolLit () True ]
356 , ExprStmt () $ AssignExpr () OpAssign (LDot () (ref "e") "value") (ref "v")
357 , ExprStmt () $ obj "document.dispatchEvent" <| ref "e"
358 , ReturnStmt () (Just $ ref "v")
365 do es <- mapM expression (e1 : collect [] e2)
366 return $ ["$"] ==> foldr (<|) (ref "$") es
368 do e2' <- expression e2
369 es <- mapM expression (collect [] e1)
370 return $ foldr (<|) e2' es
372 do e1' <- expression e1
374 return $ obj "_L.append" `call` [e1', e2']
375 "::" -> expression (L span (Data "::" [e1,e2]))
377 do e1' <- expression e1
379 return $ case Map.lookup op opDict of
381 Nothing -> ref "A2" `call` [ func, e1', e2' ]
385 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
391 func | isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
392 | otherwise = dotSep parts
395 operator = last parts
397 opDict = Map.fromList (infixOps ++ specialOps)
399 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
400 infixOp str op = specialOp str (InfixExpr () op)
407 , infixOp "&&" OpLAnd
412 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
413 , specialOp "|>" $ flip (<|)
414 , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
415 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
416 , specialOp "<" $ cmp OpLT 0
417 , specialOp ">" $ cmp OpGT 0
418 , specialOp "<=" $ cmp OpLT 1
419 , specialOp ">=" $ cmp OpGT (-1)
420 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
423 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