{-# LANGUAGE TemplateHaskell #-}

module Language.JavaScript.Inline.TH (js) where

import Data.List
import Data.String
import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.JavaScript.Inline.Core
import Language.JavaScript.Inline.JSParse

-- | Generate a 'JSExpr' from inline JavaScript code. The code should be a
-- single expression or a code block with potentially multiple statements (use
-- @return@ to specify the result value in which case). Top-level @await@ is
-- supported.
--
-- Use @$var@ to refer to a Haskell variable @var@. @var@ should be an instance
-- of 'ToJS'.
--
-- Important: when using 'js', GHC calls the @node@ process at compile-time in
-- order to use a JavaScript-based JavaScript parser to extract necessary info.
-- Don't forget to ensure @node@ is available in @PATH@ at compile-time.
js :: QuasiQuoter
js :: QuasiQuoter
js = (String -> Q Exp) -> QuasiQuoter
fromQuoteExp String -> Q Exp
inlineJS

fromQuoteExp :: (String -> Q Exp) -> QuasiQuoter
fromQuoteExp :: (String -> Q Exp) -> QuasiQuoter
fromQuoteExp String -> Q Exp
q =
  QuasiQuoter :: (String -> Q Exp)
-> (String -> Q Pat)
-> (String -> Q Type)
-> (String -> Q [Dec])
-> QuasiQuoter
QuasiQuoter
    { quoteExp :: String -> Q Exp
quoteExp = String -> Q Exp
q,
      quotePat :: String -> Q Pat
quotePat = String -> String -> Q Pat
forall a. HasCallStack => String -> a
error String
"Language.JavaScript.Inline.TH: quotePat",
      quoteType :: String -> Q Type
quoteType = String -> String -> Q Type
forall a. HasCallStack => String -> a
error String
"Language.JavaScript.Inline.TH: quoteType",
      quoteDec :: String -> Q [Dec]
quoteDec = String -> String -> Q [Dec]
forall a. HasCallStack => String -> a
error String
"Language.JavaScript.Inline.TH: quoteDec"
    }

inlineJS :: String -> Q Exp
inlineJS :: String -> Q Exp
inlineJS String
js_code =
  do
    (Bool
is_sync, Bool
is_expr, [String]
hs_vars) <- IO (Bool, Bool, [String]) -> Q (Bool, Bool, [String])
forall a. IO a -> Q a
runIO (IO (Bool, Bool, [String]) -> Q (Bool, Bool, [String]))
-> IO (Bool, Bool, [String]) -> Q (Bool, Bool, [String])
forall a b. (a -> b) -> a -> b
$ String -> IO (Bool, Bool, [String])
jsParse String
js_code
    [|
      mconcat
        $( listE
             ( [ [|
                   fromString
                     $( litE
                          ( stringL
                              ( ( if is_sync
                                    then "(("
                                    else "(async ("
                                )
                                  <> intercalate
                                    ","
                                    ['$' : v | v <- hs_vars]
                                  <> ") => {"
                                  <> ( if is_expr
                                         then
                                           "return "
                                             <> js_code
                                             <> ";"
                                         else js_code
                                     )
                                  <> "})("
                              )
                          )
                      )
                   |]
               ]
                 <> intersperse
                   [|fromString ","|]
                   [ [|toJS $(varE (mkName v))|]
                     | v <- hs_vars
                   ]
                 <> [[|fromString ")"|]]
             )
         )
      |]