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 "_E" "_N.Error"
44 , varDecl "$moduleName" (string name)
47literal :: Literal -> Expression ()
50 Chr c -> obj "_U.chr" <| string [c]
52 IntNum n -> IntLit () n
53 FloatNum n -> NumLit () n
54 Boolean b -> BoolLit () b
56canonical :: Var.Canonical -> Expression ()
57canonical (Var.Canonical home name) =
58 case Help.isOp name of
59 True -> BracketRef () (dotSep (home' ++ ["_op"])) (string name)
60 False -> dotSep (home' ++ [map (swap '\'' '$') name])
62 swap from to c = if c == from then to else c
68 Var.Module path -> [ map (swap '.' '$') path ]
70expression :: Canonical.Expr -> State Int (Expression ())
71expression (A region expr) =
73 Var var -> return $ canonical var
75 Literal lit -> return $ literal lit
78 do lo' <- expression lo
80 return $ obj "_L.range" `call` [lo',hi']
84 return $ DotRef () e' (var x)
88 return $ obj "_U.remove" `call` [string x, e']
93 return $ obj "_U.insert" `call` [string x, v', e']
97 fs' <- forM fs $ \(f,v) -> do
99 return $ ArrayLit () [string f, v']
100 return $ obj "_U.replace" `call` [ArrayLit () fs', e']
103 do fields' <- forM fields $ \(f,e) -> do
104 (,) f <$> expression e
105 let fieldMap = List.foldl' combine Map.empty fields'
106 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
108 combine r (k,v) = Map.insertWith (++) k [v] r
109 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
110 Map.toList . Map.filter (not . null) $ Map.map tail fs
111 visible fs = map (first prop) . Map.toList $ Map.map head fs
113 Binop op e1 e2 -> binop region op e1 e2
115 Lambda p e@(A ann _) ->
116 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
117 body' <- expression body
118 return $ case length args < 2 || length args > 9 of
119 True -> foldr (==>) body' (map (:[]) args)
120 False -> ref ("F" ++ show (length args)) <| (args ==> body')
122 depattern (args, body) pattern =
124 P.Var x -> return (args ++ [x], body)
125 _ -> do arg <- Case.newVar
126 return ( args ++ [arg]
127 , A ann (Case (A ann (localVar arg)) [(pattern, body)]))
129 (patterns, innerBody) = collect [p] e
131 collect patterns lexpr@(A _ expr) =
133 Lambda p e -> collect (p:patterns) e
134 _ -> (patterns, lexpr)
137 do func' <- expression func
138 args' <- mapM expression args
139 return $ case args' of
140 [arg] -> func' <| arg
141 _ | length args' <= 9 -> ref aN `call` (func':args')
142 | otherwise -> foldl1 (<|) (func':args')
144 aN = "A" ++ show (length args)
145 (func, args) = getArgs e1 [e2]
148 (A _ (App f arg)) -> getArgs f (arg : args)
152 do let (defs',e') = flattenLets defs e
153 stmts <- concat <$> mapM definition defs'
155 return $ function [] (stmts ++ [ ret exp ]) `call` []
158 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
159 return $ case last branches of
160 (A _ (Var (Var.Canonical (Var.Module "Basics") "otherwise")), _) ->
162 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
163 _ -> ifs branches' (obj "_E.If" `call` [ ref "$moduleName", string (renderPretty region) ])
165 safeIfs branches = ifs (init branches) (snd (last branches))
166 ifs branches finally = foldr iff finally branches
167 iff (if', then') else' = CondExpr () if' then' else'
170 do (tempVar,initialMatch) <- Case.toMatch cases
171 (revisedMatch, stmt) <-
173 A _ (Var (Var.Canonical Var.Local x)) ->
174 return (Case.matchSubst [(tempVar,x)] initialMatch, [])
176 do e' <- expression e
177 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
178 match' <- match region revisedMatch
179 return (function [] (stmt ++ match') `call` [])
182 do es' <- mapM expression es
183 return $ obj "_L.fromArray" <| 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 GLShader _uid src _tipe ->
200 return $ ObjectLit () [(PropString () "src", literal (Str src))]
203 return $ obj "Native.Ports.portIn" `call` [ string name, Port.incoming tipe ]
205 PortOut name tipe value ->
206 do value' <- expression value
207 return $ obj "Native.Ports.portOut" `call`
208 [ string name, Port.outgoing tipe, value' ]
210definition :: Canonical.Def -> State Int [Statement ()]
211definition (Canonical.Definition pattern expr@(A region _) _) = do
212 expr' <- expression expr
213 let assign x = varDecl x expr'
217 let op = LBracket () (ref "_op") (string x) in
218 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
220 return [ VarDeclStmt () [ assign x ] ]
223 let setField f = varDecl f (dotSep ["$",f]) in
224 return [ VarDeclStmt () (assign "$" : map setField fields) ]
226 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
227 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
229 vars = getVars patterns
232 P.Var x : rest -> (x:) `fmap` getVars rest
236 decl x n = varDecl x (dotSep ["$","_" ++ show n])
238 | Help.isTuple name = assign "$" : vars
239 | otherwise = assign "_raw" : safeAssign : vars
241 safeAssign = varDecl "$" (CondExpr () if' (obj "_raw") exception)
242 if' = InfixExpr () OpStrictEq (obj "_raw.ctor") (string name)
243 exception = obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]
246 do defs' <- concat <$> mapM toDef vars
247 return (VarDeclStmt () [assign "_"] : defs')
249 vars = P.boundVarList pattern
250 mkVar = A region . localVar
251 toDef y = let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
252 in definition $ Canonical.Definition (P.Var y) expr Nothing
254match :: Region -> Case.Match -> State Int [Statement ()]
257 Case.Match name clauses mtch' ->
258 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
259 mtch'' <- match region mtch'
260 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
262 isLiteral p = case p of
263 Case.Clause (Right _) _ _ -> True
267 | any isLiteral clauses = obj name
268 | otherwise = dotSep (split name ++ ["ctor"])
271 | or isChars = InfixExpr () OpAdd e (string "")
275 return [ ExprStmt () (obj "_E.Case" `call` [ref "$moduleName", string (renderPretty region)]) ]
277 Case.Break -> return [BreakStmt () Nothing]
280 do e' <- expression e
283 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
288 Case.Other _ -> acc ++ [m]
289 _ -> dropEnd (acc ++ [m]) ms
291clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
292clause region variable (Case.Clause value vars mtch) =
293 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
295 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
298 Right (Chr c) -> (True, string [c])
299 _ -> (,) False $ case value of
300 Right (Boolean b) -> BoolLit () b
301 Right lit -> literal lit
302 Left (Var.Canonical _ name) ->
305flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
306flattenLets defs lexpr@(A _ expr) =
308 Let ds body -> flattenLets (defs ++ ds) body
311generate :: CanonicalModule -> String
313 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
314 [ assign ("Elm" : names ++ ["make"]) (function ["_elm"] programStmts) ]
316 names = Module.names modul
318 thisModule = dotSep ("_elm" : names ++ ["values"])
321 [ [ ExprStmt () $ string "use strict" ]
322 , setup "_elm" (names ++ ["values"])
323 , [ IfSingleStmt () thisModule (ret thisModule) ]
324 , [ internalImports (List.intercalate "." names) ]
325 , map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
326 , [ assign ["_op"] (ObjectLit () []) ]
327 , concat $ evalState (mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)) 0
332 jsExports = assign ("_elm" : names ++ ["values"]) (ObjectLit () exs)
334 exs = map entry $ "_op" : concatMap extract (exports modul)
335 entry x = (prop x, ref x)
339 Var.Value x | Help.isOp x -> []
341 Var.ADT _ (Var.Listing ctors _) -> ctors
345 [x] -> VarDeclStmt () [ varDecl x expr ]
347 AssignExpr () OpAssign (LDot () (dotSep (init path)) (last path)) expr
350 assign [dolarize name] $ dotSep ("Elm" : Help.splitDots name ++ ["make"]) <| ref "_elm"
352 setup namespace path = map create paths
354 create name = assign name (InfixExpr () OpLOr (dotSep name) (ObjectLit () []))
355 paths = drop 2 . init . List.inits $ namespace : path
357binop :: Region -> Var.Canonical -> Canonical.Expr -> Canonical.Expr
358 -> State Int (Expression ())
359binop region (Var.Canonical home op) e1 e2 =
361 (Var.Module "Basics", ".") ->
362 do es <- mapM expression (e1 : collect [] e2)
363 return $ ["$"] ==> foldr (<|) (ref "$") es
365 (Var.Module "Basics", "<|") ->
366 do e2' <- expression e2
367 es <- mapM expression (collect [] e1)
368 return $ foldr (<|) e2' es
370 (Var.Module "List", "++") ->
371 do e1' <- expression e1
373 return $ obj "_L.append" `call` [e1', e2']
375 (Var.BuiltIn, "::") ->
376 expression (A region (Data "::" [e1,e2]))
378 (Var.Module "Basics", _) ->
379 do e1' <- expression e1
381 return $ case Map.lookup op basicOps of
383 Nothing -> ref "A2" `call` [ func, e1', e2' ]
386 do e1' <- expression e1
388 return (ref "A2" `call` [ func, e1', e2' ])
393 A _ (Binop (Var.Canonical (Var.Module "Basics") ".") e1 e2) ->
394 collect (es ++ [e1]) e2
397 func | Help.isOp op = BracketRef () (dotSep (path ++ ["_op"])) (string op)
398 | otherwise = dotSep (path ++ [op])
403 Var.Module m -> [dolarize m]
405 basicOps = Map.fromList (infixOps ++ specialOps)
408 let infixOp str op = (str, InfixExpr () op) in
413 , infixOp "&&" OpLAnd
418 [ (,) "^" $ \a b -> obj "Math.pow" `call` [a,b]
419 , (,) "|>" $ flip (<|)
420 , (,) "==" $ \a b -> obj "_U.eq" `call` [a,b]
421 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (obj "_U.eq" `call` [a,b])
422 , (,) "<" $ cmp OpLT 0
423 , (,) ">" $ cmp OpGT 0
424 , (,) "<=" $ cmp OpLT 1
425 , (,) ">=" $ cmp OpGT (-1)
426 , (,) "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
429 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