{-|
Module      : Data.Dson.Octal
Copyright   : (c) Ezequiel Alvarez 2014
License     : MIT
Maintainer  : welcometothechango@gmail.com
Stability   : provisional
Portability : portable

Parser for octal numbers.
-}

module Data.Dson.Octal (octal) where

import Data.Dson.Lexer

import Control.Applicative
import Data.Char (digitToInt, isOctDigit)
import Data.Maybe (fromMaybe)
import Text.Parsec (satisfy, char)
import Text.Parsec.String (Parser)

octalToDouble :: (String, Maybe String, Maybe (Double, String)) -> Double
octalToDouble (int, m_dec, m_exp) = (parseOctStrs int dec) * (8 ** (sign * (parseOctStrs exp "")))
    where (sign, exp) = fromMaybe (1, "0") m_exp
          dec = fromMaybe "" m_dec

-- Conversion according to http://en.wikipedia.org/wiki/Octal#Octal_to_decimal_conversion
-- Decimal part has negative exponent.
parseOctStrs :: String -> String -> Double
parseOctStrs  intPart decPart =
    snd $ foldl expAndAdd (initlLen, 0) (intPart ++ decPart)
    where initlLen = fromIntegral . pred . length $ intPart
          expAndAdd (exp, acc) b = (pred exp, acc + (fromIntegral . digitToInt $ b) * (8 ** exp))

factor :: Parser Double
factor = (char '-' *> pure (-1)) <|> (char '+' *> pure 1) <|> pure 1

octalParts :: Parser (String, Maybe String, Maybe (Double, String))
octalParts = (,,) <$> some octDigit <*> optional octalDecimals <*> optional octalExponent
    where octDigit = satisfy isOctDigit
          octalDecimals = symbol "." *> some octDigit
          octalExponent = (,) <$> (veryVERY *> factor) <*> some octDigit
          veryVERY = symbol "very" <|> symbol "VERY"

{-|
  This parser can read a number in the following formats:

  * 5

  * -3.14

  * 2very-6

  * 1VERY4

  etc
-}
octal :: Parser Double
octal = (*) <$> factor <*> (octalToDouble <$> octalParts)