packages feed

bond-0.11.0.0: src/Language/Bond/Codegen/Util.hs

-- 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 #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

{-|
Copyright   : (c) Microsoft
License     : MIT
Maintainer  : adamsap@microsoft.com
Stability   : provisional
Portability : portable

Helper functions for creating common structures useful in code generation.
These functions often operate on 'Text' objects.
-}

module Language.Bond.Codegen.Util
    ( commonHeader
    , commaSep
    , newlineSep
    , commaLineSep
    , newlineSepEnd
    , newlineBeginSep
    , doubleLineSep
    , doubleLineSepEnd
    , uniqueName
    , uniqueNames
    , indent
    , newLine
    , slashForward
    ) where

import Data.Int (Int64)
import Data.Word
import Prelude
import Data.Text.Lazy (Text, justifyRight)
import Text.Shakespeare.Text
import Paths_bond (version)
import Data.Version (showVersion)
import Language.Bond.Util

instance ToText Word16 where
    toText = toText . show

instance ToText Double where
    toText = toText . show

instance ToText Integer where
    toText = toText . show

indent :: Int64 -> Text
indent n = justifyRight (4 * n) ' ' ""

commaLine :: Int64 -> Text
commaLine n = [lt|,
#{indent n}|]

newLine :: Int64 -> Text
newLine n = [lt|
#{indent n}|]

doubleLine :: Int64 -> Text
doubleLine n = [lt|

#{indent n}|]

-- | Separates elements of a list with a comma.
commaSep :: (a -> Text) -> [a] -> Text
commaSep = sepBy ", "

newlineSep, commaLineSep, newlineSepEnd, newlineBeginSep, doubleLineSep, doubleLineSepEnd
    :: Int64 -> (a -> Text) -> [a] -> Text

-- | Separates elements of a list with new lines. Starts new lines at the
-- specified indentation level.
newlineSep = sepBy . newLine

-- | Separates elements of a list with comma followed by a new line. Starts
-- new lines at the specified indentation level.
commaLineSep = sepBy . commaLine

-- | Separates elements of a list with new lines, ending with a new line.
-- Starts new lines at the specified indentation level.
newlineSepEnd = sepEndBy . newLine

-- | Separates elements of a list with new lines, beginning with a new line.
-- Starts new lines at the specified indentation level.
newlineBeginSep = sepBeginBy . newLine

-- | Separates elements of a list with two new lines. Starts new lines at
-- the specified indentation level.
doubleLineSep = sepBy . doubleLine

-- | Separates elements of a list with two new lines, ending with two new
-- lines. Starts new lines at the specified indentation level.
doubleLineSepEnd = sepEndBy . doubleLine

-- | Returns common header for generated files using specified single-line
-- comment lead character(s) and a file name.
commonHeader ::  ToText a => a -> a -> a -> Text
commonHeader c input output = [lt|
#{c}------------------------------------------------------------------------------
#{c} This code was generated by a tool.
#{c}
#{c}   Tool : Bond Compiler #{showVersion version}
#{c}   Input filename:  #{input}
#{c}   Output filename: #{output}
#{c}
#{c} Changes to this file may cause incorrect behavior and will be lost when
#{c} the code is regenerated.
#{c} <auto-generated />
#{c}------------------------------------------------------------------------------
|]

-- | Given an intended name and a list of already taken names, returns a
-- unique name. Assumes that it's legal to append digits to the end of the
-- intended name.
uniqueName :: String -> [String] -> String
uniqueName baseName taken = go baseName (0::Integer)
  where go name counter
          | not (name `elem` taken) = name
          | otherwise = go newName (counter + 1)
                        where newName = baseName ++ (show counter)

-- | Given a list of names with duplicates and a list of reserved names,
-- create a list of unique names using the uniqueName function.
uniqueNames :: [String] -> [String] -> [String]
uniqueNames names reservedInit = reverse $ go names [] reservedInit
  where
    go [] acc _ = acc
    go (name:remaining) acc reservedAcc = go remaining (newName:acc) (newName:reservedAcc)
      where
        newName = uniqueName name reservedAcc

-- | Converts all file path slashes to forward slashes.
slashForward :: String -> String
slashForward path = map replace path
  where replace '\\' = '/'
        replace c    = c