glsl-0.0.0.1: src/Language/GLSL/Types.hs
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StrictData #-}
{-# OPTIONS_GHC -Wno-name-shadowing #-}
module Language.GLSL.Types where
import Control.Applicative (Applicative (..), (<|>))
import Data.Attoparsec.ByteString.Char8 (IResult (Partial), Parser,
char, decimal, endOfInput,
many1, option, parse,
parseOnly, rational,
scientific, sepBy1)
import Data.List (intersperse)
import qualified Data.Scientific as Sci
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.Builder as LTB
import qualified Data.Text.Lazy.Builder.Int as LTB
import qualified Data.Text.Lazy.Builder.RealFloat as LTB
parseShader :: Annot a => LT.Text -> Either String (GLSL a)
parseShader = parseOnly parseGLSL . T.encodeUtf8 . LT.toStrict
printShader :: Annot a => GLSL a -> LT.Text
printShader = LTB.toLazyText . ppGLSL
data GLSL a = GLSL Version [TopDecl a]
deriving (Show, Functor, Foldable, Traversable)
parseGLSL :: Annot a => Parser (GLSL a)
parseGLSL = GLSL
<$> parseVersion
<*> ("\n" >> many1 parseTopDecl >>= (endOfInput >>) . pure)
ppGLSL :: Annot a => GLSL a -> LTB.Builder
ppGLSL (GLSL v decls) =
ppVersion v
<> "\n" <> ppL ppTopDecl decls
newtype Version = Version Int
deriving (Show)
parseVersion :: Parser Version
parseVersion = Version <$> ("#version " >> decimal)
ppVersion :: Version -> LTB.Builder
ppVersion (Version v) = "#version " <> ppInt v
data TopDecl a
= LayoutDecl LayoutSpec GlobalDecl
| GlobalDecl GlobalDecl
| ProcDecl ProcName [ParamDecl] [StmtAnnot a]
deriving (Show, Functor, Foldable, Traversable)
parseTopDecl :: Annot a => Parser (TopDecl a)
parseTopDecl = layoutDecl <|> globalDecl <|> procDecl
where
layoutDecl = LayoutDecl
<$> ("layout(" >> parseLayoutSpec)
<*> (") " >> parseGlobalDecl)
globalDecl = GlobalDecl
<$> parseGlobalDecl
procDecl = ProcDecl
<$> ("void " >> parseProcName)
<*> ("() " >> pure [])
-- <*> ("{\n" >> many1 parseStmtAnnot)
<*> ("{\n" >> many1 parseStmtAnnot >>= ("}\n" >>) . pure)
ppTopDecl :: Annot a => TopDecl a -> LTB.Builder
ppTopDecl (LayoutDecl e d) = "layout(" <> ppLayoutSpec e <> ") " <> ppGlobalDecl d
ppTopDecl (GlobalDecl d) = ppGlobalDecl d
ppTopDecl (ProcDecl n a b) =
"void " <> ppProcName n
<> "(" <> ppS "," ppParamDecl a <> ") {\n"
<> ppL ppStmtAnnot b
<> "}\n"
data ProcName
= ProcMain
| ProcName NameId
deriving (Show)
parseProcName :: Parser ProcName
parseProcName =
("main" >> pure ProcMain)
<|> ("p" >> ProcName <$> parseNameId)
ppProcName :: ProcName -> LTB.Builder
ppProcName ProcMain = "main"
ppProcName (ProcName n) = "p" <> ppNameId n
data LayoutSpec
= LayoutStd140
| LayoutLocation Int
deriving (Show)
parseLayoutSpec :: Parser LayoutSpec
parseLayoutSpec =
("std140" >> pure LayoutStd140)
<|> ("location = " >> LayoutLocation <$> decimal)
ppLayoutSpec :: LayoutSpec -> LTB.Builder
ppLayoutSpec LayoutStd140 = "std140"
ppLayoutSpec (LayoutLocation l) = "location = " <> ppInt l
data ParamDecl
= Param ParamKind LocalDecl
deriving (Show)
parseParamDecl :: Parser ParamDecl
parseParamDecl = Param
<$> parseParamKind
<*> (" " >> parseLocalDecl)
ppParamDecl :: ParamDecl -> LTB.Builder
ppParamDecl (Param k d) =
ppParamKind k <> " " <> ppLocalDecl d
data ParamKind
= PkIn
| PkOut
| PkInout
deriving (Show)
parseParamKind :: Parser ParamKind
parseParamKind = (char ' ' <|> pure ' ') >>
("in" >> return PkIn) <|>
("out" >> return PkOut) <|>
("inout" >> return PkInout)
ppParamKind :: ParamKind -> LTB.Builder
ppParamKind PkIn = "in"
ppParamKind PkOut = "out"
ppParamKind PkInout = "inout"
data LocalDecl
= LDecl Type NameId (Maybe Expr)
deriving (Show)
parseLocalDecl :: Parser LocalDecl
parseLocalDecl = LDecl
<$> parseType
<*> (" t" >> parseNameId)
<*> (option Nothing (" = " >> Just <$> parseExpr) >>= (";\n" >>) . pure)
ppLocalDecl :: LocalDecl -> LTB.Builder
ppLocalDecl (LDecl t n Nothing) =
ppType t
<> " t" <> ppNameId n <> ";\n"
ppLocalDecl (LDecl t n (Just e)) =
ppType t
<> " t" <> ppNameId n
<> " = " <> ppExpr e <> ";\n"
data GlobalDecl
= GDecl GDeclKind Type Name
deriving (Show)
parseGlobalDecl :: Parser GlobalDecl
parseGlobalDecl = GDecl
<$> parseGDeclKind
<*> (" " >> parseType)
<*> (" " >> parseName >>= (";\n" >>) . pure)
ppGlobalDecl :: GlobalDecl -> LTB.Builder
ppGlobalDecl (GDecl k t n) =
ppGDeclKind k
<> " " <> ppType t
<> " " <> ppName n <> ";\n"
data GDeclKind
= GkIn
| GkOut
| GkUniform
deriving (Show)
parseGDeclKind :: Parser GDeclKind
parseGDeclKind =
("in" >> return GkIn) <|>
("out" >> return GkOut) <|>
("uniform" >> return GkUniform)
ppGDeclKind :: GDeclKind -> LTB.Builder
ppGDeclKind GkIn = "in"
ppGDeclKind GkOut = "out"
ppGDeclKind GkUniform = "uniform"
data Type
= TyBool
| TyFloat
| TySampler2D
| TyVec Int
| TyMat Int Int
| TyStruct NameId [(Type, NameId)]
deriving (Show, Eq)
parseType :: Parser Type
parseType =
("bool" >> return TyBool)
<|> ("float" >> return TyFloat)
<|> ("sampler2D" >> return TySampler2D)
<|> ("vec" >> TyVec <$> decimal)
<|> ("mat" >> TyMat <$> decimal <*> ("x" >> decimal))
<|> tyStruct
where
tyStruct = TyStruct
<$> ("uBlock" >> parseNameId)
<*> (" {\n" >> many1 parseStructMember >>= ("}" >>) . pure)
parseStructMember :: Parser (Type, NameId)
parseStructMember = (,)
<$> parseType
<*> (" u" >> parseNameId >>= (";\n" >>) . pure)
ppType :: Type -> LTB.Builder
ppType TyBool = "bool"
ppType TyFloat = "float"
ppType TySampler2D = "sampler2D"
ppType (TyVec n) = "vec" <> ppInt n
ppType (TyMat n m) = "mat" <> ppInt n <> "x" <> ppInt m
ppType (TyStruct n ms) =
"uBlock" <> ppNameId n
<> " {\n" <> ppL ppStructMember ms <> "}"
where ppStructMember (t, n) = ppType t <> " u" <> ppNameId n <> ";\n"
newtype NameId = NameId Int
deriving (Show, Eq)
parseNameId :: Parser NameId
parseNameId = NameId
<$> decimal
ppNameId :: NameId -> LTB.Builder
ppNameId (NameId n) = ppInt n
data Name
= Name Namespace NameId
deriving (Show)
parseName :: Parser Name
parseName = Name
<$> parseNamespace
<*> parseNameId
ppName :: Name -> LTB.Builder
ppName (Name ns n) = ppNamespace ns <> ppNameId n
data Namespace
= NsT
| NsS
| NsU
| NsVF
| NsIn
| NsOut
deriving (Show, Eq)
parseNamespace :: Parser Namespace
parseNamespace =
("in" >> pure NsIn)
<|> ("out" >> pure NsOut)
<|> ("vf" >> pure NsVF)
<|> (char 't' >> pure NsT)
<|> (char 'u' >> pure NsU)
<|> (char 's' >> pure NsS)
ppNamespace :: Namespace -> LTB.Builder
ppNamespace NsT = "t"
ppNamespace NsS = "s"
ppNamespace NsU = "u"
ppNamespace NsVF = "vf"
ppNamespace NsIn = "in"
ppNamespace NsOut = "out"
data FunName
= PrimMain
| PrimMat3x3
| PrimMat4x4
| PrimVec2
| PrimVec3
| PrimVec4
| PrimPow
| PrimDot
| PrimCos
| PrimAtan
| PrimMod
| PrimAbs
| PrimCross
| PrimLength
| PrimAsin
| PrimSmoothstep
| PrimStep
| PrimFract
| PrimFloor
| PrimSin
| PrimTan
| PrimSqrt
| PrimNormalize
deriving (Show, Eq)
parseFunName :: Parser FunName
parseFunName =
("main" >> pure PrimMain)
<|> ("mat3x3" >> pure PrimMat3x3)
<|> ("mat4x4" >> pure PrimMat4x4)
<|> ("vec2" >> pure PrimVec2)
<|> ("vec3" >> pure PrimVec3)
<|> ("vec4" >> pure PrimVec4)
<|> ("pow" >> pure PrimPow)
<|> ("dot" >> pure PrimDot)
<|> ("cos" >> pure PrimCos)
<|> ("atan" >> pure PrimAtan)
<|> ("mod" >> pure PrimMod)
<|> ("abs" >> pure PrimAbs)
<|> ("cross" >> pure PrimCross)
<|> ("length" >> pure PrimLength)
<|> ("asin" >> pure PrimAsin)
<|> ("smoothstep" >> pure PrimSmoothstep)
<|> ("step" >> pure PrimStep)
<|> ("fract" >> pure PrimFract)
<|> ("floor" >> pure PrimFloor)
<|> ("sin" >> pure PrimSin)
<|> ("tan" >> pure PrimTan)
<|> ("sqrt" >> pure PrimSqrt)
<|> ("normalize" >> pure PrimNormalize)
ppFunName :: FunName -> LTB.Builder
ppFunName PrimMain = "main"
ppFunName PrimMat3x3 = "mat3x3"
ppFunName PrimMat4x4 = "mat4x4"
ppFunName PrimVec2 = "vec2"
ppFunName PrimVec3 = "vec3"
ppFunName PrimVec4 = "vec4"
ppFunName PrimPow = "pow"
ppFunName PrimDot = "dot"
ppFunName PrimCos = "cos"
ppFunName PrimAtan = "atan"
ppFunName PrimMod = "mod"
ppFunName PrimAbs = "abs"
ppFunName PrimCross = "cross"
ppFunName PrimLength = "length"
ppFunName PrimAsin = "asin"
ppFunName PrimSmoothstep = "smoothstep"
ppFunName PrimStep = "step"
ppFunName PrimFract = "fract"
ppFunName PrimFloor = "floor"
ppFunName PrimSin = "sin"
ppFunName PrimTan = "tan"
ppFunName PrimSqrt = "sqrt"
ppFunName PrimNormalize = "normalize"
data Swizzle
= X | Y | Z | W
deriving (Show, Eq)
parseSwizzle :: Parser Swizzle
parseSwizzle =
(char 'x' >> pure X)
<|> (char 'y' >> pure Y)
<|> (char 'z' >> pure Z)
<|> (char 'w' >> pure W)
ppSwizzle :: Swizzle -> LTB.Builder
ppSwizzle X = "x"
ppSwizzle Y = "y"
ppSwizzle Z = "z"
ppSwizzle W = "w"
parseVecIndex :: Parser Swizzle
parseVecIndex =
(char '0' >> pure X)
<|> (char '1' >> pure Y)
<|> (char '2' >> pure Z)
<|> (char '3' >> pure W)
ppVecIndex :: Swizzle -> LTB.Builder
ppVecIndex X = "0"
ppVecIndex Y = "1"
ppVecIndex Z = "2"
ppVecIndex W = "3"
data NameExpr
= NameExpr Name
| UniformExpr NameId NameId
deriving (Show)
parseNameExpr :: Parser NameExpr
parseNameExpr =
UniformExpr <$> (char 'u' >> parseNameId) <*> (".u" >> parseNameId)
<|> NameExpr <$> parseName
ppNameExpr :: NameExpr -> LTB.Builder
ppNameExpr (NameExpr n) = ppName n
ppNameExpr (UniformExpr n m) = "u" <> ppNameId n <> ".u" <> ppNameId m
data Cast
= Cast
| NoCast
deriving (Show)
data ExprAtom
= LitIntExpr Cast Int
| LitFloatExpr Cast Float
| IdentifierExpr NameExpr
| SwizzleExpr NameId Swizzle
| VecIndexExpr NameExpr Swizzle
| MatIndexExpr NameExpr Swizzle Swizzle
deriving (Show)
parseExprAtom :: Parser ExprAtom
parseExprAtom =
litNumber <$> scientific
<|> LitIntExpr Cast <$> ("int(" >> decimal >>= (")" >>) . pure)
<|> LitFloatExpr Cast <$> ("float(" >> rational >>= (")" >>) . pure)
<|> SwizzleExpr <$> (char 't' >> parseNameId) <*> (char '.' >> parseSwizzle)
<|> MatIndexExpr <$> parseNameExpr <*> ("[" >> parseVecIndex) <*> ("][" >> parseVecIndex >>= ("]" >>) . pure)
<|> VecIndexExpr <$> parseNameExpr <*> ("[" >> parseVecIndex >>= ("]" >>) . pure)
<|> IdentifierExpr <$> parseNameExpr
where
litNumber s =
let e = Sci.base10Exponent s
c = Sci.coefficient s
in if e >= 0
then LitIntExpr NoCast (fromInteger (c * 10 ^ e))
else LitFloatExpr NoCast (Sci.toRealFloat s)
ppExprAtom :: ExprAtom -> LTB.Builder
ppExprAtom (LitIntExpr Cast i) = "int(" <> ppInt i <> ")"
ppExprAtom (LitIntExpr NoCast i) = ppInt i
ppExprAtom (LitFloatExpr Cast n) = "float(" <> ppFloat n <> ")"
ppExprAtom (LitFloatExpr NoCast r) = ppFloat r
ppExprAtom (IdentifierExpr n) = ppNameExpr n
ppExprAtom (SwizzleExpr n m) = "t" <> ppNameId n <> "." <> ppSwizzle m
ppExprAtom (VecIndexExpr n i) = ppNameExpr n <> "[" <> ppVecIndex i <> "]"
ppExprAtom (MatIndexExpr n i j) = ppNameExpr n <> "[" <> ppVecIndex i <> "]" <> "[" <> ppVecIndex j <> "]"
data Expr
= UnaryExpr UnaryOp ExprAtom
| BinaryExpr ExprAtom BinaryOp ExprAtom
| FunCallExpr FunName [ExprAtom]
| TextureExpr ExprAtom ExprAtom ExprAtom
| AtomExpr ExprAtom
deriving (Show)
parseExpr :: Parser Expr
parseExpr =
(char '(' >> operatorExpr >>= (char ')' >>) . pure)
<|> textureExpr
<|> funCallExpr
<|> AtomExpr <$> parseExprAtom
where
operatorExpr =
BinaryExpr <$> parseExprAtom <*> parseBinaryOp <*> parseExprAtom
<|> UnaryExpr <$> parseUnaryOp <*> parseExprAtom
textureExpr = TextureExpr
<$> ("texture(" >> parseExprAtom)
<*> (",vec2(" >> parseExprAtom)
<*> ("," >> parseExprAtom >>= ("))" >>) . pure)
funCallExpr = FunCallExpr
<$> parseFunName
<*> (char '(' >> sepBy1 parseExprAtom (char ',') >>= (char ')' >>) . pure)
ppExpr :: Expr -> LTB.Builder
ppExpr (AtomExpr e) = ppExprAtom e
ppExpr (UnaryExpr o e) = "(" <> ppUnaryOp o <> ppExprAtom e <> ")"
ppExpr (BinaryExpr l o r) = "(" <> ppExprAtom l <> ppBinaryOp o <> ppExprAtom r <> ")"
ppExpr (FunCallExpr n args) = ppFunName n <> "(" <> ppS "," ppExprAtom args <> ")"
ppExpr (TextureExpr t x y) = "texture(" <> ppExprAtom t <> ",vec2(" <> ppExprAtom x <> "," <> ppExprAtom y <> "))"
data BinaryOp
= BOpPlus
| BOpMinus
| BOpMul
| BOpDiv
| BOpGE
| BOpGT
| BOpLE
| BOpLT
| BOpAnd
| BOpOr
deriving (Show, Eq)
parseBinaryOp :: Parser BinaryOp
parseBinaryOp =
(char '+' >> pure BOpPlus)
<|> (char '-' >> pure BOpMinus)
<|> (char '*' >> pure BOpMul)
<|> (char '/' >> pure BOpDiv)
<|> (">=" >> pure BOpGE)
<|> (">" >> pure BOpGT)
<|> ("<=" >> pure BOpLE)
<|> ("<" >> pure BOpLT)
<|> ("&&" >> pure BOpAnd)
<|> ("||" >> pure BOpOr)
ppBinaryOp :: BinaryOp -> LTB.Builder
ppBinaryOp BOpPlus = "+"
ppBinaryOp BOpMinus = "-"
ppBinaryOp BOpMul = "*"
ppBinaryOp BOpDiv = "/"
ppBinaryOp BOpGE = ">="
ppBinaryOp BOpGT = ">"
ppBinaryOp BOpLE = "<="
ppBinaryOp BOpLT = "<"
ppBinaryOp BOpAnd = "&&"
ppBinaryOp BOpOr = "||"
data UnaryOp
= UOpMinus
| UOpNot
deriving (Show, Eq)
parseUnaryOp :: Parser UnaryOp
parseUnaryOp =
(char '-' >> pure UOpMinus)
<|> (char '!' >> pure UOpMinus)
ppUnaryOp :: UnaryOp -> LTB.Builder
ppUnaryOp UOpMinus = "-"
ppUnaryOp UOpNot = "!"
data StmtAnnot a = SA
{ annot :: a
, unAnnot :: Stmt a
}
deriving (Show, Functor, Foldable, Traversable)
instance Applicative StmtAnnot where
pure a = SA a (pure a)
liftA2 f a b = SA (f (annot a) (annot b)) $ liftA2 f (unAnnot a) (unAnnot b)
parseStmtAnnot :: Annot a => Parser (StmtAnnot a)
parseStmtAnnot = SA <$> parseAnnot <*> parseStmt
ppStmtAnnot :: Annot a => StmtAnnot a -> LTB.Builder
ppStmtAnnot (SA a s) =
maybe "" (\ltb -> "// " <> ltb <> "\n") (ppAnnot a) <> ppStmt s
data Stmt a
= AssignStmt Name Expr
| DeclStmt LocalDecl
| EmitStmt Emit
| IfStmt NameId [StmtAnnot a] [StmtAnnot a]
deriving (Show, Functor, Foldable, Traversable)
instance Applicative Stmt where
-- Arbitrary decision because "pure" doesn't really make sense.
pure _ = EmitStmt EmitFragDepth
liftA2 f (IfStmt n t1 e1) (IfStmt _ t2 e2) = IfStmt n
((zipWith . liftA2) f t1 t2)
((zipWith . liftA2) f e1 e2)
liftA2 _ (AssignStmt n e) _ = AssignStmt n e
liftA2 _ (DeclStmt d) _ = DeclStmt d
liftA2 _ (EmitStmt e) _ = EmitStmt e
liftA2 _ (IfStmt n _ _) _ = IfStmt n [] []
parseStmt :: Annot a => Parser (Stmt a)
parseStmt =
IfStmt <$> ("if(t" >> parseNameId >>= ("){\n" >>) . pure)
<*> many1 parseStmtAnnot
<*> ("} else {\n" >> many1 parseStmtAnnot >>= ("}\n" >>) . pure)
<|> AssignStmt <$> parseName <*> (" = " >> parseExpr >>= (";\n" >>) . pure)
<|> DeclStmt <$> parseLocalDecl
<|> EmitStmt <$> parseEmit
ppStmt :: Annot a => Stmt a -> LTB.Builder
ppStmt (AssignStmt n e) = ppName n <> " = " <> ppExpr e <> ";\n"
ppStmt (DeclStmt d) = ppLocalDecl d
ppStmt (EmitStmt e) = ppEmit e
ppStmt (IfStmt c t e) =
"if(t" <> ppNameId c <> "){\n"
<> ppL ppStmtAnnot t
<> "} else {\n"
<> ppL ppStmtAnnot e
<> "}\n"
data Emit
= EmitPosition Expr
| EmitFragDepth
deriving (Show)
parseEmit :: Parser Emit
parseEmit =
EmitPosition <$> ("gl_Position = " >> parseExpr >>= (";\n" >>) . pure)
<|> ("gl_FragDepth = gl_FragCoord[2];\n" >> pure EmitFragDepth)
ppEmit :: Emit -> LTB.Builder
ppEmit (EmitPosition e) = "gl_Position = " <> ppExpr e <> ";\n"
ppEmit EmitFragDepth = "gl_FragDepth = gl_FragCoord[2];\n"
ppInt :: Int -> LTB.Builder
ppInt = LTB.decimal
ppFloat :: Float -> LTB.Builder
ppFloat = LTB.realFloat
ppL :: (a -> LTB.Builder) -> [a] -> LTB.Builder
ppL printer = mconcat . map printer
ppS :: LTB.Builder -> (a -> LTB.Builder) -> [a] -> LTB.Builder
ppS sep printer = mconcat . intersperse sep . map printer
class Annot a where
parseAnnot :: Parser a
ppAnnot :: a -> Maybe LTB.Builder
instance Annot () where
parseAnnot = pure ()
ppAnnot = const Nothing
instance (Annot a, Annot b) => Annot (a, b) where
parseAnnot = error "not implemented"
ppAnnot (a, b) = do
ppA <- ppAnnot a
ppB <- ppAnnot b
return $ "(" <> ppA <> ", " <> ppB <> ")"
----------------------------------
parseTest :: Show a => Parser a -> LT.Text -> IO ()
parseTest p input =
let r = show . fromPartial . parse p . T.encodeUtf8 . LT.toStrict $ input in
if length r > 600
then
let start = take 500 r
end = reverse $ take 100 $ reverse r
in
putStrLn $ start <> " ... " <> end
else putStrLn r
where
fromPartial (Partial cont) = cont mempty
fromPartial r = r
t :: Show a => Parser a -> String -> IO ()
t p = parseTest p . LT.pack
pp :: (a -> LTB.Builder) -> a -> String
pp printer = LT.unpack . LTB.toLazyText . printer
ppl :: (a -> LTB.Builder) -> [a] -> String
ppl printer = LT.unpack . LTB.toLazyText . ppL printer