packages feed

spirv-headers (empty) → 0.1.0.0

raw patch · 8 files changed

+430/−0 lines, 8 filesdep +aesondep +basedep +bytestringsetup-changed

Dependencies added: aeson, base, bytestring, containers, directory, filepath, optparse-applicative, shower, spirv-headers, text

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog for `spirv-headers`++All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and this project adheres to the+[Haskell Package Versioning Policy](https://pvp.haskell.org/).++## Unreleased++## 0.1.0.0 - YYYY-MM-DD
+ LICENSE view
@@ -0,0 +1,26 @@+Copyright 2024 IC Rainbow++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1.  Redistributions of source code must retain the above copyright notice, this+    list of conditions and the following disclaimer.++2.  Redistributions in binary form must reproduce the above copyright notice,+    this list of conditions and the following disclaimer in the documentation+    and/or other materials provided with the distribution.++3.  Neither the name of the copyright holder nor the names of its contributors+    may be used to endorse or promote products derived from this software+    without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,1 @@+# spirv-headers
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/gen-spirv-enum/Main.hs view
@@ -0,0 +1,219 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Main (main) where++import Options.Applicative++import Data.Char (toUpper)+import Data.Bifunctor (first)+import Data.Bits (bit)+import Data.ByteString.Builder (Builder)+import Data.ByteString.Builder qualified as Builder+import Data.List qualified as List+import Data.Map.Strict qualified as Map+import Data.SpirV.Headers.Enum qualified as Enum+import Data.Text (Text)+import Data.Text qualified as Text+import Data.Text.Encoding (encodeUtf8Builder)+import System.Directory (createDirectoryIfMissing)+import System.FilePath (joinPath, (</>), (<.>))++main :: IO ()+main = execParser (info optionsP idm) >>= run++run :: Options -> IO ()+run Options{..} = do+  Enum.Spv{meta=_, enum} <- Enum.decode input >>= either fail pure+  genIndexModule output modulePrefix $ List.sortOn Enum.name enum+  mapM_ (genEnumModule output modulePrefix) enum++genIndexModule :: FilePath -> [Text] -> [Enum.Enum] -> IO ()+genIndexModule base path enums = do+  createDirectoryIfMissing True dirPath+  putStrLn filePath+  Builder.writeFile filePath body+  where+    filePath = dirPath <.> "hs"+    dirPath = base </> joinPath (map Text.unpack path)+    modulePath = encodeUtf8Builder $ Text.intercalate "." path+    body :: Builder+    body = mconcat $ List.intersperse "\n" $ concat+      [ [ "module " <> modulePath+        ]+      , reexports+      , [ "  ) where"+        , ""+        ]+      , imports+      ]+    reexports = do+      (Enum.Enum{name}, prefix) <- zip enums ("  (" : repeat "  ,")+      pure $ prefix <> " module " <> qualified name+    imports = do+      Enum.Enum{name, type_} <- enums+      let+        tn = encodeUtf8Builder name+        imported = case type_ of+          Enum.Value -> tn <> "(..)"+          Enum.Bit -> tn <> ", " <> tn <> "Bits(..)"+      pure $ "import " <> qualified name <> " (" <> imported <> ")"+    qualified :: Text -> Builder+    qualified =+      if null path then+        encodeUtf8Builder+      else+        \name -> modulePath <> "." <> encodeUtf8Builder name++genEnumModule :: FilePath -> [Text] -> Enum.Enum -> IO ()+genEnumModule base path Enum.Enum{name, type_, values} = do+  createDirectoryIfMissing True dirPath+  putStrLn filePath+  Builder.writeFile filePath body+  where+    filePath = dirPath </> Text.unpack name <.> "hs"+    dirPath = base </> joinPath (map Text.unpack path)+    modulePath = Text.intercalate "." $ path <> [name]+    items = map (first pascalCase) . List.sortOn snd $ Map.assocs values+      where+        pascalCase t = case Text.uncons t of+          Just (c, t') | c /= c' -> Builder.char7 c' <> encodeUtf8Builder t'+            where+              c' = toUpper c+          _ -> encodeUtf8Builder t++    body :: Builder+    body = mconcat $ List.intersperse "\n" $ concat+      [ prologue+      , imports+      , [""]+      , typeDecl+      ]+    prologue :: [Builder]+    prologue =+      [ "{-# LANGUAGE GeneralizedNewtypeDeriving #-}"+      , "{-# LANGUAGE DerivingStrategies #-}"+      , "{-# LANGUAGE PatternSynonyms #-}"+      , "{-# LANGUAGE TypeSynonymInstances #-}"+      , ""+      , "module " <> encodeUtf8Builder modulePath <> " where"+      , ""+      ]+    imports :: [Builder]+    imports = case type_ of+      Enum.Value ->+        [ "import Data.Int (Int32)"+        , "import Foreign.Storable (Storable)"+        ]+      Enum.Bit ->+        [ "import Data.Bits (Bits, FiniteBits, (.|.))"+        , "import Data.Word (Word32)"+        , "import Foreign.Storable (Storable)"+        ]+    typeDecl :: [Builder]+    typeDecl = case type_ of+      Enum.Value ->+        [ "newtype " <> valueName <> " = " <> valueName <> " Int32"+        , "  deriving newtype (Eq, Ord, Storable)"+        ] <> instanceShow <> patterns+        where+          valueName = encodeUtf8Builder name+          instanceShow =+            [ ""+            , "instance Show " <> valueName <> " where"+            , "  showsPrec p (" <> valueName <> " v) = case v of"+            ] <> showCase <> showUnknown+          showCase = do+            (k, v) <- List.nubBy (\(_, a) (_, b) -> a == b) items+            pure $ "    " <> Builder.integerDec v <> " -> showString \"" <> k <> "\""+          showUnknown =+            [ "    x -> showParen (p > 10) $ showString \"" <> valueName <> " \" . showsPrec (p + 1) x"+            ]+          patterns :: [Builder]+          patterns = do+            (k, v) <- items+            [ ""+              , "pattern " <> k <> " :: " <> valueName+              , "pattern " <> k <> " = " <> valueName <> " " <> Builder.integerDec v+              ]+      Enum.Bit ->+        [ "type " <> flagName <> " = " <> bitsName+        , ""+        , "newtype " <> bitsName <> " = " <> bitsName <> " Word32"+        , "  deriving newtype (Eq, Ord, Storable, Bits, FiniteBits)"+        ] <> instanceShow <> instanceSemigroup <> instanceMonoid <> patterns+        where+          flagName = encodeUtf8Builder name+          bitsName = encodeUtf8Builder name <> "Bits"+          instanceShow =+            []+          instanceSemigroup =+            [ ""+            , "instance Semigroup " <> flagName <> " where"+            , "  (" <> bitsName <> " a) <> (" <> bitsName <> " b) = " <> bitsName <> " (a .|. b)"+            ]+          instanceMonoid =+            [ ""+            , "instance Monoid " <> flagName <> " where"+            , "  mempty = " <> bitsName <> " 0"+            ]+          patterns :: [Builder]+          patterns = do+            (k, v) <- items+            [ ""+              , "pattern " <> k <> " :: " <> bitsName+              , "pattern " <> k <> " = " <> bitsName <> " 0x" <> Builder.word32HexFixed (bit $ fromIntegral v)+              ]++data Options = Options+  { input :: FilePath+  , output :: FilePath+  , modulePrefix :: [Text]+  }++defaultOptions :: Options+defaultOptions = Options+  { input = "SPIRV-Headers/include/spirv/unified1/spirv.json"+  , output = "spirv-enum/src"+  , modulePrefix = ["Data", "SpirV", "Enum"]+  }++optionsP :: Parser Options+optionsP = do+  input <- strOption $ mconcat+    [ long "input"+    , short 'i'+    , metavar "FILE"+    , help "Path to spirv.json file."+    , value $ input defaultOptions+    , showDefault+    ]+  output <- strOption $ mconcat+    [ long "output"+    , short 'o'+    , metavar "DIR"+    , help "Root path for generated modules."+    , value $ output defaultOptions+    , showDefault+    ]+  modulePrefix <- option (eitherReader $ modulePrefixP . Text.pack) $ mconcat+    [ long "module-prefix"+    , short 'm'+    , metavar "HASKELL.MODULE.PATH"+    , help "Name and directory prefix for generated modules."+    , value $ modulePrefix defaultOptions+    , showDefaultWith (Text.unpack . Text.intercalate ".")+    ]+  pure Options{..}++modulePrefixP :: Text -> Either String [Text]+modulePrefixP = \case+  "" -> Left "Empty path"+  s | ".." `Text.isInfixOf` s -> Left "Empty module segment"+  s | ('/' `Text.elem` s) || ('\\' `Text.elem` s) -> Left "Module paths are dotted, not slashed."+  s | not $ null (drop 1 $ Text.words s) -> Left "Module paths can't contain spaces."+  s -> Right $ Text.split (== '.') s
+ spirv-headers.cabal view
@@ -0,0 +1,85 @@+cabal-version: 2.2++-- This file has been generated from package.yaml by hpack version 0.36.0.+--+-- see: https://github.com/sol/hpack++name:           spirv-headers+version:        0.1.0.0+synopsis:       Types and generator for SPIR-V JSON spec.+category:       Graphics+author:         IC Rainbow+maintainer:     aenor.realm@gmail.com+copyright:      2024 IC Rainbow+license:        BSD-3-Clause+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+extra-doc-files:+    CHANGELOG.md++source-repository head+  type: git+  location: https://gitlab.com/dpwiz/spirv-headers++flag lib-only+  manual: True+  default: True++library+  exposed-modules:+      Data.SpirV.Headers.Enum+  other-modules:+      Paths_spirv_headers+  autogen-modules:+      Paths_spirv_headers+  hs-source-dirs:+      src+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+  build-depends:+      aeson+    , base >=4.7 && <5+    , containers+    , text+  default-language: Haskell2010++executable gen-spirv-enum+  main-is: Main.hs+  other-modules:+      Paths_spirv_headers+  autogen-modules:+      Paths_spirv_headers+  hs-source-dirs:+      app/gen-spirv-enum+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+  build-depends:+      base >=4.7 && <5+    , bytestring+    , containers+    , directory+    , filepath+    , optparse-applicative+    , spirv-headers+    , text+  default-language: Haskell2010+  if flag(lib-only)+    buildable: False++test-suite spirv-headers-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_spirv_headers+  autogen-modules:+      Paths_spirv_headers+  hs-source-dirs:+      test+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+  build-depends:+      base >=4.7 && <5+    , shower+    , spirv-headers+  default-language: Haskell2010+  if flag(lib-only)+    buildable: False
+ src/Data/SpirV/Headers/Enum.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE StrictData #-}++module Data.SpirV.Headers.Enum+  ( decode+  , SpirvJson(..)+  , Spv(..)+  , Meta(..)+  , Enum(..)+  , Type(..)+  ) where++import Prelude hiding (Enum)++import Data.Aeson (eitherDecodeFileStrict)+import Data.Aeson.Types (FromJSON(..), Options(..), defaultOptions, genericParseJSON)+import Data.Char (toUpper)+import Data.Map.Strict (Map)+import Data.Text (Text)+import Data.Word (Word32)+import GHC.Generics (Generic)++decode :: FilePath -> IO (Either String Spv)+decode fp = fmap spv <$> eitherDecodeFileStrict fp++newtype SpirvJson = SpirvJson+  { spv :: Spv+  }+  deriving (Eq, Show, Generic)++instance FromJSON SpirvJson++data Spv = Spv+  { meta :: Meta+  , enum :: [Enum]+  }+  deriving (Eq, Show, Generic)++instance FromJSON Spv++data Meta = Meta+  { comment :: [[Text]]+  , magicNumber :: Int+  , version :: Int+  , revision :: Int+  , opCodeMask :: Word32+  , wordCountShift :: Int+  }+  deriving (Eq, Show, Generic)++instance FromJSON Meta where+  parseJSON = genericParseJSON pascalCase++data Enum = Enum+  { name :: Text+  , type_ :: Type+  , values :: Map Text Integer+  }+  deriving (Eq, Show, Generic)++instance FromJSON Enum where+  parseJSON = genericParseJSON pascalCase++data Type+  = Value+  | Bit+  deriving (Eq, Show, Generic)++instance FromJSON Type++pascalCase :: Options+pascalCase = defaultOptions+  { fieldLabelModifier = \case+      [] -> []+      "type_" -> "Type"+      x : xs -> toUpper x : xs+  }
+ test/Spec.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE ImportQualifiedPost #-}++import Data.SpirV.Headers.Enum qualified as Enum++import Shower (printer)++main :: IO ()+main = Enum.decode "SPIRV-Headers/include/spirv/unified1/spirv.json" >>= either fail printer