C#-词法结构

程序

C# 程序 (program) 由一个或多个源文件 (source file) 组成,源文件的正式名称是编译单元 (compilation unit)。源文件是有序的 Unicode 字符序列。
源文件与文件系统中的文件通常具有一对一的对应关系,但这种对应关系不是必需的。为实现可移植性的最大化,建议这些文件在文件系统中应按 UTF-8 编码规范编码。
从概念上讲,程序的编译分三个步骤:
1> 转换 : 这一步将用特定字符指令系统和编码方案编写的文件转换为 Unicode 字符序列
2> 词法分析 : 这一步将 Unicode 输入字符流转换为标记流
3> 句法分析 : 这一步将标记流转换为可执行代码

文法

本规范采用两种文法 (grammar) 来表示 C# 编程语言的语法 (syntax)。
词法文法 (lexical grammar)规定怎样将 Unicode 字符组合成行结束符、空白、注释、标记和预处理指令等。
句法文法 (syntactic grammar)规定如何将那些由词法文法产生的标记组合成 C# 程序。

1> 文法表示法

词法文法和句法文法用文法产生式 (grammar production) 来表示。每个文法产生式定义一个非结束符号和它可能的扩展(由非结束符或结束符组成的序列)。在文法产生式中,non-terminal 符号显示为斜体,而 terminal 符号显示为等宽字体。
文法产生式的第一行是该产生式所定义的非结束符号的名称,后跟一个冒号。每个后续的缩进行列出一个可能的扩展,它是以非结束符或结束符组成的序列的形式给出的。
例如,产生式:

while-statement:
    while(boolean-expression) embedded-statement

定义了一个 while-statement,它是这样构成的:由标记 while 开始,后跟标记“(”、boolean-expression、标记“)”,最后是一个 embedded-statement。
当有不止一个可能的非结束符号扩展时,列出这些可能的扩展(每个扩展单独占一行)。
例如,产生式:

statement-list:
    statement
    statement-list statement

定义一个 statement-list,它或仅含有一个 statement,或由一个 statement-list 和随后跟着的一个 statement 组成。换言之,定义是递归的,语句列表由一个或多个语句组成。
一个符号若以下标“[opt]”作其后缀,就表明该符号是可选的。
产生式:

block:
    {
    statement-list[opt] }

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

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

2> 词法文法

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

3> 句法文法

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

语法分析

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

input:
    input-section[opt]
input-section:
    input-section-part
    input-section input-section-part
input-section-part:
    input-elements[opt] new-line
    pp-directive
input-elements:
    input-element
    input-elements input-element
input-element:
    whitespace
    comment
    token

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

1> 行结束符

行结束符将 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# 程序中的每个源文件:
<1> 如果源文件的最后一个字符为 Control-Z 字符 (U+001A),则删除此字符。
<2> 如果源文件非空并且源文件的最后一个字符不是回车符 (U+000D)、换行符 (U+000A)、行分隔符 (U+2028) 或段落分隔符 (U+2029),则将在源文件的结尾添加一个回车符 (U+000D)。

2> 注释

支持两种形式的注释:单行注释和带分隔符的注释。单行注释 (Single-line comment) 以字符 // 开头并延续到源行的结尾。带分隔符的注释 (Delimited comment) 以字符 /* 开头,以字符 */ 结束。带分隔符的注释可以跨多行。

comment:
    single-line-comment
    delimited-comment
single-line-comment:
    // input-characters[opt]
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-text[opt] asterisks /
delimited-comment-text:
    delimited-comment-section
    delimited-comment-text delimited-comment-section
delimited-comment-section:
    /
    asterisks[opt] 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");
    }
}

演示了若干单行注释。

3> 空白

空白被定义为任何含 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

1> 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());
        }
    }
}

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

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

2> 标识符

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

identifier:
    available-identifier
    @ identifier-or-keyword
available-identifier:
    An identifier-or-keyword that is not a keyword
identifier-or
  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LL分析是一种自顶向下的语法分析方法,通常用于解析上下文无关文法(CFG)描述的语言。在C#中,可以使用ANTLR工具生成LL分析器。 以下是一个简单的C# LL分析器示例代码: ```csharp using Antlr4.Runtime; using Antlr4.Runtime.Tree; public class MyParser { public void Parse(string input) { var inputStream = new AntlrInputStream(input); var lexer = new MyLexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new MyParser(tokenStream); var tree = parser.start(); var visitor = new MyVisitor(); visitor.Visit(tree); } } public class MyVisitor : MyParserBaseVisitor<object> { public override object VisitProgram(MyParser.ProgramContext context) { // do something return base.VisitProgram(context); } public override object VisitStatement(MyParser.StatementContext context) { // do something return base.VisitStatement(context); } public override object VisitExpression(MyParser.ExpressionContext context) { // do something return base.VisitExpression(context); } } public class MyLexer : Lexer { // define lexer rules } public class MyParser : Parser { // define parser rules } ``` 在这个示例中,我们定义了一个名为`MyParser`的类来解析输入字符串。该类使用ANTLR工具生成的词法分析器和语法分析器。在`Parse`方法中,我们首先将输入字符串转换为`AntlrInputStream`,然后使用`MyLexer`将其解析为一系列令牌。接下来,我们通过将令牌传递给`MyParser`来使用语法分析器生成语法分析树。最后,我们使用`MyVisitor`类来遍历语法分析树并执行相应的操作。 在`MyVisitor`类中,我们覆盖了一些方法来处理不同类型的语法结构。例如,`VisitProgram`方法处理程序的整个内容,`VisitStatement`方法处理语句,`VisitExpression`方法处理表达式等等。在这些方法中,我们可以编写自己的操作以处理语法结构。 在`MyLexer`和`MyParser`类中,我们定义了词法规则和语法规则。词法规则定义了令牌的类型,例如标识符、关键字、运算符等等。语法规则定义了语法结构,例如程序、函数、语句、表达式等等。ANTLR工具会使用这些规则生成词法分析器和语法分析器的代码。 这只是一个简单的示例,实际上LL分析器可以处理更复杂的语法。如果你想深入了解LL分析器的工作原理和实现方法,可以参考相关的计算机科学课程和教材。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值