1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript.Expression (generateDef) where
4import Control.Arrow (second)
5import Control.Monad.State (State, foldM)
6import qualified Data.List as List
7import qualified Data.Map as Map
8import Data.Monoid ((<>))
9import qualified Data.Text as Text
10import Data.Text (Text)
12import qualified AST.Expression.Canonical as Can
13import AST.Expression.Optimized as Opt
14import qualified AST.Literal as L
15import qualified AST.Module.Name as ModuleName
16import qualified AST.Variable as Var
17import Generate.JavaScript.Helpers as Help
18import qualified Generate.JavaScript.Builder as JS
19import qualified Generate.JavaScript.BuiltIn as BuiltIn
20import qualified Generate.JavaScript.Foreign as Foreign
21import qualified Generate.JavaScript.Literal as Literal
22import qualified Generate.JavaScript.Variable as Var
23import qualified Optimize.DecisionTree as DT
35jsExpr :: JS.Expr -> State Int Code
40jsBlock :: [JS.Stmt] -> State Int Code
45isBlock :: Code -> Bool
52toStatementList :: Code -> [JS.Stmt]
56 [ JS.Return (Just expr) ]
62toStatement :: Code -> JS.Stmt
75toExpr :: Code -> JS.Expr
82 JS.Call (function [] stmts) []
89generateDef :: Opt.Def -> State Int [JS.Stmt]
91 do (home, name, jsBody) <-
93 Opt.TailDef (Opt.Facts home) name argNames body ->
94 (,,) home name <$> generateTailFunction name argNames body
96 Opt.Def (Opt.Facts home) name body ->
97 (,,) home name <$> generateJsExpr body
99 return (Var.define home name jsBody)
106generateJsExpr :: Opt.Expr -> State Int JS.Expr
107generateJsExpr optExpr =
108 toExpr <$> generateCode optExpr
111generateCode :: Opt.Expr -> State Int Code
115 jsExpr $ Var.canonical var
118 jsExpr (Literal.literal lit)
120 Access record field ->
121 do jsRecord <- generateJsExpr record
122 jsExpr $ JS.DotRef jsRecord (JS.Id (Var.safe field))
124 Update record fields ->
126 fieldToJs (field, value) =
127 do jsValue <- generateJsExpr value
128 return (Var.safe field, jsValue)
130 do jsRecord <- generateJsExpr record
131 jsFields <- mapM fieldToJs fields
132 jsExpr $ BuiltIn.recordUpdate jsRecord jsFields
136 toField (field, value) =
137 do jsValue <- generateJsExpr value
138 return (Var.safe field ==> jsValue)
140 do jsFields <- mapM toField fields
141 jsExpr $ JS.Object jsFields
143 Binop op leftExpr rightExpr ->
144 binop op leftExpr rightExpr
146 Function args body ->
147 generateFunction args body
150 generateCall func args
152 TailCall name argNames args ->
154 reassign name tempName =
156 JS.Assign JS.OpAssign (JS.LVar (Var.safe name)) (ref tempName)
158 do args' <- mapM generateJsExpr args
159 tempNames <- mapM (\_ -> Var.fresh) args
161 JS.VarDeclStmt (zipWith varDecl tempNames args')
162 : zipWith reassign argNames tempNames
163 ++ [JS.Continue (Just (JS.Id (Var.safe name)))]
166 do stmts <- mapM generateDef defs
167 code <- generateCode body
168 jsBlock (concat stmts ++ toStatementList code)
170 If branches finally ->
171 generateIf branches finally
173 Case exprName decider jumps ->
174 JsBlock <$> generateCase exprName decider jumps
177 do jsElements <- mapM generateJsExpr elements
178 jsExpr $ BuiltIn.list jsElements
183 "ctor" ==> JS.String tag
186 ("_" <> Text.pack (show n)) ==> entry
188 do jsMembers <- mapM generateJsExpr members
189 jsExpr $ JS.Object (ctor : zipWith toEntry jsMembers [ 0 :: Int .. ])
191 CtorAccess dataExpr index ->
192 do jsDataExpr <- generateJsExpr dataExpr
193 jsExpr $ JS.DotRef jsDataExpr (JS.Id ("_" <> Text.pack (show index)))
196 jsExpr $ BuiltIn.effect moduleName
199 jsExpr $ BuiltIn.effect moduleName
201 OutgoingPort name tipe ->
202 jsExpr $ BuiltIn.outgoingPort name (Foreign.encode tipe)
204 IncomingPort name tipe ->
205 do jsDecoder <- generateJsExpr (Foreign.decode tipe)
206 jsExpr $ BuiltIn.incomingPort name jsDecoder
209 generateProgram kind body
211 GLShader _uid src _tipe ->
212 jsExpr $ JS.Object [(JS.StringProp "src", Literal.literal (L.Str src))]
214 Crash home region maybeBranchProblem ->
215 do maybeOptBranchProblem <- traverse generateJsExpr maybeBranchProblem
216 jsExpr $ BuiltIn.crash home region maybeOptBranchProblem
223generateProgram :: Can.Main -> Opt.Expr -> State Int Code
224generateProgram kind body =
227 do html <- generateJsExpr body
228 jsExpr (Var.staticProgram <| html)
231 do almostProgram <- generateJsExpr body
232 jsExpr (JS.Call almostProgram [])
235 do almostProgram <- generateJsExpr body
236 flagDecoder <- generateJsExpr (Foreign.decode tipe)
237 jsExpr (almostProgram <| flagDecoder)
244generateFunction :: [Text] -> Opt.Expr -> State Int Code
245generateFunction args body =
246 do code <- generateCode body
247 jsExpr (generateFunctionWithArity args code)
250generateTailFunction :: Text -> [Text] -> Opt.Expr -> State Int JS.Expr
251generateTailFunction name args body =
252 do code <- generateCode body
253 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
255 (JS.Id (Var.safe name))
256 (JS.While (JS.Bool True) (toStatement code))
259generateFunctionWithArity :: [Text] -> Code -> JS.Expr
260generateFunctionWithArity rawArgs code =
262 args = map Var.safe rawArgs
265 if 2 <= arity && arity <= 9 then
267 fN = "F" <> Text.pack (show arity)
269 ref fN <| function args (toStatementList code)
272 (lastArg:otherArgs) = reverse args
273 innerBody = function [lastArg] (toStatementList code)
275 foldl (\body arg -> function [arg] [JS.Return (Just body)]) innerBody otherArgs
282generateCall :: Opt.Expr -> [Opt.Expr] -> State Int Code
283generateCall func args =
285 (Opt.Var var, [arg]) ->
286 case getUnaryOp var of
288 do jsArg <- generateJsExpr arg
289 jsExpr $ JS.Prefix op jsArg
292 generateCallHelp func args
294 (Opt.Var var, [ arg1, arg2 ]) ->
295 case getBinaryOp var arg1 arg2 of
296 Just (op, left, right) ->
297 do jsLeft <- generateJsExpr left
298 jsRight <- generateJsExpr right
299 jsExpr $ JS.Infix op jsLeft jsRight
302 generateCallHelp func args
305 generateCallHelp func args
308generateCallHelp :: Opt.Expr -> [Opt.Expr] -> State Int Code
309generateCallHelp func args =
312 aN = "A" <> Text.pack (show arity)
314 do jsFunc <- generateJsExpr func
315 jsArgs <- mapM generateJsExpr args
317 if 2 <= arity && arity <= 9 then
318 JS.Call (ref aN) (jsFunc:jsArgs)
320 foldl1 (<|) (jsFunc:jsArgs)
323getUnaryOp :: Var.Canonical -> Maybe JS.PrefixOp
325 if var == bitwiseComplement then
328 else if var == basicsNot then
335getBinaryOp :: Var.Canonical -> Opt.Expr -> Opt.Expr -> Maybe (JS.InfixOp, Opt.Expr, Opt.Expr)
336getBinaryOp (Var.Canonical home name) left right =
337 if home /= bitwise then
342 "and" -> Just ( JS.OpBAnd, left, right )
343 "or" -> Just ( JS.OpBOr, left, right )
344 "xor" -> Just ( JS.OpBXor, left, right )
345 "shiftLeftBy" -> Just ( JS.OpLShift, right, left )
346 "shiftRightBy" -> Just ( JS.OpSpRShift, right, left )
347 "shiftRightZfBy" -> Just ( JS.OpZfRShift, right, left )
351bitwiseComplement :: Var.Canonical
353 Var.inCore "Bitwise" "complement"
356basicsNot :: Var.Canonical
358 Var.inCore "Basics" "not"
363 Var.Module (ModuleName.inCore "Bitwise")
370generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
371generateIf givenBranches givenFinally =
373 (branches, finally) =
374 crushIfs givenBranches givenFinally
376 convertBranch (condition, expr) =
377 (,) <$> generateJsExpr condition <*> generateCode expr
379 ifExpression (condition, branch) otherwise =
380 JS.If condition branch otherwise
382 ifStatement (condition, branch) otherwise =
383 JS.IfStmt condition branch otherwise
385 do jsBranches <- mapM convertBranch branches
386 jsFinally <- generateCode finally
388 if any isBlock (jsFinally : map snd jsBranches)
390 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
392 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
396 :: [(Opt.Expr, Opt.Expr)]
398 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
399crushIfs branches finally =
400 crushIfsHelp [] branches finally
404 :: [(Opt.Expr, Opt.Expr)]
405 -> [(Opt.Expr, Opt.Expr)]
407 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
408crushIfsHelp visitedBranches unvisitedBranches finally =
409 case unvisitedBranches of
412 If subBranches subFinally ->
413 crushIfsHelp visitedBranches subBranches subFinally
416 (reverse visitedBranches, finally)
418 (Literal (L.Boolean True), branch) : _ ->
419 crushIfsHelp visitedBranches [] branch
421 visiting : unvisited ->
422 crushIfsHelp (visiting : visitedBranches) unvisited finally
429generateCase :: Text -> Opt.Decider Opt.Choice -> [(Int, Opt.Expr)] -> State Int [JS.Stmt]
430generateCase exprName decider jumps =
431 do labelRoot <- Var.fresh
432 decider <- generateDecider exprName labelRoot decider
433 foldM (goto labelRoot) decider jumps
440goto :: Text -> [JS.Stmt] -> (Int, Opt.Expr) -> State Int [JS.Stmt]
441goto labelRoot deciderStmts (target, branch) =
445 (toLabel labelRoot target)
446 (JS.DoWhile (JS.Block deciderStmts) (JS.Bool False))
448 do code <- generateCode branch
449 return (labeledDeciderStmt : toStatementList code)
452toLabel :: Text -> Int -> JS.Id
454 JS.Id (root <> "_" <> Text.pack (show target))
458-- turn deciders into ifs and switches
461generateDecider :: Text -> Text -> Opt.Decider Opt.Choice -> State Int [JS.Stmt]
462generateDecider exprName labelRoot decisionTree =
464 Opt.Leaf (Opt.Inline branch) ->
465 toStatementList <$> generateCode branch
467 Opt.Leaf (Opt.Jump target) ->
468 return [ JS.Break (Just (toLabel labelRoot target)) ]
470 Opt.Chain testChain success failure ->
472 makeTest (path, test) =
473 do testExpr <- pathToTestableExpr exprName path test
474 return (JS.Infix JS.OpStrictEq testExpr (testToExpr test))
476 do testExprs <- mapM makeTest testChain
477 let cond = List.foldl1' (JS.Infix JS.OpLAnd) testExprs
478 thenBranch <- generateDecider exprName labelRoot success
479 elseBranch <- generateDecider exprName labelRoot failure
480 return [ JS.IfStmt cond (JS.Block thenBranch) (JS.Block elseBranch) ]
482 Opt.FanOut path edges fallback ->
483 do testExpr <- pathToTestableExpr exprName path (fst (head edges))
484 caseClauses <- mapM (edgeToCase exprName labelRoot) edges
485 caseDefault <- JS.Default <$> generateDecider exprName labelRoot fallback
486 return [ JS.Switch testExpr (caseClauses ++ [caseDefault]) ]
489edgeToCase :: Text -> Text -> (DT.Test, Opt.Decider Opt.Choice) -> State Int JS.Case
490edgeToCase exprName labelRoot (test, subTree) =
491 JS.Case (testToExpr test) <$> generateDecider exprName labelRoot subTree
494testToExpr :: DT.Test -> JS.Expr
497 DT.Constructor (Var.Canonical _ tag) ->
500 DT.Literal (L.Chr char) ->
511pathToTestableExpr :: Text -> DT.Path -> DT.Test -> State Int JS.Expr
512pathToTestableExpr root path exampleTest =
513 do accessExpr <- generateJsExpr (pathToExpr root path)
516 return $ JS.DotRef accessExpr (JS.Id "ctor")
518 DT.Literal (L.Chr _) ->
519 return $ JS.Call (JS.DotRef accessExpr (JS.Id "valueOf")) []
525pathToExpr :: Text -> DT.Path -> Opt.Expr
526pathToExpr root fullPath =
527 go (Opt.Var (Var.local root)) fullPath
531 DT.Position index subpath ->
532 go (Opt.CtorAccess expr index) subpath
534 DT.Field field subpath ->
535 go (Opt.Access expr field) subpath
553binop func left right =
554 do jsLeft <- generateJsExpr left
555 jsRight <- generateJsExpr right
556 jsExpr (binopHelp func jsLeft jsRight)
560-- BINARY OPERATOR HELPERS
563binopHelp :: Var.Canonical -> JS.Expr -> JS.Expr -> JS.Expr
564binopHelp qualifiedOp@(Var.Canonical home op) leftExpr rightExpr =
566 simpleMake left right =
567 JS.Call (ref "A2") [ Var.canonical qualifiedOp, left, right ]
569 if home == basicsModule then
570 (Map.findWithDefault simpleMake op basicOps) leftExpr rightExpr
572 else if op == "::" && (home == listModule || home == listModuleInternals) then
573 BuiltIn.cons leftExpr rightExpr
576 simpleMake leftExpr rightExpr
579listModule :: Var.Home
581 Var.Module (ModuleName.inCore "List")
584listModuleInternals :: Var.Home
586 Var.TopLevel (ModuleName.inCore "List")
589basicsModule :: Var.Home
591 Var.Module (ModuleName.inCore "Basics")
594basicOps :: Map.Map Text (JS.Expr -> JS.Expr -> JS.Expr)
596 Map.fromList (infixOps ++ specialOps)
599infixOps :: [(Text, JS.Expr -> JS.Expr -> JS.Expr)]
605 [ infixOp "+" JS.OpAdd
606 , infixOp "-" JS.OpSub
607 , infixOp "*" JS.OpMul
608 , infixOp "/" JS.OpDiv
609 , infixOp "&&" JS.OpLAnd
610 , infixOp "||" JS.OpLOr
614specialOps :: [(Text, JS.Expr -> JS.Expr -> JS.Expr)]
616 [ (,) "^" $ \a b -> JS.Call (obj ["Math","pow"]) [a,b]
617 , (,) "==" $ \a b -> BuiltIn.eq a b
618 , (,) "/=" $ \a b -> JS.Prefix JS.PrefixLNot (BuiltIn.eq a b)
619 , (,) "<" $ cmp JS.OpLT 0
620 , (,) ">" $ cmp JS.OpGT 0
621 , (,) "<=" $ cmp JS.OpLT 1
622 , (,) ">=" $ cmp JS.OpGT (-1)
623 , (,) "//" $ \a b -> JS.Infix JS.OpBOr (JS.Infix JS.OpDiv a b) (JS.Int 0)
627cmp :: JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> JS.Expr
629 JS.Infix op (BuiltIn.cmp a b) (JS.Int 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