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 Language.ECMAScript3.Syntax
11import AST.Expression.General as Expr
12import qualified AST.Expression.Optimized as Opt
13import qualified AST.Helpers as Help
15import qualified AST.Module as Module
16import qualified AST.Module.Name as ModuleName
17import qualified AST.Pattern as P
18import qualified AST.Variable as Var
19import Generate.JavaScript.Helpers as Help
20import qualified Generate.JavaScript.BuiltIn as BuiltIn
21import qualified Generate.JavaScript.Crash as Crash
22import qualified Generate.JavaScript.Literal as Literal
23import qualified Generate.JavaScript.Port as Port
24import qualified Generate.JavaScript.Variable as Var
25import qualified Optimize.Cases as Case
26import qualified Reporting.Annotation as A
27import qualified Reporting.Crash as Crash
30generate :: Module.Optimized -> String
32 error "TODO" modul exprToCode
38 = JsExpr (Expression ())
39 | JsBlock [Statement ()]
42jsExpr :: Expression () -> State Int Code
47jsBlock :: [Statement ()] -> State Int Code
52isBlock :: Code -> Bool
59toStatementList :: Code -> [Statement ()]
68toStatement :: Code -> Statement ()
79toExpr :: Code -> Expression ()
85 function [] stmts `call` []
91 Maybe (String, [String], Label)
100toJsExpr :: Opt.Expr -> State Int (Expression ())
105toCode :: Opt.Expr -> State Int Code
110exprToJsExpr :: TailCallContext -> Opt.Expr -> State Int (Expression ())
111exprToJsExpr tcc canonicalExpr =
112 toExpr <$> exprToCode tcc canonicalExpr
115exprToCode :: TailCallContext -> Opt.Expr -> State Int Code
116exprToCode tcc annotatedExpr@(A.A _ expr) =
119 jsExpr $ Var.canonical var
122 jsExpr (Literal.literal lit)
125 do lo' <- toJsExpr lo
127 jsExpr $ BuiltIn.range lo' hi'
129 Access record field ->
130 do record' <- toJsExpr record
131 jsExpr $ DotRef () record' (var (Var.safe field))
133 Update record fields ->
135 fieldToJs (field, value) =
136 do jsValue <- toJsExpr value
137 return (Var.safe field, jsValue)
139 do jsRecord <- toJsExpr record
140 jsFields <- mapM fieldToJs fields
141 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
145 forM fields $ \(field, e) ->
146 (,) (Var.safe field) <$> toJsExpr e
149 List.foldl' combine Map.empty fields'
151 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
153 combine record (field, value) =
154 Map.insertWith (++) field [value] record
157 ObjectLit () . map (prop *** ArrayLit ()) $
158 Map.toList (Map.filter (not . null) (Map.map tail fs))
161 map (first prop) (Map.toList (Map.map head fs))
163 Binop op leftExpr rightExpr ->
164 binop op leftExpr rightExpr
167 generateFunction (Expr.collectLambdas annotatedExpr)
170 generateApplication tcc annotatedExpr
173 generateLet tcc defs body
175 If branches finally ->
176 generateIf tcc branches finally
178 Case expr branches ->
179 generateCase tcc expr branches
181 ExplicitList elements ->
182 do jsElements <- mapM toJsExpr elements
183 jsExpr $ BuiltIn.list jsElements
186 do jsMembers <- mapM toJsExpr members
187 jsExpr $ ObjectLit () (ctor : fields jsMembers)
189 ctor = (prop "ctor", string tag)
191 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
193 GLShader _uid src _tipe ->
194 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (Str src))]
199 jsExpr (Port.inbound name portType)
201 Out name expr portType ->
202 do expr' <- toJsExpr expr
203 jsExpr (Port.outbound name expr' portType)
205 Task name expr portType ->
206 do expr' <- toJsExpr expr
207 jsExpr (Port.task name expr' portType)
210 jsExpr $ Crash.crash details
215generateFunction :: ([P.Optimized], Opt.Expr) -> State Int Code
216generateFunction (args, initialBody) =
217 do (argVars, patternDefs) <- destructuredArgs args
218 code <- generateLet Nothing patternDefs initialBody
219 jsExpr (generateFunctionWithArity argVars code)
222generateTailFunction :: String -> ([P.Optimized], Opt.Expr) -> State Int (Expression ())
223generateTailFunction name (args, initialBody) =
224 do (argVars, patternDefs) <- destructuredArgs args
225 label <- Id () <$> Case.newVar
226 code <- generateLet (Just (name, argVars, label)) patternDefs initialBody
228 JsBlock [ LabelledStmt () label (WhileStmt () (BoolLit () True) (toStatement code)) ]
229 return (generateFunctionWithArity argVars whileLoop)
232generateFunctionWithArity :: [String] -> Code -> Expression ()
233generateFunctionWithArity args code =
237 if 2 <= arity && arity <= 9 then
239 fN = "F" ++ show arity
241 ref fN <| function args (toStatementList code)
244 (lastArg:otherArgs) = reverse args
245 innerBody = function [lastArg] (toStatementList code)
247 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
250destructuredArgs :: [P.Optimized] -> State Int ([String], [Opt.Def])
251destructuredArgs args =
252 do (argVars, maybePatternDefs) <- unzip <$> mapM destructPattern args
253 return (argVars, Maybe.catMaybes maybePatternDefs)
256destructPattern :: P.Optimized -> State Int (String, Maybe Opt.Def)
257destructPattern pattern@(A.A ann pat) =
260 return (Var.safe x, Nothing)
263 do arg <- Case.newVar
266 , Just $ Opt.Definition Opt.dummyFacts pattern (A.A ann (localVar arg))
272generateApplication :: TailCallContext -> Opt.Expr -> State Int Code
273generateApplication tcc expr =
276 Expr.collectApps expr
281 reassign name tempName =
282 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
287 case isTailCall tcc func arity of
288 Just (argNames, label) ->
289 do args' <- mapM toJsExpr args
290 tempNames <- mapM (\_ -> Case.newVar) args
292 VarDeclStmt () (zipWith varDecl tempNames args')
293 : zipWith reassign argNames tempNames
294 ++ [ContinueStmt () (Just label)]
297 do func' <- toJsExpr func
298 args' <- mapM toJsExpr args
300 if 2 <= arity && arity <= 9 then
301 ref aN `call` (func':args')
303 foldl1 (<|) (func':args')
306isTailCall :: TailCallContext -> Opt.Expr -> Int -> Maybe ([String], Label)
307isTailCall tcc (A.A _ func) arity =
309 (Var (Var.Canonical Var.Local name), Just (tcName, tcArgs, tcLabel)) ->
310 if name == tcName && length tcArgs == arity then
311 Just (tcArgs, tcLabel)
315 (Var (Var.Canonical (Var.TopLevel _) name), Just (tcName, tcArgs, tcLabel)) ->
316 if name == tcName && length tcArgs == arity then
317 Just (tcArgs, tcLabel)
332generateLet tcc givenDefs givenBody =
333 do let (defs, body) = flattenLets givenDefs givenBody
334 stmts <- concat <$> mapM defToStatements defs
335 code <- exprToCode tcc body
336 jsBlock (stmts ++ toStatementList code)
341generateIf :: TailCallContext -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
342generateIf tcc givenBranches givenFinally =
344 (branches, finally) =
345 crushIfs givenBranches givenFinally
347 convertBranch (condition, expr) =
348 (,) <$> toJsExpr condition <*> exprToCode tcc expr
350 ifExpression (condition, branch) otherwise =
351 CondExpr () condition branch otherwise
353 ifStatement (condition, branch) otherwise =
354 IfStmt () condition branch otherwise
356 do jsBranches <- mapM convertBranch branches
357 jsFinally <- exprToCode tcc finally
359 if any isBlock (jsFinally : map snd jsBranches)
361 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
363 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
367 :: [(Opt.Expr, Opt.Expr)]
369 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
370crushIfs branches finally =
371 crushIfsHelp [] branches finally
375 :: [(Opt.Expr, Opt.Expr)]
376 -> [(Opt.Expr, Opt.Expr)]
378 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
379crushIfsHelp visitedBranches unvisitedBranches finally =
380 case unvisitedBranches of
383 A.A _ (If subBranches subFinally) ->
384 crushIfsHelp visitedBranches subBranches subFinally
387 (reverse visitedBranches, finally)
389 (A.A _ (Literal (Boolean True)), branch) : _ ->
390 crushIfsHelp visitedBranches [] branch
392 visiting : unvisited ->
393 crushIfsHelp (visiting : visitedBranches) unvisited finally
399defToStatements :: Opt.Def -> State Int [Statement ()]
400defToStatements (Opt.Definition facts pattern expr) =
401 case Opt.tailRecursionDetails facts of
403 do func <- generateTailFunction name (Expr.collectLambdas expr)
404 return [ VarDeclStmt () [ varDecl name func ] ]
407 defToStatementsHelp facts pattern =<< toJsExpr expr
414 -> State Int [Statement ()]
415defToStatementsHelp facts annPattern@(A.A _ pattern) jsExpr =
424 op = LBracket () (ref "_op") (string x)
426 return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
428 return [ VarDeclStmt () [ define (Var.safe x) ] ]
433 varDecl name (obj ["$",name])
435 return [ VarDeclStmt () (define "$" : map setField fields) ]
437 P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
438 return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
440 vars = getVars patterns
443 A.A _ (P.Var x) : rest ->
444 (Var.safe x :) `fmap` getVars rest
450 decl x n = varDecl x (obj ["$","_" ++ show n])
452 | Help.isTuple name = define "$" : vars
453 | otherwise = define "_raw" : safeAssign : vars
455 safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
456 if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
457 exception = Crash.crash (Crash.IncompletePatternMatch (error "TODO"))
460 do defs' <- concat <$> mapM toDef vars
461 return (VarDeclStmt () [define "_"] : defs')
463 vars = P.boundVarList annPattern
464 mkVar = A.A () . localVar
466 let expr = A.A () $ Case (mkVar "_") [(annPattern, mkVar y)]
467 pat = A.A () (P.Var y)
469 defToStatements (Opt.Definition facts pat expr)
474generateCase :: TailCallContext -> Opt.Expr -> [(P.Optimized, Opt.Expr)] -> State Int Code
475generateCase tcc expr branches =
476 do (tempVar, initialMatch) <-
477 Case.toMatch branches
479 (revisedMatch, stmt) <-
481 A.A _ (Var (Var.Canonical Var.Local x)) ->
482 return (Case.matchSubst [(tempVar, Var.safe x)] initialMatch, [])
484 A.A _ (Var (Var.Canonical (Var.TopLevel _) x)) ->
485 return (Case.matchSubst [(tempVar, Var.safe x)] initialMatch, [])
488 do expr' <- toJsExpr expr
489 return (initialMatch, [VarDeclStmt () [varDecl tempVar expr']])
491 match' <- match tcc revisedMatch
493 jsBlock (stmt ++ match')
496match :: TailCallContext -> Case.Match -> State Int [Statement ()]
499 Case.Match name clauses mtch' ->
500 do (isChars, clauses') <- unzip <$> mapM (clause tcc name) clauses
501 mtch'' <- match tcc mtch'
502 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
506 Case.Clause (Right _) _ _ ->
512 if any isLiteral clauses then
515 obj (Help.splitDots name ++ ["ctor"])
517 format isChars expr =
519 InfixExpr () OpAdd expr (string "")
524 return [ ExprStmt () (Crash.crash (Crash.IncompletePatternMatch (error "TODO"))) ]
527 return [BreakStmt () Nothing]
530 toStatementList <$> exprToCode tcc expr
533 concat <$> mapM (match tcc) (dropEnd [] ms)
538 Case.Other _ -> acc ++ [m]
539 _ -> dropEnd (acc ++ [m]) ms
542clause :: TailCallContext -> String -> Case.Clause -> State Int (Bool, CaseClause ())
543clause tcc variable (Case.Clause value vars mtch) =
544 do statemens <- match tcc (Case.matchSubst (zip vars vars') mtch)
545 return ((,) isChar (CaseClause () pattern statemens))
548 map (\n -> variable ++ "._" ++ show n) [0..]
552 Right (Chr c) -> (True, string [c])
556 Right (Boolean b) -> BoolLit () b
557 Right lit -> Literal.literal lit
558 Left (Var.Canonical _ name) ->
562flattenLets :: [Opt.Def] -> Opt.Expr -> ([Opt.Def], Opt.Expr)
563flattenLets defs lexpr@(A.A _ expr) =
565 Let ds body -> flattenLets (defs ++ ds) body
577 | func == Var.Canonical Var.BuiltIn "::" =
578 toCode (A.A () (Data "::" [left,right]))
580 | func == forwardCompose =
581 compose (collectLeftAssoc forwardCompose left right)
583 | func == backwardCompose =
584 compose (collectRightAssoc backwardCompose left right)
586 | func == forwardApply =
587 pipe (collectLeftAssoc forwardApply left right)
589 | func == backwardApply =
590 pipe (collectRightAssoc backwardApply left right)
593 do jsLeft <- toJsExpr left
594 jsRight <- toJsExpr right
595 jsExpr (makeExpr func jsLeft jsRight)
598-- CONTROL FLOW OPERATOR HELPERS
600apply :: Expression () -> Expression () -> Expression ()
605compose :: [Opt.Expr] -> State Int Code
607 do jsFunctions <- mapM toJsExpr functions
608 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
611pipe :: [Opt.Expr] -> State Int Code
613 do (value:functions) <- mapM toJsExpr expressions
614 jsExpr $ List.foldl' apply value functions
617forwardCompose :: Var.Canonical
622backwardCompose :: Var.Canonical
627forwardApply :: Var.Canonical
632backwardApply :: Var.Canonical
637inBasics :: String -> Var.Canonical
639 Var.inCore ["Basics"] name
642-- BINARY OPERATOR HELPERS
644makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
645makeExpr qualifiedOp@(Var.Canonical home op) =
647 simpleMake left right =
648 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
650 if home == Var.Module (ModuleName.inCore ["Basics"]) then
651 Map.findWithDefault simpleMake op basicOps
656basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
658 Map.fromList (infixOps ++ specialOps)
661infixOps :: [(String, Expression () -> Expression () -> Expression ())]
665 (str, InfixExpr () op)
671 , infixOp "&&" OpLAnd
676specialOps :: [(String, Expression () -> Expression () -> Expression ())]
678 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
679 , (,) "|>" $ flip (<|)
680 , (,) "==" $ \a b -> BuiltIn.eq a b
681 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
682 , (,) "<" $ cmp OpLT 0
683 , (,) ">" $ cmp OpGT 0
684 , (,) "<=" $ cmp OpLT 1
685 , (,) ">=" $ cmp OpGT (-1)
686 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
690cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
692 InfixExpr () op (BuiltIn.compare a b) (IntLit () n)
695-- BINARY OPERATOR COLLECTORS
697-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
698collectRightAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
699collectRightAssoc desiredOp left right =
700 collectRightAssocHelp desiredOp [left] right
703collectRightAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
704collectRightAssocHelp desiredOp exprList expr =
706 A.A _ (Binop op left right) | op == desiredOp ->
707 collectRightAssocHelp desiredOp (left : exprList) right
713-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
714collectLeftAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
715collectLeftAssoc desiredOp left right =
716 collectLeftAssocHelp desiredOp [right] left
719collectLeftAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
720collectLeftAssocHelp desiredOp exprList expr =
722 A.A _ (Binop op left right) | op == desiredOp ->
723 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