packages feed

clevercss 0.1.1 → 0.2

raw patch · 6 files changed

+454/−261 lines, 6 filesdep −haskell98dep ~basedep ~parsec

Dependencies removed: haskell98

Dependency ranges changed: base, parsec

Files

CCMain.hs view
@@ -2,13 +2,13 @@ -- CleverCSS in Haskell, (c) 2007, 2008 Georg Brandl. Licensed under the BSD license. -- -- CCMain module: command-line interface.------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------  module Main (main) where  import Control.Monad (when) import Data.List (findIndices)-import System+import System.Environment import System.Console.GetOpt import System.IO @@ -58,7 +58,8 @@                     "Converted file " ++ filename ++ " to " ++ outfilename ++ ".\n"     process inaction filename outfile defs = do          input <- inaction-         case cleverCSSConvert filename input defs of+         result <- cleverCSSConvert filename input defs+         case result of            Left err  -> do              hPutStr stderr $ "While processing " ++ filename ++ ":\n" ++ err ++ "\n"              return False
Text/CSS/CleverCSS.hs view
@@ -4,23 +4,34 @@ -- Text.CSS.CleverCSS module: main parsing and evaluation. -- -- TODO: properly parse selectors before splitting them------------------------------------------------------------------------------------------- -{-# LANGUAGE PatternGuards #-}+------------------------------------------------------------------------------------------+{-# LANGUAGE PatternGuards, TypeSynonymInstances, CPP #-}  module Text.CSS.CleverCSS (cleverCSSConvert) where +import Control.Applicative (Applicative(..), (<$>)) import Control.Monad.Error import Control.Monad.RWS import Data.Char (toUpper, toLower) import Data.List (findIndex)+import Data.Ratio+import Data.Sequence (Seq, singleton) import Text.Printf (printf) import Text.ParserCombinators.Parsec hiding (newline)+import qualified Control.Exception as E+import qualified Data.Foldable as F import qualified Data.Map as Map  import Text.CSS.CleverCSSUtil  css_functions = ["url", "attr", "counter"] -- rgb() is special +#if PARSEC2+instance Applicative (GenParser toc st) where+  pure  = return+  (<*>) = ap+#endif+ ------------------------------------------------------------------------------------------ -- "AST" for clevercss templates @@ -28,32 +39,53 @@ type CSSColor = Either String Color  -- color name or RGB data AssignType = Always | IfNotAssigned  deriving Eq -data Topl = Assign !AssignType !Line !String [Expr]-          | Import !Line [Expr]-          | Block  !Line ![String] [Item]+data Topl = Assign !AssignType !Line !String [Expr]  -- a [?]= b+          | Import !Line [Expr]                      -- @import url(...)+          | Include !Line [Expr]                     -- @include "..."+          | Macro  !Line !String ![String] [Item]    -- @define mac(...):+          | Block  !Line ![String] [Item]            -- sel:+          | SetFilename !String                      -- pseudo item             deriving Eq-data Item = Property !Line !String [Expr]-          | SubBlock !Line ![String] [Item]-          | SubGroup !Line !String [Item]+data Item = Property !Line !String [Expr]            -- prop: val+          | UseMacro !Line !String [Expr]            -- %macro(...)+          | SubBlock !Line ![String] [Item]          -- subsel:+          | SubGroup !Line !String [Item]            -- prop->             deriving Eq-data Expr = Plus Expr Expr | Minus Expr Expr-          | Mul Expr Expr | Divide Expr Expr | Modulo Expr Expr-          | ExprListCons Expr Expr | ExprList [Expr] | Subseq [Expr]-          | Call Expr !String (Maybe Expr)-          | Var !String | Bare !String | String !String | CSSFunc !String Expr-          | Number !Rational | Dim !CSSNumber | Color !CSSColor | Rgb Expr Expr Expr-          | Error !String+data Expr = Plus Expr Expr                           -- x + y+          | Minus Expr Expr                          -- x - y+          | Mul Expr Expr                            -- x * y+          | Divide Expr Expr                         -- x / y+          | Modulo Expr Expr                         -- x % y+          | ExprListCons Expr Expr                   -- x, xs+          | ExprList [Expr]                          -- x, y, z+          | Subseq [Expr]                            -- x y z+          | Call Expr !String (Maybe Expr)           -- x.y([z])+          | Var !String                              -- $x+          | Bare !String                             -- x+          | String !String                           -- "x"+          | CSSFunc !String Expr                     -- url(x)+          | Number !Rational                         -- 42+          | Dim !CSSNumber                           -- 42px+          | Color !CSSColor                          -- #fff+          | Rgb Expr Expr Expr                       -- rgb(1,0,0)+          | Error !String                            -- evaluation error+          | NoExpr                                   -- no arg in macro calls             deriving Eq  instance Show Topl where   show (Assign Always _ name exprs) = name ++ " = " ++ joinShow " " exprs   show (Assign IfNotAssigned _ name exprs) = name ++ " ?= " ++ joinShow " " exprs   show (Import _ exprs) = "@import " ++ joinShow " " exprs+  show (Include _ exprs) = "@include " ++ joinShow " " exprs+  show (Macro _ sel argnames items) = "@define " ++ sel ++ "(" ++ joinStr ", " argnames +++                                  "):\n" ++ unlines (map ("  "++) (map show items))   show (Block _ sels items) = (joinStr ", " sels) ++ ":\n" ++-                            unlines (map ("  "++) (map show items))+                              unlines (map ("  "++) (map show items))+  show (SetFilename s) = "<SetFilename " ++ s ++ ">"  instance Show Item where   show (Property _ name exprs) = name ++ ": " ++ joinShow " " exprs+  show (UseMacro _ name args)  = "%" ++ name ++ "(" ++ joinShow ", " args ++ ")"   show (SubGroup _ name items) = name ++ "->\n" ++                                  unlines (map ("    "++) (map show items))   show (SubBlock _ sels items) = (joinStr ", " sels) ++ ":\n" ++@@ -72,25 +104,32 @@   show (Var a)             = printf "<Var %s>" a   show (Rgb r g b)         = printf "<Rgb %s %s %s>" (show r) (show g) (show b)   show (Error e)           = printf "<Error: %s>" e+  show NoExpr              = "<NoExpr>"   -- things that can remain and need to be pretty-printed   show (ExprList l)        = joinShow ", " l   show (Subseq l)          = joinShow " " l   show (String s)          = cssShow s-  show (Number n)          = show (fromRational n)-  show (Dim (n, u))        = show (fromRational n) ++ u+  show (Number n)          = showRational n+  show (Dim (n, u))        = showRational n ++ u   show (CSSFunc name args) = name ++ "(" ++ show args ++ ")"   show (Color (Left n))    = n   show (Color (Right (r,g,b))) = case Map.lookup (r,g,b) reverse_colors of-                                   Just name -> name +                                   Just name -> name                                    Nothing -> printf "#%02x%02x%02x" r g b   show (Bare s)            = s +showRational r | rest == 0 = show whole+               | otherwise = show (fromRational r)+  where d = denominator r+        n = numerator r+        (whole, rest) = n `divMod` d+ ------------------------------------------------------------------------------------------ -- the tokenizer and parser  -- helpers for toplevel and expression parsers nl           = char '\n' <?> "end of line"-ws           = many (char ' ') <?> "whitespace"+ws           = many (char ' ' <?> "whitespace") comment      = string "//" >> many (noneOf "\n") <?> "comment" wscomment    = ws >> option "" (try comment) emptyLine    = wscomment >> nl <?> "empty line"@@ -102,37 +141,34 @@                       ((char '_' <|> letter <|> escape) +:+                        many (char '_' <|> char '-' <|> alphaNum <|> escape)))                <?> "identifier"-escape       = char '\\' >> (uniescape <|> charescape)-uniescape    = (varCount 1 6 hexDigit ~>> perhaps (oneOf " \n"))-               >>= (return . hexToString)-charescape   = noneOf ("\n" ++ ['0'..'9'] ++ ['a'..'f'] ++ ['A'..'F'])+escape       = char '\\' >> (uniescape <|> charescape) where+  uniescape    = (varCount 1 6 hexDigit ~>> perhaps (oneOf " \n"))+                 >>= (return . hexToString)+  charescape   = noneOf ("\n" ++ ['0'..'9'] ++ ['a'..'f'] ++ ['A'..'F']) at_ident     = (char '@' >> ident) <?> "at-identifier" varname      = (char '_' <|> letter) +:+ many (char '_' <|> alphaNum)  -- top-level parser parser :: GenParser Char [Int] [Topl]-parser = many emptyLine >> many (import_ <|> assign <|> cassign <|> block) ~>> end+parser = many emptyLine >> many (atclause <|> assign <|> cassign <|> block) ~>> end   where-    import_ = do+    atclause = do       atid <- at_ident-      if atid == "import" then liftM2 Import getline exprseq_nl-                          else unexpected "at-identifier, expecting @import"-    assign  = liftM3 (Assign Always) getline varassign exprseq_nl-    cassign = liftM3 (Assign IfNotAssigned) getline cvarassign exprseq_nl-    block = do-      line <- getline-      selectornames <- selector False-      firstitem <- subblock True <|> subgroup True <|> property True-      restitems <- many $ (subblock False <|> subgroup False <|> property False)-      updateState tail-      return $! Block line selectornames (firstitem:restitems)-    subblock fst = do-      line <- getline-      selectornames <- selector fst-      firstitem <- subblock True <|> subgroup True <|> property True-      restitems <- many $ (subblock False <|> subgroup False <|> property False)-      updateState tail-      return $! SubBlock line selectornames (firstitem:restitems)+      case atid of+        "import" -> Import <$> getline <*> exprseq_nl+        "include" -> Include <$> getline <*> exprseq_nl+        "define" -> Macro <$> getline <*> defname <*> defargs <*> blockitems+        _        -> unexpected "at-identifier, expecting @import, @include or @define"+    assign  = Assign Always <$> getline <*> varassign <*> exprseq_nl+    cassign = Assign IfNotAssigned <$> getline <*> cvarassign <*> exprseq_nl+    blockitems = do+      firstitem <- subblock True <|> subgroup True <|> defsubst True <|> property True+      restitems <- many $ (subblock False <|> subgroup False <|>+                           defsubst False <|> property False)+      updateState tail  -- remove indentation level from stack+      return (firstitem:restitems)+    block = Block <$> getline <*> selector False <*> blockitems+    subblock fst = SubBlock <$> getline <*> selector fst <*> blockitems     subgroup fst = do       line <- getline       groupname <- grpname fst@@ -140,14 +176,19 @@       restitems <- many $ property False       updateState tail       return $! SubGroup line groupname (firstitem:restitems)-    property fst = liftM3 Property getline (propname fst) exprseq_nl+    defsubst fst = UseMacro <$> getline <*> macname fst <*> (exprs2list <$> macargs)+    property fst = Property <$> getline <*> propname fst <*> exprseq_nl     selector fst = do       names <- selnames fst       return $! map trim (split "," names) -- XXX: breaks with fancy selectors      -- position helper-    getline      = liftM sourceLine getPosition+    getline      = sourceLine <$> getPosition +    -- exprlist helper for macro arguments+    exprs2list (ExprListCons a b) = a : exprs2list b+    exprs2list c                  = [c]+     -- toplevel variable assignment     varassign    = (try $ varname ~>> (ws >> char '=' >> ws)) <?> "assignment"     cvarassign   = (try $ varname ~>> (ws >> string "?=" >> ws)) <?> "assignment"@@ -156,6 +197,13 @@     selnames fst = (try $ indented fst $ --string "def " >> ws >>                         noneOf "\n" `manyTill` (try $ char ':' >> newline))                    <?> "selectors"+    defname      = varname <?> "macro name"+    defargs      = (char '(' >> (varname `sepBy` (char ',' >> ws)) ~>>+                    (pws $ char ')') ~>> char ':' ~>> newline)+                   <?> "macro arguments"+    macname fst  = (try $ indented fst $ char '%' >> varname) <?> "macro substitution"+    macargs      = (char '(' >> option NoExpr expression ~>> char ')' ~>> newline)+                   <?> "macro arguments"     propname fst = (try $ indented fst $ ident ~>> char ':') <?> "property name"     grpname  fst = (try $ indented fst $ grpident ~>> (string "->" >> newline))                    <?> "group name"@@ -179,38 +227,35 @@                             else unexpected "indentation"              else if ilen == olen then return tok else unexpected "indentation level" --- expression parser-exprseq_nl = exprseq ~>> (option ';' (char ';') >> newline)+    exprseq_nl = exprseq ~>> (option ';' (char ';') >> newline) +-- expression parser exprseq :: GenParser Char [Int] [Expr] exprseq = ws >> many1 expression+expression :: GenParser Char [Int] Expr+expression = plusExpr `chainr1` listOp   where-    expression = plusExpr `chainr1` listOp     listOp = op ',' >> return ExprListCons-    plusOp = (op '+' >> return Plus) <|> (op '-' >> return Minus)     plusExpr = mulExpr `chainl1` plusOp+    plusOp = (op '+' >> return Plus) <|> (op '-' >> return Minus)+    mulExpr = primary `chainl1` mulOp     mulOp = (op '*' >> return Mul) <|> (op '/' >> return Divide) <|>             (op '%' >> return Modulo)-    mulExpr = primary `chainl1` mulOp     primary = do       object <- parenthesized <|> str <|> dimension <|> number <|> color <|>                 func <|> rgb <|> var <|> bare       calltails object-    parenthesized = liftM Subseq $ between (op '(') (op ')') (many1 expression)-    str = liftM String $ (sqstring <|> dqstring)-    number = liftM (Number . readNum) num-    dimension = liftM (Dim . readDim) dim-    color = liftM (Color . Right . hexToColor) hexcolor-    var = liftM Var varref+    parenthesized = Subseq <$> between (op '(') (op ')') (many1 expression)+    str = String <$> (sqstring <|> dqstring)+    number = (Number . readNum) <$> num+    dimension = (Dim . readDim) <$> dim+    color = (Color . Right . hexToColor) <$> hexcolor+    var = Var <$> varref     bare = do       name <- ident       if Map.member name colors then return $ Color (Left name)                                 else return $ Bare name-    func = do-      funcname <- choice (map funcall css_functions)-      args <- expression-      op ')'-      return $! CSSFunc funcname args+    func = CSSFunc <$> choice (map funcall css_functions) <*> expression ~>> op ')'     rgb = do       funcall "rgb"       channels <- expression -- r, g, b@@ -227,7 +272,7 @@         Just called -> calltails called     call object = do       methname <- methcall-      callexpr <- option Nothing (liftM Just expression)+      callexpr <- option Nothing (Just <$> expression)       op ')'       return $! Just (Call object methname callexpr) @@ -239,95 +284,148 @@     num          = (pws $ (perhaps $ char '-') +++ many1 digit +++                         option "" (char '.' +:+ many1 digit)) <?> "number"     dim          = (pws $ try $ num +++ ws +++ unit) <?> "dimension"-    unit         = choice (map string+    unit         = choice (map (try . string)                            ["em", "ex", "px", "cm", "mm", "in", "pt", "pc", "deg",                             "rad", "grad", "ms", "s", "Hz", "kHz", "%"])-    dqstring     = (pws $ char '"' >> many1 (noneOf "\n\\\"" <|> escape) ~>> char '"')+    dqstring     = (pws $ char '"' >> many (noneOf "\n\\\"" <|> escape) ~>> char '"')                    <?> "string"-    sqstring     = (pws $ char '\'' >> many1 (noneOf "\n\\'" <|> escape) ~>> char '\'')+    sqstring     = (pws $ char '\'' >> many (noneOf "\n\\'" <|> escape) ~>> char '\'')                    <?> "string"     hexcolor     = (pws $ char '#' >> ((try $ count 6 hexDigit) <|> count 3 hexDigit))                    <?> "color"-    op c         = (pws $ char c) <?> "operator " ++ [c]+    op c         = (pws $ char c) <?> "operator " ++ show c  ------------------------------------------------------------------------------------------ -- the evaluator -data EvalError = EvalErr !Line !String  -- line, message-type EvalMonad = RWST (Map.Map String Expr) () [Topl] (Either EvalError) ()+data EvalError = EvalErr !SourceName !Line !String  -- filename, line, message+type Dict cont = Map.Map String cont+data Env = Env { vars :: Dict Expr, macros :: Dict (Line, [String], [Item]) }+-- the main evaluator monad, using every part of RWST:+--     the Reader type is the environment of variables and macros+--     the Writer type is the output of evaluated toplevels+--     the State type is the filename of the part of code currently evaluated+--     the underlying monad handles errors and eventually does IO+--     (which is necessary for including files)+type Eval res  = RWST Env (Seq Topl) SourceName (ErrorT EvalError IO) res  instance Error EvalError where-  strMsg s = EvalErr 0 s+  strMsg s = EvalErr "" 0 s  instance Show EvalError where-  show (EvalErr 0 msg) = ": " ++ msg-  show (EvalErr l msg) = "(line " ++ show l ++ "):\n" ++ msg+  show (EvalErr f 0 msg) = "(file " ++ show f ++ "): " ++ msg+  show (EvalErr f l msg) = "(file " ++ show f ++ ", line " ++ show l ++ "):\n" ++ msg -translate :: [Topl] -> [(String, String)] -> Either EvalError [Topl]-translate toplevels initial_map =-  fmap fst $ execRWST (resolve_toplevels toplevels) (eval_map Map.empty initial_map) []-  where-  -- evaluate items in the initial map-  eval_map :: Map.Map String Expr -> [(String, String)] -> Map.Map String Expr-  eval_map map [] = map-  eval_map map ((n,v):ds) = eval_map (Map.insert n-                                           (evaluate map "initial variables" v) map) ds+evalErr line err = do+  fname <- get+  throwError $ EvalErr fname line err -  -- this keeps a state of all collected blocks.-  -- it returns (Left error) or (Right ([evaluated blocks], ()))-  resolve_toplevels :: [Topl] -> EvalMonad-  resolve_toplevels (Block line sels items : ts) = do-    resolve_block line sels items-    resolve_toplevels ts-  resolve_toplevels (Import line exprseq : ts) = do-    exprs <- eval_exprseq exprseq-    case exprs of-      CSSFunc "url" u -> modify (Import line [CSSFunc "url" u] : )-      v -> throwError $ EvalErr line $-           "invalid thing to import, should be url(): " ++ show v-    resolve_toplevels ts-  resolve_toplevels (Assign how line name exprseq : ts) = do-    ispresent <- asks (Map.member name)-    if ispresent && how == IfNotAssigned then resolve_toplevels ts else do-      exprs <- eval_exprseq exprseq-      case exprs of-        Error err -> throwError $ EvalErr line err-        _         -> local (Map.insert name exprs) (resolve_toplevels ts)-  resolve_toplevels [] = return ()-    -  resolve_block line sels items = do-    props <- mapM (resolve_item sels) items-    modify (Block line sels (concat props) : )+updateVars   f r = r { vars   = f (vars r)   }+updateMacros f r = r { macros = f (macros r) } -  resolve_item _ (Property line name exprseq) = do-    exprs <- eval_exprseq exprseq+translate :: String -> [Topl] -> Dict Expr -> IO (Either EvalError (Seq Topl))+translate filename toplevels varmap = do+  let initialEnv = Env { vars = varmap, macros = Map.empty }+  res <- runErrorT $ execRWST (resolveToplevels toplevels) initialEnv filename+  return (snd <$> res)+  where+  emitBlock = tell . singleton+  -- resolve a list of toplevels -- this "writes" collected blocks+  -- returns (Left error) or (Right (filename, collected blocks))+  resolveToplevels :: [Topl] -> Eval ()+  resolveToplevels (SetFilename filename : ts) = do+    put filename+    resolveToplevels ts+  resolveToplevels (Block line sels items : ts) = do+    -- block: resolve it and continue+    resolveBlock line sels items+    resolveToplevels ts+  resolveToplevels (Macro line sel argnames items : ts) = do+    -- macro definition: store the items in the macro map and continue+    local (updateMacros $ Map.insert sel (line, argnames, items)) (resolveToplevels ts)+  resolveToplevels (Import line exprseq : ts) = do+    -- import: just append it to the collected blocks+    exprs <- evalExprseq line exprjoin exprseq     case exprs of-      Error err -> throwError $ EvalErr line err-      _         -> return [Property line name [exprs]]-  resolve_item sels (SubBlock line subsels items) =-    resolve_block line (combine_sels sels subsels) items >> return []-  resolve_item _ (SubGroup _ name items) = mapM (resolve_group name) items+      CSSFunc "url" u -> emitBlock $ Import line [CSSFunc "url" u]+      v -> evalErr line $ "invalid thing to import, should be url(): " ++ show v+    resolveToplevels ts+  resolveToplevels (Include line exprseq : ts) = do+    -- include: read and append it+    exprs <- evalExprseq line exprjoin exprseq+    case exprs of+      String filename -> do+        oldfilename <- get+        contents <- liftIO $ E.try (readFile filename)+        case contents of+          Left ex -> evalErr line ("error reading included file: " +++                                   show (ex :: IOError))+          Right c ->+            case runParser parser [0] filename (preprocess c) of+              Left err -> evalErr line $ "parse error in @include " ++ show err+              Right parse -> resolveToplevels ((SetFilename filename : parse) +++                                               (SetFilename oldfilename : ts))+      v -> evalErr line $ "invalid thing to include, should be a string: " ++ show v+  resolveToplevels (Assign how line name exprseq : ts) = do+    -- assignment: store it in the variable map and continue+    ispresent <- asks (Map.member name . vars)+    if ispresent && how == IfNotAssigned then resolveToplevels ts else do+      exprs <- evalExprseq line exprjoin exprseq+      local (updateVars $ Map.insert name exprs) (resolveToplevels ts)+  resolveToplevels [] = return () -  resolve_group name (Property line prop exprs) =-    fmap head $ resolve_item [] (Property line (name ++ "-" ++ prop) exprs)-  resolve_group _ _ = error "impossible item in group"+  resolveBlock :: Line -> [String] -> [Item] -> Eval ()+  resolveBlock line sels items = do+    props <- mapM (resolveItem sels) items+    emitBlock $ Block line sels (concat props) -  combine_sels sels subsels = [comb s1 s2 | s1 <- sels, s2 <- subsels]+  resolveItem :: [String] -> Item -> Eval [Item]+  resolveItem _ (Property line name exprseq) = do+    expr <- evalExprseq line exprjoin exprseq+    return [Property line name [expr]]+  resolveItem sels (UseMacro line name args) = do+    lookresult <- asks (Map.lookup name . macros)+    case lookresult of+      Nothing -> evalErr line ("macro " ++ name ++ " is not defined")+      Just (_, argnames, items) -> do+        let numargs = length argnames+            given   = length args+        if numargs /= given+          then evalErr line ("wrong number of arguments for macro " ++ name +++                             ": given " ++ show given ++ ", should be " ++ show numargs)+          else do+            evaledargs <- evalExprseq line id args+            -- update locals with evaluated arguments+            let updfunc = updateVars $ Map.union (Map.fromList $ zip argnames evaledargs)+            local updfunc (concat <$> mapM (resolveItem sels) items)+  resolveItem sels (SubBlock line subsels items) =+    resolveBlock line (combineSels sels subsels) items >> return []+  resolveItem _ (SubGroup _ name items) = mapM (resolveGroup name) items++  resolveGroup name (Property line prop exprs) =+    head <$> resolveItem [] (Property line (name ++ "-" ++ prop) exprs)+  resolveGroup _ _ = error "impossible item in group"++  combineSels sels subsels = [comb s1 s2 | s1 <- sels, s2 <- subsels]     where comb s1 s2 = maybe (s1 ++ " " ++ s2)                        (\i -> (take i s2) ++ s1 ++ (drop (i+1) s2))                        (findIndex (=='&') s2) -  eval_exprseq []  = return $ String ""-  eval_exprseq [e] = asks id >>= return . (flip eval e)-  eval_exprseq seq = do-    varmap <- asks id-    return $ findError (Bare . joinShow " ") $ map (eval varmap) seq+  exprjoin [e] = e+  exprjoin es  = Bare $ joinShow " " es -  -- evaluate an Expr-  eval varmap exp = let eval' = eval varmap in case exp of+  evalExprseq _    cons []  = return $ cons [String ""]+  evalExprseq line cons seq = do+    varmap <- asks vars+    case findError (map (eval varmap) seq) of+      [Error err] -> evalErr line err+      result      -> return $ cons result++-- evaluate an Expr+eval varmap exp = let eval' = eval varmap in case exp of     Var a -> case Map.lookup a varmap of       Just val -> val-      Nothing -> Error $ "variable " ++ a ++ " is not defined" +      Nothing  -> Error $ "variable " ++ a ++ " is not defined"     Plus a b -> case (eval' a, eval' b) of       (String s, String t) -> String (s ++ t)       (Number n, Number m) -> Number (n + m)@@ -382,7 +480,7 @@       (e@(Error _), _) -> e       (e, ExprList es) -> ExprList (e:es)       (e1, e2) -> ExprList [e1, e2]-    Subseq es -> findError Subseq (map eval' es)+    Subseq es -> findError2 Subseq (map eval' es)     CSSFunc name args -> case (name, eval' args) of       ("url", String url) -> CSSFunc "url" (String url)       ("attr", args) -> CSSFunc "attr" args@@ -401,7 +499,7 @@       ("strip", String s, Nothing) -> String (trim s)       ("split", String s, Just (String delim)) ->         ExprList (map String (split delim s))-      ("eval", String s, Nothing) -> evaluate varmap "evaled string" s+      ("eval", String s, Nothing) -> evalString varmap "evaled string" s       -- Number methods       ("round", Number n, Just (Number p)) -> Number (roundRat n p)       ("round", Number n, Nothing) -> Number (roundRat n 0)@@ -427,7 +525,7 @@         Color $ Right $ darkenColor (rgbColor col) (getAmount arg)       -- All else is invalid       (name, exp, arg) ->-        Error $ printf "cannot call method %s(%s) on %s" name (show arg) (show exp)+        Error $ printf "cannot call method %s(%s) on %s" name (jshow arg) (show exp)     Rgb r g b -> case (eval' r, eval' g, eval' b) of       (Number r', Number g', Number b') -> Color $ Right (cx r', cx g', cx b')       (Dim (r', "%"), Dim (g', "%"), Dim (b', "%")) ->@@ -436,42 +534,59 @@       where cx = inrange 0 255 . floor     -- all other cases are primitive     atom -> atom+  where+    jshow Nothing  = ""+    jshow (Just a) = show a -  -- evaluate a string-  evaluate varmap source string = case runParser exprseq [] "" string of-    Left err  -> Error $ showWithoutPos ("in "++source++":") err-    Right []  -> String ""-    Right [e] -> eval varmap e-    Right seq -> findError Subseq $ map (eval varmap) seq+    rgbColor = either (colors Map.!) id -  -- return either the first Error in xs, or else (cons xs)-  findError cons xs = head $ [Error e | Error e <- xs] ++ [cons xs]+    getAmount arg = case arg of+      Nothing -> 0.1+      Just (Number am) -> (fromRational am) / 100+      Just (Dim (am, "%")) -> (fromRational am) / 100+      _ -> 0 -  rgbColor = either (colors Map.!) id -  getAmount arg = case arg of-    Nothing -> 0.1-    Just (Number am) -> (fromRational am) / 100-    Just (Dim (am, "%")) -> (fromRational am) / 100-    _ -> 0+-- return either the first Error in xs, or else xs+findError xs  = head $ [[Error e] | Error e <- xs] ++ [xs]+-- return either the first Error in xs, or else (cons xs)+findError2 cons xs = head $ [Error e | Error e <- xs] ++ [cons xs] +-- evaluate a string+evalString varmap source string = case runParser exprseq [] "" string of+  Left err  -> Error $ showWithoutPos ("in " ++ source ++ ":") err+  Right []  -> String ""+  Right [e] -> eval varmap e+  Right seq -> findError2 Subseq $ map (eval varmap) seq++-- evaluate expressions into a map+evalMap :: Dict Expr -> [(String, String)] -> Dict Expr+evalMap map [] = map+evalMap map ((n,v):ds) = evalMap (Map.insert n+                                  (evalString map "initial variables" v) map) ds+ ------------------------------------------------------------------------------------------ -- main conversion function -format :: [Topl] -> String-format blocks = concatMap format_block blocks where-  format_block (Block _ sels props) =-    joinStr ", " sels ++ " {\n" ++ unlines (map format_prop props) ++ "}\n\n"-  format_block (Import _ exprs) = "@import " ++ joinShow " " exprs ++ ";\n"-  format_block (Assign _ _ _ _) = error "remaining assignment in eval result"-  format_prop (Property _ name [val]) = "    " ++ name ++  ": " ++ show val ++ ";"-  format_prop (Property _ _ _) = error "property has not exactly one value" -  format_prop _ = error "remaining subitems in block" +format :: Seq Topl -> String+format blocks = F.foldl (\x y -> x ++ formatBlock y) "" blocks where+  formatBlock (Block _ sels props) =+    joinStr ", " sels ++ " {\n" ++ unlines (map formatProp props) ++ "}\n\n"+  formatBlock (Import _ exprs) = "@import " ++ joinShow " " exprs ++ ";\n"+  formatBlock (Include _ _) = error "remaining include in eval result"+  formatBlock (Macro _ _ _ _) = error "remaining definition in eval result"+  formatBlock (Assign _ _ _ _) = error "remaining assignment in eval result"+  formatBlock (SetFilename _) = error "remaining filename in eval result"+  formatProp (Property _ name [val]) = "    " ++ name ++  ": " ++ show val ++ ";"+  formatProp (Property _ _ _) = error "property has not exactly one value"+  formatProp _ = error "remaining subitems in block" -cleverCSSConvert :: SourceName -> String -> [(String, String)] -> Either String String+cleverCSSConvert :: SourceName -> String -> [(String, String)] -> IO (Either String String) cleverCSSConvert name input initial_map =   case runParser parser [0] name (preprocess input) of-      Left err    -> Left $ "Parse error " ++ show err-      Right parse -> case translate parse initial_map of-        Left evalerr -> Left $ "Evaluation error " ++ show evalerr-        Right blocks -> Right $ format (reverse blocks)+      Left err    -> return . Left $ "Parse error " ++ show err+      Right parse -> do+        result <- translate name parse (evalMap Map.empty initial_map)+        case result of+          Left evalerr -> return . Left $ "Evaluation error " ++ show evalerr+          Right blocks -> return . Right $ format blocks
Text/CSS/CleverCSSUtil.hs view
@@ -2,7 +2,7 @@ -- CleverCSS in Haskell, (c) 2007, 2008 Georg Brandl. Licensed under the BSD license. -- -- Text.CSS.CleverCSSUtil module: utilities.------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------  module Text.CSS.CleverCSSUtil (              (+++), (+:+), (~>>), varCount, perhaps,@@ -18,7 +18,7 @@ import Control.Monad (msum) import Data.Char import Data.List hiding (partition)-import Data.Ratio (Ratio, (%), numerator, denominator)+import Data.Ratio ((%), numerator, denominator) import Numeric (readFloat) import Text.Printf (printf) import Text.ParserCombinators.Parsec (choice, count, try, option, GenParser)@@ -303,10 +303,10 @@ breakList :: ([a] -> Bool) -> [a] -> ([a], [a]) breakList func = spanList (not . func) -split :: String -> String -> [String] +split :: String -> String -> [String] split _ [] = [] split delim str =-    let (firstline, remainder) = breakList (isPrefixOf delim) str in +    let (firstline, remainder) = breakList (isPrefixOf delim) str in     firstline : case remainder of                   [] -> []                   x -> if x == delim then [] : []@@ -314,7 +314,7 @@  {-# INLINE joinStr #-} joinStr d x = concat (intersperse d x)-{-# INLINE joinShow #-} +{-# INLINE joinShow #-} joinShow d x = joinStr d (map show x)  hexToString :: String -> Char@@ -341,6 +341,6 @@   ny = numerator y * (d `div` dy)  roundRat :: Rational -> Rational -> Rational-roundRat num places = +roundRat num places =     let exp = round places :: Integer in     (round (num * (10^exp))) % (10^exp) -- XXX todo
clevercss.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.2 Name: clevercss-Version: 0.1.1+Version: 0.2 Category: Text Synopsis: A CSS preprocessor Description: CleverCSS is a CSS preprocessing library that allows defining variables and nesting selectors so that you don't need to Repeat Yourself.@@ -16,11 +16,19 @@ Flag splitBase   Description: Choose the new smaller, split-up base package. +Flag parsec2+  Description: Are we using parsec2?+ Library   If flag(splitBase)-    Build-Depends: base >= 3, containers, parsec, mtl, haskell98+    Build-Depends: base >= 4 && < 5, containers, mtl   Else-    Build-Depends: base < 3, parsec, mtl, haskell98+    Build-Depends: base < 4, mtl+  If flag(parsec2)+    Build-Depends: parsec >= 2.1+    CPP-Options: -DPARSEC2+  Else+    Build-Depends: parsec >= 3 && < 4   Exposed-Modules: Text.CSS.CleverCSS   Other-Modules: Text.CSS.CleverCSSUtil   Extensions: PatternGuards@@ -30,4 +38,9 @@   Main-is: CCMain.hs   Extensions: PatternGuards   GHC-Options: -funbox-strict-fields+  If flag(parsec2)+    Build-Depends: parsec >= 2.1+    CPP-Options: -DPARSEC2+  Else+    Build-Depends: parsec >= 3 && < 4 
documentation.html view
@@ -3,22 +3,22 @@ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />-<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />+<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" /> <title>CleverCSS (Haskell) Documentation</title> <style type="text/css">  body {-    margin: 0.0;-    padding: 0.0 10.0% 0.0 10.0%;+    margin: 0;+    padding: 0 10% 0 10%;     background-color: #5985de;     font-family: "Bitstream Vera Sans", sans-serif; }  .document {     background-color: white;-    padding: 1.0px 20.0px 1.0px 20.0px;-    border-left: 5.0px solid #aaaaff;-    border-right: 5.0px solid #aaaaff;+    padding: 1px 20px 1px 20px;+    border-left: 5px solid #aaaaff;+    border-right: 5px solid #aaaaff; }  h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {@@ -27,26 +27,26 @@  h1, h2, h3, h4, h5, h6 {     font-family: "Arial", sans-serif;-    margin-top: 1.0em;+    margin-top: 1em; }  h1.title {     font-size: 2.5em;     color: #333333;-    margin-top: 10.0px;-    margin-left: -20.0px;-    margin-right: -20.0px;-    padding-left: 20.0px;-    padding-right: 20.0px;-    padding-bottom: 10.0px;-    border-bottom: 2.0px solid #cccccc;+    margin-top: 10px;+    margin-left: -20px;+    margin-right: -20px;+    padding-left: 20px;+    padding-right: 20px;+    padding-bottom: 10px;+    border-bottom: 2px solid #cccccc; }  pre {     background-color: #d8f5c0;-    padding: 5.0px;-    border-top: 1.0px solid #cccccc;-    border-bottom: 1.0px solid #cccccc;+    padding: 5px;+    border-top: 1px solid #cccccc;+    border-bottom: 1px solid #cccccc; }  a {@@ -54,7 +54,7 @@ }  table td {-    padding: 2.0px;+    padding: 2px; }  table {@@ -68,8 +68,9 @@ <body> <div class="document" id="clevercss-haskell-documentation"> <h1 class="title">CleverCSS (Haskell) Documentation</h1>-<div class="section">-<h1><a id="introduction" name="introduction">Introduction</a></h1>++<div class="section" id="introduction">+<h1>Introduction</h1> <p>CleverCSS is a system for writing templates that are translated into CSS, allowing to build style sheets in a clean and structured way, following the DRY (don't repeat yourself) principle.</p>@@ -99,30 +100,30 @@    font-size: 1.2em; } </pre>-<p>You can already see that the template doesn't repeat the <tt class="docutils literal"><span class="pre">ul#comments</span></tt> and-<tt class="docutils literal"><span class="pre">ol#comments</span></tt> selectors.  Also, should the desired background color ever+<p>You can already see that the template doesn't repeat the <tt class="docutils literal">ul#comments</tt> and+<tt class="docutils literal">ol#comments</tt> selectors.  Also, should the desired background color ever change, you won't need to replace a zillion references to it, but only one variable assignment.</p> <p>Other than that, CleverCSS allows expressions involving CSS values as well as grouping of similar property names, as shown below.</p> </div>-<div class="section">-<h1><a id="syntax" name="syntax">Syntax</a></h1>+<div class="section" id="syntax">+<h1>Syntax</h1> <p>As you have already seen, you use indentation and colons to express grouping. There are also other small syntactic differences to CSS that make writing templates less pain:</p> <ul class="simple"> <li>Semicolons at line ends are optional.</li>-<li>There are no <tt class="docutils literal"><span class="pre">/*</span> <span class="pre">...</span> <span class="pre">*/</span></tt> multiline comments, but Java-style <tt class="docutils literal"><span class="pre">//</span></tt> line+<li>There are no <tt class="docutils literal">/* ... */</tt> multiline comments, but Java-style <tt class="docutils literal">//</tt> line comments.</li>-<li>A line consisting only of <tt class="docutils literal"><span class="pre">__END__</span></tt> means &quot;end of template&quot;.  Everything+<li>A line consisting only of <tt class="docutils literal">__END__</tt> means &quot;end of template&quot;.  Everything after that line is completely ignored by CleverCSS.</li> </ul> </div>-<div class="section">-<h1><a id="constructs" name="constructs">Constructs</a></h1>-<div class="section">-<h2><a id="variable-assignments" name="variable-assignments">Variable assignments</a></h2>+<div class="section" id="constructs">+<h1>Constructs</h1>+<div class="section" id="variable-assignments">+<h2>Variable assignments</h2> <p>You can assign to variables with an equals sign.  Variable assignments are not allowed inside blocks, only at the toplevel.  Variables can be assigned to multiple times.</p>@@ -134,7 +135,7 @@ p:    margin: $foo </pre>-<p>would result in <tt class="docutils literal"><span class="pre">body</span> <span class="pre">{</span> <span class="pre">margin:</span> <span class="pre">1px</span> <span class="pre">}</span></tt> and <tt class="docutils literal"><span class="pre">p</span> <span class="pre">{</span> <span class="pre">margin:</span> <span class="pre">2px</span> <span class="pre">}</span></tt>.  Referencing+<p>would result in <tt class="docutils literal">body { margin: 1px }</tt> and <tt class="docutils literal">p { margin: 2px }</tt>.  Referencing variables before they are defined is an error.</p> <p>There's also another assignment operator, <tt class="docutils literal"><span class="pre">?=</span></tt>.  It only has an effect if the variable is not assigned to yet.  This is helpful to provide defaults that can@@ -145,11 +146,11 @@ foo = 2px bar ?= 2px </pre>-<p><tt class="docutils literal"><span class="pre">$foo</span></tt> would be replaced by <tt class="docutils literal"><span class="pre">2px</span></tt>, while <tt class="docutils literal"><span class="pre">$bar</span></tt> would be replaced by-<tt class="docutils literal"><span class="pre">1px</span></tt>.</p>+<p><tt class="docutils literal">$foo</tt> would be replaced by <tt class="docutils literal">2px</tt>, while <tt class="docutils literal">$bar</tt> would be replaced by+<tt class="docutils literal">1px</tt>.</p> </div>-<div class="section">-<h2><a id="selectors" name="selectors">Selectors</a></h2>+<div class="section" id="selectors">+<h2>Selectors</h2> <p>CleverCSS does not process selectors in a special way, but as shown above you can nest selector blocks instead of repeating parent selectors in individual blocks:</p>@@ -175,7 +176,7 @@ incorrectly at the moment.</p> <p>As you see, the above handles basic parent-child relations by joining parent and child selectors with a space.  However, you can also explicitly determine where-to put the parent selector, using the <tt class="docutils literal"><span class="pre">&amp;</span></tt> character:</p>+to put the parent selector, using the <tt class="docutils literal">&amp;</tt> character:</p> <pre class="literal-block"> strong, em:    a &amp;, #&amp;:@@ -188,14 +189,31 @@ <p>Using this technique, you can construct selectors quite freely.  Multiple occurrences of the ampersand are replaced.</p> </div>-<div class="section">-<h2><a id="properties" name="properties">Properties</a></h2>+<div class="section" id="properties">+<h2>Properties</h2> <p>Property specifications work like in CSS: just put a colon after the property name.  You can use expressions or variable references (see below) only in property values, not in property names.</p>+<div class="note">+<p class="first admonition-title">Note</p>+<p>Due to the current limited parsing of CSS selectors, CleverCSS can confuse+the <tt class="docutils literal">://</tt> in URLs occurring in property values with a selector and a+following comment.  Use a variable for the URL (or at least a part of it+that includes the <tt class="docutils literal">://</tt>) to work around that:</p>+<pre class="last literal-block">+// bad - will cause parsing error+div:+    background-image: url(&quot;http://www.example.com/image.png&quot;)++// workaround+host = &quot;http://www.example.com/&quot;+div:+    background-image: url($host + &quot;image.png&quot;)+</pre> </div>-<div class="section">-<h2><a id="property-groups" name="property-groups">Property groups</a></h2>+</div>+<div class="section" id="property-groups">+<h2>Property groups</h2> <p>Since CSS has many property names with common prefixes, CleverCSS includes one more shortening notation, best described by an example:</p> <pre class="literal-block">@@ -216,9 +234,42 @@ <p>The <tt class="docutils literal"><span class="pre">-&gt;</span></tt> symbol, followed by a block of properties, concatenates the name before it with each of the property names in the block.</p> </div>+<div class="section" id="macros">+<h2>Macros</h2>+<p>Macros can help you writing often used groups of properties and sub-blocks only+once.  Define macros like this:</p>+<pre class="literal-block">+&#64;define mymacro(arg1, arg2):+    font-family: $arg1+    p:+        display: inline+        color: $arg2+</pre>+<p>Macros can have zero or more arguments.  Inside a macro definition, the+arguments are accessible like normal variables.  Macros must be defined at+top-level, and their names live in a different namespace than variables.</p>+<p>Use (&quot;substitute&quot;) them like this:</p>+<pre class="literal-block">+body:+    %mymacro(&quot;Verdana&quot;, blue)+    font-size: 1.1em+</pre>+<p>Macro substitutions are handled as if the macro's contents are placed at the+exact location of the substitution, with the argument variables replaced by the+given expressions.</p> </div>-<div class="section">-<h1><a id="values-and-expressions" name="values-and-expressions">Values and expressions</a></h1>+<div class="section" id="includes">+<h2>Includes</h2>+<p>Including a file:</p>+<pre class="literal-block">+&#64;include &quot;filename.ccs&quot;+</pre>+<p>The contents of the included file are treated as if they occurred in the+including file at the point of the include command.</p>+</div>+</div>+<div class="section" id="values-and-expressions">+<h1>Values and expressions</h1> <p>CleverCSS property values are of multiple types which also exist in CSS:</p> <table border="1" class="docutils"> <colgroup>@@ -234,43 +285,43 @@ </thead> <tbody valign="top"> <tr><td>barewords (also called identifiers)</td>-<td><tt class="docutils literal"><span class="pre">font-size:</span> <span class="pre">small</span></tt></td>+<td><tt class="docutils literal"><span class="pre">font-size:</span> small</tt></td> <td>&nbsp;</td> </tr> <tr><td>strings (double or single quoted)</td>-<td><tt class="docutils literal"><span class="pre">font-family:</span> <span class="pre">&quot;Times&quot;</span></tt></td>+<td><tt class="docutils literal"><span class="pre">font-family:</span> &quot;Times&quot;</tt></td> <td>&nbsp;</td> </tr> <tr><td>numbers (integral or floating)</td>-<td><tt class="docutils literal"><span class="pre">line-height:</span> <span class="pre">2</span></tt></td>+<td><tt class="docutils literal"><span class="pre">line-height:</span> 2</tt></td> <td>&nbsp;</td> </tr> <tr><td>dimensions (number + unit)</td>-<td><tt class="docutils literal"><span class="pre">margin-left:</span> <span class="pre">2px</span></tt></td>+<td><tt class="docutils literal"><span class="pre">margin-left:</span> 2px</tt></td> <td>&nbsp;</td> </tr> <tr><td>hexadecimal colors</td>-<td><tt class="docutils literal"><span class="pre">color:</span> <span class="pre">#f0f0f0</span></tt></td>+<td><tt class="docutils literal">color: #f0f0f0</tt></td> <td>&nbsp;</td> </tr> <tr><td>colors as color names</td>-<td><tt class="docutils literal"><span class="pre">color:</span> <span class="pre">black</span></tt></td>+<td><tt class="docutils literal">color: black</tt></td> <td>(1)</td> </tr> <tr><td>colors via the RGB function</td>-<td><tt class="docutils literal"><span class="pre">color:</span> <span class="pre">rgb(10%,</span> <span class="pre">20%,</span> <span class="pre">30%)</span></tt></td>+<td><tt class="docutils literal">color: rgb(10%, 20%, 30%)</tt></td> <td>&nbsp;</td> </tr> <tr><td>value sequences (separated by whitespace)</td>-<td><tt class="docutils literal"><span class="pre">margin:</span> <span class="pre">2px</span> <span class="pre">0</span> <span class="pre">2px</span> <span class="pre">0</span></tt></td>+<td><tt class="docutils literal">margin: 2px 0 2px 0</tt></td> <td>&nbsp;</td> </tr> <tr><td>value lists (separated by commas)</td>-<td><tt class="docutils literal"><span class="pre">font-family:</span> <span class="pre">&quot;Times&quot;,</span> <span class="pre">serif</span></tt></td>+<td><tt class="docutils literal"><span class="pre">font-family:</span> &quot;Times&quot;, serif</tt></td> <td>(2)</td> </tr> <tr><td>function calls</td>-<td><tt class="docutils literal"><span class="pre">content:</span> <span class="pre">attr(id)</span></tt></td>+<td><tt class="docutils literal">content: attr(id)</tt></td> <td>(3)</td> </tr> </tbody>@@ -284,8 +335,8 @@ <li>This is only used in CSS for the <tt class="docutils literal"><span class="pre">font-family</span></tt> property, but you are free to use value lists anywhere and convert them to other values using their methods, see below.</li>-<li>The functions recognized by CleverCSS, in addition to <tt class="docutils literal"><span class="pre">rgb</span></tt>, are <tt class="docutils literal"><span class="pre">attr</span></tt>,-<tt class="docutils literal"><span class="pre">counter</span></tt> and <tt class="docutils literal"><span class="pre">url</span></tt>.</li>+<li>The functions recognized by CleverCSS, in addition to <tt class="docutils literal">rgb</tt>, are <tt class="docutils literal">attr</tt>,+<tt class="docutils literal">counter</tt> and <tt class="docutils literal">url</tt>.</li> </ol> <p>CleverCSS extends CSS in that it allows writing not only these literal values, but also expressions involving these values.  These expressions can contain@@ -304,36 +355,36 @@ </thead> <tbody valign="top"> <tr><td>Arithmetic on numbers and values;-operators are <tt class="docutils literal"><span class="pre">+</span></tt>, <tt class="docutils literal"><span class="pre">-</span></tt>, <tt class="docutils literal"><span class="pre">*</span></tt>, <tt class="docutils literal"><span class="pre">/</span></tt>-and <tt class="docutils literal"><span class="pre">%</span></tt> (modulo)</td>-<td><tt class="docutils literal"><span class="pre">4</span> <span class="pre">%</span> <span class="pre">3</span> <span class="pre">=</span> <span class="pre">1</span></tt>, <tt class="docutils literal"><span class="pre">2px</span> <span class="pre">+</span> <span class="pre">4px</span> <span class="pre">=</span> <span class="pre">6px</span></tt>,-<tt class="docutils literal"><span class="pre">2</span> <span class="pre">*</span> <span class="pre">2px</span> <span class="pre">=</span> <span class="pre">4px</span></tt></td>+operators are <tt class="docutils literal">+</tt>, <tt class="docutils literal">-</tt>, <tt class="docutils literal">*</tt>, <tt class="docutils literal">/</tt>+and <tt class="docutils literal">%</tt> (modulo)</td>+<td><tt class="docutils literal">4 % 3 = 1</tt>, <tt class="docutils literal">2px + 4px = 6px</tt>,+<tt class="docutils literal">2 * 2px = 4px</tt></td> <td>(1)</td> </tr>-<tr><td>Arithmetic on colors with <tt class="docutils literal"><span class="pre">+</span></tt> and <tt class="docutils literal"><span class="pre">-</span></tt></td>-<td><tt class="docutils literal"><span class="pre">#f0f000</span> <span class="pre">+</span> <span class="pre">#000030</span> <span class="pre">=</span> <span class="pre">#f0f030</span></tt>,-<tt class="docutils literal"><span class="pre">#808080</span> <span class="pre">+</span> <span class="pre">16</span> <span class="pre">=</span> <span class="pre">#909090</span></tt></td>+<tr><td>Arithmetic on colors with <tt class="docutils literal">+</tt> and <tt class="docutils literal">-</tt></td>+<td><tt class="docutils literal">#f0f000 + #000030 = #f0f030</tt>,+<tt class="docutils literal">#808080 + 16 = #909090</tt></td> <td>(2)</td> </tr>-<tr><td>Concatenation of strings with <tt class="docutils literal"><span class="pre">+</span></tt></td>-<td><tt class="docutils literal"><span class="pre">&quot;hello</span> <span class="pre">&quot;</span> <span class="pre">+</span> <span class="pre">&quot;world&quot;</span> <span class="pre">=</span> <span class="pre">&quot;hello</span> <span class="pre">world&quot;</span></tt></td>+<tr><td>Concatenation of strings with <tt class="docutils literal">+</tt></td>+<td><tt class="docutils literal">&quot;hello &quot; + &quot;world&quot; = &quot;hello world&quot;</tt></td> <td>(3)</td> </tr> <tr><td>String multiplication</td>-<td><tt class="docutils literal"><span class="pre">&quot;a</span> <span class="pre">&quot;</span> <span class="pre">*</span> <span class="pre">3</span> <span class="pre">=</span> <span class="pre">&quot;a</span> <span class="pre">a</span> <span class="pre">a</span> <span class="pre">&quot;</span></tt></td>+<td><tt class="docutils literal">&quot;a &quot; * 3 = &quot;a a a &quot;</tt></td> <td>&nbsp;</td> </tr> <tr><td>Expression grouping with parentheses</td>-<td><tt class="docutils literal"><span class="pre">(2</span> <span class="pre">+</span> <span class="pre">3)</span> <span class="pre">*</span> <span class="pre">5px</span></tt></td>+<td><tt class="docutils literal">(2 + 3) * 5px</tt></td> <td>&nbsp;</td> </tr> <tr><td>Method calls</td>-<td><tt class="docutils literal"><span class="pre">#303030.brighten(40%)</span> <span class="pre">=</span> <span class="pre">#434343</span></tt>,-<tt class="docutils literal"><span class="pre">&quot;hello&quot;.bare()</span> <span class="pre">=</span> <span class="pre">hello</span></tt></td>+<td><tt class="docutils literal">#303030.brighten(40%) = #434343</tt>,+<tt class="docutils literal"><span class="pre">&quot;hello&quot;.bare()</span> = hello</tt></td> <td>(4)</td> </tr> <tr><td>Variable references</td>-<td><tt class="docutils literal"><span class="pre">$foo</span> <span class="pre">=</span> <span class="pre">&lt;whatever</span> <span class="pre">foo</span> <span class="pre">was</span> <span class="pre">assigned&gt;</span></tt></td>+<td><tt class="docutils literal">$foo = &lt;whatever foo was assigned&gt;</tt></td> <td>&nbsp;</td> </tr> </tbody>@@ -352,13 +403,13 @@ channels.</p> </li> <li><p class="first">Barewords cannot be added, but you can convert strings to barewords with the-<tt class="docutils literal"><span class="pre">bare()</span></tt> method afterwards.</p>+<tt class="docutils literal">bare()</tt> method afterwards.</p> </li> <li><p class="first">For a list of available methods, see below.</p> </li> </ol>-<div class="section">-<h2><a id="methods" name="methods">Methods</a></h2>+<div class="section" id="methods">+<h2>Methods</h2> <p>On values of all types:</p> <ul class="simple"> <li><strong>string()</strong>: convert to a string.</li>@@ -390,17 +441,17 @@ <ul class="simple"> <li><strong>length()</strong>: return the list or sequence's length.</li> <li><strong>join([delim])</strong>: return a string consisting the items converted to strings-and joined by <em>delim</em>, which defaults to <tt class="docutils literal"><span class="pre">&quot;,</span> <span class="pre">&quot;</span></tt> for lists and <tt class="docutils literal"><span class="pre">&quot;</span> <span class="pre">&quot;</span></tt> for+and joined by <em>delim</em>, which defaults to <tt class="docutils literal">&quot;, &quot;</tt> for lists and <tt class="docutils literal">&quot; &quot;</tt> for sequences.</li> <li><strong>list()</strong>: return the sequence as a list, or the list unchanged.</li> <li><strong>seq()</strong>: return the list as a sequence, or the sequence unchanged.</li> </ul> </div> </div>-<div class="section">-<h1><a id="library-usage" name="library-usage">Library usage</a></h1>-<p>Using the CleverCSS library is straightforward, just import <tt class="docutils literal"><span class="pre">Text.CSS.CleverCSS</span></tt>-and use the <tt class="docutils literal"><span class="pre">cleverCSSConvert</span></tt> function, which is defined as</p>+<div class="section" id="library-usage">+<h1>Library usage</h1>+<p>Using the CleverCSS library is straightforward, just import <tt class="docutils literal">Text.CSS.CleverCSS</tt>+and use the <tt class="docutils literal">cleverCSSConvert</tt> function, which is defined as</p> <pre class="literal-block"> cleverCSSConvert :: SourceName -&gt; String -&gt; [(String, String)] -&gt; Either String String </pre>@@ -408,42 +459,42 @@ <ul class="simple"> <li>name of input (normally file name, only used for error messages)</li> <li>input template</li>-<li>initial variable assignments as <tt class="docutils literal"><span class="pre">(name,</span> <span class="pre">value)</span></tt> pairs; the value is evaluated+<li>initial variable assignments as <tt class="docutils literal">(name, value)</tt> pairs; the value is evaluated as a CleverCSS expression when used</li> </ul>-<p>The return value is either <tt class="docutils literal"><span class="pre">Left</span> <span class="pre">errormessage</span></tt> or <tt class="docutils literal"><span class="pre">Right</span> <span class="pre">stylesheet</span></tt>.</p>+<p>The return value is either <tt class="docutils literal">Left errormessage</tt> or <tt class="docutils literal">Right stylesheet</tt>.</p> </div>-<div class="section">-<h1><a id="command-line-usage" name="command-line-usage">Command-line usage</a></h1>+<div class="section" id="command-line-usage">+<h1>Command-line usage</h1> <p>CleverCSS also can be compiled as a standalone command-line program.  It can be called with no arguments, in which case it will convert standard input to standard output, or with file names as arguments, in which case it will convert the files named to CSS and store them in a file with the same name, but the-extension replaced with <tt class="docutils literal"><span class="pre">.css</span></tt> (e.g., <tt class="docutils literal"><span class="pre">example.clevercss</span></tt> is converted to-<tt class="docutils literal"><span class="pre">example.css</span></tt>).</p>-<p>You can use the <tt class="docutils literal"><span class="pre">-D</span> <span class="pre">name=value</span></tt> command line option to assign initial+extension replaced with <tt class="docutils literal">.css</tt> (e.g., <tt class="docutils literal">example.clevercss</tt> is converted to+<tt class="docutils literal">example.css</tt>).</p>+<p>You can use the <tt class="docutils literal"><span class="pre">-D</span> name=value</tt> command line option to assign initial variables.  The value is evaluated as a CleverCSS expression when used.</p> </div>-<div class="section">-<h1><a id="how-to-get-and-install-it" name="how-to-get-and-install-it">How to get and install it</a></h1>+<div class="section" id="how-to-get-and-install-it">+<h1>How to get and install it</h1> <p>CleverCSS can be downloaded from-<a class="reference" href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/clevercss">Hackage</a>-or checked out from Mercurial at &lt;<a class="reference" href="http://dev.pocoo.org/hg/clevercss-hs-main">http://dev.pocoo.org/hg/clevercss-hs-main</a>&gt;.+<a class="reference external" href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/clevercss">Hackage</a>+or checked out from Mercurial at &lt;<a class="reference external" href="http://dev.pocoo.org/hg/clevercss-hs-main">http://dev.pocoo.org/hg/clevercss-hs-main</a>&gt;. It is a cabalized package, so the usual</p> <pre class="literal-block"> runhaskell Setup.lhs configure runhaskell Setup.lhs build sudo runhaskell Setup.lhs install </pre>-<p>should be enough to get the <tt class="docutils literal"><span class="pre">clevercss</span></tt> binary and the <tt class="docutils literal"><span class="pre">Text.CSS.CleverCSS</span></tt>+<p>should be enough to get the <tt class="docutils literal">clevercss</tt> binary and the <tt class="docutils literal">Text.CSS.CleverCSS</tt> library installed.</p> </div>-<div class="section">-<h1><a id="authors" name="authors">Authors</a></h1>-<p>The Haskell CleverCSS library is written by Georg Brandl &lt;<a class="reference" href="mailto:georg&#64;python.org">georg&#64;python.org</a>&gt;.+<div class="section" id="authors">+<h1>Authors</h1>+<p>The Haskell CleverCSS library is written by Georg Brandl &lt;<a class="reference external" href="mailto:georg&#64;python.org">georg&#64;python.org</a>&gt;. Bug reports and suggestions are welcome!</p> <p>The CleverCSS template language was initially devised and implemented in Python-by Armin Ronacher, see &lt;<a class="reference" href="http://sandbox.pocoo.org/clevercss">http://sandbox.pocoo.org/clevercss</a>&gt;.</p>+by Armin Ronacher, see &lt;<a class="reference external" href="http://sandbox.pocoo.org/clevercss">http://sandbox.pocoo.org/clevercss</a>&gt;.</p> </div> </div> </body>
example.ccs view
@@ -5,10 +5,22 @@ background_color = #eee // this is a comment text_color = #111 link_color = #ff0000+font = Times  @import url($base_url + "/styles.css"); +@include "inc.ccs"++@define disp(a):+    display: $a++@define macro(a, b):+    color: $a+    li:+        %disp($b)+ body:+    %macro(rgb(0, 0, 10), inline)     font-family: 'Verdana', 'Times New Roman', serif     color: $text_color     padding->@@ -33,7 +45,8 @@ base_padding = $base_padding + 4px;  div.navigation:-    height: 1.2em;+    height: 1.2 em;+    width: 1.2ex     padding: 0.2cm + 1mm;     color: rgb(40, 50, 7 * 24) // this is a comment too     background-color: #606060.brighten(30%)