1module Generate.JavaScript.Expression (generateDef) where
3import Control.Arrow (first, second, (***))
4import Control.Monad.State (State, forM, foldM)
5import qualified Data.List as List
6import qualified Data.Map as Map
7import Language.ECMAScript3.Syntax
9import qualified AST.Expression.General as Expr
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.Literal as Literal
17import qualified Generate.JavaScript.Port as Port
18import qualified Generate.JavaScript.Variable as Var
19import qualified Optimize.Patterns.DecisionTree as DT
25 = JsExpr (Expression ())
26 | JsBlock [Statement ()]
29jsExpr :: Expression () -> State Int Code
34jsBlock :: [Statement ()] -> State Int Code
39isBlock :: Code -> Bool
46toStatementList :: Code -> [Statement ()]
50 [ ReturnStmt () (Just expr) ]
56toStatement :: Code -> Statement ()
60 ReturnStmt () (Just expr)
69toExpr :: Code -> Expression ()
76 function [] stmts `call` []
81generateDef :: Opt.Def -> State Int (Statement ())
83 do (home, name, jsBody) <-
85 Opt.TailDef (Opt.Facts home _) name argNames body ->
86 (,,) home name <$> generateTailFunction name argNames body
88 Opt.Def (Opt.Facts home _) name body ->
89 (,,) home name <$> generateJsExpr body
91 return (Var.define name jsBody)
96generateJsExpr :: Opt.Expr -> State Int (Expression ())
97generateJsExpr optExpr =
98 toExpr <$> generateCode optExpr
101generateCode :: Opt.Expr -> State Int Code
105 jsExpr $ Var.canonical var
108 jsExpr (Literal.literal lit)
111 do lo' <- generateJsExpr lo
112 hi' <- generateJsExpr hi
113 jsExpr $ BuiltIn.range lo' hi'
115 Access record field ->
116 do record' <- generateJsExpr record
117 jsExpr $ DotRef () record' (var (Var.safe field))
119 Update record fields ->
121 fieldToJs (field, value) =
122 do jsValue <- generateJsExpr value
123 return (Var.safe field, jsValue)
125 do jsRecord <- generateJsExpr record
126 jsFields <- mapM fieldToJs fields
127 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
131 forM fields $ \(field, e) ->
132 (,) (Var.safe field) <$> generateJsExpr e
135 List.foldl' combine Map.empty fields'
137 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
139 combine record (field, value) =
140 Map.insertWith (++) field [value] record
143 ObjectLit () . map (prop *** ArrayLit ()) $
144 Map.toList (Map.filter (not . null) (Map.map tail fs))
147 map (first prop) (Map.toList (Map.map head fs))
149 Binop op leftExpr rightExpr ->
150 binop op leftExpr rightExpr
152 Function args body ->
153 generateFunction args body
158 aN = "A" ++ show arity
160 do jsFunc <- generateJsExpr func
161 jsArgs <- mapM generateJsExpr args
163 if 2 <= arity && arity <= 9 then
164 ref aN `call` (jsFunc:jsArgs)
166 foldl1 (<|) (jsFunc:jsArgs)
168 TailCall name argNames args ->
170 reassign name tempName =
171 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
173 do args' <- mapM generateJsExpr args
174 tempNames <- mapM (\_ -> Var.fresh) args
176 VarDeclStmt () (zipWith varDecl tempNames args')
177 : zipWith reassign argNames tempNames
178 ++ [ContinueStmt () (Just (Id () name))]
181 do stmts <- mapM generateDef defs
182 code <- generateCode body
183 jsBlock (stmts ++ toStatementList code)
185 If branches finally ->
186 generateIf branches finally
188 Case root decisionTree branches ->
189 JsBlock <$> generateCase root decisionTree branches
191 ExplicitList elements ->
192 do jsElements <- mapM generateJsExpr elements
193 jsExpr $ BuiltIn.list jsElements
196 do jsMembers <- mapM generateJsExpr members
197 jsExpr $ ObjectLit () (ctor : fields jsMembers)
199 ctor = (prop "ctor", StringLit () tag)
201 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
203 DataAccess dataExpr index ->
204 do jsDataExpr <- generateJsExpr dataExpr
205 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
207 GLShader _uid src _tipe ->
208 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (L.Str src))]
212 Expr.In name portType ->
213 jsExpr (Port.inbound name portType)
215 Expr.Out name expr portType ->
216 do expr' <- generateJsExpr expr
217 jsExpr (Port.outbound name expr' portType)
219 Expr.Task name expr portType ->
220 do expr' <- generateJsExpr expr
221 jsExpr (Port.task name expr' portType)
223 Crash region maybeCaseCrashValue ->
224 jsExpr $ BuiltIn.crash region maybeCaseCrashValue
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 $ (:[]) $
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
265generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
266generateIf givenBranches givenFinally =
268 (branches, finally) =
269 crushIfs givenBranches givenFinally
271 convertBranch (condition, expr) =
272 (,) <$> generateJsExpr condition <*> generateCode expr
274 ifExpression (condition, branch) otherwise =
275 CondExpr () condition branch otherwise
277 ifStatement (condition, branch) otherwise =
278 IfStmt () condition branch otherwise
280 do jsBranches <- mapM convertBranch branches
281 jsFinally <- generateCode finally
283 if any isBlock (jsFinally : map snd jsBranches)
285 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
287 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
291 :: [(Opt.Expr, Opt.Expr)]
293 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
294crushIfs branches finally =
295 crushIfsHelp [] branches finally
299 :: [(Opt.Expr, Opt.Expr)]
300 -> [(Opt.Expr, Opt.Expr)]
302 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
303crushIfsHelp visitedBranches unvisitedBranches finally =
304 case unvisitedBranches of
307 If subBranches subFinally ->
308 crushIfsHelp visitedBranches subBranches subFinally
311 (reverse visitedBranches, finally)
313 (Literal (L.Boolean True), branch) : _ ->
314 crushIfsHelp visitedBranches [] branch
316 visiting : unvisited ->
317 crushIfsHelp (visiting : visitedBranches) unvisited finally
324 -> DT.DecisionTree Opt.Jump
325 -> [(Int, Opt.Branch)]
326 -> State Int [Statement ()]
327generateCase root decisionTree jumpBranches =
328 do decider <- generateDecisionTree root decisionTree
329 foldM (goto root) decider jumpBranches
332goto :: String -> [Statement ()] -> (Int, Opt.Branch) -> State Int [Statement ()]
333goto root decider (target, branch) =
337 (targetLabel root target)
338 (DoWhileStmt () (BlockStmt () decider) (BoolLit () False))
340 do stmts <- generateBranch root branch
341 return (deciderStmt : stmts)
344targetLabel :: String -> Int -> Id ()
345targetLabel root target =
346 Id () (root ++ "_" ++ show target)
349generateDecisionTree :: String -> DT.DecisionTree Opt.Jump -> State Int [Statement ()]
350generateDecisionTree root decisionTree =
352 DT.Match (Opt.Inline branch) ->
353 generateBranch root branch
355 DT.Match (Opt.Jump target) ->
356 return [ BreakStmt () (Just (targetLabel root target)) ]
358 DT.Decision testPath [(test, subTree)] (Just fallback) ->
360 (tests, deepestSubTree) =
361 collapseTests [(testPath, test)] fallback subTree
363 makeTest (path, test) =
364 do testExpr <- pathToTestableExpr root path test
365 return (InfixExpr () OpStrictEq testExpr (testToExpr test))
367 do testExprs <- mapM makeTest tests
368 let cond = List.foldl1' (InfixExpr () OpLAnd) testExprs
369 thenBranch <- generateDecisionTree root deepestSubTree
370 elseBranch <- generateDecisionTree root fallback
371 return [ IfStmt () cond (BlockStmt () thenBranch) (BlockStmt () elseBranch) ]
373 DT.Decision testPath edges fallback ->
374 do testExpr <- pathToTestableExpr root testPath (fst (head edges))
375 caseClauses <- mapM (edgeToCaseClause root) edges
376 caseDefault <- fallbackToDefault root fallback
377 return [ SwitchStmt () testExpr (caseClauses ++ caseDefault) ]
381 :: [(DT.Path, DT.Test)]
382 -> DT.DecisionTree Opt.Jump
383 -> DT.DecisionTree Opt.Jump
384 -> ([(DT.Path, DT.Test)], DT.DecisionTree Opt.Jump)
385collapseTests tests outerFallback decisionTree =
387 DT.Decision testPath [(test, subTree)] (Just fallback)
388 | fallback == outerFallback ->
389 collapseTests ((testPath, test) : tests) outerFallback subTree
392 (tests, decisionTree)
395edgeToCaseClause :: String -> (DT.Test, DT.DecisionTree Opt.Jump) -> State Int (CaseClause ())
396edgeToCaseClause root (test, subTree) =
397 CaseClause () (testToExpr test) <$> generateDecisionTree root subTree
400pathToTestableExpr :: String -> DT.Path -> DT.Test -> State Int (Expression ())
401pathToTestableExpr root path exampleTest =
402 do accessExpr <- generateJsExpr (pathToExpr root path)
405 return (DotRef () accessExpr (Id () "ctor"))
411testToExpr :: DT.Test -> Expression ()
414 DT.Constructor (Var.Canonical _ tag) ->
417 DT.Literal (L.Chr char) ->
424fallbackToDefault :: String -> Maybe (DT.DecisionTree Opt.Jump) -> State Int [CaseClause ()]
425fallbackToDefault root fallback =
431 do stmts <- generateDecisionTree root subTree
432 return [ CaseDefault () stmts ]
435generateBranch :: String -> Opt.Branch -> State Int [Statement ()]
436generateBranch root (Opt.Branch substitutions expr) =
437 do substs <- mapM (loadPath root) substitutions
439 if null substs then [] else [ VarDeclStmt () substs ]
440 code <- generateCode expr
441 return (substStmts ++ toStatementList code)
444loadPath :: String -> (String, DT.Path) -> State Int (VarDecl ())
445loadPath root (name, path) =
446 do jsAccessExpr <- generateJsExpr (pathToExpr root path)
447 return $ varDecl (Var.safe name) jsAccessExpr
450pathToExpr :: String -> DT.Path -> Opt.Expr
451pathToExpr root fullPath =
452 go (Opt.Var (Var.local root)) fullPath
456 DT.Position index subpath ->
457 go (Opt.DataAccess expr index) subpath
459 DT.Field field subpath ->
460 go (Opt.Access expr field) subpath
476binop func left right =
477 do jsLeft <- generateJsExpr left
478 jsRight <- generateJsExpr right
479 jsExpr (makeExpr func jsLeft jsRight)
482-- BINARY OPERATOR HELPERS
484makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
485makeExpr qualifiedOp@(Var.Canonical home op) =
487 simpleMake left right =
488 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
490 if home == Var.Module (ModuleName.inCore ["Basics"]) then
491 Map.findWithDefault simpleMake op basicOps
496basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
498 Map.fromList (infixOps ++ specialOps)
501infixOps :: [(String, Expression () -> Expression () -> Expression ())]
505 (str, InfixExpr () op)
511 , infixOp "&&" OpLAnd
516specialOps :: [(String, Expression () -> Expression () -> Expression ())]
518 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
519 , (,) "==" $ \a b -> BuiltIn.eq a b
520 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
521 , (,) "<" $ cmp OpLT 0
522 , (,) ">" $ cmp OpGT 0
523 , (,) "<=" $ cmp OpLT 1
524 , (,) ">=" $ cmp OpGT (-1)
525 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
529cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
531 InfixExpr () op (BuiltIn.compare 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