{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RebindableSyntax #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-missing-signatures #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fprint-explicit-kinds #-} module Graphics.IxShader.Socket where import Data.List (intercalate) import Data.Promotion.Prelude import Graphics.IxShader.IxShader import Language.Haskell.TH import Prelude hiding (return, (>>), (>>=)) class KnownTypeSymbol a where typeSymbolVal :: Proxy a -> String genKnownTypeSymbol :: TypeQ -> ExpQ -> DecsQ genKnownTypeSymbol t s = [d| instance KnownTypeSymbol $t where typeSymbolVal _ = $s |] -- | A socket is simply a place where you can stick an external expression -- as a string. It's good for named uninitializeds, function application, all sorts of -- stuff. class Socketed a where unSocket :: a -> String socket :: String -> a genSocketed :: TypeQ -> ExpQ -> ExpQ -> DecsQ genSocketed t un con = [d| instance Socketed $t where unSocket = $un socket = $con |] call :: (Socketed a, Socketed b) => String -> a -> b call fncstr a = socket $ concat [fncstr, "(", unSocket a, ")"] call2 :: (Socketed a, Socketed b, Socketed c) => String -> a -> b -> c call2 fncstr a b = socket $ concat [fncstr, "(", unSocket a, ",", unSocket b, ")"] call3 :: (Socketed a, Socketed b, Socketed c, Socketed d) => String -> a -> b -> c -> d call3 fncstr a b c = socket $ concat [fncstr, "(", unSocket a, ",", unSocket b, ",", unSocket c, ")"] call4 :: (Socketed a, Socketed b, Socketed c, Socketed d, Socketed e) => String -> a -> b -> c -> d -> e call4 fncstr a b c d = socket $ concat [fncstr, "(", params, ")"] where params = intercalate "," [unSocket a, unSocket b, unSocket c, unSocket d] callInfix :: (Socketed a, Socketed b, Socketed c) => String -> a -> b -> c callInfix fncstr a b = socket $ concat ["(", unSocket a, fncstr, unSocket b, ")"] toDefinition :: forall a. (Socketed a, KnownTypeSymbol a) => a -> String toDefinition a = unwords [typeSymbolVal $ Proxy @a, unSocket a] -- | Construct a new thing. Declares the thing w/o initialization. define :: (Socketed a, KnownTypeSymbol a) => a -> IxShader ctx i i a define a = nxt (toDefinition a ++ ";") a stringDefinition :: (Socketed k, KnownTypeSymbol k) => k -> k -> String stringDefinition k v = toDefinition k ++ " = " ++ unSocket v ++ ";" -- | Construct a new assignable thing. Initializes it with another thing. defineAs :: (Socketed a, KnownTypeSymbol a) => String -> a -> IxShader ctx i i a defineAs s v = let k = socket s in nxt (stringDefinition k v) k def :: (Socketed a, KnownTypeSymbol a) => String -> a -> IxShader ctx i i a def = defineAs