{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.UnitsOfMeasure.Read
( readQuantity
, readUnit
, readWithUnit
, Some(..)
, QuantityWithUnit(..)
) where
import Control.Monad (join)
import GHC.TypeLits
import Data.List (genericReplicate)
import Data.Proxy
import Data.Type.Equality ((:~:)(..))
import Text.Parse.Units (parseUnit, universalSymbolTable, UnitExp(..))
import Data.UnitsOfMeasure.Internal
import Data.UnitsOfMeasure.Singleton
data QuantityWithUnit a u where
QuantityWithUnit :: Quantity a (Pack u) -> SUnit u -> QuantityWithUnit a u
data Some p where
Some :: p x -> Some p
instance (KnownUnit (Unpack u), u ~ Pack (Unpack u), Read a) => Read (Quantity a u) where
readsPrec i (' ':s) = readsPrec i s
readsPrec _ ('[':'u':'|':s)
| (t, '|':']':r) <- break (== '|') s
, Right v <- readWithUnit (Proxy :: Proxy (Unpack u)) t = [(v, r)]
readsPrec _ _ = []
readWithUnit :: forall proxy a u . (Read a, KnownUnit u)
=> proxy u -> String -> Either String (Quantity a (Pack u))
readWithUnit _ s = do
Some (QuantityWithUnit (q :: Quantity a _) v) <- readQuantity s
case testEquivalentSUnit (unitSing :: SUnit u) v of
Just Refl -> Right q
Nothing -> Left ("wrong units: got " ++ show (forgetSUnit v))
readQuantity :: Read a => String -> Either String (Some (QuantityWithUnit a))
readQuantity s = case reads s of
[(n, s')] -> do Some u <- readUnit s'
return $ Some (QuantityWithUnit (MkQuantity n) u)
_ -> Left "reads: no parse"
readUnit :: String -> Either String (Some SUnit)
readUnit s = expToSomeUnit <$> parseUnit universalSymbolTable s
expToSomeUnit :: UnitExp () String -> Some SUnit
expToSomeUnit = unitSyntaxToSomeUnit . expToUnitSyntax
unitSyntaxToSomeUnit :: UnitSyntax String -> Some SUnit
unitSyntaxToSomeUnit (xs :/ ys) = case (someListVal xs, someListVal ys) of
(Some xs', Some ys') -> Some (SUnit xs' ys')
someListVal :: [String] -> Some SList
someListVal [] = Some SNil
someListVal (x:xs) = case (someSymbolVal x, someListVal xs) of
(SomeSymbol x', Some xs') -> Some (SCons x' xs')
expToUnitSyntax :: UnitExp () String -> UnitSyntax String
expToUnitSyntax Unity = [] :/ []
expToUnitSyntax (Unit _ s) = [s] :/ []
expToUnitSyntax (u `Mult` v) = (u_xs ++ v_xs) :/ (u_ys ++ v_ys)
where
u_xs :/ u_ys = expToUnitSyntax u
v_xs :/ v_ys = expToUnitSyntax v
expToUnitSyntax (u `Div` v) = (u_xs ++ v_ys) :/ (u_ys ++ v_xs)
where
u_xs :/ u_ys = expToUnitSyntax u
v_xs :/ v_ys = expToUnitSyntax v
expToUnitSyntax (u `Pow` n)
| n >= 0 = join (genericReplicate n u_xs) :/ join (genericReplicate n u_ys)
| otherwise = join (genericReplicate (negate n) u_ys) :/ join (genericReplicate (negate n) u_xs)
where
u_xs :/ u_ys = expToUnitSyntax u