Контекстно-свободная грамматика состоит из ряда порождающих правил productions. В левой части каждого правила имеется абстрактный символ – нетерминал, а в правой части – последовательность нетерминальных и терминальных символов в количестве ноль или более. Для каждой грамматики терминальные символы берутся из определенного предписанного алфавита.
Начиная с предложения, состоящего из одиночного помеченного нетерминала – начального символа, данная КС грамматика определяет язык, а именно – ряд (возможно, бесконечный) допустимых последовательностей терминальных символов, которые могут получиться в результате неоднократной замены какого-либо нетерминала в этой последовательности на символы, указанные в правой стороне правила, для которого этот нетерминал является левой частью.
Лексическая грамматика для ECMAScript приведена в Главе 7. В качестве терминальных символов в этой грамматике используются символы Юникода, подчиняющиеся правилам для символа SourceCharacter Исходный символ, изложенным в Главе 6. Эта грамматика определяет набор правил, начиная с начального символа InputElementDiv Входной элемент деления или InputElementRegExp Входной элемент регулярного выражения, описывающих преобразование последовательностей указанных символов в последовательность входных элементов.
В синтаксической грамматике для ECMAScript терминальными символами являются входные элементы, отличные от пробельных символов и комментариев. Они называются токенами ECMAScript. К ним относятся зарезервированные (ключевые) слова, идентификаторы, литералы и знаки пунктуации языка ECMAScript. Кроме того, символы окончания строки, хоть и не считаются токенами, также становятся частью потока входных элементов и руководят процессом автоматической вставки точки с запятой (См. пункт 7.9). Простые пробелы и однострочные комментарии игнорируются и не появляются в потоке входных элементов синтаксической грамматики. Символ MultiLineComment Многострочный комментерий – то есть, комментарий в виде “/*
…*/
”, независимо от того, занимает ли он одну или несколько строк – также просто игнорируется, если не содержит символа окончания строки. Если же он содержит один или несколько символов окончания строки, он заменяется единичным символом окончания строки, который становится частью потока входных элементов синтаксической грамматики.
Грамматика регулярных выражений (от английского RegExp grammar, то есть "Regular Expressions grammar" – прим. перев.) для ECMAScript приведена в Главе 15.10. Символы, которые используются в этой грамматике в качестве терминальных, также подчиняются правилам для символа SourceCharacter. Она определяет набор правил, начиная с начального символа Pattern Шаблон, описывающих преобразование последовательностей символов в шаблоны регулярных выражений.
В качестве разделительного знака в правилах лексической грамматики и грамматики регулярных выражений используется двойное двоеточие “::”. Некоторые правила являются общими для этих грамматик.
Еще одна грамматика используется для преобразования строки в числовое значение. Она схожа с лексической грамматикой в той части, которая касается числовых литералов. В качестве терминального символа используется. Описание этой грамматики приводится в пункте 9.3.1.
В качестве разделительного знака строково-числовой грамматики используется тройное двоеточие “:::”.
Синтаксическая грамматика для ECMAScript описана в разделах 11, 12, 13 и 14. Терминальными символами в данной грамматике являются токены ECMAScript, определенные в лексической грамматике (см. пункт 5.1.2). Синтаксическая грамматика определяет набор правил, начиная с начального символа Program Программа, описывающих, как последовательности токенов могут образовывать синтаксически корректные программы на ECMAScript.
При распознавании потока символов в качестве программы на ECMAScript сначала этот поток преобразуется в поток входных элементов посредством неоднократного применения лексической грамматики, а затем производится распознавание этого потока входных элементов посредством однократного применения синтаксической грамматики. Считается, что программа имеет синтаксическую ошибку, если токены в этом потоке входных элементов не могут быть распознаны как единичный экземпляр начального нетерминала Program так, чтобы при этом не оставалось лишних токенов.
В качестве разделительного знака в правилах синтаксической грамматики используется одиночное двоеточие “:”.
Вообще-то, описание синтаксической грамматики, приведенное в пунктах 11, 12, 13 и 14, не полностью определяет, какие последовательности токенов являются допустимыми в качестве корректных программ на ECMAScript. Допускаются также еще некоторые дополнительные последовательности токенов, а именно – те, которые бы описывала эта грамматика, если бы к этим последовательностям добавлялись синтаксические знаки "точка с запятой" в определенных местах – например, перед символами окончания строки. Кроме того, некоторые описываемые последовательности токенов считаются недопустимыми, если символ окончания строки встречается в определенных "неудобных" местах.
Грамматика JSON используется для преобразования строки, описывающей набор объектов ECMAScript, в фактические объекты. Грамматика JSON приведена в пункте 15.12.1.
Грамматика JSON состоит из лексической грамматики JSON и синтаксической грамматики JSON. Лексическая грамматика JSON используется для преобразования последовательностей символов в токены. В некоторых частях она аналогична лексической грамматике ECMAScript. Синтаксическая грамматика JSON определяет способ преобразования последовательностей токенов из лексической грамматики JSON в синтаксически правильные описания объекта JSON.
В качестве разделительного знака в правилах лексической грамматики JSON используется двойное двоеточие “::”. В лексической грамматике JSON используются некоторые правила из лексической грамматики ECMAScript. В некоторых частях синтаксическая грамматика JSON аналогична синтаксической грамматике ECMAScript. В качестве разделительного знака в правилах синтаксической грамматики JSON используется одиночное двоеточие “:”.
Терминальные символы лексической и строковой грамматики, а также некоторые терминальные символы синтаксической грамматики обозначаются моноширинным
шрифтом и в правилах грамматики, и в тексте данной спецификации, если речь идет непосредственно о таком терминальном символе. В программе эти символы должны быть указаны в точности так, как они пишутся. Все терминальные символы, обозначенные таким образом, должны пониматься как соответствующие юникодные ASCII-символы, и не должны толковаться как другие символы Юникода, аналогичные по написанию.
Для обозначения нетерминальных символов используется курсив. При определении нетерминала сначала указывается имя определяемого нетерминала, после которого следует одно или несколько двоеточий – в зависимости от типа грамматики, к которой относится данное правило. Затем на следующих строках содержится один или несколько вариантов правой части правила для этого нетерминала. Например, в приведенном ниже синтаксическом определении
WhileStatement :
while
(
Выражение )
Инструкция
указано, что нетерминал WhileStatement Инструкция While представляет токен while
, за которым следует токен открывающейся скобки, затем Expression Выражение, а затем токен закрывающейся скобки, за которым следует Statement Инструкция. Экземпляры Expression и Statement сами являются нетерминалами. Приведем еще один пример синтаксического определения:
ArgumentList:
AssignmentExpression
ArgumentList,
AssignmentExpression
в котором утверждается, что ArgumentList Список аргументов может представлять либо единичное AssignmentExpression Выражение присваивания, либо ArgumentList, за которым следует запятая, а за ней – AssignmentExpression. Данное определение нетерминала ArgumentList является рекурсивным, то есть – на основе самого себя. В результате ArgumentList может содержать любое положительное число аргументов, разделенных запятыми, где каждое выражение аргумента представляет собой AssignmentExpression. Такие рекурсивные определения нетерминалов используются достаточно часто.
Если после терминального или нетерминального символа стоит подстрочный суффикс “opt” (от английского "optional" – прим. перев.), он означает, что этот символ является необязательным. Вариант, содержащий необязательный символ, в действительности определяет две правые части: одну с этим обязательным элементом и одну – без него. Это означает, что
VariableDeclaration:
Identifier Initialiseropt
является удобной сокращенной формой следующего представления:
VariableDeclaration:
Identifier
Identifier Initialiser
а также, что
IterationStatement:
for (
ExpressionNoInopt;
Expressionopt;
Expressionopt)
Statement
является удобной сокращенной формой для следующей записи:
ИнструкцияИтерации:
for ( ;
Expressionopt;
Expressionopt)
Statement
ExpressionNoIn
for (;
Expressionopt;
Expressionopt)
Statement
которая, в свою очередь, является сокращением для следующей записи:
IterationStatement:
for ( ; ;
Expressionopt)
Statement
Expression
for ( ;;
Expressionopt)
Statement
ExpressionNoIn
for (; ;
Expressionopt)
Statement
ExpressionNoIn
for (;
Expression;
Expressionopt)
Statement
которая, в свою очередь, является сокращением для следующего:
IterationStatement:
for ( ; ; )
Statementfor ( ; ;
Expression)
Statement
Expression
for ( ;; )
Statement
Expression
for ( ;;
Expression)
Statement
ExpressionNoIn
for (; ; )
Statement
ExpressionNoIn
for (; ;
Expression)
Statement
ExpressionNoIn
for (;
Expression; )
Statement
ExpressionNoIn
for (;
Expression;
Expression)
Statement
и, таким образом, нетерминал ИнструкцияИтерации в действительности имеет восемь вариантов правой части.
Если в правой части правила содержится фраза "[empty]" [пусто], это означает, что в правой части правила отсутствуют терминалы или нетерминалы.
Если в правой части правила содержится фраза “[lookahead ∉ set], [предпросмотр ∉ множество], это означает, что данное правило не может быть использовано, если непосредственно следующий за ним входной токен является элементом этого set. Этот set может быть описан в виде списка терминалов, взятого в фигурные скобки. В целях удобства это множества также может быть описано в виде нетерминала, и в этом случае оно представляет собой множество всех терминалов, на которые можно разложить этот нетерминал. Например, с учетом следующих определений
DecimalDigit ::one of
0 1 2 3 4 5 6 7 8 9
DecimalDigits::
DecimalDigit
DecimalDigits DecimalDigit
определение
LookaheadExample::
n
[lookahead ∉ {1
, 3
, 5
, 7
, 9
}]DecimalDigits
DecimalDigit [lookahead ∉ DecimalDigit ]
соответствует либо букве n
, после которой следует одна или несколько десятичных цифр, первая из которых является четной, либо соответствует десятичной цифре, после которой не следует какой-либо другой десятичной цифры.
Если в правой части правила синтаксической грамматики содержится фраза “[no LineTerminator here]” [здесь Конец строки отсутствует], это указывает на то, что данное правило является ограниченным порождением, или ограниченным правилом (англ. "restricted production" – прим. перев.): оно не может быть использовано, если во входном потоке в указанной позиции встречается LineTerminator Конец строки. Например, приведенное ниже правило
ReturnStatement:
return
[no LineTerminator here] Expressionopt
;
указывает на то, что данное правило не может быть использовано, если в программе между токеном return
и Expression стоит LineTerminator.
Если ограниченное правило не запрещает присутствие символа LineTerminator то между двумя последовательными токенами в потоке входных элементов может находиться любое количество символов LineTerminator, не затрагивая синтаксической корректности этой программы.
Если после единичного или многократного двоеточия в грамматическом определении следует фраза “one of” один из это значит, что каждый из терминальных символов, расположенных на следующей строке или строках, является одним из вариантов определения. Например, лексическая грамматика ECMAScript содержит следующее правило:
NonZeroDigit ::one of
1 2 3 4 5 6 7 8 9
что, по сути, является удобной сокращенной формой для следующей записи:
NonZeroDigit ::
1
2
3
4
5
6
7
8
9
Если альтернативный вариант в правиле лексической грамматики или строково-числовой грамматики является многознаковым токеном, он представляет собой последовательность символов, которые могли бы образовать такой токен.
Правая сторона правила может содержать указание на то, что некоторые варианты развертывания недопустимы. Для этого используется фраза “but not” но не, а после нее – развертывания, которые следует исключить. Например, приведенное ниже правило
Identifier::
IdentifierName but not ReservedWord
означает, что нетерминал Identifier Идентификатор может быть заменен любой последовательностью символов, которые могут заменить IdentifierName Имя идентификатора, при условии, что эта последовательность символов не может заменить ReservedWord Зарезервированное слово.
И, наконец, если перечислить все возможные варианты нереально, некоторые нетерминальные символы передаются описательными фразами, изложенными рубленым шрифтом:
SourceCharacter ::
любой символ Юникода
В данной спецификации для обозначения шагов алгоритма часто используются нумерованные списки. Эти алгоритмы используются для точного указания обязательной семантики языковых конструкций ECMAScript. Данные алгоритмы не подразумевают использование какой-либо определенной техники реализации. На деле, вполне возможно наличие более эффективных алгоритмов для реализации тех или иных функциональных возможностей.
Некоторые алгоритмы, которые называются абстрактные операции, именуются и записываются в параметризованной функциональной форме так, чтобы на них можно было легко ссылаться по имени из любого другого алгоритма. Это предусмотрено для того, чтобы сделать более удобным использование этих алгоритмов в различных частях данной спецификации.
Если в результате выполнения алгоритма должно получиться значение, используется директива “return x”, вернуть x, указывающая на то, что результатом этого алгоритма является значение x, и что выполнение этого алгоритма должно прекратиться. Запись "Result(n)" Результат(n) является сокращенным вариантом фразы “результат шага n”.
Для более понятного изложения шаги алгоритма могут быть поделены на дальнейшие подшаги. Подшаги, в свою очередь, тоже могут подразделяться на следующие подшаги, оформленные с отступом. Для обозначения подшагов используются правила нумерации: для нумерации подшагов первого уровня используют знаки алфавита в нижнем регистре, а для нумерации подшагов второго уровня используют римские цифры в нижнем регистре. Если для отображения алгоритма требуется более трех уровней, эти правила нумерации повторяются, начиная с цифрового обозначения на четвертом уровне. Например:
Шаг верхнего уровня
Подшаг
Подшаг
Подподшаг
Подподшаг
Подподподшаг
Подподподподшаг
Шаг или подшаг может быть записан как предикат "if" если, определяющий свои подшаги, тогда переход на уровень подшагов будет возможен только в том случае, если условие в предикате является истинным. Шаг, или подшаг, начинающийся со слова "else" иначе, является предикатом, отрицающим предшествующий шаг-предикат "if" того же уровня.
Шаг может задавать итеративное применение своих подшагов.
Математические операции, такие как сложение, вычитание, отрицание, умножение, деление, а также математические функции, определение которым будет дано далее в этом пункте, следует всегда понимать как получение точных математических результатов вычислений, производимых с действительными математическими числами, к которым не относятся бесконечности и отрицательный ноль (отличный от положительного нуля). Алгоритмы в данном стандарте, по которым строятся арифметические действия с плавающей запятой, в случае необходимости включают явные шаги для работы с бесконечностями, с нулем со знаком, а также для выполнения округления. Если к числу с плавающей запятой применяется математическая операция или функция, следует понимать, что эта операция или функция применяются к точному математическому значению, представленному этим числом с плавающей запятой. При этом такое число должно быть конечным; если это +0 или −0, то соответствующее математическое значение принимается как просто 0.
Математическая функция abs(x) возвращает абсолютное значение (модуль) x, которое равно −x, если x отрицателен (меньше нуля); в противном случае оно равно собственно x.
Математическая функция sign(x) возвращает 1 , если x положительный, и −1, если x отрицательный. В данном стандарте знаковая функция не используется в тех случаях, если x равен нулю.
Запись “x modulo y” (y должен быть конечным и отличным от нуля) вычисляет значение k с таким же знаком, что и y (или ноль) так, чтобы abs(k) < abs(y) и x−k = q × y для некоторого целого q.
Математическая функция floor(x) возвращает наибольшее целое (ближайшее к положительной бесконечности), не превышающее x.
ПРИМЕЧАНИЕ floor(x) = x−(x modulo 1).
Если по определению алгоритм должен "сгенерировать исключение", выполнение алгоритма прекращается, при этом результат не возвращается. Кроме того, прекращается выполнение вызвавших его алгоритмов до момента достижения того шага, который явно обрабатывает это исключение, используя терминологию типа "Если было сгенерировано исключение..." Сразу же по достижении такого шага алгоритма считается, что исключение не имело места.