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.Cases as Case
24import qualified Generate.JavaScript.Crash as Crash
25import qualified Generate.JavaScript.Port as Port
26import qualified Generate.JavaScript.Variable as Var
27import qualified Reporting.Annotation as A
28import qualified Reporting.Crash as Crash
31internalImports :: Module.Name -> [VarDecl ()]
33 [ varDecl "_N" (obj ["Elm","Native"])
34 , include "_U" "Utils"
36 , varDecl Crash.localModuleName (string (Module.nameToString name))
39 include :: String -> String -> VarDecl ()
41 varDecl alias (Help.make ["_N", modul])
44_Utils :: String -> Expression ()
49_List :: String -> Expression ()
54literal :: Literal -> Expression ()
57 Chr c -> _Utils "chr" <| string [c]
59 IntNum n -> IntLit () n
60 FloatNum n -> NumLit () n
61 Boolean b -> BoolLit () b
67 = JsExpr (Expression ())
68 | JsBlock [Statement ()]
71jsExpr :: Expression () -> State Int Code
76jsBlock :: [Statement ()] -> State Int Code
81isBlock :: Code -> Bool
88toStatementList :: Code -> [Statement ()]
97toStatement :: Code -> Statement ()
108toExpr :: Code -> Expression ()
114 function [] stmts `call` []
119exprToJsExpr :: Opt.Expr -> State Int (Expression ())
120exprToJsExpr canonicalExpr =
121 toExpr <$> exprToCode canonicalExpr
124exprToCode :: Opt.Expr -> State Int Code
125exprToCode annotatedExpr@(A.A _ expr) =
128 jsExpr $ Var.canonical var
134 do lo' <- exprToJsExpr lo
135 hi' <- exprToJsExpr hi
136 jsExpr $ _List "range" `call` [lo',hi']
138 Access record field ->
139 do record' <- exprToJsExpr record
140 jsExpr $ DotRef () record' (var (Var.varName field))
142 Remove record field ->
143 do record' <- exprToJsExpr record
144 jsExpr $ _Utils "remove" `call` [ string (Var.varName field), record' ]
146 Insert record field value ->
147 do value' <- exprToJsExpr value
148 record' <- exprToJsExpr record
149 jsExpr $ _Utils "insert" `call` [ string (Var.varName field), value', record' ]
151 Modify record fields ->
152 do record' <- exprToJsExpr record
154 forM fields $ \(field, value) ->
155 do value' <- exprToJsExpr value
156 return $ ArrayLit () [ string (Var.varName field), value' ]
158 jsExpr $ _Utils "replace" `call` [ArrayLit () fields', record']
162 forM fields $ \(field, e) ->
163 (,) (Var.varName field) <$> exprToJsExpr e
166 List.foldl' combine Map.empty fields'
168 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
170 combine record (field, value) =
171 Map.insertWith (++) field [value] record
174 ObjectLit () . map (prop *** ArrayLit ()) $
175 Map.toList (Map.filter (not . null) (Map.map tail fs))
178 map (first prop) (Map.toList (Map.map head fs))
180 Binop op leftExpr rightExpr ->
181 binop op leftExpr rightExpr
184 generateFunction (Expr.collectLambdas annotatedExpr)
186 App funcExpr argExpr ->
187 do func' <- exprToJsExpr func
188 args' <- mapM exprToJsExpr args
191 [arg] -> func' <| arg
192 _ | length args' <= 9 -> ref aN `call` (func':args')
193 | otherwise -> foldl1 (<|) (func':args')
195 aN = "A" ++ show (length args)
196 (func, args) = getArgs funcExpr [argExpr]
199 (A.A _ (App f arg)) -> getArgs f (arg : args)
203 generateLet defs body
205 MultiIf branches finally ->
206 generateIf branches finally
208 Case expr branches ->
209 generateCase expr branches
211 ExplicitList elements ->
212 do jsElements <- mapM exprToJsExpr elements
213 jsExpr $ _List "fromArray" <| ArrayLit () jsElements
216 do jsMembers <- mapM exprToJsExpr 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' <- exprToJsExpr expr
233 jsExpr (Port.outbound name expr' portType)
235 Task name expr portType ->
236 do expr' <- exprToJsExpr 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 patternDefs initialBody
249 jsExpr (generateFunctionWithArity argVars code)
252generateFunctionWithArity :: [String] -> Code -> Expression ()
253generateFunctionWithArity args code =
257 if 2 <= arity && arity <= 9 then
259 fN = "F" ++ show arity
261 ref fN <| function args (toStatementList code)
264 (lastArg:otherArgs) = reverse args
265 innerBody = function [lastArg] (toStatementList code)
267 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
270destructuredArgs :: [P.Optimized] -> State Int ([String], [Opt.Def])
271destructuredArgs args =
272 do (argVars, maybePatternDefs) <- unzip <$> mapM destructPattern args
273 return (argVars, Maybe.catMaybes maybePatternDefs)
276destructPattern :: P.Optimized -> State Int (String, Maybe Opt.Def)
277destructPattern pattern@(A.A ann pat) =
280 return (Var.varName x, Nothing)
283 do arg <- Case.newVar
286 , Just $ Opt.Definition Opt.dummyFacts pattern (A.A ann (localVar arg))
296generateLet givenDefs givenBody =
297 do let (defs, body) = flattenLets givenDefs givenBody
303 do stmts <- concat <$> mapM defToStatements defs
304 code <- exprToCode body
305 jsBlock (stmts ++ toStatementList code)
310generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
311generateIf givenBranches givenFinally =
313 (branches, finally) =
314 crushIfs givenBranches givenFinally
316 convertBranch (condition, expr) =
317 (,) <$> exprToJsExpr condition <*> exprToCode expr
319 ifExpression (condition, branch) otherwise =
320 CondExpr () condition branch otherwise
322 ifStatement (condition, branch) otherwise =
323 IfStmt () condition branch otherwise
325 do jsBranches <- mapM convertBranch branches
326 jsFinally <- exprToCode finally
328 if any isBlock (jsFinally : map snd jsBranches)
330 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
332 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
336 :: [(Opt.Expr, Opt.Expr)]
338 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
339crushIfs branches finally =
340 crushIfsHelp [] branches finally
344 :: [(Opt.Expr, Opt.Expr)]
345 -> [(Opt.Expr, Opt.Expr)]
347 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
348crushIfsHelp visitedBranches unvisitedBranches finally =
349 case unvisitedBranches of
352 A.A _ (MultiIf subBranches subFinally) ->
353 crushIfsHelp visitedBranches subBranches subFinally
356 (reverse visitedBranches, finally)
358 (A.A _ (Literal (Boolean True)), branch) : _ ->
359 crushIfsHelp visitedBranches [] branch
361 (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), branch) : _ ->
362 crushIfsHelp visitedBranches [] branch
364 visiting : unvisited ->
365 crushIfsHelp (visiting : visitedBranches) unvisited finally
371defToStatements :: Opt.Def -> State Int [Statement ()]
372defToStatements (Opt.Definition facts pattern expr) =
373 case Opt.tailRecursionDetails facts of
374 Just (name, arity) ->
375 error "TODO" name arity
378 defToStatementsHelp facts pattern =<< exprToJsExpr expr
385 -> State Int [Statement ()]
386defToStatementsHelp facts annPattern@(A.A _ pattern) jsExpr =
395 op = LBracket () (ref "_op") (string x)
397 return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
399 return [ VarDeclStmt () [ define (Var.varName x) ] ]
404 varDecl name (obj ["$",name])
406 return [ VarDeclStmt () (define "$" : map setField fields) ]
408 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
409 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
411 vars = getVars patterns
414 A.A _ (P.Var x) : rest ->
415 (Var.varName x :) `fmap` getVars rest
421 decl x n = varDecl x (obj ["$","_" ++ show n])
423 | Help.isTuple name = define "$" : vars
424 | otherwise = define "_raw" : safeAssign : vars
426 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
427 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
428 exception = Crash.crash (Crash.IncompletePatternMatch (error "TODO"))
431 do defs' <- concat <$> mapM toDef vars
432 return (VarDeclStmt () [define "_"] : defs')
434 vars = P.boundVarList annPattern
435 mkVar = A.A () . localVar
437 let expr = A.A () $ Case (mkVar "_") [(annPattern, mkVar y)]
438 pat = A.A () (P.Var y)
440 defToStatements (Opt.Definition facts pat expr)
445generateCase :: Opt.Expr -> [(P.Optimized, Opt.Expr)] -> State Int Code
446generateCase expr branches =
447 do (tempVar, initialMatch) <-
448 Case.toMatch branches
450 (revisedMatch, stmt) <-
452 A.A _ (Var (Var.Canonical Var.Local x)) ->
453 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
455 do expr' <- exprToJsExpr expr
456 return (initialMatch, [VarDeclStmt () [varDecl tempVar expr']])
458 match' <- match revisedMatch
460 jsBlock (stmt ++ match')
463match :: Case.Match -> State Int [Statement ()]
466 Case.Match name clauses mtch' ->
467 do (isChars, clauses') <- unzip <$> mapM (clause name) clauses
468 mtch'' <- match mtch'
469 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
473 Case.Clause (Right _) _ _ ->
479 if any isLiteral clauses then
482 obj (Help.splitDots name ++ ["ctor"])
484 format isChars expr =
486 InfixExpr () OpAdd expr (string "")
491 return [ ExprStmt () (Crash.crash (Crash.IncompletePatternMatch (error "TODO"))) ]
494 return [BreakStmt () Nothing]
497 toStatementList <$> exprToCode expr
500 concat <$> mapM match (dropEnd [] ms)
505 Case.Other _ -> acc ++ [m]
506 _ -> dropEnd (acc ++ [m]) ms
509clause :: String -> Case.Clause -> State Int (Bool, CaseClause ())
510clause variable (Case.Clause value vars mtch) =
511 (,) isChar . CaseClause () pattern <$> match (Case.matchSubst (zip vars vars') mtch)
514 map (\n -> variable ++ "._" ++ show n) [0..]
518 Right (Chr c) -> (True, string [c])
522 Right (Boolean b) -> BoolLit () b
523 Right lit -> literal lit
524 Left (Var.Canonical _ name) ->
528flattenLets :: [Opt.Def] -> Opt.Expr -> ([Opt.Def], Opt.Expr)
529flattenLets defs lexpr@(A.A _ expr) =
531 Let ds body -> flattenLets (defs ++ ds) body
535generate :: Module.Optimized -> String
537 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
538 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
541 names = Module.names modul
543 thisModule :: Expression ()
544 thisModule = obj (localRuntime : names ++ ["values"])
546 programStmts :: [Statement ()]
549 [ [ ExprStmt () (string "use strict") ]
550 , setup localRuntime (names ++ ["values"])
551 , [ IfSingleStmt () thisModule (ret thisModule) ]
552 , [ VarDeclStmt () localVars ]
558 localVars :: [VarDecl ()]
560 varDecl "_op" (ObjectLit () [])
561 : internalImports (Module.names modul)
564 explicitImports :: [VarDecl ()]
571 jsImport :: Module.Name -> VarDecl ()
573 varDecl (Var.moduleName name) $
574 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
576 body :: [Statement ()]
578 concat (evalState defs 0)
581 Module.program (Module.body modul)
584 |> mapM defToStatements
586 setup namespace path =
590 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
598 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
600 exs = map entry $ "_op" : concatMap extract (exports modul)
601 entry x = (prop x, ref x)
608 | otherwise -> [Var.varName x]
610 Var.Union _ (Var.Listing ctors _) ->
611 map Var.varName ctors
615 [x] -> VarDeclStmt () [ varDecl x expr ]
618 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
629 | func == Var.Canonical Var.BuiltIn "::" =
630 exprToCode (A.A () (Data "::" [left,right]))
632 | func == forwardCompose =
633 compose (collectLeftAssoc forwardCompose left right)
635 | func == backwardCompose =
636 compose (collectRightAssoc backwardCompose left right)
638 | func == forwardApply =
639 pipe (collectLeftAssoc forwardApply left right)
641 | func == backwardApply =
642 pipe (collectRightAssoc backwardApply left right)
645 do jsLeft <- exprToJsExpr left
646 jsRight <- exprToJsExpr right
647 jsExpr (makeExpr func jsLeft jsRight)
650-- CONTROL FLOW OPERATOR HELPERS
652apply :: Expression () -> Expression () -> Expression ()
657compose :: [Opt.Expr] -> State Int Code
659 do jsFunctions <- mapM exprToJsExpr functions
660 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
663pipe :: [Opt.Expr] -> State Int Code
665 do (value:functions) <- mapM exprToJsExpr expressions
666 jsExpr $ List.foldl' apply value functions
669forwardCompose :: Var.Canonical
674backwardCompose :: Var.Canonical
679forwardApply :: Var.Canonical
684backwardApply :: Var.Canonical
689inBasics :: String -> Var.Canonical
691 Var.Canonical (Var.Module ["Basics"]) name
694-- BINARY OPERATOR HELPERS
696makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
697makeExpr qualifiedOp@(Var.Canonical home op) =
699 simpleMake left right =
700 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
702 if home == Var.Module ["Basics"] then
703 Map.findWithDefault simpleMake op basicOps
708basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
710 Map.fromList (infixOps ++ specialOps)
713infixOps :: [(String, Expression () -> Expression () -> Expression ())]
717 (str, InfixExpr () op)
723 , infixOp "&&" OpLAnd
728specialOps :: [(String, Expression () -> Expression () -> Expression ())]
730 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
731 , (,) "|>" $ flip (<|)
732 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
733 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
734 , (,) "<" $ cmp OpLT 0
735 , (,) ">" $ cmp OpGT 0
736 , (,) "<=" $ cmp OpLT 1
737 , (,) ">=" $ cmp OpGT (-1)
738 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
742cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
744 InfixExpr () op (_Utils "cmp" `call` [a,b]) (IntLit () n)
747-- BINARY OPERATOR COLLECTORS
749-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
750collectRightAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
751collectRightAssoc desiredOp left right =
752 collectRightAssocHelp desiredOp [left] right
755collectRightAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
756collectRightAssocHelp desiredOp exprList expr =
758 A.A _ (Binop op left right) | op == desiredOp ->
759 collectRightAssocHelp desiredOp (left : exprList) right
765-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
766collectLeftAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
767collectLeftAssoc desiredOp left right =
768 collectLeftAssocHelp desiredOp [right] left
771collectLeftAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
772collectLeftAssocHelp desiredOp exprList expr =
774 A.A _ (Binop op left right) | op == desiredOp ->
775 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