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.Optimized as Opt
10import qualified AST.Literal as L
11import qualified AST.Module.Name as ModuleName
12import qualified AST.Variable as Var
13import Generate.JavaScript.Helpers as Help
14import qualified Generate.JavaScript.BuiltIn as BuiltIn
15import qualified Generate.JavaScript.Foreign as Foreign
16import qualified Generate.JavaScript.Literal as Literal
17import qualified Generate.JavaScript.Variable as Var
18import qualified Optimize.DecisionTree as DT
26 = JsExpr (Expression ())
27 | JsBlock [Statement ()]
30jsExpr :: Expression () -> State Int Code
35jsBlock :: [Statement ()] -> State Int Code
40isBlock :: Code -> Bool
47toStatementList :: Code -> [Statement ()]
51 [ ReturnStmt () (Just expr) ]
57toStatement :: Code -> Statement ()
61 ReturnStmt () (Just expr)
70toExpr :: Code -> Expression ()
77 function [] stmts `call` []
84generateDef :: Opt.Def -> State Int [Statement ()]
86 do (home, name, jsBody) <-
88 Opt.TailDef (Opt.Facts home _) name argNames body ->
89 (,,) home name <$> generateTailFunction name argNames body
91 Opt.Def (Opt.Facts home _) name body ->
92 (,,) home name <$> generateJsExpr body
94 return (Var.define home name jsBody)
101generateJsExpr :: Opt.Expr -> State Int (Expression ())
102generateJsExpr optExpr =
103 toExpr <$> generateCode optExpr
106generateCode :: Opt.Expr -> State Int Code
110 jsExpr $ Var.canonical var
113 jsExpr (Literal.literal lit)
116 do lo' <- generateJsExpr lo
117 hi' <- generateJsExpr hi
118 jsExpr $ BuiltIn.range lo' hi'
120 Access record field ->
121 do record' <- generateJsExpr record
122 jsExpr $ DotRef () record' (var (Var.safe field))
124 Update record fields ->
126 fieldToJs (field, value) =
127 do jsValue <- generateJsExpr value
128 return (Var.safe field, jsValue)
130 do jsRecord <- generateJsExpr record
131 jsFields <- mapM fieldToJs fields
132 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
136 toField (field, value) =
137 do jsValue <- generateJsExpr value
138 return (Var.safe field ==> jsValue)
140 do jsFields <- mapM toField fields
141 jsExpr $ ObjectLit () jsFields
143 Binop op leftExpr rightExpr ->
144 binop op leftExpr rightExpr
146 Function args body ->
147 generateFunction args body
152 aN = "A" ++ show arity
154 do jsFunc <- generateJsExpr func
155 jsArgs <- mapM generateJsExpr args
157 if 2 <= arity && arity <= 9 then
158 ref aN `call` (jsFunc:jsArgs)
160 foldl1 (<|) (jsFunc:jsArgs)
162 TailCall name argNames args ->
164 reassign name tempName =
165 ExprStmt () (AssignExpr () OpAssign (LVar () (Var.safe name)) (ref tempName))
167 do args' <- mapM generateJsExpr args
168 tempNames <- mapM (\_ -> Var.fresh) args
170 VarDeclStmt () (zipWith varDecl tempNames args')
171 : zipWith reassign argNames tempNames
172 ++ [ContinueStmt () (Just (Id () (Var.safe name)))]
175 do stmts <- mapM generateDef defs
176 code <- generateCode body
177 jsBlock (concat stmts ++ toStatementList code)
179 If branches finally ->
180 generateIf branches finally
182 Case exprName decider jumps ->
183 JsBlock <$> generateCase exprName decider jumps
185 ExplicitList elements ->
186 do jsElements <- mapM generateJsExpr elements
187 jsExpr $ BuiltIn.list jsElements
192 "ctor" ==> StringLit () tag
195 ("_" ++ show n) ==> entry
197 do jsMembers <- mapM generateJsExpr members
198 jsExpr $ ObjectLit () (ctor : zipWith toEntry jsMembers [ 0 :: Int .. ])
200 DataAccess dataExpr index ->
201 do jsDataExpr <- generateJsExpr dataExpr
202 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
205 jsExpr $ BuiltIn.effect moduleName
208 jsExpr $ BuiltIn.effect moduleName
210 ForeignCmd name tipe ->
211 jsExpr $ BuiltIn.foreignCmd name (Foreign.encode tipe)
213 ForeignSub name tipe ->
214 do jsDecoder <- generateJsExpr (Foreign.decode tipe)
215 jsExpr $ BuiltIn.foreignSub name jsDecoder
217 GLShader _uid src _tipe ->
218 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (L.Str src))]
220 Crash home region maybeBranchProblem ->
221 do maybeOptBranchProblem <- traverse generateJsExpr maybeBranchProblem
222 jsExpr $ BuiltIn.crash home region maybeOptBranchProblem
229generateFunction :: [String] -> Opt.Expr -> State Int Code
230generateFunction args body =
231 do code <- generateCode body
232 jsExpr (generateFunctionWithArity args code)
235generateTailFunction :: String -> [String] -> Opt.Expr -> State Int (Expression ())
236generateTailFunction name args body =
237 do code <- generateCode body
238 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
240 (Id () (Var.safe name))
241 (WhileStmt () (BoolLit () True) (toStatement code))
244generateFunctionWithArity :: [String] -> Code -> Expression ()
245generateFunctionWithArity rawArgs code =
247 args = map Var.safe rawArgs
250 if 2 <= arity && arity <= 9 then
252 fN = "F" ++ show arity
254 ref fN <| function args (toStatementList code)
257 (lastArg:otherArgs) = reverse args
258 innerBody = function [lastArg] (toStatementList code)
260 foldl (\body arg -> function [arg] [ReturnStmt () (Just body)]) innerBody otherArgs
267generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
268generateIf givenBranches givenFinally =
270 (branches, finally) =
271 crushIfs givenBranches givenFinally
273 convertBranch (condition, expr) =
274 (,) <$> generateJsExpr condition <*> generateCode expr
276 ifExpression (condition, branch) otherwise =
277 CondExpr () condition branch otherwise
279 ifStatement (condition, branch) otherwise =
280 IfStmt () condition branch otherwise
282 do jsBranches <- mapM convertBranch branches
283 jsFinally <- generateCode finally
285 if any isBlock (jsFinally : map snd jsBranches)
287 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
289 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
293 :: [(Opt.Expr, Opt.Expr)]
295 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
296crushIfs branches finally =
297 crushIfsHelp [] branches finally
301 :: [(Opt.Expr, Opt.Expr)]
302 -> [(Opt.Expr, Opt.Expr)]
304 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
305crushIfsHelp visitedBranches unvisitedBranches finally =
306 case unvisitedBranches of
309 If subBranches subFinally ->
310 crushIfsHelp visitedBranches subBranches subFinally
313 (reverse visitedBranches, finally)
315 (Literal (L.Boolean True), branch) : _ ->
316 crushIfsHelp visitedBranches [] branch
318 visiting : unvisited ->
319 crushIfsHelp (visiting : visitedBranches) unvisited finally
328 -> Opt.Decider Opt.Choice
330 -> State Int [Statement ()]
331generateCase exprName decider jumps =
332 do labelRoot <- Var.fresh
333 decider <- generateDecider exprName labelRoot decider
334 foldM (goto labelRoot) decider jumps
341goto :: String -> [Statement ()] -> (Int, Opt.Expr) -> State Int [Statement ()]
342goto labelRoot deciderStmts (target, branch) =
346 (toLabel labelRoot target)
347 (DoWhileStmt () (BlockStmt () deciderStmts) (BoolLit () False))
349 do code <- generateCode branch
350 return (labeledDeciderStmt : toStatementList code)
353toLabel :: String -> Int -> Id ()
355 Id () (root ++ "_" ++ show target)
359-- turn deciders into ifs and switches
365 -> Opt.Decider Opt.Choice
366 -> State Int [Statement ()]
367generateDecider exprName labelRoot decisionTree =
369 Opt.Leaf (Opt.Inline branch) ->
370 toStatementList <$> generateCode branch
372 Opt.Leaf (Opt.Jump target) ->
373 return [ BreakStmt () (Just (toLabel labelRoot target)) ]
375 Opt.Chain testChain success failure ->
377 makeTest (path, test) =
378 do testExpr <- pathToTestableExpr exprName path test
379 return (InfixExpr () OpStrictEq testExpr (testToExpr test))
381 do testExprs <- mapM makeTest testChain
382 let cond = List.foldl1' (InfixExpr () OpLAnd) testExprs
383 thenBranch <- generateDecider exprName labelRoot success
384 elseBranch <- generateDecider exprName labelRoot failure
385 return [ IfStmt () cond (BlockStmt () thenBranch) (BlockStmt () elseBranch) ]
387 Opt.FanOut path edges fallback ->
388 do testExpr <- pathToTestableExpr exprName path (fst (head edges))
389 caseClauses <- mapM (edgeToCaseClause exprName labelRoot) edges
390 caseDefault <- CaseDefault () <$> generateDecider exprName labelRoot fallback
391 return [ SwitchStmt () testExpr (caseClauses ++ [caseDefault]) ]
397 -> (DT.Test, Opt.Decider Opt.Choice)
398 -> State Int (CaseClause ())
399edgeToCaseClause exprName labelRoot (test, subTree) =
400 CaseClause () (testToExpr test) <$> generateDecider exprName labelRoot subTree
403testToExpr :: DT.Test -> Expression ()
406 DT.Constructor (Var.Canonical _ tag) ->
409 DT.Literal (L.Chr char) ->
420pathToTestableExpr :: String -> DT.Path -> DT.Test -> State Int (Expression ())
421pathToTestableExpr root path exampleTest =
422 do accessExpr <- generateJsExpr (pathToExpr root path)
425 return (DotRef () accessExpr (Id () "ctor"))
427 DT.Literal (L.Chr _) ->
428 return (DotRef () accessExpr (Id () "valueOf") `call` [])
434pathToExpr :: String -> DT.Path -> Opt.Expr
435pathToExpr root fullPath =
436 go (Opt.Var (Var.local root)) fullPath
440 DT.Position index subpath ->
441 go (Opt.DataAccess expr index) subpath
443 DT.Field field subpath ->
444 go (Opt.Access expr field) subpath
462binop func left right =
463 do jsLeft <- generateJsExpr left
464 jsRight <- generateJsExpr right
465 jsExpr (makeExpr func jsLeft jsRight)
469-- BINARY OPERATOR HELPERS
472makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
473makeExpr qualifiedOp@(Var.Canonical home op) =
475 simpleMake left right =
476 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
478 if home == Var.Module (ModuleName.inCore ["Basics"]) then
479 Map.findWithDefault simpleMake op basicOps
484basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
486 Map.fromList (infixOps ++ specialOps)
489infixOps :: [(String, Expression () -> Expression () -> Expression ())]
493 (str, InfixExpr () op)
499 , infixOp "&&" OpLAnd
504specialOps :: [(String, Expression () -> Expression () -> Expression ())]
506 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
507 , (,) "==" $ \a b -> BuiltIn.eq a b
508 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
509 , (,) "<" $ cmp OpLT 0
510 , (,) ">" $ cmp OpGT 0
511 , (,) "<=" $ cmp OpLT 1
512 , (,) ">=" $ cmp OpGT (-1)
513 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
517cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
519 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