1module Generate.JavaScript (generate) where
3import Control.Applicative ((<$>),(<*>))
4import Control.Arrow (first,(***))
5import Control.Monad.State
6import qualified Data.List as List
7import qualified Data.Map as Map
8import qualified Data.Set as Set
9import Language.ECMAScript3.PrettyPrint
10import Language.ECMAScript3.Syntax
12import Generate.JavaScript.Helpers as Help
13import qualified Generate.Cases as Case
14import qualified Generate.JavaScript.Ports as Port
15import qualified Generate.JavaScript.Variable as Var
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 qualified AST.Variable as Var
28varDecl :: String -> Expression () -> VarDecl ()
30 VarDecl () (var x) (Just expr)
32internalImports :: Module.Name -> [VarDecl ()]
34 [ varDecl "_N" (obj ["Elm","Native"])
35 , include "_U" "Utils"
37 , varDecl Help.localModuleName (string (Module.nameToString name))
40 include :: String -> String -> VarDecl ()
42 varDecl alias (obj ["_N", modul, "make"] <| ref "_elm")
44_Utils :: String -> Expression ()
45_Utils x = obj ["_U", x]
47_List :: String -> Expression ()
48_List x = obj ["_L", x]
50literal :: Literal -> Expression ()
53 Chr c -> _Utils "chr" <| string [c]
55 IntNum n -> IntLit () n
56 FloatNum n -> NumLit () n
57 Boolean b -> BoolLit () b
59expression :: Canonical.Expr -> State Int (Expression ())
60expression (A region expr) =
62 Var var -> return $ Var.canonical var
64 Literal lit -> return $ literal lit
67 do lo' <- expression lo
69 return $ _List "range" `call` [lo',hi']
73 return $ DotRef () e' (var x)
77 return $ _Utils "remove" `call` [string x, e']
82 return $ _Utils "insert" `call` [string x, v', e']
86 fs' <- forM fs $ \(f,v) -> do
88 return $ ArrayLit () [string f, v']
89 return $ _Utils "replace" `call` [ArrayLit () fs', e']
92 do fields' <- forM fields $ \(f,e) -> do
93 (,) f <$> expression e
94 let fieldMap = List.foldl' combine Map.empty fields'
95 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
97 combine r (k,v) = Map.insertWith (++) k [v] r
98 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
99 Map.toList . Map.filter (not . null) $ Map.map tail fs
100 visible fs = map (first prop) . Map.toList $ Map.map head fs
102 Binop op e1 e2 -> binop region op e1 e2
104 Lambda p e@(A ann _) ->
105 do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
106 body' <- expression body
107 return $ case length args < 2 || length args > 9 of
108 True -> foldr (==>) body' (map (:[]) args)
109 False -> ref ("F" ++ show (length args)) <| (args ==> body')
111 depattern (args, body) pattern =
113 P.Var x -> return (args ++ [ Var.varName x ], body)
114 _ -> do arg <- Case.newVar
115 return ( args ++ [arg]
116 , A ann (Case (A ann (localVar arg)) [(pattern, body)]))
118 (patterns, innerBody) = collect [p] e
120 collect patterns lexpr@(A _ expr) =
122 Lambda p e -> collect (p:patterns) e
123 _ -> (patterns, lexpr)
126 do func' <- expression func
127 args' <- mapM expression args
128 return $ case args' of
129 [arg] -> func' <| arg
130 _ | length args' <= 9 -> ref aN `call` (func':args')
131 | otherwise -> foldl1 (<|) (func':args')
133 aN = "A" ++ show (length args)
134 (func, args) = getArgs e1 [e2]
137 (A _ (App f arg)) -> getArgs f (arg : args)
141 do let (defs',e') = flattenLets defs e
142 stmts <- concat <$> mapM definition defs'
144 return $ function [] (stmts ++ [ ret exp ]) `call` []
147 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
148 return $ case last branches of
149 (A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
151 (A _ (Literal (Boolean True)), _) -> safeIfs branches'
152 _ -> ifs branches' (throw "badIf" region)
154 safeIfs branches = ifs (init branches) (snd (last branches))
155 ifs branches finally = foldr iff finally branches
156 iff (if', then') else' = CondExpr () if' then' else'
159 do (tempVar,initialMatch) <- Case.toMatch cases
160 (revisedMatch, stmt) <-
162 A _ (Var (Var.Canonical Var.Local x)) ->
163 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
165 do e' <- expression e
166 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
167 match' <- match region revisedMatch
168 return (function [] (stmt ++ match') `call` [])
171 do es' <- mapM expression es
172 return $ _List "fromArray" <| ArrayLit () es'
175 do es' <- mapM expression es
176 return $ ObjectLit () (ctor : fields es')
178 ctor = (prop "ctor", string name)
179 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
182 return $ Var.value ["Text"] "markdown" `call` [ string md, string uid ]
184 pad = "<div style=\"height:0;width:0;\"> </div>"
185 md = pad ++ MD.toHtml doc ++ pad
187 GLShader _uid src _tipe ->
188 return $ ObjectLit () [(PropString () "src", literal (Str src))]
191 return $ Var.value ["Native","Ports"] "portIn" `call`
192 [ string name, Port.incoming tipe ]
194 PortOut name tipe value ->
195 do value' <- expression value
196 return $ Var.value ["Native","Ports"] "portOut" `call`
197 [ string name, Port.outgoing tipe, value' ]
199definition :: Canonical.Def -> State Int [Statement ()]
200definition (Canonical.Definition pattern expr@(A region _) _) = do
201 expr' <- expression expr
202 let assign x = varDecl x expr'
206 let op = LBracket () (ref "_op") (string x) in
207 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
209 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
212 let setField f = varDecl f (obj ["$",f]) in
213 return [ VarDeclStmt () (assign "$" : map setField fields) ]
215 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
216 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
218 vars = getVars patterns
221 P.Var x : rest -> (Var.varName x :) `fmap` getVars rest
225 decl x n = varDecl x (obj ["$","_" ++ show n])
227 | Help.isTuple name = assign "$" : vars
228 | otherwise = assign "_raw" : safeAssign : vars
230 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
231 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
232 exception = Help.throw "badCase" region
235 do defs' <- concat <$> mapM toDef vars
236 return (VarDeclStmt () [assign "_"] : defs')
238 vars = P.boundVarList pattern
239 mkVar = A region . localVar
240 toDef y = let expr = A region $ Case (mkVar "_") [(pattern, mkVar y)]
241 in definition $ Canonical.Definition (P.Var y) expr Nothing
243match :: Region -> Case.Match -> State Int [Statement ()]
246 Case.Match name clauses mtch' ->
247 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
248 mtch'' <- match region mtch'
249 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
251 isLiteral p = case p of
252 Case.Clause (Right _) _ _ -> True
256 | any isLiteral clauses = obj [name]
257 | otherwise = obj (Help.splitDots name ++ ["ctor"])
260 | or isChars = InfixExpr () OpAdd e (string "")
264 return [ ExprStmt () (Help.throw "badCase" region) ]
266 Case.Break -> return [BreakStmt () Nothing]
269 do e' <- expression e
272 Case.Seq ms -> concat <$> mapM (match region) (dropEnd [] ms)
277 Case.Other _ -> acc ++ [m]
278 _ -> dropEnd (acc ++ [m]) ms
280clause :: Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
281clause region variable (Case.Clause value vars mtch) =
282 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
284 vars' = map (\n -> variable ++ "._" ++ show n) [0..]
287 Right (Chr c) -> (True, string [c])
288 _ -> (,) False $ case value of
289 Right (Boolean b) -> BoolLit () b
290 Right lit -> literal lit
291 Left (Var.Canonical _ name) ->
294flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
295flattenLets defs lexpr@(A _ expr) =
297 Let ds body -> flattenLets (defs ++ ds) body
300generate :: CanonicalModule -> String
302 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
303 [ assign ("Elm" : names ++ ["make"]) (function ["_elm"] programStmts) ]
306 names = Module.names modul
308 thisModule :: Expression ()
309 thisModule = obj ("_elm" : names ++ ["values"])
311 programStmts :: [Statement ()]
314 [ [ ExprStmt () (string "use strict") ]
315 , setup "_elm" (names ++ ["values"])
316 , [ IfSingleStmt () thisModule (ret thisModule) ]
317 , [ VarDeclStmt () localVars ]
323 localVars :: [VarDecl ()]
324 localVars = varDecl "_op" (ObjectLit () []) :
325 internalImports (Module.names modul) ++
328 explicitImports :: [VarDecl ()]
330 map jsImport . Set.toList . Set.fromList . map fst $ Module.imports modul
332 jsImport :: Module.Name -> VarDecl ()
334 varDecl (Var.moduleName name) $
335 obj ("Elm" : name ++ ["make"]) <| ref "_elm"
337 body :: [Statement ()]
338 body = concat $ evalState defs 0
340 defs = mapM definition . fst . flattenLets [] $ Module.program (Module.body modul)
342 setup namespace path = map create paths
344 create name = assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
345 paths = drop 2 . init . List.inits $ namespace : path
347 jsExports = assign ("_elm" : names ++ ["values"]) (ObjectLit () exs)
349 exs = map entry $ "_op" : concatMap extract (exports modul)
350 entry x = (prop x, ref x)
354 Var.Value x | Help.isOp x -> []
355 | otherwise -> [Var.varName x]
356 Var.ADT _ (Var.Listing ctors _) -> map Var.varName ctors
360 [x] -> VarDeclStmt () [ varDecl x expr ]
362 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
364binop :: Region -> Var.Canonical -> Canonical.Expr -> Canonical.Expr
365 -> State Int (Expression ())
366binop region func@(Var.Canonical home op) e1 e2 =
368 (Var.Module ["Basics"], ">>") ->
369 do es <- mapM expression (collectLeftAssoc [e2] e1)
370 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
372 (Var.Module ["Basics"], "<<") ->
373 do es <- mapM expression (e1 : collectRightAssoc [] e2)
374 return $ ["$"] ==> foldr (<|) (ref "$") es
376 (Var.Module ["Basics"], "<|") ->
377 do e2' <- expression e2
378 es <- mapM expression (collectRightAssoc [] e1)
379 return $ foldr (<|) e2' es
381 (Var.Module ["List"], "++") ->
382 do e1' <- expression e1
384 return $ _List "append" `call` [e1', e2']
386 (Var.BuiltIn, "::") ->
387 expression (A region (Data "::" [e1,e2]))
389 (Var.Module ["Basics"], _) ->
390 do e1' <- expression e1
392 return $ case Map.lookup op basicOps of
394 Nothing -> ref "A2" `call` [ Var.canonical func, e1', e2' ]
397 do e1' <- expression e1
399 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
402 collectRightAssoc es e =
404 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
405 collectRightAssoc (es ++ [e1]) e2
408 collectLeftAssoc es e =
410 A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
411 collectLeftAssoc (e2 : es) e1
414 basicOps = Map.fromList (infixOps ++ specialOps)
417 let infixOp str op = (str, InfixExpr () op) in
422 , infixOp "&&" OpLAnd
427 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
428 , (,) "|>" $ flip (<|)
429 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
430 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
431 , (,) "<" $ cmp OpLT 0
432 , (,) ">" $ cmp OpGT 0
433 , (,) "<=" $ cmp OpLT 1
434 , (,) ">=" $ cmp OpGT (-1)
435 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
438 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