bond 0.4.0.2 → 0.4.1.0
raw patch · 14 files changed
+374/−37 lines, 14 filesdep +aeson-prettydep ~Diffdep ~aesondep ~processPVP ok
version bump matches the API change (PVP)
Dependencies added: aeson-pretty
Dependency ranges changed: Diff, aeson, process
API changes (from Hackage documentation)
+ Language.Bond.Codegen.Templates: comm_interface_cs :: MappingContext -> String -> [Import] -> [Declaration] -> (String, Text)
+ Language.Bond.Codegen.Templates: comm_proxy_cs :: MappingContext -> String -> [Import] -> [Declaration] -> (String, Text)
+ Language.Bond.Codegen.Templates: comm_service_cs :: MappingContext -> String -> [Import] -> [Declaration] -> (String, Text)
+ Language.Bond.Syntax.JSON: instance Data.Aeson.Types.Class.FromJSON Language.Bond.Syntax.Types.Method
+ Language.Bond.Syntax.JSON: instance Data.Aeson.Types.Class.ToJSON Language.Bond.Syntax.Types.Method
+ Language.Bond.Syntax.SchemaDef: makeSchemaDef :: Type -> SchemaDef
+ Language.Bond.Syntax.Types: Event :: [Attribute] -> String -> Maybe Type -> Method
+ Language.Bond.Syntax.Types: Function :: [Attribute] -> Maybe Type -> String -> Maybe Type -> Method
+ Language.Bond.Syntax.Types: Service :: [Namespace] -> [Attribute] -> String -> [TypeParam] -> [Method] -> Declaration
+ Language.Bond.Syntax.Types: [methodAttributes] :: Method -> [Attribute]
+ Language.Bond.Syntax.Types: [methodInput] :: Method -> Maybe Type
+ Language.Bond.Syntax.Types: [methodName] :: Method -> String
+ Language.Bond.Syntax.Types: [methodResult] :: Method -> Maybe Type
+ Language.Bond.Syntax.Types: [serviceMethods] :: Declaration -> [Method]
+ Language.Bond.Syntax.Types: data Method
+ Language.Bond.Syntax.Types: instance GHC.Classes.Eq Language.Bond.Syntax.Types.Method
+ Language.Bond.Syntax.Types: instance GHC.Show.Show Language.Bond.Syntax.Types.Method
Files
- IO.hs +25/−2
- Main.hs +1/−3
- bond.cabal +12/−8
- src/Language/Bond/Codegen/Cs/Comm_cs.hs +201/−0
- src/Language/Bond/Codegen/Cs/Util.hs +4/−0
- src/Language/Bond/Codegen/Templates.hs +4/−0
- src/Language/Bond/Parser.hs +60/−19
- src/Language/Bond/Syntax/Internal.hs +1/−0
- src/Language/Bond/Syntax/JSON.hs +1/−1
- src/Language/Bond/Syntax/SchemaDef.hs +2/−1
- src/Language/Bond/Syntax/Types.hs +27/−1
- tests/Main.hs +13/−0
- tests/Tests/Codegen.hs +17/−1
- tests/Tests/Syntax.hs +6/−1
IO.hs view
@@ -17,8 +17,12 @@ import Control.Applicative import Prelude import Data.Aeson (eitherDecode) +import Data.Text import Control.Monad.Loops (firstM) import qualified Data.ByteString.Lazy as BL +import Text.Parsec +import Text.ParserCombinators.Parsec.Error +import Text.Printf import Language.Bond.Syntax.Types (Bond(..)) import Language.Bond.Syntax.JSON() import Language.Bond.Parser @@ -39,7 +43,7 @@ result <- parseBond file input (cwd </> file) readImportFile case result of Left err -> do - putStrLn $ "Error parsing " ++ file ++ ": " ++ show err + putStrLn $ msbuildErrorMessage err exitFailure Right bond -> return bond where @@ -77,7 +81,26 @@ parseNamespaceMappings :: [String] -> IO [NamespaceMapping] -parseNamespaceMappings = mapM $ +parseNamespaceMappings = mapM $ \ s -> case parseNamespaceMapping s of Left err -> fail $ show err Right m -> return m + +msbuildErrorMessage :: ParseError -> String +msbuildErrorMessage err = printf "%s(%d,%d) : error B0000: %s" name line col message + where + message = combinedMessage err + pos = errorPos err + name = sourceName pos + line = sourceLine pos + col = sourceColumn pos + +combinedMessage :: ParseError -> String +combinedMessage err = id $ unpack $ intercalate (pack ", ") messages + where + -- showErrorMessages returns a multi-line String starting with a blank + -- line. We need to break it up to make a useful one-line message. + messages = splitOn (pack "\n") $ strip $ pack $ + showErrorMessages "or" "unknown parse error" + "expecting" "unexpected" "end of input" + (errorMessages err)
Main.hs view
@@ -90,7 +90,6 @@ ] cppCodegen _ = error "cppCodegen: impossible happened." - csCodegen :: Options -> IO() csCodegen options@Cs {..} = do let fieldMapping = if readonly_properties @@ -99,7 +98,7 @@ then PublicFields else Properties let typeMapping = if collection_interfaces then csCollectionInterfacesTypeMapping else csTypeMapping - let templates = [ types_cs Class fieldMapping ] + let templates = [ comm_interface_cs , comm_proxy_cs , comm_service_cs , types_cs Class fieldMapping ] concurrentlyFor_ files $ codeGen options typeMapping templates csCodegen _ = error "csCodegen: impossible happened." @@ -117,4 +116,3 @@ createDirectoryIfMissing True outputDir let content = if (no_banner options) then code else (commonHeader "//" fileName <> code) L.writeFile (outputDir </> fileName) content -
bond.cabal view
@@ -2,7 +2,7 @@ -- Licensed under the MIT license. See LICENSE file in the project root for full license information. name: bond -version: 0.4.0.2 +version: 0.4.1.0 cabal-version: >= 1.8 tested-with: GHC>=7.4.1 synopsis: Bond schema compiler and code generator @@ -37,7 +37,7 @@ library hs-source-dirs: src - build-depends: aeson >= 0.7.0.6 && < 0.10.0.0, + build-depends: aeson >= 0.7.0.6 && < 0.12.0.0, base >= 4.5 && < 5, bytestring >= 0.10, filepath >= 1.0, @@ -64,6 +64,7 @@ Language.Bond.Codegen.Cpp.Types_cpp Language.Bond.Codegen.Cpp.Types_h Language.Bond.Codegen.Cs.Types_cs + Language.Bond.Codegen.Cs.Comm_cs Language.Bond.Codegen.Cpp.ApplyOverloads Language.Bond.Codegen.Cpp.Util Language.Bond.Codegen.Cs.Util @@ -79,7 +80,8 @@ Tests.Syntax ghc-options: -threaded -Wall build-depends: bond, - aeson >= 0.7.0.6 && < 0.10.0.0, + aeson >= 0.7.0.6 && < 0.12.0.0, + aeson-pretty == 0.7.2, base >= 4.5 && < 5, bytestring >= 0.10, cmdargs >= 0.10.10, @@ -90,12 +92,13 @@ derive, HUnit, QuickCheck, - Diff >= 0.2 && <= 0.3.2, + Diff >= 0.2 && < 0.4, pretty, tasty, tasty-golden, tasty-hunit, - tasty-quickcheck + tasty-quickcheck, + parsec >= 3.1 executable gbc main-is: Main.hs @@ -103,13 +106,14 @@ Options ghc-options: -threaded -Wall build-depends: bond, - aeson >= 0.7.0.6 && < 0.10.0.0, + aeson >= 0.7.0.6 && < 0.12.0.0, async >= 2.0.1.0, base >= 4.5 && < 5, bytestring >= 0.10, cmdargs >= 0.10.10, - process < 1.4, + process < 1.5, directory >= 1.1, filepath >= 1.0, monad-loops >= 0.4, - text >= 0.11 + text >= 0.11, + parsec >= 3.1
+ src/Language/Bond/Codegen/Cs/Comm_cs.hs view
@@ -0,0 +1,201 @@+-- Copyright (c) Microsoft. All rights reserved. +-- Licensed under the MIT license. See LICENSE file in the project root for full license information. + +{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-} + +module Language.Bond.Codegen.Cs.Comm_cs ( + comm_interface_cs, + comm_proxy_cs, + comm_service_cs) +where + +import Data.Maybe +import Data.Monoid +import Data.List (nub) +import Prelude +import qualified Data.Text.Lazy as L +import Data.Text.Lazy.Builder +import Text.Shakespeare.Text +import Language.Bond.Syntax.Types +import Language.Bond.Util +import Language.Bond.Codegen.Util +import Language.Bond.Codegen.TypeMapping +import qualified Language.Bond.Codegen.Cs.Util as CS + + +getMessageTypeName :: MappingContext -> Maybe Type -> Builder +getMessageTypeName ctx t = maybe "global::Bond.Void" (getTypeName ctx) t + +getMessageProxyInputParam :: MappingContext -> Maybe Type -> Builder +getMessageProxyInputParam ctx t = maybe "" constructParam t + where + constructParam x = getTypeName ctx x `mappend` fromText " param" + +paramOrBondVoid :: Maybe Type -> String +paramOrBondVoid t = if isNothing t then "new global::Bond.Void()" else "param" + +-- | Codegen template for generating code containing declarations of +-- of services including interfaces, proxies and services files. + +comm_interface_cs :: MappingContext -> String -> [Import] -> [Declaration] -> (String, L.Text) +comm_interface_cs cs _ _ declarations = ("_interfaces.cs", [lt| +#{CS.disableCscWarnings} +#{CS.disableReSharperWarnings} +namespace #{csNamespace} +{ + #{doubleLineSep 1 comm declarations} +} // #{csNamespace} +|]) + where + csNamespace = sepBy "." toText $ getNamespace cs + + comm s@Service{..} = [lt|#{CS.typeAttributes cs s}interface I#{declName}#{generics} + { + #{doubleLineSep 2 methodDeclaration serviceMethods} + } + |] + where + generics = angles $ sepBy ", " paramName declParams + + methodDeclaration Function{..} = [lt|global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param, global::System.Threading.CancellationToken ct);|] + where + getMessageResultTypeName = getMessageTypeName cs methodResult + getMessageInputTypeName = getMessageTypeName cs methodInput + + methodDeclaration Event{..} = [lt|void #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param);|] + where + getMessageInputTypeName = getMessageTypeName cs methodInput + + comm _ = mempty + +comm_proxy_cs :: MappingContext -> String -> [Import] -> [Declaration] -> (String, L.Text) +comm_proxy_cs cs _ _ declarations = ("_proxies.cs", [lt| +#{CS.disableCscWarnings} +#{CS.disableReSharperWarnings} +namespace #{csNamespace} +{ + #{doubleLineSep 1 comm declarations} +} // #{csNamespace} +|]) + where + csNamespace = sepBy "." toText $ getNamespace cs + idl = MappingContext idlTypeMapping [] [] [] + + comm s@Service{..} = [lt|#{CS.typeAttributes cs s}public class #{declName}Proxy<#{proxyGenerics}TConnection> : I#{declName}#{interfaceGenerics}#{connConstraint} + { + private readonly TConnection m_connection; + + public #{declName}Proxy(TConnection connection) + { + m_connection = connection; + } + + #{doubleLineSep 2 proxyMethod serviceMethods} + }|] + where + methodCapability Function {} = "global::Bond.Comm.IRequestResponseConnection" + methodCapability Event {} = "global::Bond.Comm.IEventConnection" + + getCapabilities :: [Method] -> [String] + getCapabilities m = nub $ map methodCapability m + connConstraint = " where TConnection : " ++ sepBy ", " (\p -> p) (getCapabilities serviceMethods) + + interfaceGenerics = angles $ sepBy "," paramName declParams -- of the form "<T1, T2, T3>" + proxyGenerics = sepEndBy ", " paramName declParams -- of the form "T1, T2, T3, " + + proxyMethod Function{..} = [lt|public global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(#{getMessageProxyInputParam cs methodInput}) + { + var message = new global::Bond.Comm.Message<#{getMessageInputTypeName}>(#{paramOrBondVoid methodInput}); + return #{methodName}Async(message, global::System.Threading.CancellationToken.None); + } + + public global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param, global::System.Threading.CancellationToken ct) + { + return m_connection.RequestResponseAsync<#{getMessageInputTypeName}, #{getMessageResultTypeName}>( + "#{getDeclTypeName idl s}.#{methodName}", + param, + ct); + }|] + where + getMessageResultTypeName = getMessageTypeName cs methodResult + getMessageInputTypeName = getMessageTypeName cs methodInput + + proxyMethod Event{..} = [lt|public void #{methodName}Async(#{getMessageProxyInputParam cs methodInput}) + { + var message = new global::Bond.Comm.Message<#{getMessageInputTypeName}>(#{paramOrBondVoid methodInput}); + #{methodName}Async(message); + } + + public void #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param) + { + m_connection.FireEventAsync<#{getMessageInputTypeName}>( + "#{getDeclTypeName idl s}.#{methodName}", + param); + }|] + where + getMessageInputTypeName = getMessageTypeName cs methodInput + + comm _ = mempty + +comm_service_cs :: MappingContext -> String -> [Import] -> [Declaration] -> (String, L.Text) +comm_service_cs cs _ _ declarations = ("_services.cs", [lt| +#{CS.disableCscWarnings} +#{CS.disableReSharperWarnings} +namespace #{csNamespace} +{ + #{doubleLineSep 1 comm declarations} +} // #{csNamespace} +|]) + where + csNamespace = sepBy "." toText $ getNamespace cs + idl = MappingContext idlTypeMapping [] [] [] + + comm s@Service{..} = [lt|#{CS.typeAttributes cs s}public abstract class #{declName}ServiceBase#{generics} : I#{declName}#{generics}, global::Bond.Comm.IService + { + public global::System.Collections.Generic.IEnumerable<global::Bond.Comm.ServiceMethodInfo> Methods + { + get + { + #{newlineSep 4 methodInfo serviceMethods} + } + } + + #{doubleLineSep 2 methodAbstract serviceMethods} + + #{doubleLineSep 2 methodGlue serviceMethods} + } + |] + where + generics = angles $ sepBy ", " paramName declParams + + methodInfo Function{..} = [lt|yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="#{getDeclTypeName idl s}.#{methodName}", Callback = #{methodName}Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};|] + methodInfo Event{..} = [lt|yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="#{getDeclTypeName idl s}.#{methodName}", Callback = #{methodName}Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.Event};|] + + methodAbstract Function{..} = [lt|public abstract global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param, global::System.Threading.CancellationToken ct);|] + where + getMessageResultTypeName = getMessageTypeName cs methodResult + getMessageInputTypeName = getMessageTypeName cs methodInput + + methodAbstract Event{..} = [lt|public abstract void #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param);|] + where + getMessageInputTypeName = getMessageTypeName cs methodInput + + methodGlue Function{..} = [lt|private global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage> #{methodName}Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct) + { + return global::Bond.Comm.CodegenHelpers.Upcast<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>, + global::Bond.Comm.IMessage>( + #{methodName}Async(param.Convert<#{getMessageInputTypeName}>(), ct)); + }|] + where + getMessageResultTypeName = getMessageTypeName cs methodResult + getMessageInputTypeName = getMessageTypeName cs methodInput + + methodGlue Event{..} = [lt|private global::System.Threading.Tasks.Task #{methodName}Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct) + { + #{methodName}Async(param.Convert<#{getMessageInputTypeName}>()); + return global::Bond.Comm.CodegenHelpers.CompletedTask; + }|] + where + getMessageInputTypeName = getMessageTypeName cs methodInput + + comm _ = mempty
src/Language/Bond/Codegen/Cs/Util.hs view
@@ -73,6 +73,10 @@ typeAttributes cs e@Enum {..} = optionalTypeAttributes cs e <> generatedCodeAttr + +-- C# service attributes +typeAttributes _ Service {..} = generatedCodeAttr + typeAttributes _ _ = error "typeAttributes: impossible happened." generatedCodeAttr :: Text
src/Language/Bond/Codegen/Templates.hs view
@@ -45,6 +45,9 @@ , FieldMapping(..) , StructMapping(..) , types_cs + , comm_interface_cs + , comm_proxy_cs + , comm_service_cs ) where @@ -56,6 +59,7 @@ import Language.Bond.Codegen.Cpp.Types_cpp import Language.Bond.Codegen.Cpp.Types_h import Language.Bond.Codegen.Cs.Types_cs +import Language.Bond.Codegen.Cs.Comm_cs -- redundant imports for haddock import Language.Bond.Codegen.TypeMapping import Language.Bond.Syntax.Types
src/Language/Bond/Parser.hs view
@@ -81,10 +81,10 @@ import_ :: Parser Import import_ = do i <- Import <$ keyword "import" <*> unescapedStringLiteral <?> "import statement" - input <- getInput + src <- getInput pos <- getPosition processImport i - setInput input + setInput src setPosition pos return i @@ -107,6 +107,7 @@ <|> try view <|> try enum <|> try alias + <|> try service updateSymbols decl <?> "declaration" return decl @@ -140,13 +141,13 @@ doFind = do namespaces <- asks currentNamespaces Symbols { symbols = symbols } <- getState - case find (delcMatching namespaces name) symbols of + case find (declMatching namespaces name) symbols of Just decl -> return decl Nothing -> fail $ "Unknown symbol: " ++ showQualifiedName name - delcMatching namespaces [unqualifiedName] decl = + declMatching namespaces [unqualifiedName] decl = unqualifiedName == declName decl && (not $ null $ intersectBy nsMatching namespaces (declNamespaces decl)) - delcMatching _ qualifiedName' decl = + declMatching _ qualifiedName' decl = takeName qualifiedName' == declName decl && any ((takeNamespace qualifiedName' ==) . nsName) (declNamespaces decl) nsMatching ns1 ns2 = @@ -210,7 +211,7 @@ view :: Parser Declaration view = do attr <- attributes - name <- keyword "struct" *> identifier + name <- keyword "struct" *> identifier <?> "struct view definition" decl <- keyword "view_of" *> qualifiedName >>= findStruct fields <- braces $ semiOrCommaSepEnd1 identifier namespaces <- asks currentNamespaces @@ -308,26 +309,26 @@ <|> keyword "map" *> angles (BT_Map <$> keyType <* comma <*> type_) <|> keyword "bonded" *> angles (BT_Bonded <$> userStruct) where - keyType = try (basicType <|> checkUserType validKeyType) <?> "scalar, string or enum" - userStruct = try (checkUserType validBondedType) <?> "user defined struct" - checkUserType valid = do - t <- userType - if valid t then return t else unexpected "type" - validKeyType t = case t of - BT_TypeParam _ -> True - _ -> isScalar t || isString t - validBondedType t = case t of - BT_TypeParam _ -> True - _ -> isStruct t + keyType = try (basicType <|> checkUserType isValidKeyType) <?> "scalar, string or enum" + userStruct = try (checkUserType isStruct) <?> "user defined struct" + isValidKeyType t = isScalar t || isString t -- parser for user defined type (struct, enum, alias or type parameter) userType :: Parser Type userType = do + symbol_ <- userSymbol + case symbol_ of + Left param -> return $ BT_TypeParam param + Right (Service {..}, _) -> fail $ "Unexpected service " ++ declName ++ ". Expected struct, enum or alias." + Right (decl, args) -> return $ BT_UserDefined decl args + +userSymbol :: Parser (Either TypeParam (Declaration, [Type])) +userSymbol = do name <- qualifiedName params <- asks currentParams case find (isParam name) params of - Just param -> return $ BT_TypeParam param + Just param -> return $ Left param Nothing -> do decl <- findSymbol name args <- option [] (angles $ commaSep1 arg) @@ -338,7 +339,7 @@ else " is not a generic type" else - return $ BT_UserDefined decl args + return $ Right (decl, args) where paramsCount Enum{} = 0 paramsCount decl = length $ declParams decl @@ -357,4 +358,44 @@ ftype = keyword "bond_meta::name" *> pure BT_MetaName <|> keyword "bond_meta::full_name" *> pure BT_MetaFullName <|> type_ + +-- service definition parser +service :: Parser Declaration +service = do + attr <- attributes + name <- keyword "service" *> identifier <?> "service definition" + params <- parameters + namespaces <- asks currentNamespaces + local (with params) $ Service namespaces attr name params <$> methods <* optional semi + where + with params e = e { currentParams = params } + methods = braces $ semiEnd (try event <|> try function) + +function :: Parser Method +function = Function <$> attributes <*> payload <*> identifier <*> input + +event :: Parser Method +event = Event <$> attributes <* keyword "nothing" <*> identifier <*> input + +input :: Parser (Maybe Type) +input = do + pld <- parens $ optional payload + case pld of + Nothing -> pure Nothing + Just m -> return m + +payload :: Parser (Maybe Type) +payload = void_ <|> liftM Just userStruct + where + void_ = keyword "void" *> pure Nothing + userStruct = try (checkUserType isStruct) <?> "user defined struct" + +checkUserType :: (Type -> Bool) -> Parser Type +checkUserType check = do + t <- userType + if (valid t) then return t else unexpected "type" + where + valid t = case t of + BT_TypeParam _ -> True + _ -> check t
src/Language/Bond/Syntax/Internal.hs view
@@ -43,6 +43,7 @@ showPretty Enum {..} = "enum " ++ declName showPretty Forward {..} = "struct declaration " ++ declName ++ showTypeParams declParams showPretty Alias {..} = "alias " ++ declName ++ showTypeParams declParams + showPretty Service {..} = "service " ++ declName ++ showTypeParams declParams metaField :: Field -> Any metaField Field {..} = Any $ isMetaName fieldType
src/Language/Bond/Syntax/JSON.hs view
@@ -267,4 +267,4 @@ $(deriveJSON defaultOptions ''Declaration) $(deriveJSON defaultOptions ''Import) $(deriveJSON defaultOptions ''Language) - +$(deriveJSON defaultOptions ''Method)
src/Language/Bond/Syntax/SchemaDef.hs view
@@ -14,7 +14,8 @@ module Language.Bond.Syntax.SchemaDef ( -- * Runtime schema (aka SchemaDef) support - encodeSchemaDef + encodeSchemaDef, + makeSchemaDef ) where import Data.Word
src/Language/Bond/Syntax/Types.hs view
@@ -30,6 +30,8 @@ , Type(..) , TypeParam(..) , Constraint(..) + -- ** Comm + , Method(..) -- ** Metadata , Attribute(..) -- ** Deprecated @@ -119,7 +121,23 @@ } deriving (Eq, Show) --- | A declaration of a schema type. +-- | Method of a service +data Method = + Function + { methodAttributes :: [Attribute] -- zero or more attributes + , methodResult :: Maybe Type -- method result + , methodName :: String -- method name + , methodInput :: Maybe Type -- method parameter + } + | + Event + { methodAttributes :: [Attribute] -- zero or more attributes + , methodName :: String -- method name + , methodInput :: Maybe Type -- method parameter + } + deriving (Eq, Show) + +-- | Bond schema declaration data Declaration = Struct { declNamespaces :: [Namespace] -- namespace(s) in which the struct is declared @@ -149,6 +167,14 @@ , declParams :: [TypeParam] -- type parameters for generics , aliasType :: Type -- aliased type } -- ^ <https://microsoft.github.io/bond/manual/compiler.html#type-aliases type alias definition> + | + Service + { declNamespaces :: [Namespace] -- namespace(s) in which the service is declared + , declAttributes :: [Attribute] -- zero or more attributes + , declName :: String -- service name + , declParams :: [TypeParam] -- type parameters for generic service + , serviceMethods :: [Method] -- zero or more methods + } -- ^ <https://microsoft.github.io/bond/manual/compiler.html#service-definition service definition> deriving (Eq, Show) -- | <https://microsoft.github.io/bond/manual/compiler.html#import-statements Import> declaration.
tests/Main.hs view
@@ -23,6 +23,9 @@ , testCase "inheritance" $ compareAST "inheritance" , testCase "type aliases" $ compareAST "aliases" , testCase "documentation example" $ compareAST "example" + , testCase "simple service syntax" $ compareAST "service" + , testCase "service attributes" $ compareAST "service_attributes" + , testCase "generic service" $ compareAST "generic_service" ] ] , testGroup "SchemaDef" @@ -101,6 +104,16 @@ , "--using=time=System.DateTime" ] "nullable_alias" + , testGroup "Comm" + [ verifyCsCommCodegen + [ "c#" + ] + "service" + , verifyCsCommCodegen + [ "c#" + ] + "generic_service" + ] ] ] ]
tests/Tests/Codegen.hs view
@@ -9,6 +9,7 @@ , verifyCppCodegen , verifyApplyCodegen , verifyCsCodegen + , verifyCsCommCodegen ) where import System.FilePath @@ -61,6 +62,22 @@ ] +verifyCsCommCodegen :: [String] -> FilePath -> TestTree +verifyCsCommCodegen args baseName = + testGroup baseName $ + map (verifyFile (processOptions args) baseName csTypeMapping "") + [ comm_interface_cs + , comm_proxy_cs + , comm_service_cs + , types_cs Class (fieldMapping (processOptions args)) + ] + where + fieldMapping Cs {..} = if readonly_properties + then ReadOnlyProperties + else if fields + then PublicFields + else Properties + verifyFiles :: Options -> FilePath -> [TestTree] verifyFiles options baseName = map (verify (typeMapping options) "") (templates options) @@ -115,4 +132,3 @@ (text "test output") (text . BS.unpack) (getContextDiff 3 (BS.lines x) (BS.lines y)) -
tests/Tests/Syntax.hs view
@@ -14,6 +14,7 @@ import Data.Maybe import Data.List import Data.Aeson (encode, decode) +import Data.Aeson.Encode.Pretty (Config(..), encodePretty') import Data.DeriveTH import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL @@ -40,6 +41,7 @@ derive makeArbitrary ''Namespace derive makeArbitrary ''Type derive makeArbitrary ''TypeParam +derive makeArbitrary ''Method roundtripAST :: Bond -> Bool roundtripAST x = (decode . encode) x == Just x @@ -106,8 +108,11 @@ BL.fromStrict $ replace "\\u003c" "<" $ replace "\\u003e" ">" $ - BL.toStrict $ encodeSchemaDef $ BT_UserDefined schema [] + BL.toStrict $ prettyEncode $ makeSchemaDef $ BT_UserDefined schema [] where + prettyEncode = encodePretty' (Config indentSpaces compare) + where + indentSpaces = 2 replace s r bs = if B.null t then h else B.append h (B.append r $ replace s r (B.drop (B.length s) t)) where