{-# 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 -- Luite Stegeman -- Sylvain Henry -- Josh Meredith -- 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