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 Update record fields ->
165 do record' <- toJsExpr record
167 forM fields $ \(field, value) ->
168 do value' <- toJsExpr value
169 return $ ArrayLit () [ string (Var.varName field), value' ]
171 jsExpr $ _Utils "replace" `call` [ArrayLit () fields', record']
175 forM fields $ \(field, e) ->
176 (,) (Var.varName field) <$> toJsExpr e
179 List.foldl' combine Map.empty fields'
181 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
183 combine record (field, value) =
184 Map.insertWith (++) field [value] record
187 ObjectLit () . map (prop *** ArrayLit ()) $
188 Map.toList (Map.filter (not . null) (Map.map tail fs))
191 map (first prop) (Map.toList (Map.map head fs))
193 Binop op leftExpr rightExpr ->
194 binop op leftExpr rightExpr
197 generateFunction (Expr.collectLambdas annotatedExpr)
200 generateApplication tcc annotatedExpr
203 generateLet tcc defs body
205 If branches finally ->
206 generateIf tcc branches finally
208 Case expr branches ->
209 generateCase tcc expr branches
211 ExplicitList elements ->
212 do jsElements <- mapM toJsExpr elements
213 jsExpr $ _List "fromArray" <| ArrayLit () jsElements
216 do jsMembers <- mapM toJsExpr members
217 jsExpr $ ObjectLit () (ctor : fields jsMembers)
219 ctor = (prop "ctor", string tag)
221 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
223 GLShader _uid src _tipe ->
224 jsExpr $ ObjectLit () [(PropString () "src", literal (Str src))]
229 jsExpr (Port.inbound name portType)
231 Out name expr portType ->
232 do expr' <- toJsExpr expr
233 jsExpr (Port.outbound name expr' portType)
235 Task name expr portType ->
236 do expr' <- toJsExpr expr
237 jsExpr (Port.task name expr' portType)
240 jsExpr $ Crash.crash details
245generateFunction :: ([P.Optimized], Opt.Expr) -> State Int Code
246generateFunction (args, initialBody) =
247 do (argVars, patternDefs) <- destructuredArgs args
248 code <- generateLet Nothing patternDefs initialBody
249 jsExpr (generateFunctionWithArity argVars code)
252generateTailFunction :: String -> ([P.Optimized], Opt.Expr) -> State Int (Expression ())
253generateTailFunction name (args, initialBody) =
254 do (argVars, patternDefs) <- destructuredArgs args
255 label <- Id () <$> Case.newVar
256 code <- generateLet (Just (name, argVars, label)) patternDefs initialBody
258 JsBlock [ LabelledStmt () label (WhileStmt () (BoolLit () True) (toStatement code)) ]
259 return (generateFunctionWithArity argVars whileLoop)
262generateFunctionWithArity :: [String] -> Code -> Expression ()
263generateFunctionWithArity args code =
267 if 2 <= arity && arity <= 9 then
269 fN = "F" ++ show arity
271 ref fN <| function args (toStatementList code)
274 (lastArg:otherArgs) = reverse args
275 innerBody = function [lastArg] (toStatementList code)
277 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
280destructuredArgs :: [P.Optimized] -> State Int ([String], [Opt.Def])
281destructuredArgs args =
282 do (argVars, maybePatternDefs) <- unzip <$> mapM destructPattern args
283 return (argVars, Maybe.catMaybes maybePatternDefs)
286destructPattern :: P.Optimized -> State Int (String, Maybe Opt.Def)
287destructPattern pattern@(A.A ann pat) =
290 return (Var.varName x, Nothing)
293 do arg <- Case.newVar
296 , Just $ Opt.Definition Opt.dummyFacts pattern (A.A ann (localVar arg))
302generateApplication :: TailCallContext -> Opt.Expr -> State Int Code
303generateApplication tcc expr =
306 Expr.collectApps expr
311 reassign name tempName =
312 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
317 case isTailCall tcc func arity of
318 Just (argNames, label) ->
319 do args' <- mapM toJsExpr args
320 tempNames <- mapM (\_ -> Case.newVar) args
322 VarDeclStmt () (zipWith varDecl tempNames args')
323 : zipWith reassign argNames tempNames
324 ++ [ContinueStmt () (Just label)]
327 do func' <- toJsExpr func
328 args' <- mapM toJsExpr args
330 if 2 <= arity && arity <= 9 then
331 ref aN `call` (func':args')
333 foldl1 (<|) (func':args')
336isTailCall :: TailCallContext -> Opt.Expr -> Int -> Maybe ([String], Label)
337isTailCall tcc (A.A _ func) arity =
339 (Var (Var.Canonical Var.Local name), Just (tcName, tcArgs, tcLabel)) ->
340 if name == tcName && length tcArgs == arity then
341 Just (tcArgs, tcLabel)
345 (Var (Var.Canonical (Var.TopLevel _) name), Just (tcName, tcArgs, tcLabel)) ->
346 if name == tcName && length tcArgs == arity then
347 Just (tcArgs, tcLabel)
362generateLet tcc givenDefs givenBody =
363 do let (defs, body) = flattenLets givenDefs givenBody
364 stmts <- concat <$> mapM defToStatements defs
365 code <- exprToCode tcc body
366 jsBlock (stmts ++ toStatementList code)
371generateIf :: TailCallContext -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
372generateIf tcc givenBranches givenFinally =
374 (branches, finally) =
375 crushIfs givenBranches givenFinally
377 convertBranch (condition, expr) =
378 (,) <$> toJsExpr condition <*> exprToCode tcc expr
380 ifExpression (condition, branch) otherwise =
381 CondExpr () condition branch otherwise
383 ifStatement (condition, branch) otherwise =
384 IfStmt () condition branch otherwise
386 do jsBranches <- mapM convertBranch branches
387 jsFinally <- exprToCode tcc finally
389 if any isBlock (jsFinally : map snd jsBranches)
391 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
393 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
397 :: [(Opt.Expr, Opt.Expr)]
399 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
400crushIfs branches finally =
401 crushIfsHelp [] branches finally
405 :: [(Opt.Expr, Opt.Expr)]
406 -> [(Opt.Expr, Opt.Expr)]
408 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
409crushIfsHelp visitedBranches unvisitedBranches finally =
410 case unvisitedBranches of
413 A.A _ (If subBranches subFinally) ->
414 crushIfsHelp visitedBranches subBranches subFinally
417 (reverse visitedBranches, finally)
419 (A.A _ (Literal (Boolean True)), branch) : _ ->
420 crushIfsHelp visitedBranches [] branch
422 (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), branch) : _ ->
423 crushIfsHelp visitedBranches [] branch
425 visiting : unvisited ->
426 crushIfsHelp (visiting : visitedBranches) unvisited finally
432defToStatements :: Opt.Def -> State Int [Statement ()]
433defToStatements (Opt.Definition facts pattern expr) =
434 case Opt.tailRecursionDetails facts of
436 do func <- generateTailFunction name (Expr.collectLambdas expr)
437 return [ VarDeclStmt () [ varDecl name func ] ]
440 defToStatementsHelp facts pattern =<< toJsExpr expr
447 -> State Int [Statement ()]
448defToStatementsHelp facts annPattern@(A.A _ pattern) jsExpr =
457 op = LBracket () (ref "_op") (string x)
459 return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
461 return [ VarDeclStmt () [ define (Var.varName x) ] ]
466 varDecl name (obj ["$",name])
468 return [ VarDeclStmt () (define "$" : map setField fields) ]
470 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
471 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
473 vars = getVars patterns
476 A.A _ (P.Var x) : rest ->
477 (Var.varName x :) `fmap` getVars rest
483 decl x n = varDecl x (obj ["$","_" ++ show n])
485 | Help.isTuple name = define "$" : vars
486 | otherwise = define "_raw" : safeAssign : vars
488 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
489 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
490 exception = Crash.crash (Crash.IncompletePatternMatch (error "TODO"))
493 do defs' <- concat <$> mapM toDef vars
494 return (VarDeclStmt () [define "_"] : defs')
496 vars = P.boundVarList annPattern
497 mkVar = A.A () . localVar
499 let expr = A.A () $ Case (mkVar "_") [(annPattern, mkVar y)]
500 pat = A.A () (P.Var y)
502 defToStatements (Opt.Definition facts pat expr)
507generateCase :: TailCallContext -> Opt.Expr -> [(P.Optimized, Opt.Expr)] -> State Int Code
508generateCase tcc expr branches =
509 do (tempVar, initialMatch) <-
510 Case.toMatch branches
512 (revisedMatch, stmt) <-
514 A.A _ (Var (Var.Canonical Var.Local x)) ->
515 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
516 A.A _ (Var (Var.Canonical (Var.TopLevel _) x)) ->
517 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
519 do expr' <- toJsExpr expr
520 return (initialMatch, [VarDeclStmt () [varDecl tempVar expr']])
522 match' <- match tcc revisedMatch
524 jsBlock (stmt ++ match')
527match :: TailCallContext -> Case.Match -> State Int [Statement ()]
530 Case.Match name clauses mtch' ->
531 do (isChars, clauses') <- unzip <$> mapM (clause tcc name) clauses
532 mtch'' <- match tcc mtch'
533 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
537 Case.Clause (Right _) _ _ ->
543 if any isLiteral clauses then
546 obj (Help.splitDots name ++ ["ctor"])
548 format isChars expr =
550 InfixExpr () OpAdd expr (string "")
555 return [ ExprStmt () (Crash.crash (Crash.IncompletePatternMatch (error "TODO"))) ]
558 return [BreakStmt () Nothing]
561 toStatementList <$> exprToCode tcc expr
564 concat <$> mapM (match tcc) (dropEnd [] ms)
569 Case.Other _ -> acc ++ [m]
570 _ -> dropEnd (acc ++ [m]) ms
573clause :: TailCallContext -> String -> Case.Clause -> State Int (Bool, CaseClause ())
574clause tcc variable (Case.Clause value vars mtch) =
575 do statemens <- match tcc (Case.matchSubst (zip vars vars') mtch)
576 return ((,) isChar (CaseClause () pattern statemens))
579 map (\n -> variable ++ "._" ++ show n) [0..]
583 Right (Chr c) -> (True, string [c])
587 Right (Boolean b) -> BoolLit () b
588 Right lit -> literal lit
589 Left (Var.Canonical _ name) ->
593flattenLets :: [Opt.Def] -> Opt.Expr -> ([Opt.Def], Opt.Expr)
594flattenLets defs lexpr@(A.A _ expr) =
596 Let ds body -> flattenLets (defs ++ ds) body
600generate :: Module.Optimized -> String
602 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
603 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
606 names = Module.names modul
608 thisModule :: Expression ()
609 thisModule = obj (localRuntime : names ++ ["values"])
611 programStmts :: [Statement ()]
614 [ [ ExprStmt () (string "use strict") ]
615 , setup localRuntime (names ++ ["values"])
616 , [ IfSingleStmt () thisModule (ret thisModule) ]
617 , [ VarDeclStmt () localVars ]
623 localVars :: [VarDecl ()]
625 varDecl "_op" (ObjectLit () [])
626 : internalImports (Module.names modul)
629 explicitImports :: [VarDecl ()]
636 jsImport :: Module.Name -> VarDecl ()
638 varDecl (Var.moduleName name) $
639 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
641 body :: [Statement ()]
643 concat (evalState defs 0)
646 Module.program (Module.body modul)
649 |> mapM defToStatements
651 setup namespace path =
655 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
663 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
665 exs = map entry $ "_op" : concatMap extract (exports modul)
666 entry x = (prop x, ref x)
673 | otherwise -> [Var.varName x]
675 Var.Union _ (Var.Listing ctors _) ->
676 map Var.varName ctors
680 [x] -> VarDeclStmt () [ varDecl x expr ]
683 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
694 | func == Var.Canonical Var.BuiltIn "::" =
695 toCode (A.A () (Data "::" [left,right]))
697 | func == forwardCompose =
698 compose (collectLeftAssoc forwardCompose left right)
700 | func == backwardCompose =
701 compose (collectRightAssoc backwardCompose left right)
703 | func == forwardApply =
704 pipe (collectLeftAssoc forwardApply left right)
706 | func == backwardApply =
707 pipe (collectRightAssoc backwardApply left right)
710 do jsLeft <- toJsExpr left
711 jsRight <- toJsExpr right
712 jsExpr (makeExpr func jsLeft jsRight)
715-- CONTROL FLOW OPERATOR HELPERS
717apply :: Expression () -> Expression () -> Expression ()
722compose :: [Opt.Expr] -> State Int Code
724 do jsFunctions <- mapM toJsExpr functions
725 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
728pipe :: [Opt.Expr] -> State Int Code
730 do (value:functions) <- mapM toJsExpr expressions
731 jsExpr $ List.foldl' apply value functions
734forwardCompose :: Var.Canonical
739backwardCompose :: Var.Canonical
744forwardApply :: Var.Canonical
749backwardApply :: Var.Canonical
754inBasics :: String -> Var.Canonical
756 Var.Canonical (Var.Module ["Basics"]) name
759-- BINARY OPERATOR HELPERS
761makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
762makeExpr qualifiedOp@(Var.Canonical home op) =
764 simpleMake left right =
765 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
767 if home == Var.Module ["Basics"] then
768 Map.findWithDefault simpleMake op basicOps
773basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
775 Map.fromList (infixOps ++ specialOps)
778infixOps :: [(String, Expression () -> Expression () -> Expression ())]
782 (str, InfixExpr () op)
788 , infixOp "&&" OpLAnd
793specialOps :: [(String, Expression () -> Expression () -> Expression ())]
795 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
796 , (,) "|>" $ flip (<|)
797 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
798 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
799 , (,) "<" $ cmp OpLT 0
800 , (,) ">" $ cmp OpGT 0
801 , (,) "<=" $ cmp OpLT 1
802 , (,) ">=" $ cmp OpGT (-1)
803 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
807cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
809 InfixExpr () op (_Utils "cmp" `call` [a,b]) (IntLit () n)
812-- BINARY OPERATOR COLLECTORS
814-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
815collectRightAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
816collectRightAssoc desiredOp left right =
817 collectRightAssocHelp desiredOp [left] right
820collectRightAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
821collectRightAssocHelp desiredOp exprList expr =
823 A.A _ (Binop op left right) | op == desiredOp ->
824 collectRightAssocHelp desiredOp (left : exprList) right
830-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
831collectLeftAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
832collectLeftAssoc desiredOp left right =
833 collectLeftAssocHelp desiredOp [right] left
836collectLeftAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
837collectLeftAssocHelp desiredOp exprList expr =
839 A.A _ (Binop op left right) | op == desiredOp ->
840 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