1module Generate.JavaScript (generate) where
3import Control.Applicative ((<$>),(<*>))
4import Control.Arrow (first, second, (***))
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.Optimized as Opt
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.JavaScript.Crash as Crash
24import qualified Generate.JavaScript.Port as Port
25import qualified Generate.JavaScript.Variable as Var
26import qualified Optimize.Cases as Case
27import qualified Reporting.Annotation as A
28import qualified Reporting.Crash as Crash
33internalImports :: Module.Name -> [VarDecl ()]
35 [ varDecl "_N" (obj ["Elm","Native"])
36 , include "_U" "Utils"
38 , varDecl Crash.localModuleName (string (Module.nameToString name))
41 include :: String -> String -> VarDecl ()
43 varDecl alias (Help.make ["_N", modul])
46_Utils :: String -> Expression ()
51_List :: String -> Expression ()
56literal :: Literal -> Expression ()
59 Chr c -> _Utils "chr" <| string [c]
61 IntNum n -> IntLit () n
62 FloatNum n -> NumLit () n
63 Boolean b -> BoolLit () b
69 = JsExpr (Expression ())
70 | JsBlock [Statement ()]
73jsExpr :: Expression () -> State Int Code
78jsBlock :: [Statement ()] -> State Int Code
83isBlock :: Code -> Bool
90toStatementList :: Code -> [Statement ()]
99toStatement :: Code -> Statement ()
110toExpr :: Code -> Expression ()
116 function [] stmts `call` []
121type TailCallContext =
122 Maybe (String, [String], Label)
131toJsExpr :: Opt.Expr -> State Int (Expression ())
136toCode :: Opt.Expr -> State Int Code
141exprToJsExpr :: TailCallContext -> Opt.Expr -> State Int (Expression ())
142exprToJsExpr tcc canonicalExpr =
143 toExpr <$> exprToCode tcc canonicalExpr
146exprToCode :: TailCallContext -> Opt.Expr -> State Int Code
147exprToCode tcc annotatedExpr@(A.A _ expr) =
150 jsExpr $ Var.canonical var
156 do lo' <- toJsExpr lo
158 jsExpr $ _List "range" `call` [lo',hi']
160 Access record field ->
161 do record' <- toJsExpr record
162 jsExpr $ DotRef () record' (var (Var.varName field))
164 Remove record field ->
165 do record' <- toJsExpr record
166 jsExpr $ _Utils "remove" `call` [ string (Var.varName field), record' ]
168 Insert record field value ->
169 do value' <- toJsExpr value
170 record' <- toJsExpr record
171 jsExpr $ _Utils "insert" `call` [ string (Var.varName field), value', record' ]
173 Modify record fields ->
174 do record' <- toJsExpr record
176 forM fields $ \(field, value) ->
177 do value' <- toJsExpr value
178 return $ ArrayLit () [ string (Var.varName field), value' ]
180 jsExpr $ _Utils "replace" `call` [ArrayLit () fields', record']
184 forM fields $ \(field, e) ->
185 (,) (Var.varName field) <$> toJsExpr e
188 List.foldl' combine Map.empty fields'
190 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
192 combine record (field, value) =
193 Map.insertWith (++) field [value] record
196 ObjectLit () . map (prop *** ArrayLit ()) $
197 Map.toList (Map.filter (not . null) (Map.map tail fs))
200 map (first prop) (Map.toList (Map.map head fs))
202 Binop op leftExpr rightExpr ->
203 binop op leftExpr rightExpr
206 generateFunction (Expr.collectLambdas annotatedExpr)
209 generateApplication tcc annotatedExpr
212 generateLet tcc defs body
214 MultiIf branches finally ->
215 generateIf tcc branches finally
217 Case expr branches ->
218 generateCase tcc expr branches
220 ExplicitList elements ->
221 do jsElements <- mapM toJsExpr elements
222 jsExpr $ _List "fromArray" <| ArrayLit () jsElements
225 do jsMembers <- mapM toJsExpr members
226 jsExpr $ ObjectLit () (ctor : fields jsMembers)
228 ctor = (prop "ctor", string tag)
230 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
232 GLShader _uid src _tipe ->
233 jsExpr $ ObjectLit () [(PropString () "src", literal (Str src))]
238 jsExpr (Port.inbound name portType)
240 Out name expr portType ->
241 do expr' <- toJsExpr expr
242 jsExpr (Port.outbound name expr' portType)
244 Task name expr portType ->
245 do expr' <- toJsExpr expr
246 jsExpr (Port.task name expr' portType)
249 jsExpr $ Crash.crash details
254generateFunction :: ([P.Optimized], Opt.Expr) -> State Int Code
255generateFunction (args, initialBody) =
256 do (argVars, patternDefs) <- destructuredArgs args
257 code <- generateLet Nothing patternDefs initialBody
258 jsExpr (generateFunctionWithArity argVars code)
261generateTailFunction :: String -> ([P.Optimized], Opt.Expr) -> State Int (Expression ())
262generateTailFunction name (args, initialBody) =
263 do (argVars, patternDefs) <- destructuredArgs args
264 label <- Id () <$> Case.newVar
265 code <- generateLet (Just (name, argVars, label)) patternDefs initialBody
267 JsBlock [ LabelledStmt () label (WhileStmt () (BoolLit () True) (toStatement code)) ]
268 return (generateFunctionWithArity argVars whileLoop)
271generateFunctionWithArity :: [String] -> Code -> Expression ()
272generateFunctionWithArity args code =
276 if 2 <= arity && arity <= 9 then
278 fN = "F" ++ show arity
280 ref fN <| function args (toStatementList code)
283 (lastArg:otherArgs) = reverse args
284 innerBody = function [lastArg] (toStatementList code)
286 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
289destructuredArgs :: [P.Optimized] -> State Int ([String], [Opt.Def])
290destructuredArgs args =
291 do (argVars, maybePatternDefs) <- unzip <$> mapM destructPattern args
292 return (argVars, Maybe.catMaybes maybePatternDefs)
295destructPattern :: P.Optimized -> State Int (String, Maybe Opt.Def)
296destructPattern pattern@(A.A ann pat) =
299 return (Var.varName x, Nothing)
302 do arg <- Case.newVar
305 , Just $ Opt.Definition Opt.dummyFacts pattern (A.A ann (localVar arg))
311generateApplication :: TailCallContext -> Opt.Expr -> State Int Code
312generateApplication tcc expr =
315 Expr.collectApps expr
320 reassign name tempName =
321 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
326 case isTailCall tcc func arity of
327 Just (argNames, label) ->
328 do args' <- mapM toJsExpr args
329 tempNames <- mapM (\_ -> Case.newVar) args
331 VarDeclStmt () (zipWith varDecl tempNames args')
332 : zipWith reassign argNames tempNames
333 ++ [ContinueStmt () (Just label)]
336 do func' <- toJsExpr func
337 args' <- mapM toJsExpr args
339 if 2 <= arity && arity <= 9 then
340 ref aN `call` (func':args')
342 foldl1 (<|) (func':args')
345isTailCall :: TailCallContext -> Opt.Expr -> Int -> Maybe ([String], Label)
346isTailCall tcc (A.A _ func) arity =
348 (Var (Var.Canonical Var.Local name), Just (tcName, tcArgs, tcLabel)) ->
349 if name == tcName && length tcArgs == arity then
350 Just (tcArgs, tcLabel)
354 (Var (Var.Canonical (Var.TopLevel _) name), Just (tcName, tcArgs, tcLabel)) ->
355 if name == tcName && length tcArgs == arity then
356 Just (tcArgs, tcLabel)
371generateLet tcc givenDefs givenBody =
372 do let (defs, body) = flattenLets givenDefs givenBody
373 stmts <- concat <$> mapM defToStatements defs
374 code <- exprToCode tcc body
375 jsBlock (stmts ++ toStatementList code)
380generateIf :: TailCallContext -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
381generateIf tcc givenBranches givenFinally =
383 (branches, finally) =
384 crushIfs givenBranches givenFinally
386 convertBranch (condition, expr) =
387 (,) <$> toJsExpr condition <*> exprToCode tcc expr
389 ifExpression (condition, branch) otherwise =
390 CondExpr () condition branch otherwise
392 ifStatement (condition, branch) otherwise =
393 IfStmt () condition branch otherwise
395 do jsBranches <- mapM convertBranch branches
396 jsFinally <- exprToCode tcc finally
398 if any isBlock (jsFinally : map snd jsBranches)
400 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
402 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
406 :: [(Opt.Expr, Opt.Expr)]
408 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
409crushIfs branches finally =
410 crushIfsHelp [] branches finally
414 :: [(Opt.Expr, Opt.Expr)]
415 -> [(Opt.Expr, Opt.Expr)]
417 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
418crushIfsHelp visitedBranches unvisitedBranches finally =
419 case unvisitedBranches of
422 A.A _ (MultiIf subBranches subFinally) ->
423 crushIfsHelp visitedBranches subBranches subFinally
426 (reverse visitedBranches, finally)
428 (A.A _ (Literal (Boolean True)), branch) : _ ->
429 crushIfsHelp visitedBranches [] branch
431 (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), branch) : _ ->
432 crushIfsHelp visitedBranches [] branch
434 visiting : unvisited ->
435 crushIfsHelp (visiting : visitedBranches) unvisited finally
441defToStatements :: Opt.Def -> State Int [Statement ()]
442defToStatements (Opt.Definition facts pattern expr) =
443 case Opt.tailRecursionDetails facts of
445 do func <- generateTailFunction name (Expr.collectLambdas expr)
446 return [ VarDeclStmt () [ varDecl name func ] ]
449 defToStatementsHelp facts pattern =<< toJsExpr expr
456 -> State Int [Statement ()]
457defToStatementsHelp facts annPattern@(A.A _ pattern) jsExpr =
466 op = LBracket () (ref "_op") (string x)
468 return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
470 return [ VarDeclStmt () [ define (Var.varName x) ] ]
475 varDecl name (obj ["$",name])
477 return [ VarDeclStmt () (define "$" : map setField fields) ]
479 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
480 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
482 vars = getVars patterns
485 A.A _ (P.Var x) : rest ->
486 (Var.varName x :) `fmap` getVars rest
492 decl x n = varDecl x (obj ["$","_" ++ show n])
494 | Help.isTuple name = define "$" : vars
495 | otherwise = define "_raw" : safeAssign : vars
497 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
498 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
499 exception = Crash.crash (Crash.IncompletePatternMatch (error "TODO"))
502 do defs' <- concat <$> mapM toDef vars
503 return (VarDeclStmt () [define "_"] : defs')
505 vars = P.boundVarList annPattern
506 mkVar = A.A () . localVar
508 let expr = A.A () $ Case (mkVar "_") [(annPattern, mkVar y)]
509 pat = A.A () (P.Var y)
511 defToStatements (Opt.Definition facts pat expr)
516generateCase :: TailCallContext -> Opt.Expr -> [(P.Optimized, Opt.Expr)] -> State Int Code
517generateCase tcc expr branches =
518 do (tempVar, initialMatch) <-
519 Case.toMatch branches
521 (revisedMatch, stmt) <-
523 A.A _ (Var (Var.Canonical Var.Local x)) ->
524 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
525 A.A _ (Var (Var.Canonical (Var.TopLevel _) x)) ->
526 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
528 do expr' <- toJsExpr expr
529 return (initialMatch, [VarDeclStmt () [varDecl tempVar expr']])
531 match' <- match tcc revisedMatch
533 jsBlock (stmt ++ match')
536match :: TailCallContext -> Case.Match -> State Int [Statement ()]
539 Case.Match name clauses mtch' ->
540 do (isChars, clauses') <- unzip <$> mapM (clause tcc name) clauses
541 mtch'' <- match tcc mtch'
542 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
546 Case.Clause (Right _) _ _ ->
552 if any isLiteral clauses then
555 obj (Help.splitDots name ++ ["ctor"])
557 format isChars expr =
559 InfixExpr () OpAdd expr (string "")
564 return [ ExprStmt () (Crash.crash (Crash.IncompletePatternMatch (error "TODO"))) ]
567 return [BreakStmt () Nothing]
570 toStatementList <$> exprToCode tcc expr
573 concat <$> mapM (match tcc) (dropEnd [] ms)
578 Case.Other _ -> acc ++ [m]
579 _ -> dropEnd (acc ++ [m]) ms
582clause :: TailCallContext -> String -> Case.Clause -> State Int (Bool, CaseClause ())
583clause tcc variable (Case.Clause value vars mtch) =
584 do statemens <- match tcc (Case.matchSubst (zip vars vars') mtch)
585 return ((,) isChar (CaseClause () pattern statemens))
588 map (\n -> variable ++ "._" ++ show n) [0..]
592 Right (Chr c) -> (True, string [c])
596 Right (Boolean b) -> BoolLit () b
597 Right lit -> literal lit
598 Left (Var.Canonical _ name) ->
602flattenLets :: [Opt.Def] -> Opt.Expr -> ([Opt.Def], Opt.Expr)
603flattenLets defs lexpr@(A.A _ expr) =
605 Let ds body -> flattenLets (defs ++ ds) body
609generate :: Module.Optimized -> String
611 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
612 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
615 names = Module.names modul
617 thisModule :: Expression ()
618 thisModule = obj (localRuntime : names ++ ["values"])
620 programStmts :: [Statement ()]
623 [ [ ExprStmt () (string "use strict") ]
624 , setup localRuntime (names ++ ["values"])
625 , [ IfSingleStmt () thisModule (ret thisModule) ]
626 , [ VarDeclStmt () localVars ]
632 localVars :: [VarDecl ()]
634 varDecl "_op" (ObjectLit () [])
635 : internalImports (Module.names modul)
638 explicitImports :: [VarDecl ()]
645 jsImport :: Module.Name -> VarDecl ()
647 varDecl (Var.moduleName name) $
648 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
650 body :: [Statement ()]
652 concat (evalState defs 0)
655 Module.program (Module.body modul)
658 |> mapM defToStatements
660 setup namespace path =
664 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
672 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
674 exs = map entry $ "_op" : concatMap extract (exports modul)
675 entry x = (prop x, ref x)
682 | otherwise -> [Var.varName x]
684 Var.Union _ (Var.Listing ctors _) ->
685 map Var.varName ctors
689 [x] -> VarDeclStmt () [ varDecl x expr ]
692 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
703 | func == Var.Canonical Var.BuiltIn "::" =
704 toCode (A.A () (Data "::" [left,right]))
706 | func == forwardCompose =
707 compose (collectLeftAssoc forwardCompose left right)
709 | func == backwardCompose =
710 compose (collectRightAssoc backwardCompose left right)
712 | func == forwardApply =
713 pipe (collectLeftAssoc forwardApply left right)
715 | func == backwardApply =
716 pipe (collectRightAssoc backwardApply left right)
719 do jsLeft <- toJsExpr left
720 jsRight <- toJsExpr right
721 jsExpr (makeExpr func jsLeft jsRight)
724-- CONTROL FLOW OPERATOR HELPERS
726apply :: Expression () -> Expression () -> Expression ()
731compose :: [Opt.Expr] -> State Int Code
733 do jsFunctions <- mapM toJsExpr functions
734 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
737pipe :: [Opt.Expr] -> State Int Code
739 do (value:functions) <- mapM toJsExpr expressions
740 jsExpr $ List.foldl' apply value functions
743forwardCompose :: Var.Canonical
748backwardCompose :: Var.Canonical
753forwardApply :: Var.Canonical
758backwardApply :: Var.Canonical
763inBasics :: String -> Var.Canonical
765 Var.Canonical (Var.Module ["Basics"]) name
768-- BINARY OPERATOR HELPERS
770makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
771makeExpr qualifiedOp@(Var.Canonical home op) =
773 simpleMake left right =
774 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
776 if home == Var.Module ["Basics"] then
777 Map.findWithDefault simpleMake op basicOps
782basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
784 Map.fromList (infixOps ++ specialOps)
787infixOps :: [(String, Expression () -> Expression () -> Expression ())]
791 (str, InfixExpr () op)
797 , infixOp "&&" OpLAnd
802specialOps :: [(String, Expression () -> Expression () -> Expression ())]
804 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
805 , (,) "|>" $ flip (<|)
806 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
807 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
808 , (,) "<" $ cmp OpLT 0
809 , (,) ">" $ cmp OpGT 0
810 , (,) "<=" $ cmp OpLT 1
811 , (,) ">=" $ cmp OpGT (-1)
812 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
816cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
818 InfixExpr () op (_Utils "cmp" `call` [a,b]) (IntLit () n)
821-- BINARY OPERATOR COLLECTORS
823-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
824collectRightAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
825collectRightAssoc desiredOp left right =
826 collectRightAssocHelp desiredOp [left] right
829collectRightAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
830collectRightAssocHelp desiredOp exprList expr =
832 A.A _ (Binop op left right) | op == desiredOp ->
833 collectRightAssocHelp desiredOp (left : exprList) right
839-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
840collectLeftAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
841collectLeftAssoc desiredOp left right =
842 collectLeftAssocHelp desiredOp [right] left
845collectLeftAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
846collectLeftAssocHelp desiredOp exprList expr =
848 A.A _ (Binop op left right) | op == desiredOp ->
849 collectLeftAssocHelp desiredOp (right : exprList) left
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