module Vulkan.Utils.ShaderQQ.HLSL
  ( hlsl
  , insertLineDirective
  ) where

import           Language.Haskell.TH
import           Language.Haskell.TH.Quote
import           Vulkan.Utils.Internal                  ( badQQ )
import           Vulkan.Utils.ShaderQQ.Interpolate

-- | 'hlsl' is a QuasiQuoter which produces HLSL source code with a @#line@
-- directive inserted so that error locations point to the correct location in
-- the Haskell source file. It also permits basic string interpolation.
--
-- - Interpolated variables are prefixed with @$@
-- - They can optionally be surrounded with braces like @${foo}@
-- - Interpolated variables are converted to strings with 'show'
-- - To escape a @$@ use @\\$@
--
-- An explicit example (@<interactive>@ is from doctest):
--
-- >>> let foo = 450 :: Int in [hlsl|const float foo = $foo|]
-- "#line 77 \"<interactive>\"\nconst float foo = 450"
--
-- Note that line number will be thrown off if any of the interpolated
-- variables contain newlines.
hlsl :: QuasiQuoter
hlsl :: QuasiQuoter
hlsl = (String -> QuasiQuoter
badQQ String
"hlsl")
  { quoteExp :: String -> Q Exp
quoteExp = \String
s -> do
                 Loc
loc <- Q Loc
location
                 -- Insert the directive here, `compileShaderQ` will insert
                 -- another one, but it's before this one, so who cares.
                 let codeWithLineDirective :: String
codeWithLineDirective = String -> Loc -> String
insertLineDirective String
s Loc
loc
                 String -> Q Exp
interpExp String
codeWithLineDirective
  }

-- Insert a #line directive with the specified location at the top of the file
insertLineDirective :: String -> Loc -> String
insertLineDirective :: String -> Loc -> String
insertLineDirective String
code Loc {String
CharPos
loc_end :: Loc -> CharPos
loc_filename :: Loc -> String
loc_module :: Loc -> String
loc_package :: Loc -> String
loc_start :: Loc -> CharPos
loc_end :: CharPos
loc_start :: CharPos
loc_module :: String
loc_package :: String
loc_filename :: String
..} =
  let lineDirective :: String
lineDirective =
        String
"#line " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show (CharPos -> Int
forall a b. (a, b) -> a
fst CharPos
loc_start) String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" \"" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
loc_filename String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"\""
  in  String
lineDirective String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"\n" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
code