language-thrift (empty) → 0.1.0.0
raw patch · 7 files changed
+528/−0 lines, 7 filesdep +basedep +mtldep +parserssetup-changed
Dependencies added: base, mtl, parsers, text, trifecta
Files
- CHANGES.md +4/−0
- LICENSE +30/−0
- Language/Thrift/Parser.hs +293/−0
- Language/Thrift/Types.hs +154/−0
- README.md +13/−0
- Setup.hs +2/−0
- language-thrift.cabal +32/−0
+ CHANGES.md view
@@ -0,0 +1,4 @@+0.1.0.0+=======++- Initial release
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Abhinav Gupta++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * 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.++ * Neither the name of Abhinav Gupta nor the names of other+ 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+OWNER 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.
+ Language/Thrift/Parser.hs view
@@ -0,0 +1,293 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+module Language.Thrift.Parser+ ( ThriftParser+ , runThriftParser++ , program+ , header+ , definition+ , typeDefinition+ , typedef+ , enum+ , enumDef+ , senum+ , struct+ , union+ , exception+ , fieldRequiredness+ , fieldType+ , field+ , constant+ , constantValue+ , service+ , function+ , typeAnnotations+ ) where++import Control.Applicative+import Control.Monad+import Control.Monad.State (StateT)+import Data.Text (Text)+import Text.Trifecta+import Text.Parser.Token.Style (emptyIdents)++import qualified Control.Monad.State as State+import qualified Data.Text as Text++import qualified Language.Thrift.Types as T+++newtype ParserState = ParserState+ { parserLastDocstring :: T.Docstring+ } deriving (Show, Ord, Eq)++newtype ThriftParser a = ThriftParser (StateT ParserState Parser a)+ deriving+ ( Functor+ , Applicative+ , Alternative+ , Monad+ , MonadPlus+ , Parsing+ , CharParsing+ )++lastDocstring :: ThriftParser T.Docstring+lastDocstring = ThriftParser $ do+ s <- State.gets parserLastDocstring+ State.put (ParserState Nothing)+ return s++runThriftParser :: ThriftParser a -> Parser a+runThriftParser (ThriftParser p) = State.evalStateT p (ParserState Nothing)++instance TokenParsing ThriftParser where+ someSpace = skipSome $+ readDocstring <|> skipComments <|> skipSpace+ where+ skipSpace = choice [+ newline *> clearDocstring+ , ThriftParser someSpace+ ]++ skipComments = choice [+ char '#' *> skipLine+ , text "//" *> skipLine+ , text "/*" *> skipCStyleComment+ ] *> clearDocstring+ skipLine = skipMany (satisfy (/= '\n')) <* newline+ skipCStyleComment = choice [+ text "*/" *> pure ()+ , skipSome (noneOf "/*") *> skipCStyleComment+ , oneOf "/*" *> skipCStyleComment+ ]++ -- TODO this is really ugly. use some sort of docstring parser instead+ clearDocstring = ThriftParser $ State.put (ParserState Nothing)+ readDocstring = text "/**" *> loop []+ where+ saveDocstring s = unless (Text.null s') $+ ThriftParser . State.put . ParserState . Just $ s'+ where+ s' = sanitizeDocstring s+ loop chunks = choice [+ text "*/" *> optional (newline <|> space) *>+ saveDocstring (Text.strip . Text.concat $ reverse chunks)+ , Text.pack <$> some (noneOf "/*") >>= loop . (:chunks)+ , Text.singleton <$> oneOf "/*" >>= loop . (:chunks)+ ]+ sanitizeDocstring =+ Text.unlines . map (Text.dropWhile (`elem` "* ")) . Text.lines++idStyle :: IdentifierStyle ThriftParser+idStyle = (emptyIdents :: IdentifierStyle ThriftParser)+ { _styleStart = letter <|> char '_'+ , _styleLetter = alphaNum <|> oneOf "_."+ }++reserved :: Text -> ThriftParser ()+reserved = reserveText idStyle++program :: ThriftParser (T.Program T.Docstring)+program = whiteSpace >> T.Program <$> many header <*> many definition++literal :: ThriftParser Text+literal = stringLiteral <|> stringLiteral'++identifier :: ThriftParser Text+identifier = ident idStyle++header :: ThriftParser T.Header+header = choice [+ reserved "include" >> T.Include <$> literal+ , reserved "namespace" >>+ T.Namespace <$> (text "*" <|> identifier) <*> identifier+ , reserved "cpp_namespace" >> T.Namespace "cpp" <$> identifier+ , reserved "php_namespace" >> T.Namespace "php" <$> identifier+ , reserved "py_module" >> T.Namespace "py" <$> identifier+ , reserved "perl_package" >> T.Namespace "perl" <$> identifier+ , reserved "ruby_namespace" >> T.Namespace "rb" <$> identifier+ , reserved "java_package" >> T.Namespace "java" <$> identifier+ , reserved "cocoa_package" >> T.Namespace "cocoa" <$> identifier+ , reserved "csharp_namespace" >> T.Namespace "csharp" <$> identifier+ ]++docstring :: ThriftParser (T.Docstring -> a) -> ThriftParser a+docstring p = lastDocstring >>= \s -> p <*> pure s++definition :: ThriftParser (T.Definition T.Docstring)+definition = choice [constant, typeDefinition, service]++typeDefinition :: ThriftParser (T.Definition T.Docstring)+typeDefinition =+ T.TypeDefinition+ <$> choice [typedef, enum, senum, struct, union, exception]+ <*> typeAnnotations++typedef :: ThriftParser (T.Type T.Docstring)+typedef = reserved "typedef" >>+ docstring (T.Typedef <$> fieldType <*> identifier)++enum :: ThriftParser (T.Type T.Docstring)+enum = reserved "enum" >>+ docstring (T.Enum <$> identifier <*> braces (many enumDef))++struct :: ThriftParser (T.Type T.Docstring)+struct = reserved "struct" >>+ docstring (T.Struct <$> identifier <*> braces (many field))++union :: ThriftParser (T.Type T.Docstring)+union = reserved "union" >>+ docstring (T.Union <$> identifier <*> braces (many field))++exception :: ThriftParser (T.Type T.Docstring)+exception = reserved "exception" >>+ docstring (T.Exception <$> identifier <*> braces (many field))++fieldRequiredness :: ThriftParser T.FieldRequiredness+fieldRequiredness = choice [+ reserved "required" *> pure T.Required+ , reserved "optional" *> pure T.Optional+ ]++field :: ThriftParser (T.Field T.Docstring)+field = docstring $+ T.Field+ <$> optional (integer <* symbolic ':')+ <*> optional fieldRequiredness+ <*> fieldType+ <*> identifier+ <*> optional (equals *> constantValue)+ <*> typeAnnotations+ <* optionalSep++equals :: ThriftParser ()+equals = void $ symbolic '='++enumDef :: ThriftParser (T.EnumDef T.Docstring)+enumDef = docstring $+ T.EnumDef+ <$> identifier+ <*> optional (equals *> integer)+ <*> typeAnnotations+ <* optionalSep++senum :: ThriftParser (T.Type T.Docstring)+senum = reserved "senum" >> docstring+ (T.Senum <$> identifier <*> braces (many (literal <* optionalSep)))++constant :: ThriftParser (T.Definition T.Docstring)+constant = do+ reserved "const"+ docstring $+ T.ConstDefinition+ <$> fieldType+ <*> (identifier <* equals)+ <*> constantValue+ <* optionalSep++constantValue :: ThriftParser T.ConstValue+constantValue = choice [+ either T.ConstInt T.ConstFloat <$> integerOrDouble+ , T.ConstLiteral <$> literal+ , T.ConstIdentifier <$> identifier+ , T.ConstList <$> constList+ , T.ConstMap <$> constMap+ ]++constList :: ThriftParser [T.ConstValue]+constList = brackets $ commaSep (constantValue <* optionalSep)++constMap :: ThriftParser [(T.ConstValue, T.ConstValue)]+constMap = braces $ commaSep constantValuePair++constantValuePair :: ThriftParser (T.ConstValue, T.ConstValue)+constantValuePair =+ (,) <$> (constantValue <* colon)+ <*> (constantValue <* optionalSep)++fieldType :: ThriftParser T.FieldType+fieldType = choice [+ baseType+ , containerType+ , T.DefinedType <$> identifier+ ]++baseType :: ThriftParser T.FieldType+baseType =+ choice [reserved s *> (v <$> typeAnnotations) | (s, v) <- bases]+ where+ bases = [+ ("string", T.StringType)+ , ("binary", T.BinaryType)+ , ("slist", T.SListType)+ , ("bool", T.BoolType)+ , ("byte", T.ByteType)+ , ("i16", T.I16Type)+ , ("i32", T.I32Type)+ , ("i64", T.I64Type)+ , ("double", T.DoubleType)+ ]++containerType :: ThriftParser T.FieldType+containerType =+ choice [mapType, setType, listType] <*> typeAnnotations+ where+ mapType = reserved "map" >>+ angles (T.MapType <$> (fieldType <* comma) <*> fieldType)+ setType = reserved "set" >> angles (T.SetType <$> fieldType)+ listType = reserved "list" >> angles (T.ListType <$> fieldType)++service :: ThriftParser (T.Definition T.Docstring)+service = do+ reserved "service"+ docstring $+ T.ServiceDefinition+ <$> identifier+ <*> optional (reserved "extends" *> identifier)+ <*> braces (many function)+ <*> typeAnnotations++function :: ThriftParser (T.Function T.Docstring)+function = docstring $+ T.Function+ <$> ((reserved "oneway" *> pure True) <|> pure False)+ <*> ((reserved "void" *> pure Nothing) <|> Just <$> fieldType)+ <*> identifier+ <*> parens (many field)+ <*> optional (reserved "throws" *> parens (many field))+ <*> typeAnnotations+ <* optionalSep++typeAnnotations :: ThriftParser [T.TypeAnnotation]+typeAnnotations = parens (many typeAnnotation) <|> pure []++typeAnnotation :: ThriftParser T.TypeAnnotation+typeAnnotation =+ T.TypeAnnotation+ <$> identifier+ <*> (equals *> literal <* optionalSep)++optionalSep :: ThriftParser ()+optionalSep = void $ optional (comma <|> semi)
+ Language/Thrift/Types.hs view
@@ -0,0 +1,154 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+module Language.Thrift.Types+ ( Program(..)+ , Header(..)+ , Definition(..)+ , Type(..)+ , FieldRequiredness(..)+ , Field(..)+ , EnumDef(..)+ , ConstValue(..)+ , FieldType(..)+ , Function(..)+ , TypeAnnotation(..)+ , Docstring+ ) where++import Data.Data (Data, Typeable)+import Data.Text (Text)+import GHC.Generics (Generic)++data Program srcAnnot = Program+ { programHeaders :: [Header]+ , programDefinitions :: [Definition srcAnnot]+ }+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data Header+ = Include { includePath :: Text }+ | Namespace { namespaceLanguage, namespaceName :: Text }+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data Definition srcAnnot+ = ConstDefinition+ { constType :: FieldType+ , constName :: Text+ , constValue :: ConstValue+ , constSrcAnnot :: srcAnnot+ }+ | TypeDefinition+ { typeDefinition :: Type srcAnnot+ , typeAnnotations :: [TypeAnnotation]+ }+ | ServiceDefinition+ { serviceName :: Text+ , serviceExtends :: Maybe Text+ , serviceFunctions :: [Function srcAnnot]+ , serviceAnnotations :: [TypeAnnotation]+ , serviceSrcAnnot :: srcAnnot+ }+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data Type srcAnnot+ = Typedef+ { typedefType :: FieldType+ , typedefName :: Text+ , typedefSrcAnnot :: srcAnnot+ }+ | Enum+ { enumName :: Text+ , enumValues :: [EnumDef srcAnnot]+ , enumSrcAnnot :: srcAnnot+ }+ | Struct+ { structName :: Text+ , structFields :: [Field srcAnnot]+ , structSrcAnnot :: srcAnnot+ }+ | Union+ { unionName :: Text+ , unionFields :: [Field srcAnnot]+ , unionSrcAnnot :: srcAnnot+ }+ | Exception+ { exceptionName :: Text+ , exceptionFields :: [Field srcAnnot]+ , exceptionSrcAnnot :: srcAnnot+ }+ | Senum+ { senumName :: Text+ , senumValues :: [Text]+ , senumSrcAnnot :: srcAnnot+ }+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data FieldRequiredness = Required | Optional+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data Field srcAnnot = Field+ { fieldIdentifier :: Maybe Integer+ , fieldRequiredNess :: Maybe FieldRequiredness+ , fieldType :: FieldType+ , fieldName :: Text+ , fieldDefault :: Maybe ConstValue+ , fieldAnnotations :: [TypeAnnotation]+ , fieldSrcAnnot :: srcAnnot+ }+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data EnumDef srcAnnot = EnumDef+ { enumDefName :: Text+ , enumDefValue :: Maybe Integer+ , enumDefAnnotations :: [TypeAnnotation]+ , enumDefSrcAnnot :: srcAnnot+ }+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data ConstValue+ = ConstInt Integer+ | ConstFloat Double+ | ConstLiteral Text+ | ConstIdentifier Text+ | ConstList [ConstValue]+ | ConstMap [(ConstValue, ConstValue)]+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data FieldType+ = DefinedType Text++ -- Base types+ | StringType [TypeAnnotation]+ | BinaryType [TypeAnnotation]+ | SListType [TypeAnnotation]+ | BoolType [TypeAnnotation]+ | ByteType [TypeAnnotation]+ | I16Type [TypeAnnotation]+ | I32Type [TypeAnnotation]+ | I64Type [TypeAnnotation]+ | DoubleType [TypeAnnotation]++ -- Container types+ | MapType FieldType FieldType [TypeAnnotation]+ | SetType FieldType [TypeAnnotation]+ | ListType FieldType [TypeAnnotation]+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data Function srcAnnot = Function+ { functionOneWay :: Bool+ , functionReturnType :: Maybe FieldType+ , functionName :: Text+ , functionParameters :: [Field srcAnnot]+ , functionExceptions :: Maybe [Field srcAnnot]+ , functionAnnotations :: [TypeAnnotation]+ , functionSrcAnnot :: srcAnnot+ }+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++data TypeAnnotation = TypeAnnotation+ { typeAnnotationName :: Text+ , typeAnnotationValue :: Text+ }+ deriving (Show, Ord, Eq, Data, Typeable, Generic)++type Docstring = Maybe Text
+ README.md view
@@ -0,0 +1,13 @@+`language-thrift` provides a parser for the [Thrift IDL format][]. The parser+is written using [`trifecta`][] to provide helpful error messages when parsing+fails. The parser keeps track of Javadoc-style comments (`/** ... */`) and+associates them with the type, service, function, or field above which they+were added.++Documentation is sparse; most of the types are self-explanatory.+Haddock-generated docs are available on [Hackage][] and [here][].++ [Thrift IDL format]: http://thrift.apache.org/docs/idl+ [`trifecta`]: http://hackage.haskell.org/package/trifecta+ [Hackage]: http://hackage.haskell.org/package/language-thrift+ [here]: http://abhinavg.net/language-thrift/
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ language-thrift.cabal view
@@ -0,0 +1,32 @@+name : language-thrift+version : 0.1.0.0+synopsis : Parser for the Thrift IDL format.+homepage : https://github.com/abhinav/language-thrift+license : BSD3+license-file : LICENSE+author : Abhinav Gupta+maintainer : Abhinav Gupta <mail@abhinavg.net>+category : Language+build-type : Simple+cabal-version : >=1.10+description :+ This package provides a parser for the+ <http://thrift.apache.org/docs/idl Thrift IDL format>.+extra-source-files:+ README.md+ CHANGES.md+-- examples/*.hs TODO: Add examples++library+ exposed-modules : Language.Thrift.Parser+ , Language.Thrift.Types+ build-depends : base >= 4.7 && < 4.8+ , mtl+ , text >= 1.2+ , parsers >= 0.12 && < 0.13+ , trifecta >= 1.5 && < 1.6+ default-language : Haskell2010++source-repository head+ type: git+ location: git://github.com/abhinav/language-thrift.git