```{- |
Module      : Data.Ratio.ParseFloat
Copyright   : (c) Jun Narumi 2018
Maintainer  : narumij@gmail.com
Stability   : experimental
Portability : ?

Floating point parser

Temporary solution to the problem below

> ghci> realToFrac (read "1.1" :: Double) :: Rational
> 2476979795053773 % 2251799813685248

-}
module Data.Ratio.ParseFloat (
floating,
) where

import Data.Ratio
import Text.ParserCombinators.Parsec

-- このような一見、車輪の再発明に思えるコードをわざわざ書いたのは
-- ghci> realToFrac (read "1.1" :: Double) :: Rational
-- 2476979795053773 % 2251799813685248
-- という問題に対処するため。
-- これ以外に良い方法、良い書き方が分かれば、削除します。

-- | Obtain fractions from floating point representation string
--
-- 11 % 10
-- 1 % 2
-- 1 % 2
-- 10 % 1
-- 10 % 1
-- 51 % 5
-- 1 % 10
-- (-1) % 20
-- 500 % 1
-- 500 % 1
readFloatingPoint :: Integral a => String -> Ratio a
readFloatingPoint s = case parse floating s s of
Left  e -> error \$ show e
Right r -> r

zero :: CharParser () String
zero = do
char '0'
return "0"

num :: CharParser () String
num = do
x <- oneOf "123456789"
xs <- many digit
return \$ x : xs

int :: CharParser () String
int = zero <|> num

sign :: CharParser () Char
sign = oneOf "-+"

decimal :: CharParser () String
decimal = do
char '.'
many digit

exponent' :: Integral a => CharParser () a
exponent' = do
oneOf "eE"
s <- optionMaybe sign
e <- int
return \$ signPart s \$ read' e

-- | Parser section
floating :: Integral a => CharParser () (Ratio a)
floating = do
s <- optionMaybe sign
i <- option "" int
f <- option "" decimal
e <- optionMaybe exponent'
return \$ signPart s . expPart e \$ intPart i + decimalPart f

signPart :: Num a => Maybe Char -> a -> a
signPart (Just '-') = negate
signPart _          = id

intPart :: Integral a => String -> Ratio a
intPart "" = 0
intPart i  = read' i % 1

decimalPart :: Integral a => String -> Ratio a
decimalPart "" = 0
decimalPart f  | read f == 0 = 0
| otherwise = read' f % (10 ^ length f)