14 KiB
Syntax
Table of Contents
Definitions
Source File
A source file is a Sass abstract syntax tree along with an absolute URL, known as that file's canonical URL; and an importer. A given canonical URL cannot be associated with more than one source file.
Vendor Prefix
Some identifiers have a vendor prefix, which is an initial substring beginning with U+002D HYPHEN-MINUS code point followed by one or more non-U+002D code points followed by another U+002D. An identifier only has a vendor prefix if the final U+002D is followed by additional text. This additional text is referred to as the unprefixed identifier.
Syntax
Unless otherwise specified, if a grammar production's value is parsed as a single other named Sass production, then the AST does not contain a representation of the intermediate production.
For example, suppose we have the productions:
Foo ::= Bar '*'? Bar ::= <ident-token>Parsing
"ident*"
creates an AST that contains aFoo
that contains aBar
. But parsing"ident"
alone creates an AST that only contains aBar
—theFoo
wrapper is removed. (TheBar
wrapper is not removed because<ident-token>
is a CSS production, not a Sass production.)
InterpolatedIdentifier
InterpolatedIdentifier ::= (<ident-token> | '-'? Interpolation) (Name | Interpolation)*
No whitespace is allowed between components of an InterpolatedIdentifier
.
InterpolatedUrl
InterpolatedUrl ::= 'url(' (QuotedString | InterpolatedUnquotedUrlContents) ')' InterpolatedUnquotedUrlContents ::= (unescaped url contents | escape | Interpolation)*
No whitespace is allowed between components of an InterpolatedUnquotedUrlContents
.
Name
Name ::= (identifier code point | escape)+
SpecialFunctionExpression
These functions are "special" in the sense that their arguments don't use the normal CSS expression-level syntax, and so have to be parsed more broadly than a normal SassScript expression.
SpecialFunctionExpression ::= SpecialFunctionName InterpolatedDeclarationValue ')' SpecialFunctionName¹ ::= VendorPrefix? ('element(' | 'expression(') | VendorPrefix 'calc(' VendorPrefix¹ ::= '-' (identifier-start code point | digit) '-'
1: Both SpecialFunctionName
and VendorPrefix
are matched case-insensitively,
and neither may contain whitespace.
PseudoSelector
PseudoSelector ::= NormalPseudoSelector | SelectorPseudo | NthSelectorPseudo NormalPseudoSelector ::= ':' ':'? VendorPrefix? <ident-token> ('(' <declaration-value> ')')? SelectorPseudo ::= SelectorPseudoName '(' Selector ')' NthSelectorPseudo ::= NthSelectorPseudoName '(' <an+b> 'of'¹ Selector ')' SelectorPseudoName ::= ':' ('not' | 'matches' | 'any' | 'current' | 'has' | 'host' | 'host-context') | '::slotted' NthSelectorPseudoName ::= ':' ('nth-child' | 'nth-last-child')
1: The string of
is matched case-insensitively. In addition, it must be parsed
as an identifier.
In other words, it must have whitespace separating it from other identifiers, so
:nth-child(2nof a)
and:nth-child(2n ofa)
are both invalid. However,:nth-child(2of.foo)
is valid.
If a PseudoSelector
begins withSelectorPseudoName
or NthSelectorPseudoName
followed by a parenthesis, it must be parsed as a SelectorPseudo
or an
NthSelectorPseudo
respectively, not as a NormalPseudoSelector
.
No whitespace is allowed anywhere in a PseudoSelector
except within
parentheses.
ProductExpression
ProductExpression ::= (ProductExpression ('*' | '%'))? UnaryPlusExpression
Procedures
Parsing Text
This algorithm takes a string text
and a syntax syntax
("indented", "scss",
or "sass"), and returns a Sass abstract syntax tree.
-
If
syntax
is "indented", return the result of parsingtext
as the indented syntax. -
If
syntax
is "css", return the result of parsingtext
as CSS. -
If
syntax
is "scss", return the result of parsingtext
as SCSS.
Parsing Text as CSS
This algorithm takes a string, text
, and returns a Sass abstract syntax tree.
This algorithm is designed with two goals in mind:
CSS imported from Sass should be as compatible with standard CSS as possible. In some cases we err even more towards CSS compatibility than SCSS does, because the CSS being imported is likely not written by someone who knows to avoid things that Sass interprets specially (such as certain
@import
URLs).We should provide clear and eager feedback to users who accidentally try to use Sass features in CSS imports. We don't allow these features, and we want users to know that through error messages rather than digging through generated CSS only to find that Sass features were passed through unmodified. This is a particular concern because LibSass has historically allowed the use of Sass features in CSS imports.
The algorithm for parsing text as CSS works like parsing text as SCSS, with some modifications. The following productions should produce errors:
-
Any at-rules that are defined in Sass and not in plain CSS. At the time of writing, this means:
@at-root
@content
@debug
@each
@error
@extend
@for
@forward
@function
@if
@include
@mixin
@return
@use
@warn
@while
-
An
@import
that contains interpolation in theurl()
or any of itsImportModifier
s. -
An
@import
that appears within a style rule or at-rule. -
An
@import
with more than one argument. -
A declaration followed by an open curly brace (that is, a nested declaration).
-
The parent selector
&
in a declaration value. -
A style rule whose selector contains a trailing combinator.
-
Placeholder selectors.
-
All built-in functions, excluding the following:
rgb()
rgba()
hsl()
hsla()
grayscale()
invert()
alpha()
opacity()
Note that user-defined functions are not forbidden, whether they're defined using
@function
or through a host language API. -
Any function called with keyword arguments or variable-length arguments.
-
Interpolation anywhere its contents would be evaluated. At the time of writing, this means:
- At-rule values (including
@media
queries) - Declaration names
- Declaration values
- Style rule selectors
- At-rule values (including
-
All SassScript operations except for:
not
or
and
Note that although unary
-
is forbidden, the-
that appears at the beginning of a number literal is part of that literal and thus allowed. -
Parentheses in declaration values that aren't part of a CSS production.
-
Map literals.
-
The empty list literal
(,)
. -
Uses or declarations of Sass variables.
-
//
-style ("silent") comments outside of an expression context.
In addition, some productions should be parsed differently than they would be in SCSS:
-
All functions that don't produce errors should be parsed as plain CSS functions, regardless of whether a Sass function with that name is defined.
-
All
@import
s that don't produce errors should be parsed as static CSS imports. -
The tokens
not
,or
,and
, andnull
should be parsed as unquoted strings. -
In an expression context,
//
is not parsed as a silent comment. Instead, two adjacent/
s in aSlashListExpression
may have no whitespace between them, so//
is parsed as two slash separators in a slash-separated list. -
A
ParentSelector
may appear anywhere in aCompoundSelector
, rather than just as the firstSimpleSelector
. -
A
ParentSelector
may not have asuffix
.
Consuming an Identifier
This algorithm consumes input from a stream of code points and returns a string.
This production has the same grammar as <ident-token>
.
-
Let
string
be an empty string. -
If the stream starts with
--
, consume it and append it tostring
. -
Otherwise:
-
If the stream starts with
-
, consume it and append it tostring
. -
If the stream starts with
\
, consume an escaped code point with thestart
flag set and append it tostring
. -
Otherwise, if the stream starts with an identifier-start code point, consume it and append it to
string
. -
Otherwise, throw an error.
-
-
Consume a name and append it to
string
. -
Return
string
.
Consuming an Interpolated Identifier
This algorithm consumes input from a stream of code points and returns a
sequence of strings and/or expressions. It follows the grammar for an
InterpolatedIdentifier
.
-
Let
components
be an empty list of strings and/or expressions. -
If the input starts with
-#{
, consume a single code point and add"-"
tocomponents
. -
If the input starts with
#{
, consume an interpolation and add its expression tocomponents
. -
Otherwise, consume an identifier and add its string to
components
. -
While the input starts with
#{
, a identifier code point, or\
:-
If the input starts with
#{
, consume an interpolation and add its expression tocomponents
. -
Otherwise, consume a name and add its string to
components
.
-
-
Return
components
.
Consuming a Name
This algorithm consumes input from a stream of code points and returns a string. The grammar for this production is:
Name ::= (identifier code point | escape)+
-
Let
string
be an empty string. -
While the input starts with a identifier code point or
\
:-
If the input starts with a identifier code point, consume it and append it to
string
. -
Otherwise, consume an escaped code point and append it to
string
.
-
-
Return
string
.
Consuming an Escaped Code Point
This algorithm consumes input from a stream of code points. It takes an
optional boolean flag, start
, which indicates whether it's at the beginning of
an identifier and defaults to false. It returns a string.
This production has the same grammar as escape
in CSS Syntax Level 3.
-
If the stream doesn't start with a valid escape, throw an error.
-
Let
codepoint
be the result of consuming an escaped code point. -
Let
character
be the string containing onlycodepoint
. -
If
codepoint
is a identifier-start code point, returncharacter
. -
Otherwise, if
codepoint
is an identifier code point and thestart
flag is not set, returncharacter
. -
Otherwise, if
codepoint
is a non-printable code point, U+0009 CHARACTER TABULATION, U+000A LINE FEED, U+000D CARRIAGE RETURN, or U+000C FORM FEED; or ifcodepoint
is a digit and thestart
flag is set:-
Let
code
be the lowercase hexadecimal representation ofcodepoint
, with no leading0
s. -
Return
"\"
+code
+" "
.
Tab characters are parsed as explicit escapes in order to support a browser hack that targets IE 10 and earlier, wherein ending a declaration value with
\9
would cause IE to interpret it as valid but other browsers to ignore it. -
-
Otherwise, return
"\"
+character
.
Consuming a special function
This algorithm consumes input from a stream of code points and returns a SassScript expression.
-
Let
expression
be the result of consuming aSpecialFunctionExpression
. -
Return an unquoted interpolated string expression that would be identical to the source text according to CSS semantics for all possible interpolated strings.