packages feed

atomo-0.4: src/Atomo/Format/Types.hs

{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, TypeSynonymInstances #-}
module Atomo.Format.Types where

import Control.Monad.RWS
import Data.Typeable
import Text.PrettyPrint
import qualified Data.Text as T

import Atomo.Pretty
import Atomo.Types


-- | The core types of formats to use.
data Segment
    -- | Arbitrary text.
    = SChunk T.Text

    -- | %s
    | SString

    -- | %d
    | SDecimal

    -- | %x
    | SHex

    -- | %o
    | SOctal

    -- | %b
    | SBinary

    -- | %r
    | SRadix

    -- | %f
    | SFloat

    -- | %e
    | SExponent

    -- | %g
    | SGeneral

    -- | %c
    | SCharacter

    -- | %a
    | SAsString

    -- | %v
    | SAny

    -- | %p(...)
    | SPluralize [Flagged] (Maybe [Flagged])

    -- | %l(...)
    | SLowercase [Flagged]

    -- | %c(...)
    | SCapitalize [Flagged]

    -- | %u(...)
    | SUppercase [Flagged]

    -- | %_
    | SSkip

    -- | %%
    | SIndirection

    -- | %{...}
    | SIterate [Flagged]

    -- | %^
    | SBreak

    -- | %[...]+(...)? where + = one or more and ? = optional (the default)
    | SConditional [[Flagged]] (Maybe [Flagged])

    -- | %j(...)+ where + = one or more
    | SJustify [[Flagged]]
    deriving (Show, Typeable)

-- Various modifiers, for our segments.
data Flag
    -- | FNumber
    -- The Maybe is Nothing if they used #, in which case we use the number
    -- of remaining values.
    = FNumber (Maybe Int)

    -- | FSome symbol presumeably known by the segment.
    | FSymbol Char

    -- | FUsed by %f and %d
    | FZeroPad

    -- | FUsed by %f
    | FPrecision Int
    deriving (Eq, Show, Typeable)

data FormatState =
    FormatState
        { fsInput :: [Value]
        , fsPosition :: Int
        , fsStop :: Bool
        , fsIterating :: [Value]
        }

type Flagged = (Segment, [Flag])

type FormatterT = RWST Format T.Text FormatState

type Formatter = FormatterT VM

type Format = [Flagged]

instance Pretty Format where
    prettyFrom _ fs = hcat (map pretty fs)

instance Pretty Flagged where
    prettyFrom _ (SChunk s, _) = text (T.unpack (T.replace "\"" "\\\"" s))
    prettyFrom _ (s, fs) = char '%' <> hcat (map pretty fs) <> pretty s

instance Pretty Flag where
    prettyFrom _ (FNumber Nothing) = char '#'
    prettyFrom _ (FNumber (Just n)) = int n
    prettyFrom _ (FSymbol c) = char c
    prettyFrom _ FZeroPad = char '0'
    prettyFrom _ (FPrecision n) = char '.' <> int n

instance Pretty Segment where
    prettyFrom _ (SChunk _) = error "pretty-printing a Chunk segment"
    prettyFrom _ SString = char 's'
    prettyFrom _ SDecimal = char 'd'
    prettyFrom _ SHex = char 'x'
    prettyFrom _ SOctal = char 'o'
    prettyFrom _ SBinary = char 'b'
    prettyFrom _ SRadix = char 'r'
    prettyFrom _ SFloat = char 'f'
    prettyFrom _ SExponent = char 'e'
    prettyFrom _ SGeneral = char 'g'
    prettyFrom _ SCharacter = char 'c'
    prettyFrom _ SAsString = char 'a'
    prettyFrom _ SAny = char 'v'
    prettyFrom _ (SPluralize s Nothing) = char 'p' <> parens (pretty s)
    prettyFrom _ (SPluralize s (Just p)) =
        char 'p' <> parens (pretty s) <> parens (pretty p)
    prettyFrom _ (SLowercase fs) = char 'l' <> parens (pretty fs)
    prettyFrom _ (SCapitalize fs) = char 'c' <> parens (pretty fs)
    prettyFrom _ (SUppercase fs) = char 'u' <> parens (pretty fs)
    prettyFrom _ SSkip = char '_'
    prettyFrom _ SIndirection = char '%'
    prettyFrom _ (SIterate fs) = braces (pretty fs)
    prettyFrom _ SBreak = char '^'
    prettyFrom _ (SConditional bs Nothing) =
        hcat (map (brackets . pretty) bs)
    prettyFrom _ (SConditional bs (Just d)) =
        hcat (map (brackets . pretty) bs) <> parens (pretty d)
    prettyFrom _ (SJustify fs) =
        char 'j' <> hcat (map (parens . pretty) fs)


startState :: [Value] -> FormatState
startState vs = FormatState vs 0 False []