【C#语言规范版本5.0学习】2 词法结构(一、简述)

➤ 程序

C# 程序 (program) 由一个或多个源文件 (source file) 组成,源文件的正式名称是编译单元 (compilation unit)

源文件是有序的 Unicode 字符序列。源文件与文件系统中的文件通常具有一对一的对应关系,但这种对应关系不是必需的。为实现可移植性的最大化,建议这些文件在文件系统中应按 UTF-8 编码规范编码。 从概念上讲,程序的编译分三个步骤:

  • 1. 转换,这一步将用特定字符指令系统和编码方案编写的文件转换为 Unicode 字符序列。
  • 2. 词法分析,这一步将 Unicode 输入字符流转换为标记流。
  • 3. 句法分析,这一步将标记流转换为可执行代码。

➤ 文法

本规范采用两种文法 (grammar) 来表示 C# 编程语言的语法 (syntax)。

词法文法 (lexical grammar)规定怎样将 Unicode 字符组合成行结束符、空白、注释、标记和预处理指令等。

句法文法 (syntactic grammar)规定如何将那些由词法文法产生的标记组合成 C# 程序。

⟰ 文法表示法

词法文法和句法文法用文法产生式 (grammar production) 来表示。每个文法产生式定义一个非结束符号和它可能的扩展(由非结束符或结束符组成的序列)。在文法产生式中,non-terminal 符号显示为斜体而 terminal 符号显示为等宽字体。

文法产生式的第一行是该产生式所定义的非结束符号的名称,后跟一个冒号。每个后续的缩进行列出一个可能的扩展,它是以非结束符或结束符组成的序列的形式给出的。例如,产生式:

while-statement:

     while (  boolean-expression  )  embedded-statement

定义了一个 while-statement,它是这样构成的:由标记 while 开始,后跟标记“(”、booleanexpression、标记“)”,最后是一个 embedded-statement

当有不止一个可能的非结束符号扩展时,列出这些可能的扩展(每个扩展单独占一行)。例如,产生式:

statement-list:

statement

statement-list  statement

定义一个 statement-list,它或仅含有一个 statement,或由一个 statement-list 和随后跟着的一个 statement 组成。换言之,定义是递归的,语句列表由一个或多个语句组成。

一个符号若以下标“opt”作其后缀,就表明该符号是可选的。产生式:

block: { statement-listopt }

是以下产生式的简短形式:

block:

{ }

{ statement-list }

它定义了一个 block,此块由一个用“{”和“}”标记括起来的可选 statement-list 组成。 可选项通常在单独的行上列出,但是当有许多可选项时,可以在单行上给定的扩展列表之后加上短语 “之一”。这只是在单独一行上列出每个可选项的简短形式。例如,产生式:

real-type-suffix: one of

     F   f   D   d   M   m

是以下产生式的简短形式:

real-type-suffix:

F

f

D

d

M

m

⟰ 词法文法

C# 的词法文法在后面中介绍。词法文法的结束符号为 Unicode 字符集的字符,并且词法文法指定如何组合字符以构成标记、空白、注释和预处理指令。 C# 程序中的每个源文件都必须符合词法文法的 input 产生式。

⟰ 句法文法

本章后面的章节和附录介绍 C# 的句法文法。句法文法的结束符号是由词法文法定义的标记,句法文法指定如何组合这些标记以构成 C# 程序。 C# 程序中的每个源文件都必须符合句法文法的 compilation-unit 产生式。

➤ 语法分析

input 产生式定义 C# 源文件的词法结构。C# 程序中的每个源文件都必须符合此词法文法产生式。

input:
    input-sectionopt
input-section:
    input-section-part
    input-section input-section-part
input-section-part:
    input-elementsopt new-line
    pp-directive
input-elements:
   input-element
   input-elements input-element
input-element:
   whitespace
   comment
   token

C# 源文件的词法结构由五个基本元素组成:行结束符、空白、注释、标记和预处理指令

在这些基本元素中,只有标记在 C# 程序的句法文法中具有重要意义。 对 C# 源文件的词法处理就是将文件缩减成标记序列,该序列然后即成为句法分析的输入。行结束符、 空白和注释可用于分隔标记,预处理指令可导致跳过源文件中的某些节,除此之外这些词法元素对 C# 程序的句法结构没有任何影响。 当有若干词法文法产生式与源文件中的一个字符序列匹配时,词法处理总是构成尽可能最长的词法元素。 例如,字符序列 // 按单行注释的开头处理,这是因为该词法元素比一个 / 标记要长。

⟰ 行结束符

行结束符将 C# 源文件的字符划分为行。

new-line:
Carriage return character (U+000D)
Line feed character (U+000A)
Carriage return character (U+000D) followed by line feed character (U+000A)
Next line character (U+0085)
Line separator character (U+2028)
Paragraph separator character (U+2029)

为了与添加文件尾标记的源代码编辑工具兼容,并能够以正确结束的行序列的形式查看源文件,下列转换按顺序应用到 C# 程序中的每个源文件:

  • 如果源文件的最后一个字符为 Control-Z 字符 (U+001A),则删除此字符。
  • 如果源文件非空并且源文件的最后一个字符不是回车符 (U+000D)、换行符 (U+000A)、行分隔符 (U+2028) 或段落分隔符 (U+2029),则将在源文件的结尾添加一个回车符 (U+000D)。

⟰ 注 释

支持两种形式的注释:单行注释和带分隔符的注释

单行注释 (Single-line comment) 以字符 // 开头并延续到源行的结尾。

带分隔符的注释 (Delimited comment) 以字符 /* 开头,以字符 */ 结束。带分隔符的注释可以跨多行。

comment:
single-line-comment
delimited-comment
single-line-comment:
// input-charactersopt
input-characters:
input-character
input-characters input-character
input-character:
Any Unicode character except a new-line-character
new
-line-character: Carriage return character (U+000D) Line feed character (U+000A) Next line character (U+0085) Line separator character (U+2028) Paragraph separator character (U+2029) delimited-comment: /* delimited-comment-textopt asterisks / delimited-comment-text: delimited-comment-section delimited-comment-text delimited-comment-section delimited-comment-section: / asterisksopt not-slash-or-asterisk asterisks: * asterisks * not-slash-or-asterisk: Any Unicode character except / or *

 注释不嵌套。字符序列 /* 和 */ 在 // 注释中没有任何特殊含义,字符序列 // 和 /* 在带分隔符的注释中没有任何特殊含义。 在字符和字符串内不处理注释。 下面的示例

/* Hello, world program
This program writes “hello, world” to the console
*/
class Hello
{
static void Main() {
System.Console.WriteLine("hello, world");
}

 包含一个带分隔符的注释。 下面的示例

// Hello, world program
// This program writes “hello, world” to the console
//
class Hello // any name will do for this class
{
static void Main() { // this method must be named "Main"
System.Console.WriteLine("hello, world");
}
}

⟰ 空 白

空白被定义为任何含 Unicode 类 Zs 的字符(包括空白字符)以及水平制表符、垂直制表符和换页符。

whitespace:
Any character with Unicode class Zs
Horizontal tab character (U+0009)
Vertical tab character (U+000B)
Form feed character (U+000C)

➤ 标 记

有几类标记:标识符、关键字、文本、运算符和标点符号空白和注释不是标记,但它们可充当标记的分隔符

token:
    identifier
    keyword
    integer-literal
    real-literal
    character-literal
    string-literal
    operator-or-punctuator

⟰ Unicode 字符转义序列

Unicode 字符转义序列表示一个 Unicode 字符。Unicode 字符转义序列在标识符、字符和规则字符串中处理。不在其他任何位置处理 Unicode 字符转义(例如,在构成运算符、标点符号或关键字时)。

unicode-escape-sequence:
  \u hex-digit hex-digit hex-digit hex-digit
  \U hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit

Unicode 转义序列表示由“\u”或“\U”字符后面的十六进制数字构成的单个 Unicode 字符。由于 C# 在字符和字符串值中使用 Unicode 代码点的16 位编码,因此从 U+10000 到 U+10FFFF 的 Unicode 字符不能在字符中使用,在字符串中则用一个 Unicode 代理项对来表示。不支持代码数据点在 0x10FFFF 以上的 Unicode 字符。

不执行多次转换。例如,字符串文本“\u005Cu005C”等同于“\u005C”,而不是“\”。Unicode 值 \u005C 是字符“\”。下面的示例

class Class1
{
static void Test(bool \u0066) {
char c = '\u0066';
if (\u0066)
System.Console.WriteLine(c.ToString()); //f
}
}

演示了 \u0066(它是字母“f”的转义序列)的一些用法。该程序等效于

class Class1
{
static void Test(bool f) {
char c = 'f';
if (f)
System.Console.WriteLine(c.ToString()); //f
}
}

⟰ 标识符

本节给出的标识符规则完全符合 Unicode 标准附件 31 推荐的规则,但以下情况除外:允许将下划线用作初始字符(这是 C 编程语言的传统),允许在标识符中使用 Unicode 转义序列,以及允许“@”字符作为前缀以使关键字能够用作标识符。

identifier:
available-identifier
@ identifier-or-keyword
available-identifier:
An identifier-or-keyword that is not a keyword
identifier-or-keyword:
identifier-start-character identifier-part-charactersopt
identifier-start-character:
letter-character _ (the underscore character U+005F)
identifier-part-characters:
identifier-part-character
identifier-part-characters identifier-part-character
identifier-part-character:
letter-character
decimal-digit-character
connecting-character
combining-character
formatting-character
letter-character:
A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl
A unicode-escape-sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl
combining-character:
A Unicode character of classes Mn or Mc
A unicode-escape-sequence representing a character of classes Mn or Mc
decimal-digit-character:
A Unicode character of the class Nd
A unicode-escape-sequence representing a character of the class Nd
connecting-character:
A Unicode character of the class Pc
A unicode-escape-sequence representing a character of the class Pc
formatting-character:
A Unicode character of the class Cf
A unicode-escape-sequence representing a character of the class Cf

有关上面提到的 Unicode 字符类的信息,请参见《Unicode 标准 3.0 版》。 有效标识符的示例包括“identifier1”、“_identifier2”和“@if”。 符合规范的程序中的标识符必须遵循由“Unicode 标准化格式 C”(按“Unicode 标准附录 15”中的定 义)定义的规范格式。当遇到非“标准化格式 C”格式的标识符时,怎样处理它可由 C 的具体实现确定, 但是不要求诊断。

使用前缀“@”可以将关键字用作标识符,这在与其他编程语言建立接口时很有用。字符 @ 并不是标识符的实际组成部分,因此在其他语言中可能将此标识符视为不带前缀的正常标识符。带 @ 前缀的标识符称作逐字标识符 (verbatim identifier)。允许将 @ 前缀用于非关键字的标识符,但是(从代码书写样式的意义上)强烈建议不要这样做。 示例:

class @class
{
public static void @static(bool @bool) {
if (@bool)
System.Console.WriteLine("true");
else
System.Console.WriteLine("false");
}
}
class Class1
{
static void M() {
cl\u0061ss.st\u0061tic(true);
}
}

定义一个名为“class”的类,该类具有一个名为“static”的静态方法,此方法带一个名为“bool” 的参数。请注意,由于在关键字中不允许使用 Unicode 转义符,因此标记“cl\u0061ss”是标识符, 与“@class”标识符相同。

两个标识符如果在按顺序实施了下列转换后相同,则被视为相同:

 如果使用了前缀“@”,移除它。

 将每个 unicode-escape-sequence 转换为它的对应 Unicode 字符。

 移除所有 formatting-character。

包含两个连续下划线字符 (U+005F) 的标识符被保留供具体实现使用。例如,一个实现可以设置它自己的以两个下划线开头的扩展关键字。

⟰ 关键字

关键字 (keyword) 是类似标识符的保留的字符序列,不能用作标识符(以 @ 字符开头时除外)。

keyword: one of
  abstract    as        base       bool          break
  byte        case      catch      char          checked
  class       const     continue   decimal       default
  delegate    do        double     else          enum
  event       explicit  extern     false         finally
  fixed       float     for        foreach       goto
  if          implicit  in         int           interface
  internal    is        lock       long          namespace
  new         null      object     operator      out
  override    params    private    protected     public
  readonly    ref       return     sbyte         sealed
  short       sizeof    stackalloc static        string
  struct      switch    this       throw         true
  try         typeof    uint       ulong         unchecked
  unsafe      ushort    using      virtual       void
  volatile    while

 在文法中的某些位置,特定的标识符有特殊的含义,但不是关键字。这类标识符有时称为“上下文关键字”。例如,在属性声明中,“get”和“set”标识符有特殊的含义。在这些位置从不允许使用 get 或 set 之外的标识符,因此此用法与这些词用作标识符并不冲突。在其他情况下,如对于隐式类型化局部变量声明中的标识符“var”,上下文关键字可能与声明的名称冲突。 在这类情况下,声明的名称优先于将标识符用作上下文关键字。

⟰ 文 本

文本 (literal) 是一个值的源代码表示形式。

⟰ 运算符和标点符号

有若干种运算符和标点符号。运算符在表达式中用于描述涉及一个或多个操作数的运算例如,表达式 a + b 使用 + 运算符添加两个操作数 a 和 b标点符号用于分组和分隔

operator-or-punctuator: one of
{        }       [        ]     (      )      .      ,      :       ;
+        -       *        /     %      &      |      ^      !       ~
=        <       >        ?     ??     ::     ++     --     &&      ||
->       ==      !=       <=     >=    +=    -=      *=     /=      %= 
&= |= ^= << <<= =>
right
-shift:
>|>
right
-shift-assignment:
>|>=

 right-shift 和 right-shift-assignment 产生式中的竖线用来表示:和采用句法文法的其他产生式不同,在标记之间不允许有任何类型的字符(甚至不允许空白)。为了能正确处理 type-parameter-list,要对这些产生式进行特殊处理。

➤ 预处理指令

预处理指令提供按条件跳过源文件中的节、报告错误和警告条件,以及描绘源代码的不同区域的能力。 使用术语“预处理指令”只是为了与 C 和 C++ 编程语言保持一致。在 C# 中没有单独的预处理步骤;预 处理指令按词法分析阶段的一部分处理。

posted @ 2021-01-29 15:32  TechSingularity  阅读(271)  评论(0编辑  收藏  举报