Этот раздел описывает контекстно-свободные грамматики, использованные в этой спецификации для определения лексической и синтаксической структуры программы на ECMAScript.
Контекстно-свободная грамматика состоит из набора правил. Каждое правило состоит из абстрактного символа, называемого нетерминалом, в качестве левой части и последовательности из нуля или более нетерминальных или терминальных символов в качестве правой части. Для каждой грамматики терминальные символы берутся из указанного алфавита.
Начиная с предложения, состоящего из одиночного специального нетерминала, называемого начальным символом, данная контекстно-свободная грамматика описывает язык, а именно, набор (возможно, бесконечный) допустимых последовательностей терминальных символов, которые могут быть порождены в результате рекурсивной замены любого нетерминала в последовательности на правую сторону правила, для которого этот терминал является левой стороной.
Лексическая грамматика для ECMAScript приведена в разделе 7. Терминальными в этой грамматике являются символы Юникода. Она содержит набор правил, начинающийся с начального символа ВходнойЭлементДеления или ВходнойЭлементРегВыр, определяющий способ преобразования последовательности символов Юникода в последовательность входных элементов.
Входные элементы, отличные от пробелов и комментариев, образуют терминалы синтаксической грамматики ECMAScript и называются токенами ECMAScript. К токенам относятся ключевые слова, идентификаторы, литералы и знаки пунктуации языка ECMAScript. Более того, символы окончания строк, хотя и не считаются токенами, тоже становятся частью потока входных элементов и руководят процессом автоматической подстановки точек с запятой (7.9). Простые пробелы и однострочные комментарии игнорируются и не появляются в потоке входных элементов для синтаксической грамматики. МногострочныйКомментарий (то есть комментарий вида "/* --- */", вне зависимости от того, занимает ли он в действительности одну или несколько строк) аналогичным образом игнорируется, если в нём не содержится переводов строк; однако если МногострочныйКомментарий содержит один или более символов окончания строки, то он заменяется на единственный перевод строки, который становится частью потока входных элементов синтаксической грамматики.
РегВыр-грамматика для ECMAScript приведена в разделе 15.10. Символы Юникода являются терминальными также и в этой грамматике. Она содержит набор правил, начинающийся с начального символа Шаблон, определяющий способ преобразования последовательностей символов Юникода в шаблоны регулярных выражений.
Правила лексических и РегВыр-грамматик описываются с использованием двойного двоеточия ("::") в качестве разделительного знака. Некоторые правила являются общими для лексических и РегВыр-грамматик.
Следующая грамматика используется для перевода строк в числовые значения. Она является аналогичной той части лексической грамматики, которая описывает численные литералы, и её терминалами являются символы Юникода. Эта грамматика описывается в разделе 9.3.1.
Правила численной строковой грамматики описываются с использованием тройного двоеточия (":::") в качестве разделительного знака.
Синтаксическая грамматика для ECMAScript приведена в разделах 11, 12, 13 и 14. В этой грамматике терминалами являются токены ECMAScript, определенные в лексической грамматике (5.1.2). Она содержит набор правил, начинающийся с начального символа Программа, определяющий способ преобразования последовательностей токенов в синтаксически корректные программы на ECMAScript.
Когда поток символов Юникода распознается в качестве программы на ECMAScript, он предварительно преобразуется в поток входных элементов путём рекурсивного применения правил лексической грамматики. Затем этот поток входных элементов распознаётся путём однократного применения синтаксической грамматики. Программа считается синтаксически неверной, если токены потока входных элементов не могут быть распознаны как единичный экземпляр начального нетерминала Программа без оставшихся избыточных токенов.
Правила численной строковой грамматики описываются с использованием одиночного двоеточия (":") в качестве разделительного знака. Синтаксическая грамматика, описанная в разделах 0, 0, 0 и 0 (прим. ред. - это в англ. версии стандарта опечатка), на самом деле не является исчерпывающим определением того, какие последовательности токенов принимаются в качестве корректных программ на ECMAScript. Приемлемыми являются также некоторые дополнительные последовательности токенов, а именно те, которые описывались бы грамматикой, если бы символы "точка с запятой" были добавлены в последовательность в некоторых местах (например перед символами перевода строк). Кроме того, некоторые последовательности токенов считаются неприемлемыми, если символ окончания строки встречается в определённых "неудобных" местах.
Терминальные символы лексической и строковой грамматик и некоторые из терминальных символов синтаксической грамматики обозначаются шрифтом постоянной ширины как в правилах грамматики, так и всюду в тексте спецификации, где текст явно ссылается на этот терминальный символ. Эти символы должны появляться в программе точно в таком же виде. Все нетерминальные символы, обозначенные таким образом, следует понимать как соответствующие символы Юникода из ASCII-диапазона, а не аналогично выглядящие символы из других диапазонов.
Нетерминальные символы обозначаются курсивом. Определение нетерминала начинается с имени нетерминала, за которым следует одно или более двоеточий. (По числу двоеточий можно определить, к какой грамматике относится правило.) За ними, на последующих строках, приводится одна или несколько альтервстроенных правых сторон нетерминала. Например, синтаксическое определение:
гласит, что нетерминал ИнструкцияWith представляет токен with, за которым следует токен "левая скобка", Выражение и токен "правая скобка", за которыми, в свою очередь, идет Инструкция. Здесь элементы Выражение и Инструкция сами по себе являются нетерминалами. В качестве другого примера приведём синтаксическое определение:
которое гласит, что СписокАргументов может представлять либо отдельное ВыражениеПрисваивания, либо последовательность из СпискаАргументов, запятой и ВыраженияПрисваивания. Это определение СпискаАргументов является рекурсивным, то есть в своем определении ссылается на само себя. В результате СписокАргументов может содержать любое положительное число аргументов, разделённых запятыми, где выражением для каждого аргумента является ВыражениеПрисваивания. Подобные рекурсивные определения нетерминалов весьма часто используются в описаниях формальных грамматик.
Набранный нижним индексом суффикс "опц", который может быть указан после терминала или нетерминала, указывает на необязательный (опциональный) символ. Вариант, содержащий опциональный символ, в действительности описывает две правые части: не включающую опциональный символ и включающую его. Это означает, что:
является сокращённым для удобства вариантом полной записи:
и что:
является сокращённым для удобства вариантом полной записи:
которая, в свою очередь, является сокращением для:
которая, в свою очередь, является сокращением для:
Таким образом, нетерминал ИнструкцияИтератор на самом деле имеет восемь вариантов правых частей.
Если в качестве правой части правила указывается обозначение "[пусто]" - это значит, что правая часть правила не содержит терминалов или нетерминалов.
Если в правой стороне правила встречается обозначение "[предпросмотр ∉ множество]" - это значит, что правило не может быть использовано, если непосредственно за терминалом следует один из элементов указанного множества. Множество может быть описано как список терминалов, заключённый в фигурные скобки. Для простоты множество также может описываться нетерминалом. В этом случае, оно представляет собой множество всех терминалов, в которые может быть развернут указанный нетерминал. Например, если уже имеются определения
то определение
описывает либо букву n, за которой следует одна или более десятичных цифр, первая из которых чётная, либо десятичная цифра, за которой не следует другая десятичная цифра.
Если обозначение "[здесь нет КонцаСтроки]" появляется в правой части правила синтаксической грамматики, оно обозначает, что данное правило является ограниченным правилом, т.е. не может быть использовано, если КонецСтроки встречается в данном месте входного потока. Например, правило:
означает, что данное правило не может быть использовано, если КонецСтроки встречается в программе между токеном return и Выражением.
За исключением тех случаев, когда появление КонцаСтроки явно запрещается ограниченным правилом, КонецСтроки может появляться любое количество раз между двумя соседними токенами потока входных элементов, не нарушая корректности текста программы.
Когда слова "один из" следуют за двоеточием(-ями) в определении грамматического правила, они обозначают, что каждый из терминальных символов на последующей строке или строках представляет собой вариант правой части. К примеру, лексическая грамматика ECMAScript содержит следующее определение:
которое просто является удобной сокращённой записью для:
Когда в качестве одного из вариантов в правиле лексической или численно-строковой грамматики приводится токен, состоящий из нескольких символов, он обозначает последовательность символов, которые бы составили вместе такой токен.
В определении правой части правила также может уточняться, что некоторые её трактовки не являются допустимыми. На это указывают слова "но не", за которыми приводятся трактовки, подлежащие исключению. Например, правило:
означает, что нетерминал Идентификатор может быть заменён любой последовательностью символов, которая может заменить ИмяИдентификатора, кроме тех последовательностей, которые также могут заменить КлючевоеСлово.
Наконец, в тех случаях, когда явно приводить все возможные варианты было бы нецелесообразно, для некоторых нетерминальных символов приводится словесное описание в обычном шрифте (Roman):
В данной спецификации часто приводятся нумерованные списки, обозначающие шаги алгоритма. Эти алгоритмы используются для прояснения семантики языка. В действительности, для реализации той или иной функциональности могут использоваться более эффективные алгоритмы.
Когда алгоритм должен вернуть некоторое значение в качестве результата, директива "вернуть x" означает, что результатом алгоритма является значение x и что алгоритм должен остановиться в данной точке. Обозначение Результат(n) используется как сокращение для "результат шага n". Тип(x) используется как сокращение для "тип x".
Математические операции, такие как сложение, вычитание, отрицание, умножение, деление и математические функции, определенные ниже в данном разделе всегда должны пониматься как вычисление точного математического результата в математических вещественных числах, которые не включают бесконечностей и отрицательного нуля, отличающегося от положительного нуля. Алгоритмы данного стандарта, моделирующие работу арифметики с плавающей запятой, при необходимости явно включают в себя шаги по произведению округлений и обработке бесконечностей и знакового нуля. Если математическая операция или функция применяется к числу с плавающей запятой, её следует понимать как применённую к точному математическому значению, представленному этим числом. Такое число с плавающей запятой должно быть конечным, и если оно равняется +0 или -0, то соответствующее математическое значение принимается просто за 0.
Математическая функция abs(x) возвращает модуль x, который равен -x, если x отрицателен (меньше нуля), и равен просто x в противном случае.
Математическая функция sign(x) возвращает 1 для положительного x и -1 для отрицательного x. Функция sign не применяется в данном стандарте для случаев, когда 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).
Если алгоритм по определению должен в данной точке "бросить исключение", выполнение алгоритма прекращается, и никакого результата не возвращается. Выполнение иерархии вызвавших его алгоритмов тоже последовательно прекращается, до тех пор пока не будет достигнут уровень алгоритма, который явно обрабатывает исключение, используя терминологию наподобие "если было брошено исключение...". Как только уровень такого алгоритма был достигнут, исключение более не считается случившимся.