Current Version: 1.0.32
Project Name: csspp
Parser Rules

Note
Many of the SVG images below were taken from the CSS Syntax Module Level 3 document.

The parser is composed of the following rules:

Parser Input (CSS Preprocessor Detail)

The parser input are nodes representing tokens as returned by a lexer object.

The parser has multiple entry points to accomodate the various type of input data a CSS parser is expected to support. These are:

  • Style Tag – in an HTML document, data in:
    <style> ... </style>
  • Inline Style – in an HTML document, style found in the style attribute:
    <div style="background: red">...</div>
  • CSS file – a .css file with CSS rules

Stylesheet "stylesheet" and "stylesheet-list" (CSS 3)

The stylesheet is an entry point used to parse rules found in a <style> tag. This includes rules such as the CDO and CDC which allow the user to write the style data in what looks like a comment:

<style>
<!--
div { margin: 0 }
-->
</style>

Other documents do not accept the CDO and CDC tokens.

There are our corresponding YACC-like rules:

stylesheet: <empty>
| stylesheet-list
stylesheet-list: CDO
| CDC
| WHITESPACE
| qualified-rule
| at-rule
| stylesheet-list stylesheet-list

Rule List "rule" and "rule-list" (CSS 3)

The Rule List includes qualified rules, @ rules, and whitespace tokens.

There are our corresponding YACC-like rules:

rule-list: <empty>
| rule
| rule rule-list
rule: WHITESPACE
| qualified-rule
| at-rule

At-Rule "at-rule" (CSS 3)

At values define special rules. Some of the at-values are defined by the CSS Preprocessor as extensions. However, the parser has not specifics to handle such.

There is our corresponding YACC-like rule:

at-rule: AT-KEYWORD-TOKEN component-value block
| AT-KEYWORD-TOKEN component-value ';'

Qualified Rule "qualified-rule" (CSS 3)

component-value {}-block

Qualified rules are selectors followed by a block. The selector list can be empty.

Warning
Although it is not clear from the graph, a {}-block ends a list of component-value.

Note that a component value is just whatever preserved token so you can create a field with the qualified-rule grammar:

IDENTIFIER ':' '{' ... '}'

In order to support the SASS syntax, we have an exception that allows us to set variables in the "global scope". So the following works:

$green: green;

Even when you are in the global scope. This is not a valid rule that otherwise matches a valid CSS3 qualified rule. Note that a variable rule can also be terminated by a block since the content of a variable can be set to an entire block of data.

There is our corresponding YACC-like rule:

qualified-rule: component-value-list block
| VARIABLE WHITESPACE ':' component-value-list

Declaration List "declaration-list" (CSS 3)

A declaration list is a list of declarations separated by semi-colons. Such a list can be started with an at-rule.

Whitespaces can appear to separate various elements in such a list.

There is our corresponding YACC-like rule:

declaration-list: WHITESPACE
| WHITESPACE declaration ';' declaration-list
| WHITESPACE at-rule declaration-list

Declaration "declaration" (CSS 3, CSS Preprocessor)

A declaration is a component value with the particularity of starting with an IDENTIFIER which is followed by a colon (:).

To support variables, we also accept a VARIABLE followed by a colon (:).

And to support declarations of functions, we support VARIABLE_FUNCTION followed by a colon (:).

We also support two special extensions:

  • One named !global which is used with variable declarations to mark a variable as a global variable wherever it gets defined:
a {
$width: 3em !global;
width: $width;
}
span {
width: $width; // works because $width was marked !global
}
  • The other named !default which is used with variable declarations to mark that specific declaration as a default value meaning that if the variable is already defined, it does not get modified:
$width: 5em;
a {
$width: 3em !default; // keeps 5em
width: $width;
}
span {
$width: 3em;
width: $width; // use the 3em
}

There is our corresponding YACC-like rule:

declaration: IDENTIFIER WHITESPACE ':' component-value-list
| IDENTIFIER WHITESPACE ':' component-value-list !important
| IDENTIFIER WHITESPACE ':' component-value-list !global
| IDENTIFIER WHITESPACE ':' component-value-list !default

However, we do not enforce the exclamation flag name until later when we know exactly how it is getting used.

!important "!important" (CSS 3)

The !important keyword can be used to prevent overloading certain declaration. It is generally not recommended unless you are not in full control of your entire CSS rules.

There is our corresponding YACC-like rule:

!important: '!' WHITESPACE IDENTIFIER(=="important") WHITESPACE

!global "!global" (CSS Preprocessor Extension)

The !global keyword can be used to mark a variable as global even when declared within a sub-block. This makes the value of the variable available to all the other blocks.

Note that the use of the !global keyword is not recommended.

There is our corresponding YACC-like rule:

!global: '!' WHITESPACE IDENTIFIER(=="global") WHITESPACE

!default "!default" (CSS Preprocessor Extension)

The !default keyword can be used to mark a variable declaration as the default declaration. This means the existing value of the variable, if such exists, does not get modified.

Note that when the !default keyword is used, it does not set the variable if it is defined in any block or globally.

There is our corresponding YACC-like rule:

!default: '!' WHITESPACE IDENTIFIER(=="default") WHITESPACE

Component Value "component-value" and "component-value-list" (CSS 3)

A component value is a preserved token or a block. Blocks are viewed as "non-preserved tokens" and everything else is viewed as a preserved token.

Note that CSS defines preserved tokens because the parsing of various parts of your CSS rules may end up being done by various compilers and thus one compiler may fail where another succeeds and for that reason the preserved tokens is a rather loose definition. For the CSS Preprocessor, all tokens must be understood 100%. Anything that we do not know about, we cannot verify and thus we want to reject.

There is our corresponding YACC-like rule:

component-value: <empty>
| {}-block
| component-value-list
| component-value-list {}-block
component-value-list: component-value-item
| component-value-list component-value-item
component-value-item: preserved-token
| ()-block
| []-block
| function-block
Warning
Notice that our Yacc rule does not follow the declaration of component-value to the letter. All the other grammar rules expect a list of component-value which may also be empty. However, the main difference not conveyed in the graphs is the fact that a {}-block can only be used at the end of a list of 'component-value' entries. (see http://www.w3.org/TR/css-syntax-3/#consume-a-qualified-rule0). In other words, the following are three 'component-value' entries and not just one:
a { b: c } { d: e } { f: g } ...

Preserved Token "preserved-token" (CSS 3)

any token except '{', '(', '[', FUNCTION

The CSS 3 definition of a preserved token is:

Any token except the '{', '(', '[', and FUNCTION.

The importance of this is the fact that all preserved tokens are expected to be kept in the parser output. Since we are a preprocessor which compacts CSS, we do tend to ignore this definition because we want to (1) remove any comment that is not marked as @preserve, (2) compress anything we can, (3) verify the syntax and only allow what is permissible (functional on at least one browser.)

This being said, we still attempt to keep as many tokens as we can in our tree of nodes.

Note that "any token" does not include ';' which marks the end of the a declaration, an -keyword, a variable set... Also, the closing '}', ')', ']' should match open '{', '(', and '[' and that's why they do not appear here either. Again, although this is correct the fact is that we save the opening in our list of preserved tokens.

There is our corresponding YACC-like rule:

preserved-token: ANY-TOKEN except '{', '(', '[', and FUNCTION

{}-block "{}-block" (CSS 3)

Define a sub-block of values.

Warning
The rules as presented here do not specify how things are really work: a {}-block is also a terminator rule when it is found in a component-value. See http://www.w3.org/TR/css-syntax-3/#consume-a-qualified-rule0 as a reference to that rule.

Specifically, curly bracket blocks are used to add any level of declarations within a qualified declaration (a declaration that starts with a list of identifiers and other tokens representing selectors.)

There is our corresponding YACC-like rule:

{}-block: '{' component-value-list '}'

()-block "()-block" (CSS 3)

Define a sub-block of values.

In general, parenthesis are used to group expressions together.

There is our corresponding YACC-like rule:

()-block: '(' component-value-list ')'

[]-block "[]-block" (CSS 3)

Define a sub-block of values.

Specifically, the square bracket blocks are used to test the attributes of an element in a list of selectors.

There is our corresponding YACC-like rule:

[]-block: '[' component-value-list ']'

Function Block "function-block" (CSS 3)

Functions are identifiers immediately followed by an open parenthesis, variable functions are variables immediately followed by an open parenthesis, either is then followed by a list of space or comma separated parameters, and a closing parenthesis. Note that the parsing is very similar to a ()-block.

There is our corresponding YACC-like rule:

function-block: FUNCTION component-value-list ')'
| VARIABLE_FUNCTION component-value-list ')'

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.