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 State.evalState (mapM generateDef definitions) 0
32 PP.displayS (PP.renderPretty 0.4 120 (ES.prettyPrint stmts)) ""
38 = JsExpr (Expression ())
39 | JsBlock [Statement ()]
42jsExpr :: Expression () -> State Int Code
47jsBlock :: [Statement ()] -> State Int Code
52isBlock :: Code -> Bool
59toStatementList :: Code -> [Statement ()]
68toStatement :: Code -> Statement ()
79toExpr :: Code -> Expression ()
86 function [] stmts `call` []
91generateDef :: Opt.Def -> State Int (Statement ())
93 do (home, name, jsBody) <-
95 Opt.TailDef (Opt.Facts home _) name argNames body ->
96 (,,) home name <$> generateTailFunction name argNames body
98 Opt.Def (Opt.Facts home _) name body ->
99 (,,) home name <$> generateJsExpr body
101 return (VarDeclStmt () [ varDecl (Var.defName home name) jsBody ])
106generateJsExpr :: Opt.Expr -> State Int (Expression ())
107generateJsExpr optExpr =
108 toExpr <$> generateCode optExpr
111generateCode :: Opt.Expr -> State Int Code
115 jsExpr $ Var.canonical var
118 jsExpr (Literal.literal lit)
121 do lo' <- generateJsExpr lo
122 hi' <- generateJsExpr hi
123 jsExpr $ BuiltIn.range lo' hi'
125 Access record field ->
126 do record' <- generateJsExpr record
127 jsExpr $ DotRef () record' (var (Var.safe field))
129 Update record fields ->
131 fieldToJs (field, value) =
132 do jsValue <- generateJsExpr value
133 return (Var.safe field, jsValue)
135 do jsRecord <- generateJsExpr record
136 jsFields <- mapM fieldToJs fields
137 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
141 forM fields $ \(field, e) ->
142 (,) (Var.safe field) <$> generateJsExpr e
145 List.foldl' combine Map.empty fields'
147 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
149 combine record (field, value) =
150 Map.insertWith (++) field [value] record
153 ObjectLit () . map (prop *** ArrayLit ()) $
154 Map.toList (Map.filter (not . null) (Map.map tail fs))
157 map (first prop) (Map.toList (Map.map head fs))
159 Binop op leftExpr rightExpr ->
160 binop op leftExpr rightExpr
162 Function args body ->
163 generateFunction args body
168 aN = "A" ++ show arity
170 do jsFunc <- generateJsExpr func
171 jsArgs <- mapM generateJsExpr args
173 if 2 <= arity && arity <= 9 then
174 ref aN `call` (jsFunc:jsArgs)
176 foldl1 (<|) (jsFunc:jsArgs)
178 TailCall name argNames args ->
180 reassign name tempName =
181 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
183 do args' <- mapM generateJsExpr args
184 tempNames <- mapM (\_ -> Var.fresh) args
186 VarDeclStmt () (zipWith varDecl tempNames args')
187 : zipWith reassign argNames tempNames
188 ++ [ContinueStmt () (Just (Id () name))]
191 do stmts <- mapM generateDef defs
192 code <- generateCode body
193 jsBlock (stmts ++ toStatementList code)
195 If branches finally ->
196 generateIf branches finally
198 Case root decisionTree branches ->
199 JsBlock <$> generateCase root decisionTree branches
201 ExplicitList elements ->
202 do jsElements <- mapM generateJsExpr elements
203 jsExpr $ BuiltIn.list jsElements
206 do jsMembers <- mapM generateJsExpr members
207 jsExpr $ ObjectLit () (ctor : fields jsMembers)
209 ctor = (prop "ctor", string tag)
211 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
213 DataAccess dataExpr index ->
214 do jsDataExpr <- generateJsExpr dataExpr
215 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
217 GLShader _uid src _tipe ->
218 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (L.Str src))]
222 Expr.In name portType ->
223 jsExpr (Port.inbound name portType)
225 Expr.Out name expr portType ->
226 do expr' <- generateJsExpr expr
227 jsExpr (Port.outbound name expr' portType)
229 Expr.Task name expr portType ->
230 do expr' <- generateJsExpr expr
231 jsExpr (Port.task name expr' portType)
233 Crash region maybeCaseCrashValue ->
234 jsExpr $ BuiltIn.crash region maybeCaseCrashValue
239generateFunction :: [String] -> Opt.Expr -> State Int Code
240generateFunction args body =
241 do code <- generateCode body
242 jsExpr (generateFunctionWithArity args code)
245generateTailFunction :: String -> [String] -> Opt.Expr -> State Int (Expression ())
246generateTailFunction name args body =
247 do code <- generateCode body
248 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
251 (WhileStmt () (BoolLit () True) (toStatement code))
254generateFunctionWithArity :: [String] -> Code -> Expression ()
255generateFunctionWithArity args code =
259 if 2 <= arity && arity <= 9 then
261 fN = "F" ++ show arity
263 ref fN <| function args (toStatementList code)
266 (lastArg:otherArgs) = reverse args
267 innerBody = function [lastArg] (toStatementList code)
269 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
274generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
275generateIf givenBranches givenFinally =
277 (branches, finally) =
278 crushIfs givenBranches givenFinally
280 convertBranch (condition, expr) =
281 (,) <$> generateJsExpr condition <*> generateCode expr
283 ifExpression (condition, branch) otherwise =
284 CondExpr () condition branch otherwise
286 ifStatement (condition, branch) otherwise =
287 IfStmt () condition branch otherwise
289 do jsBranches <- mapM convertBranch branches
290 jsFinally <- generateCode finally
292 if any isBlock (jsFinally : map snd jsBranches)
294 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
296 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
300 :: [(Opt.Expr, Opt.Expr)]
302 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
303crushIfs branches finally =
304 crushIfsHelp [] branches finally
308 :: [(Opt.Expr, Opt.Expr)]
309 -> [(Opt.Expr, Opt.Expr)]
311 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
312crushIfsHelp visitedBranches unvisitedBranches finally =
313 case unvisitedBranches of
316 If subBranches subFinally ->
317 crushIfsHelp visitedBranches subBranches subFinally
320 (reverse visitedBranches, finally)
322 (Literal (L.Boolean True), branch) : _ ->
323 crushIfsHelp visitedBranches [] branch
325 visiting : unvisited ->
326 crushIfsHelp (visiting : visitedBranches) unvisited finally
333 -> DT.DecisionTree Opt.Jump
334 -> [(Int, Opt.Branch)]
335 -> State Int [Statement ()]
336generateCase root decisionTree jumpBranches =
337 do decider <- generateDecisionTree root decisionTree
338 foldM (goto root) decider jumpBranches
341goto :: String -> [Statement ()] -> (Int, Opt.Branch) -> State Int [Statement ()]
342goto root decider (target, branch) =
346 (targetLabel root target)
347 (DoWhileStmt () (BlockStmt () decider) (BoolLit () False))
349 do stmts <- generateBranch root branch
350 return (deciderStmt : stmts)
353targetLabel :: String -> Int -> Id ()
354targetLabel root target =
355 Id () (root ++ "_" ++ show target)
358generateDecisionTree :: String -> DT.DecisionTree Opt.Jump -> State Int [Statement ()]
359generateDecisionTree root decisionTree =
361 DT.Match (Opt.Inline branch) ->
362 generateBranch root branch
364 DT.Match (Opt.Jump target) ->
365 return [ BreakStmt () (Just (targetLabel root target)) ]
367 DT.Decision testPath edges fallback ->
368 do accessExpr <- generateJsExpr (pathToExpr root testPath)
371 case fst (head edges) of
373 DotRef () accessExpr (Id () "ctor")
378 caseClauses <- mapM (edgeToCaseClause root) edges
379 caseDefault <- fallbackToDefault root fallback
381 return [ SwitchStmt () testExpr (caseClauses ++ caseDefault) ]
384edgeToCaseClause :: String -> (DT.Test, DT.DecisionTree Opt.Jump) -> State Int (CaseClause ())
385edgeToCaseClause root (test, subTree) =
386 CaseClause () (testToExpr test) <$> generateDecisionTree root subTree
389testToExpr :: DT.Test -> Expression ()
392 DT.Constructor (Var.Canonical _ tag) ->
395 DT.Literal (L.Chr char) ->
402fallbackToDefault :: String -> Maybe (DT.DecisionTree Opt.Jump) -> State Int [CaseClause ()]
403fallbackToDefault root fallback =
409 do stmts <- generateDecisionTree root subTree
410 return [ CaseDefault () stmts ]
413generateBranch :: String -> Opt.Branch -> State Int [Statement ()]
414generateBranch root (Opt.Branch substitutions expr) =
415 do subts <- mapM (loadPath root) substitutions
416 code <- generateCode expr
417 return (subts ++ toStatementList code)
420loadPath :: String -> (String, DT.Path) -> State Int (Statement ())
421loadPath root (name, path) =
422 do jsAccessExpr <- generateJsExpr (pathToExpr root path)
423 return $ VarDeclStmt () [ varDecl name jsAccessExpr ]
426pathToExpr :: String -> DT.Path -> Opt.Expr
427pathToExpr root fullPath =
428 go (Opt.Var (Var.local root)) fullPath
432 DT.Position index subpath ->
433 go (Opt.DataAccess expr index) subpath
435 DT.Field field subpath ->
436 go (Opt.Access expr field) subpath
452binop func left right =
453 do jsLeft <- generateJsExpr left
454 jsRight <- generateJsExpr right
455 jsExpr (makeExpr func jsLeft jsRight)
458-- BINARY OPERATOR HELPERS
460makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
461makeExpr qualifiedOp@(Var.Canonical home op) =
463 simpleMake left right =
464 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
466 if home == Var.Module (ModuleName.inCore ["Basics"]) then
467 Map.findWithDefault simpleMake op basicOps
472basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
474 Map.fromList (infixOps ++ specialOps)
477infixOps :: [(String, Expression () -> Expression () -> Expression ())]
481 (str, InfixExpr () op)
487 , infixOp "&&" OpLAnd
492specialOps :: [(String, Expression () -> Expression () -> Expression ())]
494 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
495 , (,) "==" $ \a b -> BuiltIn.eq a b
496 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
497 , (,) "<" $ cmp OpLT 0
498 , (,) ">" $ cmp OpGT 0
499 , (,) "<=" $ cmp OpLT 1
500 , (,) ">=" $ cmp OpGT (-1)
501 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
505cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
507 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