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 as Pattern
15import SourceSyntax.Location
16import SourceSyntax.Expression
17import SourceSyntax.Module
18import Language.ECMAScript3.Syntax
19import Language.ECMAScript3.PrettyPrint
20import qualified Transform.SafeNames as MakeSafe
22split :: String -> [String]
26 case break (=='.') str of
27 (x,'.':rest) | Help.isOp x -> vars ++ [x ++ '.' : rest]
28 | otherwise -> go (vars ++ [x]) rest
32ref name = VarRef () (var name)
33prop name = PropId () (var name)
34f <| x = CallExpr () f [x]
35args ==> e = FuncExpr () Nothing (map var args) [ ReturnStmt () (Just e) ]
36function args stmts = FuncExpr () Nothing (map var args) stmts
40dotSep (x:xs) = foldl (DotRef ()) (ref x) (map var xs)
43varDecl :: String -> Expression () -> VarDecl ()
45 VarDecl () (var x) (Just expr)
47include alias moduleName =
48 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
52 [ varDecl "N" (obj "Elm.Native")
53 , include "_N" "N.Utils"
54 , include "_L" "N.List"
55 , include "_E" "N.Error"
56 , include "_J" "N.JavaScript"
57 , varDecl "$moduleName" (string name)
60literal :: Literal -> Expression ()
63 Chr c -> obj "_N.chr" <| string [c]
65 IntNum n -> IntLit () n
66 FloatNum n -> NumLit () n
67 Boolean b -> BoolLit () b
69expression :: LExpr -> State Int (Expression ())
70expression (L span expr) =
72 Var x -> return $ ref x
73 Literal lit -> return $ literal lit
76 do lo' <- expression lo
78 return $ obj "_L.range" `call` [lo',hi']
82 return $ DotRef () e' (var x)
86 return $ obj "_N.remove" `call` [string x, e']
91 return $ obj "_N.insert" `call` [string x, v', e']
95 fs' <- forM fs $ \(f,v) -> do
97 return $ ArrayLit () [string f, v']
98 return $ obj "_N.replace" `call` [ArrayLit () fs', e']
101 do fields' <- forM fields $ \(f,e) -> do
102 (,) f <$> expression e
103 let fieldMap = List.foldl' combine Map.empty fields'
104 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
106 combine r (k,v) = Map.insertWith (++) k [v] r
107 prop = PropId () . var
108 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
109 Map.toList . Map.filter (not . null) $ Map.map tail fs
110 visible fs = map (first prop) . Map.toList $ Map.map head fs
112 Binop op e1 e2 -> binop span op e1 e2
114 Lambda p e@(L s _) ->
115 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
116 body' <- expression body
117 return $ case length args < 2 || length args > 9 of
118 True -> foldr (==>) body' (map (:[]) args)
119 False -> ref ("F" ++ show (length args)) <| (args ==> body')
121 depattern (args, body) pattern =
123 PVar x -> return (args ++ [x], body)
124 _ -> do arg <- Case.newVar
125 return (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
127 (patterns, innerBody) = collect [p] e
129 collect patterns lexpr@(L _ expr) =
131 Lambda p e -> collect (p:patterns) e
132 _ -> (patterns, lexpr)
135 do func' <- expression func
136 args' <- mapM expression args
137 return $ case args' of
138 [arg] -> func' <| arg
139 _ | length args' <= 9 -> ref aN `call` (func':args')
140 | otherwise -> foldl1 (<|) (func':args')
142 aN = "A" ++ show (length args)
143 (func, args) = getArgs e1 [e2]
146 (L _ (App f arg)) -> getArgs f (arg : args)
150 do let (defs',e') = flattenLets defs e
151 stmts <- concat <$> mapM definition defs'
153 return $ function [] (stmts ++ [ ReturnStmt () (Just exp) ]) `call` []
156 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
157 return $ case last branches of
158 (L _ (Var "Basics.otherwise"), _) -> safeIfs branches'
159 (L _ (Literal (Boolean True)), _) -> safeIfs branches'
160 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
162 safeIfs branches = ifs (init branches) (snd (last branches))
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 ()]
195definition (Definition pattern expr@(L span _) _) = do
196 expr' <- expression expr
197 let assign x = varDecl x expr'
201 let op = LBracket () (ref "_op") (string x) in
202 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
204 return [ VarDeclStmt () [ assign x ] ]
207 let setField f = varDecl f (dotSep ["$",f]) in
208 return [ VarDeclStmt () (assign "$" : map setField fields) ]
210 PData name patterns | vars /= Nothing ->
212 Just vs -> return [ VarDeclStmt () (setup (zipWith decl vs [0..])) ]
214 vars = getVars patterns
217 PVar x : rest -> (x:) `fmap` getVars rest
221 decl x n = varDecl x (dotSep ["$","_" ++ show n])
223 | Help.isTuple name = assign "$" : vars
224 | otherwise = assign "$raw" : safeAssign : vars
226 safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
227 if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
228 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
231 do defs' <- concat <$> mapM toDef vars
232 return (VarDeclStmt () [assign "$"] : defs')
234 vars = Set.toList $ Pattern.boundVars pattern
236 toDef y = let expr = L span $ Case (mkVar "$") [(pattern, mkVar y)]
237 in definition $ Definition (PVar y) expr Nothing
239match :: (Show a) => a -> Case.Match -> State Int [Statement ()]
242 Case.Match name clauses mtch' ->
243 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
244 mtch'' <- match span mtch'
245 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
247 isLiteral p = case p of
248 Case.Clause (Right _) _ _ -> True
250 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
252 | or isChars = InfixExpr () OpAdd e (string "")
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 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
273 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
276 Right (Chr c) -> (True, string [c])
277 _ -> (,) False $ case value of
278 Right (Boolean b) -> BoolLit () b
279 Right lit -> literal lit
280 Left name -> string $ case List.elemIndices '.' name of
282 is -> drop (last is + 1) name
284flattenLets defs lexpr@(L _ expr) =
286 Let ds body -> flattenLets (defs ++ ds) body
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 , checkInPorts (recvPorts modul)
302 , map inPort (recvPorts modul)
303 , map outPort (sendPorts modul)
304 , [ assign ["_op"] (ObjectLit () []) ]
305 , concat $ evalState (mapM definition . fst . flattenLets [] $ program modul) 0
307 , [ ReturnStmt () (Just thisModule) ]
310 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
312 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
313 entry x = (PropId () (var x), ref x)
317 [x] -> VarDeclStmt () [ varDecl x expr ]
319 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
321 jsImport (modul,_) = setup Nothing path ++ [ include ]
324 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "_elm"
326 setup namespace path = map create paths
328 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
329 paths = case namespace of
330 Nothing -> tail . init $ List.inits path
331 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
333 addId js = InfixExpr () OpAdd (string (js++"_")) (obj "_elm.id")
336 [ ExprStmt () $ obj "_N.checkPorts" `call` [ref "$moduleName", names] ]
338 names = ArrayLit () [ string name | (name, _, _) <- ports ]
340 inPort (name, expr, _) =
341 assign [name] $ obj "_N.inPort" `call` [ dotSep ["_elm","ports_in",name]
342 , evalState (expression expr) 0 ]
344 outPort (name, expr, _) =
345 let signal = evalState (expression expr) 0 in
346 assign ["_elm","ports_out",name] signal
351 do es <- mapM expression (e1 : collect [] e2)
352 return $ ["$"] ==> foldr (<|) (ref "$") es
354 do e2' <- expression e2
355 es <- mapM expression (collect [] e1)
356 return $ foldr (<|) e2' es
358 do e1' <- expression e1
360 return $ obj "_L.append" `call` [e1', e2']
361 "::" -> expression (L span (Data "::" [e1,e2]))
363 do e1' <- expression e1
365 return $ case Map.lookup op opDict of
367 Nothing -> ref "A2" `call` [ func, e1', e2' ]
371 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
377 func | Help.isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
378 | otherwise = dotSep parts
381 operator = last parts
383 opDict = Map.fromList (infixOps ++ specialOps)
385 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
386 infixOp str op = specialOp str (InfixExpr () op)
393 , infixOp "&&" OpLAnd
398 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
399 , specialOp "|>" $ flip (<|)
400 , specialOp "==" $ \a b -> obj "_N.eq" `call` [a,b]
401 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_N.eq" `call` [a,b])
402 , specialOp "<" $ cmp OpLT 0
403 , specialOp ">" $ cmp OpGT 0
404 , specialOp "<=" $ cmp OpLT 1
405 , specialOp ">=" $ cmp OpGT (-1)
406 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
409 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