词法
News
[Home] [Search] [D] Last update Aug 3, 2004
词法
在 D 中,词法分析独立于语法分析和语义分析。词法分析器将源文件分割成记号。词法描述了如何识别记号。D 的词法被设计为适于高速扫描,它拥有最小的特殊规则集合,只有一遍翻译,这使得构造一个正确的扫描程序很容易。对于熟悉 C 和 C++ 的人来说, 记号也很容易识别。编译的阶段
编译被分为多个阶段。ÿ个阶段都不依赖于后继的阶段。例如,扫描程序不依赖于语义分析程序。这种分离使语法制导编辑器等语言工具相对容易构造。这也使通过将其存储为‘符号’形式来压缩 D 源码成为可能。- 源码字符集
先检查源文件使用的是什ô字符集,然后使用合适的扫描程序。可以使用 ASCII 或 UTF 格式。 - 词法分析
源文件被分割为记号序列。特殊记号会被处理,然后删除。 - 语法分析
符号序列被解析为语法树。 - 语义分析
遍历语法树,声明变量、载入符号表、分配型别并从大体上决定程序的意义。 - 优化
优化是可选的一步,它试图语义等价的重写程序,但是生成一个更为快速的版本。 - 代码生成
采用目标架构的指令来实现程序的语义。典型的结果是生成一个目标文件,它会作为连接器的输入。
源码文本
D 源码文本可以是下面各种形式之一:- ASCII
- UTF-8
- UTF-16BE
- UTF-16LE
- UTF-32BE
- UTF-32LE
| 格式 | BOM |
|---|---|
| UTF-8 | EF BB BF |
| UTF-16BE | FE FF |
| UTF-16LE | FF FE |
| UTF-32BE | 00 00 FE FF |
| UTF-32LE | FF FE 00 00 |
| ASCII | no BOM |
D 中û有“双连符”或者“三连符” 。(译注:三连符是一些由 ?? 开头的连续的三字符组合,它包括 ??=,??/,??',??(,??),??!,??<,??>和??-,这些字符将被直接替换为对应的字符,分别为#,,^,[,],|,和~。引入三连符是为了方便的输入这些字符,早期有些键盘不支持它们。双连符同理。显然 Walter 认为这些东西早就过时了。)
源代码文档由 空白、行β、注释、特殊记号序列、记号等组成,结β处必须是 文件β 。
应使用贪心算法将源代码文档分割为记号,也就是词法分析器ÿ次都试图生成一个最长的符号。例如:>> 是一个右移运算符,而不是两个大于运算符。
文件β
文件β:
文件的物理结β \u0000 \u001A
EndOfFile:在遇到上述之一时认为文件终止。
physical end of the file \u0000 \u001A
行β
行β:
\u000D
\u000A
\u000D \u000A
文件β
EndOfLine:不允许用反斜线来将一行分为多行,行长度也û有限制。
\u000D
\u000A
\u000D \u000A
EndOfFile
空白
空白:
空格 空格 空白 空格:
\u0020
\u0009
\u000B
\u000C
行β 注释
WhiteSpace:空白被定义为一系列的一个或多个空格、制表符、垂直制表符、表格填充、行β或者注释。
Space Space WhiteSpace Space:
\u0020
\u0009
\u000B
\u000C
EndOfLine Comment
注释
注释:
/* 字符 */ // 字符 行β /+ 字符 +/
Comment:D 有三种注释:
/* Characters */ // Characters EndOfLine /+ Characters +/
- 块注释可以跨越多行,但是不能嵌套。
- 单行注释在行β结束。
- 嵌套注释可以跨越多行并且可以嵌套。
a = /+ // +/ 1; // 解析为 'a = 1;' a = /+ "+/" +/ 1"; // 解析为 'a = " +/1";' a = /+ /* +/ */ 3; // 解析为 'a = */ 3;'注释不能被用作记号连接符,例如
abc/**/def 是两个符号,abc 和 def ,而不是记号 abcdef 。
记号
记号:
标志符 字符串文字量 字符文字量 整数文字量 浮点数文字量 关键字 / /= . .. ... & &= && | |= || - -= -- + += ++ < <= << <<= <> <>= > >= >>= >>>= >> >>> ! != !== !<> !<>= !< !<= !> !>= ( ) [ ] ? , ; : $ = == === * *= % %= ^ ^= ~ ~=
Token:
Identifier StringLiteral CharacterLiteral IntegerLiteral FloatLiteral Keyword / /= . .. ... & &= && | |= || - -= -- + += ++ < <= << <<= <> <>= > >= >>= >>>= >> >>> ! != !== !<> !<>= !< !<= !> !>= ( ) [ ] ? , ; : $ = == === * *= % %= ^ ^= ~ ~=
标志符
标志符:
标志符起始 标志符起始 多个标志符字符 多个标志符字符:
标志符字符 标志符字符 多个标志符字符 标志符起始:
_ 字母 通用字母 标志符字符:
标志符起始 数字
Identifier:标志符由一个字母、下划线或者一个 unicode 字母开头,后面跟着任意个字母、下划线、数字或者通用字母。通用字母的定义请参考 ISO/IEC 9899:1999(E) 附¼ D 。(这是 C99 标准) 标志符长度任意,并且区分大小写。以两个下划线开头的标志符是保留的。
IdentiferStart IdentiferStart IdentifierChars IdentifierChars:
IdentiferChar IdentiferChar IdentifierChars IdentifierStart:
_ Letter UniversalAlpha IdentifierChar:
IdentiferStart Digit
字符串文字量
字符串文字量:
所见即所得字符串 替代所见即所得字符串 双引号字符串 转义序列 十六进制字符串 所见即所得字符串:
r" 多个所见即所得字符 " 替代所见即所得字符串:
` 多个所见即所得字符 ` 所见即所得字符:
字符 行β 双引号字符串:
" 多个双引号字符 " 双引号字符:
字符 转义序列 行β 转义序列:
\' \" \? \\ \a \b \f \n \r \t \v \ 文件β \x 十六进制数字 十六进制数字 \ 八进制数字 \ 八进制数字 八进制数字 \ 八进制数字 八进制数字 八进制数字 \u 十六进制数字 十六进制数字 十六进制数字 十六进制数字 \U 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制字符串:
x" 多个十六进制字符串字符 " 多个十六进制字符串字符:
十六进制数字 空白 行β
StringLiteral:字符串文字量可以是一个双引号字符串、一个所见即所得引号字符串、一个转义序列、或者一个十六进制字符串。
WysiwygString AlternateWysiwygString DoubleQuotedString EscapeSequence HexString WysiwygString:
r" WysiwygCharacters " AlternateWysiwygString:
` WysiwygCharacters ` WysiwygCharacter:
Character EndOfLine DoubleQuotedString:
" DoubleQuotedCharacters " DoubleQuotedCharacter:
Character EscapeSequence EndOfLine EscapeSequence:
\' \" \? \\ \a \b \f \n \r \t \v \ EndOfFile \x HexDigit HexDigit \ OctalDigit \ OctalDigit OctalDigit \ OctalDigit OctalDigit OctalDigit \u HexDigit HexDigit HexDigit HexDigit \U HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexString:
x" HexStringChars " HexStringChar HexDigit WhiteSpace EndOfLine
所见即所得引号字符串由‘r"’和‘"’包Χ起来。所有λ于‘r"’和‘"’之间的字符都是字符串的一部分,只有 行β 除外,他被视作一个‘\n’字符。在 r" " 中û有转义序列:
r"hello" r"c:\root\foo.exe" r"ab\n" // 由四个字符组成的字符串:'a'、'b'、'\'、'n'所见即所得字符串的另一种形式是使用反引号‘`’代替双引号。‘`’字符并不是所有的键盘上都有,而且有时在屏幕上难以同另一个常用的字符‘'’区分。尽管‘`’很少使用,当用在包含‘"’的字符串时就会体现出它的价值。
`hello` `c:\root\foo.exe` `ab\n` // 由四个字符组成的字符串:'a'、'b'、'\'、'n'双引号字符串是用‘""’包Χ起来的字符串。可以使用典型的‘\’记号在其中嵌入转义序列。行β 被视作一个‘\n’字符。
"hello" "c:\\root\\foo.exe" "ab\n" // 由三个字符组成的字符串:'a'、'b'和一个新行符 "ab转义字符串由‘\’开始,他和其后的字符构成了一个转义字符序列。相邻的转义字符串会被连接在一起:
" // 由三个字符组成的字符串:'a'、'b'和一个新行符
\n // 新行符 \t // 制表符 \" // 双引号 \012 // 八进制 \x1A // 十六进制 \u1234 // wchar 字符 \U00101234 // dchar 字符 \r\n // 回车换行除了上面列出的,其他的转义序列都是非法的。
十六进制字符串使用十六进制数据构造字符串:
x"0A" // 等价于 "\x0A" x"00 FBCD 32FD 0A" // 等价于 "\x00\xFB\xCD\x32\xFD\x0A"空白和新行符会被忽略,因此可以很方便的格式化。十六进制字符的个数必须是2的倍数。
相邻的字符串应该用 ~ 运算符连接,或者也可以仅仅并置即可:
"hello " ~ "world" ~ \n // 构成字符串:'h','e','l','l','o',' ','w','o','r','l','d',字符串下面的形式都是等价的:
"ab" "c" r"ab" r"c" r"a" "bc" "a" ~ "b" ~ "c" \x61"bc"
字符文字量
字符文字量:
' 单引号字符 ' 单引号字符 字符 转义序列
CharacterLiteral:字符文字量是单个的字符或者由单引号括起来的转义序列,' ' 。
' SingleQuotedCharacter ' SingleQuotedCharacter Character EscapeSequence
整数文字量
整数文字量:
整数 整数 整数后缀 整数:
十进制数 二进制数 八进制数 十六进制数 整数 _ 整数后缀:
l L u U lu Lu lU LU ul uL Ul UL 十进制数:
0 非零数字 非零数字 十进制数 非零数字 _ 十进制数 二进制数:
0b 二进制数字 0B 二进制数字 八进制数:
0 八进制数字 十六进制数:
0x 十六进制数字 0X 十六进制数字
IntegerLiteral:整数可以采用十进制、二进制、八进制或者十六进制。
Integer Integer IntegerSuffix Integer:
Decimal Binary Octal Hexadecimal Integer _ IntegerSuffix:
l L u U lu Lu lU LU ul uL Ul UL Decimal:
0 NonZeroDigit NonZeroDigit Decimal NonZeroDigit _ Decimal Binary:
0b BinaryDigits 0B BinaryDigits Octal:
0 OctalDigits Hexadecimal:
0x HexDigits 0X HexDigits
十进制整数是十进制数字的序列。
二进制整数是二进制数字的序列,以‘0b’为前缀。
八进制整数是八进制数字的序列,以‘0’为前缀。
十六进制整数是十六进制数字的序列,以‘0x’为前缀,或者使用‘h’作为后缀。
整数可以内嵌 '_' 字符,它们会被忽略。嵌入的 '_' 可以用于格式化较长的文字量,例如作为千λ分隔符:
123_456 // 123456 1_2_3_4_5_6_ // 123456整数后可以紧跟着一个 'l' 或者一个 'u' 或者两者都有。
整数的类型按照下述规则判断:
- 如果是十进制的,它是 ulong、long、int 中从后向前找到的第一个可以表示它的类型。
- 如果不是十进制的,它是 ulong、long、uint、int 中从后向前找到的第一个可以表示它的类型。
- 如果它有后缀‘u’,它是 ulong、uint 中从后向前找到的第一个可以表示它的类型。
- 如果它有后缀‘l’,它是 ulong、long 中从后向前找到的第一个可以表示它的类型。
- 如果它同时拥有后缀‘u’和‘l’,它是 ulong 类型。
浮点数文字量
浮点数文字量:
浮点数 浮点数 浮点数后缀 浮点数 虚数后缀 浮点数 浮点数后缀 虚数后缀 浮点数:
十进制浮点数 十六进制浮点数 浮点数 _ 浮点数后缀:
f F l L 虚数后缀:
i I
FloatLiteral:浮点数可以使用十进制或者十六进制格式,如同标准 C 一样。
Float Float FloatSuffix Float ImaginarySuffix Float FloatSuffix ImaginarySuffix Float:
DecimalFloat HexFloat Float _ FloatSuffix:
f F l L ImaginarySuffix:
i I
十六进制浮点数以 0x 开头,阶码以 p 或者 P 开头,后面跟着以 2 为底的阶数。
浮点文字量可以有嵌入的‘_’字符,它们会被忽略。嵌入的‘_’用来格式化冗长的文字量以提高可读性,例如可以将它们用作千λ分隔符:
123_456.567_8 // 123456.5678 1_2_3_4_5_6_._5_6_7_8 // 123456.5678 1_2_3_4_5_6_._5e-6_ // 123456.5e-6浮点数可以跟随有一个 f、F、l 或者 L 后缀。f 或 F 后缀说明这是一个浮点数,l 或 L 说明这是一个扩展格式浮点数。
如果浮点文字量后面跟着 i 或者 I ,那ô它就是一个 ireal (虚数)类型。
示例:
0x1.FFFFFFFFFFFFFp1023 // double.max 0x1p-52 // double.epsilon 1.175494351e-38F // float.min 6.3i // idouble 6.3 6.3fi // ifloat 6.3 6.3LI // ireal 6.3如果文字量超出了该类型的表示范Χ,会被视为错误。如果文字量取整后可以用该类型的有效λ数字表示,就不是错误。
复数文字量不是记号,而是在语义分析时用实数和虚数表达式构造的:
4.5 + 6.2i // 复数
关键字
关键字是保留的标志符:Keyword:
abstract alias align asm assert auto bit body break byte case cast catch cent char class cfloat cdouble creal const continue dchar debug default delegate delete deprecated do double else enum export extern false final finally float for foreach function goto idouble if ifloat import in inout int interface invariant ireal is long mixin module new null out override package pragma private protected public real return short static struct super switch synchronized template this throw true try typedef typeof ubyte ucent uint ulong union unittest ushort version void volatile wchar while with
特殊记号序列
特殊记号序列由词法分析程序处理,它可以出现在其他记号之间,并且不影响语法分析。目前只有一个特殊记号序列,#line 。
特殊记号序列 # line 整数 行β # line 整数 Filespec 行β 指定文件 " 字符 "
SpecialTokenSequence # line Integer EndOfLine # line Integer Filespec EndOfLine Filespec " Characters "它会将源代码的行号设置为 整数 的值,将源代码文件名设置为可选的 Filespec 的值,从源码文本的下一行生效。与码文件名和行号用于打印调试信息,还被符号调试器用于将生成的代码映射回源代码。
例如:
int #line 6 "foo\bar" x; // 这里是文件 foo\bar 的第6行注意,Filespec 字符串中的反斜杠不会被特殊对待。
Feedback and Comments
联系我:Copyright (c) 1999-2004 by Digital Mars, All Rights Reserved
