2module Generate.JavaScript (generate) where
4import Control.Applicative ((<$>),(<*>))
5import Control.Arrow (first,(***))
6import Control.Monad.State
7import qualified Data.List as List
8import qualified Data.Map as Map
9import qualified Data.Set as Set
10import Language.ECMAScript3.PrettyPrint
11import Language.ECMAScript3.Syntax
13import Generate.JavaScript.Helpers
14import qualified Generate.Cases as Case
15import qualified Generate.JavaScript.Ports as Port
16import qualified Generate.Markdown as MD
17import SourceSyntax.Annotation
18import SourceSyntax.Expression
19import qualified SourceSyntax.Helpers as Help
20import SourceSyntax.Literal
21import SourceSyntax.Module
22import qualified SourceSyntax.Pattern as P
23import SourceSyntax.PrettyPrint (renderPretty)
24import qualified SourceSyntax.Variable as V
25import qualified Transform.SafeNames as MakeSafe
27varDecl :: String -> Expression () -> VarDecl ()
29 VarDecl () (var x) (Just expr)
31include :: String -> String -> VarDecl ()
32include alias moduleName =
33 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
35internalImports :: String -> Statement ()
38 [ varDecl "_N" (obj "Elm.Native")
39 , include "_U" "_N.Utils"
40 , include "_L" "_N.List"
41 , include "_E" "_N.Error"
42 , include "_J" "_N.JavaScript"
43 , varDecl "$moduleName" (string name)
46literal :: Literal -> Expression ()
49 Chr c -> obj "_U.chr" <| string [c]
51 IntNum n -> IntLit () n
52 FloatNum n -> NumLit () n
53 Boolean b -> BoolLit () b
55expression :: Expr -> State Int (Expression ())
56expression (A region expr) =
58 Var (V.Raw x) -> return $ ref x
59 Literal lit -> return $ literal lit
62 do lo' <- expression lo
64 return $ obj "_L.range" `call` [lo',hi']
68 return $ DotRef () e' (var x)
72 return $ obj "_U.remove" `call` [string x, e']
77 return $ obj "_U.insert" `call` [string x, v', e']
81 fs' <- forM fs $ \(f,v) -> do
83 return $ ArrayLit () [string f, v']
84 return $ obj "_U.replace" `call` [ArrayLit () fs', e']
87 do fields' <- forM fields $ \(f,e) -> do
88 (,) f <$> expression e
89 let fieldMap = List.foldl' combine Map.empty fields'
90 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
92 combine r (k,v) = Map.insertWith (++) k [v] r
93 prop = PropId () . var
94 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
95 Map.toList . Map.filter (not . null) $ Map.map tail fs
96 visible fs = map (first prop) . Map.toList $ Map.map head fs
98 Binop op e1 e2 -> binop region op e1 e2
100 Lambda p e@(A ann _) ->
101 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
102 body' <- expression body
103 return $ case length args < 2 || length args > 9 of
104 True -> foldr (==>) body' (map (:[]) args)
105 False -> ref ("F" ++ show (length args)) <| (args ==> body')
107 depattern (args, body) pattern =
109 P.Var x -> return (args ++ [x], body)
110 _ -> do arg <- Case.newVar
111 return ( args ++ [arg]
112 , A ann (Case (A ann (rawVar arg)) [(pattern, body)]))
114 (patterns, innerBody) = collect [p] e
116 collect patterns lexpr@(A _ expr) =
118 Lambda p e -> collect (p:patterns) e
119 _ -> (patterns, lexpr)
122 do func' <- expression func
123 args' <- mapM expression args
124 return $ case args' of
125 [arg] -> func' <| arg
126 _ | length args' <= 9 -> ref aN `call` (func':args')
127 | otherwise -> foldl1 (<|) (func':args')
129 aN = "A" ++ show (length args)
130 (func, args) = getArgs e1 [e2]
133 (A _ (App f arg)) -> getArgs f (arg : args)
137 do let (defs',e') = flattenLets defs e
138 stmts <- concat <$> mapM definition defs'
140 return $ function [] (stmts ++ [ ret exp ]) `call` []
143 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
144 return $ case last branches of
145 (A _ (Var (V.Raw "Basics.otherwise")), _) -> safeIfs branches'
146 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
147 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (renderPretty region) ])
149 safeIfs branches = ifs (init branches) (snd (last branches))
150 ifs branches finally = foldr iff finally branches
151 iff (if', then') else' = CondExpr () if' then' else'
154 do (tempVar,initialMatch) <- Case.toMatch cases
155 (revisedMatch, stmt) <-
157 A _ (Var (V.Raw x)) ->
158 return (Case.matchSubst [(tempVar,x)] initialMatch, [])
160 do e' <- expression e
161 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
162 match' <- match region revisedMatch
163 return (function [] (stmt ++ match') `call` [])
166 do es' <- mapM expression es
167 return $ obj "_J.toList" <| ArrayLit () es'
170 do es' <- mapM expression es
171 return $ ObjectLit () (ctor : fields es')
173 ctor = (prop "ctor", string name)
174 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
176 Markdown uid doc es ->
177 do es' <- mapM expression es
178 return $ obj "Text.markdown" `call` (string md : string uid : es')
180 pad = "<div style=\"height:0;width:0;\"> </div>"
181 md = pad ++ MD.toHtml doc ++ pad
183 GLShader _uid src _tipe ->
184 return $ ObjectLit () [(PropString () "src", literal (Str src))]
187 return $ obj "Native.Ports.portIn" `call` [ string name, Port.incoming tipe ]
189 PortOut name tipe value ->
190 do value' <- expression value
191 return $ obj "Native.Ports.portOut" `call`
192 [ string name, Port.outgoing tipe, value' ]
194definition :: Def -> State Int [Statement ()]
195definition (Definition pattern expr@(A region _) _) = 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 P.Data name patterns | vars /= Nothing ->
211 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
213 vars = getVars patterns
216 P.Var x : rest -> (x:) `fmap` getVars rest
220 decl x n = varDecl x (dotSep ["$","_" ++ show n])
222 | Help.isTuple name = assign "$" : vars
223 | otherwise = assign "$raw" : safeAssign : vars
225 safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
226 if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
227 exception = obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]
230 do defs' <- concat <$> mapM toDef vars
231 return (VarDeclStmt () [assign "$"] : defs')
233 vars = P.boundVarList pattern
234 mkVar = A region . rawVar
235 toDef y = let expr = A region $ Case (mkVar "$") [(pattern, mkVar y)]
236 in definition $ Definition (P.Var y) expr Nothing
238match :: Region -> Case.Match -> State Int [Statement ()]
241 Case.Match name clauses mtch' ->
242 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
243 mtch'' <- match region mtch'
244 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
246 isLiteral p = case p of
247 Case.Clause (Right _) _ _ -> True
249 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
251 | or isChars = InfixExpr () OpAdd e (string "")
255 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]) ]
257 Case.Break -> return [BreakStmt () Nothing]
259 do e' <- expression e
261 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
266 Case.Other _ -> acc ++ [m]
267 _ -> dropEnd (acc ++ [m]) ms
269clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
270clause region variable (Case.Clause value vars mtch) =
271 (,) isChar . CaseClause () pattern <$> match region (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 :: [Def] -> Expr -> ([Def], Expr)
285flattenLets defs lexpr@(A _ expr) =
287 Let ds body -> flattenLets (defs ++ ds) body
290generate :: MetadataModule -> String
291generate unsafeModule =
292 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
293 [ assign ("Elm" : names modul ++ ["make"]) (function ["_elm"] programStmts) ]
295 modul = MakeSafe.metadataModule unsafeModule
296 thisModule = dotSep ("_elm" : names modul ++ ["values"])
299 [ [ ExprStmt () $ string "use strict" ]
300 , setup (Just "_elm") (names modul ++ ["values"])
301 , [ IfSingleStmt () thisModule (ret thisModule) ]
302 , [ internalImports (List.intercalate "." (names modul)) ]
303 , concatMap jsImport . Set.toList . Set.fromList . map fst $ imports modul
304 , [ assign ["_op"] (ObjectLit () []) ]
305 , concat $ evalState (mapM definition . fst . flattenLets [] $ program modul) 0
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 ]
323 path = Help.splitDots modul
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
333binop :: Region -> String -> Expr -> Expr -> State Int (Expression ())
334binop region op e1 e2 =
337 do es <- mapM expression (e1 : collect [] e2)
338 return $ ["$"] ==> foldr (<|) (ref "$") es
340 do e2' <- expression e2
341 es <- mapM expression (collect [] e1)
342 return $ foldr (<|) e2' es
344 do e1' <- expression e1
346 return $ obj "_L.append" `call` [e1', e2']
347 "::" -> expression (A region (Data "::" [e1,e2]))
349 do e1' <- expression e1
351 return $ case Map.lookup op opDict of
353 Nothing -> ref "A2" `call` [ func, e1', e2' ]
357 A _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
360 func | Help.isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
361 | otherwise = dotSep parts
363 parts = Help.splitDots op
364 operator = last parts
366 opDict = Map.fromList (infixOps ++ specialOps)
368 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
369 infixOp str op = specialOp str (InfixExpr () op)
376 , infixOp "&&" OpLAnd
381 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
382 , specialOp "|>" $ flip (<|)
383 , specialOp "==" $ \a b -> obj "_U.eq" `call` [a,b]
384 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_U.eq" `call` [a,b])
385 , specialOp "<" $ cmp OpLT 0
386 , specialOp ">" $ cmp OpGT 0
387 , specialOp "<=" $ cmp OpLT 1
388 , specialOp ">=" $ cmp OpGT (-1)
389 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
392 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