module Bookhound.Parsers.Number (int, double, posInt, negInt, unsignedInt, hexInt, octInt, intLike) where

import Bookhound.Parser            (ParseError (..), Parser, errorParser,
                                    withError)
import Bookhound.ParserCombinators (IsMatch (..), (<|>), (>>>), (|+), (|?))
import Bookhound.Parsers.Char      (dash, digit, dot, plus)


hexInt :: Parser Integer
hexInt :: Parser Integer
hexInt = String -> Parser Integer -> Parser Integer
forall a. String -> Parser a -> Parser a
withError String
"Hex Int"
 (Parser Integer -> Parser Integer)
-> Parser Integer -> Parser Integer
forall a b. (a -> b) -> a -> b
$ String -> Integer
forall a. Read a => String -> a
read (String -> Integer) -> Parser String -> Parser Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (String -> Parser String
forall a. IsMatch a => a -> Parser a
is String
"0x" Parser String -> Parser String -> Parser String
forall a b.
(ToString a, ToString b) =>
Parser a -> Parser b -> Parser String
>>> ((Parser Char
digit Parser Char -> Parser Char -> Parser Char
forall a. Parser a -> Parser a -> Parser a
<|> String -> Parser Char
forall a. IsMatch a => [a] -> Parser a
oneOf [Char
'A' .. Char
'F'] Parser Char -> Parser Char -> Parser Char
forall a. Parser a -> Parser a -> Parser a
<|> String -> Parser Char
forall a. IsMatch a => [a] -> Parser a
oneOf [Char
'a' .. Char
'f']) Parser Char -> Parser String
forall a. Parser a -> Parser [a]
|+))

octInt :: Parser Integer
octInt :: Parser Integer
octInt = String -> Parser Integer -> Parser Integer
forall a. String -> Parser a -> Parser a
withError String
"Oct Int"
  (Parser Integer -> Parser Integer)
-> Parser Integer -> Parser Integer
forall a b. (a -> b) -> a -> b
$ String -> Integer
forall a. Read a => String -> a
read (String -> Integer) -> Parser String -> Parser Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (String -> Parser String
forall a. IsMatch a => a -> Parser a
is String
"0o" Parser String -> Parser String -> Parser String
forall a b.
(ToString a, ToString b) =>
Parser a -> Parser b -> Parser String
>>> (String -> Parser Char
forall a. IsMatch a => [a] -> Parser a
oneOf [Char
'0' .. Char
'7'] Parser Char -> Parser String
forall a. Parser a -> Parser [a]
|+))

unsignedInt :: Parser Integer
unsignedInt :: Parser Integer
unsignedInt = String -> Parser Integer -> Parser Integer
forall a. String -> Parser a -> Parser a
withError String
"Unsigned Int"
  (Parser Integer -> Parser Integer)
-> Parser Integer -> Parser Integer
forall a b. (a -> b) -> a -> b
$ String -> Integer
forall a. Read a => String -> a
read (String -> Integer) -> Parser String -> Parser Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parser Char
digit Parser Char -> Parser String
forall a. Parser a -> Parser [a]
|+)

posInt :: Parser Integer
posInt :: Parser Integer
posInt = String -> Parser Integer -> Parser Integer
forall a. String -> Parser a -> Parser a
withError String
"Positive Int"
  (Parser Integer -> Parser Integer)
-> Parser Integer -> Parser Integer
forall a b. (a -> b) -> a -> b
$ String -> Integer
forall a. Read a => String -> a
read (String -> Integer) -> Parser String -> Parser Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parser Char
plus Parser Char -> Parser (Maybe Char)
forall a. Parser a -> Parser (Maybe a)
|?) Parser (Maybe Char) -> Parser String -> Parser String
forall a b.
(ToString a, ToString b) =>
Parser a -> Parser b -> Parser String
>>> (Parser Char
digit Parser Char -> Parser String
forall a. Parser a -> Parser [a]
|+)

negInt :: Parser Integer
negInt :: Parser Integer
negInt = String -> Parser Integer -> Parser Integer
forall a. String -> Parser a -> Parser a
withError String
"Negative Int"
  (Parser Integer -> Parser Integer)
-> Parser Integer -> Parser Integer
forall a b. (a -> b) -> a -> b
$ String -> Integer
forall a. Read a => String -> a
read (String -> Integer) -> Parser String -> Parser Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char
dash Parser Char -> Parser String -> Parser String
forall a b.
(ToString a, ToString b) =>
Parser a -> Parser b -> Parser String
>>> (Parser Char
digit Parser Char -> Parser String
forall a. Parser a -> Parser [a]
|+)

int :: Parser Integer
int :: Parser Integer
int = String -> Parser Integer -> Parser Integer
forall a. String -> Parser a -> Parser a
withError String
"Int" (Parser Integer -> Parser Integer)
-> Parser Integer -> Parser Integer
forall a b. (a -> b) -> a -> b
$ Parser Integer
negInt Parser Integer -> Parser Integer -> Parser Integer
forall a. Parser a -> Parser a -> Parser a
<|> Parser Integer
posInt

intLike :: Parser Integer
intLike :: Parser Integer
intLike = Parser Integer
parser Parser Integer -> Parser Integer -> Parser Integer
forall a. Parser a -> Parser a -> Parser a
<|> Parser Integer
int
  where
    parser :: Parser Integer
parser = do String
n1      <- Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Parser Integer -> Parser String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Integer
int
                String
n2      <- Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Parser Integer -> Parser String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parser Char
dot Parser Char -> Parser Integer -> Parser Integer
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Integer
unsignedInt)
                Integer
expNum  <- String -> Parser Char
forall a. IsMatch a => [a] -> Parser a
oneOf [Char
'e', Char
'E'] Parser Char -> Parser Integer -> Parser Integer
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Integer
int

                if String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
n1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
n2 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer -> Int
forall a. Num a => Integer -> a
fromInteger Integer
expNum then
                  Integer -> Parser Integer
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Integer -> Parser Integer)
-> (String -> Integer) -> String -> Parser Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Integer
forall a. Read a => String -> a
read (String -> Parser Integer) -> String -> Parser Integer
forall a b. (a -> b) -> a -> b
$ String
n1 String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"." String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
n2 String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"E" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Integer -> String
forall a. Show a => a -> String
show Integer
expNum
                else
                  ParseError -> Parser Integer
forall a. ParseError -> Parser a
errorParser (ParseError -> Parser Integer) -> ParseError -> Parser Integer
forall a b. (a -> b) -> a -> b
$ String -> ParseError
NoMatch String
"Int Like"


double :: Parser Double
double :: Parser Double
double = String -> Parser Double -> Parser Double
forall a. String -> Parser a -> Parser a
withError String
"Double"
  (Parser Double -> Parser Double) -> Parser Double -> Parser Double
forall a b. (a -> b) -> a -> b
$ String -> Double
forall a. Read a => String -> a
read (String -> Double) -> Parser String -> Parser Double
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Integer
int Parser Integer -> Parser (Maybe String) -> Parser String
forall a b.
(ToString a, ToString b) =>
Parser a -> Parser b -> Parser String
>>> (Parser String
decimals Parser String -> Parser (Maybe String)
forall a. Parser a -> Parser (Maybe a)
|?) Parser String -> Parser (Maybe String) -> Parser String
forall a b.
(ToString a, ToString b) =>
Parser a -> Parser b -> Parser String
>>> (Parser String
expn Parser String -> Parser (Maybe String)
forall a. Parser a -> Parser (Maybe a)
|?) where

  decimals :: Parser String
decimals = Parser Char
dot Parser Char -> Parser Integer -> Parser String
forall a b.
(ToString a, ToString b) =>
Parser a -> Parser b -> Parser String
>>> Parser Integer
unsignedInt
  expn :: Parser String
expn      = String -> Parser Char
forall a. IsMatch a => [a] -> Parser a
oneOf [Char
'e', Char
'E'] Parser Char -> Parser Integer -> Parser String
forall a b.
(ToString a, ToString b) =>
Parser a -> Parser b -> Parser String
>>> Parser Integer
int