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
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.Cases as PM
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 ()
85 function [] stmts `call` []
91 Maybe (String, [String], Label)
100toJsExpr :: Opt.Expr -> State Int (Expression ())
105toCode :: Opt.Expr -> State Int Code
110exprToJsExpr :: TailCallContext -> Opt.Expr -> State Int (Expression ())
111exprToJsExpr tcc canonicalExpr =
112 toExpr <$> exprToCode tcc canonicalExpr
115exprToCode :: TailCallContext -> Opt.Expr -> State Int Code
119 jsExpr $ Var.canonical var
122 jsExpr (Literal.literal lit)
125 do lo' <- toJsExpr lo
127 jsExpr $ BuiltIn.range lo' hi'
129 Access record field ->
130 do record' <- toJsExpr record
131 jsExpr $ DotRef () record' (var (Var.safe field))
133 Update record fields ->
135 fieldToJs (field, value) =
136 do jsValue <- toJsExpr value
137 return (Var.safe field, jsValue)
139 do jsRecord <- toJsExpr record
140 jsFields <- mapM fieldToJs fields
141 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
145 forM fields $ \(field, e) ->
146 (,) (Var.safe field) <$> toJsExpr e
149 List.foldl' combine Map.empty fields'
151 jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
153 combine record (field, value) =
154 Map.insertWith (++) field [value] record
157 ObjectLit () . map (prop *** ArrayLit ()) $
158 Map.toList (Map.filter (not . null) (Map.map tail fs))
161 map (first prop) (Map.toList (Map.map head fs))
163 Binop op leftExpr rightExpr ->
164 binop op leftExpr rightExpr
166 Function args body ->
167 generateFunction args body
172 aN = "A" ++ show arity
174 do jsFunc <- toJsExpr func
175 jsArgs <- mapM toJsExpr args
177 if 2 <= arity && arity <= 9 then
178 ref aN `call` (jsFunc:jsArgs)
180 foldl1 (<|) (jsFunc:jsArgs)
182 TailCall name argNames args ->
184 reassign name tempName =
185 ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
187 do args' <- mapM toJsExpr args
188 tempNames <- mapM (\_ -> Var.fresh) args
190 VarDeclStmt () (zipWith varDecl tempNames args')
191 : zipWith reassign argNames tempNames
192 ++ [ContinueStmt () (Just (Id () name))]
195 generateLet tcc defs body
197 If branches finally ->
198 generateIf tcc branches finally
200 Case expr decisionTree branches ->
201 generateCase tcc expr decisionTree branches
203 ExplicitList elements ->
204 do jsElements <- mapM toJsExpr elements
205 jsExpr $ BuiltIn.list jsElements
208 do jsMembers <- mapM toJsExpr members
209 jsExpr $ ObjectLit () (ctor : fields jsMembers)
211 ctor = (prop "ctor", string tag)
213 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
215 DataAccess dataExpr index ->
216 do jsDataExpr <- toJsExpr dataExpr
217 jsExpr $ DotRef () jsDataExpr (var ("_" ++ show index))
219 GLShader _uid src _tipe ->
220 jsExpr $ ObjectLit () [(PropString () "src", Literal.literal (Str src))]
224 Expr.In name portType ->
225 jsExpr (Port.inbound name portType)
227 Expr.Out name expr portType ->
228 do expr' <- toJsExpr expr
229 jsExpr (Port.outbound name expr' portType)
231 Expr.Task name expr portType ->
232 do expr' <- toJsExpr expr
233 jsExpr (Port.task name expr' portType)
241generateFunction :: [String] -> Opt.Expr -> State Int Code
242generateFunction args body =
243 do code <- exprToCode Nothing body
244 jsExpr (generateFunctionWithArity args code)
247generateTailFunction :: String -> [String] -> Opt.Expr -> State Int (Expression ())
248generateTailFunction name args body =
249 do code <- exprToCode Nothing body
250 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
253 (WhileStmt () (BoolLit () True) (toStatement code))
256generateFunctionWithArity :: [String] -> Code -> Expression ()
257generateFunctionWithArity args code =
261 if 2 <= arity && arity <= 9 then
263 fN = "F" ++ show arity
265 ref fN <| function args (toStatementList code)
268 (lastArg:otherArgs) = reverse args
269 innerBody = function [lastArg] (toStatementList code)
271 foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
281generateLet tcc defs body =
282 do stmts <- mapM generateDef defs
283 code <- exprToCode tcc body
284 jsBlock (stmts ++ toStatementList code)
289generateIf :: TailCallContext -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
290generateIf tcc givenBranches givenFinally =
292 (branches, finally) =
293 crushIfs givenBranches givenFinally
295 convertBranch (condition, expr) =
296 (,) <$> toJsExpr condition <*> exprToCode tcc expr
298 ifExpression (condition, branch) otherwise =
299 CondExpr () condition branch otherwise
301 ifStatement (condition, branch) otherwise =
302 IfStmt () condition branch otherwise
304 do jsBranches <- mapM convertBranch branches
305 jsFinally <- exprToCode tcc finally
307 if any isBlock (jsFinally : map snd jsBranches)
309 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
311 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
315 :: [(Opt.Expr, Opt.Expr)]
317 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
318crushIfs branches finally =
319 crushIfsHelp [] branches finally
323 :: [(Opt.Expr, Opt.Expr)]
324 -> [(Opt.Expr, Opt.Expr)]
326 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
327crushIfsHelp visitedBranches unvisitedBranches finally =
328 case unvisitedBranches of
331 If subBranches subFinally ->
332 crushIfsHelp visitedBranches subBranches subFinally
335 (reverse visitedBranches, finally)
337 (Literal (Boolean True), branch) : _ ->
338 crushIfsHelp visitedBranches [] branch
340 visiting : unvisited ->
341 crushIfsHelp (visiting : visitedBranches) unvisited finally
347generateDef :: Opt.Def -> State Int (Statement ())
349 do (home, name, jsBody) <-
351 Opt.TailDef (Opt.Facts home _) name argNames body ->
352 (,,) home name <$> generateTailFunction name argNames body
354 Opt.Def (Opt.Facts home _) name body ->
355 (,,) home name <$> toJsExpr body
357 return (VarDeclStmt () [ varDecl (Var.defName home name) jsBody ])
365 -> PM.DecisionTree Int
366 -> Map.Map Int Opt.Expr
368generateCase tcc expr decisionTree branches =
369 error "TODO" tcc expr decisionTree branches
380 | func == Var.Canonical Var.BuiltIn "::" =
381 toCode (Data "::" [left,right])
383 | func == forwardCompose =
384 compose (collectLeftAssoc forwardCompose left right)
386 | func == backwardCompose =
387 compose (collectRightAssoc backwardCompose left right)
389 | func == forwardApply =
390 pipe (collectLeftAssoc forwardApply left right)
392 | func == backwardApply =
393 pipe (collectRightAssoc backwardApply left right)
396 do jsLeft <- toJsExpr left
397 jsRight <- toJsExpr right
398 jsExpr (makeExpr func jsLeft jsRight)
401-- CONTROL FLOW OPERATOR HELPERS
403apply :: Expression () -> Expression () -> Expression ()
408compose :: [Opt.Expr] -> State Int Code
410 do jsFunctions <- mapM toJsExpr functions
411 jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
414pipe :: [Opt.Expr] -> State Int Code
416 do (value:functions) <- mapM toJsExpr expressions
417 jsExpr $ List.foldl' apply value functions
420forwardCompose :: Var.Canonical
425backwardCompose :: Var.Canonical
430forwardApply :: Var.Canonical
435backwardApply :: Var.Canonical
440inBasics :: String -> Var.Canonical
442 Var.inCore ["Basics"] name
445-- BINARY OPERATOR HELPERS
447makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
448makeExpr qualifiedOp@(Var.Canonical home op) =
450 simpleMake left right =
451 ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
453 if home == Var.Module (ModuleName.inCore ["Basics"]) then
454 Map.findWithDefault simpleMake op basicOps
459basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
461 Map.fromList (infixOps ++ specialOps)
464infixOps :: [(String, Expression () -> Expression () -> Expression ())]
468 (str, InfixExpr () op)
474 , infixOp "&&" OpLAnd
479specialOps :: [(String, Expression () -> Expression () -> Expression ())]
481 [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
482 , (,) "|>" $ flip (<|)
483 , (,) "==" $ \a b -> BuiltIn.eq a b
484 , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (BuiltIn.eq a b)
485 , (,) "<" $ cmp OpLT 0
486 , (,) ">" $ cmp OpGT 0
487 , (,) "<=" $ cmp OpLT 1
488 , (,) ">=" $ cmp OpGT (-1)
489 , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
493cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
495 InfixExpr () op (BuiltIn.compare a b) (IntLit () n)
498-- BINARY OPERATOR COLLECTORS
500-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
501collectRightAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
502collectRightAssoc desiredOp left right =
503 collectRightAssocHelp desiredOp [left] right
506collectRightAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
507collectRightAssocHelp desiredOp exprList expr =
509 Binop op left right | op == desiredOp ->
510 collectRightAssocHelp desiredOp (left : exprList) right
516-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
517collectLeftAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
518collectLeftAssoc desiredOp left right =
519 collectLeftAssocHelp desiredOp [right] left
522collectLeftAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
523collectLeftAssocHelp desiredOp exprList expr =
525 Binop op left right | op == desiredOp ->
526 collectLeftAssocHelp desiredOp (right : exprList) left
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