packages feed

computational-algebra-0.0.2.0: Algebra/Ring/Polynomial/Parser.hs

module Algebra.Ring.Polynomial.Parser where
import           Algebra.Ring.Polynomial.Monomorphic
import           Control.Applicative                 hiding (many)
import           Control.Arrow
import           Data.Char
import qualified Data.Map                            as M
import           Data.Maybe
import           Data.Ratio
import qualified Numeric.Algebra                     as NA
import           Text.Parsec                         hiding (optional, (<|>))
import           Text.Parsec.String

variable :: Parser Variable
variable = Variable <$> letter <*> optional (char '_' *> index)

variableWithPower :: Parser (Variable, Integer)
variableWithPower = (,) <$> lexeme variable <*> option 1 power
  where
    power = symbol '^' *> parseInt

index :: Parser Int
index = digitToInt <$> digit
    <|> read <$ symbol '{' <*> lexeme (many1 digit) <* symbol '}'

monomial :: Parser Monomial
monomial = M.fromList <$> many variableWithPower

term :: Parser (Monomial, Rational)
term = signed' $ try $ flip (,) <$> option 1 coefficient
                                <*> monomial
                   <|> flip (,) <$> number <*> pure M.empty

signed' p = do
  s <- optional sign
  (n, c) <- p
  return (n, fromMaybe 1 s * c)
  where
    sign = lexeme $ char '-' *> return (negate 1)
                <|> char '+' *> return 1


symbol :: Char -> Parser Char
symbol = lexeme . char

lexeme :: Parser a -> Parser a
lexeme p = p <* spaces

toPolyn = normalize . Polynomial . M.fromList

polyOp :: Parser (Polynomial Rational -> Polynomial Rational -> Polynomial Rational)
polyOp = (NA.-) <$ symbol '-'
    <|> (NA.+) <$ symbol '+'

expression :: Parser (Polynomial Rational)
expression =  (spaces *> (toPolyn <$> count 1 term) `chainl1` polyOp <* eof)

coefficient :: Parser Rational
coefficient = char '(' *> number <* char ')'
          <|> number

number :: Parser Rational
number = signed $
              try (toRational <$> parseDouble)
          <|> try (lexeme $ (%) <$> parseInt
                         <* symbol '/'
                         <*> parseInt)
          <|> toRational <$> parseInt

parseInt :: Parser Integer
parseInt = lexeme $ read <$> many1 digit

signed :: Num b => Parser b -> Parser b
signed p = do
  s <- optional sign
  n <- p
  return $ fromMaybe 1 s * n
  where
    sign = lexeme $ char '-' *> return (negate 1)
                <|> char '+' *> return 1

parseDouble :: Parser Double
parseDouble = lexeme $ do
  int <- many1 digit
  _ <- char '.'
  float <- many1 digit
  return $ read $ int ++ '.':float

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