packages feed

capnp-0.17.0.0: cmd/capnpc-haskell/IR/Name.hs

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module IR.Name where

import Data.Char (toLower)
import Data.List (intersperse)
import qualified Data.Set as S
import Data.String (IsString (fromString))
import qualified Data.Text as T
import Data.Word

class HasUnQ a where
  getUnQ :: a -> UnQ

class MkSub a where
  mkSub :: a -> UnQ -> a

instance HasUnQ UnQ where
  getUnQ = id

instance HasUnQ LocalQ where
  getUnQ = localUnQ

instance HasUnQ CapnpQ where
  getUnQ CapnpQ {local} = getUnQ local

instance HasUnQ GlobalQ where
  getUnQ GlobalQ {local} = getUnQ local

newtype UnQ = UnQ T.Text
  deriving (Show, Read, Eq, Ord, IsString, Semigroup)

newtype NS = NS [T.Text]
  deriving (Show, Read, Eq, Ord)

data LocalQ = LocalQ
  { localUnQ :: UnQ,
    localNS :: NS
  }
  deriving (Show, Read, Eq, Ord)

instance IsString LocalQ where
  fromString s =
    LocalQ
      { localUnQ = fromString s,
        localNS = emptyNS
      }

-- | A fully qualified name for something defined in a capnproto schema.
-- this includes a local name within a file, and the file's capnp id.
data CapnpQ = CapnpQ
  { local :: LocalQ,
    fileId :: !Word64
  }
  deriving (Show, Read, Eq, Ord)

data GlobalQ = GlobalQ
  { local :: LocalQ,
    globalNS :: NS
  }
  deriving (Show, Read, Eq, Ord)

emptyNS :: NS
emptyNS = NS []

mkLocal :: NS -> UnQ -> LocalQ
mkLocal localNS localUnQ = LocalQ {localNS, localUnQ}

unQToLocal :: UnQ -> LocalQ
unQToLocal = mkLocal emptyNS

instance MkSub LocalQ where
  mkSub q = mkLocal (localQToNS q)

instance MkSub GlobalQ where
  mkSub GlobalQ {local, ..} unQ = GlobalQ {local = mkSub local unQ, ..}

instance MkSub CapnpQ where
  mkSub CapnpQ {local, ..} unQ = CapnpQ {local = mkSub local unQ, ..}

localQToNS :: LocalQ -> NS
localQToNS LocalQ {localUnQ = UnQ part, localNS = NS parts} = NS (part : parts)

localToUnQ :: LocalQ -> UnQ
localToUnQ LocalQ {localUnQ, localNS}
  | localNS == emptyNS = localUnQ
  | otherwise = UnQ (renderLocalNS localNS <> "'" <> renderUnQ localUnQ)

renderUnQ :: UnQ -> T.Text
renderUnQ (UnQ name)
  | name `S.member` keywords = name <> "_"
  | otherwise = name
  where
    keywords =
      S.fromList
        [ "as",
          "case",
          "of",
          "class",
          "data",
          "family",
          "instance",
          "default",
          "deriving",
          "do",
          "forall",
          "foreign",
          "hiding",
          "if",
          "then",
          "else",
          "import",
          "infix",
          "infixl",
          "infixr",
          "let",
          "in",
          "mdo",
          "module",
          "newtype",
          "proc",
          "qualified",
          "rec",
          "type",
          "where"
        ]

renderLocalQ :: LocalQ -> T.Text
renderLocalQ = renderUnQ . localToUnQ

renderLocalNS :: NS -> T.Text
renderLocalNS (NS parts) = mconcat $ intersperse "'" $ reverse parts

getterName, setterName, hasFnName, newFnName :: LocalQ -> UnQ
getterName = accessorName "get_"
setterName = accessorName "set_"
hasFnName = accessorName "has_"
newFnName = accessorName "new_"

accessorName :: T.Text -> LocalQ -> UnQ
accessorName prefix = UnQ . (prefix <>) . renderLocalQ

-- | Lower-case the first letter of a name, making it legal as the name of a
-- variable (as opposed to a type or data constructor).
valueName :: LocalQ -> UnQ
valueName = lowerFstName

typeVarName :: UnQ -> T.Text
typeVarName (UnQ txt) = lowerFst txt

-- | Lower-case the first letter of a name
lowerFstName :: LocalQ -> UnQ
lowerFstName name = UnQ $ lowerFst $ renderLocalQ name

lowerFst :: T.Text -> T.Text
lowerFst txt = case T.unpack txt of
  [] -> ""
  (c : cs) -> T.pack $ toLower c : cs