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.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.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
29import qualified Reporting.Region as R
32internalImports :: Module.Name -> [VarDecl ()]
34 [ varDecl "_N" (obj ["Elm","Native"])
35 , include "_U" "Utils"
37 , varDecl Crash.localModuleName (string (Module.nameToString name))
40 include :: String -> String -> VarDecl ()
42 varDecl alias (Help.make ["_N", modul])
45_Utils :: String -> Expression ()
50_List :: String -> Expression ()
55literal :: Literal -> Expression ()
58 Chr c -> _Utils "chr" <| string [c]
60 IntNum n -> IntLit () n
61 FloatNum n -> NumLit () n
62 Boolean b -> BoolLit () b
68 = JsExpr (Expression ())
69 | JsBlock [Statement ()]
72jsExpr :: Expression () -> State Int Code
77jsBlock :: [Statement ()] -> State Int Code
82isBlock :: Code -> Bool
89toStatementList :: Code -> [Statement ()]
98toStatement :: Code -> Statement ()
109toExpr :: Code -> Expression ()
115 function [] stmts `call` []
120exprToJsExpr :: Canonical.Expr -> State Int (Expression ())
121exprToJsExpr canonicalExpr =
122 toExpr <$> exprToCode canonicalExpr
125exprToCode :: Canonical.Expr -> State Int Code
126exprToCode annotatedExpr@(A.A region expr) =
129 jsExpr $ Var.canonical var
135 do lo' <- exprToJsExpr lo
136 hi' <- exprToJsExpr hi
137 jsExpr $ _List "range" `call` [lo',hi']
139 Access record field ->
140 do record' <- exprToJsExpr record
141 jsExpr $ DotRef () record' (var (Var.varName field))
143 Remove record field ->
144 do record' <- exprToJsExpr record
145 jsExpr $ _Utils "remove" `call` [ string (Var.varName field), record' ]
147 Insert record field value ->
148 do value' <- exprToJsExpr value
149 record' <- exprToJsExpr record
150 jsExpr $ _Utils "insert" `call` [ string (Var.varName field), value', record' ]
152 Modify record fields ->
153 do record' <- exprToJsExpr record
155 forM fields $ \(field, value) ->
156 do value' <- exprToJsExpr value
157 return $ ArrayLit () [ string (Var.varName field), value' ]
159 jsExpr $ _Utils "replace" `call` [ArrayLit () fields', record']
163 forM fields $ \(field, e) ->
164 (,) (Var.varName field) <$> exprToJsExpr e
167 List.foldl' combine Map.empty fields'
169 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
171 combine record (field, value) =
172 Map.insertWith (++) field [value] record
175 ObjectLit () . map (prop *** ArrayLit ()) $
176 Map.toList (Map.filter (not . null) (Map.map tail fs))
179 map (first prop) (Map.toList (Map.map head fs))
181 Binop op leftExpr rightExpr ->
182 binop region op leftExpr rightExpr
185 generateFunction (Expr.collectLambdas annotatedExpr)
187 App funcExpr argExpr ->
188 do func' <- exprToJsExpr func
189 args' <- mapM exprToJsExpr args
192 [arg] -> func' <| arg
193 _ | length args' <= 9 -> ref aN `call` (func':args')
194 | otherwise -> foldl1 (<|) (func':args')
196 aN = "A" ++ show (length args)
197 (func, args) = getArgs funcExpr [argExpr]
200 (A.A _ (App f arg)) -> getArgs f (arg : args)
204 generateLet defs body
206 MultiIf branches finally ->
207 generateIf branches finally
209 Case expr branches ->
210 generateCase region expr branches
212 ExplicitList elements ->
213 do jsElements <- mapM exprToJsExpr elements
214 jsExpr $ _List "fromArray" <| ArrayLit () jsElements
217 do jsMembers <- mapM exprToJsExpr members
218 jsExpr $ ObjectLit () (ctor : fields jsMembers)
220 ctor = (prop "ctor", string tag)
222 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
224 GLShader _uid src _tipe ->
225 jsExpr $ ObjectLit () [(PropString () "src", literal (Str src))]
230 jsExpr (Port.inbound name portType)
232 Out name expr portType ->
233 do expr' <- exprToJsExpr expr
234 jsExpr (Port.outbound name expr' portType)
236 Task name expr portType ->
237 do expr' <- exprToJsExpr expr
238 jsExpr (Port.task name expr' portType)
241 jsExpr $ Crash.crash region details
246generateFunction :: ([P.CanonicalPattern], Canonical.Expr) -> State Int Code
247generateFunction (args, initialBody) =
248 do (argVars, patternDefs) <- destructuredArgs args
249 code <- generateLet patternDefs initialBody
250 jsExpr (generateFunctionWithArity argVars code)
253generateFunctionWithArity :: [String] -> Code -> Expression ()
254generateFunctionWithArity args code =
258 if 2 <= arity && arity <= 9 then
260 fN = "F" ++ show arity
262 ref fN <| function args (toStatementList code)
265 (lastArg:otherArgs) = reverse args
266 innerBody = function [lastArg] (toStatementList code)
268 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
271destructuredArgs :: [P.CanonicalPattern] -> State Int ([String], [Canonical.Def])
272destructuredArgs args =
273 do (argVars, maybePatternDefs) <- unzip <$> mapM destructPattern args
274 return (argVars, Maybe.catMaybes maybePatternDefs)
277destructPattern :: P.CanonicalPattern -> State Int (String, Maybe Canonical.Def)
278destructPattern pattern@(A.A ann pat) =
281 return (Var.varName x, Nothing)
284 do arg <- Case.newVar
287 , Just $ Canonical.Definition pattern (A.A ann (localVar arg)) Nothing
297generateLet givenDefs givenBody =
298 do let (defs, body) = flattenLets givenDefs givenBody
304 do stmts <- concat <$> mapM defToStatements defs
305 code <- exprToCode body
306 jsBlock (stmts ++ toStatementList code)
311generateIf :: [(Canonical.Expr, Canonical.Expr)] -> Canonical.Expr -> State Int Code
312generateIf branches finally =
314 convertBranch (condition, expr) =
315 (,) <$> exprToJsExpr condition <*> exprToCode expr
317 ifExpression (condition, branch) otherwise =
318 CondExpr () condition branch otherwise
320 ifStatement (condition, branch) otherwise =
321 IfStmt () condition branch otherwise
323 do jsBranches <- mapM convertBranch branches
324 jsFinally <- exprToCode finally
326 if any isBlock (jsFinally : map snd jsBranches)
328 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
330 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
335defToStatements :: Canonical.Def -> State Int [Statement ()]
336defToStatements (Canonical.Definition pattern expr _) =
337 defToStatementsHelp pattern =<< exprToJsExpr expr
340defToStatementsHelp :: P.CanonicalPattern -> Expression () -> State Int [Statement ()]
341defToStatementsHelp annPattern@(A.A patternRegion pattern) jsExpr =
350 op = LBracket () (ref "_op") (string x)
352 return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
354 return [ VarDeclStmt () [ define (Var.varName x) ] ]
359 varDecl name (obj ["$",name])
361 return [ VarDeclStmt () (define "$" : map setField fields) ]
363 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
364 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
366 vars = getVars patterns
369 A.A _ (P.Var x) : rest ->
370 (Var.varName x :) `fmap` getVars rest
376 decl x n = varDecl x (obj ["$","_" ++ show n])
378 | Help.isTuple name = define "$" : vars
379 | otherwise = define "_raw" : safeAssign : vars
381 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
382 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
383 exception = Crash.crash patternRegion Crash.IncompletePatternMatch
386 do defs' <- concat <$> mapM toDef vars
387 return (VarDeclStmt () [define "_"] : defs')
389 vars = P.boundVarList annPattern
390 mkVar = A.A patternRegion . localVar
392 let expr = A.A patternRegion $ Case (mkVar "_") [(annPattern, mkVar y)]
393 pat = A.A patternRegion (P.Var y)
395 defToStatements (Canonical.Definition pat expr Nothing)
400generateCase :: R.Region -> Canonical.Expr -> [(P.CanonicalPattern, Canonical.Expr)] -> State Int Code
401generateCase region expr branches =
402 do (tempVar, initialMatch) <-
403 Case.toMatch branches
405 (revisedMatch, stmt) <-
407 A.A _ (Var (Var.Canonical Var.Local x)) ->
408 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
410 do expr' <- exprToJsExpr expr
411 return (initialMatch, [VarDeclStmt () [varDecl tempVar expr']])
413 match' <- match region revisedMatch
415 jsBlock (stmt ++ match')
418match :: R.Region -> Case.Match -> State Int [Statement ()]
421 Case.Match name clauses mtch' ->
422 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
423 mtch'' <- match region mtch'
424 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
428 Case.Clause (Right _) _ _ ->
434 if any isLiteral clauses then
437 obj (Help.splitDots name ++ ["ctor"])
439 format isChars expr =
441 InfixExpr () OpAdd expr (string "")
446 return [ ExprStmt () (Crash.crash region Crash.IncompletePatternMatch) ]
449 return [BreakStmt () Nothing]
452 toStatementList <$> exprToCode expr
455 concat <$> mapM (match region) (dropEnd [] ms)
460 Case.Other _ -> acc ++ [m]
461 _ -> dropEnd (acc ++ [m]) ms
464clause :: R.Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
465clause region variable (Case.Clause value vars mtch) =
466 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
469 map (\n -> variable ++ "._" ++ show n) [0..]
473 Right (Chr c) -> (True, string [c])
477 Right (Boolean b) -> BoolLit () b
478 Right lit -> literal lit
479 Left (Var.Canonical _ name) ->
483flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
484flattenLets defs lexpr@(A.A _ expr) =
486 Let ds body -> flattenLets (defs ++ ds) body
490generate :: Module.CanonicalModule -> String
492 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
493 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
496 names = Module.names modul
498 thisModule :: Expression ()
499 thisModule = obj (localRuntime : names ++ ["values"])
501 programStmts :: [Statement ()]
504 [ [ ExprStmt () (string "use strict") ]
505 , setup localRuntime (names ++ ["values"])
506 , [ IfSingleStmt () thisModule (ret thisModule) ]
507 , [ VarDeclStmt () localVars ]
513 localVars :: [VarDecl ()]
515 varDecl "_op" (ObjectLit () [])
516 : internalImports (Module.names modul)
519 explicitImports :: [VarDecl ()]
526 jsImport :: Module.Name -> VarDecl ()
528 varDecl (Var.moduleName name) $
529 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
531 body :: [Statement ()]
533 concat (evalState defs 0)
536 Module.program (Module.body modul)
539 |> mapM defToStatements
541 setup namespace path =
545 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
553 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
555 exs = map entry $ "_op" : concatMap extract (exports modul)
556 entry x = (prop x, ref x)
563 | otherwise -> [Var.varName x]
565 Var.Union _ (Var.Listing ctors _) ->
566 map Var.varName ctors
570 [x] -> VarDeclStmt () [ varDecl x expr ]
573 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
584binop region func left right
585 | func == Var.Canonical Var.BuiltIn "::" =
586 exprToCode (A.A region (Data "::" [left,right]))
588 | func == forwardCompose =
589 compose (collectLeftAssoc forwardCompose left right)
591 | func == backwardCompose =
592 compose (collectRightAssoc backwardCompose left right)
594 | func == forwardApply =
595 pipe (collectLeftAssoc forwardApply left right)
597 | func == backwardApply =
598 pipe (collectRightAssoc backwardApply left right)
601 do jsLeft <- exprToJsExpr left
602 jsRight <- exprToJsExpr right
603 jsExpr (makeExpr func jsLeft jsRight)
606-- CONTROL FLOW OPERATOR HELPERS
608apply :: Expression () -> Expression () -> Expression ()
613compose :: [Canonical.Expr] -> State Int Code
615 do jsFunctions <- mapM exprToJsExpr functions
616 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
619pipe :: [Canonical.Expr] -> State Int Code
621 do (value:functions) <- mapM exprToJsExpr expressions
622 jsExpr $ List.foldl' apply value functions
625forwardCompose :: Var.Canonical
630backwardCompose :: Var.Canonical
635forwardApply :: Var.Canonical
640backwardApply :: Var.Canonical
645inBasics :: String -> Var.Canonical
647 Var.Canonical (Var.Module ["Basics"]) name
650-- BINARY OPERATOR HELPERS
652makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
653makeExpr qualifiedOp@(Var.Canonical home op) =
655 simpleMake left right =
656 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
658 if home == Var.Module ["Basics"] then
659 Map.findWithDefault simpleMake op basicOps
664basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
666 Map.fromList (infixOps ++ specialOps)
669infixOps :: [(String, Expression () -> Expression () -> Expression ())]
673 (str, InfixExpr () op)
679 , infixOp "&&" OpLAnd
684specialOps :: [(String, Expression () -> Expression () -> Expression ())]
686 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
687 , (,) "|>" $ flip (<|)
688 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
689 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
690 , (,) "<" $ cmp OpLT 0
691 , (,) ">" $ cmp OpGT 0
692 , (,) "<=" $ cmp OpLT 1
693 , (,) ">=" $ cmp OpGT (-1)
694 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
698cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
700 InfixExpr () op (_Utils "cmp" `call` [a,b]) (IntLit () n)
703-- BINARY OPERATOR COLLECTORS
705-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
706collectRightAssoc :: Var.Canonical -> Canonical.Expr -> Canonical.Expr -> [Canonical.Expr]
707collectRightAssoc desiredOp left right =
708 collectRightAssocHelp desiredOp [left] right
711collectRightAssocHelp :: Var.Canonical -> [Canonical.Expr] -> Canonical.Expr -> [Canonical.Expr]
712collectRightAssocHelp desiredOp exprList expr =
714 A.A _ (Binop op left right) | op == desiredOp ->
715 collectRightAssocHelp desiredOp (left : exprList) right
721-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
722collectLeftAssoc :: Var.Canonical -> Canonical.Expr -> Canonical.Expr -> [Canonical.Expr]
723collectLeftAssoc desiredOp left right =
724 collectLeftAssocHelp desiredOp [right] left
727collectLeftAssocHelp :: Var.Canonical -> [Canonical.Expr] -> Canonical.Expr -> [Canonical.Expr]
728collectLeftAssocHelp desiredOp exprList expr =
730 A.A _ (Binop op left right) | op == desiredOp ->
731 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