{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE QuasiQuotes #-}
module Algebra.Ring.Polynomial.Parser ( monomial, expression, variable, variableWithPower
                                      , number, integer, natural, parsePolyn) where
import           Algebra.Ring.Polynomial.Monomorphic
import           Control.Applicative                 hiding (many)
import qualified Data.Map                            as M
import           Data.Ratio
import qualified Numeric.Algebra as NA
import           Text.Peggy

[peggy|
expression :: Polynomial Rational
  = expr !.

letter :: Char
  = [a-zA-Z]

variable :: Variable
  = letter ('_' integer)? { Variable $1 (fromInteger <$> $2) }

variableWithPower :: (Variable, Integer)
  = variable "^" natural { ($1, $2) }
  / variable  { ($1, 1) }

expr :: Polynomial Rational
  = expr "+" term { $1 + $2 }
  / expr "-" term { $1 - $2 }
  / term

term :: Polynomial Rational
   = number space* monoms { injectCoeff $1 * $3 }
   / number { injectCoeff $1 }
   / monoms

monoms :: Polynomial Rational
  = monoms space * fact { $1 * $3 }
  / fact

fact :: Polynomial Rational
  = fact "^" natural { $1 ^ $2 }
  / "(" expr ")"
  / monomial { toPolyn [($1, 1)] }

monomial :: Monomial
  = variableWithPower+ { M.fromListWith (+) $1 }

number :: Rational
  = integer "/" integer { $1 % $2 }
  / integer '.' [0-9]+ { realToFrac (read (show $1 ++ '.' : $2) :: Double) }
  / integer { fromInteger $1 }

integer :: Integer
  = "-" natural { negate $1 }
  / natural

natural :: Integer
  = [1-9] [0-9]* { read ($1 : $2) }

|]

toPolyn :: [(Monomial, Ratio Integer)] -> Polynomial (Ratio Integer)
toPolyn = normalize . Polynomial . M.fromList

parsePolyn :: String -> Either ParseError (Polynomial Rational)
parsePolyn = parseString expression "polynomial"