{
{-# LANGUAGE Trustworthy #-}

module Config.NumberParser where

import Data.List (foldl')
import Config.Number

}

%tokentype
    { Char}
%token
'+' { '+' }
'-' { '-' }
'.' { '.' }
'0' { '0' }
'1' { '1' }
'2' { '2' }
'3' { '3' }
'4' { '4' }
'5' { '5' }
'6' { '6' }
'7' { '7' }
'8' { '8' }
'9' { '9' }
'A' { 'A' }
'B' { 'B' }
'C' { 'C' }
'D' { 'D' }
'E' { 'E' }
'F' { 'F' }
'O' { 'O' }
'P' { 'P' }
'X' { 'X' }

%name number

%%

number ::                               { Number        }
  : '-' unsigned_number                 { negNum $2     }
  |     unsigned_number                 {        $1     }

unsigned_number
  : '0' 'X' hexadecimal fracpart(hexadecimal) exppart('P')
                                        { mkNum (Radix16 $5) $3 $4 }
  |         decimal     fracpart(decimal    ) exppart('E')
                                        { mkNum (Radix10 $3) $1 $2 }
  | '0' 'O' octal       fracpart(octal      )
                                        { mkNum Radix8 $3 $4 }
  | '0' 'B' binary      fracpart(binary     )
                                        { mkNum Radix2 $3 $4 }

fracpart(p) ::                          { [Int]         }
  :                                     { []            }
  | '.'                                 { []            }
  | '.' p                               { $2            }

exppart(p) ::                           { Integer       }
  :                                     { 0             }
  | p expnum                            { $2            }


expnum ::                               { Integer       }
  : '+' decimal                         {   toInt 10 $2 }
  | '-' decimal                         { - toInt 10 $2 }
  |     decimal                         {   toInt 10 $1 }

hexadecimal ::                          { [Int]         }
  :             hexdigit                { [$1]          }
  | hexadecimal hexdigit                { $2 : $1       }

decimal ::                              { [Int]         }
  :         decdigit                    { [$1]          }
  | decimal decdigit                    { $2 : $1       }

octal ::                                { [Int]         }
  :       octdigit                      { [$1]          }
  | octal octdigit                      { $2 : $1       }

binary ::                               { [Int]         }
  :        bindigit                     { [$1]          }
  | binary bindigit                     { $2 : $1       }

hexdigit :: { Int }
  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}
  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}
  | '8' { 8} | '9' { 9} | 'A' {10} | 'B' {11}
  | 'C' {12} | 'D' {13} | 'E' {14} | 'F' {15}

decdigit
  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}
  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}
  | '8' { 8} | '9' { 9}

octdigit :: { Int }
  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}
  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}

bindigit :: { Int }
  : '0' { 0} | '1' { 1}

{

mkNum :: Radix -> [Int] -> [Int] -> Number
mkNum radix coef frac =
  MkNumber radix (fromInteger (toInt base coef) + toFrac base frac)
  where
    base = radixToInt radix

negNum :: Number -> Number
negNum n = n { numberCoefficient = - numberCoefficient n }

toInt :: Int -> [Int] -> Integer
toInt base = foldl' (\acc i -> acc*base' + fromIntegral i) 0 . reverse
  where base' = fromIntegral base

toFrac :: Int -> [Int] -> Rational
toFrac base = foldl' (\acc i -> (fromIntegral i+acc)/base') 0
  where base' = fromIntegral base

happyError [] = error "Unexpected EOF"
happyError (c:_) = error ("Unexpected: "++[c])
}
