词法 2.014

在 D 中,词法分析独立于语法分析和语义分析。词法分析器是将源文件分割成特征符。词
法描述的是特征符是些什么。D 的词法设计适合高速扫描,它拥有最小的特例集;由于只有
一遍翻译,使得编写一个正确的扫描程序相当容易。对于熟悉 C 和 C++ 的人来说,特征符
也很容易识别。

1.1 编译的阶段
编译被分为多个阶段。每个阶段都不依赖于后继的阶段。例如,扫描程序不依赖于语义分析
程序。这种分离使语法制导编辑器等语言工具相对容易构造。这也使通过将其存储为‘符
号’形式来压缩 D 源码成为可能。

1. 源文件字符集(source character set)
先检查源文件使用的字符集是什么,然后装载相应的扫描器(scanner)。允许的格式是
ASCII 或 UTF。

2. 脚本行(script line)
如果第一行以 #! 开头,那么第一行将被忽略。

3. 词法分析(lexical analysis)
源文件被分割为特征符序列。特殊特征符会被替换成其它的特征符。特殊特征符序列
先会被处理,接着被移除。

4. 语法分析(syntax analysis)
特征符序列会被解析为语法树。

5. 语义分析(semantic analysis)
通过遍历语法树来声明变量、载入符号表、分配类型并在大体上判断程序的意图。

6. 优化(optimization)
这步是可选的——它试着在保持语义等价的同时重写程序,只是生成一个运行速度更
快的版本。

7. 代码生成(code generation)
根据目标架构选取相应的指令来实现程序的语义。通常的结果是生成一个目标文件,
方便用于连接器的输入。

1.2 源文本(Source Text)
D 源文本可以是下面各种形式之一:
• ASCII
• UTF-8
• UTF-16BE
• UTF-16LE
• UTF-32BE
• UTF-32LE
UTF-8 是传统的“7-位 ASCII”的超集(superset)。源文本的开头可以是下列 UTF 字节序标志
(BOM)之一:
格式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
如果源文件不是以 BOM 开头,那么第一个字符必须小于或等于 U0000007F。
在 D 中没有“双连字(digraphs)”或者“三连字(trigraphs)”。
源文本的源表示(source representation)被解码成 Unicode 字符。这些 字符 又被进一步分
成: 空白符、行尾符、注释符、特殊特征符序列、特征符,以及跟在前面那些字符之后的
文尾符。
应用贪心算法(即:词法分析器每次都尽量生成一个最长的特征符)将源代码文本分割成很
多特征符。如:“>>”是一个右移符号,而不是两个大于符号。
1.3 文尾符(End of File)
文尾符:
文件的物理结束符
\u0000
\u001A
在遇到上述符号之一时就认为文件终止。

1.4 行尾符(End of Line)
行尾符:
\u000D
\u000A
\u000D \u000A
文尾符
不允许用反斜线来将一行分为多行,每行的长度也没有限制。

1.5 空白符(White Space)
空白:
空格
空格 空白符
空格:
\u0020
\u0009
\u000B
\u000C
1.6 注释(Comments)
注释:
/* 字符 */
// 字符 行尾符
嵌套注释块
字符:
单个字符
单个字符 多个字符
嵌套注释块:
/+ 嵌套注释块字符 +/
嵌套注释块字符:
嵌套注释块单个字符
嵌套注释块单个字符 嵌套注释块多个字符
嵌套注释块单个字符:
单个字符
嵌套注释块
D 语言有三种注释:
1. 可以跨越多行,但是不能嵌套的块注释。
2. 在行尾结束的单行注释。
3. 可以跨越多行并且可以嵌套的嵌套注释。
字符串和注释的内容不会被分析成特征符。因此,在一个字符串内注释起始符并不会引导一
个注释,并且在注释内的字符串分割符也不会影响对注释结尾符以及嵌套的"/+"注释起始符
的识别。为了避免"/+"出现在"/+"注释中,在注释里的注释起始符将会被忽略。
a = /+ // +/ 1; // 解析为 'a = 1;'
a = /+ "+/" +/ 1"; // 解析为 'a = " +/ 1";'
a = /+ /* +/ */ 3; // 解析为 'a = */ 3;'
注释不能被用作连接符,例如:abc/**/def 是两个特征符:abc 和 def,而不是只有一
个 abcdef。

1.7 特征符(Tokens)
特征符:
标识符
单个字符串文字
字符文字
整数文字
浮点数文字
关键字


/
/=
.
..
...
&
&=
&&
|
|=
||
-
-=
--
+
+=
++
<
<=
<<
<<=
<>
<>=
>
>=
>>=
>>>=
>>
>>>
!
!=
!<>
!<>=
!<
!<=
!>
!>=
(
)
[
]
{
}
?
,
;
:
$
=
==
*
*=
%
%=
^
^=
~
~=

1.8 标识符(Identifiers)
标识符:
标志符起始符
标志符起始符 多个标志符字符
多个标志符字符:
单个标志符字符
单个标志符字符 多个标志符字符
标志符起始符:
_
字母
通用字母
单个标志符字符:
标志符起始符
0
非零数字
标识符的起始符由一个字母、_ 或通用字母组成,紧跟的字符可以是字母、_、数字或通用
字母。通用字母在ISO/IEC 9899:1999(E)的附录 D(即 C99 标准)中有定义。标识符长度任
意,并且区分大小写。以'__ (两个下划线)'开头的标识符被保留了。

1.9 字符串字法(String Literals)
字符串文字:
所见即所得字符串
另一种所见即所得字符串
双引号字符串
转义序列
十六进制字符串

[color=red]DelimitedString 2.0
TokenString 2.0[/color]


所见即所得字符串:
r" 多个所见即所得字符 " 后缀可选的
另一种所见即所得字符串:
` 所见即所得字符 ` 后缀可选的

多个所见即所得字符:
单个所见即所得字符
单个所见即所得字符 多个所见即所得字符
所见即所得字符:
单个字符
行尾符
双引号字符串:
" 多个双引号字符 " 后缀可选的
多个双引号字符:
单个双引号字符
单个双引号字符 多个双引号字符
单个双引号字符:
单个字符
转义序列
行尾符
转义序列:
\'
\"
\?
\\
\a
\b
\f
\n
\r
\t
\v
\ 文尾符
\x 单个十六进制数字 单个十六进制数字
\ 单个八进制数字
\ 单个八进制数字 单个八进制数字
\ 单个八进制数字 单个八进制数字 单个八进制数字
\u 单个十六进制数字 单个十六进制数字 单个十六进制数字 单个十六进制数字
\U 单个十六进制数字 单个十六进制数字 单个十六进制数字 单个十六进制数字 单个十六进制数
字 单个十六进制数字 单个十六进制数字 单个十六进制数字
\& 命名的字符实体 ;
十六进制字符串:
x" 多个十六进制串字符 " 后缀
可选的
多个十六进制串字符:
单个十六进制串字符
单个十六进制串字符 多个十六进制串字符

单个十六进制串字符:
单个十六进制数字
空白
行尾符

后缀:
c
w
d

[color=red]DelimitedString: 2.0
q" Delimiter WysiwygCharacters MatchingDelimiter " 2.0

TokenString: 2.0
q{ Tokens } 2.0[/color]


字符串文字可以是一个双引号字符串、一个引起来的所见即所得字符串、一个转义序列、[color=red]a delimited string, a token string,(2.0)[/color] 或
者一个十六进制字符串。
所见即所得字符串需要使用‘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 8进制
\x1A 十六进制
\u1234 wchar 字符
\U00101234 dchar 字符
\® ® dchar 字符
\r\n 回车换行
其它未定义的转义序列都是错误的。虽然字符串文字被定义为由 UTF 字符组成,但也允许
使用八进制和十六进制转义序列符插入任意二进制数据。\u 和 \U 转义序列符仅被用来插入

有效的 UTF 字符。
十六进制字符串使用十六进制数据构造字符串。十六进制数据不需要组成有效的 UTF 字
符。

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"

可选的 后缀 字符产生了特定类型的字符串,而且还需要由上下文来确定。这在字符类型不
能清楚地确定时相当有用,例如,基于字符串类型的重载。后缀字符的种类有:
后缀类型
c char[ ]
w wchar[ ]
d dchar[ ]
"hello"c // char[]
"hello"w // wchar[]
"hello"d // dchar[]
字符串文字是只读的。对字符串文字的写入并不能总是被检测到,而写入的话会引起“未定
义的行为(undefined behavior)”。


 2.014 内容
Delimited Strings
Delimited strings use various forms of delimiters. A nesting delimiter nests, and is one of the following characters:

Nesting Delimiters Delimiter Matching Delimiter
[ ]
( )
< >
{ }

q"(foo(xxx))" // "foo(xxx)"
q"[foo{]" // "foo{"
If the delimiter is an identifier, the identifier must be immediately followed by a newline, and the matching delimiter is the same identifier starting at the beginning of the line:

writefln(q"EOS
This
is a multi-line
heredoc string
EOS"
);
The newline following the opening identifier is not part of the string, but the last newline before the closing identifier is part of the string.

Otherwise, the matching delimiter is the same as the delimiter character:

q"/foo]/" // "foo]"
q"/abc/def/" // error
Token Strings
Token strings open with the characters q{ and close with the token }. In between must be valid D tokens. The { and } tokens nest. The string is formed of all the characters between the opening and closing of the token string, including comments.

q{foo} // "foo"
q{/*}*/ } // "/*}*/ "
q{ foo(q{hello}); } // " foo(q{hello}); "
q{ @ } // error, @ is not a valid D token
q{ __TIME__ } // " __TIME__ ", i.e. it is not replaced with the time
q{ __EOF__ } // error, as __EOF__ is not a token, it's end of file


10 字符字法(Character Literals)
字符字法:
' 单引号字符 '
单引号字符:
单个字符
转义序列

字符文字是单个的字符或者由单引号' '括起来的转义序列。

11 整数字法(Integer Literals)
整数字法:
整数
整数 整数后缀
整数:
十进制数
二进制数
八进制数
十六进制数
整数后缀:
L
u
U
Lu
LU
uL
UL
十进制数:
0
非零数字
非零数字 多个十进制数字
二进制数:
0b 多个二进制数字
0B 多个二进制数字
八进制数:
0 多个八进制数字
十六进制数:
0x 多个十六进制数字
0X 多个十六进制数字
非零数字:
1
2
3
4
5
6
7
8
9
多个十进制数字:
单个十进制数字
单个十进制数字 多个十进制数字
9
第 1 章 词法 — 张雪平
单个十进制数字:
0
非零数字
_
多个二进制数字:
单个二进制数字
单个二进制数字 多个二进制数字
单个二进制数字:
0
1
_
多个八进制数字:
单个八进制数字
单个八进制数字 多个八进制数字
单个八进制数字:
0
1
2
3
4
5
6
7
_
多个十六进制数字:
单个十六进制数字
单个十六进制数字 多个十六进制数字
单个十六进制数字:
单个十进制数字
a
b
c
d
e
f
A
B
C
D
E
F
_

整数可以采用十进制、二进制、八进制或者十六进制。
十进制整数是十进制数字的序列。
二进制整数是二进制数字的序列,以‘0b’为前缀。
八进制整数是八进制数字的序列,以‘0’为前缀。
十六进制整数指的是一个以‘0x’为前缀的十六进制数字序列。
整数可以内嵌 '_' 字符,它们会被忽略。嵌入的 '_' 可以用于格式化较长的文字,例如作为千位分隔符:
123_456 // 123456
1_2_3_4_5_6_ // 123456
整数后可以紧跟着一个 'l' 或者一个 'u' 或者两者都有。
整数的类型按照下述规则判断:
十进制范围类型
0 .. 2_147_483_647 int
2_147_483_648 .. 9_223_372_036_854_775_807L long
十进制范围,带L后缀类型
0L .. 9_223_372_036_854_775_807L long
十进制范围,带U后缀类型
0U .. 4_294_967_296U uint
4_294_967_296U .. 18_446_744_073_709_551_615UL ulong
十进制范围,带UL后缀类型
0UL .. 18_446_744_073_709_551_615UL ulong
非十进制范围类型
0x0 .. 0x7FFF_FFFF int
0x8000_0000 .. 0xFFFF_FFFF uint
0x1_0000_0000 .. 0x7FFF_FFFF_FFFF_FFFF long
0x8000_0000_0000_0000 .. 0xFFFF_FFFF_FFFF_FFFF ulong
非十进制范围,带L后缀类型
0x0L .. 0x7FFF_FFFF_FFFF_FFFFL long
0x8000_0000_0000_0000L .. 0xFFFF_FFFF_FFFF_FFFFL ulong
非十进制范围,带U后缀类型
0x0U .. 0xFFFF_FFFFU uint
0x1_0000_0000UL .. 0xFFFF_FFFF_FFFF_FFFFUL ulong
非十进制范围,带UL后缀类型
0x0UL .. 0xFFFF_FFFF_FFFF_FFFFUL ulong

1.12 浮点数字法(Floating Literals)
浮点数字法:
浮点数
浮点数 后缀
整数 虚数后缀
整数 浮点后缀 虚数后缀
整数 实数后缀 虚数后缀
浮点数:
十进制浮点数
十六进制浮点数
十进制浮点数:
多个十进制数字 .
多个十进制数字 .多个十进制数字
多个十进制数字 .多个十进制数字 十进制指数
. 十进制数
. 十进制数 十进制指数
多个十进制数字 十进制指数
十进制指数
e 多个十进制数字
E 多个十进制数字
e+ 多个十进制数字
E+ 多个十进制数字
e- 多个十进制数字
E- 多个十进制数字
十六进制浮点数:
十六进制前缀 多个十六进制数字 .多个十六进制数字 十六进制指数
十六进制前缀 .多个十六进制数字 十六进制指数
十六进制前缀 多个十六进制数字 十六进制指数
十六进制前缀:
0x
0X
十六进制指数:
p 多个十六进制数字
P 多个十六进制数字
p+ 多个十六进制数字
P+ 多个十六进制数字
p- 多个十六进制数字
P- 多个十六进制数字
后缀:
浮点后缀
实数后缀
虚数后缀
浮点后缀 虚数后缀
实数 虚数后缀
浮点后缀:
f
F
实数后缀:
L
虚数后缀:
i
浮点数可以使用十进制或者十六进制格式,如同标准 C 一样。
十六进制浮点数以 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
不带后缀浮点文字类型是 double。浮点数可以跟随有一个 f、F 或 L 后缀。f 或 F 后缀说明是fload;L 代表 real。
如果浮点文字后面跟着 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 // 复数

1.13 关键字(Keywords)
关键字是保留的标识符:

关键字:
abstract
alias
align
asm
assert
auto
body
bool
break
byte
case
cast
catch
cdouble
cent
cfloat
char
class
const
continue
creal
dchar
debug
default
delegate
delete
deprecated
do
double
else
enum
export
extern
false
final
finally
float
for
foreach
foreach_reverse
function
goto
idouble
if
ifloat
import
in
inout
int
interface
invariant
ireal
is
lazy
long
macro
mixin
module

[color=red]nothrow 2.014[/color]

new
null
out
override
package
pragma
private
protected
public

[color=red]pure 2.014[/color]
real
ref
return
scope
short
static
struct
super
switch
synchronized
template
this
throw
true
try
typedef
typeid
typeof
ubyte
ucent
uint
ulong
union
unittest
ushort
version
void
volatile
wchar
while
with

[color=red] __FILE__ 2.014
__LINE__ 2.014
__traits 2.014[/color]

1.14 特殊特征符(Special Tokens)
这些特征符会根据下面的表格替换成其它的特征符:
特殊特征符替换为...
[color=red]__FILE__ 字符串文字,表示的是源文件名 2.014 文档的里没有
__LINE__ 整型文字,表示的是当前源文件的行号 2.014 文档的里没有[/color]
__DATE__ 字符串文字,表示的是编译日期:"mmm dd yyyy"
__TIME__ 字符串文字,表示的是编译时间:"hh:mm:ss"
__TIMESTAMP__ 字符串文字,表示的是编译日期和时间:"www mmm dd hh:mm:ss
yyyy"
__VENDOR__ 字符串文字,表示的是编译器服务商,如"Digital Mars D"
__VERSION__ 整数,表示的是编译器版本,如:2001

1.15 特殊特征符序列(Special Token Sequences)
特殊特征符序列由词法分析程序处理,它可以出现在其他特征符之间,并且不影响语法分
析。
目前只有一个特殊特征符序列,#line。
特殊特征符序列:
# line 整数 行尾
# line 整数 指定文件 行尾
指定文件:
" 多个字符 "
它会将源代码的行号设置为某一个 整数,可选地将源文件名设置为 指定的文件,紧跟在源
文件文本的下一行。与码文件名和行号用于打印调试信息,还被符号调试器用于将生成的代
码映射回源代码。
例如:
int #line 6 "foo\bar"
x; // 这里是文件 foo\bar 的第6行
注意,Filespec 字符串中的反斜杠不会被特殊处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值