packages feed

wtk-0.1: Text/WtkParser.hs

---------------------------------------------------------
--
-- Module        : Text.WtkParser
-- Copyright     : Bartosz Wójcik (2012)
-- License       : BSD3
--
-- Maintainer    : bartek@sudety.it
-- Stability     : Unstable
-- Portability   : portable
--
-- Module is part of Wojcik Tool Kit package.
-- | Simple parser for internal usage of Wtk.
-- Motivation for this parser vs. already existing ones
-- was its lightweightness. This decision can be changed
-- in the future, the parser is not available outside wtk.
---------------------------------------------------------

module Text.WtkParser
where

import Control.Monad
import Control.Monad.Trans.Error
import Control.Monad.Trans.State
import Control.Monad.Trans.Class
import Data.Char
import Data.Either

newtype Parser a = Parser {
                   runParser :: String -> Either String (String,a) }
                   
eval :: Parser a -> String -> Either String a
eval p s = case runParser p s of
              Left err      -> Left err
              Right (_,res) -> Right res

instance Monad Parser where
   return a = Parser (\xl -> Right (xl,a))
   fail   s = Parser (\xl -> Left s)
   Parser m >>= k = Parser $ \xl ->
      case m xl of
         Left s        -> Left s
         Right (xl',a) ->
            let Parser n = k a
            in n xl'

instance MonadPlus Parser where
   mzero = Parser (\xl -> Left "mzero")
   Parser p `mplus` Parser q = Parser $ \xl ->
      case p xl of
         Right a  -> Right a
         Left err -> case q xl of
                        Right b -> Right b
                        Left _  -> Left err

char :: Char -> Parser Char
char c = Parser char'
   where char' []    = Left ("expecting " ++ [c] ++ ",got EOF")
         char' (x:xs) | x == c    = Right (xs,c)
                      | otherwise = Left ("expecting " ++ [c] ++ ",got " ++ show x)

matchChar :: (Char -> Bool) -> Parser Char
matchChar isTrue = Parser mC
   where mC [] = Left ("expecting char, got EOF")
         mC (x:xs) | isTrue x  = Right (xs,x)
                   | otherwise = Left ("expecting char, got " ++ show x)

many :: Parser a -> Parser [a]
many (Parser p) = Parser many'
   where many' x =
            case p x of
               Left err     -> Right (x,[])
               Right (x',a) ->
                  let Right (x'',rest) = many' x'
                  in Right (x'',a:rest)

manyF :: (a -> a -> a) -> Parser a -> a -> Parser a
manyF f (Parser p) n = Parser many'
   where many' x =
            case p x of
               Left err     -> Right (x,n)
               Right (x',a) ->
                  let Right (x'',rest) = many' x'
                  in Right (x'',rest `f` a)

skipMany :: Parser p -> Parser ()
skipMany (Parser p) = Parser skip'
   where skip' x =
            case p x of
               Left err     -> Right (x,())
               Right (x',_) -> skip' x'

skipSpaces = (skipMany . matchChar) isSpace

lexeme p = do
            v <- p
            skipSpaces
            return v

--int :: Parser Int
int = do
   t1 <- matchChar isDigit
   tr <- many $ matchChar isDigit
   return (read (t1:tr))

--intSigned :: Parser Int
intSigned = (char '-' >> liftM (*(-1)) int) `mplus` int

fromIntSign :: (Num a) => Parser a
fromIntSign = liftM fromIntegral intSigned

fromIntUnsign :: (Num a) => Parser a
fromIntUnsign = liftM fromIntegral int

decimalize :: (Ord a, Fractional a) => a -> a
decimalize n | abs n >= 1 = decimalize (n / 10)
             | otherwise = n

real :: (Fractional a, Ord a) => Parser a
real  = sign >>= \s ->
        fromIntUnsign >>= \i ->
        (decimalPart >>= \d ->
         (expPart >>= \e ->
          return (withExp (s * (i + d)) e))
          `mplus`
          return (s * (i + d))
        )
        `mplus`
        ((expPart >>= \e ->
         return (withExp (s*i) e)
         )
         `mplus`
         return (s*i)
        )
  where sign = (char '-' >> return (-1)) `mplus` return 1
        withExp n e | e < 0     = n / 10 ^^ (-e)
                    | otherwise = n * 10 ^^ e
        decimalPart = (char '.' `mplus` char ',') >>
                      many (char '0') >>= \zeroes ->
                      (fromIntUnsign >>= \b ->
                       return ((decimalize b) / 10 ^ length zeroes)
                      )
                      `mplus`
                      return 0
        expPart = char 'e' >> fromIntSign

eof = Parser many'
   where many' [] = Right ([],())
         many' xs = Left $ "Epexted EOF, got " ++ take 10 xs