1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript.Expression
7import Control.Arrow (second)
8import Control.Monad (foldM)
9import qualified Data.List as List
10import qualified Data.Map as Map
11import Data.Monoid ((<>))
12import qualified Data.Text as Text
13import Data.Text (Text)
15import qualified AST.Expression.Canonical as Can
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.Foreign as Foreign
24import qualified Generate.JavaScript.Literal as Literal
25import qualified Generate.JavaScript.Variable as Var
26import Generate.JavaScript.Variable (Generator)
27import qualified Optimize.DecisionTree as DT
31-- GENERATE DECLARATION
34generateDecl :: ModuleName.Canonical -> Text -> Opt.Def -> Generator JS.Stmt
35generateDecl home name def =
36 do jsName <- Var.getGlobalName (V.Global home name)
37 jsBody <- generateDef name def
38 return $ JS.VarDeclStmt [ Help.varDecl jsName jsBody ]
50jsExpr :: JS.Expr -> Generator Code
55jsBlock :: [JS.Stmt] -> Generator Code
60isBlock :: Code -> Bool
67toStatementList :: Code -> [JS.Stmt]
71 [ JS.Return (Just expr) ]
77toStatement :: Code -> JS.Stmt
90toExpr :: Code -> JS.Expr
97 JS.Call (function [] stmts) []
104generateJsExpr :: Opt.Expr -> Generator JS.Expr
105generateJsExpr optExpr =
106 toExpr <$> generateCode optExpr
109generateCode :: Opt.Expr -> Generator Code
113 jsExpr $ Var.local name
116 JsExpr <$> Var.global name
119 JsExpr <$> Literal.literal lit
121 Access record field ->
122 do jsRecord <- generateJsExpr record
123 jsExpr $ JS.DotRef jsRecord (JS.Id (Var.safe field))
125 Update record fields ->
127 fieldToJs (field, value) =
128 do jsValue <- generateJsExpr value
129 return (Var.safe field, jsValue)
131 do jsRecord <- generateJsExpr record
132 jsFields <- mapM fieldToJs fields
133 JsExpr <$> BuiltIn.recordUpdate jsRecord jsFields
137 toField (field, value) =
138 do jsValue <- generateJsExpr value
139 return (Var.safe field ==> jsValue)
141 do jsFields <- mapM toField fields
142 jsExpr $ JS.Object jsFields
144 Binop op leftExpr rightExpr ->
145 generateBinop op leftExpr rightExpr
147 Function args body ->
148 generateFunction args body
151 generateCall func args
153 TailCall name argNames args ->
155 reassign name tempName =
157 JS.Assign (JS.LVar (Var.safe name)) (ref tempName)
159 do args' <- mapM generateJsExpr args
160 tempNames <- mapM (\_ -> Var.fresh) args
162 JS.VarDeclStmt (zipWith varDecl tempNames args')
163 : zipWith reassign argNames tempNames
164 ++ [JS.Continue (Just (JS.Id (Var.safe name)))]
167 do stmts <- mapM generateLetDef defs
168 code <- generateCode body
169 jsBlock (stmts ++ toStatementList code)
171 If branches finally ->
172 generateIf branches finally
174 Case exprName decider jumps ->
175 JsBlock <$> generateCase exprName decider jumps
178 do jsElements <- mapM generateJsExpr elements
179 JsExpr <$> BuiltIn.list jsElements
184 "ctor" ==> JS.String tag
187 ("_" <> Text.pack (show n)) ==> entry
189 do jsMembers <- mapM generateJsExpr members
190 jsExpr $ JS.Object (ctor : zipWith toEntry jsMembers [ 0 :: Int .. ])
192 CtorAccess dataExpr index ->
193 do jsDataExpr <- generateJsExpr dataExpr
194 jsExpr $ JS.DotRef jsDataExpr (JS.Id ("_" <> Text.pack (show index)))
197 JsExpr <$> BuiltIn.effect moduleName
200 JsExpr <$> BuiltIn.effect moduleName
202 OutgoingPort name tipe ->
203 do encoder <- Foreign.encode tipe
204 JsExpr <$> BuiltIn.outgoingPort name encoder
206 IncomingPort name tipe ->
207 do jsDecoder <- generateJsExpr (Foreign.decode tipe)
208 JsExpr <$> BuiltIn.incomingPort name jsDecoder
211 generateProgram kind body
214 do string <- Literal.literal (L.Str src)
215 jsExpr $ JS.Object [(JS.StringProp "src", string)]
217 Crash home region maybeBranchProblem ->
218 do maybeOptBranchProblem <- traverse generateJsExpr maybeBranchProblem
219 JsExpr <$> BuiltIn.crash home region maybeOptBranchProblem
226generateProgram :: Can.Main -> Opt.Expr -> Generator Code
227generateProgram kind body =
230 do html <- generateJsExpr body
231 func <- BuiltIn.staticProgram
232 jsExpr (func <| html)
235 do almostProgram <- generateJsExpr body
236 jsExpr (JS.Call almostProgram [])
239 do almostProgram <- generateJsExpr body
240 flagDecoder <- generateJsExpr (Foreign.decode tipe)
241 jsExpr (almostProgram <| flagDecoder)
248generateLetDef :: (Text, Opt.Def) -> Generator JS.Stmt
249generateLetDef (name, def) =
250 do jsBody <- generateDef name def
251 return $ JS.VarDeclStmt [ Help.varDecl name jsBody ]
254generateDef :: Text -> Opt.Def -> Generator JS.Expr
255generateDef name def =
257 Opt.TailDef argNames body ->
258 generateTailFunction name argNames body
264generateTailFunction :: Text -> [Text] -> Opt.Expr -> Generator JS.Expr
265generateTailFunction name args body =
266 do code <- generateCode body
267 return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
269 (JS.Id (Var.safe name))
270 (JS.While (JS.Bool True) (toStatement code))
277generateFunction :: [Text] -> Opt.Expr -> Generator Code
278generateFunction args body =
279 do code <- generateCode body
280 jsExpr (generateFunctionWithArity args code)
283generateFunctionWithArity :: [Text] -> Code -> JS.Expr
284generateFunctionWithArity rawArgs code =
286 args = map Var.safe rawArgs
289 if 2 <= arity && arity <= 9 then
291 fN = "F" <> Text.pack (show arity)
293 ref fN <| function args (toStatementList code)
296 (lastArg:otherArgs) = reverse args
297 innerBody = function [lastArg] (toStatementList code)
299 foldl (\body arg -> function [arg] [JS.Return (Just body)]) innerBody otherArgs
306generateCall :: Opt.Expr -> [Opt.Expr] -> Generator Code
307generateCall func args =
309 (Opt.VarGlobal (V.Global home name), [arg]) ->
311 "complement" | home == bitwise -> genPrefix JS.PrefixBNot arg
312 "not" | home == basics -> genPrefix JS.PrefixLNot arg
313 _ -> generateCallHelp func args
315 (Opt.VarGlobal (V.Global home name), [ left, right ]) | home == bitwise ->
317 "and" -> genInfix JS.OpBAnd left right
318 "or" -> genInfix JS.OpBOr left right
319 "xor" -> genInfix JS.OpBXor left right
320 "shiftLeftBy" -> genInfix JS.OpLShift right left
321 "shiftRightBy" -> genInfix JS.OpSpRShift right left
322 "shiftRightZfBy" -> genInfix JS.OpZfRShift right left
323 _ -> generateCallHelp func args
326 generateCallHelp func args
329genPrefix :: JS.PrefixOp -> Opt.Expr -> Generator Code
331 do jsArg <- generateJsExpr arg
332 jsExpr (JS.Prefix op jsArg)
335genInfix :: JS.InfixOp -> Opt.Expr -> Opt.Expr -> Generator Code
336genInfix op left right =
337 do jsLeft <- generateJsExpr left
338 jsRight <- generateJsExpr right
339 jsExpr $ JS.Infix op jsLeft jsRight
342basics :: ModuleName.Canonical
344 ModuleName.inCore "Basics"
347bitwise :: ModuleName.Canonical
349 ModuleName.inCore "Bitwise"
352generateCallHelp :: Opt.Expr -> [Opt.Expr] -> Generator Code
353generateCallHelp func args =
356 aN = "A" <> Text.pack (show arity)
358 do jsFunc <- generateJsExpr func
359 jsArgs <- mapM generateJsExpr args
361 if 2 <= arity && arity <= 9 then
362 JS.Call (ref aN) (jsFunc:jsArgs)
364 foldl1 (<|) (jsFunc:jsArgs)
371generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> Generator Code
372generateIf givenBranches givenFinally =
374 (branches, finally) =
375 crushIfs givenBranches givenFinally
377 convertBranch (condition, expr) =
378 (,) <$> generateJsExpr condition <*> generateCode expr
380 ifExpression (condition, branch) otherwise =
381 JS.If condition branch otherwise
383 ifStatement (condition, branch) otherwise =
384 JS.IfStmt condition branch otherwise
386 do jsBranches <- mapM convertBranch branches
387 jsFinally <- generateCode finally
389 if any isBlock (jsFinally : map snd jsBranches)
391 jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
393 jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
397 :: [(Opt.Expr, Opt.Expr)]
399 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
400crushIfs branches finally =
401 crushIfsHelp [] branches finally
405 :: [(Opt.Expr, Opt.Expr)]
406 -> [(Opt.Expr, Opt.Expr)]
408 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
409crushIfsHelp visitedBranches unvisitedBranches finally =
410 case unvisitedBranches of
413 If subBranches subFinally ->
414 crushIfsHelp visitedBranches subBranches subFinally
417 (reverse visitedBranches, finally)
419 (Literal (L.Boolean True), branch) : _ ->
420 crushIfsHelp visitedBranches [] branch
422 visiting : unvisited ->
423 crushIfsHelp (visiting : visitedBranches) unvisited finally
430generateCase :: Text -> Opt.Decider Opt.Choice -> [(Int, Opt.Expr)] -> Generator [JS.Stmt]
431generateCase exprName decider jumps =
432 do labelRoot <- Var.fresh
433 decider <- generateDecider exprName labelRoot decider
434 foldM (goto labelRoot) decider jumps
441goto :: Text -> [JS.Stmt] -> (Int, Opt.Expr) -> Generator [JS.Stmt]
442goto labelRoot deciderStmts (target, branch) =
446 (toLabel labelRoot target)
447 (JS.DoWhile (JS.Block deciderStmts) (JS.Bool False))
449 do code <- generateCode branch
450 return (labeledDeciderStmt : toStatementList code)
453toLabel :: Text -> Int -> JS.Id
455 JS.Id (root <> "_" <> Text.pack (show target))
459-- turn deciders into ifs and switches
462generateDecider :: Text -> Text -> Opt.Decider Opt.Choice -> Generator [JS.Stmt]
463generateDecider exprName labelRoot decisionTree =
465 Opt.Leaf (Opt.Inline branch) ->
466 toStatementList <$> generateCode branch
468 Opt.Leaf (Opt.Jump target) ->
469 return [ JS.Break (Just (toLabel labelRoot target)) ]
471 Opt.Chain testChain success failure ->
473 makeTest (path, test) =
474 do testExpr <- pathToTestableExpr exprName path test
475 JS.Infix JS.OpStrictEq testExpr <$> testToExpr test
477 do testExprs <- mapM makeTest testChain
478 let cond = List.foldl1' (JS.Infix JS.OpLAnd) testExprs
479 thenBranch <- generateDecider exprName labelRoot success
480 elseBranch <- generateDecider exprName labelRoot failure
481 return [ JS.IfStmt cond (JS.Block thenBranch) (JS.Block elseBranch) ]
483 Opt.FanOut path edges fallback ->
484 do testExpr <- pathToTestableExpr exprName path (fst (head edges))
485 caseClauses <- mapM (edgeToCase exprName labelRoot) edges
486 caseDefault <- JS.Default <$> generateDecider exprName labelRoot fallback
487 return [ JS.Switch testExpr (caseClauses ++ [caseDefault]) ]
490edgeToCase :: Text -> Text -> (DT.Test, Opt.Decider Opt.Choice) -> Generator JS.Case
491edgeToCase exprName labelRoot (test, subTree) =
494 <*> generateDecider exprName labelRoot subTree
497testToExpr :: DT.Test -> Generator JS.Expr
500 DT.Constructor (V.Canonical _ tag) ->
501 return $ JS.String tag
503 DT.Literal (L.Chr char) ->
504 return $ JS.String char
514pathToTestableExpr :: Text -> DT.Path -> DT.Test -> Generator JS.Expr
515pathToTestableExpr root path exampleTest =
516 do accessExpr <- generateJsExpr (pathToExpr root path)
519 return $ JS.DotRef accessExpr (JS.Id "ctor")
521 DT.Literal (L.Chr _) ->
522 return $ JS.Call (JS.DotRef accessExpr (JS.Id "valueOf")) []
528pathToExpr :: Text -> DT.Path -> Opt.Expr
529pathToExpr root fullPath =
530 go (Opt.VarLocal root) fullPath
534 DT.Position index subpath ->
535 go (Opt.CtorAccess expr index) subpath
537 DT.Field field subpath ->
538 go (Opt.Access expr field) subpath
551generateBinop :: V.Global -> Opt.Expr -> Opt.Expr -> Generator Code
552generateBinop op left right =
553 do jsLeft <- generateJsExpr left
554 jsRight <- generateJsExpr right
555 JsExpr <$> generateBinopHelp op jsLeft jsRight
559-- BINARY OPERATOR HELPERS
562generateBinopHelp :: V.Global -> JS.Expr -> JS.Expr -> Generator JS.Expr
563generateBinopHelp var@(V.Global home op) jsLeft jsRight =
565 simpleMake left right =
566 do func <- Var.global var
567 return $ JS.Call (ref "A2") [ func, left, right ]
569 if home == basics then
570 (Map.findWithDefault simpleMake op basicsOps) jsLeft jsRight
572 else if op == "::" && home == list then
573 BuiltIn.cons jsLeft jsRight
576 simpleMake jsLeft jsRight
579list :: ModuleName.Canonical
581 ModuleName.inCore "List"
584basicsOps :: Map.Map Text (JS.Expr -> JS.Expr -> Generator JS.Expr)
586 Map.fromList (basicsOpsNormal ++ basicsOpsSpecial)
589basicsOpsNormal :: [(Text, JS.Expr -> JS.Expr -> Generator JS.Expr)]
593 ( str, \left right -> return (JS.Infix op left right) )
604basicsOpsSpecial :: [(Text, JS.Expr -> JS.Expr -> Generator JS.Expr)]
609 [ "^" =*> \a b -> return $ JS.Call (obj ["Math","pow"]) [a,b]
610 , "==" =*> BuiltIn.eq
611 , "/=" =*> \a b -> JS.Prefix JS.PrefixLNot <$> BuiltIn.eq a b
612 , "<" =*> cmp JS.OpLT 0
613 , ">" =*> cmp JS.OpGT 0
614 , "<=" =*> cmp JS.OpLT 1
615 , ">=" =*> cmp JS.OpGT (-1)
616 , "//" =*> \a b -> return $ JS.Infix JS.OpBOr (JS.Infix JS.OpDiv a b) (JS.Int 0)
620cmp :: JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> Generator JS.Expr
622 do ordering <- BuiltIn.cmp a b
623 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