1module Generate.JavaScript (generate) where
3import Control.Applicative ((<$>),(<*>))
4import Control.Arrow (first, second, (***))
5import Control.Monad.State as State
6import qualified Data.List as List
7import qualified Data.Map as Map
8import qualified Data.Maybe as Maybe
9import Language.ECMAScript3.PrettyPrint as ES
10import Language.ECMAScript3.Syntax
11import qualified Text.PrettyPrint.Leijen as PP
13import AST.Expression.General as Expr
14import qualified AST.Expression.Optimized as Opt
15import qualified AST.Helpers as Help
17import qualified AST.Module as Module
18import qualified AST.Module.Name as ModuleName
19import qualified AST.Pattern as P
20import qualified AST.Variable as Var
21import Generate.JavaScript.Helpers as Help
22import qualified Generate.JavaScript.BuiltIn as BuiltIn
23import qualified Generate.JavaScript.Crash as Crash
24import qualified Generate.JavaScript.Literal as Literal
25import qualified Generate.JavaScript.Port as Port
26import qualified Generate.JavaScript.Variable as Var
27import qualified Optimize.Cases as Case
28import qualified Reporting.Annotation as A
29import qualified Reporting.Crash as Crash
32generate :: Module.Optimized -> String
36 flattenLets [] (Module.program (Module.body module_))
39 concat (State.evalState (mapM defToStatements definitions) 0)
41 PP.displayS (PP.renderPretty 0.4 120 (ES.prettyPrint stmts)) ""
47 = JsExpr (Expression ())
48 | JsBlock [Statement ()]
51jsExpr :: Expression () -> State Int Code
56jsBlock :: [Statement ()] -> State Int Code
61isBlock :: Code -> Bool
68toStatementList :: Code -> [Statement ()]
77toStatement :: Code -> Statement ()
88toExpr :: Code -> Expression ()
94 function [] stmts `call` []
100 Maybe (String, [String], Label)
109toJsExpr :: Opt.Expr -> State Int (Expression ())
114toCode :: Opt.Expr -> State Int Code
119exprToJsExpr :: TailCallContext -> Opt.Expr -> State Int (Expression ())
120exprToJsExpr tcc canonicalExpr =
121 toExpr <$> exprToCode tcc canonicalExpr
124exprToCode :: TailCallContext -> Opt.Expr -> State Int Code
125exprToCode tcc annotatedExpr@(A.A _ expr) =
128 jsExpr $ Var.canonical var
131 jsExpr (Literal.literal lit)
134 do lo' <- toJsExpr lo
136 jsExpr $ BuiltIn.range lo' hi'
138 Access record field ->
139 do record' <- toJsExpr record
140 jsExpr $ DotRef () record' (var (Var.safe field))
142 Update record fields ->
144 fieldToJs (field, value) =
145 do jsValue <- toJsExpr value
146 return (Var.safe field, jsValue)
148 do jsRecord <- toJsExpr record
149 jsFields <- mapM fieldToJs fields
150 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
154 forM fields $ \(field, e) ->
155 (,) (Var.safe field) <$> toJsExpr e
158 List.foldl' combine Map.empty fields'
160 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
162 combine record (field, value) =
163 Map.insertWith (++) field [value] record
166 ObjectLit () . map (prop *** ArrayLit ()) $
167 Map.toList (Map.filter (not . null) (Map.map tail fs))
170 map (first prop) (Map.toList (Map.map head fs))
172 Binop op leftExpr rightExpr ->
173 binop op leftExpr rightExpr
176 generateFunction (Expr.collectLambdas annotatedExpr)
179 generateApplication tcc annotatedExpr
182 generateLet tcc defs body
184 If branches finally ->
185 generateIf tcc branches finally
187 Case expr branches ->
188 generateCase tcc expr branches
190 ExplicitList elements ->
191 do jsElements <- mapM toJsExpr elements
192 jsExpr $ BuiltIn.list jsElements
195 do jsMembers <- mapM toJsExpr members
196 jsExpr $ ObjectLit () (ctor : fields jsMembers)
198 ctor = (prop "ctor", string tag)
200 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
202 GLShader _uid src _tipe ->
203 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (Str src))]
208 jsExpr (Port.inbound name portType)
210 Out name expr portType ->
211 do expr' <- toJsExpr expr
212 jsExpr (Port.outbound name expr' portType)
214 Task name expr portType ->
215 do expr' <- toJsExpr expr
216 jsExpr (Port.task name expr' portType)
219 jsExpr $ Crash.crash details
224generateFunction :: ([P.Optimized], Opt.Expr) -> State Int Code
225generateFunction (args, initialBody) =
226 do (argVars, patternDefs) <- destructuredArgs args
227 code <- generateLet Nothing patternDefs initialBody
228 jsExpr (generateFunctionWithArity argVars code)
231generateTailFunction :: String -> ([P.Optimized], Opt.Expr) -> State Int (Expression ())
232generateTailFunction name (args, initialBody) =
233 do (argVars, patternDefs) <- destructuredArgs args
234 label <- Id () <$> Var.fresh
235 code <- generateLet (Just (name, argVars, label)) patternDefs initialBody
237 JsBlock [ LabelledStmt () label (WhileStmt () (BoolLit () True) (toStatement code)) ]
238 return (generateFunctionWithArity argVars whileLoop)
241generateFunctionWithArity :: [String] -> Code -> Expression ()
242generateFunctionWithArity args code =
246 if 2 <= arity && arity <= 9 then
248 fN = "F" ++ show arity
250 ref fN <| function args (toStatementList code)
253 (lastArg:otherArgs) = reverse args
254 innerBody = function [lastArg] (toStatementList code)
256 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
259destructuredArgs :: [P.Optimized] -> State Int ([String], [Opt.Def])
260destructuredArgs args =
261 do (argVars, maybePatternDefs) <- unzip <$> mapM destructPattern args
262 return (argVars, Maybe.catMaybes maybePatternDefs)
265destructPattern :: P.Optimized -> State Int (String, Maybe Opt.Def)
266destructPattern pattern@(A.A ann pat) =
269 return (Var.safe x, Nothing)
275 , Just $ Opt.Definition Opt.dummyFacts pattern (A.A ann (localVar arg))
281generateApplication :: TailCallContext -> Opt.Expr -> State Int Code
282generateApplication tcc expr =
285 Expr.collectApps expr
290 reassign name tempName =
291 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
296 case isTailCall tcc func arity of
297 Just (argNames, label) ->
298 do args' <- mapM toJsExpr args
299 tempNames <- mapM (\_ -> Var.fresh) args
301 VarDeclStmt () (zipWith varDecl tempNames args')
302 : zipWith reassign argNames tempNames
303 ++ [ContinueStmt () (Just label)]
306 do func' <- toJsExpr func
307 args' <- mapM toJsExpr args
309 if 2 <= arity && arity <= 9 then
310 ref aN `call` (func':args')
312 foldl1 (<|) (func':args')
315isTailCall :: TailCallContext -> Opt.Expr -> Int -> Maybe ([String], Label)
316isTailCall tcc (A.A _ func) arity =
318 (Var (Var.Canonical Var.Local name), Just (tcName, tcArgs, tcLabel)) ->
319 if name == tcName && length tcArgs == arity then
320 Just (tcArgs, tcLabel)
324 (Var (Var.Canonical (Var.TopLevel _) name), Just (tcName, tcArgs, tcLabel)) ->
325 if name == tcName && length tcArgs == arity then
326 Just (tcArgs, tcLabel)
341generateLet tcc givenDefs givenBody =
342 do let (defs, body) = flattenLets givenDefs givenBody
343 stmts <- concat <$> mapM defToStatements defs
344 code <- exprToCode tcc body
345 jsBlock (stmts ++ toStatementList code)
348flattenLets :: [Opt.Def] -> Opt.Expr -> ([Opt.Def], Opt.Expr)
349flattenLets defs lexpr@(A.A _ expr) =
351 Let ds body -> flattenLets (defs ++ ds) body
357generateIf :: TailCallContext -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
358generateIf tcc givenBranches givenFinally =
360 (branches, finally) =
361 crushIfs givenBranches givenFinally
363 convertBranch (condition, expr) =
364 (,) <$> toJsExpr condition <*> exprToCode tcc expr
366 ifExpression (condition, branch) otherwise =
367 CondExpr () condition branch otherwise
369 ifStatement (condition, branch) otherwise =
370 IfStmt () condition branch otherwise
372 do jsBranches <- mapM convertBranch branches
373 jsFinally <- exprToCode tcc finally
375 if any isBlock (jsFinally : map snd jsBranches)
377 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
379 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
383 :: [(Opt.Expr, Opt.Expr)]
385 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
386crushIfs branches finally =
387 crushIfsHelp [] branches finally
391 :: [(Opt.Expr, Opt.Expr)]
392 -> [(Opt.Expr, Opt.Expr)]
394 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
395crushIfsHelp visitedBranches unvisitedBranches finally =
396 case unvisitedBranches of
399 A.A _ (If subBranches subFinally) ->
400 crushIfsHelp visitedBranches subBranches subFinally
403 (reverse visitedBranches, finally)
405 (A.A _ (Literal (Boolean True)), branch) : _ ->
406 crushIfsHelp visitedBranches [] branch
408 visiting : unvisited ->
409 crushIfsHelp (visiting : visitedBranches) unvisited finally
415defToStatements :: Opt.Def -> State Int [Statement ()]
416defToStatements (Opt.Definition facts pattern expr) =
417 case Opt.tailRecursionDetails facts of
419 do func <- generateTailFunction name (Expr.collectLambdas expr)
420 return [ VarDeclStmt () [ varDecl name func ] ]
423 defToStatementsHelp facts pattern =<< toJsExpr expr
430 -> State Int [Statement ()]
431defToStatementsHelp facts annPattern@(A.A _ pattern) jsExpr =
440 op = LBracket () (ref "_op") (string x)
442 return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
444 return [ VarDeclStmt () [ define (Var.safe x) ] ]
449 varDecl name (obj ["$",name])
451 return [ VarDeclStmt () (define "$" : map setField fields) ]
453 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
454 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
456 vars = getVars patterns
459 A.A _ (P.Var x) : rest ->
460 (Var.safe x :) `fmap` getVars rest
466 decl x n = varDecl x (obj ["$","_" ++ show n])
468 | Help.isTuple name = define "$" : vars
469 | otherwise = define "_raw" : safeAssign : vars
471 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
472 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
473 exception = Crash.crash (Crash.IncompletePatternMatch (error "TODO"))
476 do defs' <- concat <$> mapM toDef vars
477 return (VarDeclStmt () [define "_"] : defs')
479 vars = P.boundVarList annPattern
480 mkVar = A.A () . localVar
482 let expr = A.A () $ Case (mkVar "_") [(annPattern, mkVar y)]
483 pat = A.A () (P.Var y)
485 defToStatements (Opt.Definition facts pat expr)
490generateCase :: TailCallContext -> Opt.Expr -> [(P.Optimized, Opt.Expr)] -> State Int Code
491generateCase tcc expr branches =
492 error "TODO" tcc expr branches
503 | func == Var.Canonical Var.BuiltIn "::" =
504 toCode (A.A () (Data "::" [left,right]))
506 | func == forwardCompose =
507 compose (collectLeftAssoc forwardCompose left right)
509 | func == backwardCompose =
510 compose (collectRightAssoc backwardCompose left right)
512 | func == forwardApply =
513 pipe (collectLeftAssoc forwardApply left right)
515 | func == backwardApply =
516 pipe (collectRightAssoc backwardApply left right)
519 do jsLeft <- toJsExpr left
520 jsRight <- toJsExpr right
521 jsExpr (makeExpr func jsLeft jsRight)
524-- CONTROL FLOW OPERATOR HELPERS
526apply :: Expression () -> Expression () -> Expression ()
531compose :: [Opt.Expr] -> State Int Code
533 do jsFunctions <- mapM toJsExpr functions
534 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
537pipe :: [Opt.Expr] -> State Int Code
539 do (value:functions) <- mapM toJsExpr expressions
540 jsExpr $ List.foldl' apply value functions
543forwardCompose :: Var.Canonical
548backwardCompose :: Var.Canonical
553forwardApply :: Var.Canonical
558backwardApply :: Var.Canonical
563inBasics :: String -> Var.Canonical
565 Var.inCore ["Basics"] name
568-- BINARY OPERATOR HELPERS
570makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
571makeExpr qualifiedOp@(Var.Canonical home op) =
573 simpleMake left right =
574 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
576 if home == Var.Module (ModuleName.inCore ["Basics"]) then
577 Map.findWithDefault simpleMake op basicOps
582basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
584 Map.fromList (infixOps ++ specialOps)
587infixOps :: [(String, Expression () -> Expression () -> Expression ())]
591 (str, InfixExpr () op)
597 , infixOp "&&" OpLAnd
602specialOps :: [(String, Expression () -> Expression () -> Expression ())]
604 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
605 , (,) "|>" $ flip (<|)
606 , (,) "==" $ \a b -> BuiltIn.eq a b
607 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
608 , (,) "<" $ cmp OpLT 0
609 , (,) ">" $ cmp OpGT 0
610 , (,) "<=" $ cmp OpLT 1
611 , (,) ">=" $ cmp OpGT (-1)
612 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
616cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
618 InfixExpr () op (BuiltIn.compare a b) (IntLit () n)
621-- BINARY OPERATOR COLLECTORS
623-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
624collectRightAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
625collectRightAssoc desiredOp left right =
626 collectRightAssocHelp desiredOp [left] right
629collectRightAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
630collectRightAssocHelp desiredOp exprList expr =
632 A.A _ (Binop op left right) | op == desiredOp ->
633 collectRightAssocHelp desiredOp (left : exprList) right
639-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
640collectLeftAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
641collectLeftAssoc desiredOp left right =
642 collectLeftAssocHelp desiredOp [right] left
645collectLeftAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
646collectLeftAssocHelp desiredOp exprList expr =
648 A.A _ (Binop op left right) | op == desiredOp ->
649 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