-% ------------------------------------------------------------------------------
-% $Id: Read.lhs,v 1.3 2001/12/21 15:07:25 simonmar Exp $
-%
-% (c) The University of Glasgow, 1994-2000
-%
+\begin{code}
+{-# OPTIONS_GHC -fno-implicit-prelude #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.Read
+-- Copyright : (c) The University of Glasgow, 1994-2002
+-- License : see libraries/base/LICENSE
+--
+-- Maintainer : cvs-ghc@haskell.org
+-- Stability : internal
+-- Portability : non-portable (GHC Extensions)
+--
+-- The 'Read' class and instances for basic data types.
+--
+-----------------------------------------------------------------------------
+
+module GHC.Read
+ ( Read(..) -- class
+
+ -- ReadS type
+ , ReadS -- :: *; = String -> [(a,String)]
+
+ -- utility functions
+ , reads -- :: Read a => ReadS a
+ , readp -- :: Read a => ReadP a
+ , readEither -- :: Read a => String -> Either String a
+ , read -- :: Read a => String -> a
+
+ -- H98 compatibility
+ , lex -- :: ReadS String
+ , lexLitChar -- :: ReadS String
+ , readLitChar -- :: ReadS Char
+ , lexDigits -- :: ReadS String
+
+ -- defining readers
+ , lexP -- :: ReadPrec Lexeme
+ , paren -- :: ReadPrec a -> ReadPrec a
+ , parens -- :: ReadPrec a -> ReadPrec a
+ , list -- :: ReadPrec a -> ReadPrec [a]
+ , choose -- :: [(String, ReadPrec a)] -> ReadPrec a
+ , readListDefault, readListPrecDefault
+
+ -- Temporary
+ , readParen
+ )
+ where
-\section[GHC.Read]{Module @GHC.Read@}
+import qualified Text.ParserCombinators.ReadP as P
-Instances of the Read class.
+import Text.ParserCombinators.ReadP
+ ( ReadP
+ , ReadS
+ , readP_to_S
+ )
-\begin{code}
-{-# OPTIONS -fno-implicit-prelude #-}
+import qualified Text.Read.Lex as L
+-- Lex exports 'lex', which is also defined here,
+-- hence the qualified import.
+-- We can't import *anything* unqualified, because that
+-- confuses Haddock.
-module GHC.Read where
+import Text.ParserCombinators.ReadPrec
import Data.Maybe
import Data.Either
import {-# SOURCE #-} GHC.Err ( error )
-import GHC.Enum ( Enum(..), maxBound )
+#ifndef __HADDOCK__
+import {-# SOURCE #-} GHC.Unicode ( isDigit )
+#endif
import GHC.Num
import GHC.Real
import GHC.Float
import GHC.List
-import GHC.Show -- isAlpha etc
+import GHC.Show
import GHC.Base
+import GHC.Arr
\end{code}
-%*********************************************************
-%* *
-\subsection{The @Read@ class}
-%* *
-%*********************************************************
-
-Note: if you compile this with -DNEW_READS_REP, you'll get
-a (simpler) ReadS representation that only allow one valid
-parse of a string of characters, instead of a list of
-possible ones.
-
-[changing the ReadS rep has implications for the deriving
-machinery for Read, a change that hasn't been made, so you
-probably won't want to compile in this new rep. except
-when in an experimental mood.]
-
-\begin{code}
-
-#ifndef NEW_READS_REP
-type ReadS a = String -> [(a,String)]
-#else
-type ReadS a = String -> Maybe (a,String)
-#endif
-
-class Read a where
- readsPrec :: Int -> ReadS a
-
- readList :: ReadS [a]
- readList = readList__ reads
-\end{code}
-
-In this module we treat [(a,String)] as a monad in Control.MonadPlus
-But Control.MonadPlus isn't defined yet, so we simply give local
-declarations for mzero and guard suitable for this particular
-type. It would also be reasonably to move Control.MonadPlus to GHC.Base
-along with Control.Monad and Functor, but that seems overkill for one
-example
-
-\begin{code}
-mzero :: [a]
-mzero = []
-
-guard :: Bool -> [()]
-guard True = [()]
-guard False = []
-\end{code}
-
-%*********************************************************
-%* *
-\subsection{Utility functions}
-%* *
-%*********************************************************
-
-\begin{code}
-reads :: (Read a) => ReadS a
-reads = readsPrec 0
-
-read :: (Read a) => String -> a
-read s =
- case read_s s of
-#ifndef NEW_READS_REP
- [x] -> x
- [] -> error "Prelude.read: no parse"
- _ -> error "Prelude.read: ambiguous parse"
-#else
- Just x -> x
- Nothing -> error "Prelude.read: no parse"
-#endif
- where
- read_s str = do
- (x,str1) <- reads str
- ("","") <- lex str1
- return x
-\end{code}
\begin{code}
+-- | @'readParen' 'True' p@ parses what @p@ parses, but surrounded with
+-- parentheses.
+--
+-- @'readParen' 'False' p@ parses what @p@ parses, but optionally
+-- surrounded with parentheses.
readParen :: Bool -> ReadS a -> ReadS a
+-- A Haskell 98 function
readParen b g = if b then mandatory else optional
where optional r = g r ++ mandatory r
mandatory r = do
(x,t) <- optional s
(")",u) <- lex t
return (x,u)
-
-
-readList__ :: ReadS a -> ReadS [a]
-
-readList__ readx
- = readParen False (\r -> do
- ("[",s) <- lex r
- readl s)
- where readl s =
- (do { ("]",t) <- lex s ; return ([],t) }) ++
- (do { (x,t) <- readx s ; (xs,u) <- readl2 t ; return (x:xs,u) })
-
- readl2 s =
- (do { ("]",t) <- lex s ; return ([],t) }) ++
- (do { (",",t) <- lex s ; (x,u) <- readx t ; (xs,v) <- readl2 u ; return (x:xs,v) })
-
\end{code}
%*********************************************************
%* *
-\subsection{Lexical analysis}
+\subsection{The @Read@ class}
%* *
%*********************************************************
-This lexer is not completely faithful to the Haskell lexical syntax.
-Current limitations:
- Qualified names are not handled properly
- A `--' does not terminate a symbol
- Octal and hexidecimal numerics are not recognized as a single token
-
\begin{code}
-lex :: ReadS String
-
-lex "" = return ("","")
-lex (c:s) | isSpace c = lex (dropWhile isSpace s)
-lex ('\'':s) = do
- (ch, '\'':t) <- lexLitChar s
- guard (ch /= "'")
- return ('\'':ch++"'", t)
-lex ('"':s) = do
- (str,t) <- lexString s
- return ('"':str, t)
-
- where
- lexString ('"':s) = return ("\"",s)
- lexString s = do
- (ch,t) <- lexStrItem s
- (str,u) <- lexString t
- return (ch++str, u)
-
-
- lexStrItem ('\\':'&':s) = return ("\\&",s)
- lexStrItem ('\\':c:s) | isSpace c = do
- ('\\':t) <- return (dropWhile isSpace s)
- return ("\\&",t)
- lexStrItem s = lexLitChar s
-
-lex (c:s) | isSingle c = return ([c],s)
- | isSym c = do
- (sym,t) <- return (span isSym s)
- return (c:sym,t)
- | isAlpha c = do
- (nam,t) <- return (span isIdChar s)
- return (c:nam, t)
- | isDigit c = do
-{- Removed, 13/03/2000 by SDM.
- Doesn't work, and not required by Haskell report.
- let
- (pred, s', isDec) =
- case s of
- ('o':rs) -> (isOctDigit, rs, False)
- ('O':rs) -> (isOctDigit, rs, False)
- ('x':rs) -> (isHexDigit, rs, False)
- ('X':rs) -> (isHexDigit, rs, False)
- _ -> (isDigit, s, True)
--}
- (ds,s) <- return (span isDigit s)
- (fe,t) <- lexFracExp s
- return (c:ds++fe,t)
- | otherwise = mzero -- bad character
- where
- isSingle c = c `elem` ",;()[]{}_`"
- isSym c = c `elem` "!@#$%&*+./<=>?\\^|:-~"
- isIdChar c = isAlphaNum c || c `elem` "_'"
-
- lexFracExp ('.':c:cs) | isDigit c = do
- (ds,t) <- lex0Digits cs
- (e,u) <- lexExp t
- return ('.':c:ds++e,u)
- lexFracExp s = return ("",s)
-
- lexExp (e:s) | e `elem` "eE" =
- (do
- (c:t) <- return s
- guard (c `elem` "+-")
- (ds,u) <- lexDecDigits t
- return (e:c:ds,u)) ++
- (do
- (ds,t) <- lexDecDigits s
- return (e:ds,t))
-
- lexExp s = return ("",s)
-
-lexDigits :: ReadS String
-lexDigits = lexDecDigits
-
-lexDecDigits :: ReadS String
-lexDecDigits = nonnull isDigit
-
-lexOctDigits :: ReadS String
-lexOctDigits = nonnull isOctDigit
-
-lexHexDigits :: ReadS String
-lexHexDigits = nonnull isHexDigit
-
--- 0 or more digits
-lex0Digits :: ReadS String
-lex0Digits s = return (span isDigit s)
-
-nonnull :: (Char -> Bool) -> ReadS String
-nonnull p s = do
- (cs@(_:_),t) <- return (span p s)
- return (cs,t)
-
-lexLitChar :: ReadS String
-lexLitChar ('\\':s) = do
- (esc,t) <- lexEsc s
- return ('\\':esc, t)
- where
- lexEsc (c:s) | c `elem` escChars = return ([c],s)
- lexEsc s@(d:_) | isDigit d = checkSize 10 lexDecDigits s
- lexEsc ('o':d:s) | isOctDigit d = checkSize 8 lexOctDigits (d:s)
- lexEsc ('O':d:s) | isOctDigit d = checkSize 8 lexOctDigits (d:s)
- lexEsc ('x':d:s) | isHexDigit d = checkSize 16 lexHexDigits (d:s)
- lexEsc ('X':d:s) | isHexDigit d = checkSize 16 lexHexDigits (d:s)
- lexEsc ('^':c:s) | c >= '@' && c <= '_' = [(['^',c],s)] -- cf. cntrl in 2.6 of H. report.
- lexEsc s@(c:_) | isUpper c = fromAsciiLab s
- lexEsc _ = mzero
-
- escChars = "abfnrtv\\\"'"
-
- fromAsciiLab (x:y:z:ls) | isUpper y && (isUpper z || isDigit z) &&
- [x,y,z] `elem` asciiEscTab = return ([x,y,z], ls)
- fromAsciiLab (x:y:ls) | isUpper y &&
- [x,y] `elem` asciiEscTab = return ([x,y], ls)
- fromAsciiLab _ = mzero
-
- asciiEscTab = "DEL" : asciiTab
-
- {-
- Check that the numerically escaped char literals are
- within accepted boundaries.
-
- Note: this allows char lits with leading zeros, i.e.,
- \0000000000000000000000000000001.
- -}
- checkSize base f str = do
- (num, res) <- f str
- if toAnInteger base num > toInteger (ord maxBound) then
- mzero
- else
- case base of
- 8 -> return ('o':num, res)
- 16 -> return ('x':num, res)
- _ -> return (num, res)
-
- toAnInteger base = foldl (\ acc n -> acc*base + toInteger (digitToInt n)) 0
-
-
-lexLitChar (c:s) = return ([c],s)
-lexLitChar "" = mzero
-
-digitToInt :: Char -> Int
-digitToInt c
- | isDigit c = fromEnum c - fromEnum '0'
- | c >= 'a' && c <= 'f' = fromEnum c - fromEnum 'a' + 10
- | c >= 'A' && c <= 'F' = fromEnum c - fromEnum 'A' + 10
- | otherwise = error ("Char.digitToInt: not a digit " ++ show c) -- sigh
+------------------------------------------------------------------------
+-- class Read
+
+-- | Parsing of 'String's, producing values.
+--
+-- Minimal complete definition: 'readsPrec' (or, for GHC only, 'readPrec')
+--
+-- Derived instances of 'Read' make the following assumptions, which
+-- derived instances of 'Text.Show.Show' obey:
+--
+-- * If the constructor is defined to be an infix operator, then the
+-- derived 'Read' instance will parse only infix applications of
+-- the constructor (not the prefix form).
+--
+-- * Associativity is not used to reduce the occurrence of parentheses,
+-- although precedence may be.
+--
+-- * If the constructor is defined using record syntax, the derived 'Read'
+-- will parse only the record-syntax form, and furthermore, the fields
+-- must be given in the same order as the original declaration.
+--
+-- * The derived 'Read' instance allows arbitrary Haskell whitespace
+-- between tokens of the input string. Extra parentheses are also
+-- allowed.
+--
+-- For example, given the declarations
+--
+-- > infixr 5 :^:
+-- > data Tree a = Leaf a | Tree a :^: Tree a
+--
+-- the derived instance of 'Read' is equivalent to
+--
+-- > instance (Read a) => Read (Tree a) where
+-- >
+-- > readsPrec d r = readParen (d > up_prec)
+-- > (\r -> [(u:^:v,w) |
+-- > (u,s) <- readsPrec (up_prec+1) r,
+-- > (":^:",t) <- lex s,
+-- > (v,w) <- readsPrec (up_prec+1) t]) r
+-- >
+-- > ++ readParen (d > app_prec)
+-- > (\r -> [(Leaf m,t) |
+-- > ("Leaf",s) <- lex r,
+-- > (m,t) <- readsPrec (app_prec+1) s]) r
+-- >
+-- > where up_prec = 5
+-- > app_prec = 10
+--
+-- Note that right-associativity of @:^:@ is unused.
+
+class Read a where
+ -- | attempts to parse a value from the front of the string, returning
+ -- a list of (parsed value, remaining string) pairs. If there is no
+ -- successful parse, the returned list is empty.
+ --
+ -- Derived instances of 'Read' and 'Text.Show.Show' satisfy the following:
+ --
+ -- * @(x,\"\")@ is an element of
+ -- @('readsPrec' d ('Text.Show.showsPrec' d x \"\"))@.
+ --
+ -- That is, 'readsPrec' parses the string produced by
+ -- 'Text.Show.showsPrec', and delivers the value that
+ -- 'Text.Show.showsPrec' started with.
+
+ readsPrec :: Int -- ^ the operator precedence of the enclosing
+ -- context (a number from @0@ to @11@).
+ -- Function application has precedence @10@.
+ -> ReadS a
+
+ -- | The method 'readList' is provided to allow the programmer to
+ -- give a specialised way of parsing lists of values.
+ -- For example, this is used by the predefined 'Read' instance of
+ -- the 'Char' type, where values of type 'String' should be are
+ -- expected to use double quotes, rather than square brackets.
+ readList :: ReadS [a]
+
+ -- | Proposed replacement for 'readsPrec' using new-style parsers (GHC only).
+ readPrec :: ReadPrec a
+
+ -- | Proposed replacement for 'readList' using new-style parsers (GHC only).
+ readListPrec :: ReadPrec [a]
+
+ -- default definitions
+ readsPrec = readPrec_to_S readPrec
+ readList = readPrec_to_S (list readPrec) 0
+ readPrec = readS_to_Prec readsPrec
+ readListPrec = readS_to_Prec (\_ -> readList)
+
+readListDefault :: Read a => ReadS [a]
+-- ^ Use this to define the 'readList' method, if you don't want a special
+-- case (GHC only; for other systems the default suffices).
+readListDefault = readPrec_to_S readListPrec 0
+
+readListPrecDefault :: Read a => ReadPrec [a]
+-- ^ Use this to define the 'readListPrec' method, if you
+-- don't want a special case (GHC only).
+readListPrecDefault = list readPrec
+
+------------------------------------------------------------------------
+-- utility functions
+
+-- | equivalent to 'readsPrec' with a precedence of 0.
+reads :: Read a => ReadS a
+reads = readsPrec minPrec
+
+readp :: Read a => ReadP a
+readp = readPrec_to_P readPrec minPrec
+
+readEither :: Read a => String -> Either String a
+readEither s =
+ case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of
+ [x] -> Right x
+ [] -> Left "Prelude.read: no parse"
+ _ -> Left "Prelude.read: ambiguous parse"
+ where
+ read' =
+ do x <- readPrec
+ lift P.skipSpaces
+ return x
+
+-- | The 'read' function reads input from a string, which must be
+-- completely consumed by the input process.
+read :: Read a => String -> a
+read s = either error id (readEither s)
+
+------------------------------------------------------------------------
+-- H98 compatibility
+
+-- | The 'lex' function reads a single lexeme from the input, discarding
+-- initial white space, and returning the characters that constitute the
+-- lexeme. If the input string contains only white space, 'lex' returns a
+-- single successful \`lexeme\' consisting of the empty string. (Thus
+-- @'lex' \"\" = [(\"\",\"\")]@.) If there is no legal lexeme at the
+-- beginning of the input string, 'lex' fails (i.e. returns @[]@).
+--
+-- This lexer is not completely faithful to the Haskell lexical syntax
+-- in the following respects:
+--
+-- * Qualified names are not handled properly
+--
+-- * Octal and hexadecimal numerics are not recognized as a single token
+--
+-- * Comments are not treated properly
+lex :: ReadS String -- As defined by H98
+lex s = readP_to_S L.hsLex s
+
+-- | Read a string representation of a character, using Haskell
+-- source-language escape conventions. For example:
+--
+-- > lexLitChar "\\nHello" = [("\\n", "Hello")]
+--
+lexLitChar :: ReadS String -- As defined by H98
+lexLitChar = readP_to_S (do { (s, _) <- P.gather L.lexChar ;
+ return s })
+ -- There was a skipSpaces before the P.gather L.lexChar,
+ -- but that seems inconsistent with readLitChar
+
+-- | Read a string representation of a character, using Haskell
+-- source-language escape conventions, and convert it to the character
+-- that it encodes. For example:
+--
+-- > readLitChar "\\nHello" = [('\n', "Hello")]
+--
+readLitChar :: ReadS Char -- As defined by H98
+readLitChar = readP_to_S L.lexChar
+
+-- | Reads a non-empty string of decimal digits.
+lexDigits :: ReadS String
+lexDigits = readP_to_S (P.munch1 isDigit)
+
+------------------------------------------------------------------------
+-- utility parsers
+
+lexP :: ReadPrec L.Lexeme
+-- ^ Parse a single lexeme
+lexP = lift L.lex
+
+paren :: ReadPrec a -> ReadPrec a
+-- ^ @(paren p)@ parses \"(P0)\"
+-- where @p@ parses \"P0\" in precedence context zero
+paren p = do L.Punc "(" <- lexP
+ x <- reset p
+ L.Punc ")" <- lexP
+ return x
+
+parens :: ReadPrec a -> ReadPrec a
+-- ^ @(parens p)@ parses \"P\", \"(P0)\", \"((P0))\", etc,
+-- where @p@ parses \"P\" in the current precedence context
+-- parses \"P0\" in precedence context zero
+parens p = optional
+ where
+ optional = p +++ mandatory
+ mandatory = paren optional
+
+list :: ReadPrec a -> ReadPrec [a]
+-- ^ @(list p)@ parses a list of things parsed by @p@,
+-- using the usual square-bracket syntax.
+list readx =
+ parens
+ ( do L.Punc "[" <- lexP
+ (listRest False +++ listNext)
+ )
+ where
+ listRest started =
+ do L.Punc c <- lexP
+ case c of
+ "]" -> return []
+ "," | started -> listNext
+ _ -> pfail
+
+ listNext =
+ do x <- reset readx
+ xs <- listRest True
+ return (x:xs)
+
+choose :: [(String, ReadPrec a)] -> ReadPrec a
+-- ^ Parse the specified lexeme and continue as specified.
+-- Esp useful for nullary constructors; e.g.
+-- @choose [(\"A\", return A), (\"B\", return B)]@
+choose sps = foldr ((+++) . try_one) pfail sps
+ where
+ try_one (s,p) = do { L.Ident s' <- lexP ;
+ if s == s' then p else pfail }
\end{code}
+
%*********************************************************
%* *
-\subsection{Instances of @Read@}
+\subsection{Simple instances of Read}
%* *
%*********************************************************
\begin{code}
-instance Read Char where
- readsPrec _ = readParen False
- (\r -> do
- ('\'':s,t) <- lex r
- (c,"\'") <- readLitChar s
- return (c,t))
-
- readList = readParen False (\r -> do
- ('"':s,t) <- lex r
- (l,_) <- readl s
- return (l,t))
- where readl ('"':s) = return ("",s)
- readl ('\\':'&':s) = readl s
- readl s = do
- (c,t) <- readLitChar s
- (cs,u) <- readl t
- return (c:cs,u)
+instance Read Char where
+ readPrec =
+ parens
+ ( do L.Char c <- lexP
+ return c
+ )
+
+ readListPrec =
+ parens
+ ( do L.String s <- lexP -- Looks for "foo"
+ return s
+ +++
+ readListPrecDefault -- Looks for ['f','o','o']
+ ) -- (more generous than H98 spec)
+
+ readList = readListDefault
instance Read Bool where
- readsPrec _ = readParen False
- (\r ->
- lex r >>= \ lr ->
- (do { ("True", rest) <- return lr ; return (True, rest) }) ++
- (do { ("False", rest) <- return lr ; return (False, rest) }))
-
+ readPrec =
+ parens
+ ( do L.Ident s <- lexP
+ case s of
+ "True" -> return True
+ "False" -> return False
+ _ -> pfail
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
instance Read Ordering where
- readsPrec _ = readParen False
- (\r ->
- lex r >>= \ lr ->
- (do { ("LT", rest) <- return lr ; return (LT, rest) }) ++
- (do { ("EQ", rest) <- return lr ; return (EQ, rest) }) ++
- (do { ("GT", rest) <- return lr ; return (GT, rest) }))
+ readPrec =
+ parens
+ ( do L.Ident s <- lexP
+ case s of
+ "LT" -> return LT
+ "EQ" -> return EQ
+ "GT" -> return GT
+ _ -> pfail
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+\end{code}
-instance Read a => Read (Maybe a) where
- readsPrec _ = readParen False
- (\r ->
- lex r >>= \ lr ->
- (do { ("Nothing", rest) <- return lr ; return (Nothing, rest)}) ++
- (do
- ("Just", rest1) <- return lr
- (x, rest2) <- reads rest1
- return (Just x, rest2)))
-instance (Read a, Read b) => Read (Either a b) where
- readsPrec _ = readParen False
- (\r ->
- lex r >>= \ lr ->
- (do
- ("Left", rest1) <- return lr
- (x, rest2) <- reads rest1
- return (Left x, rest2)) ++
- (do
- ("Right", rest1) <- return lr
- (x, rest2) <- reads rest1
- return (Right x, rest2)))
-
-instance Read Int where
- readsPrec _ x = readSigned readDec x
-
-instance Read Integer where
- readsPrec _ x = readSigned readDec x
-
-instance Read Float where
- readsPrec _ x = readSigned readFloat x
-
-instance Read Double where
- readsPrec _ x = readSigned readFloat x
-
-instance (Integral a, Read a) => Read (Ratio a) where
- readsPrec p = readParen (p > ratio_prec)
- (\r -> do
- (x,s) <- reads r
- ("%",t) <- lex s
- (y,u) <- reads t
- return (x % y,u))
-
-instance (Read a) => Read [a] where
- readsPrec _ = readList
+%*********************************************************
+%* *
+\subsection{Structure instances of Read: Maybe, List etc}
+%* *
+%*********************************************************
-instance Read () where
- readsPrec _ = readParen False
- (\r -> do
- ("(",s) <- lex r
- (")",t) <- lex s
- return ((),t))
-
-instance (Read a, Read b) => Read (a,b) where
- readsPrec _ = readParen False
- (\r -> do
- ("(",s) <- lex r
- (x,t) <- readsPrec 0 s
- (",",u) <- lex t
- (y,v) <- readsPrec 0 u
- (")",w) <- lex v
- return ((x,y), w))
+For structured instances of Read we start using the precedences. The
+idea is then that 'parens (prec k p)' will fail immediately when trying
+to parse it in a context with a higher precedence level than k. But if
+there is one parenthesis parsed, then the required precedence level
+drops to 0 again, and parsing inside p may succeed.
-instance (Read a, Read b, Read c) => Read (a, b, c) where
- readsPrec _ = readParen False
- (\a -> do
- ("(",b) <- lex a
- (x,c) <- readsPrec 0 b
- (",",d) <- lex c
- (y,e) <- readsPrec 0 d
- (",",f) <- lex e
- (z,g) <- readsPrec 0 f
- (")",h) <- lex g
- return ((x,y,z), h))
+'appPrec' is just the precedence level of function application. So,
+if we are parsing function application, we'd better require the
+precedence level to be at least 'appPrec'. Otherwise, we have to put
+parentheses around it.
-instance (Read a, Read b, Read c, Read d) => Read (a, b, c, d) where
- readsPrec _ = readParen False
- (\a -> do
- ("(",b) <- lex a
- (w,c) <- readsPrec 0 b
- (",",d) <- lex c
- (x,e) <- readsPrec 0 d
- (",",f) <- lex e
- (y,g) <- readsPrec 0 f
- (",",h) <- lex g
- (z,h) <- readsPrec 0 h
- (")",i) <- lex h
- return ((w,x,y,z), i))
+'step' is used to increase the precedence levels inside a
+parser, and can be used to express left- or right- associativity. For
+example, % is defined to be left associative, so we only increase
+precedence on the right hand side.
-instance (Read a, Read b, Read c, Read d, Read e) => Read (a, b, c, d, e) where
- readsPrec _ = readParen False
- (\a -> do
- ("(",b) <- lex a
- (v,c) <- readsPrec 0 b
- (",",d) <- lex c
- (w,e) <- readsPrec 0 d
- (",",f) <- lex e
- (x,g) <- readsPrec 0 f
- (",",h) <- lex g
- (y,i) <- readsPrec 0 h
- (",",j) <- lex i
- (z,k) <- readsPrec 0 j
- (")",l) <- lex k
- return ((v,w,x,y,z), l))
+Note how step is used in for example the Maybe parser to increase the
+precedence beyond appPrec, so that basically only literals and
+parenthesis-like objects such as (...) and [...] can be an argument to
+'Just'.
+
+\begin{code}
+instance Read a => Read (Maybe a) where
+ readPrec =
+ parens
+ (do L.Ident "Nothing" <- lexP
+ return Nothing
+ +++
+ prec appPrec (
+ do L.Ident "Just" <- lexP
+ x <- step readPrec
+ return (Just x))
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance (Read a, Read b) => Read (Either a b) where
+ readPrec =
+ parens
+ ( prec appPrec
+ ( do L.Ident "Left" <- lexP
+ x <- step readPrec
+ return (Left x)
+ +++
+ do L.Ident "Right" <- lexP
+ y <- step readPrec
+ return (Right y)
+ )
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance Read a => Read [a] where
+ readPrec = readListPrec
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance (Ix a, Read a, Read b) => Read (Array a b) where
+ readPrec = parens $ prec appPrec $
+ do L.Ident "array" <- lexP
+ bounds <- step readPrec
+ vals <- step readPrec
+ return (array bounds vals)
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance Read L.Lexeme where
+ readPrec = lexP
+ readListPrec = readListPrecDefault
+ readList = readListDefault
\end{code}
%*********************************************************
%* *
-\subsection{Reading characters}
+\subsection{Numeric instances of Read}
%* *
%*********************************************************
\begin{code}
-readLitChar :: ReadS Char
-
-readLitChar [] = mzero
-readLitChar ('\\':s) = readEsc s
- where
- readEsc ('a':s) = return ('\a',s)
- readEsc ('b':s) = return ('\b',s)
- readEsc ('f':s) = return ('\f',s)
- readEsc ('n':s) = return ('\n',s)
- readEsc ('r':s) = return ('\r',s)
- readEsc ('t':s) = return ('\t',s)
- readEsc ('v':s) = return ('\v',s)
- readEsc ('\\':s) = return ('\\',s)
- readEsc ('"':s) = return ('"',s)
- readEsc ('\'':s) = return ('\'',s)
- readEsc ('^':c:s) | c >= '@' && c <= '_'
- = return (chr (ord c - ord '@'), s)
- readEsc s@(d:_) | isDigit d
- = do
- (n,t) <- readDec s
- return (chr n,t)
- readEsc ('o':s) = do
- (n,t) <- readOct s
- return (chr n,t)
- readEsc ('x':s) = do
- (n,t) <- readHex s
- return (chr n,t)
-
- readEsc s@(c:_) | isUpper c
- = let table = ('\DEL', "DEL") : zip ['\NUL'..] asciiTab
- in case [(c,s') | (c, mne) <- table,
- ([],s') <- [match mne s]]
- of (pr:_) -> return pr
- [] -> mzero
- readEsc _ = mzero
-
-readLitChar (c:s) = return (c,s)
-
-match :: (Eq a) => [a] -> [a] -> ([a],[a])
-match (x:xs) (y:ys) | x == y = match xs ys
-match xs ys = (xs,ys)
-
+readNumber :: Num a => (L.Lexeme -> Maybe a) -> ReadPrec a
+-- Read a signed number
+readNumber convert =
+ parens
+ ( do x <- lexP
+ case x of
+ L.Symbol "-" -> do n <- readNumber convert
+ return (negate n)
+
+ _ -> case convert x of
+ Just n -> return n
+ Nothing -> pfail
+ )
+
+convertInt :: Num a => L.Lexeme -> Maybe a
+convertInt (L.Int i) = Just (fromInteger i)
+convertInt _ = Nothing
+
+convertFrac :: Fractional a => L.Lexeme -> Maybe a
+convertFrac (L.Int i) = Just (fromInteger i)
+convertFrac (L.Rat r) = Just (fromRational r)
+convertFrac _ = Nothing
+
+instance Read Int where
+ readPrec = readNumber convertInt
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance Read Integer where
+ readPrec = readNumber convertInt
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance Read Float where
+ readPrec = readNumber convertFrac
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance Read Double where
+ readPrec = readNumber convertFrac
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance (Integral a, Read a) => Read (Ratio a) where
+ readPrec =
+ parens
+ ( prec ratioPrec
+ ( do x <- step readPrec
+ L.Symbol "%" <- lexP
+ y <- step readPrec
+ return (x % y)
+ )
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
\end{code}
%*********************************************************
%* *
-\subsection{Reading numbers}
+\subsection{Tuple instances of Read}
%* *
%*********************************************************
-Note: reading numbers at bases different than 10, does not
-include lexing common prefixes such as '0x' or '0o' etc.
-
\begin{code}
-{-# SPECIALISE readDec ::
- ReadS Int,
- ReadS Integer #-}
-readDec :: (Integral a) => ReadS a
-readDec = readInt 10 isDigit (\d -> ord d - ord '0')
-
-{-# SPECIALISE readOct ::
- ReadS Int,
- ReadS Integer #-}
-readOct :: (Integral a) => ReadS a
-readOct = readInt 8 isOctDigit (\d -> ord d - ord '0')
-
-{-# SPECIALISE readHex ::
- ReadS Int,
- ReadS Integer #-}
-readHex :: (Integral a) => ReadS a
-readHex = readInt 16 isHexDigit hex
- where hex d = ord d - (if isDigit d then ord '0'
- else ord (if isUpper d then 'A' else 'a') - 10)
-
-readInt :: (Integral a) => a -> (Char -> Bool) -> (Char -> Int) -> ReadS a
-readInt radix isDig digToInt s = do
- (ds,r) <- nonnull isDig s
- return (foldl1 (\n d -> n * radix + d)
- (map (fromInteger . toInteger . digToInt) ds), r)
-
-{-# SPECIALISE readSigned ::
- ReadS Int -> ReadS Int,
- ReadS Integer -> ReadS Integer,
- ReadS Double -> ReadS Double #-}
-readSigned :: (Real a) => ReadS a -> ReadS a
-readSigned readPos = readParen False read'
- where read' r = read'' r ++
- (do
- ("-",s) <- lex r
- (x,t) <- read'' s
- return (-x,t))
- read'' r = do
- (str,s) <- lex r
- (n,"") <- readPos str
- return (n,s)
-\end{code}
+instance Read () where
+ readPrec =
+ parens
+ ( paren
+ ( return ()
+ )
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance (Read a, Read b) => Read (a,b) where
+ readPrec =
+ parens
+ ( paren
+ ( do x <- readPrec
+ L.Punc "," <- lexP
+ y <- readPrec
+ return (x,y)
+ )
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
-The functions readFloat below uses rational arithmetic
-to ensure correct conversion between the floating-point radix and
-decimal. It is often possible to use a higher-precision floating-
-point type to obtain the same results.
-\begin{code}
-{-# SPECIALISE readFloat ::
- ReadS Double,
- ReadS Float #-}
-readFloat :: (RealFloat a) => ReadS a
-readFloat r =
- (do
- (x,t) <- readRational r
- return (fromRational x,t) ) ++
- (do
- ("NaN",t) <- lex r
- return (0/0,t) ) ++
- (do
- ("Infinity",t) <- lex r
- return (1/0,t) )
-
-readRational :: ReadS Rational -- NB: doesn't handle leading "-"
-readRational r = do
- (n,d,s) <- readFix r
- (k,t) <- readExp s
- return ((n%1)*10^^(k-d), t)
- where
- readFix r = do
- (ds,s) <- lexDecDigits r
- (ds',t) <- lexDotDigits s
- return (read (ds++ds'), length ds', t)
-
- readExp (e:s) | e `elem` "eE" = readExp' s
- readExp s = return (0,s)
-
- readExp' ('+':s) = readDec s
- readExp' ('-':s) = do
- (k,t) <- readDec s
- return (-k,t)
- readExp' s = readDec s
-
- lexDotDigits ('.':s) = lex0Digits s
- lexDotDigits s = return ("",s)
-
-readRational__ :: String -> Rational -- we export this one (non-std)
- -- NB: *does* handle a leading "-"
-readRational__ top_s
- = case top_s of
- '-' : xs -> - (read_me xs)
- xs -> read_me xs
- where
- read_me s
- = case (do { (x,t) <- readRational s ; ("","") <- lex t ; return x }) of
-#ifndef NEW_READS_REP
- [x] -> x
- [] -> error ("readRational__: no parse:" ++ top_s)
- _ -> error ("readRational__: ambiguous parse:" ++ top_s)
-#else
- Just x -> x
- Nothing -> error ("readRational__: no parse:" ++ top_s)
-#endif
+instance (Read a, Read b, Read c) => Read (a, b, c) where
+ readPrec =
+ parens
+ ( paren
+ ( do x <- readPrec
+ L.Punc "," <- lexP
+ y <- readPrec
+ L.Punc "," <- lexP
+ z <- readPrec
+ return (x,y,z)
+ )
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+
+instance (Read a, Read b, Read c, Read d) => Read (a, b, c, d) where
+ readPrec =
+ parens
+ ( paren
+ ( do w <- readPrec
+ L.Punc "," <- lexP
+ x <- readPrec
+ L.Punc "," <- lexP
+ y <- readPrec
+ L.Punc "," <- lexP
+ z <- readPrec
+ return (w,x,y,z)
+ )
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
+instance (Read a, Read b, Read c, Read d, Read e) => Read (a, b, c, d, e) where
+ readPrec =
+ parens
+ ( paren
+ ( do v <- readPrec
+ L.Punc "," <- lexP
+ w <- readPrec
+ L.Punc "," <- lexP
+ x <- readPrec
+ L.Punc "," <- lexP
+ y <- readPrec
+ L.Punc "," <- lexP
+ z <- readPrec
+ return (v,w,x,y,z)
+ )
+ )
+
+ readListPrec = readListPrecDefault
+ readList = readListDefault
\end{code}