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 Var.define (Just (Module.name module_)) "_op" (ObjectLit () [])
36 State.evalState (mapM generateDef definitions) 0
38 PP.displayS (PP.renderPretty 0.4 120 (ES.prettyPrint (setup:stmts))) ""
44 = JsExpr (Expression ())
45 | JsBlock [Statement ()]
48jsExpr :: Expression () -> State Int Code
53jsBlock :: [Statement ()] -> State Int Code
58isBlock :: Code -> Bool
65toStatementList :: Code -> [Statement ()]
74toStatement :: Code -> Statement ()
85toExpr :: Code -> Expression ()
92 function [] stmts `call` []
97generateDef :: Opt.Def -> State Int (Statement ())
99 do (home, name, jsBody) <-
101 Opt.TailDef (Opt.Facts home _) name argNames body ->
102 (,,) home name <$> generateTailFunction name argNames body
104 Opt.Def (Opt.Facts home _) name body ->
105 (,,) home name <$> generateJsExpr body
107 return (Var.define home name jsBody)
112generateJsExpr :: Opt.Expr -> State Int (Expression ())
113generateJsExpr optExpr =
114 toExpr <$> generateCode optExpr
117generateCode :: Opt.Expr -> State Int Code
121 jsExpr $ Var.canonical var
124 jsExpr (Literal.literal lit)
127 do lo' <- generateJsExpr lo
128 hi' <- generateJsExpr hi
129 jsExpr $ BuiltIn.range lo' hi'
131 Access record field ->
132 do record' <- generateJsExpr record
133 jsExpr $ DotRef () record' (var (Var.safe field))
135 Update record fields ->
137 fieldToJs (field, value) =
138 do jsValue <- generateJsExpr value
139 return (Var.safe field, jsValue)
141 do jsRecord <- generateJsExpr record
142 jsFields <- mapM fieldToJs fields
143 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
147 forM fields $ \(field, e) ->
148 (,) (Var.safe field) <$> generateJsExpr e
151 List.foldl' combine Map.empty fields'
153 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
155 combine record (field, value) =
156 Map.insertWith (++) field [value] record
159 ObjectLit () . map (prop *** ArrayLit ()) $
160 Map.toList (Map.filter (not . null) (Map.map tail fs))
163 map (first prop) (Map.toList (Map.map head fs))
165 Binop op leftExpr rightExpr ->
166 binop op leftExpr rightExpr
168 Function args body ->
169 generateFunction args body
174 aN = "A" ++ show arity
176 do jsFunc <- generateJsExpr func
177 jsArgs <- mapM generateJsExpr args
179 if 2 <= arity && arity <= 9 then
180 ref aN `call` (jsFunc:jsArgs)
182 foldl1 (<|) (jsFunc:jsArgs)
184 TailCall name argNames args ->
186 reassign name tempName =
187 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
189 do args' <- mapM generateJsExpr args
190 tempNames <- mapM (\_ -> Var.fresh) args
192 VarDeclStmt () (zipWith varDecl tempNames args')
193 : zipWith reassign argNames tempNames
194 ++ [ContinueStmt () (Just (Id () name))]
197 do stmts <- mapM generateDef defs
198 code <- generateCode body
199 jsBlock (stmts ++ toStatementList code)
201 If branches finally ->
202 generateIf branches finally
204 Case root decisionTree branches ->
205 JsBlock <$> generateCase root decisionTree branches
207 ExplicitList elements ->
208 do jsElements <- mapM generateJsExpr elements
209 jsExpr $ BuiltIn.list jsElements
212 do jsMembers <- mapM generateJsExpr members
213 jsExpr $ ObjectLit () (ctor : fields jsMembers)
215 ctor = (prop "ctor", string tag)
217 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
219 DataAccess dataExpr index ->
220 do jsDataExpr <- generateJsExpr dataExpr
221 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
223 GLShader _uid src _tipe ->
224 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (L.Str src))]
228 Expr.In name portType ->
229 jsExpr (Port.inbound name portType)
231 Expr.Out name expr portType ->
232 do expr' <- generateJsExpr expr
233 jsExpr (Port.outbound name expr' portType)
235 Expr.Task name expr portType ->
236 do expr' <- generateJsExpr expr
237 jsExpr (Port.task name expr' portType)
239 Crash region maybeCaseCrashValue ->
240 jsExpr $ BuiltIn.crash region maybeCaseCrashValue
245generateFunction :: [String] -> Opt.Expr -> State Int Code
246generateFunction args body =
247 do code <- generateCode body
248 jsExpr (generateFunctionWithArity args code)
251generateTailFunction :: String -> [String] -> Opt.Expr -> State Int (Expression ())
252generateTailFunction name args body =
253 do code <- generateCode body
254 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
257 (WhileStmt () (BoolLit () True) (toStatement code))
260generateFunctionWithArity :: [String] -> Code -> Expression ()
261generateFunctionWithArity rawArgs code =
263 args = map Var.safe rawArgs
266 if 2 <= arity && arity <= 9 then
268 fN = "F" ++ show arity
270 ref fN <| function args (toStatementList code)
273 (lastArg:otherArgs) = reverse args
274 innerBody = function [lastArg] (toStatementList code)
276 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
281generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
282generateIf givenBranches givenFinally =
284 (branches, finally) =
285 crushIfs givenBranches givenFinally
287 convertBranch (condition, expr) =
288 (,) <$> generateJsExpr condition <*> generateCode expr
290 ifExpression (condition, branch) otherwise =
291 CondExpr () condition branch otherwise
293 ifStatement (condition, branch) otherwise =
294 IfStmt () condition branch otherwise
296 do jsBranches <- mapM convertBranch branches
297 jsFinally <- generateCode finally
299 if any isBlock (jsFinally : map snd jsBranches)
301 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
303 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
307 :: [(Opt.Expr, Opt.Expr)]
309 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
310crushIfs branches finally =
311 crushIfsHelp [] branches finally
315 :: [(Opt.Expr, Opt.Expr)]
316 -> [(Opt.Expr, Opt.Expr)]
318 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
319crushIfsHelp visitedBranches unvisitedBranches finally =
320 case unvisitedBranches of
323 If subBranches subFinally ->
324 crushIfsHelp visitedBranches subBranches subFinally
327 (reverse visitedBranches, finally)
329 (Literal (L.Boolean True), branch) : _ ->
330 crushIfsHelp visitedBranches [] branch
332 visiting : unvisited ->
333 crushIfsHelp (visiting : visitedBranches) unvisited finally
340 -> DT.DecisionTree Opt.Jump
341 -> [(Int, Opt.Branch)]
342 -> State Int [Statement ()]
343generateCase root decisionTree jumpBranches =
344 do decider <- generateDecisionTree root decisionTree
345 foldM (goto root) decider jumpBranches
348goto :: String -> [Statement ()] -> (Int, Opt.Branch) -> State Int [Statement ()]
349goto root decider (target, branch) =
353 (targetLabel root target)
354 (DoWhileStmt () (BlockStmt () decider) (BoolLit () False))
356 do stmts <- generateBranch root branch
357 return (deciderStmt : stmts)
360targetLabel :: String -> Int -> Id ()
361targetLabel root target =
362 Id () (root ++ "_" ++ show target)
365generateDecisionTree :: String -> DT.DecisionTree Opt.Jump -> State Int [Statement ()]
366generateDecisionTree root decisionTree =
368 DT.Match (Opt.Inline branch) ->
369 generateBranch root branch
371 DT.Match (Opt.Jump target) ->
372 return [ BreakStmt () (Just (targetLabel root target)) ]
374 DT.Decision testPath edges fallback ->
375 do accessExpr <- generateJsExpr (pathToExpr root testPath)
378 case fst (head edges) of
380 DotRef () accessExpr (Id () "ctor")
385 caseClauses <- mapM (edgeToCaseClause root) edges
386 caseDefault <- fallbackToDefault root fallback
388 return [ SwitchStmt () testExpr (caseClauses ++ caseDefault) ]
391edgeToCaseClause :: String -> (DT.Test, DT.DecisionTree Opt.Jump) -> State Int (CaseClause ())
392edgeToCaseClause root (test, subTree) =
393 CaseClause () (testToExpr test) <$> generateDecisionTree root subTree
396testToExpr :: DT.Test -> Expression ()
399 DT.Constructor (Var.Canonical _ tag) ->
402 DT.Literal (L.Chr char) ->
409fallbackToDefault :: String -> Maybe (DT.DecisionTree Opt.Jump) -> State Int [CaseClause ()]
410fallbackToDefault root fallback =
416 do stmts <- generateDecisionTree root subTree
417 return [ CaseDefault () stmts ]
420generateBranch :: String -> Opt.Branch -> State Int [Statement ()]
421generateBranch root (Opt.Branch substitutions expr) =
422 do subts <- mapM (loadPath root) substitutions
423 code <- generateCode expr
424 return (subts ++ toStatementList code)
427loadPath :: String -> (String, DT.Path) -> State Int (Statement ())
428loadPath root (name, path) =
429 do jsAccessExpr <- generateJsExpr (pathToExpr root path)
430 return $ VarDeclStmt () [ varDecl name jsAccessExpr ]
433pathToExpr :: String -> DT.Path -> Opt.Expr
434pathToExpr root fullPath =
435 go (Opt.Var (Var.local root)) fullPath
439 DT.Position index subpath ->
440 go (Opt.DataAccess expr index) subpath
442 DT.Field field subpath ->
443 go (Opt.Access expr field) subpath
459binop func left right =
460 do jsLeft <- generateJsExpr left
461 jsRight <- generateJsExpr right
462 jsExpr (makeExpr func jsLeft jsRight)
465-- BINARY OPERATOR HELPERS
467makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
468makeExpr qualifiedOp@(Var.Canonical home op) =
470 simpleMake left right =
471 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
473 if home == Var.Module (ModuleName.inCore ["Basics"]) then
474 Map.findWithDefault simpleMake op basicOps
479basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
481 Map.fromList (infixOps ++ specialOps)
484infixOps :: [(String, Expression () -> Expression () -> Expression ())]
488 (str, InfixExpr () op)
494 , infixOp "&&" OpLAnd
499specialOps :: [(String, Expression () -> Expression () -> Expression ())]
501 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
502 , (,) "==" $ \a b -> BuiltIn.eq a b
503 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
504 , (,) "<" $ cmp OpLT 0
505 , (,) ">" $ cmp OpGT 0
506 , (,) "<=" $ cmp OpLT 1
507 , (,) ">=" $ cmp OpGT (-1)
508 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
512cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
514 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