{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, TemplateHaskell, TypeFamilies, TypeSynonymInstances, TypeOperators #-}
module Text.Boomerang.String
(
StringBoomerang, StringPrinterParser, StringError
, alpha, anyChar, char, digit, int
, integer, lit, satisfy, space
, isComplete, parseString, unparseString
)
where
import Prelude hiding ((.), id, (/))
import Control.Category (Category((.), id))
import Data.Char (isAlpha, isDigit, isSpace)
import Data.Data (Data, Typeable)
import Data.List (stripPrefix)
import Data.String (IsString(..))
import Text.Boomerang.Combinators (opt, rCons, rList1)
import Text.Boomerang.Error (ParserError(..),ErrorMsg(..), (<?>), condenseErrors, mkParserError)
import Text.Boomerang.HStack ((:-)(..))
import Text.Boomerang.Pos (InitialPosition(..), MajorMinorPos(..), incMajor, incMinor)
import Text.Boomerang.Prim (Parser(..), Boomerang(..), parse1, xmaph, unparse1, val)
type StringError = ParserError MajorMinorPos
type StringBoomerang = Boomerang StringError String
type StringPrinterParser = StringBoomerang
{-# DEPRECATED StringPrinterParser "Use StringBoomerang instead" #-}
instance InitialPosition StringError where
initialPos _ = MajorMinorPos 0 0
lit :: String -> StringBoomerang r r
lit l = Boomerang pf sf
where
pf = Parser $ \tok pos ->
case tok of
[] -> mkParserError pos [EOI "input", Expect (show l)]
_ -> parseLit l tok pos
sf b = [ (\string -> (l ++ string), b)]
parseLit :: String -> String -> MajorMinorPos -> [Either StringError ((r -> r, String), MajorMinorPos)]
parseLit [] ss pos = [Right ((id, ss), pos)]
parseLit (l:_) [] pos = mkParserError pos [EOI "input", Expect (show l)]
parseLit (l:ls) (s:ss) pos
| l /= s = mkParserError pos [UnExpect (show s), Expect (show l)]
| otherwise = parseLit ls ss (if l == '\n' then incMajor 1 pos else incMinor 1 pos)
instance a ~ b => IsString (Boomerang StringError String a b) where
fromString = lit
satisfy :: (Char -> Bool) -> StringBoomerang r (Char :- r)
satisfy p = val
(Parser $ \tok pos ->
case tok of
[] -> mkParserError pos [EOI "input"]
(c:cs)
| p c ->
do [Right ((c, cs), if (c == '\n') then incMajor 1 pos else incMinor 1 pos)]
| otherwise ->
do mkParserError pos [SysUnExpect $ show c]
)
(\c -> [ \paths -> (c:paths) | p c ])
digit :: StringBoomerang r (Char :- r)
digit = satisfy isDigit <?> "a digit 0-9"
alpha :: StringBoomerang r (Char :- r)
alpha = satisfy isAlpha <?> "an alphabetic Unicode character"
space :: StringBoomerang r (Char :- r)
space = satisfy isSpace <?> "a white-space character"
anyChar :: StringBoomerang r (Char :- r)
anyChar = satisfy (const True)
char :: Char -> StringBoomerang r (Char :- r)
char c = satisfy (== c) <?> show [c]
readIntegral :: (Read a, Eq a, Num a) => String -> a
readIntegral s =
case reads s of
[(x, [])] -> x
[] -> error "readIntegral: no parse"
_ -> error "readIntegral: ambiguous parse"
int :: StringBoomerang r (Int :- r)
int = xmaph readIntegral (Just . show) (opt (rCons . char '-') . (rList1 digit))
integer :: StringBoomerang r (Integer :- r)
integer = xmaph readIntegral (Just . show) (opt (rCons . char '-') . (rList1 digit))
isComplete :: String -> Bool
isComplete = null
parseString :: StringBoomerang () (r :- ())
-> String
-> Either StringError r
parseString pp strs =
either (Left . condenseErrors) Right $ parse1 isComplete pp strs
unparseString :: StringBoomerang () (r :- ()) -> r -> Maybe String
unparseString pp r = unparse1 [] pp r