{-# LANGUAGE CPP , UnicodeSyntax , NoImplicitPrelude , OverloadedStrings , ScopedTypeVariables , DeriveDataTypeable #-} -------------------------------------------------------------------------------- -- | -- Module : Text.Repr -- Copyright : (c) 2009–2011 Bas van Dijk -- License : BSD-style (see the file LICENSE) -- Maintainer : Bas van Dijk -- -- Textual representation of values. -- -------------------------------------------------------------------------------- module Text.Repr ( Repr , extract , renderer , Renderer , Precedence , Fixity(..) , () , pure , repr -- * Utilities -- | Handy utilities when writing type class instances for @Reprs@. , constant , to, to2 , app, app2 , infx ) where -------------------------------------------------------------------------------- -- Imports -------------------------------------------------------------------------------- -- from base: import Prelude ( Enum(..) , Bounded(..) , Num(..) , Real(..) , Integral(..) , Fractional(..) , Floating(..) , RealFrac(..) , RealFloat(..) , undefined ) import Data.Eq ( Eq(..) ) import Data.Ord ( Ord(..) ) import Data.String ( IsString(..) ) import Data.Monoid ( Monoid(..) ) import Data.Bits ( Bits(..) ) import Data.Function ( ($) ) import Data.Functor ( fmap ) import Data.Fixed ( HasResolution(..) ) import Data.List ( map, zipWith, take, length, unzip ) import Data.Int ( Int ) import Data.Ix ( Ix(..) ) import Foreign.Storable ( Storable(..) ) import Foreign.Ptr ( castPtr ) import Data.Typeable ( Typeable ) import Control.Applicative ( liftA2 ) import Control.Monad ( return ) import Control.Arrow ( first, (&&&) ) import Text.Show ( Show(..) ) import Text.Read ( Read(..) ) #if MIN_VERSION_base(4,0,0) import Control.Exception ( Exception(..) ) #endif #if __GLASGOW_HASKELL__ < 700 import Control.Monad ( (>>=), fail ) #endif -- from base-unicode-symbols: import Data.Function.Unicode ( (∘) ) import Data.Bool.Unicode ( (∧), (∨) ) -- from random: import System.Random ( Random(..) ) -- from string-combinators: import Data.String.Combinators ( (<>), (<+>) , between, parens, thenParens, brackets , punctuate, fromShow, integer, int, unwords ) -- from dstring: import Data.DString ( DString, fromShowS, toShowS ) -------------------------------------------------------------------------------- -- Repr -------------------------------------------------------------------------------- {-| @Repr α@ is a value of type @α@ paired with a way to render that value to its textual representation. @Repr@s follow the property that given a @Repr@ @r@ if you evaluate the textual representation of @r@ you should get the value or @r@. Note that @Repr α@ has an instance for most classes in 'base' provided that @α@ has instances for the respected classes. This allows you to write a numeric expression of type @Repr α@. For example: @ *Repr> let r = 1.5 + 2 + (3 + (-4) * (5 - pi / sqrt 6)) :: Repr Double @ You can 'extract' the value of @r@: @ *Repr> extract r 17.281195923884734 @ And you can render @r@ to its textual representation using 'show': @ *Repr> show r \"1.5 + 2.0 + (3.0 + negate 4.0 * (5.0 - pi / sqrt 6.0))\" @ -} data Repr α = Repr { extract ∷ α -- ^ Extract the value of the @Repr@. , renderer ∷ Renderer -- ^ Extract the renderer of the @Repr@. } deriving Typeable {-| To render you need to supply the precedence and fixity of the enclosing context. For more documentation about precedence and fixity see: The reason the renderer returns a 'DString', instead of for example a 'String' has to do with efficiency. The rendering of expressions involves lots of left-factored appends i.e.: @((a ++ b) ++ c) ++ d@. A 'DString', which is equivalent to a 'ShowS', has a O(1) append operation while a 'String' has a O(n) append. -} type Renderer = Precedence → Fixity → DString {-| The precedence of operators and function application. * Operators usually have a precedence in the range of 0 to 9. * Function application always has precedence 10. -} type Precedence = Int -- | Precedence of function application. funAppPrec ∷ Precedence funAppPrec = 10 -- | Fixity of operators. data Fixity = Non -- ^ No fixity information. | L -- ^ Left associative operator. | R -- ^ Right associative operator. deriving Eq {-| @x \ s@ annotates the rendering with the given string. The rendering wil look like: @\"({\- s -\} ...)\"@ where @...@ is the rendering of @x@. This combinator is handy when you want to render the ouput of a function and you want to see how the parameters of the function contribute to the result. For example, suppose you defined the following function @f@: @ f p0 p1 p2 = p0 ^ 2 + sqrt p1 * ([p2..] !! 10) @ You can then apply @f@ to some parameters annotated with some descriptive strings (the name of the parameter is usally a good idea): @ f (1 \ \"p0\") (2 \ \"p1\") (3 \ \"p2\") @ The rendering will then look like: @ \"({\- p0 -\} fromInteger 1) * ({\- p0 -\} fromInteger 1) + sqrt ({\- p1 -\} (fromInteger 2)) * enumFrom ({\- p2 -\} (fromInteger 3)) !! 10\" @ -} () ∷ Repr α → DString → Repr α (Repr x rx) s = constant x $ parens $ between "{- " " -}" s <+> runRenderer rx {-| @pure x@ constructs a 'Repr' which has @x@ as value and the showed @x@ as rendering. For example: @ *Repr> let r = pure [1,2,3] *Repr> extract r [1,2,3] *Repr> show r \"[1,2,3]\" @ -} pure ∷ Show α ⇒ α → Repr α pure x = repr x $ \prec _ → fromShowS $ showsPrec prec x -- | Construct a @Repr@ from the given value and its renderer. repr ∷ α → Renderer → Repr α repr = Repr -------------------------------------------------------------------------------- -- Handy CPP macro's -------------------------------------------------------------------------------- #define PURE(N) N = pure ∘ (N) #define TO(N) N = to (N) #define TO2(N) (N) = to2 (N) #define FROM(N) N = from (N) "N" #define FROM2(N) N = from2 (N) "N" #define INFX(F,P,N) (N) = infx F P (N) "N" #define APP(N) N = app (N) "N" #define APP2(N) N = app2 (N) "N" #define APP2SHOW(N) N = app2Show (N) "N" #define TUP(N) N = tup (N) "N" #define CONSTANT(N) N = constant (N) "N" -------------------------------------------------------------------------------- -- Instances -------------------------------------------------------------------------------- instance Show (Repr α) where showsPrec prec r = toShowS $ renderer r prec Non instance Read α ⇒ Read (Repr α) where readsPrec prec str = map (\(x, rst) → ( constant x $ fromString $ take (length str - length rst) str , rst ) ) $ readsPrec prec str instance IsString α ⇒ IsString (Repr α) where fromString = liftA2 constant fromString fromShow instance Num α ⇒ Num (Repr α) where PURE(fromInteger) INFX(L, 6, +) INFX(L, 6, -) INFX(L, 7, *) APP(negate) APP(abs) APP(signum) instance Real α ⇒ Real (Repr α) where TO(toRational) instance Integral α ⇒ Integral (Repr α) where APP2(quot) APP2(rem) APP2(div) APP2(mod) TUP(quotRem) TUP(divMod) TO(toInteger) instance Fractional α ⇒ Fractional (Repr α) where INFX(L, 7, /) APP(recip) PURE(fromRational) instance Floating α ⇒ Floating (Repr α) where CONSTANT(pi) INFX(R, 8, **) APP2(logBase) APP(exp) APP(sqrt) APP(log) APP(sin) APP(tan) APP(cos) APP(asin) APP(atan) APP(acos) APP(sinh) APP(tanh) APP(cosh) APP(asinh) APP(atanh) APP(acosh) instance RealFrac α ⇒ RealFrac (Repr α) where TO(truncate) TO(round) TO(ceiling) TO(floor) properFraction (Repr x rx) = let (n, f) = properFraction x in (n, repr f $ "snd" `apply` parens ("properFraction" <+> args [rx])) instance RealFloat α ⇒ RealFloat (Repr α) where TO(floatRadix) TO(floatDigits) TO(floatRange) TO(decodeFloat) TO(isNaN) TO(isInfinite) TO(isDenormalized) TO(isNegativeZero) TO(isIEEE) TO(exponent) APP(significand) APP2(atan2) FROM2(encodeFloat) scaleFloat i = app (scaleFloat i) ("scaleFloat" <+> int i) instance Enum α ⇒ Enum (Repr α) where APP(succ) APP(pred) FROM(toEnum) TO(fromEnum) enumFrom (Repr x rx) = enum "From" (enumFrom x) [rx] enumFromThen (Repr x rx) (Repr y ry) = enum "FromThen" (enumFromThen x y) [rx, ry] enumFromTo (Repr x rx) (Repr y ry) = enum "FromTo" (enumFromTo x y) [rx, ry] enumFromThenTo (Repr x rx) (Repr y ry) (Repr z rz) = enum "FromThenTo" (enumFromThenTo x y z) [rx, ry, rz] enum ∷ DString → [α] → [Renderer] → [Repr α] enum enumStr xs rxs = list xs (("enum" <> enumStr) `applies` rxs) instance Ord α ⇒ Ord (Repr α) where compare = to2 compare TO2(<) TO2(>=) TO2(>) TO2(<=) APP2(max) APP2(min) instance Eq α ⇒ Eq (Repr α) where TO2(==) TO2(/=) instance Bounded α ⇒ Bounded (Repr α) where CONSTANT(minBound) CONSTANT(maxBound) instance Monoid α ⇒ Monoid (Repr α) where CONSTANT(mempty) APP2(mappend) mconcat reprs = let (xs, rs) = unzipReprs reprs in Repr (mconcat xs) ("mconcat" `apply` brackets (commas rs)) unzipReprs ∷ [Repr α] → ([α], [Renderer]) unzipReprs = unzip ∘ map (extract &&& renderer) instance Bits α ⇒ Bits (Repr α) where INFX(L, 7, .&.) INFX(L, 5, .|.) APP2(xor) APP(complement) APP2SHOW(shift) APP2SHOW(rotate) FROM(bit) APP2SHOW(setBit) APP2SHOW(clearBit) APP2SHOW(complementBit) TO(testBit) TO(bitSize) TO(isSigned) APP2SHOW(shiftL) APP2SHOW(shiftR) APP2SHOW(rotateL) APP2SHOW(rotateR) #if MIN_VERSION_base(4,2,0) instance HasResolution α ⇒ HasResolution (Repr α) where resolution (_ ∷ p (Repr α)) = resolution (undefined ∷ p α) #else instance HasResolution α ⇒ HasResolution (Repr α) where TO(resolution) #endif instance Ix α ⇒ Ix (Repr α) where range (Repr b rb, Repr e re) = list (range (b, e)) ("range" `apply` parens (commas [rb, re])) index (b, e) p = index (extract b, extract e) (extract p) inRange (b, e) p = inRange (extract b, extract e) (extract p) rangeSize (b, e) = rangeSize (extract b, extract e) instance (Show α, Storable α) ⇒ Storable (Repr α) where TO(sizeOf) TO(alignment) peekElemOff rPtr off = do x ← peekElemOff (castPtr rPtr) off return $ pure x ("peekElemOff" <+> showFuncArg rPtr <+> showFuncArg off) peekByteOff ptr off = do x ← peekByteOff ptr off return $ pure x ("peekByteOff" <+> showFuncArg ptr <+> showFuncArg off) peek rPtr = do x ← peek (castPtr rPtr) return $ pure x ("peek" <+> showFuncArg rPtr) poke rPtr r = poke (castPtr rPtr) (extract r) pokeElemOff rPtr off r = pokeElemOff (castPtr rPtr) off (extract r) pokeByteOff ptr off r = pokeByteOff ptr off (extract r) #if MIN_VERSION_base(4,0,0) instance Exception α ⇒ Exception (Repr α) where TO(toException) fromException se = fmap (\x → pure x ( "fromJust" <+> parens ( "fromException" <+> parens ( "toException" <+> parens (showFuncArg x) ) ) ) ) $ fromException se #endif instance (Random α, Show α) ⇒ Random (Repr α) where randomR (b, e) = first pure ∘ randomR (extract b, extract e) random = first pure ∘ random -------------------------------------------------------------------------------- -- Utility functions -------------------------------------------------------------------------------- mapRepr ∷ (α → β) → (Renderer → Renderer) → (Repr α → Repr β) mapRepr f g = \(Repr x rx) → repr (f x) (g rx) mapRepr2 ∷ (α → β → γ) → (Renderer → Renderer → Renderer) → (Repr α → Repr β → Repr γ) mapRepr2 f g = \(Repr x rx) (Repr y ry) → repr (f x y) (g rx ry) runRenderer ∷ Renderer → DString runRenderer r = r 0 Non -- | For example: @pi = 'constant' 'pi' \"pi\"@ constant ∷ α → DString → Repr α constant x xStr = repr x $ \_ _ → xStr showFuncArg ∷ Show α ⇒ α → DString showFuncArg = fromShowS ∘ showsPrec funAppPrec from ∷ Show α ⇒ (α → β) → DString → (α → Repr β) from f fStr = \x → repr (f x) $ fStr `apply` showFuncArg x from2 ∷ (Show α, Show β) ⇒ (α → β → γ) → DString → (α → β → Repr γ) from2 f fStr = \x y → repr (f x y) $ fStr `apply`(showFuncArg x <+> showFuncArg y) -- | For example: @toInteger = 'to' 'toInteger'@ to ∷ (α → β) → (Repr α → β) to f = f ∘ extract -- | For example: @(<) = 'to2' ('<')@ to2 ∷ (α → β → γ) → (Repr α → Repr β → γ) to2 f = \x y → f (extract x) (extract y) -- | For example: @abs = 'app' 'abs' \"abs\"@ app ∷ (α → β) → DString → (Repr α → Repr β) app f fStr = mapRepr f (\rx → fStr `applies` [rx]) -- | For example: @div = 'app2' 'div' \"div\"@ app2 ∷ (α → β → γ) → DString → (Repr α → Repr β → Repr γ) app2 f fStr = mapRepr2 f (\rx ry → fStr `applies` [rx, ry]) app2Show ∷ Show β ⇒ (α → β → α) → DString → (Repr α → β → Repr α) app2Show f fStr = \(Repr x rx) y → repr (f x y) (fStr `applies` [rx, \prec _ → fromShowS $ showsPrec prec y]) -- | For example: @(+) = 'infx' 'L' 6 ('+') \"+\"@ infx ∷ Fixity → Precedence → (α → β → γ) → DString → (Repr α → Repr β → Repr γ) infx opFix opPrec op opStr = mapRepr2 op (bin opFix opPrec opStr) bin ∷ Fixity → Precedence → DString → Renderer → Renderer → Renderer bin opFix opPrec opStr l r = \prec fixity → (prec > opPrec ∨ (prec == opPrec ∧ fixity /= Non ∧ fixity /= opFix)) `thenParens` (l opPrec L <+> opStr <+> r opPrec R) apply ∷ DString → DString → Renderer fStr `apply` argsStr = \prec _ → (prec >= funAppPrec) `thenParens` (fStr <+> argsStr) applies ∷ DString → [Renderer] → Renderer applies fStr rs = fStr `apply` args rs args ∷ [Renderer] → DString args = unwords ∘ map (\rx → rx funAppPrec Non) list ∷ [α] → Renderer → [Repr α] list xs rXs = zipWith combine [0..] xs where combine ix x = repr x $ bin L 9 "!!" rXs (\_ _ → integer ix) commas ∷ [Renderer] → DString commas = unwords ∘ punctuate "," ∘ map runRenderer tup ∷ (α → β → (γ, δ)) → DString → (Repr α → Repr β → (Repr γ, Repr δ)) tup f fStr = \(Repr x rx) (Repr y ry) → let (q, r) = f x y s = parens (fStr <+> args [rx, ry]) in ( repr q $ "fst" `apply` s , repr r $ "snd" `apply` s ) -- The End ---------------------------------------------------------------------