packages feed

godot-megaparsec 0.1.0.0 → 0.2.0.0

raw patch · 4 files changed

+126/−55 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Godot.Parser.Resource: instance Godot.Parser.Resource.HasDescriptor Godot.Parser.Resource.GdnsParsed Godot.Parser.Resource.GdnsDescriptor
- Godot.Parser.Resource: instance Godot.Parser.Resource.HasDescriptor Godot.Parser.Resource.TscnParsed Godot.Parser.Resource.TscnDescriptor
- Godot.Parser.Resource: instance Godot.Parser.Resource.HasFormat Godot.Parser.Resource.GdnsDescriptor GHC.Types.Int
- Godot.Parser.Resource: instance Godot.Parser.Resource.HasFormat Godot.Parser.Resource.TscnDescriptor GHC.Types.Int
- Godot.Parser.Resource: instance Godot.Parser.Resource.HasLoadSteps Godot.Parser.Resource.GdnsDescriptor GHC.Types.Int
- Godot.Parser.Resource: instance Godot.Parser.Resource.HasLoadSteps Godot.Parser.Resource.TscnDescriptor GHC.Types.Int
- Godot.Parser.Resource: instance Godot.Parser.Resource.HasSections Godot.Parser.Resource.GdnsParsed [Godot.Parser.Resource.GodotSection]
- Godot.Parser.Resource: instance Godot.Parser.Resource.HasSections Godot.Parser.Resource.TscnParsed [Godot.Parser.Resource.GodotSection]
- Godot.Parser.Resource: instance Godot.Parser.Resource.HasTy Godot.Parser.Resource.GdnsDescriptor Data.Text.Internal.Text
+ Godot.Parser.Resource: Other :: OtherParsed -> GodotParsed
+ Godot.Parser.Resource: OtherDescriptor :: Text -> HashMap Text GodotValue -> OtherDescriptor
+ Godot.Parser.Resource: OtherParsed :: OtherDescriptor -> [GodotSection] -> OtherParsed
+ Godot.Parser.Resource: [_otherDescriptorHeaderName] :: OtherDescriptor -> Text
+ Godot.Parser.Resource: [_otherDescriptorHeaders] :: OtherDescriptor -> HashMap Text GodotValue
+ Godot.Parser.Resource: [_otherParsedDescriptor] :: OtherParsed -> OtherDescriptor
+ Godot.Parser.Resource: [_otherParsedSections] :: OtherParsed -> [GodotSection]
+ Godot.Parser.Resource: data OtherDescriptor
+ Godot.Parser.Resource: data OtherParsed
+ Godot.Parser.Resource: godotParser :: Parser GodotParsed
+ Godot.Parser.Resource: instance GHC.Generics.Generic Godot.Parser.Resource.OtherDescriptor
+ Godot.Parser.Resource: instance GHC.Generics.Generic Godot.Parser.Resource.OtherParsed
+ Godot.Parser.Resource: instance GHC.Show.Show Godot.Parser.Resource.OtherDescriptor
+ Godot.Parser.Resource: instance GHC.Show.Show Godot.Parser.Resource.OtherParsed
+ Godot.Parser.Resource: otherParser :: Parser OtherParsed
+ Godot.Parser.Resource.Lens: class HasDescriptor s a | s -> a
+ Godot.Parser.Resource.Lens: class HasFormat s a | s -> a
+ Godot.Parser.Resource.Lens: class HasHeaderName s a | s -> a
+ Godot.Parser.Resource.Lens: class HasHeaders s a | s -> a
+ Godot.Parser.Resource.Lens: class HasLoadSteps s a | s -> a
+ Godot.Parser.Resource.Lens: class HasSections s a | s -> a
+ Godot.Parser.Resource.Lens: class HasTy s a | s -> a
+ Godot.Parser.Resource.Lens: descriptor :: HasDescriptor s a => Lens' s a
+ Godot.Parser.Resource.Lens: format :: HasFormat s a => Lens' s a
+ Godot.Parser.Resource.Lens: headerName :: HasHeaderName s a => Lens' s a
+ Godot.Parser.Resource.Lens: headers :: HasHeaders s a => Lens' s a
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasDescriptor Godot.Parser.Resource.GdnsParsed Godot.Parser.Resource.GdnsDescriptor
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasDescriptor Godot.Parser.Resource.OtherParsed Godot.Parser.Resource.OtherDescriptor
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasDescriptor Godot.Parser.Resource.TscnParsed Godot.Parser.Resource.TscnDescriptor
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasFormat Godot.Parser.Resource.GdnsDescriptor GHC.Types.Int
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasFormat Godot.Parser.Resource.TscnDescriptor GHC.Types.Int
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasHeaderName Godot.Parser.Resource.OtherDescriptor Data.Text.Internal.Text
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasHeaders Godot.Parser.Resource.OtherDescriptor (Data.HashMap.Internal.HashMap Data.Text.Internal.Text Godot.Parser.Resource.GodotValue)
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasLoadSteps Godot.Parser.Resource.GdnsDescriptor GHC.Types.Int
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasLoadSteps Godot.Parser.Resource.TscnDescriptor GHC.Types.Int
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasSections Godot.Parser.Resource.GdnsParsed [Godot.Parser.Resource.GodotSection]
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasSections Godot.Parser.Resource.OtherParsed [Godot.Parser.Resource.GodotSection]
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasSections Godot.Parser.Resource.TscnParsed [Godot.Parser.Resource.GodotSection]
+ Godot.Parser.Resource.Lens: instance Godot.Parser.Resource.Lens.HasTy Godot.Parser.Resource.GdnsDescriptor Data.Text.Internal.Text
+ Godot.Parser.Resource.Lens: loadSteps :: HasLoadSteps s a => Lens' s a
+ Godot.Parser.Resource.Lens: sections :: HasSections s a => Lens' s a
+ Godot.Parser.Resource.Lens: ty :: HasTy s a => Lens' s a

Files

bench/Main.hs view
@@ -13,7 +13,11 @@  main :: IO () main = do-  let parseFile f = fromRight (error $ "Unable to parse " <> f) . P.parse tscnParser "" <$> T.readFile f+  let parseFile f =+        fromRight (error $ "Unable to parse " <> f) . P.parse godotParser ""+        <$> T.readFile f   defaultMain [ bench "EntityMap" $ whnfIO (parseFile "bench/EntityMap.tscn")               , bench "Geography" $ whnfIO (parseFile "bench/Geography.tscn")+              , bench "MarketActor" $ whnfIO (parseFile "bench/MarketActor.tscn")+              , bench "Other" $ whnfIO (parseFile "bench/Other.other")               , bench "BigFile" $ whnfIO (parseFile "bench/BigFile.tscn")]
godot-megaparsec.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               godot-megaparsec-version:            0.1.0.0+version:            0.2.0.0  -- A short (one-line) description of the package. synopsis:           Megaparsec parser for Godot `tscn` and `gdns` files.@@ -29,7 +29,9 @@   location: https://github.com/WinstonHartnett/godot-megaparsec.git  library-  exposed-modules:  Godot.Parser.Resource+  exposed-modules:+    Godot.Parser.Resource+    Godot.Parser.Resource.Lens    -- Modules included in this library but not exported.   -- other-modules:
src/Godot/Parser/Resource.hs view
@@ -1,30 +1,39 @@-{-# LANGUAGE DeriveGeneric #-}+{-|+Module      : Godot.Parser.Resource+Description : Megaparsec parser for the Godot resource file format.+Copyright   : (c) Winston Hartnett, 2021+License     : GPL-3+Maintainer  : whartnett@gmail.com+Stability   : experimental+Portability : POSIX -{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE MultiParamTypeClasses #-}+A parser for Godot resource file formats. Currently only supports auto-generated+`tscn` and `gdns` files.+-}+{-# LANGUAGE DeriveGeneric #-}  {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}  module Godot.Parser.Resource   (GodotValue(..)   ,GodotSection(..)   ,TscnDescriptor(..)   ,TscnParsed(..)+  ,OtherDescriptor(..)+  ,OtherParsed(..)   ,GdnsDescriptor(..)   ,GdnsParsed(..)   ,GodotParsed(..)   ,tscnParser-  ,gdnsParser) where+  ,gdnsParser+  ,otherParser+  ,godotParser) where  import           Control.Applicative        ((<|>),liftA2)-import           Control.Lens import           Control.Monad              (unless)  import           Data.Char                  (isAlphaNum,isDigit,isUpper) import           Data.Either                (fromRight)-import           Data.Foldable              (foldl') import           Data.Functor               (($>)) import qualified Data.HashMap.Lazy          as M import qualified Data.HashSet               as S@@ -35,6 +44,8 @@  import           GHC.Generics               (Generic) +import           Prelude                    hiding (exponent)+ import qualified Text.Megaparsec            as P import qualified Text.Megaparsec.Char       as P import qualified Text.Megaparsec.Char.Lexer as P (decimal,signed)@@ -103,17 +114,19 @@ godotValueP = do   nc <- T.head . P.stateInput <$> P.getParserState   case nc of-    '"'       -> GodotString <$> godotStringP-    '['       -> GodotArr <$> godotArrP-    '{'       -> GodotDict <$> godotDictP-    't'       -> GodotBool <$> godotBoolP-    'f'       -> GodotBool <$> godotBoolP-    'n'       -> godotNullP+    '"' -> GodotString <$> godotStringP+    '[' -> GodotArr <$> godotArrP+    '{' -> GodotDict <$> godotDictP+    't' -> GodotBool <$> godotBoolP+    'f' -> GodotBool <$> godotBoolP+    'n' -> godotNullP     l       | isUpper l || l == '@' -> GodotConstructor <$> godotConstructorP-    _ -> P.try (GodotFloat <$> godotFloatP) <|> P.try (GodotInt <$> godotIntP)+    _   -> P.try (GodotFloat <$> godotFloatP) <|> P.try (GodotInt <$> godotIntP)  -- | Values parsed from a Tscn file.+--+-- Constructors are `(constructor name, constructor args)`. data GodotValue   = GodotConstructor (T.Text, [GodotValue])   | GodotInt Int@@ -125,8 +138,6 @@   | GodotNull   deriving (Show,Generic,Eq) -makeLenses ''GodotValue- -- There aren't any lenses to unwrap sum types AFAIK :/ -- Surely there's a better way to do this. unGodotConstructor k = fmap (\(GodotConstructor (n, a)) -> (n, a)) . M.lookup k@@ -159,39 +170,45 @@  collectRest its = M.filterWithKey (\k _ -> k `S.member` S.fromList its) --- | As of Godot 3.3+-- | Godot resource section prefixed with a bracket-enclosed header, optionally+-- with body entries.+--+-- Header entries not specified in a record are accessed with the relevant `headers` field.+-- Likewise, body entries not specified are accessed with the `entries` field.+-- Note that explicitly specified section fields are not duplicated in `headers` and+-- `entries` fields. data GodotSection   = ExtResourceSection     { _extResourceSectionPath    :: T.Text     , _extResourceSectionTy      :: T.Text     , _extResourceSectionId      :: Int-      -- | Other header information+      -- | Other header information.     , _extResourceSectionHeaders :: M.HashMap T.Text GodotValue-      -- | Body of the configuration entry+      -- | Body of the configuration entry.     , _extResourceSectionEntries :: M.HashMap T.Text GodotValue     }   | SubResourceSection     { _subResourceSectionTy      :: T.Text     , _subResourceSectionId      :: Int-      -- | Other header information+      -- | Other header information.     , _subResourceSectionHeaders :: M.HashMap T.Text GodotValue-      -- | Body of the configuration entry+      -- | Body of the configuration entry.     , _subResourceSectionEntries :: M.HashMap T.Text GodotValue     }   | NodeSection     { _nodeSectionTy :: Maybe T.Text     , _nodeSectionName :: T.Text-      -- | If Nothing, then this node is the root+      -- | If `Nothing`, then this node is the root.     , _nodeSectionParent :: Maybe T.Text-      -- | Instance is GodotConstructor+      -- | Instance refers to an `ExtResource` ID, usually listed at the top of a file.     , _nodeSectionInst :: Maybe Int     , _nodeSectionInstPlaceholder :: Maybe T.Text     , _nodeSectionOwner :: Maybe T.Text     , _nodeSectionIndex :: Maybe Int     , _nodeSectionGroups :: Maybe [T.Text]-      -- | Other header information+      -- | Other header information.     , _nodeSectionHeaders :: M.HashMap T.Text GodotValue-      -- | Body of the configuration entry+      -- | Body of the configuration entry.     , _nodeSectionEntries :: M.HashMap T.Text GodotValue     }   | ConnectionSection@@ -199,9 +216,9 @@     , _connectionSectionFrom    :: T.Text     , _connectionSectionTo      :: T.Text     , _connectionSectionMethod  :: T.Text-      -- | Other header information+      -- | Other header information.     , _connectionSectionHeaders :: M.HashMap T.Text GodotValue-      -- | Body of the configuration entry+      -- | Body of the configuration entry.     , _connectionSectionEntries :: M.HashMap T.Text GodotValue     }   | ResourceSection@@ -216,8 +233,7 @@     }   deriving (Show,Generic) -makeFields ''GodotSection-+-- | `tscn` file descriptor. data TscnDescriptor =   TscnDescriptor   { _tscnDescriptorLoadSteps :: Int@@ -225,8 +241,7 @@   }   deriving (Show,Generic) -makeFields ''TscnDescriptor-+-- | Parsed `tscn` file. data TscnParsed =   TscnParsed   { _tscnParsedDescriptor :: TscnDescriptor@@ -234,8 +249,7 @@   }   deriving (Show,Generic) -makeFields ''TscnParsed-+-- | `gdns` file descriptor. data GdnsDescriptor =   GdnsDescriptor   { _gdnsDescriptorTy        :: T.Text@@ -244,8 +258,7 @@   }   deriving (Show,Generic) -makeFields ''GdnsDescriptor-+-- | Parsed `gdns` file. data GdnsParsed =   GdnsParsed   { _gdnsParsedDescriptor :: GdnsDescriptor@@ -253,15 +266,29 @@   }   deriving (Show,Generic) -makeFields ''GdnsParsed+-- | An unknown file descriptor.+data OtherDescriptor =+  OtherDescriptor+  { _otherDescriptorHeaderName :: T.Text+  , _otherDescriptorHeaders    :: M.HashMap T.Text GodotValue+  }+  deriving (Show,Generic) +-- | An unknown file parsing result.+data OtherParsed =+  OtherParsed+  { _otherParsedDescriptor :: OtherDescriptor+  , _otherParsedSections   :: [GodotSection]+  }+  deriving (Show,Generic)++-- | Parsed godot resource file. data GodotParsed   = Tscn TscnParsed   | Gdns GdnsParsed+  | Other OtherParsed   deriving (Show,Generic) -makeFields ''GodotParsed- tscnHeaderKVP :: Parser (T.Text, GodotValue) tscnHeaderKVP = liftA2 (,) (P.takeWhileP Nothing (/= '=')) (P.char '=' *> godotValueP) @@ -337,6 +364,12 @@    (unGodotString' "from" kvs) (unGodotString' "to" kvs) (unGodotString' "method" kvs)    (collectRest ["signal", "from", "to", "method"] kvs) bodyKvs) +-- | Parse an unspecified section.+otherP :: Parser GodotSection+otherP = do+  (headerName, headerKvs', bodyKvs) <- bodyAndKvs+  pure $ OtherSection headerName headerKvs' bodyKvs+ -- | Parse a `tscn` file. tscnParser :: Parser TscnParsed tscnParser = do@@ -345,17 +378,17 @@       format    = unGodotInt' "format" kvs       sectionP  =         P.choice-        (map P.try [tscnConnectionP, tscnExtResourceP, tscnSubResourceP, tscnNodeP])+        (map P.try+         [tscnConnectionP, tscnExtResourceP, tscnSubResourceP, tscnNodeP, otherP])   sections <- P.manyTill sectionP P.eof   pure $ TscnParsed (TscnDescriptor loadSteps format) sections  -- | Parse a `[resource]` section.-resourceParser :: Parser GodotSection-resourceParser = do-  (headerName, headerKvs, bodyKvs) <- bodyAndKvs-  pure-    $ ResourceSection (unGodotString "resource_name" bodyKvs)-    (unGodotString "class_name" bodyKvs) (unGodotConstructor "library" bodyKvs)+resourceP :: Parser GodotSection+resourceP =+  headerWrapper "resource"+  (\_ bodyKvs -> ResourceSection (unGodotString "resource_name" bodyKvs)+   (unGodotString "class_name" bodyKvs) (unGodotConstructor "library" bodyKvs))  -- | Parse a `gdns` file. gdnsParser :: Parser GdnsParsed@@ -364,15 +397,19 @@   let ty        = unGodotString' "type" kvs       loadSteps = unGodotInt' "load_steps" kvs       format    = unGodotInt' "format" kvs-      sectionP  = P.choice (map P.try [tscnExtResourceP, resourceParser])+      sectionP  = P.choice (map P.try [tscnExtResourceP, resourceP, otherP])   sections <- P.manyTill sectionP P.eof   pure $ GdnsParsed (GdnsDescriptor ty loadSteps format) sections -otherParser :: Parser GodotSection+-- | Parse an unknown resource file.+otherParser :: Parser OtherParsed otherParser = do-  (headerName, headerKvs, bodyKvs) <- bodyAndKvs-  pure $ OtherSection headerName headerKvs bodyKvs+  hName <- P.char '[' *> P.takeWhile1P Nothing (/= ' ') <* P.char ' '+  hKvs <- headerKvs <* P.char ']' <* P.space+  sections <- P.manyTill otherP P.eof+  pure $ OtherParsed (OtherDescriptor hName hKvs) sections --- | Parse a resource file (currently only `tscn` and `gdns`).+-- | Parse some Godot resource file. godotParser :: Parser GodotParsed-godotParser = P.try (Tscn <$> tscnParser) <|> P.try (Gdns <$> gdnsParser)+godotParser =+  P.choice (map P.try [Tscn <$> tscnParser, Gdns <$> gdnsParser, Other <$> otherParser])
+ src/Godot/Parser/Resource/Lens.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}++module Godot.Parser.Resource.Lens where++import           Control.Lens++import           Godot.Parser.Resource++makeLenses ''GodotValue++makeFields ''GodotSection++makeFields ''TscnDescriptor++makeFields ''TscnParsed++makeFields ''GdnsDescriptor++makeFields ''GdnsParsed++makeFields ''OtherDescriptor++makeFields ''OtherParsed++makeFields ''GodotParsed