hasmin-1.0.2: src/Hasmin/Parser/Dimension.hs
-----------------------------------------------------------------------------
-- |
-- Module : Hasmin.Parser.Dimension
-- Copyright : (c) 2017 Cristian Adrián Ontivero
-- License : BSD3
-- Stability : experimental
-- Portability : unknown
--
-- Parsers for CSS dimensions, i.e. \<length>, \<time>, etc.
--
-----------------------------------------------------------------------------
module Hasmin.Parser.Dimension where
import Data.Attoparsec.Text (Parser)
import qualified Data.Attoparsec.Text as A
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import qualified Data.Char as C
import Data.Text (Text)
import qualified Data.Text as T
import Control.Monad (mzero)
import Control.Arrow ((&&&))
import Hasmin.Parser.Utils
import Hasmin.Parser.Numeric
import Hasmin.Types.Numeric
import Hasmin.Class
import Hasmin.Types.Dimension
distance :: Parser Length
distance = dimensionParser distanceConstructorsMap NullLength
where distanceConstructorsMap = Map.fromList distanceConstructorsList
angle :: Parser Angle
angle = dimensionParser angleConstructorsMap NullAngle
where angleConstructorsMap = Map.fromList angleConstructorsList
time :: Parser Time
time = do
n <- number
u <- opt (A.takeWhile1 C.isAlpha)
if T.null u
then mzero
else case Map.lookup (T.toLower u) timeConstructorsMap of
Just f -> pure $ f n
Nothing -> mzero -- parsed units aren't angle units, fail
where timeConstructorsMap = Map.fromList timeConstructorsList
timeConstructorsList :: [(Text, Number -> Time)]
timeConstructorsList = fmap (toText &&& flip Time) [minBound..]
angleConstructorsList :: [(Text, Number -> Angle)]
angleConstructorsList = fmap (toText &&& flip Angle) [minBound..]
distanceConstructorsList :: [(Text, Number -> Length)]
distanceConstructorsList = fmap (toText &&& flip Length) [minBound..]
-- Create a numerical parser based on a Map.
-- See for instance, the "angle" parser
dimensionParser :: Map Text (Number -> a) -> a -> Parser a
dimensionParser m unitlessValue = do
n <- number
u <- opt (A.takeWhile1 C.isAlpha)
if T.null u
then if n == 0
then pure unitlessValue -- <angle> 0, without units
else mzero -- Non-zero <number>, fail
else case Map.lookup (T.toCaseFold u) m of
Just f -> pure $ f n
Nothing -> mzero -- parsed units aren't angle units, fail