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 Generate.JavaScript.Helpers
12import qualified Generate.Cases as Case
13import qualified Generate.JavaScript.Ports as Port
14import qualified Generate.Markdown as MD
15import qualified SourceSyntax.Helpers as Help
16import SourceSyntax.Literal
17import SourceSyntax.Pattern as Pattern
18import SourceSyntax.Location
19import SourceSyntax.Expression
20import SourceSyntax.Module
21import Language.ECMAScript3.Syntax
22import Language.ECMAScript3.PrettyPrint
23import qualified Transform.SafeNames as MakeSafe
25varDecl :: String -> Expression () -> VarDecl ()
27 VarDecl () (var x) (Just expr)
29include :: String -> String -> VarDecl ()
30include alias moduleName =
31 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
33internalImports :: String -> Statement ()
36 [ varDecl "_N" (obj "Elm.Native")
37 , include "_U" "_N.Utils"
38 , include "_L" "_N.List"
39 , include "_E" "_N.Error"
40 , include "_J" "_N.JavaScript"
41 , varDecl "$moduleName" (string name)
44literal :: Literal -> Expression ()
47 Chr c -> obj "_U.chr" <| string [c]
49 IntNum n -> IntLit () n
50 FloatNum n -> NumLit () n
51 Boolean b -> BoolLit () b
53expression :: LExpr -> State Int (Expression ())
54expression (L span expr) =
56 Var x -> return $ ref x
57 Literal lit -> return $ literal lit
60 do lo' <- expression lo
62 return $ obj "_L.range" `call` [lo',hi']
66 return $ DotRef () e' (var x)
70 return $ obj "_U.remove" `call` [string x, e']
75 return $ obj "_U.insert" `call` [string x, v', e']
79 fs' <- forM fs $ \(f,v) -> do
81 return $ ArrayLit () [string f, v']
82 return $ obj "_U.replace" `call` [ArrayLit () fs', e']
85 do fields' <- forM fields $ \(f,e) -> do
86 (,) f <$> expression e
87 let fieldMap = List.foldl' combine Map.empty fields'
88 return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
90 combine r (k,v) = Map.insertWith (++) k [v] r
91 prop = PropId () . var
92 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
93 Map.toList . Map.filter (not . null) $ Map.map tail fs
94 visible fs = map (first prop) . Map.toList $ Map.map head fs
96 Binop op e1 e2 -> binop span op e1 e2
99 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
100 body' <- expression body
101 return $ case length args < 2 || length args > 9 of
102 True -> foldr (==>) body' (map (:[]) args)
103 False -> ref ("F" ++ show (length args)) <| (args ==> body')
105 depattern (args, body) pattern =
107 PVar x -> return (args ++ [x], body)
108 _ -> do arg <- Case.newVar
109 return (args ++ [arg], L s (Case (L s (Var arg)) [(pattern, body)]))
111 (patterns, innerBody) = collect [p] e
113 collect patterns lexpr@(L _ expr) =
115 Lambda p e -> collect (p:patterns) e
116 _ -> (patterns, lexpr)
119 do func' <- expression func
120 args' <- mapM expression args
121 return $ case args' of
122 [arg] -> func' <| arg
123 _ | length args' <= 9 -> ref aN `call` (func':args')
124 | otherwise -> foldl1 (<|) (func':args')
126 aN = "A" ++ show (length args)
127 (func, args) = getArgs e1 [e2]
130 (L _ (App f arg)) -> getArgs f (arg : args)
134 do let (defs',e') = flattenLets defs e
135 stmts <- concat <$> mapM definition defs'
137 return $ function [] (stmts ++ [ ret exp ]) `call` []
140 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
141 return $ case last branches of
142 (L _ (Var "Basics.otherwise"), _) -> safeIfs branches'
143 (L _ (Literal (Boolean True)), _) -> safeIfs branches'
144 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (show span) ])
146 safeIfs branches = ifs (init branches) (snd (last branches))
147 ifs branches finally = foldr iff finally branches
148 iff (if', then') else' = CondExpr () if' then' else'
151 do (tempVar,initialMatch) <- Case.toMatch cases
152 (revisedMatch, stmt) <-
154 L _ (Var x) -> return (Case.matchSubst [(tempVar,x)] initialMatch, [])
155 _ -> do e' <- expression e
156 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
157 match' <- match span revisedMatch
158 return (function [] (stmt ++ match') `call` [])
161 do es' <- mapM expression es
162 return $ obj "_J.toList" <| ArrayLit () es'
165 do es' <- mapM expression es
166 return $ ObjectLit () (ctor : fields es')
168 ctor = (prop "ctor", string name)
169 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
171 Markdown uid doc es ->
172 do es' <- mapM expression es
173 return $ obj "Text.markdown" `call` (string md : string uid : es')
175 pad = "<div style=\"height:0;width:0;\"> </div>"
176 md = pad ++ MD.toHtml doc ++ pad
178 GLShader _uid src _tipe ->
179 return $ ObjectLit () [(PropString () "src", literal (Str src))]
182 return $ obj "Native.Ports.portIn" `call` [ string name, Port.incoming tipe ]
184 PortOut name tipe value ->
185 do value' <- expression value
186 return $ obj "Native.Ports.portOut" `call`
187 [ string name, Port.outgoing tipe, value' ]
189definition :: Def -> State Int [Statement ()]
190definition (Definition pattern expr@(L span _) _) = do
191 expr' <- expression expr
192 let assign x = varDecl x expr'
196 let op = LBracket () (ref "_op") (string x) in
197 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
199 return [ VarDeclStmt () [ assign x ] ]
202 let setField f = varDecl f (dotSep ["$",f]) in
203 return [ VarDeclStmt () (assign "$" : map setField fields) ]
205 PData name patterns | vars /= Nothing ->
206 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
208 vars = getVars patterns
211 PVar x : rest -> (x:) `fmap` getVars rest
215 decl x n = varDecl x (dotSep ["$","_" ++ show n])
217 | Help.isTuple name = assign "$" : vars
218 | otherwise = assign "$raw" : safeAssign : vars
220 safeAssign = varDecl "$" (CondExpr () if' (obj "$raw") exception)
221 if' = InfixExpr () OpStrictEq (obj "$raw.ctor") (string name)
222 exception = obj "_E.Case" `call` [ref "$moduleName", string (show span)]
225 do defs' <- concat <$> mapM toDef vars
226 return (VarDeclStmt () [assign "$"] : defs')
228 vars = Set.toList $ Pattern.boundVars pattern
230 toDef y = let expr = L span $ Case (mkVar "$") [(pattern, mkVar y)]
231 in definition $ Definition (PVar y) expr Nothing
233match :: SrcSpan -> Case.Match -> State Int [Statement ()]
236 Case.Match name clauses mtch' ->
237 do (isChars, clauses') <- unzip <$> mapM (clause span name) clauses
238 mtch'' <- match span mtch'
239 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
241 isLiteral p = case p of
242 Case.Clause (Right _) _ _ -> True
244 access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
246 | or isChars = InfixExpr () OpAdd e (string "")
250 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (show span)]) ]
252 Case.Break -> return [BreakStmt () Nothing]
254 do e' <- expression e
256 Case.Seq ms -> concat <$> mapM (match span) (dropEnd [] ms)
261 Case.Other _ -> acc ++ [m]
262 _ -> dropEnd (acc ++ [m]) ms
264clause :: SrcSpan -> String -> Case.Clause -> State Int (Bool, CaseClause ())
265clause span variable (Case.Clause value vars mtch) =
266 (,) isChar . CaseClause () pattern <$> match span (Case.matchSubst (zip vars vars') mtch)
268 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
271 Right (Chr c) -> (True, string [c])
272 _ -> (,) False $ case value of
273 Right (Boolean b) -> BoolLit () b
274 Right lit -> literal lit
275 Left name -> string $ case List.elemIndices '.' name of
277 is -> drop (last is + 1) name
279flattenLets :: [Def] -> LExpr -> ([Def], LExpr)
280flattenLets defs lexpr@(L _ expr) =
282 Let ds body -> flattenLets (defs ++ ds) body
285generate :: MetadataModule -> String
286generate unsafeModule =
287 show . prettyPrint $ setup (Just "Elm") (names modul ++ ["make"]) ++
288 [ assign ("Elm" : names modul ++ ["make"]) (function ["_elm"] programStmts) ]
290 modul = MakeSafe.metadataModule unsafeModule
291 thisModule = dotSep ("_elm" : names modul ++ ["values"])
294 [ setup (Just "_elm") (names modul ++ ["values"])
295 , [ IfSingleStmt () thisModule (ret thisModule) ]
296 , [ internalImports (List.intercalate "." (names modul)) ]
297 , concatMap jsImport . Set.toList . Set.fromList . map fst $ imports modul
298 , [ assign ["_op"] (ObjectLit () []) ]
299 , concat $ evalState (mapM definition . fst . flattenLets [] $ program modul) 0
304 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
306 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
307 entry x = (PropId () (var x), ref x)
311 [x] -> VarDeclStmt () [ varDecl x expr ]
313 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
315 jsImport modul = setup Nothing path ++ [ include ]
317 path = Help.splitDots modul
318 include = assign path $ dotSep ("Elm" : path ++ ["make"]) <| ref "_elm"
320 setup namespace path = map create paths
322 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
323 paths = case namespace of
324 Nothing -> tail . init $ List.inits path
325 Just nmspc -> drop 2 . init . List.inits $ nmspc : path
327binop :: SrcSpan -> String -> LExpr -> LExpr -> State Int (Expression ())
331 do es <- mapM expression (e1 : collect [] e2)
332 return $ ["$"] ==> foldr (<|) (ref "$") es
334 do e2' <- expression e2
335 es <- mapM expression (collect [] e1)
336 return $ foldr (<|) e2' es
338 do e1' <- expression e1
340 return $ obj "_L.append" `call` [e1', e2']
341 "::" -> expression (L span (Data "::" [e1,e2]))
343 do e1' <- expression e1
345 return $ case Map.lookup op opDict of
347 Nothing -> ref "A2" `call` [ func, e1', e2' ]
351 L _ (Binop op e1 e2) | op == "Basics.." -> collect (es ++ [e1]) e2
354 func | Help.isOp operator = BracketRef () (dotSep (init parts ++ ["_op"])) (string operator)
355 | otherwise = dotSep parts
357 parts = Help.splitDots op
358 operator = last parts
360 opDict = Map.fromList (infixOps ++ specialOps)
362 specialOp str func = [ (str, func), ("Basics." ++ str, func) ]
363 infixOp str op = specialOp str (InfixExpr () op)
370 , infixOp "&&" OpLAnd
375 [ specialOp "^" $ \a b -> obj "Math.pow" `call` [a,b]
376 , specialOp "|>" $ flip (<|)
377 , specialOp "==" $ \a b -> obj "_U.eq" `call` [a,b]
378 , specialOp "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_U.eq" `call` [a,b])
379 , specialOp "<" $ cmp OpLT 0
380 , specialOp ">" $ cmp OpGT 0
381 , specialOp "<=" $ cmp OpLT 1
382 , specialOp ">=" $ cmp OpGT (-1)
383 , specialOp "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
386 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