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
149 aN = "A" ++ show arity
151 do jsFunc <- generateJsExpr func
152 jsArgs <- mapM generateJsExpr args
154 if 2 <= arity && arity <= 9 then
155 ref aN `call` (jsFunc:jsArgs)
157 foldl1 (<|) (jsFunc:jsArgs)
159 TailCall name argNames args ->
161 reassign name tempName =
162 ExprStmt () (AssignExpr () OpAssign (LVar () (Var.safe name)) (ref tempName))
164 do args' <- mapM generateJsExpr args
165 tempNames <- mapM (\_ -> Var.fresh) args
167 VarDeclStmt () (zipWith varDecl tempNames args')
168 : zipWith reassign argNames tempNames
169 ++ [ContinueStmt () (Just (Id () (Var.safe name)))]
172 do stmts <- mapM generateDef defs
173 code <- generateCode body
174 jsBlock (concat stmts ++ toStatementList code)
176 If branches finally ->
177 generateIf branches finally
179 Case exprName decider jumps ->
180 JsBlock <$> generateCase exprName decider jumps
182 ExplicitList elements ->
183 do jsElements <- mapM generateJsExpr elements
184 jsExpr $ BuiltIn.list jsElements
189 "ctor" ==> StringLit () tag
192 ("_" ++ show n) ==> entry
194 do jsMembers <- mapM generateJsExpr members
195 jsExpr $ ObjectLit () (ctor : zipWith toEntry jsMembers [ 0 :: Int .. ])
197 DataAccess dataExpr index ->
198 do jsDataExpr <- generateJsExpr dataExpr
199 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
202 jsExpr $ BuiltIn.effect moduleName
205 jsExpr $ BuiltIn.effect moduleName
207 OutgoingPort name tipe ->
208 jsExpr $ BuiltIn.outgoingPort name (Foreign.encode tipe)
210 IncomingPort name tipe ->
211 do jsDecoder <- generateJsExpr (Foreign.decode tipe)
212 jsExpr $ BuiltIn.incomingPort name jsDecoder
215 generateProgram kind body
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
229generateProgram :: Expr.Main Type.Canonical -> Opt.Expr -> State Int Code
230generateProgram kind body =
233 do html <- generateJsExpr body
234 jsExpr (Var.coreNative "Platform" "htmlToProgram" <| html)
237 do almostProgram <- generateJsExpr body
238 jsExpr (almostProgram `call` [])
241 do almostProgram <- generateJsExpr body
242 flagDecoder <- generateJsExpr (Foreign.decode tipe)
243 jsExpr (almostProgram <| flagDecoder)
250generateFunction :: [String] -> Opt.Expr -> State Int Code
251generateFunction args body =
252 do code <- generateCode body
253 jsExpr (generateFunctionWithArity args code)
256generateTailFunction :: String -> [String] -> Opt.Expr -> State Int (Expression ())
257generateTailFunction name args body =
258 do code <- generateCode body
259 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
261 (Id () (Var.safe name))
262 (WhileStmt () (BoolLit () True) (toStatement code))
265generateFunctionWithArity :: [String] -> Code -> Expression ()
266generateFunctionWithArity rawArgs code =
268 args = map Var.safe rawArgs
271 if 2 <= arity && arity <= 9 then
273 fN = "F" ++ show arity
275 ref fN <| function args (toStatementList code)
278 (lastArg:otherArgs) = reverse args
279 innerBody = function [lastArg] (toStatementList code)
281 foldl (\body arg -> function [arg] [ReturnStmt () (Just body)]) innerBody otherArgs
288generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
289generateIf givenBranches givenFinally =
291 (branches, finally) =
292 crushIfs givenBranches givenFinally
294 convertBranch (condition, expr) =
295 (,) <$> generateJsExpr condition <*> generateCode expr
297 ifExpression (condition, branch) otherwise =
298 CondExpr () condition branch otherwise
300 ifStatement (condition, branch) otherwise =
301 IfStmt () condition branch otherwise
303 do jsBranches <- mapM convertBranch branches
304 jsFinally <- generateCode finally
306 if any isBlock (jsFinally : map snd jsBranches)
308 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
310 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
314 :: [(Opt.Expr, Opt.Expr)]
316 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
317crushIfs branches finally =
318 crushIfsHelp [] branches finally
322 :: [(Opt.Expr, Opt.Expr)]
323 -> [(Opt.Expr, Opt.Expr)]
325 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
326crushIfsHelp visitedBranches unvisitedBranches finally =
327 case unvisitedBranches of
330 If subBranches subFinally ->
331 crushIfsHelp visitedBranches subBranches subFinally
334 (reverse visitedBranches, finally)
336 (Literal (L.Boolean True), branch) : _ ->
337 crushIfsHelp visitedBranches [] branch
339 visiting : unvisited ->
340 crushIfsHelp (visiting : visitedBranches) unvisited finally
349 -> Opt.Decider Opt.Choice
351 -> State Int [Statement ()]
352generateCase exprName decider jumps =
353 do labelRoot <- Var.fresh
354 decider <- generateDecider exprName labelRoot decider
355 foldM (goto labelRoot) decider jumps
362goto :: String -> [Statement ()] -> (Int, Opt.Expr) -> State Int [Statement ()]
363goto labelRoot deciderStmts (target, branch) =
367 (toLabel labelRoot target)
368 (DoWhileStmt () (BlockStmt () deciderStmts) (BoolLit () False))
370 do code <- generateCode branch
371 return (labeledDeciderStmt : toStatementList code)
374toLabel :: String -> Int -> Id ()
376 Id () (root ++ "_" ++ show target)
380-- turn deciders into ifs and switches
386 -> Opt.Decider Opt.Choice
387 -> State Int [Statement ()]
388generateDecider exprName labelRoot decisionTree =
390 Opt.Leaf (Opt.Inline branch) ->
391 toStatementList <$> generateCode branch
393 Opt.Leaf (Opt.Jump target) ->
394 return [ BreakStmt () (Just (toLabel labelRoot target)) ]
396 Opt.Chain testChain success failure ->
398 makeTest (path, test) =
399 do testExpr <- pathToTestableExpr exprName path test
400 return (InfixExpr () OpStrictEq testExpr (testToExpr test))
402 do testExprs <- mapM makeTest testChain
403 let cond = List.foldl1' (InfixExpr () OpLAnd) testExprs
404 thenBranch <- generateDecider exprName labelRoot success
405 elseBranch <- generateDecider exprName labelRoot failure
406 return [ IfStmt () cond (BlockStmt () thenBranch) (BlockStmt () elseBranch) ]
408 Opt.FanOut path edges fallback ->
409 do testExpr <- pathToTestableExpr exprName path (fst (head edges))
410 caseClauses <- mapM (edgeToCaseClause exprName labelRoot) edges
411 caseDefault <- CaseDefault () <$> generateDecider exprName labelRoot fallback
412 return [ SwitchStmt () testExpr (caseClauses ++ [caseDefault]) ]
418 -> (DT.Test, Opt.Decider Opt.Choice)
419 -> State Int (CaseClause ())
420edgeToCaseClause exprName labelRoot (test, subTree) =
421 CaseClause () (testToExpr test) <$> generateDecider exprName labelRoot subTree
424testToExpr :: DT.Test -> Expression ()
427 DT.Constructor (Var.Canonical _ tag) ->
430 DT.Literal (L.Chr char) ->
441pathToTestableExpr :: String -> DT.Path -> DT.Test -> State Int (Expression ())
442pathToTestableExpr root path exampleTest =
443 do accessExpr <- generateJsExpr (pathToExpr root path)
446 return (DotRef () accessExpr (Id () "ctor"))
448 DT.Literal (L.Chr _) ->
449 return (DotRef () accessExpr (Id () "valueOf") `call` [])
455pathToExpr :: String -> DT.Path -> Opt.Expr
456pathToExpr root fullPath =
457 go (Opt.Var (Var.local root)) fullPath
461 DT.Position index subpath ->
462 go (Opt.DataAccess expr index) subpath
464 DT.Field field subpath ->
465 go (Opt.Access expr field) subpath
483binop func left right =
484 do jsLeft <- generateJsExpr left
485 jsRight <- generateJsExpr right
486 jsExpr (makeExpr func jsLeft jsRight)
490-- BINARY OPERATOR HELPERS
493makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
494makeExpr qualifiedOp@(Var.Canonical home op) =
496 simpleMake left right =
497 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
499 if home == Var.Module (ModuleName.inCore ["Basics"]) then
500 Map.findWithDefault simpleMake op basicOps
505basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
507 Map.fromList (infixOps ++ specialOps)
510infixOps :: [(String, Expression () -> Expression () -> Expression ())]
514 (str, InfixExpr () op)
520 , infixOp "&&" OpLAnd
525specialOps :: [(String, Expression () -> Expression () -> Expression ())]
527 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
528 , (,) "==" $ \a b -> BuiltIn.eq a b
529 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
530 , (,) "<" $ cmp OpLT 0
531 , (,) ">" $ cmp OpGT 0
532 , (,) "<=" $ cmp OpLT 1
533 , (,) ">=" $ cmp OpGT (-1)
534 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
538cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
540 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