packages feed

css-parser-0.0.1: src/CssParser/FixRule.hs

-- | Functions for Rule types helping with Happy gramma disambiguation
module CssParser.FixRule where

import Control.Monad ( foldM )
import CssParser.Descriptor (Descriptor (CustomDescriptor, KnownDescriptor), KnownDescriptor (ResultT))
import CssParser.At.Function ( ConstEntry(..) )
import CssParser.Ident
import CssParser.Prelude
import CssParser.Parser.Monad ( P(Failed), reorderInP, SyntaxTree )
import CssParser.Rule
import CssParser.Show ( CssShow(toCssText), toCssStr )
import CssParser.Rule.TypedNum
    ( TypedNum(TypedNum), RawNum(RawNum) )
import CssParser.Rule.Value
    ( CalcExpr(BinOpCe, ValCe, CalcNeg),
      CalcOp(PlusCe, MinusCe),
      Important,
      stripParens,
      PropVal(IdentRef),
      PropVals(..),
      PropValsList(PropValsList), HasParens )
import CssParser.Rule.Pseudo (AtomicPseudoClass (UnknownPc), BrowserSpecificIdent (..))
import CssParser.Rule.Show ()
import Data.Text qualified as T
import Data.Text.Lazy qualified as L


nullTagSelector :: TagSelector
nullTagSelector = TagSelector NoBar NoTag []

identOnly :: PropertyName -> (Ident -> a) -> P a
identOnly pn f = identOnlyP pn (pure . f)

identOnlyP :: PropertyName -> (Ident -> P a) -> P a
identOnlyP (PropertyName i) f = f i
identOnlyP (VarProp i) _ = Failed $ "Var " <> show i <> " is not expected"

varOrResult :: Descriptor -> NonEmpty PropVals -> P (Either ConstEntry (NonEmpty PropVals))
varOrResult d pvs =
  case d of
    KnownDescriptor ResultT -> pure $ Right pvs
    CustomDescriptor i -> pure . Left $ ConstEntry (Var i) (PropValsList pvs)
    o -> Failed $ "Expected var or result but got " <> toCssStr o

findResult :: [ Either ConstEntry (NonEmpty PropVals) ] -> P (NonEmpty PropVals, [ConstEntry])
findResult l =
  foldM go (Nothing, []) l >>= \case
    (Nothing, _) -> Failed "Function body do not have result: descriptor"
    (Just r, ces) -> pure (r, reverse ces)
  where
    go b a =
      case (b, a) of
       ((Just r, _), Right secondR) ->
         Failed $ "Multiple result descriptors with values: " <>
           toCssStr r <> " and " <> toCssStr secondR
       ((Nothing, ces), Right r) ->
         pure (Just r, ces)
       ((r, ces), Left ce) ->
         pure (r, ce : ces)


specificDescOnlyP :: Descriptor -> Descriptor -> P ()
specificDescOnlyP e g
  | e == g = pure ()
  | otherwise =
    Failed $ "Expected " <> toCssStr e <> " but got " <> toCssStr g

pclassToIdent :: AtomicPseudoClass -> Ident
pclassToIdent = Ident . T.drop 1 . L.toStrict . toCssText

prependPropVal :: PropVal -> PropVals -> PropVals
prependPropVal pv (PropVals pvs mi) = PropVals (pv <| pvs) mi

pclassToPropVal :: AtomicPseudoClass -> PropVal
pclassToPropVal pc = IdentRef (pclassToIdent pc)

pclassToPropVals :: Maybe Important-> AtomicPseudoClass -> PropVals
pclassToPropVals mi pc = PropVals (pclassToPropVal pc :| []) mi

mkCustomSelector :: AtomicPseudoClass -> NonEmpty Selector -> P AtRule
mkCustomSelector pc sel =
  case pc of
    UnknownPc (BrowserSpecificIdent i) -> pure $ CustomSelector (CustomSelectorName i) sel
    o -> Failed $ "Expected custom pseudo class but got: " <> toCssStr o

mkLeaf :: Descriptor -> NonEmpty PropVals -> CssRuleBodyItem
mkLeaf pn = \case
  (x :| []) -> CssLeafRule pn x
  o -> CssEnumLeaf pn (PropValsList o)

massageExpr :: (Show b, HasParens b, SyntaxTree b String) => (b -> a) -> b -> P a
massageExpr f e = fmap (f . stripParens) (reorderInP e)

chopOffLeftmostSign :: CalcExpr -> Maybe (CalcOp, CalcExpr)
chopOffLeftmostSign = \case
  BinOpCe a op b -> do
    case chopOffLeftmostSign a of
      Nothing -> Nothing
      Just (lop, a') -> Just (lop, BinOpCe a' op b)
  ValCe (TypedNum (RawNum rn) pt) ->
    case T.uncons rn of
      Just ('-', absRn) -> pure (MinusCe, ValCe (TypedNum (RawNum absRn) pt))
      Just ('+', absRn) -> pure (PlusCe, ValCe (TypedNum (RawNum absRn) pt))
      _ -> Nothing
  _ -> Nothing

recoverCalcBinOp :: CalcExpr -> CalcExpr -> P CalcExpr
recoverCalcBinOp fo = \case
  CalcNeg x ->
    pure $ BinOpCe fo MinusCe x
  so ->
    case chopOffLeftmostSign so of
      Nothing ->
        fail $ "Expected operator between " <> toCssStr fo <> " and " <> toCssStr so
      Just (lop, so') ->
        pure $ BinOpCe fo lop so'

atrCaseSensetivity :: Ident -> P CaseSensetivity
atrCaseSensetivity = \case
 Ident "i" -> pure CaseInsensetive
 Ident "I" -> pure CaseInsensetive
 Ident "s" -> pure CaseSensetive
 Ident "S" -> pure CaseSensetive
 o -> Failed $ "Expected i or s for case sensetivity but got: " <> toCssStr o