packages feed

css-parser-0.0.1: src/CssParser/Rule/Value.hs

{-# OPTIONS_GHC -Wno-orphans #-}
module CssParser.Rule.Value
  ( module CssParser.Rule.Value
  , reorder
  ) where

import CssParser.Ident ( Ident, Var, PropertyName(..), AttrName )
import CssParser.Prelude
import CssParser.Rule.Type ( CssType )
import CssParser.Rule.TypedNum
    ( TypedNum, RawNum(..), PropValType, mkRawNum )
import CssParser.Show
    ( CssShow(..), ShowSpaceBetween(..), encodeStringLiteral, mayCss )
import Expression.Reorder

newtype Unsigned = Unsigned RawNum
  deriving newtype (Eq, Show, Ord, CssShow)
  deriving (Generic)

data Ratio = Ratio Unsigned Unsigned deriving (Eq, Show, Ord, Generic)

instance CssShow Ratio where
  toCssText (Ratio a b) = toCssText a <> "/" <> toCssText b

readRatio :: String -> Either String Ratio
readRatio s =
  case span (/= '/') s of
    ([], _) -> Left $ "No divisible in Ratio [" <> s <> "]"
    ("/", _) -> Left $ "No divisible in Ratio [" <> s <> "]"
    (_, []) -> Left $ "No divisor in Ratio [" <> s <> "]"
    (_, "/") -> Left $ "No divisor in Ratio [" <> s <> "]"
    (divisibleStr, '/':divisorStr) ->
      Right $ Ratio (Unsigned (mkRawNum  divisibleStr)) (Unsigned (mkRawNum divisorStr))
    (_, _) -> Left $ "No slash in ratio [" <> s <> "]"

data Url
  = Url { unUrl :: Text }
  | UnquotedUrl { unUrl :: Text }
  deriving (Show, Eq, Ord, Generic)
instance CssShow Url where
  toCssText = \case
    Url u -> "url(" <> encodeStringLiteral u <> ")"
    UnquotedUrl u -> "url(" <> fromStrict u <> ")"

data Source = UrlSource Url | StrSource Text
  deriving (Show, Ord, Eq, Generic)

instance CssShow Source where
  toCssText = \case
    UrlSource u -> toCssText u
    StrSource t -> encodeStringLiteral t


newtype HexColor = HC Text deriving (Eq, Ord, Show, Generic)

instance CssShow HexColor where
  toCssText (HC s) = "#" <> fromStrict s

propRef :: PropertyName -> PropVal
propRef = \case
  PropertyName i -> IdentRef i
  VarProp v -> VarRef v


data CalcOp = PlusCe | MinusCe | DivCe | ProdCe deriving (Eq, Ord, Show, Enum, Bounded, Generic)

instance CssShow CalcOp where
  toCssText = \case
    PlusCe -> " + "
    MinusCe -> " - "
    DivCe -> " / "
    ProdCe -> " * "

data CalcFns
  = CalcFn
  | MinFn
  | NoFn
  | MaxFn
  | ClampFn
  deriving (Eq, Ord, Show, Bounded, Enum, Generic)

instance CssShow CalcFns where
  toCssText = \case
    CalcFn -> "calc"
    MinFn -> "min"
    NoFn -> ""
    MaxFn -> "max"
    ClampFn -> "clamp"

data NthFormula
  = NthEven Bool
  | NthExpr CalcExpr
  deriving (Eq, Ord, Show, Generic)

instance CssShow NthFormula where
  toCssText = \case
    NthEven True -> "even"
    NthEven False -> "odd"
    NthExpr e -> toCssText e

data CalcExpr
  = BinOpCe CalcExpr CalcOp CalcExpr
  | ValCe TypedNum
  | VarCe PropertyName
  | AppCe PropertyName PropValsList
  | CalcCe CalcFns CalcExprList
  | CalcNeg CalcExpr
  deriving (Eq, Ord, Show, Generic)

instance HasParens CalcExpr where
  stripParens = \case
    BinOpCe l op r -> BinOpCe (stripParens l) op (stripParens r)
    CalcCe f (CalcExprList l) -> CalcCe f . CalcExprList $ fmap stripParens l
    CalcNeg (CalcCe NoFn (CalcExprList (x :| []))) -> stripParens $ CalcNeg x
    CalcNeg o -> CalcNeg $ stripParens o
    o@VarCe {} -> o
    o@ValCe {} -> o
    o@AppCe {} -> o

newtype CalcExprList = CalcExprList (NonEmpty CalcExpr)
  deriving newtype (Eq, Ord)
  deriving (Show, Generic)

instance CssShow CalcExprList where
  toCssText (CalcExprList cs) = intercalate ", " (toCssText <$> toList cs)

class HasParens a where
  stripParens :: a -> a

class HasFixity x where
  fixityOf :: x -> Fixity

instance HasFixity CalcOp where
  fixityOf = \case
    PlusCe -> Fixity AssocLeft 1
    MinusCe -> Fixity AssocLeft 2
    ProdCe -> Fixity AssocLeft 3
    DivCe -> Fixity AssocLeft 4

instance Semigroup a => Monad (Validation a) where
  Failure f >>= _ = Failure f
  Success a >>= f = f a

-- happy builtin capabilites for operator priority is pretty limited
-- %left and %right are just ignored for the gramma
-- AST tree is alway represented as a list always spanning to the right
instance SyntaxTree CalcExpr String where
  reorderChildren = \case
    BinOpCe l op r -> BinOpCe <$> reorder l <*> pure op <*> reorder r
    CalcCe x (CalcExprList args) -> CalcCe x . CalcExprList <$> mapM reorder args
    CalcNeg x -> CalcNeg <$> reorder x
    o -> pure o
  structureOf = \case
    BinOpCe l op r -> NodeInfix (fixityOf op) l r (`BinOpCe` op)
    CalcNeg x -> NodePrefix 5 x CalcNeg
    _ -> NodeLeaf
  makeError err _ = show err

instance CssShow CalcExpr where
  toCssText = \case
    BinOpCe a op b -> toCssText a <> toCssText op <> toCssText b
    ValCe v -> toCssText v
    VarCe v -> toCssText v
    AppCe f a -> toCssText f <> "(" <> toCssText a <> ")"
    CalcCe f a -> toCssText f <> "(" <> toCssText a <> ")"
    CalcNeg x@CalcNeg {} -> "-(" <> toCssText x <> ")"
    CalcNeg x@BinOpCe {} -> "-(" <> toCssText x <> ")"
    CalcNeg x@(VarCe _) -> "- " <> toCssText x
    CalcNeg x@(AppCe _ _) -> "- " <> toCssText x
    CalcNeg x -> "-" <> toCssText x

data AttrType
  = CssTypeAt CssType
  | UnitAt PropValType
  | RawString
  deriving (Show, Eq, Ord, Generic)

instance CssShow AttrType where
  toCssText = \case
    RawString -> " raw-string"
    CssTypeAt x -> " type(" <> toCssText x <> ")"
    UnitAt x -> " " <> toCssText x

data PropVal
  = AlphaF Unsigned
  | AppConst PropertyName
  | AppFun PropertyName PropVals
  | AppFunEnum PropertyName PropValsList
  | AttrFun AttrName (Maybe AttrType) (Maybe PropVal)
  | BoolVal Bool
  | CalcFun CalcExpr
  | Div PropVal PropVal
  | DotVal
  | HexColor HexColor
  | IdentRef Ident
  | IntVal TypedNum
  | RatioVal Ratio
  | StrVal Text
  | UnicodeRangeVal UnicodeRange
  | UrlVal Url
  | VarRef Var
  | BracketVal Ident
  deriving (Eq, Ord, Show, Generic)

newtype LiteralString = LiteralString Text deriving newtype (Eq, Ord, Show, IsString) deriving (Generic)

instance CssShow LiteralString where
  toCssText (LiteralString s) = encodeStringLiteral s

instance CssShow PropVal where
  toCssText = \case
    IntVal i -> toCssText i
    VarRef v -> toCssText v
    IdentRef i -> toCssText i
    HexColor c -> toCssText c
    StrVal s -> encodeStringLiteral s
    UrlVal u -> toCssText u
    BoolVal bv -> toCssText bv
    RatioVal rv -> toCssText rv
    CalcFun ce -> toCssText ce
    BracketVal ce -> "[" <> toCssText ce <> "]"
    AttrFun an at dv ->
      "attr(" <> toCssText an <> toCssText at <> mayCss (", " <>) dv <> ")"
    Div a b -> toCssText a <> " / " <> toCssText b
    AppFun fn args -> toCssText fn <> "(" <> toCssText args <> ")"
    AppFunEnum fn args -> toCssText fn <> "(" <> toCssText args <> ")"
    AppConst fn -> toCssText fn <> "()"
    UnicodeRangeVal ur -> toCssText ur
    AlphaF o -> "alpha(opacity=" <> toCssText o <> ")"
    DotVal -> "."


data Important = Important deriving (Show, Eq, Ord, Generic)
instance CssShow Important where
  toCssText _  = " !important"

data PropVals = PropVals (NonEmpty PropVal) (Maybe Important) deriving (Show, Eq, Ord, Generic)

instance CssShow PropVals where
  toCssText (PropVals ne mi) =
    unwords $ (toCssText <$> toList ne) <> maybeToList (toCssText <$> mi)

instance ShowSpaceBetween PropVals PropVals where
  cssSpace _ _ = ", "

newtype PropValsList = PropValsList (NonEmpty PropVals) deriving (Show, Eq, Ord, Generic)

instance CssShow PropValsList where
  toCssText (PropValsList l) = toCssText l

mkAppFun :: PropertyName -> NonEmpty PropVals -> PropVal
mkAppFun pn = \case
  (x :| []) -> AppFun pn x
  o -> AppFunEnum pn (PropValsList o)

newtype UnicodeRange = UnicodeRange Text deriving (Show, Eq, Ord, Generic)
instance CssShow UnicodeRange where
  toCssText (UnicodeRange t) = "U+" <> fromStrict t
instance ShowSpaceBetween UnicodeRange UnicodeRange where
  cssSpace _ _ = ", "

newtype CommaSeparatedList
  = CommaSeparatedList (NonEmpty PropVals)
  deriving (Show, Eq, Ord, Generic)

instance ShowSpaceBetween CommaSeparatedList CommaSeparatedList where
  cssSpace _ _ = "; "
type SrcVal = CommaSeparatedList
instance CssShow CommaSeparatedList where
  toCssText (CommaSeparatedList l) = toCssText l