HTTPS SSH

CSS Parser

This was written to fulfill a need I had to run quickly through a string of CSS and identify what "type" each character was. With an optional ability to parse it into hierarchical data describing nested selectors and/or media queries (most applicable to LESS rather than vanilla CSS since LESS supports nesting of selectors whereas CSS only supports single nesting of a selector within a media query).

IEnumerable<CategorisedCharacterString> ParseCSS(string content)

and

IEnumerable<CategorisedCharacterString> ParseLESS(string content)

(within the static class CSSParser.Parser) do the basic parsing work where CategorisedCharacterString has the properties

public class CategorisedCharacterString
{
  /// <summary>
  /// This will never be null or an empty string
  /// </summary>
  public string Value { get; }

  /// <summary>
  /// This is the location of the start of the string in the source data
  /// </summary>
  public int IndexInSource { get; }

  public CharacterCategorisationOptions CharacterCategorisation { get; }
}

public enum CharacterCategorisationOptions
{
  Comment,
  CloseBrace,
  OpenBrace,
  SemiColon,
  SelectorOrStyleProperty,
  StylePropertyColon,
  Value,
  Whitespace
}

so calling ParseCSS on

/* Test */ .Content { color: black; }

will return CategorisedCharacterString instances with the data

"/* Test */"     Comment (IndexInSource 0)
" "              Whitespace (IndexInSource 10)
".Content"       SelectorOrStyleProperty (IndexInSource 11)
" "              Whitespace (IndexInSource 19)
"{"              OpenBrace (IndexInSource 20)
" "              Whitespace (IndexInSource 19)
"color"          SelectorOrStyleProperty (IndexInSource 22)
":"              StylePropertyColon (IndexInSource 27)
" "              Whitespace (IndexInSource 28)
"black"          Value (IndexInSource 29)
";"              SemiColon (IndexInSource 34)
" "              Whitespace (IndexInSource 35)
"}"              CloseBrace (IndexInSource 36)

This analysis can be done very cheaply as it is only a very simple representation. It does not, for example, differentiate between the type of the ".Content" value or "color", they are both considered to be of type SelectorOrStyleProperty.

To arrange in a hierchical manner and to categorise more strictly, the data return from ParseCSS or ParseLess can be passed into

IEnumerable<ICSSFragment> ParseIntoStructuredData(
  IEnumerable<CategorisedCharacterString> segments
)

(within the static class CSSParser.ExtendedLESSParser.LessCssHierarchicalParser) which transforms the data again. The interface ICSSFragment is implemented by the classes Import (describing an import statement for another stylesheet), a MediaQuery or a Selector (both of which have a "ChildFragments" set as they may contain other media queries, selectors and/or properties), a StylePropertyName or StylePropertyValue. Note that comments and whitespace are not included in this data.

So content such as

// Example 
html
{
  h1
  {
    color: black;
    background: white url("background.jpg") no-repeat top left;
  }
  p.Intro { padding: 8px; }
}

becomes something like

html
  h1
    color
      black
    background
      white
      url("background.jpg")
      no-repat
      top
      left
  p.Intro
    padding
      8px

(Above: "html" represent a Selector instance with a ChildFragments property containing Selector instances for the "h1" and "p", each with ChildFragments data made up of StylePropertyValue and StylePropertyValue instances).