packages feed

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 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