ECMA262 记法约定

写在最前面的话:

    首先,这将是一个系列的帖子.其次,你进来了,我很不幸的告诉你,这是一个坑,因为我不知道什么时候能放出下一章.管挖不管埋啊.

    读ECMA262.第五章-记法约定,有必要先读一下,才好在阅读其他章节的时候.更容易明白,类似Program 和 Program .同样一个词指的东西完全不同,从而更好的进行阅读.所以先搞出这章放着吧.

    这部分是平时业余时间翻译出来的,水平有限.难免有错漏的地方.欢迎指正.另外winter 和 hax,两个站着说话,不腰疼的家伙.的意思是.Edition3 out了. 应该搞Edition5.但我坚持以现行标准为主.遇到Edition5有不同,或增加的段子.就标红注释.所以就成了现在这个样子. 还一个最重要的原因就是Edition5内容真的好多.

 
记法约定(Notational Conventions)

    
语法和词法文法(Syntactic and Lexical Grammars)
 
 
本章节描述了ECMAScript程序,所使用的上下文无关文法(context-free grammars,即2型文法)是如何定义相关词法和语法结构规范的.
 
    
 
    
     
     


     
     上下文无关文法(Context-Free Grammars)
 
    
     
     
一个上下文无关文法由一些产生式构成(所以提到这些产生式,总是叫他们文法产生式,也就是一坨坨的语法、词法规则).每个产生式都有一个抽象符号,被称为非终结符,作为其左式(left-hand side).
         
 
由0个或多个非终结符以及终结符组成的序列,构成其右式(right-hand side).对于每个文法来说,终结符都是从一个其规定的字母表中筛选出来的.(注1)
     
     
     
     从一个,由被称为目标符号(goal symbol symbo)的,作为应用文法起始特征的,非终结符构成的句子开始,一个
上下文无关文法,是通过那些可能导致,反复的根据其规则进行左式非终结符与右式的,
     
     
(可能是无限的)
 
终结符序列(也应该包含非终结符,否则这里很费解).发生替换的产生式.
定义一种语言的.(这里其实就是想说,2型文法通过其约定的产生式,(即词法规则)来定义一种语言)




          词法和正则表达式文法(The Lexical and RegExp Grammars)
     
     ECMAScript所给定的词法文法参见第7章.该文法以Unicode字符集作为基础,从中定义了其终结符. 并定义了一些产生式,这些产生式定义了,从目标符除法符号或正则表达式符开始(指:/),
          Unicode字符集序列是如何被解析成输入元素序列的.

     
     ECMAScript的语法文法的终结符中,输入元素是区别于空白和注释的.这些输入元素被称为ECMAScript的标记(记号-tokens).这些标记包括ECMAScript所定义并使用的,保留字、标识符、直接量、
     
     
以及
标点符号.另外,行终结符虽然不被认为是一种标记(token).但却被认为是输入元素的一部分.并专门辅助用来实现分号自动插入机制的(参见 7.9.5章节). 一般的空白(\u3000也算)和单行注释,
          将会被忽略,即不会出现在语法文法的输入元素流(stream of input elements)中.而多行注释(/*...*/这种内部无行终结符的东东),也同样会被简单的忽略掉.但如果是一个内部存在一个或多个
          行终结符的
多行注释,则会被替换为一个单独的行终结符
(注2)
.并成为语法文法输入元素流的一部分.
     
     
          ECMAScript的正则表达式文法相关内容,参见15.10章节. 该文法所指定的终结符,同样筛选自Unicode字符集.该文法同样定义的那些产生式,定义了,如何从一个目标符模式开始,把这些字符序列
          解析成正则表达式模式的.
          
          词法产生式,和正则表达式文法产生式,都有用于分隔作用的两个冒号"::"作为其特征.词法和正则表达式文法还具有(使用,共享)一些相同的产生式.




          数字和字符串文法(The Numeric String Grammar)
     
     一个用于把字符串解释成对应的数字值的文法.此文法和词法文法中关于数字直接量的部分类似.并把这些数字字符串作为终结符的源字符. 参见9.3.1章节.
          
     
     数字型字符串的文法产生式,用三个冒号":::",作为其产生式的特征.




     
     
语法文法(The Syntactic Grammar)
     
     11、12、13、14章,具体描述了ECMAScript的语法文法.此文法包括词法文法所定义的,属于终结符的ECMAScript标记(tokens)(参见5.1.2章节).并定义了从程序的目标符开始的,一些产生式.
     
    并解释了,这些tokens 序列是如何组成正确的ECMAScript 程序的.
      
     
     
     
一个Unicode字符流被解析为ECMAScript程序,首先,反复通过词法文法的应用,把字符流解析为一个输入元素流.然后,这个输入元素流再被一个语法文法的应用所解析.如果其中的标记(tokens),无法和余下的其他标记(tokens)一起,被解析为单一的,非终结的,Program 目标符实例(注3),则这个程序有语法错误.
        
     
     语法文法的产生式,是以一个冒号":"作为特征的.
     
     
     11,12,13和14章.描述的相关语法文法,其实并不是全部,ECMAScript程序中所允许的token序列.还有一些额外的token是被接受的. 也就是说,在某些地方(比如在行终结符前)插入的分号,就需要被文法所解释.此外,假如一个终结符出现在某些"尴尬"的位置上的时候(我去,啥地方是尴尬的位置?大概是不允许出现终结字符的地方?比如字符串中的一个换行?),这样的token序列就是不被文法所接受的.
          
          
          Edition5:(此处,为Edition5新增)
          Javascript 对象字面量表示-文法(The JSON Grammar)
          JSON文法是用于把一个描述了一些ECMAScript 对象的字符串,转化为真正对象的文法.具体参见15.12.1章节.
     
     
     
     JSON文法包括JSON词法文法和语法文法两部分.其中,词法文法部分,是用于把字符序列转换成tokens的,并且类似于ECMAScript的部分词法文法. JSON语法文法,是用于描述,如何使用,通过词法文法得到的tokens,来组成符合语法的 JSON 对象的.

     
     JSON词法文法产生式,是以两个冒号 "::"作为其特征,以及分隔性的符号.JSON词法文法直接使用了一些ECMAScript词法文法的产生式,作为其产生式. 而JSON语法文法,基本与ECMAScript语法文法的某一部分相类同. JSON语法文法的产生式,是以 一个冒号":",作为其特征以及分隔符号的.





     
     
文法表示法(记法,Grammar Notation)
     
     词法和字符串文法的终结符,以及语法文法的终结符,是用等宽字体(fixed width)(注4)显示的.贯穿整个ECMA标准.
两种文法产生式中,只要出
现这种(等宽字体)文字.它都直接表示一个终结符.这些终结符,其实会出现在语言使用者所写的程序中.所有定义的终结符类型的字符,所以要用等宽字体表示,就是为了更好的在ASCII范围内识别那些,被选出的(指被选做终结符的,但属于ASCII范围的字符),以及区别那些Unicode范围内,看起来很相似的那些字符.

          非终结符用 (斜体) 表示. 非终结符的定义即是 非终结符名 一个或多个冒号":" 后面的那部分(冒号的数量,则代表其属于哪种文法. 比如前面提到的 语法文法就是用一个冒号 ":"). 一个或多个右式子(产生式的右式)中,可供选择的非终结符,会在后续行中被解释.
     
     
     
例子
:
     
     
     
     WithStatement :
     
     
    
     
with (Expression) Statement
     
          通过上面这个语法产生式.我们可以看到, 非终结符-WithStatement 代表(冒号 : 后面的部分)这样一种语法,一个 token(终结符) with,后面跟这一个token-左括号 "(",然后是一个非终结符-表达式-Expression,然后是一个token-右括号")",然后是一个 非终结符-Statement(语句). 
     
     
     另一个例子:
          
          
     
ArgumentList:
                    AssignmentExpression
     
     
  
       ArgumentList , AssignmentExpression
     
     
     
     上面这个语法产生式表示,一个ArgumentList可以代表一个单独的
AssignmentExpression,一个ArgumentList 然后是逗号 "," 然后是一个AssignmentExpression.这定义了一种递归形式的产生式.其实就是告诉我们,
ArgumentList 可以包含任意数量(正数)的,用逗号","来分隔的 参数. 其中每一个参数表达式,都是一个
AssignmentExpression.这种递归形式的定义,是通用的形式.

     
     
     关于可能出现在终结符或非终结符后面的,(产生式中的)-opt下标(注5),是一个代表可选择性意义的符号.它实际上,表示了两种右式形式.一种是包含具备可选符号元素.而另一种则没有这个东东.
     
           也就是说:
          
                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

     
     (后面还有例子.很蛋疼.这里就不抄了.......)
     
     
     
     如果 "[empty]" 作为产生式的右式出现.则表示产生式的右式中,不浩瀚任何终结符和非终结符.
     
    
      如果
 "[lookahead 
set
]"
 出现在产生式的右式中,它表示紧随的输入记号(input token),不能是给定集合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后面跟随一个 偶数.然后是 DecimalDigits. 或一个DecimalDigit(十进制数字) 紧随非十进制数字的情况.

          如果 "[no LineTerminatorhere]" 出现在语法文法产生式的右式中,这表示该产生式,是一个
限制型
产生式 : 也就是 LineTerminator不能出现在输入流的这个位置.
     
     
     来个例子:
               
     
     
     ReturnStatement :
     
     
     
     return [no LineTerminator here] Expression-opt;

          就是说,在这个产生式中. token-return Expression ,之间不能出现行终结符(如果出现了.那么就会导致分号自动插入机制产生作用.也就成为了另外的一种语法形式 return ;了).
          除非存在一个不允许出现行终结符的限制型产生式,否则,在程序的语法允许范围内,输入元素流中,任意两个连续的记号(token),之间可以出现任意个行终结符.

     
     
     
     当 "one of"(注意,这个不是终结符哈)紧随n个冒号后面,出现在文法定义中时,表示每个(可由当前非终结符推导出的)终结符会出现在后续行中. 
     
     
     
     
     来个例子,ECMAScript词法文法有这样一个产生式:
     
     
     
 
     
     
     NonZeroDigit :: one of
                    1 2 3 4 5 6 7 8 9

     
     
     实际上可以写成:

     
     
     NonZeroDigit ::
                     1
     
     
     
     
2
     
     
     
     
3
     
     
     
     4
     
     
     
     5
     
     
     
     6
     
     
     
     7
     
     
     
     8
     
     
     
     9
     
     
 
 
 
     
     当一个可选的,也许是多个字符所组成的token,出现在一个词法文法产生式,或一个数字类型的字符串文法产生式中时,
 意味着,这个字符序列需要构成一个 token.
     
     (可能我没有很好的理解这句话,个人认为这句话完全是废话.另外此废话和前面NonzeroDigit无关系)
     

          产生式的右式中,会使用短语 "but not",来制定某些扩展是不被允许的、被排除的. 
     
     
     来个例子,产生式:
               
               Identifer ::
               
     
IdentifierName but not ReservedWord
          意思就是,非终结符
Identifer
可以替换为任何非
ReservedWord原文指,不能替换为ReserverWord的,也就是不能被ReserverWord推导出的.那些终结或非终结符,其实就是非的关系
,且可被IdentifierName所替换(推导)的那些东东.


     
     最后, 一些非终结符,可能会在一些不方便列出,或无法列出其所有可选项时,用一些,使用sans-serif(非衬线字体)字体,的短语来描述. 
     
     
     来个例子:
     
     
     
     
     
     SourceCharacter ::
     
     
     
     any Unicode code unit (用的黑体哈.凑合吧)
     
     




          运算(算法)约定(Algorithm Conventions)
          算法中,经常使用一个带编号的列表来定义其步骤. 这些算法是从语义角度上,来准确描述,所需的ECMAScript语言结构的.而不是暗示语言实现者,必须按照指定算法来实现.因为也许有很多更有效率的算法.可以用于实现某些功能.

     
     Edition5:
     
     为了让语言实现者,更容易的实现此标准的各个部分,某些被命名且被书写成参数化的、函数形式的,被称为抽象操作(运算)的东东.可以通过其命名.引用到其他算法(语言实现者,真正的算法)中去.
     
     (Edition5 的这段描述很纠结.不就是想对第一段做补充么?我们都理解了.您非要来一段子费解的长句子做什吗?)


          当一个运算,产生一个值作为其结果,"return x"指令则是指, 运算的结果就是 x 的值,并且必须要结束当前运算. 表示法 Result(n) 则是 步骤n的运算结果的简写.

     
     Edition5:
     
     为了表述清楚,运算步骤可能会被进一步细化为一些子步骤. 子步骤会表现为缩进格式.而且子步骤还可能被进一步的细化为更详细的子(孙)步骤. 我们滴关于编码约定的大纲是这样滴:
     
     
     
第一级子步骤 : 使用小写字母作为其顺序的标签. 
     
     
     
第二级 : 则使用罗马数字.
     
     
     第三级 : 递归到主步骤的标签 - 数字 ...(第四级依次使用第一级子步骤.这样子.递归下去...)
          (原文,edition5这这段描述很蛋疼.所以此处,并没有按原文直译)
     
      
来个例子:
     
     
     1. 顶级的主步骤 
     
     
     
     a. 第一级子步骤1
     
     
     
     b. 第一级子步骤2
     
     
     
     
     i. 第二级子步骤1
     
     
     
     
    ii. 第二级子步骤2
     
     
     
     
     
     1. 第三级子步骤1
                              2. 第三级子步骤2
                                   a. 第四级子步骤1.
 
          一个步骤或子步骤.也许会被写成  "if" 断言(predicate)-即进入子步骤的条件.在这种情况下.子步骤只有在谓语(条件)为真的情况下才会被应用.如果一个步骤或子步骤以 "else"开始,那么此处也是一个条件断言,它与其前面的 "if" 断言部分对应.否定、且为同级步骤的关系.(老道的英文描述真不是一般的蛋疼.鄙视一下吧.好在意思大家都明白.)

          一个步骤可能会指定一个引用程序的迭代的子步骤.


          数学运算,如,加、减、取反(指负号运算符,而不是位运算的取反运算"~",更不是逻辑非运算"!")、乘、除, 以及此条款后面定义的数学函数.等精确的数学计算以及结果,是建立在数学中,实数(有理数,无理数的相关运算,与虚数对应)范畴内的.其中不包括正负无穷以及负0(区别于正0,区别于32位,代表符号位的值). 此标准中,关于浮点运算的运算过程.具备明确的步骤.比如 必须处理无穷,有符号0,以及四舍五入.如果一个数学运算或函数,应用于一个浮点数,就必须理解为,其应用于一个以该浮点数为代表的精确的数学值.这个浮点数,必须是有限的.如果它是正0或负0,那么它的数学意义上的值就是0.
     
          数学函数,abs(x) 会产生一个 x的绝对值,就是说,如果x是负数(小于0),那么产生的值就是-x.其他情况则是x自身.
               
          数学函数,sign(x),在 x是正数时候返回1,在x是负数的时候返回-1.而当x值为0时,sign方法在此标准中没神马意义.

     
     字面量 "x  modulo y" (y必须是有限且非0的),计算出一个与 x 有相同符号(正负)的值 k(或k为0), 则 abs(k)<abs(y) 并且 x-k=q×y (q是某个整数,其实就是商).(注6)

     
     数学函数,floor(x),产生一个小于等于x的最大整数.

          note :
     
     
     floor(x) = x-(x modulo 1). 

     
     如果一个运算被定义为"抛出一个异常",则运算的执行将被停止,并且不会有任何值作为运算结果返回.所调的运算也将被停止.一直到某个运算步骤与异常达成了某种约定,学术一些的说法是,一但某个异常被抛出,则那个早于这个异常的运算步骤,不再被视为执行过.

 






注释:

注释1 .   比如 一个产生式  S => E | e   ,S 是左式 即代表一个非终结符,根据产生式,进一步推导出非终结符 E,和终结符e.     E可以进一步推倒. 那就要根据另外的产生式了.
             那么,终结符由文法具体指定.这里不做解释了. 
 
 
注释2 .   参考下面的代码:
               alert(function(){
                    return /*
                              */ 1;
               }()); // 即分号自动插入机制,根据规则在 1前面自动插入分号鸟.多行注释内存在一个行终结符,那么它就被替换为一个行终结符.而不是简单的被忽略掉.

              但是,浏览器的实现并不一致. IE6-IE8, Firefox0.8-Firefox3.0, Safari5-, chrome11- 都打印1.即并没有遵守标准.
              而 IE9 , Firefox3.5+ , Opera11-(测到9.2),则打印undefined,遵守了标准.
     
 
注释3 . 他的意思其实就是做语法解析的时候,如果到最后.剩下了某些token没法用了,就有语法错误了. 而所谓 - 单一的,非终结的,Program 目标实例. 实际上是指整个输入元素流最终要解析为一个可由 program :  语法完整描述的 文法产生式. 而这个 Program ,是一个可以进一步推导的非终结符.  参考 14章节:
 
     语法
Program :
     SourceElementsopt
SourceElements :
     SourceElement 
     SourceElements SourceElement
SourceElement :
     Statement
     FunctionDeclaration  
 
 
 

注释4 . 对于等宽字体.我就用粗体表示了.只要在记法约定的例子部分.能区分,非终结符(斜体),以及 终结符(等宽字体,我用粗体表示).就算达到目的. 而其他如表达式和词法约定章节的翻译部分.涉及到产生式部分.我并没有遵守这一原则.主要原因是.太麻烦鸟.


 

注释5 . 在evernote里,我是不知道如何输入下标形式的 opt 了. 所以一概用  -opt 代替 下标opt.

注释6 对负数的取模运算,C语言的 C99规范规定:如果第一运算数为负,则得到的模为负;如果第一运算数为正,则得到的模为正实际上ECMAScript亦如此.虽然其描述,并非如此直接.

       基于 ECMAScript % 求模运算符的 运算法则. 会在 11章相关%运算符部分解释. 但应了解.ECMAScript.不仅仅支持整数范围的求模运算.还支持浮点数的求模运算.
          
          x%y  对应 7%3 -> 7/3=2 余1.其中7对应x,3对应y,2对应商,1对应余. 那么:
          根据其描述: abs(k) < abs(y) and x−k = q × y 得出.
          abs(1) < abs(3)
          7-1 =  2*3    
          基于此.我们可以断定ECMA262 原文中 computes a value k of the same sign as y 是错误的. 无论是Edition3还是Edition5. 此处y应该改为x
     
          求模公式:
          function mod(a,b){
              return  a-(a/b|0)*b;
          }
 
 
     相关数学概念:
          两个自然数求余法则:
               如果ad是两个自然数,d非零,可以证明存在两个唯一的整数 和 r,满足 a qd r 且0 ≤ r d。其中,被称为商,被称为余数。
 
          那么对于负数,是否可以沿用这样的定义呢?我们发现,假如我们按照正数求余的规则求 (-7) mod 3 的结果,就可以表示 -7 为 (-3)* 3 +2。其中,2是余数,-3是商.那么,各种编程语言和计算器是否是按照这样理解的呢?下面是几种软件中对此的理解:
 
C++(G++ 编译): cout << (-7) % 3; // 输出 -1
 
C#(4.0) : Response.Write(-7%3); //输出 -1
 
Java(1.6): System.out.println((-7) % 3); // 输出 -1
 
Python 2.6:>>>  (-7) % 3 // 输出 2
 
百度计算器:(-7) mod 3 = 2
 
Google 计算器:(-7) mod 3 = 2
 
有道计算器:(-7) mod 3 = -1
 
javascript :(-7)  % 3 = -1
 
          

可以看出问题来自自然数范围求余法则和整数范围求余法则的冲突.

 
对于,浮点数的求模运算,则同java . 即类似如下过程:
a % b  如果其中一个是浮点数,则伪代码
while(a > b){
   a = a - b;
}
return a;
 
 
 
 
posted @ 2011-05-02 18:54  Franky  阅读(1493)  评论(5编辑  收藏  举报