Skip to content

Commit

Permalink
[css-syntax-3] Restore the 'parse list of declarations' algo. Rename …
Browse files Browse the repository at this point in the history
…a few algos and the block-grammar productions.
  • Loading branch information
tabatkins committed May 12, 2023
1 parent 090f1b5 commit c7ce041
Showing 1 changed file with 103 additions and 154 deletions.
257 changes: 103 additions & 154 deletions css-syntax-3/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -1870,6 +1870,9 @@ CSS Parsing Results</h3>
(or lists of these):

<dl export dfn-for=CSS>
: [=stylesheet=]
:: A stylesheet has a list of [=rules=].

: <dfn>rule</dfn>
:: A [=rule=] is either an [=at-rule=]
or a [=qualified rule=].
Expand Down Expand Up @@ -2091,20 +2094,28 @@ Parser Entry Points</h3>
for parsing stylesheets.

<li>
"<a>Parse a list of rules</a>" is intended for the content of at-rules such as ''@media''.
It differs from "<a>Parse a stylesheet</a>" in the handling of <<CDO-token>> and <<CDC-token>>.
"<a>Parse a stylesheet's contents</a>" is intended for use by the
{{CSSStyleSheet/replace()|CSSStyleSheet replace()}} method,
and similar,
which parse text into the contents
of an existing stylesheet.

<li>
"<a>Parse a rule</a>" is intended for use by the <code>CSSStyleSheet#insertRule</code> method,
"<a>Parse a rule</a>" is intended for use by the
{{CSSStyleSheet/insertRule()|CSSStyleSheet insertRule()}} method,
and similar,
which parse text into a single rule.
<code>CSSStyleSheet#insertRule</code> method,
and similar functions which might exist,
which parse text into a single rule.

<li>
"<a>Parse a declaration</a>" is used in ''@supports'' conditions. [[CSS3-CONDITIONAL]]

<li>
"<a>Parse a list of declarations</a>" is for the contents of a <code>style</code> attribute,
which parses text into the contents of a single style rule.
"<a>Parse a block's contents</a>" is inteded for parsing the contents of any block in CSS
(including things like the style attribute},
and APIs such as {{CSSStyleDeclaration/cssText|the CSSStyleDeclaration cssText attribute}}.

<li>
"<a>Parse a component value</a>" is for things that need to consume a single value,
Expand Down Expand Up @@ -2199,7 +2210,7 @@ Parse A Comma-Separated List According To A CSS Grammar</h4>
as described in the [=CSS/parse=] algorithm).

<div algorithm>
To <dfn export lt="parse a comma-separated list according to a CSS grammar|parse a list|parsing a list" for=CSS>parse a comma-separated list according to a CSS grammar</dfn>
To <dfn export lt="parse a comma-separated list according to a CSS grammar|parse a list" for=CSS>parse a comma-separated list according to a CSS grammar</dfn>
(aka [=CSS/parse a list=])
given an |input|
and a CSS |grammar| production:
Expand Down Expand Up @@ -2251,32 +2262,45 @@ Parse a stylesheet</h4>
(or null, if |location| was not passed).

<li>
<a>Consume a list of rules</a> from |input|,
with the <var ignore>top-level flag</var> set,
and set the stylesheet's value to the result.
<a>Consume a stylesheet's contents</a> from |input|,
and set the stylesheet's rules to the result.

<li>
Return the stylesheet.
</ol>
</div>

<h4 id="parse-list-of-rules">
Parse a list of rules</h4>
<h4 id="parse-stylesheet-contents" oldids="parse-list-of-rules">
Parse a stylesheet's contents</h4>

<div algorithm>
To <dfn export>parse a list of rules</dfn> from |input|:
To <dfn export>parse a stylesheet's contents</dfn> from |input|:

<ol>
<li>
[=Normalize=] |input|,
and set |input| to the result.

<li>
<a>Consume a list of rules</a> from the |input|,
with the <var ignore>top-level flag</var> unset.
<a>Consume a stylesheet's contents</a> from |input|,
and return the result.
</ol>
</div>

<h4 id="parse-block-contents" oldids="parse-list-of-declarations">
Parse a list of declarations</h4>

<div algorithm>
To <dfn export>parse a block's contents</dfn> from |input|:

<ol>
<li>
[=Normalize=] |input|,
and set |input| to the result.

<li>
Return the returned list.
<a>Consume a block's contents</a> from |input|,
and return the result.
</ol>
</div>

Expand Down Expand Up @@ -2402,7 +2426,7 @@ Parse a comma-separated list of component values</h4>

3. While |input| is not [=token stream/empty=]:

1 [=Consume a list of component values=] from |input|,
1. [=Consume a list of component values=] from |input|,
with <<comma-token>> as the stop token,
and append the result to |groups|.
2. [=Discard a token=] from |input|.
Expand Down Expand Up @@ -2435,12 +2459,11 @@ Parser Algorithms</h3>
to enable validity-checking.


<h4 id="consume-list-of-rules">
Consume a list of rules</h4>
<h4 id="consume-stylesheet-contents" oldids="consume-list-of-rules">
Consume a stylesheet's contents</h4>

To <dfn>consume a list of rules</dfn>
from a [=token stream=] |input|,
given an optional bool |top-level| (default false):
To <dfn>consume a stylesheet's contents</dfn>
from a [=token stream=] |input|:

Let |rules| be an initially empty [=list=] of rules.

Expand All @@ -2458,8 +2481,7 @@ Consume a list of rules</h4>
<dt><<CDO-token>>
<dt><<CDC-token>>
<dd>
If |top-level| is true,
[=discard a token=] from |input|.
[=Discard a token=] from |input|.

<details class=note>
<summary>What's this for?</summary>
Expand All @@ -2481,11 +2503,6 @@ Consume a list of rules</h4>
for the same reason.
</details>

Otherwise,
[=consume a qualified rule=] from |input|.
If anything is returned,
append it to |rules|.

<dt><<at-keyword-token>>
<dd>
<a>Consume an at-rule</a> from |input|.
Expand Down Expand Up @@ -3429,142 +3446,74 @@ Defining Grammars for Rules and Other Values</h2>
<h3 id='declaration-rule-list'>
Defining Block Contents: the <<declaration-list>>, <<rule-list>>, and <<stylesheet>> productions</h3>

The CSS parser is agnostic as to the contents of blocks,
such as those that come at the end of some at-rules.
Defining the generic grammar of the blocks in terms of tokens is non-trivial,
but there are dedicated and unambiguous algorithms defined for parsing this.

<div class=issue>
Need to rewrite these productions into a more useful hierarchy:

* allows declarations and at-rules (qualified rules are parsed, but invalid by default)
* allows declarations, qualified rules, and at-rules
* allows qualified rules and at-rules (declarations are parsed, but invalid by default)
</div>

The <dfn>&lt;declaration-list></dfn> production represents a list of declarations and/or rules.
It may only be used in grammars as the sole value in a block,
and represents that the contents of the block must be parsed using the <a>consume a list of declarations</a> algorithm.

Similarly, the <dfn>&lt;rule-list></dfn> production represents a list of rules,
and may only be used in grammars as the sole value in a block.
It represents that the contents of the block must be parsed using the <a>consume a list of rules</a> algorithm.

Finally, the <dfn>&lt;stylesheet></dfn> production represents a list of rules.
It is identical to <<rule-list>>,
except that blocks using it default to accepting all rules
that aren't otherwise limited to a particular context.

<div class=note>
These productions are pretty similar to each other,
so this table summarizes what they accept
and lists some example instances of each:

<table class=data>
<thead>
<tr>
<td>
<th>Allows [=declarations=]
<th>Allows [=nested style rules=]
<th>Allow arbitrary [=qualified rules=]
<th>Allows [=at-rules=]
<th>Examples
</thead>
<tr>
<th><<declaration-list>>
<td><td><td><td>
<td>''@font'', ''@counter-style'', ''@page'', ''@keyframes'' child rules
<tr>
<th><<rule-list>>
<td><td><td><td>
<td>''@keyframes'', ''@font-feature-values''
<tr>
<th><<stylesheet>>
<td><td><td><td>
<td>stylesheets, non-nested [=conditional group rules=]
</table>

If a given context is <em>only</em> intended to accept [=at-rules=],
such as in ''@font-features-values'',
it doesn't actually matter which production is used,
but <<rule-list>> is preferred for its more straightforward name.
The CSS parser is agnostic as to the contents of blocks--
they're all parsed with the same algorithm,
and differentiate themselves solely by what constructs are valid.

When writing a rule grammar,
<dfn>&lt;block-contents></dfn> represents this agnostic parsing.
It must only be used as the sole value in a block,
and represents that no restrictions are implicitly placed
on what the block can contain.

Accompanying prose must define what is valid and invalid in this context.
If any [=declarations=] are valid,
and are [=property declarations=],
it must define whether they interact with the cascade;
if they do, it must define their specificity
and how they use <code>!important</code>.

In many cases, however,
a block can't validly contain <em>any</em> constructs of a given type.
To represent these cases more explicitly,
the following productions may be used

* <dfn>&lt;declaration-list></dfn>:
[=at-rules=] and [=qualified rules=] are automatically invalid.
* <dfn>&lt;qualified-rule-list></dfn>:
[=declarations=] and [=at-rules=] are automatically invalid.
* <dfn>&lt;declaration-rule-list></dfn>:
[=at-rules=] are automatically invalid.
* <dfn>&lt;rule-list></dfn>:
[=declarations=] are automatically invalid.

All of these are exactly equivalent to <<block-contents>> in terms of parsing,
but the accompanying prose only has to define validity
for the categories that aren't automatically invalid.

<div class=example>
Some examples of the various productions:

* A top-level ''@media'' uses <<rule-list>> for its block,
while a nested one [[CSS-NESTING-1]] uses <<block-contents>>.
* [=Style rules=] use <<block-contents>>.
* ''@font-face'' uses <<declaration-list>>.
* ''@page'' uses <<declaration-rule-list>>.
* ''@keyframes'' uses <<rule-list>>
</div>

<div class='example'>
For example, the ''@font-face'' rule is defined to have an empty prelude,
and to contain a list of declarations.
This is expressed with the following grammar:

<pre>@font-face { <<declaration-list>> }</pre>

This is a complete and sufficient definition of the rule's grammar.
<div class=example>
For example, the grammar for ''@font-face'' can be written as:

For another example,
''@keyframes'' rules are more complex,
interpreting their prelude as a name and containing keyframes rules in their block
Their grammar is:
<pre><<@font-face>> = @font-face { <<declaration-list>> }</pre>

<pre>@keyframes <<keyframes-name>> { <<rule-list>> }</pre>
</div>
and then accompanying prose defines the valid [=descriptors=]
allowed in the block.

For rules that use <<style-block>> or <<declaration-list>>,
the spec for the rule must define which properties, descriptors, and/or at-rules are valid inside the rule;
this may be as simple as saying "The @foo rule accepts the properties/descriptors defined in this specification/section.",
and extension specs may simply say "The @foo rule additionally accepts the following properties/descriptors.".
Any declarations or at-rules found inside the block that are not defined as valid
must be removed from the rule's value.

Within a <<style-block>> or <<declaration-list>>,
<code>!important</code> is automatically invalid on any descriptors.
If the rule accepts properties,
the spec for the rule must define whether the properties interact with the cascade,
and with what specificity.
If they don't interact with the cascade,
properties containing <code>!important</code> are automatically invalid;
otherwise using <code>!important</code> is valid
and causes the declaration to be [=important=]
for the purposes of the [=cascade=].
See [[!CSS-CASCADE-3]].
The grammar for ''@keyframes'' can be written as:

<div class='example'>
For example, the grammar for ''@font-face'' in the previous example must,
in addition to what is written there,
define that the allowed declarations are the descriptors defined in the Fonts spec.
</div>

For rules that use <<rule-list>>,
the spec for the rule must define what types of rules are valid inside the rule,
same as <<declaration-list>>,
and unrecognized rules must similarly be removed from the rule's value.

<div class='example'>
For example, the grammar for ''@keyframes'' in the previous example must,
in addition to what is written there,
define that the only allowed rules are <<keyframe-rule>>s,
which are defined as:

<pre><<keyframe-rule>> = <<keyframe-selector>> { <<declaration-list>> }</pre>
<pre>
<<@keyframes>> = @keyframes { <<rule-list>> }
<<keyframe-rule>> = <<keyframe-selector>> { <<declaration-list>> }
</pre>

Keyframe rules, then,
must further define that they accept as declarations all animatable CSS properties,
and then accompanying prose defines that only <<keyframe-rule>>s
are allowed in ''@keyframes'',
and that <<keyframe-rule>>s accept all animatable CSS properties,
plus the 'animation-timing-function' property,
but that they do not interact with the cascade.
but they do not interact with the cascade.
</div>

For rules that use <<stylesheet>>,
all rules are allowed by default,
but the spec for the rule may define what types of rules are <em>invalid</em> inside the rule.

<div class='example'>
For example, the ''@media'' rule accepts anything that can be placed in a stylesheet,
except more ''@media'' rules.
As such, its grammar is:

<pre>@media <<media-query-list>> { <<stylesheet>> }</pre>

It additionally defines a restriction that the <<stylesheet>> can not contain ''@media'' rules,
which causes them to be dropped from the outer rule's value if they appear.
</div>

<h3 id="any-value">
Defining Arbitrary Contents: the <<declaration-value>> and <<any-value>> productions</h3>
Expand Down

0 comments on commit c7ce041

Please sign in to comment.