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)
365generateLet tcc givenDefs givenBody =
366 do let (defs, body) = flattenLets givenDefs givenBody
367 stmts <- concat <$> mapM defToStatements defs
368 code <- exprToCode tcc body
369 jsBlock (stmts ++ toStatementList code)
374generateIf :: TailCallContext -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
375generateIf tcc givenBranches givenFinally =
377 (branches, finally) =
378 crushIfs givenBranches givenFinally
380 convertBranch (condition, expr) =
381 (,) <$> toJsExpr condition <*> exprToCode tcc expr
383 ifExpression (condition, branch) otherwise =
384 CondExpr () condition branch otherwise
386 ifStatement (condition, branch) otherwise =
387 IfStmt () condition branch otherwise
389 do jsBranches <- mapM convertBranch branches
390 jsFinally <- exprToCode tcc finally
392 if any isBlock (jsFinally : map snd jsBranches)
394 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
396 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
400 :: [(Opt.Expr, Opt.Expr)]
402 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
403crushIfs branches finally =
404 crushIfsHelp [] branches finally
408 :: [(Opt.Expr, Opt.Expr)]
409 -> [(Opt.Expr, Opt.Expr)]
411 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
412crushIfsHelp visitedBranches unvisitedBranches finally =
413 case unvisitedBranches of
416 A.A _ (MultiIf subBranches subFinally) ->
417 crushIfsHelp visitedBranches subBranches subFinally
420 (reverse visitedBranches, finally)
422 (A.A _ (Literal (Boolean True)), branch) : _ ->
423 crushIfsHelp visitedBranches [] branch
425 (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), branch) : _ ->
426 crushIfsHelp visitedBranches [] branch
428 visiting : unvisited ->
429 crushIfsHelp (visiting : visitedBranches) unvisited finally
435defToStatements :: Opt.Def -> State Int [Statement ()]
436defToStatements def@(Opt.Definition facts pattern expr) =
437 case Opt.tailRecursionDetails facts of
439 do func <- generateTailFunction name (Expr.collectLambdas expr)
440 return [ VarDeclStmt () [ varDecl name func ] ]
443 defToStatementsHelp facts pattern =<< toJsExpr expr
450 -> State Int [Statement ()]
451defToStatementsHelp facts annPattern@(A.A _ pattern) jsExpr =
460 op = LBracket () (ref "_op") (string x)
462 return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
464 return [ VarDeclStmt () [ define (Var.varName x) ] ]
469 varDecl name (obj ["$",name])
471 return [ VarDeclStmt () (define "$" : map setField fields) ]
473 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
474 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
476 vars = getVars patterns
479 A.A _ (P.Var x) : rest ->
480 (Var.varName x :) `fmap` getVars rest
486 decl x n = varDecl x (obj ["$","_" ++ show n])
488 | Help.isTuple name = define "$" : vars
489 | otherwise = define "_raw" : safeAssign : vars
491 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
492 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
493 exception = Crash.crash (Crash.IncompletePatternMatch (error "TODO"))
496 do defs' <- concat <$> mapM toDef vars
497 return (VarDeclStmt () [define "_"] : defs')
499 vars = P.boundVarList annPattern
500 mkVar = A.A () . localVar
502 let expr = A.A () $ Case (mkVar "_") [(annPattern, mkVar y)]
503 pat = A.A () (P.Var y)
505 defToStatements (Opt.Definition facts pat expr)
510generateCase :: TailCallContext -> Opt.Expr -> [(P.Optimized, Opt.Expr)] -> State Int Code
511generateCase tcc expr branches =
512 do (tempVar, initialMatch) <-
513 Case.toMatch branches
515 (revisedMatch, stmt) <-
517 A.A _ (Var (Var.Canonical Var.Local x)) ->
518 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
520 do expr' <- toJsExpr expr
521 return (initialMatch, [VarDeclStmt () [varDecl tempVar expr']])
523 match' <- match tcc revisedMatch
525 jsBlock (stmt ++ match')
528match :: TailCallContext -> Case.Match -> State Int [Statement ()]
531 Case.Match name clauses mtch' ->
532 do (isChars, clauses') <- unzip <$> mapM (clause tcc name) clauses
533 mtch'' <- match tcc mtch'
534 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
538 Case.Clause (Right _) _ _ ->
544 if any isLiteral clauses then
547 obj (Help.splitDots name ++ ["ctor"])
549 format isChars expr =
551 InfixExpr () OpAdd expr (string "")
556 return [ ExprStmt () (Crash.crash (Crash.IncompletePatternMatch (error "TODO"))) ]
559 return [BreakStmt () Nothing]
562 toStatementList <$> exprToCode tcc expr
565 concat <$> mapM (match tcc) (dropEnd [] ms)
570 Case.Other _ -> acc ++ [m]
571 _ -> dropEnd (acc ++ [m]) ms
574clause :: TailCallContext -> String -> Case.Clause -> State Int (Bool, CaseClause ())
575clause tcc variable (Case.Clause value vars mtch) =
576 do statemens <- match tcc (Case.matchSubst (zip vars vars') mtch)
577 return ((,) isChar (CaseClause () pattern statemens))
580 map (\n -> variable ++ "._" ++ show n) [0..]
584 Right (Chr c) -> (True, string [c])
588 Right (Boolean b) -> BoolLit () b
589 Right lit -> literal lit
590 Left (Var.Canonical _ name) ->
594flattenLets :: [Opt.Def] -> Opt.Expr -> ([Opt.Def], Opt.Expr)
595flattenLets defs lexpr@(A.A _ expr) =
597 Let ds body -> flattenLets (defs ++ ds) body
601generate :: Module.Optimized -> String
603 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
604 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
607 names = Module.names modul
609 thisModule :: Expression ()
610 thisModule = obj (localRuntime : names ++ ["values"])
612 programStmts :: [Statement ()]
615 [ [ ExprStmt () (string "use strict") ]
616 , setup localRuntime (names ++ ["values"])
617 , [ IfSingleStmt () thisModule (ret thisModule) ]
618 , [ VarDeclStmt () localVars ]
624 localVars :: [VarDecl ()]
626 varDecl "_op" (ObjectLit () [])
627 : internalImports (Module.names modul)
630 explicitImports :: [VarDecl ()]
637 jsImport :: Module.Name -> VarDecl ()
639 varDecl (Var.moduleName name) $
640 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
642 body :: [Statement ()]
644 concat (evalState defs 0)
647 Module.program (Module.body modul)
650 |> mapM defToStatements
652 setup namespace path =
656 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
664 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
666 exs = map entry $ "_op" : concatMap extract (exports modul)
667 entry x = (prop x, ref x)
674 | otherwise -> [Var.varName x]
676 Var.Union _ (Var.Listing ctors _) ->
677 map Var.varName ctors
681 [x] -> VarDeclStmt () [ varDecl x expr ]
684 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
695 | func == Var.Canonical Var.BuiltIn "::" =
696 toCode (A.A () (Data "::" [left,right]))
698 | func == forwardCompose =
699 compose (collectLeftAssoc forwardCompose left right)
701 | func == backwardCompose =
702 compose (collectRightAssoc backwardCompose left right)
704 | func == forwardApply =
705 pipe (collectLeftAssoc forwardApply left right)
707 | func == backwardApply =
708 pipe (collectRightAssoc backwardApply left right)
711 do jsLeft <- toJsExpr left
712 jsRight <- toJsExpr right
713 jsExpr (makeExpr func jsLeft jsRight)
716-- CONTROL FLOW OPERATOR HELPERS
718apply :: Expression () -> Expression () -> Expression ()
723compose :: [Opt.Expr] -> State Int Code
725 do jsFunctions <- mapM toJsExpr functions
726 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
729pipe :: [Opt.Expr] -> State Int Code
731 do (value:functions) <- mapM toJsExpr expressions
732 jsExpr $ List.foldl' apply value functions
735forwardCompose :: Var.Canonical
740backwardCompose :: Var.Canonical
745forwardApply :: Var.Canonical
750backwardApply :: Var.Canonical
755inBasics :: String -> Var.Canonical
757 Var.Canonical (Var.Module ["Basics"]) name
760-- BINARY OPERATOR HELPERS
762makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
763makeExpr qualifiedOp@(Var.Canonical home op) =
765 simpleMake left right =
766 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
768 if home == Var.Module ["Basics"] then
769 Map.findWithDefault simpleMake op basicOps
774basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
776 Map.fromList (infixOps ++ specialOps)
779infixOps :: [(String, Expression () -> Expression () -> Expression ())]
783 (str, InfixExpr () op)
789 , infixOp "&&" OpLAnd
794specialOps :: [(String, Expression () -> Expression () -> Expression ())]
796 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
797 , (,) "|>" $ flip (<|)
798 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
799 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
800 , (,) "<" $ cmp OpLT 0
801 , (,) ">" $ cmp OpGT 0
802 , (,) "<=" $ cmp OpLT 1
803 , (,) ">=" $ cmp OpGT (-1)
804 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
808cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
810 InfixExpr () op (_Utils "cmp" `call` [a,b]) (IntLit () n)
813-- BINARY OPERATOR COLLECTORS
815-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
816collectRightAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
817collectRightAssoc desiredOp left right =
818 collectRightAssocHelp desiredOp [left] right
821collectRightAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
822collectRightAssocHelp desiredOp exprList expr =
824 A.A _ (Binop op left right) | op == desiredOp ->
825 collectRightAssocHelp desiredOp (left : exprList) right
831-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
832collectLeftAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
833collectLeftAssoc desiredOp left right =
834 collectLeftAssocHelp desiredOp [right] left
837collectLeftAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
838collectLeftAssocHelp desiredOp exprList expr =
840 A.A _ (Binop op left right) | op == desiredOp ->
841 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