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 [(test, subTree)] (Just fallback) ->
376 (tests, deepestSubTree) =
377 collapseTests [(testPath, test)] fallback subTree
379 makeTest (path, test) =
380 do testExpr <- pathToTestableExpr root path test
381 return (InfixExpr () OpStrictEq testExpr (testToExpr test))
383 do testExprs <- mapM makeTest tests
384 let cond = List.foldl1' (InfixExpr () OpLAnd) testExprs
385 thenBranch <- generateDecisionTree root deepestSubTree
386 elseBranch <- generateDecisionTree root fallback
387 return [ IfStmt () cond (BlockStmt () thenBranch) (BlockStmt () elseBranch) ]
389 DT.Decision testPath edges fallback ->
390 do testExpr <- pathToTestableExpr root testPath (fst (head edges))
391 caseClauses <- mapM (edgeToCaseClause root) edges
392 caseDefault <- fallbackToDefault root fallback
393 return [ SwitchStmt () testExpr (caseClauses ++ caseDefault) ]
397 :: [(DT.Path, DT.Test)]
398 -> DT.DecisionTree Opt.Jump
399 -> DT.DecisionTree Opt.Jump
400 -> ([(DT.Path, DT.Test)], DT.DecisionTree Opt.Jump)
401collapseTests tests outerFallback decisionTree =
403 DT.Decision testPath [(test, subTree)] (Just fallback)
404 | fallback == outerFallback ->
405 collapseTests ((testPath, test) : tests) outerFallback subTree
408 (tests, decisionTree)
411edgeToCaseClause :: String -> (DT.Test, DT.DecisionTree Opt.Jump) -> State Int (CaseClause ())
412edgeToCaseClause root (test, subTree) =
413 CaseClause () (testToExpr test) <$> generateDecisionTree root subTree
416pathToTestableExpr :: String -> DT.Path -> DT.Test -> State Int (Expression ())
417pathToTestableExpr root path exampleTest =
418 do accessExpr <- generateJsExpr (pathToExpr root path)
421 return (DotRef () accessExpr (Id () "ctor"))
427testToExpr :: DT.Test -> Expression ()
430 DT.Constructor (Var.Canonical _ tag) ->
433 DT.Literal (L.Chr char) ->
440fallbackToDefault :: String -> Maybe (DT.DecisionTree Opt.Jump) -> State Int [CaseClause ()]
441fallbackToDefault root fallback =
447 do stmts <- generateDecisionTree root subTree
448 return [ CaseDefault () stmts ]
451generateBranch :: String -> Opt.Branch -> State Int [Statement ()]
452generateBranch root (Opt.Branch substitutions expr) =
453 do substs <- mapM (loadPath root) substitutions
455 if null substs then [] else [ VarDeclStmt () substs ]
456 code <- generateCode expr
457 return (substStmts ++ toStatementList code)
460loadPath :: String -> (String, DT.Path) -> State Int (VarDecl ())
461loadPath root (name, path) =
462 do jsAccessExpr <- generateJsExpr (pathToExpr root path)
463 return $ varDecl (Var.safe name) jsAccessExpr
466pathToExpr :: String -> DT.Path -> Opt.Expr
467pathToExpr root fullPath =
468 go (Opt.Var (Var.local root)) fullPath
472 DT.Position index subpath ->
473 go (Opt.DataAccess expr index) subpath
475 DT.Field field subpath ->
476 go (Opt.Access expr field) subpath
492binop func left right =
493 do jsLeft <- generateJsExpr left
494 jsRight <- generateJsExpr right
495 jsExpr (makeExpr func jsLeft jsRight)
498-- BINARY OPERATOR HELPERS
500makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
501makeExpr qualifiedOp@(Var.Canonical home op) =
503 simpleMake left right =
504 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
506 if home == Var.Module (ModuleName.inCore ["Basics"]) then
507 Map.findWithDefault simpleMake op basicOps
512basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
514 Map.fromList (infixOps ++ specialOps)
517infixOps :: [(String, Expression () -> Expression () -> Expression ())]
521 (str, InfixExpr () op)
527 , infixOp "&&" OpLAnd
532specialOps :: [(String, Expression () -> Expression () -> Expression ())]
534 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
535 , (,) "==" $ \a b -> BuiltIn.eq a b
536 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
537 , (,) "<" $ cmp OpLT 0
538 , (,) ">" $ cmp OpGT 0
539 , (,) "<=" $ cmp OpLT 1
540 , (,) ">=" $ cmp OpGT (-1)
541 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
545cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
547 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