module Math.Diophantine.Parser
( ReadError(..)
, readEquation
, readMaybeEquation
) where
import Data.List (isInfixOf,find)
import Data.Maybe (fromMaybe,isNothing)
import Math.Diophantine.Internal
import Text.Read (readMaybe)
data Token = Token { match :: String
, value :: Either ReadError Integer
} deriving Show
data ReadError = UnexpectedChar Char deriving Show
getToken :: String -> (Token,String)
getToken str = let (t,rs) = span (`notElem` "xy=") str
in getToken' (t,rs)
where
getToken' (t,'x':'y':rs) = (Token "xy" $ readT t,rs)
getToken' (t,v:'^':p:rs) = (Token [v,'^',p] $ readT t,rs)
getToken' (t,[]) = (Token "" $ readT t,[])
getToken' (t,v:rs) = (Token [v] $ readT t,rs)
readT = readWithError . filter (`notElem`" +")
tokenize :: String -> [Token]
tokenize [] = []
tokenize str = let (t,s) = getToken str
in t:tokenize s
readWithError :: String -> Either ReadError Integer
readWithError "" = Right 1
readWithError str
| null $ filter (`elem` "0123456789-") str = Right (1)
| otherwise = case find (`notElem` "0123456789-") str of
Nothing -> Right $ read str
Just c -> Left $ UnexpectedChar c
getVal :: Either ReadError Integer -> Integer
getVal (Left _) = error "Parsing error when filtering errors."
getVal (Right n) = n
getErr :: Either ReadError Integer -> ReadError
getErr (Right _) = error "Parse error when finding errors."
getErr (Left e) = e
getC :: (String -> Bool) -> [Token] -> Either ReadError Integer
getC b ts = value $ fromMaybeTok $ find (\t -> b $ match t) ts
fromMaybeTok :: Maybe Token -> Token
fromMaybeTok (Just (Token s v)) = Token s v
fromMaybeTok Nothing = Token "" (Right 0)
readEquation :: String -> Either ReadError Equation
readEquation str = let ts = tokenize str
a = getC ("x^2" ==) ts
b = getC ("xy" ==) ts
c = getC ("y^2" ==) ts
d = getC ("x" ==) ts
e = getC ("y" ==) ts
f = getC null ts
g = find (\n -> case n of
Left _ -> True
_ -> False) [a,b,c,d,e,f]
in maybe (Right $ GeneralEquation (getVal a) (getVal b)
(getVal c) (getVal d) (getVal e) (getVal f))
(Left . getErr) g
readMaybeEquation :: String -> Maybe Equation
readMaybeEquation str = let ts = tokenize str
a = getC ("x^2" ==) ts
b = getC ("xy" ==) ts
c = getC ("y^2" ==) ts
d = getC ("x" ==) ts
e = getC ("y" ==) ts
f = getC null ts
g = find (\n -> case n of
Left _ -> True
_ -> False) [a,b,c,d,e,f]
in if isNothing g
then Just $ GeneralEquation (getVal a) (getVal b)
(getVal c) (getVal d) (getVal e)
(getVal f)
else Nothing