2module Generate.JavaScript (generate) where
4import Control.Arrow (first,(***))
5import Control.Applicative ((<$>),(<*>))
6import Control.Monad.State
7import qualified Data.List as List
8import qualified Data.Map as Map
9import qualified Data.Set as Set
11import qualified Generate.Cases as Case
12import qualified Generate.Markdown as MD
13import qualified SourceSyntax.Helpers as Help
14import SourceSyntax.Literal
15import SourceSyntax.Pattern as Pattern
16import SourceSyntax.Location
17import SourceSyntax.Expression
18import SourceSyntax.Module
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
43 x:xs -> foldl (DotRef ()) (ref x) (map var xs)
44 [] -> error "dotSep must be called on a non-empty list of variables"
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 "_U" "_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 "_U.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 "_U.remove" `call` [string x, e']
96 return $ obj "_U.insert" `call` [string x, v', e']
100 fs' <- forM fs $ \(f,v) -> do
102 return $ ArrayLit () [string f, v']
103 return $ obj "_U.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 (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
121 body' <- expression body
122 return $ case length args < 2 || length args > 9 of
123 True -> foldr (==>) body' (map (:[]) args)
124 False -> ref ("F" ++ show (length args)) <| (args ==> body')
126 depattern (args, body) pattern =
128 PVar x -> return (args ++ [x], body)
129 _ -> do arg <- Case.newVar
130 return (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
132 (patterns, innerBody) = collect [p] e
134 collect patterns lexpr@(L _ expr) =
136 Lambda p e -> collect (p:patterns) e
137 _ -> (patterns, lexpr)
140 do func' <- expression func
141 args' <- mapM expression args
142 return $ case args' of
143 [arg] -> func' <| arg
144 _ | length args' <= 9 -> ref aN `call` (func':args')
145 | otherwise -> foldl1 (<|) (func':args')
147 aN = "A" ++ show (length args)
148 (func, args) = getArgs e1 [e2]
151 (L _ (App f arg)) -> getArgs f (arg : args)
155 do let (defs',e') = flattenLets defs e
156 stmts <- concat <$> mapM definition defs'
158 return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
161 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
162 return $ case last branches of
163 (L _ (Var "Basics.otherwise"), _) -> safeIfs branches'
164 (L _ (Literal (Boolean True)), _) -> safeIfs branches'
165 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
167 safeIfs branches = ifs (init branches) (snd (last branches))
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 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 ++ MD.toHtml doc ++ pad
199 PortIn name _ _ handler ->
200 do handler' <- expression handler
201 return $ obj "Native.Ports.portIn" `call` [ string name, handler' ]
203 PortOut name _ signal ->
204 do signal' <- expression signal
205 return $ obj "Native.Ports.portOut" `call` [ string name, signal' ]
207definition :: Def -> State Int [Statement ()]
208definition (Definition pattern expr@(L span _) _) = do
209 expr' <- expression expr
210 let assign x = varDecl x expr'
214 let op = LBracket () (ref "_op") (string x) in
215 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
217 return [ VarDeclStmt () [ assign x ] ]
220 let setField f = varDecl f (dotSep ["$",f]) in
221 return [ VarDeclStmt () (assign "$" : map setField fields) ]
223 PData name patterns | vars /= Nothing ->
224 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
226 vars = getVars patterns
229 PVar x : rest -> (x:) `fmap` getVars rest
233 decl x n = varDecl x (dotSep ["$","_" ++ show n])
235 | Help.isTuple name = assign "$" : vars
236 | otherwise = assign "$raw" : safeAssign : vars
238 safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
239 if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
240 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
243 do defs' <- concat <$> mapM toDef vars
244 return (VarDeclStmt () [assign "$"] : defs')
246 vars = Set.toList $ Pattern.boundVars pattern
248 toDef y = let expr = L span $ Case (mkVar "$") [(pattern, mkVar y)]
249 in definition $ Definition (PVar y) expr Nothing
251match :: (Show a) => a -> Case.Match -> State Int [Statement ()]
254 Case.Match name clauses mtch' ->
255 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
256 mtch'' <- match span mtch'
257 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
259 isLiteral p = case p of
260 Case.Clause (Right _) _ _ -> True
262 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
264 | or isChars = InfixExpr () OpAdd e (string "")
268 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
270 Case.Break -> return [BreakStmt () Nothing]
272 do e' <- expression e
273 return [ ReturnStmt () (Just e') ]
274 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
279 Case.Other _ -> acc ++ [m]
280 _ -> dropEnd (acc ++ [m]) ms
282clause span variable (Case.Clause value vars mtch) =
283 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
285 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
288 Right (Chr c) -> (True, string [c])
289 _ -> (,) False $ case value of
290 Right (Boolean b) -> BoolLit () b
291 Right lit -> literal lit
292 Left name -> string $ case List.elemIndices '.' name of
294 is -> drop (last is + 1) name
296flattenLets defs lexpr@(L _ expr) =
298 Let ds body -> flattenLets (defs ++ ds) body
301generate :: MetadataModule -> String
302generate unsafeModule =
303 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
304 [ assign ("Elm" : names modul ++ ["make"]) (function ["_elm"] programStmts) ]
306 modul = MakeSafe.metadataModule unsafeModule
307 thisModule = dotSep ("_elm" : names modul ++ ["values"])
310 [ setup (Just "_elm") (names modul ++ ["values"])
311 , [ IfSingleStmt () thisModule (ReturnStmt () (Just thisModule)) ]
312 , [ internalImports (List.intercalate "." (names modul)) ]
313 , concatMap jsImport . Set.toList . Set.fromList . map fst $ imports modul
314 , [ assign ["_op"] (ObjectLit () []) ]
315 , concat $ evalState (mapM definition . fst . flattenLets [] $ program modul) 0
317 , [ ReturnStmt () (Just thisModule) ]
320 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
322 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
323 entry x = (PropId () (var x), ref x)
327 [x] -> VarDeclStmt () [ varDecl x expr ]
329 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
331 jsImport modul = setup Nothing path ++ [ include ]
334 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "_elm"
336 setup namespace path = map create paths
338 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
339 paths = case namespace of
340 Nothing -> tail . init $ List.inits path
341 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
346 do es <- mapM expression (e1 : collect [] e2)
347 return $ ["$"] ==> foldr (<|) (ref "$") es
349 do e2' <- expression e2
350 es <- mapM expression (collect [] e1)
351 return $ foldr (<|) e2' es
353 do e1' <- expression e1
355 return $ obj "_L.append" `call` [e1', e2']
356 "::" -> expression (L span (Data "::" [e1,e2]))
358 do e1' <- expression e1
360 return $ case Map.lookup op opDict of
362 Nothing -> ref "A2" `call` [ func, e1', e2' ]
366 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
369 func | Help.isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
370 | otherwise = dotSep parts
373 operator = last parts
375 opDict = Map.fromList (infixOps ++ specialOps)
377 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
378 infixOp str op = specialOp str (InfixExpr () op)
385 , infixOp "&&" OpLAnd
390 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
391 , specialOp "|>" $ flip (<|)
392 , specialOp "==" $ \a b -> obj "_U.eq" `call` [a,b]
393 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_U.eq" `call` [a,b])
394 , specialOp "<" $ cmp OpLT 0
395 , specialOp ">" $ cmp OpGT 0
396 , specialOp "<=" $ cmp OpLT 1
397 , specialOp ">=" $ cmp OpGT (-1)
398 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
401 cmp op n a b = InfixExpr () op (obj "_U.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