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 AST.Expression.General as Expr (Main(..))
10import AST.Expression.Optimized as Opt
11import qualified AST.Literal as L
12import qualified AST.Module.Name as ModuleName
13import qualified AST.Type as Type
14import qualified AST.Variable as Var
15import Generate.JavaScript.Helpers as Help
16import qualified Generate.JavaScript.BuiltIn as BuiltIn
17import qualified Generate.JavaScript.Foreign as Foreign
18import qualified Generate.JavaScript.Literal as Literal
19import qualified Generate.JavaScript.Variable as Var
20import qualified Optimize.DecisionTree as DT
28 = JsExpr (Expression ())
29 | JsBlock [Statement ()]
32jsExpr :: Expression () -> State Int Code
37jsBlock :: [Statement ()] -> State Int Code
42isBlock :: Code -> Bool
49toStatementList :: Code -> [Statement ()]
53 [ ReturnStmt () (Just expr) ]
59toStatement :: Code -> Statement ()
63 ReturnStmt () (Just expr)
72toExpr :: Code -> Expression ()
79 function [] stmts `call` []
86generateDef :: Opt.Def -> State Int [Statement ()]
88 do (home, name, jsBody) <-
90 Opt.TailDef (Opt.Facts home) name argNames body ->
91 (,,) home name <$> generateTailFunction name argNames body
93 Opt.Def (Opt.Facts home) name body ->
94 (,,) home name <$> generateJsExpr body
96 return (Var.define home name jsBody)
103generateJsExpr :: Opt.Expr -> State Int (Expression ())
104generateJsExpr optExpr =
105 toExpr <$> generateCode optExpr
108generateCode :: Opt.Expr -> State Int Code
112 jsExpr $ Var.canonical var
115 jsExpr (Literal.literal lit)
117 Access record field ->
118 do record' <- generateJsExpr record
119 jsExpr $ DotRef () record' (var (Var.safe field))
121 Update record fields ->
123 fieldToJs (field, value) =
124 do jsValue <- generateJsExpr value
125 return (Var.safe field, jsValue)
127 do jsRecord <- generateJsExpr record
128 jsFields <- mapM fieldToJs fields
129 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
133 toField (field, value) =
134 do jsValue <- generateJsExpr value
135 return (Var.safe field ==> jsValue)
137 do jsFields <- mapM toField fields
138 jsExpr $ ObjectLit () jsFields
140 Binop op leftExpr rightExpr ->
141 binop op leftExpr rightExpr
143 Function args body ->
144 generateFunction args body
147 generateCall func args
149 TailCall name argNames args ->
151 reassign name tempName =
152 ExprStmt () (AssignExpr () OpAssign (LVar () (Var.safe name)) (ref tempName))
154 do args' <- mapM generateJsExpr args
155 tempNames <- mapM (\_ -> Var.fresh) args
157 VarDeclStmt () (zipWith varDecl tempNames args')
158 : zipWith reassign argNames tempNames
159 ++ [ContinueStmt () (Just (Id () (Var.safe name)))]
162 do stmts <- mapM generateDef defs
163 code <- generateCode body
164 jsBlock (concat stmts ++ toStatementList code)
166 If branches finally ->
167 generateIf branches finally
169 Case exprName decider jumps ->
170 JsBlock <$> generateCase exprName decider jumps
172 ExplicitList elements ->
173 do jsElements <- mapM generateJsExpr elements
174 jsExpr $ BuiltIn.list jsElements
179 "ctor" ==> StringLit () tag
182 ("_" ++ show n) ==> entry
184 do jsMembers <- mapM generateJsExpr members
185 jsExpr $ ObjectLit () (ctor : zipWith toEntry jsMembers [ 0 :: Int .. ])
187 DataAccess dataExpr index ->
188 do jsDataExpr <- generateJsExpr dataExpr
189 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
192 jsExpr $ BuiltIn.effect moduleName
195 jsExpr $ BuiltIn.effect moduleName
197 OutgoingPort name tipe ->
198 jsExpr $ BuiltIn.outgoingPort name (Foreign.encode tipe)
200 IncomingPort name tipe ->
201 do jsDecoder <- generateJsExpr (Foreign.decode tipe)
202 jsExpr $ BuiltIn.incomingPort name jsDecoder
205 generateProgram kind body
207 GLShader _uid src _tipe ->
208 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (L.Str src))]
210 Crash home region maybeBranchProblem ->
211 do maybeOptBranchProblem <- traverse generateJsExpr maybeBranchProblem
212 jsExpr $ BuiltIn.crash home region maybeOptBranchProblem
219generateProgram :: Expr.Main Type.Canonical -> Opt.Expr -> State Int Code
220generateProgram kind body =
223 do html <- generateJsExpr body
224 jsExpr (Var.coreNative "Platform" "htmlToProgram" <| html)
227 do almostProgram <- generateJsExpr body
228 jsExpr (almostProgram `call` [])
231 do almostProgram <- generateJsExpr body
232 flagDecoder <- generateJsExpr (Foreign.decode tipe)
233 jsExpr (almostProgram <| flagDecoder)
240generateFunction :: [String] -> Opt.Expr -> State Int Code
241generateFunction args body =
242 do code <- generateCode body
243 jsExpr (generateFunctionWithArity args code)
246generateTailFunction :: String -> [String] -> Opt.Expr -> State Int (Expression ())
247generateTailFunction name args body =
248 do code <- generateCode body
249 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
251 (Id () (Var.safe name))
252 (WhileStmt () (BoolLit () True) (toStatement code))
255generateFunctionWithArity :: [String] -> Code -> Expression ()
256generateFunctionWithArity rawArgs code =
258 args = map Var.safe rawArgs
261 if 2 <= arity && arity <= 9 then
263 fN = "F" ++ show arity
265 ref fN <| function args (toStatementList code)
268 (lastArg:otherArgs) = reverse args
269 innerBody = function [lastArg] (toStatementList code)
271 foldl (\body arg -> function [arg] [ReturnStmt () (Just body)]) innerBody otherArgs
278generateCall :: Opt.Expr -> [Opt.Expr] -> State Int Code
279generateCall func args =
281 (Opt.Var var, [arg]) ->
282 case getUnaryOp var of
284 do jsArg <- generateJsExpr arg
285 jsExpr $ PrefixExpr () op jsArg
288 generateCallHelp func args
290 (Opt.Var var, [left, right]) ->
291 case getBinaryOp var of
293 do jsLeft <- generateJsExpr left
294 jsRight <- generateJsExpr right
295 jsExpr $ InfixExpr () op jsLeft jsRight
298 generateCallHelp func args
301 generateCallHelp func args
304generateCallHelp :: Opt.Expr -> [Opt.Expr] -> State Int Code
305generateCallHelp func args =
308 aN = "A" ++ show arity
310 do jsFunc <- generateJsExpr func
311 jsArgs <- mapM generateJsExpr args
313 if 2 <= arity && arity <= 9 then
314 ref aN `call` (jsFunc:jsArgs)
316 foldl1 (<|) (jsFunc:jsArgs)
319getUnaryOp :: Var.Canonical -> Maybe PrefixOp
321 if var == bitwiseComplement then
324 else if var == basicsNot then
331getBinaryOp :: Var.Canonical -> Maybe InfixOp
332getBinaryOp (Var.Canonical home name) =
333 if home /= bitwise then
341 "shiftLeftBy" -> Just OpLShift
342 "shiftRightBy" -> Just OpSpRShift
343 "shiftRightZfBy" -> Just OpZfRShift
347bitwiseComplement :: Var.Canonical
349 Var.inCore ["Bitwise"] "complement"
352basicsNot :: Var.Canonical
354 Var.inCore ["Basics"] "not"
359 Var.Module (ModuleName.inCore ["Bitwise"])
366generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
367generateIf givenBranches givenFinally =
369 (branches, finally) =
370 crushIfs givenBranches givenFinally
372 convertBranch (condition, expr) =
373 (,) <$> generateJsExpr condition <*> generateCode expr
375 ifExpression (condition, branch) otherwise =
376 CondExpr () condition branch otherwise
378 ifStatement (condition, branch) otherwise =
379 IfStmt () condition branch otherwise
381 do jsBranches <- mapM convertBranch branches
382 jsFinally <- generateCode finally
384 if any isBlock (jsFinally : map snd jsBranches)
386 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
388 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
392 :: [(Opt.Expr, Opt.Expr)]
394 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
395crushIfs branches finally =
396 crushIfsHelp [] branches finally
400 :: [(Opt.Expr, Opt.Expr)]
401 -> [(Opt.Expr, Opt.Expr)]
403 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
404crushIfsHelp visitedBranches unvisitedBranches finally =
405 case unvisitedBranches of
408 If subBranches subFinally ->
409 crushIfsHelp visitedBranches subBranches subFinally
412 (reverse visitedBranches, finally)
414 (Literal (L.Boolean True), branch) : _ ->
415 crushIfsHelp visitedBranches [] branch
417 visiting : unvisited ->
418 crushIfsHelp (visiting : visitedBranches) unvisited finally
427 -> Opt.Decider Opt.Choice
429 -> State Int [Statement ()]
430generateCase exprName decider jumps =
431 do labelRoot <- Var.fresh
432 decider <- generateDecider exprName labelRoot decider
433 foldM (goto labelRoot) decider jumps
440goto :: String -> [Statement ()] -> (Int, Opt.Expr) -> State Int [Statement ()]
441goto labelRoot deciderStmts (target, branch) =
445 (toLabel labelRoot target)
446 (DoWhileStmt () (BlockStmt () deciderStmts) (BoolLit () False))
448 do code <- generateCode branch
449 return (labeledDeciderStmt : toStatementList code)
452toLabel :: String -> Int -> Id ()
454 Id () (root ++ "_" ++ show target)
458-- turn deciders into ifs and switches
464 -> Opt.Decider Opt.Choice
465 -> State Int [Statement ()]
466generateDecider exprName labelRoot decisionTree =
468 Opt.Leaf (Opt.Inline branch) ->
469 toStatementList <$> generateCode branch
471 Opt.Leaf (Opt.Jump target) ->
472 return [ BreakStmt () (Just (toLabel labelRoot target)) ]
474 Opt.Chain testChain success failure ->
476 makeTest (path, test) =
477 do testExpr <- pathToTestableExpr exprName path test
478 return (InfixExpr () OpStrictEq testExpr (testToExpr test))
480 do testExprs <- mapM makeTest testChain
481 let cond = List.foldl1' (InfixExpr () OpLAnd) testExprs
482 thenBranch <- generateDecider exprName labelRoot success
483 elseBranch <- generateDecider exprName labelRoot failure
484 return [ IfStmt () cond (BlockStmt () thenBranch) (BlockStmt () elseBranch) ]
486 Opt.FanOut path edges fallback ->
487 do testExpr <- pathToTestableExpr exprName path (fst (head edges))
488 caseClauses <- mapM (edgeToCaseClause exprName labelRoot) edges
489 caseDefault <- CaseDefault () <$> generateDecider exprName labelRoot fallback
490 return [ SwitchStmt () testExpr (caseClauses ++ [caseDefault]) ]
496 -> (DT.Test, Opt.Decider Opt.Choice)
497 -> State Int (CaseClause ())
498edgeToCaseClause exprName labelRoot (test, subTree) =
499 CaseClause () (testToExpr test) <$> generateDecider exprName labelRoot subTree
502testToExpr :: DT.Test -> Expression ()
505 DT.Constructor (Var.Canonical _ tag) ->
508 DT.Literal (L.Chr char) ->
519pathToTestableExpr :: String -> DT.Path -> DT.Test -> State Int (Expression ())
520pathToTestableExpr root path exampleTest =
521 do accessExpr <- generateJsExpr (pathToExpr root path)
524 return (DotRef () accessExpr (Id () "ctor"))
526 DT.Literal (L.Chr _) ->
527 return (DotRef () accessExpr (Id () "valueOf") `call` [])
533pathToExpr :: String -> DT.Path -> Opt.Expr
534pathToExpr root fullPath =
535 go (Opt.Var (Var.local root)) fullPath
539 DT.Position index subpath ->
540 go (Opt.DataAccess expr index) subpath
542 DT.Field field subpath ->
543 go (Opt.Access expr field) subpath
561binop func left right =
562 do jsLeft <- generateJsExpr left
563 jsRight <- generateJsExpr right
564 jsExpr (makeExpr func jsLeft jsRight)
568-- BINARY OPERATOR HELPERS
571makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
572makeExpr qualifiedOp@(Var.Canonical home op) =
574 simpleMake left right =
575 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
577 if home == Var.Module (ModuleName.inCore ["Basics"]) then
578 Map.findWithDefault simpleMake op basicOps
583basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
585 Map.fromList (infixOps ++ specialOps)
588infixOps :: [(String, Expression () -> Expression () -> Expression ())]
592 (str, InfixExpr () op)
598 , infixOp "&&" OpLAnd
603specialOps :: [(String, Expression () -> Expression () -> Expression ())]
605 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
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.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