1module Generate.JavaScript.Expression (generateDef) where
3import Control.Arrow (second)
4import Control.Monad.State (State, foldM)
5import qualified Data.List as List
6import qualified Data.Map as Map
7import Language.ECMAScript3.Syntax
9import qualified AST.Expression.Canonical as Can
10import AST.Expression.Optimized as Opt
11import qualified AST.Literal as L
12import qualified AST.Module.Name as ModuleName
13import qualified AST.Variable as Var
14import Generate.JavaScript.Helpers as Help
15import qualified Generate.JavaScript.BuiltIn as BuiltIn
16import qualified Generate.JavaScript.Foreign as Foreign
17import qualified Generate.JavaScript.Literal as Literal
18import qualified Generate.JavaScript.Variable as Var
19import qualified Optimize.DecisionTree as DT
27 = JsExpr (Expression ())
28 | JsBlock [Statement ()]
31jsExpr :: Expression () -> State Int Code
36jsBlock :: [Statement ()] -> State Int Code
41isBlock :: Code -> Bool
48toStatementList :: Code -> [Statement ()]
52 [ ReturnStmt () (Just expr) ]
58toStatement :: Code -> Statement ()
62 ReturnStmt () (Just expr)
71toExpr :: Code -> Expression ()
78 function [] stmts `call` []
85generateDef :: Opt.Def -> State Int [Statement ()]
87 do (home, name, jsBody) <-
89 Opt.TailDef (Opt.Facts home) name argNames body ->
90 (,,) home name <$> generateTailFunction name argNames body
92 Opt.Def (Opt.Facts home) name body ->
93 (,,) home name <$> generateJsExpr body
95 return (Var.define home name jsBody)
102generateJsExpr :: Opt.Expr -> State Int (Expression ())
103generateJsExpr optExpr =
104 toExpr <$> generateCode optExpr
107generateCode :: Opt.Expr -> State Int Code
111 jsExpr $ Var.canonical var
114 jsExpr (Literal.literal lit)
116 Access record field ->
117 do record' <- generateJsExpr record
118 jsExpr $ DotRef () record' (var (Var.safe field))
120 Update record fields ->
122 fieldToJs (field, value) =
123 do jsValue <- generateJsExpr value
124 return (Var.safe field, jsValue)
126 do jsRecord <- generateJsExpr record
127 jsFields <- mapM fieldToJs fields
128 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
132 toField (field, value) =
133 do jsValue <- generateJsExpr value
134 return (Var.safe field ==> jsValue)
136 do jsFields <- mapM toField fields
137 jsExpr $ ObjectLit () jsFields
139 Binop op leftExpr rightExpr ->
140 binop op leftExpr rightExpr
142 Function args body ->
143 generateFunction args body
146 generateCall func args
148 TailCall name argNames args ->
150 reassign name tempName =
151 ExprStmt () (AssignExpr () OpAssign (LVar () (Var.safe name)) (ref tempName))
153 do args' <- mapM generateJsExpr args
154 tempNames <- mapM (\_ -> Var.fresh) args
156 VarDeclStmt () (zipWith varDecl tempNames args')
157 : zipWith reassign argNames tempNames
158 ++ [ContinueStmt () (Just (Id () (Var.safe name)))]
161 do stmts <- mapM generateDef defs
162 code <- generateCode body
163 jsBlock (concat stmts ++ toStatementList code)
165 If branches finally ->
166 generateIf branches finally
168 Case exprName decider jumps ->
169 JsBlock <$> generateCase exprName decider jumps
172 do jsElements <- mapM generateJsExpr elements
173 jsExpr $ BuiltIn.list jsElements
178 "ctor" ==> StringLit () tag
181 ("_" ++ show n) ==> entry
183 do jsMembers <- mapM generateJsExpr members
184 jsExpr $ ObjectLit () (ctor : zipWith toEntry jsMembers [ 0 :: Int .. ])
186 CtorAccess dataExpr index ->
187 do jsDataExpr <- generateJsExpr dataExpr
188 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
191 jsExpr $ BuiltIn.effect moduleName
194 jsExpr $ BuiltIn.effect moduleName
196 OutgoingPort name tipe ->
197 jsExpr $ BuiltIn.outgoingPort name (Foreign.encode tipe)
199 IncomingPort name tipe ->
200 do jsDecoder <- generateJsExpr (Foreign.decode tipe)
201 jsExpr $ BuiltIn.incomingPort name jsDecoder
204 generateProgram kind body
206 GLShader _uid src _tipe ->
207 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (L.Str src))]
209 Crash home region maybeBranchProblem ->
210 do maybeOptBranchProblem <- traverse generateJsExpr maybeBranchProblem
211 jsExpr $ BuiltIn.crash home region maybeOptBranchProblem
218generateProgram :: Can.Main -> Opt.Expr -> State Int Code
219generateProgram kind body =
222 do html <- generateJsExpr body
223 jsExpr (Var.staticProgram <| html)
226 do almostProgram <- generateJsExpr body
227 jsExpr (almostProgram `call` [])
230 do almostProgram <- generateJsExpr body
231 flagDecoder <- generateJsExpr (Foreign.decode tipe)
232 jsExpr (almostProgram <| flagDecoder)
239generateFunction :: [String] -> Opt.Expr -> State Int Code
240generateFunction args body =
241 do code <- generateCode body
242 jsExpr (generateFunctionWithArity args code)
245generateTailFunction :: String -> [String] -> Opt.Expr -> State Int (Expression ())
246generateTailFunction name args body =
247 do code <- generateCode body
248 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
250 (Id () (Var.safe name))
251 (WhileStmt () (BoolLit () True) (toStatement code))
254generateFunctionWithArity :: [String] -> Code -> Expression ()
255generateFunctionWithArity rawArgs code =
257 args = map Var.safe rawArgs
260 if 2 <= arity && arity <= 9 then
262 fN = "F" ++ show arity
264 ref fN <| function args (toStatementList code)
267 (lastArg:otherArgs) = reverse args
268 innerBody = function [lastArg] (toStatementList code)
270 foldl (\body arg -> function [arg] [ReturnStmt () (Just body)]) innerBody otherArgs
277generateCall :: Opt.Expr -> [Opt.Expr] -> State Int Code
278generateCall func args =
280 (Opt.Var var, [arg]) ->
281 case getUnaryOp var of
283 do jsArg <- generateJsExpr arg
284 jsExpr $ PrefixExpr () op jsArg
287 generateCallHelp func args
289 (Opt.Var var, [ arg1, arg2 ]) ->
290 case getBinaryOp var arg1 arg2 of
291 Just (op, left, right) ->
292 do jsLeft <- generateJsExpr left
293 jsRight <- generateJsExpr right
294 jsExpr $ InfixExpr () op jsLeft jsRight
297 generateCallHelp func args
300 generateCallHelp func args
303generateCallHelp :: Opt.Expr -> [Opt.Expr] -> State Int Code
304generateCallHelp func args =
307 aN = "A" ++ show arity
309 do jsFunc <- generateJsExpr func
310 jsArgs <- mapM generateJsExpr args
312 if 2 <= arity && arity <= 9 then
313 ref aN `call` (jsFunc:jsArgs)
315 foldl1 (<|) (jsFunc:jsArgs)
318getUnaryOp :: Var.Canonical -> Maybe PrefixOp
320 if var == bitwiseComplement then
323 else if var == basicsNot then
330getBinaryOp :: Var.Canonical -> Opt.Expr -> Opt.Expr -> Maybe (InfixOp, Opt.Expr, Opt.Expr)
331getBinaryOp (Var.Canonical home name) left right =
332 if home /= bitwise then
337 "and" -> Just ( OpBAnd, left, right )
338 "or" -> Just ( OpBOr, left, right )
339 "xor" -> Just ( OpBXor, left, right )
340 "shiftLeftBy" -> Just ( OpLShift, right, left )
341 "shiftRightBy" -> Just ( OpSpRShift, right, left )
342 "shiftRightZfBy" -> Just ( OpZfRShift, right, left )
346bitwiseComplement :: Var.Canonical
348 Var.inCore ["Bitwise"] "complement"
351basicsNot :: Var.Canonical
353 Var.inCore ["Basics"] "not"
358 Var.Module (ModuleName.inCore ["Bitwise"])
365generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
366generateIf givenBranches givenFinally =
368 (branches, finally) =
369 crushIfs givenBranches givenFinally
371 convertBranch (condition, expr) =
372 (,) <$> generateJsExpr condition <*> generateCode expr
374 ifExpression (condition, branch) otherwise =
375 CondExpr () condition branch otherwise
377 ifStatement (condition, branch) otherwise =
378 IfStmt () condition branch otherwise
380 do jsBranches <- mapM convertBranch branches
381 jsFinally <- generateCode finally
383 if any isBlock (jsFinally : map snd jsBranches)
385 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
387 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
391 :: [(Opt.Expr, Opt.Expr)]
393 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
394crushIfs branches finally =
395 crushIfsHelp [] branches finally
399 :: [(Opt.Expr, Opt.Expr)]
400 -> [(Opt.Expr, Opt.Expr)]
402 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
403crushIfsHelp visitedBranches unvisitedBranches finally =
404 case unvisitedBranches of
407 If subBranches subFinally ->
408 crushIfsHelp visitedBranches subBranches subFinally
411 (reverse visitedBranches, finally)
413 (Literal (L.Boolean True), branch) : _ ->
414 crushIfsHelp visitedBranches [] branch
416 visiting : unvisited ->
417 crushIfsHelp (visiting : visitedBranches) unvisited finally
426 -> Opt.Decider Opt.Choice
428 -> State Int [Statement ()]
429generateCase exprName decider jumps =
430 do labelRoot <- Var.fresh
431 decider <- generateDecider exprName labelRoot decider
432 foldM (goto labelRoot) decider jumps
439goto :: String -> [Statement ()] -> (Int, Opt.Expr) -> State Int [Statement ()]
440goto labelRoot deciderStmts (target, branch) =
444 (toLabel labelRoot target)
445 (DoWhileStmt () (BlockStmt () deciderStmts) (BoolLit () False))
447 do code <- generateCode branch
448 return (labeledDeciderStmt : toStatementList code)
451toLabel :: String -> Int -> Id ()
453 Id () (root ++ "_" ++ show target)
457-- turn deciders into ifs and switches
463 -> Opt.Decider Opt.Choice
464 -> State Int [Statement ()]
465generateDecider exprName labelRoot decisionTree =
467 Opt.Leaf (Opt.Inline branch) ->
468 toStatementList <$> generateCode branch
470 Opt.Leaf (Opt.Jump target) ->
471 return [ BreakStmt () (Just (toLabel labelRoot target)) ]
473 Opt.Chain testChain success failure ->
475 makeTest (path, test) =
476 do testExpr <- pathToTestableExpr exprName path test
477 return (InfixExpr () OpStrictEq testExpr (testToExpr test))
479 do testExprs <- mapM makeTest testChain
480 let cond = List.foldl1' (InfixExpr () OpLAnd) testExprs
481 thenBranch <- generateDecider exprName labelRoot success
482 elseBranch <- generateDecider exprName labelRoot failure
483 return [ IfStmt () cond (BlockStmt () thenBranch) (BlockStmt () elseBranch) ]
485 Opt.FanOut path edges fallback ->
486 do testExpr <- pathToTestableExpr exprName path (fst (head edges))
487 caseClauses <- mapM (edgeToCaseClause exprName labelRoot) edges
488 caseDefault <- CaseDefault () <$> generateDecider exprName labelRoot fallback
489 return [ SwitchStmt () testExpr (caseClauses ++ [caseDefault]) ]
495 -> (DT.Test, Opt.Decider Opt.Choice)
496 -> State Int (CaseClause ())
497edgeToCaseClause exprName labelRoot (test, subTree) =
498 CaseClause () (testToExpr test) <$> generateDecider exprName labelRoot subTree
501testToExpr :: DT.Test -> Expression ()
504 DT.Constructor (Var.Canonical _ tag) ->
507 DT.Literal (L.Chr char) ->
518pathToTestableExpr :: String -> DT.Path -> DT.Test -> State Int (Expression ())
519pathToTestableExpr root path exampleTest =
520 do accessExpr <- generateJsExpr (pathToExpr root path)
523 return (DotRef () accessExpr (Id () "ctor"))
525 DT.Literal (L.Chr _) ->
526 return (DotRef () accessExpr (Id () "valueOf") `call` [])
532pathToExpr :: String -> DT.Path -> Opt.Expr
533pathToExpr root fullPath =
534 go (Opt.Var (Var.local root)) fullPath
538 DT.Position index subpath ->
539 go (Opt.CtorAccess expr index) subpath
541 DT.Field field subpath ->
542 go (Opt.Access expr field) subpath
560binop func left right =
561 do jsLeft <- generateJsExpr left
562 jsRight <- generateJsExpr right
563 jsExpr (binopHelp func jsLeft jsRight)
567-- BINARY OPERATOR HELPERS
570binopHelp :: Var.Canonical -> Expression () -> Expression () -> Expression ()
571binopHelp qualifiedOp@(Var.Canonical home op) leftExpr rightExpr =
573 simpleMake left right =
574 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
576 if home == basicsModule then
577 (Map.findWithDefault simpleMake op basicOps) leftExpr rightExpr
579 else if op == "::" && (home == listModule || home == listModuleInternals) then
580 BuiltIn.cons leftExpr rightExpr
583 simpleMake leftExpr rightExpr
586listModule :: Var.Home
588 Var.Module (ModuleName.inCore ["List"])
591listModuleInternals :: Var.Home
593 Var.TopLevel (ModuleName.inCore ["List"])
596basicsModule :: Var.Home
598 Var.Module (ModuleName.inCore ["Basics"])
601basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
603 Map.fromList (infixOps ++ specialOps)
606infixOps :: [(String, Expression () -> Expression () -> Expression ())]
610 (str, InfixExpr () op)
616 , infixOp "&&" OpLAnd
621specialOps :: [(String, Expression () -> Expression () -> Expression ())]
623 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
624 , (,) "==" $ \a b -> BuiltIn.eq a b
625 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
626 , (,) "<" $ cmp OpLT 0
627 , (,) ">" $ cmp OpGT 0
628 , (,) "<=" $ cmp OpLT 1
629 , (,) ">=" $ cmp OpGT (-1)
630 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
634cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
636 InfixExpr () op (BuiltIn.cmp a b) (IntLit () n)
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