packages feed

bond-0.11.0.1: src/Language/Bond/Codegen/CustomMapping.hs

-- Copyright (c) Microsoft. All rights reserved.
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.

{-# LANGUAGE OverloadedStrings #-}

module Language.Bond.Codegen.CustomMapping
    ( AliasMapping(..)
    , Fragment(..)
    , NamespaceMapping(..)
    , parseAliasMapping
    , parseNamespaceMapping
    ) where

import Control.Applicative hiding (some)
import Data.Void (Void)
import Language.Bond.Syntax.Types
import Prelude
import Text.Megaparsec hiding (many, optional, (<|>))
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L

-- | Specification of a fragment of type alias mappings.
data Fragment =
    Fragment String |                   -- ^ hardcoded string fragment
    Placeholder Int                     -- ^ placeholder for the n-th type argument of the type alias, applicable only to generic aliases

-- | Specification of a type alias mapping.
data AliasMapping = AliasMapping
    { aliasName :: QualifiedName        -- ^ qualified name of a type alias
    , aliasTemplate :: [Fragment]       -- ^ list of fragments comprising the custom mapping for the alias
    }

-- | Specification of namespace mapping.
data NamespaceMapping = NamespaceMapping
    { fromNamespace :: QualifiedName    -- ^ schema namespace
    , toNamespace :: QualifiedName      -- ^ namespace in the generated code
    }

type Parser = Parsec Void String

identifier :: Parser String
identifier = some (alphaNumChar <|> char '_') <?> "identifier"

qualifiedName :: Parser [String]
qualifiedName = sepBy1 identifier (char '.') <?> "qualified name"

sc :: Parser ()
sc = L.space space1 empty empty

symbol :: String -> Parser String
symbol = L.symbol sc

equal :: Parser String
equal = symbol "="

-- consume whitespace after every lexeme
lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc

decimal :: Parser Integer
decimal = lexeme L.decimal

hexadecimal :: Parser Integer
hexadecimal = lexeme . try $ char '0' >> char' 'x' >> L.hexadecimal

octal :: Parser Integer
octal = lexeme . try $ char '0' >> char' 'o' >> L.octal

natural :: Parser Integer
natural = hexadecimal <|> octal <|> decimal

integer :: Parser Integer
integer = L.signed sc natural

-- | Parse a type alias mapping specification used in command-line arguments of
-- <https://microsoft.github.io/bond/manual/compiler.html#command-line-options gbc>.
--
-- ==== __Examples__
--
-- > > parseAliasMapping "Example.OrderedSet=SortedSet<{0}>"
-- > Right (AliasMapping {aliasName = ["Example","OrderedSet"], aliasTemplate = [Fragment "SortedSet<",Placeholder 0,Fragment ">"]})
parseAliasMapping :: String -> Either (ParseError Char Void) AliasMapping
parseAliasMapping s = parse aliasMapping "" s
  where
    aliasMapping = AliasMapping <$> qualifiedName <* equal <*> some (placeholder <|> fragment) <* eof
    placeholder = Placeholder <$> fromIntegral <$> between (char '{') (char '}') integer
    fragment = Fragment <$> some (notChar '{')

-- | Parse a namespace mapping specification used in command-line arguments of
-- <https://microsoft.github.io/bond/manual/compiler.html#command-line-options gbc>.
--
-- ==== __Examples__
--
-- > > parseNamespaceMapping "bond=Microsoft.Bond"
-- > Right (NamespaceMapping {fromNamespace = ["bond"], toNamespace = ["Microsoft","Bond"]})
parseNamespaceMapping :: String -> Either (ParseError Char Void) NamespaceMapping
parseNamespaceMapping s = parse namespaceMapping "" s
  where
    namespaceMapping = NamespaceMapping <$> qualifiedName <* equal <*> qualifiedName