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
20import AST.Expression.General
21import qualified AST.Expression.Canonical as Canonical
22import qualified AST.Module as Module
23import qualified AST.Helpers as Help
25import qualified AST.Pattern as P
26import AST.PrettyPrint (renderPretty)
27import qualified AST.Variable as Var
29varDecl :: String -> Expression () -> VarDecl ()
31 VarDecl () (var x) (Just expr)
33include :: String -> String -> VarDecl ()
34include alias moduleName =
35 varDecl alias (obj (moduleName ++ ".make") <| ref "_elm")
37internalImports :: String -> Statement ()
40 [ varDecl "_N" (obj "Elm.Native")
41 , include "_U" "_N.Utils"
42 , include "_L" "_N.List"
43 , include "_A" "_N.Array"
44 , include "_E" "_N.Error"
45 , varDecl "$moduleName" (string name)
48literal :: Literal -> Expression ()
51 Chr c -> obj "_U.chr" <| string [c]
53 IntNum n -> IntLit () n
54 FloatNum n -> NumLit () n
55 Boolean b -> BoolLit () b
57canonical :: Var.Canonical -> Expression ()
58canonical (Var.Canonical home name) =
59 case Help.isOp name of
60 True -> BracketRef () (dotSep (home' ++ ["_op"])) (string name)
61 False -> dotSep (home' ++ [map (swap '\'' '$') name])
63 swap from to c = if c == from then to else c
69 Var.Module path -> [ map (swap '.' '$') path ]
71expression :: Canonical.Expr -> State Int (Expression ())
72expression (A region expr) =
74 Var var -> return $ canonical var
76 Literal lit -> return $ literal lit
79 do lo' <- expression lo
81 return $ obj "_L.range" `call` [lo',hi']
85 return $ DotRef () e' (var x)
89 return $ obj "_U.remove" `call` [string x, e']
94 return $ obj "_U.insert" `call` [string x, v', e']
98 fs' <- forM fs $ \(f,v) -> do
100 return $ ArrayLit () [string f, v']
101 return $ obj "_U.replace" `call` [ArrayLit () fs', e']
104 do fields' <- forM fields $ \(f,e) -> do
105 (,) f <$> expression e
106 let fieldMap = List.foldl' combine Map.empty fields'
107 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
109 combine r (k,v) = Map.insertWith (++) k [v] r
110 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
111 Map.toList . Map.filter (not . null) $ Map.map tail fs
112 visible fs = map (first prop) . Map.toList $ Map.map head fs
114 Binop op e1 e2 -> binop region op e1 e2
116 Lambda p e@(A ann _) ->
117 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
118 body' <- expression body
119 return $ case length args < 2 || length args > 9 of
120 True -> foldr (==>) body' (map (:[]) args)
121 False -> ref ("F" ++ show (length args)) <| (args ==> body')
123 depattern (args, body) pattern =
125 P.Var x -> return (args ++ [x], body)
126 _ -> do arg <- Case.newVar
127 return ( args ++ [arg]
128 , A ann (Case (A ann (localVar arg)) [(pattern, body)]))
130 (patterns, innerBody) = collect [p] e
132 collect patterns lexpr@(A _ expr) =
134 Lambda p e -> collect (p:patterns) e
135 _ -> (patterns, lexpr)
138 do func' <- expression func
139 args' <- mapM expression args
140 return $ case args' of
141 [arg] -> func' <| arg
142 _ | length args' <= 9 -> ref aN `call` (func':args')
143 | otherwise -> foldl1 (<|) (func':args')
145 aN = "A" ++ show (length args)
146 (func, args) = getArgs e1 [e2]
149 (A _ (App f arg)) -> getArgs f (arg : args)
153 do let (defs',e') = flattenLets defs e
154 stmts <- concat <$> mapM definition defs'
156 return $ function [] (stmts ++ [ ret exp ]) `call` []
159 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
160 return $ case last branches of
161 (A _ (Var (Var.Canonical (Var.Module "Basics") "otherwise")), _) ->
163 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
164 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (renderPretty region) ])
166 safeIfs branches = ifs (init branches) (snd (last branches))
167 ifs branches finally = foldr iff finally branches
168 iff (if', then') else' = CondExpr () if' then' else'
171 do (tempVar,initialMatch) <- Case.toMatch cases
172 (revisedMatch, stmt) <-
174 A _ (Var (Var.Canonical Var.Local x)) ->
175 return (Case.matchSubst [(tempVar,x)] initialMatch, [])
177 do e' <- expression e
178 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
179 match' <- match region revisedMatch
180 return (function [] (stmt ++ match') `call` [])
183 do es' <- mapM expression es
184 return $ obj "_L.fromArray" <| ArrayLit () es'
187 do es' <- mapM expression es
188 return $ ObjectLit () (ctor : fields es')
190 ctor = (prop "ctor", string name)
191 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
193 Markdown uid doc es ->
194 do es' <- mapM expression es
195 return $ obj "Text.markdown" `call` (string md : string uid : es')
197 pad = "<div style=\"height:0;width:0;\"> </div>"
198 md = pad ++ MD.toHtml doc ++ pad
200 GLShader _uid src _tipe ->
201 return $ ObjectLit () [(PropString () "src", literal (Str src))]
204 return $ obj "Native.Ports.portIn" `call` [ string name, Port.incoming tipe ]
206 PortOut name tipe value ->
207 do value' <- expression value
208 return $ obj "Native.Ports.portOut" `call`
209 [ string name, Port.outgoing tipe, value' ]
211definition :: Canonical.Def -> State Int [Statement ()]
212definition (Canonical.Definition pattern expr@(A region _) _) = do
213 expr' <- expression expr
214 let assign x = varDecl x expr'
218 let op = LBracket () (ref "_op") (string x) in
219 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
221 return [ VarDeclStmt () [ assign x ] ]
224 let setField f = varDecl f (dotSep ["$",f]) in
225 return [ VarDeclStmt () (assign "$" : map setField fields) ]
227 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
228 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
230 vars = getVars patterns
233 P.Var x : rest -> (x:) `fmap` getVars rest
237 decl x n = varDecl x (dotSep ["$","_" ++ show n])
239 | Help.isTuple name = assign "$" : vars
240 | otherwise = assign "_raw" : safeAssign : vars
242 safeAssign = varDecl "$" (CondExpr () if' (obj "_raw") exception)
243 if' = InfixExpr () OpStrictEq (obj "_raw.ctor") (string name)
244 exception = obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]
247 do defs' <- concat <$> mapM toDef vars
248 return (VarDeclStmt () [assign "_"] : defs')
250 vars = P.boundVarList pattern
251 mkVar = A region . localVar
252 toDef y = let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
253 in definition $ Canonical.Definition (P.Var y) expr Nothing
255match :: Region -> Case.Match -> State Int [Statement ()]
258 Case.Match name clauses mtch' ->
259 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
260 mtch'' <- match region mtch'
261 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
263 isLiteral p = case p of
264 Case.Clause (Right _) _ _ -> True
268 | any isLiteral clauses = obj name
269 | otherwise = dotSep (split name ++ ["ctor"])
272 | or isChars = InfixExpr () OpAdd e (string "")
276 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]) ]
278 Case.Break -> return [BreakStmt () Nothing]
281 do e' <- expression e
284 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
289 Case.Other _ -> acc ++ [m]
290 _ -> dropEnd (acc ++ [m]) ms
292clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
293clause region variable (Case.Clause value vars mtch) =
294 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
296 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
299 Right (Chr c) -> (True, string [c])
300 _ -> (,) False $ case value of
301 Right (Boolean b) -> BoolLit () b
302 Right lit -> literal lit
303 Left (Var.Canonical _ name) ->
306flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
307flattenLets defs lexpr@(A _ expr) =
309 Let ds body -> flattenLets (defs ++ ds) body
312generate :: CanonicalModule -> String
314 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
315 [ assign ("Elm" : names ++ ["make"]) (function ["_elm"] programStmts) ]
317 names = Module.names modul
319 thisModule = dotSep ("_elm" : names ++ ["values"])
322 [ [ ExprStmt () $ string "use strict" ]
323 , setup "_elm" (names ++ ["values"])
324 , [ IfSingleStmt () thisModule (ret thisModule) ]
325 , [ internalImports (List.intercalate "." names) ]
326 , map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
327 , [ assign ["_op"] (ObjectLit () []) ]
328 , concat $ evalState (mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)) 0
333 jsExports = assign ("_elm" : names ++ ["values"]) (ObjectLit () exs)
335 exs = map entry $ "_op" : concatMap extract (exports modul)
336 entry x = (prop x, ref x)
340 Var.Value x | Help.isOp x -> []
342 Var.ADT _ (Var.Listing ctors _) -> ctors
346 [x] -> VarDeclStmt () [ varDecl x expr ]
348 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
351 assign [dolarize name] $ dotSep ("Elm" : Help.splitDots name ++ ["make"]) <| ref "_elm"
353 setup namespace path = map create paths
355 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
356 paths = drop 2 . init . List.inits $ namespace : path
358binop :: Region -> Var.Canonical -> Canonical.Expr -> Canonical.Expr
359 -> State Int (Expression ())
360binop region (Var.Canonical home op) e1 e2 =
362 (Var.Module "Basics", ".") ->
363 do es <- mapM expression (e1 : collect [] e2)
364 return $ ["$"] ==> foldr (<|) (ref "$") es
366 (Var.Module "Basics", "<|") ->
367 do e2' <- expression e2
368 es <- mapM expression (collect [] e1)
369 return $ foldr (<|) e2' es
371 (Var.Module "List", "++") ->
372 do e1' <- expression e1
374 return $ obj "_L.append" `call` [e1', e2']
376 (Var.BuiltIn, "::") ->
377 expression (A region (Data "::" [e1,e2]))
379 (Var.Module "Basics", _) ->
380 do e1' <- expression e1
382 return $ case Map.lookup op basicOps of
384 Nothing -> ref "A2" `call` [ func, e1', e2' ]
387 do e1' <- expression e1
389 return (ref "A2" `call` [ func, e1', e2' ])
394 A _ (Binop (Var.Canonical (Var.Module "Basics") ".") e1 e2) ->
395 collect (es ++ [e1]) e2
398 func | Help.isOp op = BracketRef () (dotSep (path ++ ["_op"])) (string op)
399 | otherwise = dotSep (path ++ [op])
404 Var.Module m -> [dolarize m]
406 basicOps = Map.fromList (infixOps ++ specialOps)
409 let infixOp str op = (str, InfixExpr () op) in
414 , infixOp "&&" OpLAnd
419 [ (,) "^" $ \a b -> obj "Math.pow" `call` [a,b]
420 , (,) "|>" $ flip (<|)
421 , (,) "==" $ \a b -> obj "_U.eq" `call` [a,b]
422 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_U.eq" `call` [a,b])
423 , (,) "<" $ cmp OpLT 0
424 , (,) ">" $ cmp OpGT 0
425 , (,) "<=" $ cmp OpLT 1
426 , (,) ">=" $ cmp OpGT (-1)
427 , (,) "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
430 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