1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript.Expression
7import Prelude hiding (exp)
8import Control.Arrow (second)
9import Control.Monad (foldM)
10import qualified Data.List as List
11import qualified Data.Map as Map
12import Data.Monoid ((<>))
13import qualified Data.Text as Text
14import Data.Text (Text)
16import AST.Expression.Optimized as Opt
17import qualified AST.Literal as L
18import qualified AST.Module.Name as ModuleName
19import qualified AST.Variable as V
20import Generate.JavaScript.Helpers as Help
21import qualified Generate.JavaScript.Builder as JS
22import qualified Generate.JavaScript.BuiltIn as BuiltIn
23import qualified Generate.JavaScript.Literal as Literal
24import qualified Generate.JavaScript.Variable as Var
25import Generate.JavaScript.Variable (Generator)
26import qualified Optimize.DecisionTree as DT
30-- GENERATE DECLARATION
33generateDecl :: ModuleName.Canonical -> Text -> Opt.Def -> Generator JS.Stmt
34generateDecl home name def =
35 do jsName <- Var.globalName (V.Global home name)
36 jsBody <- generateDef name def
37 return $ JS.VarDeclStmt [ Help.varDecl jsName jsBody ]
49jsExpr :: JS.Expr -> Generator Code
54jsBlock :: [JS.Stmt] -> Generator Code
56 return (JsBlock stmts)
59isBlock :: Code -> Bool
66toStatementList :: Code -> [JS.Stmt]
70 [ JS.Return (Just expr) ]
76toStatement :: Code -> JS.Stmt
89toExpr :: Code -> JS.Expr
96 JS.Call (function [] stmts) []
103generateJsExpr :: Opt.Expr -> Generator JS.Expr
104generateJsExpr optExpr =
105 toExpr <$> generateCode optExpr
108generateCode :: Opt.Expr -> Generator Code
112 jsExpr $ Var.local name
115 JsExpr <$> Var.global name
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 generateBinop op leftExpr rightExpr
146 Function args body ->
147 generateFunction args body
150 generateCall func args
152 TailCall name argNames args ->
154 reassign argName tempName =
156 JS.Assign (JS.LVar (Var.safe argName)) (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 generateLetDef defs
167 code <- generateCode body
168 jsBlock (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 encoder ->
202 do jsEncoder <- generateJsExpr encoder
203 JsExpr <$> BuiltIn.outgoingPort name jsEncoder
205 IncomingPort name decoder ->
206 do jsDecoder <- generateJsExpr decoder
207 JsExpr <$> BuiltIn.incomingPort name jsDecoder
210 generateProgram kind body
213 do string <- Literal.literal (L.Str src)
214 jsExpr $ JS.Object [(JS.StringProp "src", string)]
216 Crash home region maybeBranchProblem ->
217 do maybeOptBranchProblem <- traverse generateJsExpr maybeBranchProblem
218 JsExpr <$> BuiltIn.crash home region maybeOptBranchProblem
225generateProgram :: Opt.Main -> Opt.Expr -> Generator Code
226generateProgram kind body =
229 do html <- generateJsExpr body
230 func <- BuiltIn.staticProgram
231 jsExpr (func <| html)
234 do almostProgram <- generateJsExpr body
235 jsExpr (JS.Call almostProgram [])
238 do almostProgram <- generateJsExpr body
239 flagDecoder <- generateJsExpr decoder
240 jsExpr (almostProgram <| flagDecoder)
247generateLetDef :: (Text, Opt.Def) -> Generator JS.Stmt
248generateLetDef (name, def) =
249 do jsBody <- generateDef name def
250 return $ JS.VarDeclStmt [ Help.varDecl name jsBody ]
253generateDef :: Text -> Opt.Def -> Generator JS.Expr
254generateDef name def =
256 Opt.TailDef argNames body ->
257 generateTailFunction name argNames body
263generateTailFunction :: Text -> [Text] -> Opt.Expr -> Generator JS.Expr
264generateTailFunction name args body =
265 do code <- generateCode body
266 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
268 (JS.Id (Var.safe name))
269 (JS.While (JS.Bool True) (toStatement code))
276generateFunction :: [Text] -> Opt.Expr -> Generator Code
277generateFunction args body =
278 do code <- generateCode body
279 jsExpr (generateFunctionWithArity args code)
282generateFunctionWithArity :: [Text] -> Code -> JS.Expr
283generateFunctionWithArity rawArgs code =
285 args = map Var.safe rawArgs
288 if 2 <= arity && arity <= 9 then
290 fN = "F" <> Text.pack (show arity)
292 ref fN <| function args (toStatementList code)
295 (lastArg:otherArgs) = reverse args
296 innerBody = function [lastArg] (toStatementList code)
298 foldl (\body arg -> function [arg] [JS.Return (Just body)]) innerBody otherArgs
305generateCall :: Opt.Expr -> [Opt.Expr] -> Generator Code
306generateCall func args =
308 (Opt.VarGlobal (V.Global home name), [arg]) ->
310 "complement" | home == bitwise -> genPrefix JS.PrefixBNot arg
311 "not" | home == basics -> genPrefix JS.PrefixLNot arg
312 _ -> generateCallHelp func args
314 (Opt.VarGlobal (V.Global home name), [ left, right ]) | home == bitwise ->
316 "and" -> genInfix JS.OpBAnd left right
317 "or" -> genInfix JS.OpBOr left right
318 "xor" -> genInfix JS.OpBXor left right
319 "shiftLeftBy" -> genInfix JS.OpLShift right left
320 "shiftRightBy" -> genInfix JS.OpSpRShift right left
321 "shiftRightZfBy" -> genInfix JS.OpZfRShift right left
322 _ -> generateCallHelp func args
325 generateCallHelp func args
328genPrefix :: JS.PrefixOp -> Opt.Expr -> Generator Code
330 do jsArg <- generateJsExpr arg
331 jsExpr (JS.Prefix op jsArg)
334genInfix :: JS.InfixOp -> Opt.Expr -> Opt.Expr -> Generator Code
335genInfix op left right =
336 do jsLeft <- generateJsExpr left
337 jsRight <- generateJsExpr right
338 jsExpr $ JS.Infix op jsLeft jsRight
341basics :: ModuleName.Canonical
343 ModuleName.inCore "Basics"
346bitwise :: ModuleName.Canonical
348 ModuleName.inCore "Bitwise"
351generateCallHelp :: Opt.Expr -> [Opt.Expr] -> Generator Code
352generateCallHelp func args =
355 aN = "A" <> Text.pack (show arity)
357 do jsFunc <- generateJsExpr func
358 jsArgs <- mapM generateJsExpr args
360 if 2 <= arity && arity <= 9 then
361 JS.Call (ref aN) (jsFunc:jsArgs)
363 foldl1 (<|) (jsFunc:jsArgs)
370generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> Generator Code
371generateIf givenBranches givenFinally =
373 (branches, finally) =
374 crushIfs givenBranches givenFinally
376 convertBranch (condition, expr) =
377 (,) <$> generateJsExpr condition <*> generateCode expr
379 ifExpression (condition, branch) final =
380 JS.If condition branch final
382 ifStatement (condition, branch) final =
383 JS.IfStmt condition branch final
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)] -> Generator [JS.Stmt]
430generateCase exprName decider jumps =
431 do labelRoot <- Var.fresh
432 jsDecider <- generateDecider exprName labelRoot decider
433 foldM (goto labelRoot) jsDecider jumps
440goto :: Text -> [JS.Stmt] -> (Int, Opt.Expr) -> Generator [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 -> Generator [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 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) -> Generator JS.Case
490edgeToCase exprName labelRoot (test, subTree) =
493 <*> generateDecider exprName labelRoot subTree
496testToExpr :: DT.Test -> Generator JS.Expr
499 DT.Constructor (V.Canonical _ tag) ->
500 return $ JS.String tag
502 DT.Literal (L.Chr char) ->
503 return $ JS.String char
513pathToTestableExpr :: Text -> DT.Path -> DT.Test -> Generator JS.Expr
514pathToTestableExpr root path exampleTest =
515 do accessExpr <- generateJsExpr (pathToExpr root path)
518 return $ JS.DotRef accessExpr (JS.Id "ctor")
520 DT.Literal (L.Chr _) ->
521 return $ JS.Call (JS.DotRef accessExpr (JS.Id "valueOf")) []
527pathToExpr :: Text -> DT.Path -> Opt.Expr
528pathToExpr root fullPath =
529 go (Opt.VarLocal root) fullPath
533 DT.Position index subpath ->
534 go (Opt.CtorAccess expr index) subpath
536 DT.Field field subpath ->
537 go (Opt.Access expr field) subpath
550generateBinop :: V.Global -> Opt.Expr -> Opt.Expr -> Generator Code
551generateBinop op left right =
552 do jsLeft <- generateJsExpr left
553 jsRight <- generateJsExpr right
554 JsExpr <$> generateBinopHelp op jsLeft jsRight
558-- BINARY OPERATOR HELPERS
561generateBinopHelp :: V.Global -> JS.Expr -> JS.Expr -> Generator JS.Expr
562generateBinopHelp var@(V.Global home op) jsLeft jsRight =
564 simpleMake left right =
565 do func <- Var.global var
566 return $ JS.Call (ref "A2") [ func, left, right ]
568 if home == basics then
569 (Map.findWithDefault simpleMake op basicsOps) jsLeft jsRight
571 else if op == "::" && home == list then
572 BuiltIn.cons jsLeft jsRight
575 simpleMake jsLeft jsRight
578list :: ModuleName.Canonical
580 ModuleName.inCore "List"
583basicsOps :: Map.Map Text (JS.Expr -> JS.Expr -> Generator JS.Expr)
585 Map.fromList (basicsOpsNormal ++ basicsOpsSpecial)
588basicsOpsNormal :: [(Text, JS.Expr -> JS.Expr -> Generator JS.Expr)]
592 ( str, \left right -> return (JS.Infix op left right) )
603basicsOpsSpecial :: [(Text, JS.Expr -> JS.Expr -> Generator JS.Expr)]
608 [ "^" =*> \a b -> return $ JS.Call (obj ["Math","pow"]) [a,b]
609 , "==" =*> BuiltIn.eq
610 , "/=" =*> \a b -> JS.Prefix JS.PrefixLNot <$> BuiltIn.eq a b
611 , "<" =*> cmp JS.OpLT 0
612 , ">" =*> cmp JS.OpGT 0
613 , "<=" =*> cmp JS.OpLT 1
614 , ">=" =*> cmp JS.OpGT (-1)
615 , "//" =*> \a b -> return $ JS.Infix JS.OpBOr (JS.Infix JS.OpDiv a b) (JS.Int 0)
619cmp :: JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> Generator JS.Expr
621 do ordering <- BuiltIn.cmp a b
622 return $ JS.Infix op ordering (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