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 qualified AST.Expression.Canonical as Can
17import AST.Expression.Optimized as Opt
18import qualified AST.Literal as L
19import qualified AST.Module.Name as ModuleName
20import qualified AST.Variable as V
21import Generate.JavaScript.Helpers as Help
22import qualified Generate.JavaScript.Builder as JS
23import qualified Generate.JavaScript.BuiltIn as BuiltIn
24import qualified Generate.JavaScript.Foreign as Foreign
25import qualified Generate.JavaScript.Literal as Literal
26import qualified Generate.JavaScript.Variable as Var
27import Generate.JavaScript.Variable (Generator)
28import qualified Optimize.DecisionTree as DT
32-- GENERATE DECLARATION
35generateDecl :: ModuleName.Canonical -> Text -> Opt.Def -> Generator JS.Stmt
36generateDecl home name def =
37 do jsName <- Var.getGlobalName (V.Global home name)
38 jsBody <- generateDef name def
39 return $ JS.VarDeclStmt [ Help.varDecl jsName jsBody ]
51jsExpr :: JS.Expr -> Generator Code
56jsBlock :: [JS.Stmt] -> Generator Code
58 return (JsBlock stmts)
61isBlock :: Code -> Bool
68toStatementList :: Code -> [JS.Stmt]
72 [ JS.Return (Just expr) ]
78toStatement :: Code -> JS.Stmt
91toExpr :: Code -> JS.Expr
98 JS.Call (function [] stmts) []
105generateJsExpr :: Opt.Expr -> Generator JS.Expr
106generateJsExpr optExpr =
107 toExpr <$> generateCode optExpr
110generateCode :: Opt.Expr -> Generator Code
114 jsExpr $ Var.local name
117 JsExpr <$> Var.global name
120 JsExpr <$> Literal.literal lit
122 Access record field ->
123 do jsRecord <- generateJsExpr record
124 jsExpr $ JS.DotRef jsRecord (JS.Id (Var.safe field))
126 Update record fields ->
128 fieldToJs (field, value) =
129 do jsValue <- generateJsExpr value
130 return (Var.safe field, jsValue)
132 do jsRecord <- generateJsExpr record
133 jsFields <- mapM fieldToJs fields
134 JsExpr <$> BuiltIn.recordUpdate jsRecord jsFields
138 toField (field, value) =
139 do jsValue <- generateJsExpr value
140 return (Var.safe field ==> jsValue)
142 do jsFields <- mapM toField fields
143 jsExpr $ JS.Object jsFields
145 Binop op leftExpr rightExpr ->
146 generateBinop op leftExpr rightExpr
148 Function args body ->
149 generateFunction args body
152 generateCall func args
154 TailCall name argNames args ->
156 reassign argName tempName =
158 JS.Assign (JS.LVar (Var.safe argName)) (ref tempName)
160 do args' <- mapM generateJsExpr args
161 tempNames <- mapM (\_ -> Var.fresh) args
163 JS.VarDeclStmt (zipWith varDecl tempNames args')
164 : zipWith reassign argNames tempNames
165 ++ [JS.Continue (Just (JS.Id (Var.safe name)))]
168 do stmts <- mapM generateLetDef defs
169 code <- generateCode body
170 jsBlock (stmts ++ toStatementList code)
172 If branches finally ->
173 generateIf branches finally
175 Case exprName decider jumps ->
176 JsBlock <$> generateCase exprName decider jumps
179 do jsElements <- mapM generateJsExpr elements
180 JsExpr <$> BuiltIn.list jsElements
185 "ctor" ==> JS.String tag
188 ("_" <> Text.pack (show n)) ==> entry
190 do jsMembers <- mapM generateJsExpr members
191 jsExpr $ JS.Object (ctor : zipWith toEntry jsMembers [ 0 :: Int .. ])
193 CtorAccess dataExpr index ->
194 do jsDataExpr <- generateJsExpr dataExpr
195 jsExpr $ JS.DotRef jsDataExpr (JS.Id ("_" <> Text.pack (show index)))
198 JsExpr <$> BuiltIn.effect moduleName
201 JsExpr <$> BuiltIn.effect moduleName
203 OutgoingPort name tipe ->
204 do encoder <- Foreign.encode tipe
205 JsExpr <$> BuiltIn.outgoingPort name encoder
207 IncomingPort name tipe ->
208 do jsDecoder <- generateJsExpr (Foreign.decode tipe)
209 JsExpr <$> BuiltIn.incomingPort name jsDecoder
212 generateProgram kind body
215 do string <- Literal.literal (L.Str src)
216 jsExpr $ JS.Object [(JS.StringProp "src", string)]
218 Crash home region maybeBranchProblem ->
219 do maybeOptBranchProblem <- traverse generateJsExpr maybeBranchProblem
220 JsExpr <$> BuiltIn.crash home region maybeOptBranchProblem
227generateProgram :: Can.Main -> Opt.Expr -> Generator Code
228generateProgram kind body =
231 do html <- generateJsExpr body
232 func <- BuiltIn.staticProgram
233 jsExpr (func <| html)
236 do almostProgram <- generateJsExpr body
237 jsExpr (JS.Call almostProgram [])
240 do almostProgram <- generateJsExpr body
241 flagDecoder <- generateJsExpr (Foreign.decode tipe)
242 jsExpr (almostProgram <| flagDecoder)
249generateLetDef :: (Text, Opt.Def) -> Generator JS.Stmt
250generateLetDef (name, def) =
251 do jsBody <- generateDef name def
252 return $ JS.VarDeclStmt [ Help.varDecl name jsBody ]
255generateDef :: Text -> Opt.Def -> Generator JS.Expr
256generateDef name def =
258 Opt.TailDef argNames body ->
259 generateTailFunction name argNames body
265generateTailFunction :: Text -> [Text] -> Opt.Expr -> Generator JS.Expr
266generateTailFunction name args body =
267 do code <- generateCode body
268 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
270 (JS.Id (Var.safe name))
271 (JS.While (JS.Bool True) (toStatement code))
278generateFunction :: [Text] -> Opt.Expr -> Generator Code
279generateFunction args body =
280 do code <- generateCode body
281 jsExpr (generateFunctionWithArity args code)
284generateFunctionWithArity :: [Text] -> Code -> JS.Expr
285generateFunctionWithArity rawArgs code =
287 args = map Var.safe rawArgs
290 if 2 <= arity && arity <= 9 then
292 fN = "F" <> Text.pack (show arity)
294 ref fN <| function args (toStatementList code)
297 (lastArg:otherArgs) = reverse args
298 innerBody = function [lastArg] (toStatementList code)
300 foldl (\body arg -> function [arg] [JS.Return (Just body)]) innerBody otherArgs
307generateCall :: Opt.Expr -> [Opt.Expr] -> Generator Code
308generateCall func args =
310 (Opt.VarGlobal (V.Global home name), [arg]) ->
312 "complement" | home == bitwise -> genPrefix JS.PrefixBNot arg
313 "not" | home == basics -> genPrefix JS.PrefixLNot arg
314 _ -> generateCallHelp func args
316 (Opt.VarGlobal (V.Global home name), [ left, right ]) | home == bitwise ->
318 "and" -> genInfix JS.OpBAnd left right
319 "or" -> genInfix JS.OpBOr left right
320 "xor" -> genInfix JS.OpBXor left right
321 "shiftLeftBy" -> genInfix JS.OpLShift right left
322 "shiftRightBy" -> genInfix JS.OpSpRShift right left
323 "shiftRightZfBy" -> genInfix JS.OpZfRShift right left
324 _ -> generateCallHelp func args
327 generateCallHelp func args
330genPrefix :: JS.PrefixOp -> Opt.Expr -> Generator Code
332 do jsArg <- generateJsExpr arg
333 jsExpr (JS.Prefix op jsArg)
336genInfix :: JS.InfixOp -> Opt.Expr -> Opt.Expr -> Generator Code
337genInfix op left right =
338 do jsLeft <- generateJsExpr left
339 jsRight <- generateJsExpr right
340 jsExpr $ JS.Infix op jsLeft jsRight
343basics :: ModuleName.Canonical
345 ModuleName.inCore "Basics"
348bitwise :: ModuleName.Canonical
350 ModuleName.inCore "Bitwise"
353generateCallHelp :: Opt.Expr -> [Opt.Expr] -> Generator Code
354generateCallHelp func args =
357 aN = "A" <> Text.pack (show arity)
359 do jsFunc <- generateJsExpr func
360 jsArgs <- mapM generateJsExpr args
362 if 2 <= arity && arity <= 9 then
363 JS.Call (ref aN) (jsFunc:jsArgs)
365 foldl1 (<|) (jsFunc:jsArgs)
372generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> Generator Code
373generateIf givenBranches givenFinally =
375 (branches, finally) =
376 crushIfs givenBranches givenFinally
378 convertBranch (condition, expr) =
379 (,) <$> generateJsExpr condition <*> generateCode expr
381 ifExpression (condition, branch) final =
382 JS.If condition branch final
384 ifStatement (condition, branch) final =
385 JS.IfStmt condition branch final
387 do jsBranches <- mapM convertBranch branches
388 jsFinally <- generateCode finally
390 if any isBlock (jsFinally : map snd jsBranches)
392 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
394 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
398 :: [(Opt.Expr, Opt.Expr)]
400 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
401crushIfs branches finally =
402 crushIfsHelp [] branches finally
406 :: [(Opt.Expr, Opt.Expr)]
407 -> [(Opt.Expr, Opt.Expr)]
409 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
410crushIfsHelp visitedBranches unvisitedBranches finally =
411 case unvisitedBranches of
414 If subBranches subFinally ->
415 crushIfsHelp visitedBranches subBranches subFinally
418 (reverse visitedBranches, finally)
420 (Literal (L.Boolean True), branch) : _ ->
421 crushIfsHelp visitedBranches [] branch
423 visiting : unvisited ->
424 crushIfsHelp (visiting : visitedBranches) unvisited finally
431generateCase :: Text -> Opt.Decider Opt.Choice -> [(Int, Opt.Expr)] -> Generator [JS.Stmt]
432generateCase exprName decider jumps =
433 do labelRoot <- Var.fresh
434 jsDecider <- generateDecider exprName labelRoot decider
435 foldM (goto labelRoot) jsDecider jumps
442goto :: Text -> [JS.Stmt] -> (Int, Opt.Expr) -> Generator [JS.Stmt]
443goto labelRoot deciderStmts (target, branch) =
447 (toLabel labelRoot target)
448 (JS.DoWhile (JS.Block deciderStmts) (JS.Bool False))
450 do code <- generateCode branch
451 return (labeledDeciderStmt : toStatementList code)
454toLabel :: Text -> Int -> JS.Id
456 JS.Id (root <> "_" <> Text.pack (show target))
460-- turn deciders into ifs and switches
463generateDecider :: Text -> Text -> Opt.Decider Opt.Choice -> Generator [JS.Stmt]
464generateDecider exprName labelRoot decisionTree =
466 Opt.Leaf (Opt.Inline branch) ->
467 toStatementList <$> generateCode branch
469 Opt.Leaf (Opt.Jump target) ->
470 return [ JS.Break (Just (toLabel labelRoot target)) ]
472 Opt.Chain testChain success failure ->
474 makeTest (path, test) =
475 do testExpr <- pathToTestableExpr exprName path test
476 JS.Infix JS.OpStrictEq testExpr <$> testToExpr test
478 do testExprs <- mapM makeTest testChain
479 let cond = List.foldl1' (JS.Infix JS.OpLAnd) testExprs
480 thenBranch <- generateDecider exprName labelRoot success
481 elseBranch <- generateDecider exprName labelRoot failure
482 return [ JS.IfStmt cond (JS.Block thenBranch) (JS.Block elseBranch) ]
484 Opt.FanOut path edges fallback ->
485 do testExpr <- pathToTestableExpr exprName path (fst (head edges))
486 caseClauses <- mapM (edgeToCase exprName labelRoot) edges
487 caseDefault <- JS.Default <$> generateDecider exprName labelRoot fallback
488 return [ JS.Switch testExpr (caseClauses ++ [caseDefault]) ]
491edgeToCase :: Text -> Text -> (DT.Test, Opt.Decider Opt.Choice) -> Generator JS.Case
492edgeToCase exprName labelRoot (test, subTree) =
495 <*> generateDecider exprName labelRoot subTree
498testToExpr :: DT.Test -> Generator JS.Expr
501 DT.Constructor (V.Canonical _ tag) ->
502 return $ JS.String tag
504 DT.Literal (L.Chr char) ->
505 return $ JS.String char
515pathToTestableExpr :: Text -> DT.Path -> DT.Test -> Generator JS.Expr
516pathToTestableExpr root path exampleTest =
517 do accessExpr <- generateJsExpr (pathToExpr root path)
520 return $ JS.DotRef accessExpr (JS.Id "ctor")
522 DT.Literal (L.Chr _) ->
523 return $ JS.Call (JS.DotRef accessExpr (JS.Id "valueOf")) []
529pathToExpr :: Text -> DT.Path -> Opt.Expr
530pathToExpr root fullPath =
531 go (Opt.VarLocal root) fullPath
535 DT.Position index subpath ->
536 go (Opt.CtorAccess expr index) subpath
538 DT.Field field subpath ->
539 go (Opt.Access expr field) subpath
552generateBinop :: V.Global -> Opt.Expr -> Opt.Expr -> Generator Code
553generateBinop op left right =
554 do jsLeft <- generateJsExpr left
555 jsRight <- generateJsExpr right
556 JsExpr <$> generateBinopHelp op jsLeft jsRight
560-- BINARY OPERATOR HELPERS
563generateBinopHelp :: V.Global -> JS.Expr -> JS.Expr -> Generator JS.Expr
564generateBinopHelp var@(V.Global home op) jsLeft jsRight =
566 simpleMake left right =
567 do func <- Var.global var
568 return $ JS.Call (ref "A2") [ func, left, right ]
570 if home == basics then
571 (Map.findWithDefault simpleMake op basicsOps) jsLeft jsRight
573 else if op == "::" && home == list then
574 BuiltIn.cons jsLeft jsRight
577 simpleMake jsLeft jsRight
580list :: ModuleName.Canonical
582 ModuleName.inCore "List"
585basicsOps :: Map.Map Text (JS.Expr -> JS.Expr -> Generator JS.Expr)
587 Map.fromList (basicsOpsNormal ++ basicsOpsSpecial)
590basicsOpsNormal :: [(Text, JS.Expr -> JS.Expr -> Generator JS.Expr)]
594 ( str, \left right -> return (JS.Infix op left right) )
605basicsOpsSpecial :: [(Text, JS.Expr -> JS.Expr -> Generator JS.Expr)]
610 [ "^" =*> \a b -> return $ JS.Call (obj ["Math","pow"]) [a,b]
611 , "==" =*> BuiltIn.eq
612 , "/=" =*> \a b -> JS.Prefix JS.PrefixLNot <$> BuiltIn.eq a b
613 , "<" =*> cmp JS.OpLT 0
614 , ">" =*> cmp JS.OpGT 0
615 , "<=" =*> cmp JS.OpLT 1
616 , ">=" =*> cmp JS.OpGT (-1)
617 , "//" =*> \a b -> return $ JS.Infix JS.OpBOr (JS.Infix JS.OpDiv a b) (JS.Int 0)
621cmp :: JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> Generator JS.Expr
623 do ordering <- BuiltIn.cmp a b
624 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