上下文无关文法由一系列的产生式组成。每个产生式的左边部分是一个被称为非终结符的抽象符号,右边部分是零或多个非终结符和终结符的序列。任何文法,其终结符都来自指定的字母集。
从一个名为目标符的由特殊非终结符组成的句子开始,给定的上下文无关文法就表示了语言,即:将产生式右边序列的非终结符当作左边,进行反复替换,其结果就成为可能的终结符序列集合(该集合可能无限)。
第7章给出了ECMAScript的词法。该文法的终结符字符(Unicode代码单元)符合第6章定义的*SourceCharacter的规则。该词法定义了一组产生式,从目标符InputElementDiv或InputElementRegExp*开始,描述了如何将诸如此类的字符序列转换成一个输入元素序列。
除空白字符和注释之外的输入元素构成了ECMAScript句法的终结符,同时这些输入元素被称为 ECMAScript的Token。这些Token是ECMAScript语言的保留字、标识符、字面量及标点符号。此外,行终止符虽然不被视为Token,但会成为输入元素流的一部分,用于引导处理自动插入分号。空白字符和单行注释会被简单地丢弃,而不会出现在句法的输入元素流中。如果一个多行注释(即形式为/*...*/
的注释,不管其是否跨行)不包含行终止符也会被简单地丢弃;但如果一个多行注释包含一个或多个行终止符,那么注释会被替换为一个行终止符,进而成为句法的输入元素流的一部分。
15.10给出了ECMAScript的正则表达式文法。该文法的终结符也由*SourceCharacter定义。该文法定义了一组产生式,从目标符Pattern*开始,描述了如何将诸如此类的字符序列翻译成一个正则表达式模式。
双冒号::
作为分隔符分割了词法和正则表达式的文法产生式。同时,词法和正则表达式的文法可共享某些产生式。
用于将字符串转换为数字值的另一种文法。此文法与部分词法类似,都与数字字面量有关;该文法有作为终结符的*SourceCharacter*。此文法将出现在9.3.1 。
三冒号:::
作为分隔符分割数字字符串文法的产生式。
第11、12、13和14章给出了ECMAScript的句法。句法将由词法所定义的ECMAScript Token作为它的终结符(5.1.2)。句法定义了一组起始于*Program*目标符的产生式,描述了Token序列如何才能形成句法上正确的ECMAScript程序。
当一个字符流被解析为ECMAScript程序时,它首先通过词法应用程序反复转换为一个输入元素流;然后再通过一个句法应用程序解析该输入元素流。当输入元素流没有更多的Token时,如果存在Token不能被解析为*Program*目标非终结符的实例,那么程序在句法上存在错误。
单冒号:
作为分隔符分割句法的产生式。
事实上,第11、12、13、14章所给出的句法,并不能完全说明一个正确的ECMAScript程序所能接受的Token序列。故而需要接受一些额外的Token序列,比如说,某些特殊位置(如行结束符前)加入分号可以被文法接受。此外,当终止符出现在某些“尴尬”的位置时,有些文法所描述的Token序列并不会被接受。
JSON文法将描述ECMAScript对象的字符串转换为实际的对象。15.12.1给出了JSON文法。
JSON文法由JSON词法和JSON句法组成。JSON词法用于将字符序列转换为Token,类似于ECMAScript词法。JSON句法描述了JSON词法Token序列如何才能形成句法上正确的JSON对象。
双冒号::
作为分隔符分割JSON词法的产生式。JSON词法使用了ECMAScript词法的某些产生式。JSON句法类似于ECMAScript句法。JSON句法的产生式被单冒号:
作为分隔符分割。
每当文本直接引用诸如词法、正则表达式文法、数字字符串文法及其他文法的终结符时,就使用等宽字体来显示;这些都在文法产生式中,并且贯穿该文档。这些终结符都会出现在程序中,正如写的那样。所有以这种方式指定的终结符,都可以理解为ASCII范围内完整的Unicode字符,而不是任何其他乌焉成马范围内的Unicode字符。
非终结符以斜体显示。非终结符的定义由非终结符名称和其后定义的一个或多个冒号给出。(冒号的数量表示产生式所属的文法。)非终结符的右侧有一个或多个替代子紧跟其后的行。 例如,句法定义:
WhileStatement
:
while
(
Expression
)
Statement
表示非终结符WhileStatement代表whileToken,及其后跟着的左括号Token、Expression、右括号Token和Statement。这里出现的Expression和Statement本身也是非终结符。另一个例子,句法定义:
ArgumentList
:
AssignmentExpression
ArgumentList
,
AssignmentExpression
表示ArgumentList可以代表一个AssignmentExpression或者ArgumentList及其后跟一个逗号和AssignmentExpression。该ArgumentList的定义是递归的,也就是说,它定义了其自身。其结果是,一个ArgumentList可能包含用逗号隔开的任意正整数个参数,每个参数表达式是一个AssignmentExpression。像这样的非终结符递归定义很普遍。
终结符和非终结符可带有后缀下标opt
,表示它是可选符号。实际上,包含可选符号的替代子指定了两个右边部分:一个是省略可选元素的,另一个是包含可选元素的。这意味着:
VariableDeclaration
:
Identifier
Initialiser
opt
是下面的一种简写:
VariableDeclaration
:
Identifier
Identifier
Initialiser
并且:
IterationStatement
:
for
(
ExpressionNoIn
opt
;
Expression
opt
;
Expression
opt
)
Statement
是下面的一种简写:
IterationStatement
:
for
(
;
Expression
opt
;
Expression
opt
)
Statement
for
(
ExpressionNoIn
;
Expression
opt
;
Expression
opt
)
Statement
相应的是下面的一种简写 :
IterationStatement
:
for
(
;
;
Expression
opt
)
Statement
for
(
;
Expression
;
Expression
opt
)
Statement
for
(
ExpressionNoIn
;
;
Expression
opt
)
Statement
for
(
ExpressionNoIn
;
Expression
;
Expression
opt
)
Statement
也即是下面的一种简写:
IterationStatement
:
for
(
;
;
)
Statement
for
(
;
;
Expression
)
Statement
for
(
;
Expression
;
)
Statement
for
(
;
Expression
;
Expression
)
Statement
for
(
ExpressionNoIn
;
;
)
Statement
for
(
ExpressionNoIn
;
;
Expression
)
Statement
for
(
ExpressionNoIn
;
Expression
;
)
Statement
for
(
ExpressionNoIn
;
Expression
;
Expression
)
Statement
因此,非终结符IterationStatement 实际上有8个右侧替代子。
如果文法定义的冒号后面出现“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
如果产生式的右侧出现“[empty]”,则说明该产生式的右侧既不包含终结符也不包含非终结符。
如果产生式的右侧出现“[lookahead ? set]”,则说明给定set的成员不得成为产生式紧随其后的Token。该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]”,则说明此产生式产生式是受限的:如果*LineTerminator*在输入流的指定位置出现,那么此产生式将不会被使用。例如,产生式:
ThrowStatement
:
throw
[no
LineTerminator
here]
Expression
;
表示如果程序中throw Token和Expression之间的出现*LineTerminator*,那么不得使用此产生式。
*LineTerminator*除了禁止出现在受限的产生式中,可以在输入元素流的任何两个 Token之间出现任意次数,而不会影响程序的句法验证。
当一个词法产生式或者数字字符串文法中出现多字符,它表示此字符序列将注册一个Token。
使用词组“but not”可以指定某些不允许在产生式右侧的扩展,进而说明该扩展将被排除。例如,产生式:
Identifier
::
IdentifierName
but
not
ReservedWord
表明该非终结符Identifier可被替换成任何可以替代IdentifierName的字符序列,而不能替换成等同于ReservedWord的字符序列。
最后,对于实际上不可能列出其全部可变元的少量非终结符,我们用普通字体写出的描述性短语来描述它们:
SourceCharacter
::
any Unicode code unit
本规范通常使用带编号的列表来指定算法的步骤。这些算法用来精确地指定ECMAScript语言结构所需的语义。算法无意暗示任何具体实现所使用的技术。实践中,也许可使用更有效的算法来实现一个给定功能。
为便于在本规范多个部分使用某些算法,使用可传参函数化形式来命名和编写这些算法,谓之抽象操作,进而可在其他算法中通过名来称引用它们。
当算法用来产生返回值时,“return x
”指令说明该算法的返回值是x,而算法应该终止。“第n步的结果”可简写成Result(n)。
为表达清晰,算法的步骤可细分为有序的子步骤。子步骤被缩进,也可以将子步骤自身进一步细分为缩进子步骤。大纲编号约定用于识别分步骤:第一层次的子步骤适用小写字母标记,第二层次的子步骤使用小写罗马数字标记。如果需要超过三个层次,则重复这些规则,即第四层次使用数字标记。例如:
- Top-level step
- Substep.
- Substep
- Subsubstep.
- Subsubstep.
- Subsubsubstep
- Subsubsubsubstep
- Subsubsubstep
算法步骤或子步骤可使用断言“如果”作为其子步骤的条件。在这种情况下,当断言为真时子步骤才适用。如果步骤或子步骤由“否则”开始,那么它也是一个断言,其含义与前面的同一层级的断言“如果”正好相反。
算法步骤可以表示其子步骤的迭代应用程序。
算法步骤可以断言该算法的一个不变的条件。这些断言用于明确那些本可能是隐式的算法不变式。这些断言并没有增添额外的语义要求,因此无需执行检查。它们仅仅为了澄清算法。
诸如加法、减法、取反、乘法、除法等数学运算,以及本节稍后定义的数学函数,往往被理解为精确计算数学实数的数学结果,其中既不包括无穷大,也不包括用于区别正零的负零。本标准中的浮点运算算法模型,包括明确的步骤,在必要情况下处理无穷大和有符号零,并执行四舍五入。如果一个数学运算或函数应用于一个浮点数,它应该被应用于代表此浮点数的确切的数学值;这样浮点数必须是有限的,如果它是**+0或-0**,则相应的数学值就是0。
数学函数abs(x)产生x的绝对值:如果x是负数(小于零),其结果是-x,否则是x本身。
如果x是正数,数学函数sign(x)产生1;如果x是负数,则产生**-1**。本标准中x为零的情况下不使用sign函数。
符号“xmoduloy”(y必须有限且非零)计算一个满足以下条件的k值:k与y同号(或是零);abs(k) < abs(y),并且对某些整数q满足x-k = k×k。
数学函数floor(x)产生不大于x的最大整数(最大可为正无穷)。
注:floor(x) = x - (xmodulo1)
如果算法定义了“抛出一个异常”,则算法的执行将被终止,且没有返回结果。已调用的算法也会被终止,直到算法步骤使用术语“如果一个异常被抛出 ...”明确给出异常处理。一旦遇到这种算法步骤,异常将不再被视为已发生过。