1module Generate.JavaScript (generate) where
3import Control.Applicative ((<$>),(<*>))
4import Control.Arrow (first, second, (***))
5import Control.Monad.State as State
6import qualified Data.List as List
7import qualified Data.Map as Map
8import Language.ECMAScript3.PrettyPrint as ES
9import Language.ECMAScript3.Syntax
10import qualified Text.PrettyPrint.Leijen as PP
12import qualified AST.Expression.General as Expr
13import AST.Expression.Optimized as Opt
14import qualified AST.Literal as L
15import qualified AST.Module as Module
16import qualified AST.Module.Name as ModuleName
17import qualified AST.Variable as Var
18import Generate.JavaScript.Helpers as Help
19import qualified Generate.JavaScript.BuiltIn as BuiltIn
20import qualified Generate.JavaScript.Literal as Literal
21import qualified Generate.JavaScript.Port as Port
22import qualified Generate.JavaScript.Variable as Var
23import qualified Optimize.Patterns.DecisionTree as DT
26generate :: Module.Optimized -> String
30 Module.program (Module.body module_)
33 State.evalState (mapM generateDef definitions) 0
35 PP.displayS (PP.renderPretty 0.4 120 (ES.prettyPrint stmts)) ""
41 = JsExpr (Expression ())
42 | JsBlock [Statement ()]
45jsExpr :: Expression () -> State Int Code
50jsBlock :: [Statement ()] -> State Int Code
55isBlock :: Code -> Bool
62toStatementList :: Code -> [Statement ()]
71toStatement :: Code -> Statement ()
82toExpr :: Code -> Expression ()
89 function [] stmts `call` []
94generateDef :: Opt.Def -> State Int (Statement ())
96 do (home, name, jsBody) <-
98 Opt.TailDef (Opt.Facts home _) name argNames body ->
99 (,,) home name <$> generateTailFunction name argNames body
101 Opt.Def (Opt.Facts home _) name body ->
102 (,,) home name <$> generateJsExpr body
104 return (VarDeclStmt () [ varDecl (Var.defName home name) jsBody ])
109generateJsExpr :: Opt.Expr -> State Int (Expression ())
110generateJsExpr optExpr =
111 toExpr <$> generateCode optExpr
114generateCode :: Opt.Expr -> State Int Code
118 jsExpr $ Var.canonical var
121 jsExpr (Literal.literal lit)
124 do lo' <- generateJsExpr lo
125 hi' <- generateJsExpr hi
126 jsExpr $ BuiltIn.range lo' hi'
128 Access record field ->
129 do record' <- generateJsExpr record
130 jsExpr $ DotRef () record' (var (Var.safe field))
132 Update record fields ->
134 fieldToJs (field, value) =
135 do jsValue <- generateJsExpr value
136 return (Var.safe field, jsValue)
138 do jsRecord <- generateJsExpr record
139 jsFields <- mapM fieldToJs fields
140 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
144 forM fields $ \(field, e) ->
145 (,) (Var.safe field) <$> generateJsExpr e
148 List.foldl' combine Map.empty fields'
150 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
152 combine record (field, value) =
153 Map.insertWith (++) field [value] record
156 ObjectLit () . map (prop *** ArrayLit ()) $
157 Map.toList (Map.filter (not . null) (Map.map tail fs))
160 map (first prop) (Map.toList (Map.map head fs))
162 Binop op leftExpr rightExpr ->
163 binop op leftExpr rightExpr
165 Function args body ->
166 generateFunction args body
171 aN = "A" ++ show arity
173 do jsFunc <- generateJsExpr func
174 jsArgs <- mapM generateJsExpr args
176 if 2 <= arity && arity <= 9 then
177 ref aN `call` (jsFunc:jsArgs)
179 foldl1 (<|) (jsFunc:jsArgs)
181 TailCall name argNames args ->
183 reassign name tempName =
184 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
186 do args' <- mapM generateJsExpr args
187 tempNames <- mapM (\_ -> Var.fresh) args
189 VarDeclStmt () (zipWith varDecl tempNames args')
190 : zipWith reassign argNames tempNames
191 ++ [ContinueStmt () (Just (Id () name))]
194 do stmts <- mapM generateDef defs
195 code <- generateCode body
196 jsBlock (stmts ++ toStatementList code)
198 If branches finally ->
199 generateIf branches finally
201 Case root decisionTree branches ->
202 JsBlock <$> generateCase root decisionTree branches
204 ExplicitList elements ->
205 do jsElements <- mapM generateJsExpr elements
206 jsExpr $ BuiltIn.list jsElements
209 do jsMembers <- mapM generateJsExpr members
210 jsExpr $ ObjectLit () (ctor : fields jsMembers)
212 ctor = (prop "ctor", string tag)
214 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
216 DataAccess dataExpr index ->
217 do jsDataExpr <- generateJsExpr dataExpr
218 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
220 GLShader _uid src _tipe ->
221 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (L.Str src))]
225 Expr.In name portType ->
226 jsExpr (Port.inbound name portType)
228 Expr.Out name expr portType ->
229 do expr' <- generateJsExpr expr
230 jsExpr (Port.outbound name expr' portType)
232 Expr.Task name expr portType ->
233 do expr' <- generateJsExpr expr
234 jsExpr (Port.task name expr' portType)
236 Crash region maybeCaseCrashValue ->
237 jsExpr $ BuiltIn.crash region maybeCaseCrashValue
242generateFunction :: [String] -> Opt.Expr -> State Int Code
243generateFunction args body =
244 do code <- generateCode body
245 jsExpr (generateFunctionWithArity args code)
248generateTailFunction :: String -> [String] -> Opt.Expr -> State Int (Expression ())
249generateTailFunction name args body =
250 do code <- generateCode body
251 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
254 (WhileStmt () (BoolLit () True) (toStatement code))
257generateFunctionWithArity :: [String] -> Code -> Expression ()
258generateFunctionWithArity args code =
262 if 2 <= arity && arity <= 9 then
264 fN = "F" ++ show arity
266 ref fN <| function args (toStatementList code)
269 (lastArg:otherArgs) = reverse args
270 innerBody = function [lastArg] (toStatementList code)
272 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
277generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
278generateIf givenBranches givenFinally =
280 (branches, finally) =
281 crushIfs givenBranches givenFinally
283 convertBranch (condition, expr) =
284 (,) <$> generateJsExpr condition <*> generateCode expr
286 ifExpression (condition, branch) otherwise =
287 CondExpr () condition branch otherwise
289 ifStatement (condition, branch) otherwise =
290 IfStmt () condition branch otherwise
292 do jsBranches <- mapM convertBranch branches
293 jsFinally <- generateCode finally
295 if any isBlock (jsFinally : map snd jsBranches)
297 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
299 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
303 :: [(Opt.Expr, Opt.Expr)]
305 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
306crushIfs branches finally =
307 crushIfsHelp [] branches finally
311 :: [(Opt.Expr, Opt.Expr)]
312 -> [(Opt.Expr, Opt.Expr)]
314 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
315crushIfsHelp visitedBranches unvisitedBranches finally =
316 case unvisitedBranches of
319 If subBranches subFinally ->
320 crushIfsHelp visitedBranches subBranches subFinally
323 (reverse visitedBranches, finally)
325 (Literal (L.Boolean True), branch) : _ ->
326 crushIfsHelp visitedBranches [] branch
328 visiting : unvisited ->
329 crushIfsHelp (visiting : visitedBranches) unvisited finally
336 -> DT.DecisionTree Opt.Jump
337 -> [(Int, Opt.Branch)]
338 -> State Int [Statement ()]
339generateCase root decisionTree jumpBranches =
340 do decider <- generateDecisionTree root decisionTree
341 foldM (goto root) decider jumpBranches
344goto :: String -> [Statement ()] -> (Int, Opt.Branch) -> State Int [Statement ()]
345goto root decider (target, branch) =
349 (targetLabel root target)
350 (DoWhileStmt () (BlockStmt () decider) (BoolLit () False))
352 do stmts <- generateBranch root branch
353 return (deciderStmt : stmts)
356targetLabel :: String -> Int -> Id ()
357targetLabel root target =
358 Id () (root ++ "_" ++ show target)
361generateDecisionTree :: String -> DT.DecisionTree Opt.Jump -> State Int [Statement ()]
362generateDecisionTree root decisionTree =
364 DT.Match (Opt.Inline branch) ->
365 generateBranch root branch
367 DT.Match (Opt.Jump target) ->
368 return [ BreakStmt () (Just (targetLabel root target)) ]
370 DT.Decision testPath edges fallback ->
371 do accessExpr <- generateJsExpr (pathToExpr root testPath)
374 case fst (head edges) of
376 DotRef () accessExpr (Id () "ctor")
381 caseClauses <- mapM (edgeToCaseClause root) edges
382 caseDefault <- fallbackToDefault root fallback
384 return [ SwitchStmt () testExpr (caseClauses ++ caseDefault) ]
387edgeToCaseClause :: String -> (DT.Test, DT.DecisionTree Opt.Jump) -> State Int (CaseClause ())
388edgeToCaseClause root (test, subTree) =
389 CaseClause () (testToExpr test) <$> generateDecisionTree root subTree
392testToExpr :: DT.Test -> Expression ()
395 DT.Constructor (Var.Canonical _ tag) ->
398 DT.Literal (L.Chr char) ->
405fallbackToDefault :: String -> Maybe (DT.DecisionTree Opt.Jump) -> State Int [CaseClause ()]
406fallbackToDefault root fallback =
412 do stmts <- generateDecisionTree root subTree
413 return [ CaseDefault () stmts ]
416generateBranch :: String -> Opt.Branch -> State Int [Statement ()]
417generateBranch root (Opt.Branch substitutions expr) =
418 do subts <- mapM (loadPath root) substitutions
419 code <- generateCode expr
420 return (subts ++ toStatementList code)
423loadPath :: String -> (String, DT.Path) -> State Int (Statement ())
424loadPath root (name, path) =
425 do jsAccessExpr <- generateJsExpr (pathToExpr root path)
426 return $ VarDeclStmt () [ varDecl name jsAccessExpr ]
429pathToExpr :: String -> DT.Path -> Opt.Expr
430pathToExpr root fullPath =
431 go (Opt.Var (Var.local root)) fullPath
435 DT.Position index subpath ->
436 go (Opt.DataAccess expr index) subpath
438 DT.Field field subpath ->
439 go (Opt.Access expr field) subpath
455binop func left right =
456 do jsLeft <- generateJsExpr left
457 jsRight <- generateJsExpr right
458 jsExpr (makeExpr func jsLeft jsRight)
461-- BINARY OPERATOR HELPERS
463makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
464makeExpr qualifiedOp@(Var.Canonical home op) =
466 simpleMake left right =
467 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
469 if home == Var.Module (ModuleName.inCore ["Basics"]) then
470 Map.findWithDefault simpleMake op basicOps
475basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
477 Map.fromList (infixOps ++ specialOps)
480infixOps :: [(String, Expression () -> Expression () -> Expression ())]
484 (str, InfixExpr () op)
490 , infixOp "&&" OpLAnd
495specialOps :: [(String, Expression () -> Expression () -> Expression ())]
497 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
498 , (,) "==" $ \a b -> BuiltIn.eq a b
499 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
500 , (,) "<" $ cmp OpLT 0
501 , (,) ">" $ cmp OpGT 0
502 , (,) "<=" $ cmp OpLT 1
503 , (,) ">=" $ cmp OpGT (-1)
504 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
508cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
510 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