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.Maybe as Maybe
9import qualified Data.Set as Set
10import Language.ECMAScript3.PrettyPrint
11import Language.ECMAScript3.Syntax
14import AST.Expression.General as Expr
15import qualified AST.Expression.Canonical as Canonical
16import qualified AST.Helpers as Help
18import qualified AST.Module as Module
19import qualified AST.Pattern as P
20import qualified AST.Variable as Var
21import Elm.Utils ((|>))
22import Generate.JavaScript.Helpers as Help
23import qualified Generate.Cases as Case
24import qualified Generate.JavaScript.Port as Port
25import qualified Generate.JavaScript.Variable as Var
26import qualified Reporting.Annotation as A
27import qualified Reporting.Region as R
30internalImports :: Module.Name -> [VarDecl ()]
32 [ varDecl "_N" (obj ["Elm","Native"])
33 , include "_U" "Utils"
35 , varDecl Help.localModuleName (string (Module.nameToString name))
38 include :: String -> String -> VarDecl ()
40 varDecl alias (Help.make ["_N", modul])
43_Utils :: String -> Expression ()
48_List :: String -> Expression ()
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
65expression :: Canonical.Expr -> State Int (Expression ())
66expression annotatedExpr@(A.A region expr) =
69 return $ Var.canonical var
75 do lo' <- expression lo
77 return $ _List "range" `call` [lo',hi']
81 return $ DotRef () e' (var (Var.varName field))
85 return $ _Utils "remove" `call` [ string (Var.varName field), e' ]
87 Insert e field value ->
88 do value' <- expression value
90 return $ _Utils "insert" `call` [ string (Var.varName field), value', e' ]
95 forM fields $ \(field, value) ->
96 do value' <- expression value
97 return $ ArrayLit () [ string (Var.varName field), value' ]
99 return $ _Utils "replace" `call` [ArrayLit () fields', e']
103 forM fields $ \(field, e) ->
104 (,) (Var.varName field) <$> expression e
107 List.foldl' combine Map.empty fields'
109 return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
111 combine record (field, value) =
112 Map.insertWith (++) field [value] record
115 ObjectLit () . map (prop *** ArrayLit ()) $
116 Map.toList (Map.filter (not . null) (Map.map tail fs))
119 map (first prop) (Map.toList (Map.map head fs))
122 binop region op e1 e2
125 generateFunction (Expr.collectLambdas annotatedExpr)
128 do func' <- expression func
129 args' <- mapM expression args
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.A _ (App f arg)) -> getArgs f (arg : args)
144 do (stmts, bodyExpr) <- generateLetPieces defs body
145 return $ function [] (stmts ++ [ ret bodyExpr ]) `call` []
149 do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
151 case last branches of
152 (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
154 (A.A _ (Literal (Boolean True)), _) ->
157 ifs branches' (throw "badIf" region)
159 safeIfs branches = ifs (init branches) (snd (last branches))
160 ifs branches finally = foldr iff finally branches
161 iff (if', then') else' = CondExpr () if' then' else'
164 do (tempVar,initialMatch) <- Case.toMatch cases
165 (revisedMatch, stmt) <-
167 A.A _ (Var (Var.Canonical Var.Local x)) ->
168 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
170 do e' <- expression e
171 return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
172 match' <- match region revisedMatch
173 return (function [] (stmt ++ match') `call` [])
176 do es' <- mapM expression es
177 return $ _List "fromArray" <| ArrayLit () es'
180 do es' <- mapM expression es
181 return $ ObjectLit () (ctor : fields es')
183 ctor = (prop "ctor", string name)
185 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
187 GLShader _uid src _tipe ->
188 return $ ObjectLit () [(PropString () "src", literal (Str src))]
193 return (Port.inbound name portType)
195 Out name expr portType ->
196 do expr' <- expression expr
197 return (Port.outbound name expr' portType)
199 Task name expr portType ->
200 do expr' <- expression expr
201 return (Port.task name expr' portType)
207 :: ([P.CanonicalPattern], Canonical.Expr)
208 -> State Int (Expression ())
209generateFunction (args, initialBody) =
210 do (argVars, patternDefs) <- destructuredArgs args
211 (stmts, expr) <- generateLetPieces patternDefs initialBody
212 return (generateFunctionWithArity argVars stmts expr)
215generateFunctionWithArity :: [String] -> [Statement ()] -> Expression () -> Expression ()
216generateFunctionWithArity args stmts expr =
220 if 2 <= arity && arity <= 9 then
222 fN = "F" ++ show arity
224 ref fN <| function args (stmts ++ [ret expr])
227 (lastArg:otherArgs) = reverse args
228 innerBody = function [lastArg] (stmts ++ [ret expr])
230 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
233destructuredArgs :: [P.CanonicalPattern] -> State Int ([String], [Canonical.Def])
234destructuredArgs args =
235 do (argVars, maybePatternDefs) <- unzip <$> mapM destructPattern args
236 return (argVars, Maybe.catMaybes maybePatternDefs)
239destructPattern :: P.CanonicalPattern -> State Int (String, Maybe Canonical.Def)
240destructPattern pattern@(A.A ann pat) =
243 return (Var.varName x, Nothing)
246 do arg <- Case.newVar
249 , Just $ Canonical.Definition pattern (A.A ann (localVar arg)) Nothing
258 -> State Int ([Statement ()], Expression ())
259generateLetPieces givenDefs givenBody =
260 do let (defs, body) = flattenLets givenDefs givenBody
261 stmts <- concat <$> mapM definition defs
262 expr <- expression body
268definition :: Canonical.Def -> State Int [Statement ()]
269definition (Canonical.Definition annPattern expr@(A.A region _) _) =
270 do expr' <- expression expr
271 let assign x = varDecl x expr'
272 let (A.A patternRegion pattern) = annPattern
276 let op = LBracket () (ref "_op") (string x) in
277 return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
280 return [ VarDeclStmt () [ assign (Var.varName x) ] ]
283 let setField f = varDecl f (obj ["$",f]) in
284 return [ VarDeclStmt () (assign "$" : map setField fields) ]
286 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
287 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
289 vars = getVars patterns
292 A.A _ (P.Var x) : rest ->
293 (Var.varName x :) `fmap` getVars rest
299 decl x n = varDecl x (obj ["$","_" ++ show n])
301 | Help.isTuple name = assign "$" : vars
302 | otherwise = assign "_raw" : safeAssign : vars
304 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
305 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
306 exception = Help.throw "badCase" region
309 do defs' <- concat <$> mapM toDef vars
310 return (VarDeclStmt () [assign "_"] : defs')
312 vars = P.boundVarList annPattern
313 mkVar = A.A region . localVar
315 let expr = A.A region $ Case (mkVar "_") [(annPattern, mkVar y)]
316 pat = A.A patternRegion (P.Var y)
318 definition (Canonical.Definition pat expr Nothing)
321match :: R.Region -> Case.Match -> State Int [Statement ()]
324 Case.Match name clauses mtch' ->
325 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
326 mtch'' <- match region mtch'
327 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
331 Case.Clause (Right _) _ _ -> True
335 | any isLiteral clauses = obj [name]
336 | otherwise = obj (Help.splitDots name ++ ["ctor"])
339 | or isChars = InfixExpr () OpAdd e (string "")
343 return [ ExprStmt () (Help.throw "badCase" region) ]
346 return [BreakStmt () Nothing]
349 do e' <- expression e
353 concat <$> mapM (match region) (dropEnd [] ms)
358 Case.Other _ -> acc ++ [m]
359 _ -> dropEnd (acc ++ [m]) ms
362clause :: R.Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
363clause region variable (Case.Clause value vars mtch) =
364 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
367 map (\n -> variable ++ "._" ++ show n) [0..]
371 Right (Chr c) -> (True, string [c])
375 Right (Boolean b) -> BoolLit () b
376 Right lit -> literal lit
377 Left (Var.Canonical _ name) ->
381flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
382flattenLets defs lexpr@(A.A _ expr) =
384 Let ds body -> flattenLets (defs ++ ds) body
388generate :: Module.CanonicalModule -> String
390 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
391 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
394 names = Module.names modul
396 thisModule :: Expression ()
397 thisModule = obj (localRuntime : names ++ ["values"])
399 programStmts :: [Statement ()]
402 [ [ ExprStmt () (string "use strict") ]
403 , setup localRuntime (names ++ ["values"])
404 , [ IfSingleStmt () thisModule (ret thisModule) ]
405 , [ VarDeclStmt () localVars ]
411 localVars :: [VarDecl ()]
413 varDecl "_op" (ObjectLit () [])
414 : internalImports (Module.names modul)
417 explicitImports :: [VarDecl ()]
424 jsImport :: Module.Name -> VarDecl ()
426 varDecl (Var.moduleName name) $
427 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
429 body :: [Statement ()]
431 concat (evalState defs 0)
434 Module.program (Module.body modul)
439 setup namespace path =
443 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
451 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
453 exs = map entry $ "_op" : concatMap extract (exports modul)
454 entry x = (prop x, ref x)
461 | otherwise -> [Var.varName x]
463 Var.Union _ (Var.Listing ctors _) ->
464 map Var.varName ctors
468 [x] -> VarDeclStmt () [ varDecl x expr ]
471 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
479 -> State Int (Expression ())
480binop region func@(Var.Canonical home op) e1 e2 =
482 (Var.Module ["Basics"], ">>") ->
483 do es <- mapM expression (collectLeftAssoc [e2] e1)
484 return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
486 (Var.Module ["Basics"], "<<") ->
487 do es <- mapM expression (e1 : collectRightAssoc [] e2)
488 return $ ["$"] ==> foldr (<|) (ref "$") es
490 (Var.Module ["Basics"], "<|") ->
491 do e2' <- expression e2
492 es <- mapM expression (collectRightAssoc [] e1)
493 return $ foldr (<|) e2' es
495 (Var.BuiltIn, "::") ->
496 expression (A.A region (Data "::" [e1,e2]))
498 (Var.Module ["Basics"], _) ->
499 do e1' <- expression e1
501 case Map.lookup op basicOps of
506 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
509 do e1' <- expression e1
511 return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
514 collectRightAssoc es e =
516 A.A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
517 collectRightAssoc (es ++ [e1]) e2
520 collectLeftAssoc es e =
522 A.A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
523 collectLeftAssoc (e2 : es) e1
527 Map.fromList (infixOps ++ specialOps)
530 let infixOp str op = (str, InfixExpr () op) in
535 , infixOp "&&" OpLAnd
540 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
541 , (,) "|>" $ flip (<|)
542 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
543 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
544 , (,) "<" $ cmp OpLT 0
545 , (,) ">" $ cmp OpGT 0
546 , (,) "<=" $ cmp OpLT 1
547 , (,) ">=" $ cmp OpGT (-1)
548 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
552 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