Большинство операторов и объявлений ECMAScript должны заканчиваться точкой с запятой. Такие точки с запятой всегда могут быть явно указаны в исходном тексте. Однако для удобства такие точки с запятой могут быть опущены в исходном тексте в определенных ситуациях. Эти ситуации описываются тем, что в этих ситуациях точки с запятой автоматически вставляются в поток токенов исходного кода.
12.9.1 Правила автоматической вставки точки с запятой
В следующих правилах «токен» означает фактически распознанный лексический токен, определённый с использованием текущего лексического целевого символа, как описано в разделе 12.
Три основных правила вставки точки с запятой
Правило № 1
Когда исходный текст анализируется слева направо, встречается токен (называемый ошибочным токеном — offending token), который не разрешён никаким производством грамматики, то точка с запятой автоматически вставляется перед нарушающим токеном, если один или несколько из следующих условий выполняются:
- Ошибочный токен отделён от предыдущего токена по крайней мере одним LineTerminator.
- Ошибочный токен является закрывающей фигурной скобкой }.
- Предыдущий токен является закрывающей круглой скобкой ), а вставленная точка с запятой затем будет анализироваться как завершающая точка с запятой оператора do-while (14.7.2).
Правило № 2
Когда при синтаксическом анализе исходного текста слева направо обнаруживается конец входного потока токенов, и синтаксический анализатор не может проанализировать входной поток токенов как единственный экземпляр целевого нетерминала, тогда точка с запятой автоматически вставляется в конец входного потока.
Правило № 3
Когда исходный текст разбирается слева направо, встречается токен, который разрешён некоторым производством грамматики, но производство является ограниченным, и токен будет первым токеном для терминала или нетерминала сразу после аннотации «[здесь нет LineTerminator]» в ограниченном продукте (и поэтому такой токен называется ограниченным токеном — restricted token), и ограниченный токен отделяется от предыдущего токена по крайней мере одним LineTerminator, затем точка с запятой автоматически вставляется перед ограниченным токеном.
Однако в предыдущих правилах есть дополнительное переопределяющее условие: точка с запятой никогда не вставляется автоматически, если точка с запятой затем будет анализироваться как пустой оператор или если эта точка с запятой станет одной из двух точек с запятой в заголовке оператора for (смотри раздел 14.7.4).
Примечание!
Ниже приведены единственные ограниченные конструкции в грамматике:
UpdateExpression [Yield, Await] :
LeftHandSideExpression [?Yield, ?Await] [не LineTerminator здесь] ++
LeftHandSideExpression [?Yield, ?Await] [не LineTerminator здесь] —
ContinueStatement [Yield, Await] :
continue ;
continue [не LineTerminator здесь] LabelIdentifier [?Yield, ?Await] ;
BreakStatement [Yield, Await] :
break ;
break [нет LineTerminator здесь] LabelIdentifier [?Yield, ?Await] ;
ReturnStatement [Yield, Await] :
return ;
return [не LineTerminator здесь] Expression [+In, ?Yield, ?Await] ;
ThrowStatement [Yield, Await] :
throw [не LineTerminator здесь] Expression [+In, ?Yield, ?Await] ;
ArrowFunction [In, Yield, Await] :
ArrowParameters [?Yield, ?Await] [не LineTerminator здесь] => ConciseBody [?In]
YieldExpression [In, Await] :
yield
yield [не LineTerminator здесь] AssignmentExpression [?In, +Yield, ?Await]
yield [не LineTerminator здесь] * AssignmentExpression [?In, +Yield, ?Await]
ArrowFunction [In, Yield, Await] :
ArrowParameters [?Yield, ?Await] [не LineTerminator здесь] => ConciseBody [?In]
AsyncFunctionDeclaration [Yield, Await, Default] :
async [не LineTerminator здесь] function BindingIdentifier [?Yield, ?Await] ( FormalParameters [~Yield, +Await] ) { AsyncFunctionBody }
[+Default] async [не LineTerminator здесь] function ( FormalParameters [~Yield, +Await] ) { AsyncFunctionBody }
async [не LineTerminator здесь] function BindingIdentifier [~Yield, +Await] opt ( FormalParameters [~Yield, +Await] ) { AsyncFunctionBody }
AsyncMethod [Yield, Await] :
async [не LineTerminator здесь] ClassElementName [?Yield, ?Await] ( UniqueFormalParameters [~Yield, +Await] ) { AsyncFunctionBody }
AsyncGeneratorDeclaration [Yield, Await, Default] :
async [не LineTerminator здесь] function * BindingIdentifier [?Yield, ?Await] ( FormalParameters [+Yield, +Await] ) { AsyncGeneratorBody }
[+Default] async [не LineTerminator здесь] function * ( FormalParameters [+Yield, +Await] ) { AsyncGeneratorBody }
async [не LineTerminator здесь] function * BindingIdentifier [+Yield, +Await] opt ( FormalParameters [+Yield, +Await] ) { AsyncGeneratorBody }
AsyncGeneratorMethod [Yield, Await] :
async [не LineTerminator здесь] * ClassElementName [?Yield, ?Await] ( UniqueFormalParameters [+Yield, +Await] ) { AsyncGeneratorBody }
AsyncArrowFunction [In, Yield, Await] :
async [не LineTerminator здесь] AsyncArrowBindingIdentifier [?Yield] [no LineTerminator here] => AsyncConciseBody [?In]
CoverCallExpressionAndAsyncArrowHead [?Yield, ?Await] [no LineTerminator here] => AsyncConciseBody [?In]
async [не LineTerminator здесь] ArrowFormalParameters [~Yield, +Await]
Практический эффект от этих ограниченных производств заключается в следующем:
- Когда встречается токен
++
или--
, где синтаксический анализатор будет рассматривать его как постфиксный оператор, и хотя бы один LineTerminator возник между предыдущим токеном и токеном++
или--
, то точка с запятой автоматически вставляется перед токенами++
или--
. - Когда встречается токен
continue
,break
,return
,throw
илиyield
и LineTerminator встречается перед следующим токеном, точка с запятой автоматически вставляется после токенаcontinue
,break
,return
,throw
илиyield
. - Когда за параметром(ами) стрелочной функции следует символ LineTerminator перед токеном =>, автоматически вставляется точка с запятой, а знак препинания вызывает синтаксическую ошибку.
- Когда за асинхронным токеном async следует LineTerminator перед function или IdentifierName или ( токеном, автоматически вставляется точка с запятой, и асинхронный токен async не рассматривается как часть того же выражения или элемента класса, что и следующие токены.
- Когда за токеном async следует LineTerminator перед токеном *, автоматически вставляется точка с запятой, а знак препинания вызывает синтаксическую ошибку.
Итоговый практический совет программистам на ECMAScript:
- Постфиксный оператор
++
или--
должен находиться в той же строке, что и его операнд. - Выражение Expression в операторе
return
илиthrow
или AssignmentExpression в выраженииyield
должно начинаться в той же строке, что и токенreturn
,throw
илиyield
. - LabelIdentifier в операторе
break
илиcontinue
должен находиться в той же строке, что и токенbreak
илиcontinue
. - Конец параметра(ов) стрелочной функции и его стрелки => должны быть на одной строке.
- Асинхронный токен async, предшествующий асинхронной функции или методу, должен находиться в той же строке, что и следующий за ним токен.
12.9.2 Примеры автоматической вставки точки с запятой
Этот раздел не является нормативным.
Источник
{ 1 2 } 3
не является допустимым предложением в грамматике ECMAScript, даже с учетом правил автоматической вставки точки с запятой. Напротив, источник
{ 1 2 } 3
также не является допустимым предложением ECMAScript, но преобразуется автоматической вставкой точки с запятой в следующее:
{ 1 ;2 ;} 3;
что является допустимым предложением ECMAScript.
Источник
for (a; b
)
не является допустимым предложением ECMAScript и не изменяется автоматической вставкой точки с запятой, поскольку точка с запятой необходима для заголовка оператора for. Автоматическая вставка точки с запятой никогда не вставляет одну из двух точек с запятой в заголовок оператора for.
Источник
return
a + b
преобразуется автоматической вставкой точки с запятой в следующее:
return;
a + b;
Выражение a + b не рассматривается как значение, возвращаемое оператором return, потточникому что LineTerminator отделяет его от токена return.
Источник
a = b ++c
преобразуется автоматической вставкой точки с запятой в следующее:
a = b; ++c;
Токен ++ не рассматривается как постфиксный оператор, применяемый к переменной b, потому что LineTerminator встречается между b и ++.
Иссточник
if (a > b) else c = d
не является допустимым предложением ECMAScript и не изменяется автоматической вставкой точки с запятой перед токеном else, даже если в этот момент грамматика не применяется, потому что автоматически вставленная точка с запятой будет тогда анализироваться как пустой оператор.
Источник
a = b + c
(d + e).print()
не преобразуется автоматической вставкой точки с запятой, потому что выражение в скобках, которое начинается во второй строке, может интерпретироваться как список аргументов для вызова функции:
a = b + c(d + e).print()
В том случае, когда оператор присваивания должен начинаться с левой круглой скобки, для программиста рекомендуется явно поставить точку с запятой в конце предыдущего оператора, а не полагаться на автоматическую вставку точки с запятой.
12.9.3 Интересные случаи автоматической вставки точки с запятой
Этот раздел не является нормативным.
Программы на ECMAScript могут быть написаны в стиле с очень небольшим количеством точек с запятой, полагаясь на автоматическую вставку точек с запятой. Как описано выше, точки с запятой не вставляются в каждую новую строку, а автоматическая вставка точки с запятой может зависеть от нескольких токенов через терминаторы строки.
По мере добавления новых синтаксических функций в ECMAScript могут быть добавлены дополнительные грамматические конструкции, которые заставят строки, полагающиеся на автоматическую вставку точки с запятой перед ними, изменять грамматические произведения при синтаксическом анализе.
Для целей этого раздела случай автоматической вставки точки с запятой считается интересным, если это место, где точка с запятой может или не может быть вставлена, в зависимости от исходного текста, который ей предшествует. Остальная часть этого раздела описывает ряд интересных случаев автоматической вставки точки с запятой в этой версии ECMAScript.
12.9.3.1 Интересные случаи автоматической вставки точки с запятой в списки операторов
В StatementList многие StatementListItem заканчиваются точкой с запятой, которые можно опустить, используя автоматическую вставку точки с запятой. Как следствие приведенных выше правил, в конце строки, заканчивающейся выражением, требуется точка с запятой, если следующая строка начинается с любого из следующего:
- Открывающая круглая скобка — (. Без точки с запятой две строки вместе рассматриваются как CallExpression.
- Открывающая квадратная скобка — [. Без точки с запятой две строки вместе рассматриваются как доступ к свойству, а не как ArrayLiteral или ArrayAssignmentPattern.
- Литерал шаблона — `. Без точки с запятой две строки вместе интерпретируются как помеченный шаблон (13.3.11) с предыдущим выражением как MemberExpression.
- Унарный плюс + или унарный минус —. Без точки с запятой две строки вместе интерпретируются как использование соответствующего бинарного оператора.
- Литерал RegExp. Без точки с запятой две строки вместе могут анализироваться как / MultiplicativeOperator, например, если RegExp имеет флаги.
12.9.3.2 Случаи автоматической вставки точки с запятой и «[не LineTerminator здесь ]»
Этот раздел не является нормативным.
ECMAScript содержит грамматические конструкции, которые включают «[не LineTerminator здесь]». Эти произведения иногда являются средством наличия необязательных операндов в грамматике. Внедрение LineTerminator в этих местах изменит создание грамматики исходного текста с помощью создания грамматики без необязательного операнда.
Остальная часть этого раздела описывает ряд производств, использующих «[не LineTerminator здесь]» в этой версии ECMAScript.
12.9.3.2.1 Список грамматических произведений с необязательными операндами и «[не LineTerminator здесь ]»
- UpdateExpression.
- ContinueStatement.
- BreakStatement.
- ReturnStatement.
- YieldExpression.
- Определения асинхронных функций (15.8) по отношению к определениям функций (15.2)
Информационные ссылки
ECMAScript | Язык ECMAScript: лексическая грамматика
Стандарт ECMAScript — Раздел «12.9 Automatic Semicolon Insertion» — https://tc39.es/ecma262/#sec-automatic-semicolon-insertion