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 as Help
14import qualified Generate.Cases as Case
15import qualified Generate.JavaScript.Ports as Port
16import qualified Generate.JavaScript.Variable as Var
17import qualified Generate.Markdown as MD
21import AST.Expression.General
22import qualified AST.Expression.Canonical as Canonical
23import qualified AST.Module as Module
24import qualified AST.Helpers as Help
26import qualified AST.Pattern as P
27import qualified AST.Variable as Var
29varDecl :: String -> Expression () -> VarDecl ()
31 VarDecl () (var x) (Just expr)
33internalImports :: String -> [VarDecl ()]
35 [ varDecl "_N" (obj ["Elm","Native"])
36 , include "_U" "Utils"
38 , include "_A" "Array"
39 , include "_E" "Error"
40 , varDecl Help.localModuleName (string name)
43 include :: String -> String -> VarDecl ()
45 varDecl alias (obj ["_N", modul, "make"] <| ref "_elm")
47_Utils :: String -> Expression ()
48_Utils x = obj ["_U", x]
50_List :: String -> Expression ()
51_List x = obj ["_L", x]
53literal :: Literal -> Expression ()
56 Chr c -> _Utils "chr" <| string [c]
58 IntNum n -> IntLit () n
59 FloatNum n -> NumLit () n
60 Boolean b -> BoolLit () b
62expression :: Canonical.Expr -> State Int (Expression ())
63expression (A region expr) =
65 Var var -> return $ Var.canonical var
67 Literal lit -> return $ literal lit
70 do lo' <- expression lo
72 return $ _List "range" `call` [lo',hi']
76 return $ DotRef () e' (var x)
80 return $ _Utils "remove" `call` [string x, e']
85 return $ _Utils "insert" `call` [string x, v', e']
89 fs' <- forM fs $ \(f,v) -> do
91 return $ ArrayLit () [string f, v']
92 return $ _Utils "replace" `call` [ArrayLit () fs', e']
95 do fields' <- forM fields $ \(f,e) -> do
96 (,) f <$> expression e
97 let fieldMap = List.foldl' combine Map.empty fields'
98 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
100 combine r (k,v) = Map.insertWith (++) k [v] r
101 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
102 Map.toList . Map.filter (not . null) $ Map.map tail fs
103 visible fs = map (first prop) . Map.toList $ Map.map head fs
105 Binop op e1 e2 -> binop region op e1 e2
107 Lambda p e@(A ann _) ->
108 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
109 body' <- expression body
110 return $ case length args < 2 || length args > 9 of
111 True -> foldr (==>) body' (map (:[]) args)
112 False -> ref ("F" ++ show (length args)) <| (args ==> body')
114 depattern (args, body) pattern =
116 P.Var x -> return (args ++ [ Var.varName x ], body)
117 _ -> do arg <- Case.newVar
118 return ( args ++ [arg]
119 , A ann (Case (A ann (localVar arg)) [(pattern, body)]))
121 (patterns, innerBody) = collect [p] e
123 collect patterns lexpr@(A _ expr) =
125 Lambda p e -> collect (p:patterns) e
126 _ -> (patterns, lexpr)
129 do func' <- expression func
130 args' <- mapM expression args
131 return $ case args' of
132 [arg] -> func' <| arg
133 _ | length args' <= 9 -> ref aN `call` (func':args')
134 | otherwise -> foldl1 (<|) (func':args')
136 aN = "A" ++ show (length args)
137 (func, args) = getArgs e1 [e2]
140 (A _ (App f arg)) -> getArgs f (arg : args)
144 do let (defs',e') = flattenLets defs e
145 stmts <- concat <$> mapM definition defs'
147 return $ function [] (stmts ++ [ ret exp ]) `call` []
150 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
151 return $ case last branches of
152 (A _ (Var (Var.Canonical (Var.Module "Basics") "otherwise")), _) ->
154 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
155 _ -> ifs branches' (throw "If" region)
157 safeIfs branches = ifs (init branches) (snd (last branches))
158 ifs branches finally = foldr iff finally branches
159 iff (if', then') else' = CondExpr () if' then' else'
162 do (tempVar,initialMatch) <- Case.toMatch cases
163 (revisedMatch, stmt) <-
165 A _ (Var (Var.Canonical Var.Local x)) ->
166 return (Case.matchSubst [(tempVar,x)] initialMatch, [])
168 do e' <- expression e
169 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
170 match' <- match region revisedMatch
171 return (function [] (stmt ++ match') `call` [])
174 do es' <- mapM expression es
175 return $ _List "fromArray" <| ArrayLit () es'
178 do es' <- mapM expression es
179 return $ ObjectLit () (ctor : fields es')
181 ctor = (prop "ctor", string name)
182 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
184 Markdown uid doc es ->
185 do es' <- mapM expression es
186 return $ obj ["Text","markdown"] `call` (string md : string uid : es')
188 pad = "<div style=\"height:0;width:0;\"> </div>"
189 md = pad ++ MD.toHtml doc ++ pad
191 GLShader _uid src _tipe ->
192 return $ ObjectLit () [(PropString () "src", literal (Str src))]
195 return $ Var.nativePorts "portIn" `call` [ string name, Port.incoming tipe ]
197 PortOut name tipe value ->
198 do value' <- expression value
199 return $ Var.nativePorts "portOut" `call`
200 [ string name, Port.outgoing tipe, value' ]
202definition :: Canonical.Def -> State Int [Statement ()]
203definition (Canonical.Definition pattern expr@(A region _) _) = do
204 expr' <- expression expr
205 let assign x = varDecl x expr'
209 let op = LBracket () (ref "_op") (string x) in
210 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
212 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
215 let setField f = varDecl f (obj ["$",f]) in
216 return [ VarDeclStmt () (assign "$" : map setField fields) ]
218 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
219 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
221 vars = getVars patterns
224 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
228 decl x n = varDecl x (obj ["$","_" ++ show n])
230 | Help.isTuple name = assign "$" : vars
231 | otherwise = assign "_raw" : safeAssign : vars
233 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
234 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
235 exception = Help.throw "Case" region
238 do defs' <- concat <$> mapM toDef vars
239 return (VarDeclStmt () [assign "_"] : defs')
241 vars = P.boundVarList pattern
242 mkVar = A region . localVar
243 toDef y = let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
244 in definition $ Canonical.Definition (P.Var y) expr Nothing
246match :: Region -> Case.Match -> State Int [Statement ()]
249 Case.Match name clauses mtch' ->
250 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
251 mtch'' <- match region mtch'
252 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
254 isLiteral p = case p of
255 Case.Clause (Right _) _ _ -> True
259 | any isLiteral clauses = obj [name]
260 | otherwise = obj (Help.splitDots name ++ ["ctor"])
263 | or isChars = InfixExpr () OpAdd e (string "")
267 return [ ExprStmt () (Help.throw "Case" region) ]
269 Case.Break -> return [BreakStmt () Nothing]
272 do e' <- expression e
275 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
280 Case.Other _ -> acc ++ [m]
281 _ -> dropEnd (acc ++ [m]) ms
283clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
284clause region variable (Case.Clause value vars mtch) =
285 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
287 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
290 Right (Chr c) -> (True, string [c])
291 _ -> (,) False $ case value of
292 Right (Boolean b) -> BoolLit () b
293 Right lit -> literal lit
294 Left (Var.Canonical _ name) ->
297flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
298flattenLets defs lexpr@(A _ expr) =
300 Let ds body -> flattenLets (defs ++ ds) body
303generate :: CanonicalModule -> String
305 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
306 [ assign ("Elm" : names ++ ["make"]) (function ["_elm"] programStmts) ]
309 names = Module.names modul
311 thisModule :: Expression ()
312 thisModule = obj ("_elm" : names ++ ["values"])
314 programStmts :: [Statement ()]
317 [ [ ExprStmt () (string "use strict") ]
318 , setup "_elm" (names ++ ["values"])
319 , [ IfSingleStmt () thisModule (ret thisModule) ]
320 , [ VarDeclStmt () localVars ]
326 localVars :: [VarDecl ()]
327 localVars = varDecl "_op" (ObjectLit () []) :
328 internalImports (Module.getName modul) ++
331 explicitImports :: [VarDecl ()]
333 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
335 jsImport :: String -> VarDecl ()
337 varDecl (Var.moduleName name) $
338 obj ("Elm" : Help.splitDots name ++ ["make"]) <| ref "_elm"
340 body :: [Statement ()]
341 body = concat $ evalState defs 0
343 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
345 setup namespace path = map create paths
347 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
348 paths = drop 2 . init . List.inits $ namespace : path
350 jsExports = assign ("_elm" : names ++ ["values"]) (ObjectLit () exs)
352 exs = map entry $ "_op" : concatMap extract (exports modul)
353 entry x = (prop x, ref x)
357 Var.Value x | Help.isOp x -> []
359 Var.ADT _ (Var.Listing ctors _) -> ctors
363 [x] -> VarDeclStmt () [ varDecl x expr ]
365 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
367binop :: Region -> Var.Canonical -> Canonical.Expr -> Canonical.Expr
368 -> State Int (Expression ())
369binop region func@(Var.Canonical home op) e1 e2 =
371 (Var.Module "Basics", ".") ->
372 do es <- mapM expression (e1 : collect [] e2)
373 return $ ["$"] ==> foldr (<|) (ref "$") es
375 (Var.Module "Basics", "<|") ->
376 do e2' <- expression e2
377 es <- mapM expression (collect [] e1)
378 return $ foldr (<|) e2' es
380 (Var.Module "List", "++") ->
381 do e1' <- expression e1
383 return $ _List "append" `call` [e1', e2']
385 (Var.BuiltIn, "::") ->
386 expression (A region (Data "::" [e1,e2]))
388 (Var.Module "Basics", _) ->
389 do e1' <- expression e1
391 return $ case Map.lookup op basicOps of
393 Nothing -> ref "A2" `call` [ Var.canonical func, e1', e2' ]
396 do e1' <- expression e1
398 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
403 A _ (Binop (Var.Canonical (Var.Module "Basics") ".") e1 e2) ->
404 collect (es ++ [e1]) e2
407 basicOps = Map.fromList (infixOps ++ specialOps)
410 let infixOp str op = (str, InfixExpr () op) in
415 , infixOp "&&" OpLAnd
420 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
421 , (,) "|>" $ flip (<|)
422 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
423 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
424 , (,) "<" $ cmp OpLT 0
425 , (,) ">" $ cmp OpGT 0
426 , (,) "<=" $ cmp OpLT 1
427 , (,) ">=" $ cmp OpGT (-1)
428 , (,) "div" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
431 cmp op n a b = InfixExpr () op (_Utils "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