{-# LANGUAGE NamedFieldPuns      #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Clr.FSharp.Inline
  ( fsharp
  , fsharp'
  ) where

import           Clr.FSharp.Gen
import           Clr.Inline.Config
import           Clr.Inline.Quoter
import           Language.Haskell.TH
import           Language.Haskell.TH.Quote

-- | F# declaration and expression quasiquoter.
--   Declarations can include open statements, types or even modules.
--  Example declaration:
--
-- @
-- [fsharp|
--   open System
--   open System.Collections.Generic
--   module Globals =
--      let mutable today = DateTime.Today
-- |]
-- @
--
--  Expressions are wrapped in a curly braces block @{}@ that
--  fixes the return type. An F# expression quotation can refer to
--  a Haskell binding @x@ using the syntax @($x:type)@ where type is
--  a string denoting an F# type and is only required on the first usage, and the parentheses
--  are optional. F# types are mapped to Haskell types via the 'Quotable' class.
--  An antiquotation @$x:type@
--  is well-scoped if there exists a variable @x@ with a Haskell type @U@ in
--  the Haskell context such that there exists an instance
--  @Quotable type clr marshall U@ for some @clr@ and @marshall@.
--
--  An F# expression returns an IO computation that
--  produces a value of the quoted result type if said type is 'Quotable'.
--  Example expressions:
--
-- @
-- hello :: IO (Int, Clr \"System.DateTime")
-- hello = do
--   let year = 2017 :: Int
--   aClr <- [fsharp| DateTime{ DateTime($year:int,04,10)} |]
--   anInt <- [fsharp| int{ ($aClr:System.DateTime).Year + $year:int + $year}|]
--   return (anInt, anClr)
-- @
--
--  CLR Reference types are modelled in Haskell as 'Clr' values, indexed with the
--  name of their F# type as a type level symbol. String equivalence is a poor
--  substitute for type equality, so for two 'Clr' values to have the same type
--  they must be indexed by exactly the same string.
--
--  This quasiquoter is implicitly configured with the 'defaultConfig'.
fsharp :: QuasiQuoter
fsharp = fsharp' defaultConfig

-- | Explicit configuration version of 'fsharp'.
fsharp' :: ClrInlineConfig -> QuasiQuoter
fsharp' cfg = QuasiQuoter
    { quoteExp  = fsharpExp cfg
    , quotePat  = error "Clr.FSharp.Inline: quotePat"
    , quoteType = error "Clr.FSharp.Inline: quoteType"
    , quoteDec  = fsharpDec cfg
    }

fsharpExp :: ClrInlineConfig -> String -> Q Exp
fsharpExp cfg =
  clrQuoteExp
    name
    (compile cfg)

fsharpDec :: ClrInlineConfig -> String -> Q [Dec]
fsharpDec = clrQuoteDec name . compile