.
VDS (value definition syntax)
В общих чертах: в статье описаны причины создания и принципы грамматики синтаксиса, который используется в спецификациях css для описания типа значения свойства, селекторов и вообще почти везде. Понимание этого синтаксиса значительно облегчает чтение спецификации :)
Немного формальной грамматики
Простой пример
Попробуем описать грамматику отрицательных чисел от -1 до -9 в такой форме:
<neg_num> ::= "-" <num>
<num> ::= "1" | "2" | "3" | "4" | "5" |
| "6" | "7" | "8" | "9"
Обозначим отрицательное число токеном <neg_num>
и определим его как символ -
,
сразу за которым следует цифра <num>
. Ниже раскроем токен цифры как один символ из
данного
набора: 1
, или 2
, или 3
...
В этом же синтаксисе будет так же удобно записать грамматику сложения отрицательных чисел, указав что это
одно
отрицательно число +
второе отрицательно число в скобках.
<add_expr> ::= <neg_num> "+" "(" <neg_num> ")"
В этих записях мы видим некоторые символы, записанные в кавычках, которые попадают в конечное выражение без изменений. И что-то вроде переменных, которые с помощь заданных правил можно на эти символы заменить. Так вот, символы называют терминалы, а "что-то вроде переменных" - нетерминалы. Более формальное определение:
Терминал — объект, непосредственно присутствующий в словах языка, соответствующего грамматике, и имеющий конкретное, неизменяемое значение. Нетерминал — объект, обозначающий какую-либо сущность языка и не имеющий конкретного символьного значения.
Форма Бекуса-Наура
Форма записи, которую мы использовали, называется Форма Бэкуса-Наура. Это формальная система описания синтаксиса, в которой одни синтаксические категории последовательно определяются через другие.
Запись синтаксического правила разбита на две части символом определения ::=
или иногда вместо
него
используют ->. В левой части содержится определяемый нетерминал, а справа последовательность из
терминалов и
нетерминалов. В описании правила в правой части может использоваться оператор “|”, обеспечивающий логическое
или
. В описании правила может применяться рекурсия. В общем:
"text" или 'text' | терминал |
<A> |
нетерминал |
<A> ::= <B> |
<A> заменимо на <B> |
<A> <B> |
конкатенация |
<A> | <B> |
один из элементов, “или” |
integer |
имя класса нетерминалов, описывается не через BNF |
Зная это, попробуем описать грамматику арифметических операций над натуральными положительными числами без скобок:
<expression> ::= <add_expr> | <mul_expr>
<add_expr> ::= <mul_expr> '+' <add_expr>
| <add_expr> '+' <mul_expr>
| <mul_expr> '-' <add_expr>
| <add_expr> '-' <mul_expr>
<mul_expr> ::= integer '*' <mul_expr>
| integer '/' <mul_expr>
| <mul_expr> '*' integer
| <mul_expr> '/' integer
| integer
integer ::= [0-9]+
Поясним:
<expression>
- математическое выражение, содержит в себе сложение(вычитание)<add_expr>
или умножение(деление)<mul_expr>
<add_expr>
сложение предтавлется собой прибавление или вычитание<mul_expr>
и<add_expr>
друг из друга в любом порядке<mul_expr>
умножение представлюет собой умножение или деление<mul_expr>
на число или наоборот, так же оно может быть просто числом. Это обеспечивает участие чисел в сложении.
Расширенная форма Бекуса-Наура
Можно заметить что запись грамматики выше неоптимальна. Много повторений, которые можно было бы записать короче и понятнее. Введем для этого дополнительные правила.
[<A>] |
опциональное вхождение |
(<A> | <B>) <C> |
группировка |
{<A>} |
повторение элемента (0 или более раз) |
Совокупность правил в этой таблице и БНФ называется расширенной формой Бекуса-Наура. Используем ее чтобы записать грамматику арифметикии компактнее:
<expression> ::= <add_expr> | <mul_expr>
<add_expr> ::= (<mul_expr> | <add_expr>) ('+' | '-') (<mul_expr> | <add_expr>)
<mul_expr> ::= (integer | <mul_expr>) { ('*' | '/') (integer | <mul_expr>) }
integer ::= [0-9]+
Ближе к CSS
Обладая таким удобным инструментом можно записать, например, базовую грамматику CSS:
<style-rule> ::=
<selectors-list> '{' <properties-list> '}'
<selectors-list> ::=
<selector>[':' <pseudo-class>] ['::' <pseudo-element>]
[',' <selectors-list>]
<properties-list> ::=
[<property> ':' <value>] [';' <properties-list>]
Проверим что эта запись соответствует дейвительности. Разберем примеры использования нескольких токенов.
Токен
<style-rule>
, правило стилей, - это список селекторов<selectors-list>
и набор деклаций свойств в фигурных скобках:<style-rule> ::= <selectors-list> '{' <properties-list> '}' .class#id { height: initial; width: 100%; }
Токен
<properties-list>
, список свойств, - это рекурсивынй токен. Представляет собой список деклараций: имz и набор деклаций свойств в фигурных скобках:<properties-list> ::= [<property> ':' <value>] [';' <properties-list>] .class#id {} .class#id { height: initial }
Попробуем описать грамматику свойства margin
. Его значение это 4 токена
<length>
,
или ключевое слово auto
, или одно из трех CSS-wide keywords : initial
,
unset
, inherit
, которые мы не будет включать в запись, поскольку непосредственно к
свойству они не относятся. Например:
margin: 0;
margin: 0 -7px;
margin: 0 -7px 0;
margin: 0 -7px 0 2px;
margin: auto;
То есть, используя БНФ:
<margin> ::= "auto"
| <length>
| <length> {" "} <length>
| <length> {" "} <length> {" "} <length>
| <length> {" "} <length> {" "} <length> {" "} <length>
Выглядит громозко.
Синтаксис определения значения
По этим и некоторым другим причинам в 1998 году (примерно) на основе расшренной БНФ был создан Value definition syntax ( агнл. синтаксис определения значения, в дальнейшем VDS).
В нем определены следующие сущности и грамматики:
initial |
ключевые слова |
<length> |
простые типы данных |
<'line-height'> |
сложные типы данных (именуются обычно по свойству) |
[<A> | <B>] <C> |
группировка |
<A> || <B> || <C> |
один или более из группы в заданном порядке |
<A> && <B> && <C> |
один или более из группы в любом порядке |
<A>* |
ноль или более раз |
<A>+ |
один или более раз |
<A>? |
необязательный |
<A> {n} |
А повторяется n раз |
<A> {n, m} |
А повторяется не меньше n раз и не больше m раз |
<A># |
А повторяется один или более раз, разделенных запятыми |
В последней версии спецификации есть еще скобочная нотация диапазона (bracketed range notation). Для свойств
числового
типа с использованием обозначения диапазона она записывается в квадратных скобках [min, max]
сразу
после ключевого слова идентификации, что указывает на замкнутый диапазон между (включительно)
min
и
max
. Например, <integer [0,10]>
указывает целое число от 0 до 10
включительно.
Проверим что он удобен для описания CSS на примере margin
:
<'margin'> ::= "auto" | <length> {1, 4}
Удобно, ёмко, понятно, красиво 💅 ✨
Свойства посложенее тоже:
-
Свойство толщины границы
border-width
:border-width: 3px; border-width: thick; border-width: medium thick; border-width: 8em thick;
Запишем его грамматику:
<'border-width'> = [ <length> | thick | medium | thin ]{1,4}
-
Свойство текстовой тени
text-shadow
:text-shadow: none; text-shadow: 1px 1px 2px black; text-shadow: inset 1px 1px 2px black; text-shadow: inset 1px 1px 2px black, 0 0 1em red, 0 3vw green;
Запишем его грамматику:
<'text-shadow'> = [ inset? [ <length>{2,4} <color>? ]# ] | none
Источники
- Спецификация css-values-4, раздел value definition syntax