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 givenBranches givenFinally =
314 (branches, finally) =
315 combineIfs givenBranches givenFinally
317 convertBranch (condition, expr) =
318 (,) <$> exprToJsExpr condition <*> exprToCode expr
320 ifExpression (condition, branch) otherwise =
321 CondExpr () condition branch otherwise
323 ifStatement (condition, branch) otherwise =
324 IfStmt () condition branch otherwise
326 do jsBranches <- mapM convertBranch branches
327 jsFinally <- exprToCode finally
329 if any isBlock (jsFinally : map snd jsBranches)
331 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
333 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
337 :: [(Canonical.Expr, Canonical.Expr)]
339 -> ([(Canonical.Expr, Canonical.Expr)], Canonical.Expr)
340combineIfs branches finally =
342 A.A _ (MultiIf subBranches subFinally) ->
343 combineIfs (branches ++ subBranches) subFinally
351defToStatements :: Canonical.Def -> State Int [Statement ()]
352defToStatements (Canonical.Definition pattern expr _) =
353 defToStatementsHelp pattern =<< exprToJsExpr expr
356defToStatementsHelp :: P.CanonicalPattern -> Expression () -> State Int [Statement ()]
357defToStatementsHelp annPattern@(A.A patternRegion pattern) jsExpr =
366 op = LBracket () (ref "_op") (string x)
368 return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
370 return [ VarDeclStmt () [ define (Var.varName x) ] ]
375 varDecl name (obj ["$",name])
377 return [ VarDeclStmt () (define "$" : map setField fields) ]
379 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
380 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
382 vars = getVars patterns
385 A.A _ (P.Var x) : rest ->
386 (Var.varName x :) `fmap` getVars rest
392 decl x n = varDecl x (obj ["$","_" ++ show n])
394 | Help.isTuple name = define "$" : vars
395 | otherwise = define "_raw" : safeAssign : vars
397 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
398 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
399 exception = Crash.crash patternRegion Crash.IncompletePatternMatch
402 do defs' <- concat <$> mapM toDef vars
403 return (VarDeclStmt () [define "_"] : defs')
405 vars = P.boundVarList annPattern
406 mkVar = A.A patternRegion . localVar
408 let expr = A.A patternRegion $ Case (mkVar "_") [(annPattern, mkVar y)]
409 pat = A.A patternRegion (P.Var y)
411 defToStatements (Canonical.Definition pat expr Nothing)
416generateCase :: R.Region -> Canonical.Expr -> [(P.CanonicalPattern, Canonical.Expr)] -> State Int Code
417generateCase region expr branches =
418 do (tempVar, initialMatch) <-
419 Case.toMatch branches
421 (revisedMatch, stmt) <-
423 A.A _ (Var (Var.Canonical Var.Local x)) ->
424 return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
426 do expr' <- exprToJsExpr expr
427 return (initialMatch, [VarDeclStmt () [varDecl tempVar expr']])
429 match' <- match region revisedMatch
431 jsBlock (stmt ++ match')
434match :: R.Region -> Case.Match -> State Int [Statement ()]
437 Case.Match name clauses mtch' ->
438 do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
439 mtch'' <- match region mtch'
440 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
444 Case.Clause (Right _) _ _ ->
450 if any isLiteral clauses then
453 obj (Help.splitDots name ++ ["ctor"])
455 format isChars expr =
457 InfixExpr () OpAdd expr (string "")
462 return [ ExprStmt () (Crash.crash region Crash.IncompletePatternMatch) ]
465 return [BreakStmt () Nothing]
468 toStatementList <$> exprToCode expr
471 concat <$> mapM (match region) (dropEnd [] ms)
476 Case.Other _ -> acc ++ [m]
477 _ -> dropEnd (acc ++ [m]) ms
480clause :: R.Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
481clause region variable (Case.Clause value vars mtch) =
482 (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
485 map (\n -> variable ++ "._" ++ show n) [0..]
489 Right (Chr c) -> (True, string [c])
493 Right (Boolean b) -> BoolLit () b
494 Right lit -> literal lit
495 Left (Var.Canonical _ name) ->
499flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
500flattenLets defs lexpr@(A.A _ expr) =
502 Let ds body -> flattenLets (defs ++ ds) body
506generate :: Module.CanonicalModule -> String
508 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
509 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
512 names = Module.names modul
514 thisModule :: Expression ()
515 thisModule = obj (localRuntime : names ++ ["values"])
517 programStmts :: [Statement ()]
520 [ [ ExprStmt () (string "use strict") ]
521 , setup localRuntime (names ++ ["values"])
522 , [ IfSingleStmt () thisModule (ret thisModule) ]
523 , [ VarDeclStmt () localVars ]
529 localVars :: [VarDecl ()]
531 varDecl "_op" (ObjectLit () [])
532 : internalImports (Module.names modul)
535 explicitImports :: [VarDecl ()]
542 jsImport :: Module.Name -> VarDecl ()
544 varDecl (Var.moduleName name) $
545 obj ("Elm" : name ++ ["make"]) <| ref localRuntime
547 body :: [Statement ()]
549 concat (evalState defs 0)
552 Module.program (Module.body modul)
555 |> mapM defToStatements
557 setup namespace path =
561 assign name (InfixExpr () OpLOr (obj name) (ObjectLit () []))
569 assign (localRuntime : names ++ ["values"]) (ObjectLit () exs)
571 exs = map entry $ "_op" : concatMap extract (exports modul)
572 entry x = (prop x, ref x)
579 | otherwise -> [Var.varName x]
581 Var.Union _ (Var.Listing ctors _) ->
582 map Var.varName ctors
586 [x] -> VarDeclStmt () [ varDecl x expr ]
589 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
600binop region func left right
601 | func == Var.Canonical Var.BuiltIn "::" =
602 exprToCode (A.A region (Data "::" [left,right]))
604 | func == forwardCompose =
605 compose (collectLeftAssoc forwardCompose left right)
607 | func == backwardCompose =
608 compose (collectRightAssoc backwardCompose left right)
610 | func == forwardApply =
611 pipe (collectLeftAssoc forwardApply left right)
613 | func == backwardApply =
614 pipe (collectRightAssoc backwardApply left right)
617 do jsLeft <- exprToJsExpr left
618 jsRight <- exprToJsExpr right
619 jsExpr (makeExpr func jsLeft jsRight)
622-- CONTROL FLOW OPERATOR HELPERS
624apply :: Expression () -> Expression () -> Expression ()
629compose :: [Canonical.Expr] -> State Int Code
631 do jsFunctions <- mapM exprToJsExpr functions
632 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
635pipe :: [Canonical.Expr] -> State Int Code
637 do (value:functions) <- mapM exprToJsExpr expressions
638 jsExpr $ List.foldl' apply value functions
641forwardCompose :: Var.Canonical
646backwardCompose :: Var.Canonical
651forwardApply :: Var.Canonical
656backwardApply :: Var.Canonical
661inBasics :: String -> Var.Canonical
663 Var.Canonical (Var.Module ["Basics"]) name
666-- BINARY OPERATOR HELPERS
668makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
669makeExpr qualifiedOp@(Var.Canonical home op) =
671 simpleMake left right =
672 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
674 if home == Var.Module ["Basics"] then
675 Map.findWithDefault simpleMake op basicOps
680basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
682 Map.fromList (infixOps ++ specialOps)
685infixOps :: [(String, Expression () -> Expression () -> Expression ())]
689 (str, InfixExpr () op)
695 , infixOp "&&" OpLAnd
700specialOps :: [(String, Expression () -> Expression () -> Expression ())]
702 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
703 , (,) "|>" $ flip (<|)
704 , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
705 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
706 , (,) "<" $ cmp OpLT 0
707 , (,) ">" $ cmp OpGT 0
708 , (,) "<=" $ cmp OpLT 1
709 , (,) ">=" $ cmp OpGT (-1)
710 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
714cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
716 InfixExpr () op (_Utils "cmp" `call` [a,b]) (IntLit () n)
719-- BINARY OPERATOR COLLECTORS
721-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
722collectRightAssoc :: Var.Canonical -> Canonical.Expr -> Canonical.Expr -> [Canonical.Expr]
723collectRightAssoc desiredOp left right =
724 collectRightAssocHelp desiredOp [left] right
727collectRightAssocHelp :: Var.Canonical -> [Canonical.Expr] -> Canonical.Expr -> [Canonical.Expr]
728collectRightAssocHelp desiredOp exprList expr =
730 A.A _ (Binop op left right) | op == desiredOp ->
731 collectRightAssocHelp desiredOp (left : exprList) right
737-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
738collectLeftAssoc :: Var.Canonical -> Canonical.Expr -> Canonical.Expr -> [Canonical.Expr]
739collectLeftAssoc desiredOp left right =
740 collectLeftAssocHelp desiredOp [right] left
743collectLeftAssocHelp :: Var.Canonical -> [Canonical.Expr] -> Canonical.Expr -> [Canonical.Expr]
744collectLeftAssocHelp desiredOp exprList expr =
746 A.A _ (Binop op left right) | op == desiredOp ->
747 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