1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
|
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} -- only for Num, Fractional on JStgExpr
-----------------------------------------------------------------------------
-- |
-- Module : GHC.JS.Make
-- Copyright : (c) The University of Glasgow 2001
-- License : BSD-style (see the file LICENSE)
--
-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
-- Luite Stegeman <luite.stegeman@iohk.io>
-- Sylvain Henry <sylvain.henry@iohk.io>
-- Josh Meredith <josh.meredith@iohk.io>
-- Stability : experimental
--
--
-- * Domain and Purpose
--
-- GHC.JS.Make defines helper functions to ease the creation of JavaScript
-- ASTs as defined in 'GHC.JS.Syntax'. Its purpose is twofold: make the EDSL
-- more ergonomic to program in, and make errors in the EDSL /look/ obvious
-- because the EDSL is untyped. It is primarily concerned with injecting
-- terms into the domain of the EDSL to construct JS programs in Haskell.
--
-- * Strategy
--
-- The strategy for this module comes straight from gentzen; where we have
-- two types of helper functions. Functions which inject terms into the
-- EDSL, and combinator functions which operate on terms in the EDSL to
-- construct new terms in the EDSL. Crucially, missing from this module are
-- corresponding /elimination/ or /destructing/ functions which would
-- project information from the EDSL back to Haskell. See
-- 'GHC.StgToJS.Utils' for such functions.
--
-- * /Introduction/ functions
--
-- We define various primitive helpers which /introduce/ terms in the
-- EDSL, for example 'jVar', 'jLam', and 'var' and 'jString'.
-- Similarly this module exports four typeclasses 'ToExpr', 'ToStat',
-- 'JVarMagic', 'JSArgument'. 'ToExpr' injects values as a JS
-- expression into the EDSL. 'ToStat' injects values as JS statements
-- into the EDSL. @JVarMagic@ provides a polymorphic way to introduce
-- a new name into the EDSL and @JSArgument@ provides a polymorphic
-- way to bind variable names for use in JS functions with different
-- arities.
--
-- * /Combinator/ functions
--
-- The rest of the module defines combinators which create terms in
-- the EDSL from terms in the EDSL. Notable examples are '|=' and
-- '||=', '|=' is sugar for 'AssignStat', it is a binding form that
-- declares @foo = bar@ /assuming/ foo has been already declared.
-- '||=' is more sugar on top of '|=', it is also a binding form that
-- declares the LHS of '|=' before calling '|=' to bind a value, bar,
-- to a variable foo. Other common examples are the 'if_' and 'math_'
-- helpers such as 'math_cos'.
--
-- * Consumers
--
-- The entire JS backend consumes this module, e.g., the modules in
-- GHC.StgToJS.\*.
--
-- * Notation
--
-- In this module we use @==>@ in docstrings to show the translation from
-- the JS EDSL domain to JS code. For example, @foo ||= bar ==> var foo; foo
-- = bar;@ should be read as @foo ||= bar@ is in the EDSL domain and results
-- in the JS code @var foo; foo = bar;@ when compiled.
--
-- In most cases functions prefixed with a 'j' are monadic because the
-- observably allocate. Notable exceptions are `jwhenS`, 'jString' and the
-- helpers for HashMaps.
-----------------------------------------------------------------------------
module GHC.JS.Make
( -- * Injection Type classes
-- $classes
ToJExpr(..)
, ToStat(..)
, JVarMagic(..)
, JSArgument(..)
-- * Introduction functions
-- $intro_funcs
, jString
, jLam, jLam', jFunction, jFunctionSized, jFunction'
, jVar, jVars, jFor, jForIn, jForEachIn, jTryCatchFinally
-- * Combinators
-- $combinators
, (||=), (|=), (.==.), (.===.), (.!=.), (.!==.), (.!)
, (.>.), (.>=.), (.<.), (.<=.)
, (.<<.), (.>>.), (.>>>.)
, (.|.), (.||.), (.&&.)
, if_, if10, if01, ifS, ifBlockS, jBlock, jIf
, jwhenS
, app, appS, returnS
, loop, loopBlockS
, preIncrS, postIncrS
, preDecrS, postDecrS
, off8, off16, off32, off64
, mask8, mask16
, signExtend8, signExtend16
, typeof
, returnStack, assignAllEqual, assignAll, assignAllReverseOrder
, declAssignAll
, nullStat, (.^)
, trace
-- ** Hash combinators
, jhEmpty
, jhSingle
, jhAdd
, jhFromList
-- * Literals
-- $literals
, null_
, undefined_
, false_
, true_
, zero_
, one_
, two_
, three_
-- ** Math functions
-- $math
, math_log, math_sin, math_cos, math_tan, math_exp, math_acos, math_asin,
math_atan, math_abs, math_pow, math_sqrt, math_asinh, math_acosh, math_atanh,
math_cosh, math_sinh, math_tanh, math_expm1, math_log1p, math_fround
-- * Statement helpers
, Solo(..)
, decl
#if __GLASGOW_HASKELL__ < 905
, pattern MkSolo
#endif
)
where
import GHC.Prelude hiding ((.|.))
import GHC.JS.Ident
import GHC.JS.JStg.Syntax
import GHC.JS.JStg.Monad
import GHC.JS.Transform
import Control.Arrow ((***))
import Control.Monad (replicateM)
import Data.Tuple
import qualified Data.Map as M
import GHC.Data.FastString
import GHC.Utils.Misc
import GHC.Types.Unique.Map
--------------------------------------------------------------------------------
-- Type Classes
--------------------------------------------------------------------------------
-- $classes
-- The 'ToJExpr' class handles injection of of things into the EDSL as a JS
-- expression
-- | Things that can be marshalled into javascript values.
-- Instantiate for any necessary data structures.
class ToJExpr a where
toJExpr :: a -> JStgExpr
toJExprFromList :: [a] -> JStgExpr
toJExprFromList = ValExpr . JList . map toJExpr
instance ToJExpr a => ToJExpr [a] where
toJExpr = toJExprFromList
instance ToJExpr JStgExpr where
toJExpr = id
instance ToJExpr () where
toJExpr _ = ValExpr $ JList []
instance ToJExpr Bool where
toJExpr True = var "true"
toJExpr False = var "false"
instance ToJExpr JVal where
toJExpr = ValExpr
instance ToJExpr a => ToJExpr (UniqMap FastString a) where
toJExpr = ValExpr . JHash . mapUniqMap toJExpr
instance ToJExpr a => ToJExpr (M.Map String a) where
toJExpr = ValExpr . JHash . listToUniqMap . map (mkFastString *** toJExpr) . M.toList
instance ToJExpr Double where
toJExpr = ValExpr . JDouble . SaneDouble
instance ToJExpr Int where
toJExpr = ValExpr . JInt . fromIntegral
instance ToJExpr Integer where
toJExpr = ValExpr . JInt
instance ToJExpr Char where
toJExpr = ValExpr . JStr . mkFastString . (:[])
toJExprFromList = ValExpr . JStr . mkFastString
-- where escQuotes = tailDef "" . initDef "" . show
instance ToJExpr Ident where
toJExpr = ValExpr . JVar
instance ToJExpr FastString where
toJExpr = ValExpr . JStr
instance (ToJExpr a, ToJExpr b) => ToJExpr (a,b) where
toJExpr (a,b) = ValExpr . JList $ [toJExpr a, toJExpr b]
instance (ToJExpr a, ToJExpr b, ToJExpr c) => ToJExpr (a,b,c) where
toJExpr (a,b,c) = ValExpr . JList $ [toJExpr a, toJExpr b, toJExpr c]
instance (ToJExpr a, ToJExpr b, ToJExpr c, ToJExpr d) => ToJExpr (a,b,c,d) where
toJExpr (a,b,c,d) = ValExpr . JList $ [toJExpr a, toJExpr b, toJExpr c, toJExpr d]
instance (ToJExpr a, ToJExpr b, ToJExpr c, ToJExpr d, ToJExpr e) => ToJExpr (a,b,c,d,e) where
toJExpr (a,b,c,d,e) = ValExpr . JList $ [toJExpr a, toJExpr b, toJExpr c, toJExpr d, toJExpr e]
instance (ToJExpr a, ToJExpr b, ToJExpr c, ToJExpr d, ToJExpr e, ToJExpr f) => ToJExpr (a,b,c,d,e,f) where
toJExpr (a,b,c,d,e,f) = ValExpr . JList $ [toJExpr a, toJExpr b, toJExpr c, toJExpr d, toJExpr e, toJExpr f]
-- | The 'ToStat' class handles injection of of things into the EDSL as a JS
-- statement. This ends up being polymorphic sugar for JS blocks, see helper
-- function 'GHC.JS.Make.expr2stat'. Instantiate for any necessary data
-- structures.
class ToStat a where
toStat :: a -> JStgStat
instance ToStat JStgStat where
toStat = id
instance ToStat [JStgStat] where
toStat = BlockStat
instance ToStat JStgExpr where
toStat = expr2stat
instance ToStat [JStgExpr] where
toStat = BlockStat . map expr2stat
-- | Convert A JS expression to a JS statement where applicable. This only
-- affects applications; 'ApplExpr', If-expressions; 'IfExpr', and Unary
-- expression; 'UOpExpr'.
expr2stat :: JStgExpr -> JStgStat
expr2stat (ApplExpr x y) = (ApplStat x y)
expr2stat (IfExpr x y z) = IfStat x (expr2stat y) (expr2stat z)
expr2stat (UOpExpr o x) = UOpStat o x
expr2stat _ = nullStat
--------------------------------------------------------------------------------
-- Introduction Functions
--------------------------------------------------------------------------------
-- $intro_functions
-- Introduction functions are functions that map values or terms in the Haskell
-- domain to the JS EDSL domain
-- | Create a new anonymous function. The result is a 'GHC.JS.Syntax.JExpr'
-- expression.
-- Usage:
--
-- > jLam $ \x -> jVar x + one_
-- > jLam $ \f -> (jLam $ \x -> (f `app` (x `app` x))) `app` (jLam $ \x -> (f `app` (x `app` x)))
jLam :: JSArgument args => (args -> JSM JStgStat) -> JSM JStgExpr
jLam body = do xs <- args
ValExpr . JFunc (argList xs) <$> body xs
-- | Special case of @jLam@ where the anonymous function requires no fresh
-- arguments.
jLam' :: JStgStat -> JStgExpr
jLam' body = ValExpr $ JFunc mempty body
-- | Introduce only one new variable into scope for the duration of the
-- enclosed expression. The result is a block statement. Usage:
--
-- 'jVar $ \x -> mconcat [jVar x ||= one_, ...'
jVar :: (JVarMagic t, ToJExpr t) => (t -> JSM JStgStat) -> JSM JStgStat
jVar f = jVars $ \(MkSolo only_one) -> f only_one
-- | Introduce one or many new variables into scope for the duration of the
-- enclosed expression. This function reifies the number of arguments based on
-- the container of the input function. We intentionally avoid lists and instead
-- opt for tuples because lists are not sized in general. The result is a block
-- statement. Usage:
--
-- @jVars $ \(x,y) -> mconcat [ x |= one_, y |= two_, x + y]@
jVars :: (JSArgument args) => (args -> JSM JStgStat) -> JSM JStgStat
jVars f = do as <- args
body <- f as
return $ mconcat $ fmap decl (argList as) ++ [body]
-- | Construct a top-level function subject to JS hoisting. This combinator is
-- polymorphic over function arity so you can you use to define a JS syntax
-- object in Haskell, which is a function in JS that takes 2 or 4 or whatever
-- arguments. For a singleton function use the @Solo@ constructor @MkSolo@.
-- Usage:
--
-- an example from the Rts that defines a 1-arity JS function
-- > jFunction (global "h$getReg") (\(MkSolo n) -> return $ SwitchStat n getRegCases mempty)
--
-- an example of a two argument function from the Rts
-- > jFunction (global "h$bh_lne") (\(x, frameSize) -> bhLneStats s x frameSize)
jFunction
:: (JSArgument args)
=> Ident -- ^ global name
-> (args -> JSM JStgStat) -- ^ function body, input is locally unique generated variables
-> JSM JStgStat
jFunction name body = do
func_args <- args
FuncStat name (argList func_args) <$> (body func_args)
-- | Construct a top-level function subject to JS hoisting. Special case where
-- the arity cannot be deduced from the 'args' parameter (atleast not without
-- dependent types).
jFunctionSized
:: Ident -- ^ global name
-> Int -- ^ Arity
-> ([JStgExpr] -> JSM JStgStat) -- ^ function body, input is locally unique generated variables
-> JSM JStgStat
jFunctionSized name arity body = do
func_args <- replicateM arity newIdent
FuncStat name func_args <$> (body $ toJExpr <$> func_args)
-- | Construct a top-level function subject to JS hoisting. Special case where
-- the function binds no parameters
jFunction'
:: Ident -- ^ global name
-> JSM JStgStat -- ^ function body, input is locally unique generated variables
-> JSM JStgStat
jFunction' name body = FuncStat name mempty <$> body
jBlock :: Monoid a => [JSM a] -> JSM a
jBlock = fmap mconcat . sequence
-- | Create a 'for in' statement.
-- Usage:
--
-- @jForIn {expression} $ \x -> {block involving x}@
jForIn :: JStgExpr -> (JStgExpr -> JStgStat) -> JSM JStgStat
jForIn e f = do
i <- newIdent
return $ decl i `mappend` ForInStat False i e (f (ValExpr $! JVar i))
-- | As with "jForIn" but creating a \"for each in\" statement.
jForEachIn :: JStgExpr -> (JStgExpr -> JStgStat) -> JSM JStgStat
jForEachIn e f = do i <- newIdent
return $ decl i `mappend` ForInStat True i e (f (ValExpr $! JVar i))
-- | Create a 'for' statement given a function for initialization, a predicate
-- to step to, a step and a body
-- Usage:
--
-- @ jFor (|= zero_) (.<. Int 65536) preIncrS
-- (\j -> ...something with the counter j...)@
--
jFor :: (JStgExpr -> JStgStat) -- ^ initialization function
-> (JStgExpr -> JStgExpr) -- ^ predicate
-> (JStgExpr -> JStgStat) -- ^ step function
-> (JStgExpr -> JStgStat) -- ^ body
-> JSM JStgStat
jFor init pred step body = do id <- newIdent
let i = ValExpr (JVar id)
return
$ decl id `mappend` ForStat (init i) (pred i) (step i) (body i)
-- | As with "jForIn" but creating a \"for each in\" statement.
jTryCatchFinally :: (Ident -> JStgStat) -> (Ident -> JStgStat) -> (Ident -> JStgStat) -> JSM JStgStat
jTryCatchFinally c f f2 = do i <- newIdent
return $ TryStat (c i) i (f i) (f2 i)
-- | Convert a ShortText to a Javascript String
jString :: FastString -> JStgExpr
jString = toJExpr
-- | construct a js declaration with the given identifier
decl :: Ident -> JStgStat
decl i = DeclStat i Nothing
-- | The empty JS HashMap
jhEmpty :: M.Map k JStgExpr
jhEmpty = M.empty
-- | A singleton JS HashMap
jhSingle :: (Ord k, ToJExpr a) => k -> a -> M.Map k JStgExpr
jhSingle k v = jhAdd k v jhEmpty
-- | insert a key-value pair into a JS HashMap
jhAdd :: (Ord k, ToJExpr a) => k -> a -> M.Map k JStgExpr -> M.Map k JStgExpr
jhAdd k v m = M.insert k (toJExpr v) m
-- | Construct a JS HashMap from a list of key-value pairs
jhFromList :: [(FastString, JStgExpr)] -> JVal
jhFromList = JHash . listToUniqMap
-- | The empty JS statement
nullStat :: JStgStat
nullStat = BlockStat []
--------------------------------------------------------------------------------
-- Combinators
--------------------------------------------------------------------------------
-- $combinators
-- Combinators operate on terms in the JS EDSL domain to create new terms in the
-- EDSL domain.
-- | JS infix Equality operators
(.==.), (.===.), (.!=.), (.!==.) :: JStgExpr -> JStgExpr -> JStgExpr
(.==.) = InfixExpr EqOp
(.===.) = InfixExpr StrictEqOp
(.!=.) = InfixExpr NeqOp
(.!==.) = InfixExpr StrictNeqOp
infixl 6 .==., .===., .!=., .!==.
-- | JS infix Ord operators
(.>.), (.>=.), (.<.), (.<=.) :: JStgExpr -> JStgExpr -> JStgExpr
(.>.) = InfixExpr GtOp
(.>=.) = InfixExpr GeOp
(.<.) = InfixExpr LtOp
(.<=.) = InfixExpr LeOp
infixl 7 .>., .>=., .<., .<=.
-- | JS infix bit operators
(.|.), (.||.), (.&&.) :: JStgExpr -> JStgExpr -> JStgExpr
(.|.) = InfixExpr BOrOp
(.||.) = InfixExpr LOrOp
(.&&.) = InfixExpr LAndOp
infixl 8 .||., .&&.
-- | JS infix bit shift operators
(.<<.), (.>>.), (.>>>.) :: JStgExpr -> JStgExpr -> JStgExpr
(.<<.) = InfixExpr LeftShiftOp
(.>>.) = InfixExpr RightShiftOp
(.>>>.) = InfixExpr ZRightShiftOp
infixl 9 .<<., .>>., .>>>.
-- | Given a 'JStgExpr', return the its type.
typeof :: JStgExpr -> JStgExpr
typeof = UOpExpr TypeofOp
-- | JS if-expression
--
-- > if_ e1 e2 e3 ==> e1 ? e2 : e3
if_ :: JStgExpr -> JStgExpr -> JStgExpr -> JStgExpr
if_ e1 e2 e3 = IfExpr e1 e2 e3
-- | If-expression which returns statements, see related 'ifBlockS'
--
-- > if e s1 s2 ==> if(e) { s1 } else { s2 }
ifS :: JStgExpr -> JStgStat -> JStgStat -> JStgStat
ifS e s1 s2 = IfStat e s1 s2
-- | Version of a JS if-expression which admits monadic actions in its branches
jIf :: JStgExpr -> JSM JStgStat -> JSM JStgStat -> JSM JStgStat
jIf e ma mb = do
!a <- ma
!b <- mb
pure $ IfStat e a b
-- | A when-statement as syntactic sugar via `ifS`
--
-- > jwhenS cond block ==> if(cond) { block } else { }
jwhenS :: JStgExpr -> JStgStat -> JStgStat
jwhenS cond block = IfStat cond block mempty
-- | If-expression which returns blocks
--
-- > ifBlockS e s1 s2 ==> if(e) { s1 } else { s2 }
ifBlockS :: JStgExpr -> [JStgStat] -> [JStgStat] -> JStgStat
ifBlockS e s1 s2 = IfStat e (mconcat s1) (mconcat s2)
-- | if-expression that returns 1 if condition <=> true, 0 otherwise
--
-- > if10 e ==> e ? 1 : 0
if10 :: JStgExpr -> JStgExpr
if10 e = IfExpr e one_ zero_
-- | if-expression that returns 0 if condition <=> true, 1 otherwise
--
-- > if01 e ==> e ? 0 : 1
if01 :: JStgExpr -> JStgExpr
if01 e = IfExpr e zero_ one_
-- | an expression application, see related 'appS'
--
-- > app f xs ==> f(xs)
app :: FastString -> [JStgExpr] -> JStgExpr
app f xs = ApplExpr (var f) xs
-- | A statement application, see the expression form 'app'
appS :: FastString -> [JStgExpr] -> JStgStat
appS f xs = ApplStat (var f) xs
-- | Return a 'JStgExpr'
returnS :: JStgExpr -> JStgStat
returnS e = ReturnStat e
-- | "for" loop with increment at end of body
loop :: JStgExpr -> (JStgExpr -> JStgExpr) -> (JStgExpr -> JSM JStgStat) -> JSM JStgStat
loop initial test body_ = jVar $ \i ->
do body <- body_ i
return $
mconcat [ i |= initial
, WhileStat False (test i) body
]
-- | "for" loop with increment at end of body
loopBlockS :: JStgExpr -> (JStgExpr -> JStgExpr) -> (JStgExpr -> [JStgStat]) -> JSM JStgStat
loopBlockS initial test body = jVar $ \i ->
return $
mconcat [ i |= initial
, WhileStat False (test i) (mconcat (body i))
]
-- | Prefix-increment a 'JStgExpr'
preIncrS :: JStgExpr -> JStgStat
preIncrS x = UOpStat PreIncOp x
-- | Postfix-increment a 'JStgExpr'
postIncrS :: JStgExpr -> JStgStat
postIncrS x = UOpStat PostIncOp x
-- | Prefix-decrement a 'JStgExpr'
preDecrS :: JStgExpr -> JStgStat
preDecrS x = UOpStat PreDecOp x
-- | Postfix-decrement a 'JStgExpr'
postDecrS :: JStgExpr -> JStgStat
postDecrS x = UOpStat PostDecOp x
-- | Byte indexing of o with a 64-bit offset
off64 :: JStgExpr -> JStgExpr -> JStgExpr
off64 o i = Add o (i .<<. three_)
-- | Byte indexing of o with a 32-bit offset
off32 :: JStgExpr -> JStgExpr -> JStgExpr
off32 o i = Add o (i .<<. two_)
-- | Byte indexing of o with a 16-bit offset
off16 :: JStgExpr -> JStgExpr -> JStgExpr
off16 o i = Add o (i .<<. one_)
-- | Byte indexing of o with a 8-bit offset
off8 :: JStgExpr -> JStgExpr -> JStgExpr
off8 o i = Add o i
-- | a bit mask to retrieve the lower 8-bits
mask8 :: JStgExpr -> JStgExpr
mask8 x = BAnd x (Int 0xFF)
-- | a bit mask to retrieve the lower 16-bits
mask16 :: JStgExpr -> JStgExpr
mask16 x = BAnd x (Int 0xFFFF)
-- | Sign-extend/narrow a 8-bit value
signExtend8 :: JStgExpr -> JStgExpr
signExtend8 x = (BAnd x (Int 0x7F )) `Sub` (BAnd x (Int 0x80))
-- | Sign-extend/narrow a 16-bit value
signExtend16 :: JStgExpr -> JStgExpr
signExtend16 x = (BAnd x (Int 0x7FFF)) `Sub` (BAnd x (Int 0x8000))
-- | Select a property 'prop', from and object 'obj'
--
-- > obj .^ prop ==> obj.prop
(.^) :: JStgExpr -> FastString -> JStgExpr
obj .^ prop = SelExpr obj (global prop)
infixl 8 .^
-- | Assign a variable to an expression
--
-- > foo |= expr ==> var foo = expr;
(|=) :: JStgExpr -> JStgExpr -> JStgStat
(|=) l r = AssignStat l AssignOp r
-- | Declare a variable and then Assign the variable to an expression
--
-- > foo |= expr ==> var foo; foo = expr;
(||=) :: Ident -> JStgExpr -> JStgStat
i ||= ex = DeclStat i (Just ex)
infixl 2 ||=, |=
-- | return the expression at idx of obj
--
-- > obj .! idx ==> obj[idx]
(.!) :: JStgExpr -> JStgExpr -> JStgExpr
(.!) = IdxExpr
infixl 8 .!
assignAllEqual :: HasDebugCallStack => [JStgExpr] -> [JStgExpr] -> JStgStat
assignAllEqual xs ys = mconcat (zipWithEqual "assignAllEqual" (|=) xs ys)
assignAll :: [JStgExpr] -> [JStgExpr] -> JStgStat
assignAll xs ys = mconcat (zipWith (|=) xs ys)
assignAllReverseOrder :: [JStgExpr] -> [JStgExpr] -> JStgStat
assignAllReverseOrder xs ys = mconcat (reverse (zipWith (|=) xs ys))
declAssignAll :: [Ident] -> [JStgExpr] -> JStgStat
declAssignAll xs ys = mconcat (zipWith (||=) xs ys)
trace :: ToJExpr a => a -> JStgStat
trace ex = appS "h$log" [toJExpr ex]
--------------------------------------------------------------------------------
-- Literals
--------------------------------------------------------------------------------
-- $literals
-- Literals in the JS EDSL are constants in the Haskell domain. These are useful
-- helper values and never change
-- | The JS literal 'null'
null_ :: JStgExpr
null_ = var "null"
-- | The JS literal 0
zero_ :: JStgExpr
zero_ = Int 0
-- | The JS literal 1
one_ :: JStgExpr
one_ = Int 1
-- | The JS literal 2
two_ :: JStgExpr
two_ = Int 2
-- | The JS literal 3
three_ :: JStgExpr
three_ = Int 3
-- | The JS literal 'undefined'
undefined_ :: JStgExpr
undefined_ = var "undefined"
-- | The JS literal 'true'
true_ :: JStgExpr
true_ = ValExpr (JBool True)
-- | The JS literal 'false'
false_ :: JStgExpr
false_ = ValExpr (JBool False)
returnStack :: JStgStat
returnStack = ReturnStat (ApplExpr (var "h$rs") [])
--------------------------------------------------------------------------------
-- Math functions
--------------------------------------------------------------------------------
-- $math
-- Math functions in the EDSL are literals, with the exception of 'math_' which
-- is the sole math introduction function.
math :: JStgExpr
math = var "Math"
math_ :: FastString -> [JStgExpr] -> JStgExpr
math_ op args = ApplExpr (math .^ op) args
math_log, math_sin, math_cos, math_tan, math_exp, math_acos, math_asin, math_atan,
math_abs, math_pow, math_sqrt, math_asinh, math_acosh, math_atanh, math_sign,
math_sinh, math_cosh, math_tanh, math_expm1, math_log1p, math_fround
:: [JStgExpr] -> JStgExpr
math_log = math_ "log"
math_sin = math_ "sin"
math_cos = math_ "cos"
math_tan = math_ "tan"
math_exp = math_ "exp"
math_acos = math_ "acos"
math_asin = math_ "asin"
math_atan = math_ "atan"
math_abs = math_ "abs"
math_pow = math_ "pow"
math_sign = math_ "sign"
math_sqrt = math_ "sqrt"
math_asinh = math_ "asinh"
math_acosh = math_ "acosh"
math_atanh = math_ "atanh"
math_sinh = math_ "sinh"
math_cosh = math_ "cosh"
math_tanh = math_ "tanh"
math_expm1 = math_ "expm1"
math_log1p = math_ "log1p"
math_fround = math_ "fround"
instance Num JStgExpr where
x + y = InfixExpr AddOp x y
x - y = InfixExpr SubOp x y
x * y = InfixExpr MulOp x y
abs x = math_abs [x]
negate x = UOpExpr NegOp x
signum x = math_sign [x]
fromInteger x = ValExpr (JInt x)
instance Fractional JStgExpr where
x / y = InfixExpr DivOp x y
fromRational x = ValExpr (JDouble (realToFrac x))
-- The Solo constructor was renamed to MkSolo in ghc 9.5
#if __GLASGOW_HASKELL__ < 905
pattern MkSolo :: a -> Solo a
pattern MkSolo a = Solo a
{-# COMPLETE MkSolo #-}
#endif
--------------------------------------------------------------------------------
-- New Identifiers
--------------------------------------------------------------------------------
-- | Type class that generates fresh @a@'s for the JS backend. You should almost
-- never need to use this directly. Instead use @JSArgument@, for examples of
-- how to employ these classes please see @jVar@, @jFunction@ and call sites in
-- the Rts.
class JVarMagic a where
fresh :: JSM a
-- | Type class that finds the form of arguments required for a JS syntax
-- object. This class gives us a single interface to generate variables for
-- functions that have different arities. Thus with it, we can have only one
-- @jFunction@ which is polymorphic over its arity, instead of 'jFunction2',
-- 'jFunction3' and so on.
class JSArgument args where
argList :: args -> [Ident]
args :: JSM args
instance JVarMagic Ident where
fresh = newIdent
instance JVarMagic JVal where
fresh = JVar <$> fresh
instance JVarMagic JStgExpr where
fresh = do i <- fresh
return $ ValExpr $ JVar i
instance (JVarMagic a, ToJExpr a) => JSArgument (Solo a) where
argList (MkSolo a) = concatMap identsE [toJExpr a]
args = do i <- fresh
return $ MkSolo i
instance (JVarMagic a, JVarMagic b, ToJExpr a, ToJExpr b) => JSArgument (a,b) where
argList (a,b) = concatMap identsE [toJExpr a , toJExpr b]
args = (,) <$> fresh <*> fresh
instance ( JVarMagic a, ToJExpr a
, JVarMagic b, ToJExpr b
, JVarMagic c, ToJExpr c
) => JSArgument (a,b,c) where
argList (a,b,c) = concatMap identsE [toJExpr a , toJExpr b, toJExpr c]
args = (,,) <$> fresh <*> fresh <*> fresh
instance ( JVarMagic a, ToJExpr a
, JVarMagic b, ToJExpr b
, JVarMagic c, ToJExpr c
, JVarMagic d, ToJExpr d
) => JSArgument (a,b,c,d) where
argList (a,b,c,d) = concatMap identsE [toJExpr a , toJExpr b, toJExpr c, toJExpr d]
args = (,,,) <$> fresh <*> fresh <*> fresh <*> fresh
instance ( JVarMagic a, ToJExpr a
, JVarMagic b, ToJExpr b
, JVarMagic c, ToJExpr c
, JVarMagic d, ToJExpr d
, JVarMagic e, ToJExpr e
) => JSArgument (a,b,c,d,e) where
argList (a,b,c,d,e) = concatMap identsE [toJExpr a , toJExpr b, toJExpr c, toJExpr d, toJExpr e]
args = (,,,,) <$> fresh <*> fresh <*> fresh <*> fresh <*> fresh
instance ( JVarMagic a, ToJExpr a
, JVarMagic b, ToJExpr b
, JVarMagic c, ToJExpr c
, JVarMagic d, ToJExpr d
, JVarMagic e, ToJExpr e
, JVarMagic f, ToJExpr f
) => JSArgument (a,b,c,d,e,f) where
argList (a,b,c,d,e,f) = concatMap identsE [toJExpr a , toJExpr b, toJExpr c, toJExpr d, toJExpr e, toJExpr f]
args = (,,,,,) <$> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh
instance ( JVarMagic a, ToJExpr a
, JVarMagic b, ToJExpr b
, JVarMagic c, ToJExpr c
, JVarMagic d, ToJExpr d
, JVarMagic e, ToJExpr e
, JVarMagic f, ToJExpr f
, JVarMagic g, ToJExpr g
) => JSArgument (a,b,c,d,e,f,g) where
argList (a,b,c,d,e,f,g) = concatMap identsE [toJExpr a , toJExpr b, toJExpr c, toJExpr d, toJExpr e, toJExpr f, toJExpr g]
args = (,,,,,,) <$> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh
instance ( JVarMagic a, ToJExpr a
, JVarMagic b, ToJExpr b
, JVarMagic c, ToJExpr c
, JVarMagic d, ToJExpr d
, JVarMagic e, ToJExpr e
, JVarMagic f, ToJExpr f
, JVarMagic g, ToJExpr g
, JVarMagic h, ToJExpr h
) => JSArgument (a,b,c,d,e,f,g,h) where
argList (a,b,c,d,e,f,g,h) = concatMap identsE [toJExpr a , toJExpr b, toJExpr c, toJExpr d, toJExpr e, toJExpr f, toJExpr g, toJExpr h]
args = (,,,,,,,) <$> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh
instance ( JVarMagic a, ToJExpr a
, JVarMagic b, ToJExpr b
, JVarMagic c, ToJExpr c
, JVarMagic d, ToJExpr d
, JVarMagic e, ToJExpr e
, JVarMagic f, ToJExpr f
, JVarMagic g, ToJExpr g
, JVarMagic h, ToJExpr h
, JVarMagic i, ToJExpr i
) => JSArgument (a,b,c,d,e,f,g,h,i) where
argList (a,b,c,d,e,f,g,h,i) = concatMap identsE [toJExpr a , toJExpr b, toJExpr c, toJExpr d, toJExpr e, toJExpr f, toJExpr g, toJExpr h, toJExpr i]
args = (,,,,,,,,) <$> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh
instance ( JVarMagic a, ToJExpr a
, JVarMagic b, ToJExpr b
, JVarMagic c, ToJExpr c
, JVarMagic d, ToJExpr d
, JVarMagic e, ToJExpr e
, JVarMagic f, ToJExpr f
, JVarMagic g, ToJExpr g
, JVarMagic h, ToJExpr h
, JVarMagic i, ToJExpr i
, JVarMagic j, ToJExpr j
) => JSArgument (a,b,c,d,e,f,g,h,i,j) where
argList (a,b,c,d,e,f,g,h,i,j) = concatMap identsE [toJExpr a , toJExpr b, toJExpr c, toJExpr d, toJExpr e, toJExpr f, toJExpr g, toJExpr h, toJExpr i, toJExpr j]
args = (,,,,,,,,,) <$> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh <*> fresh
|