发新话题
打印

3 D语言 词法 Lexical

3 D语言 词法 Lexical

知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070416

点击下面网址查看原文:
http://www.d-programming-language-china.org

by: uFramer uframer@sina100.com D语言论坛 http://www.d-programming-language-china.org
from: http://www.digitalmars.com/d/lex.html
version: 基于D 1.012 (Apr 12, 2007)

在 D 中,词法分析独立于语法分析和语义分析。词法分析器将源文件分割成记号。词法描述了如何识别记号。D 的词法被设计为适于高速扫描,它拥有最小的特殊规则集合,只有一遍翻译,这使得构造一个正确的扫描程序很容易。对于熟悉 C 和 C++ 的人来说,记号也很容易识别。

编译的阶段

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

1 源码字符集

先检查源文件使用的是什么字符集,然后使用合适的扫描程序。可以使用 ASCII 或 UTF 格式。

2 脚本行

如果第一行开始于#!则第一行被忽略。
D语言论坛 http://www.d-programming-language-china.org 按:
还记得本系列 D语言文档的第一篇吗,最后的hello world示例代码的的第一行就是:

#!/usr/bin/dmd -run

3 词法分析

源文件被分割为记号序列。特殊记号会被处理,然后删除。

4 语法分析

符号序列被解析为语法树。( 本文出处: http://www.d-programming-language-china.org )

5 语义分析

遍历语法树,声明变量、载入符号表、分配型别并从大体上决定程序的意义。

6 优化

优化是可选的一步,它试图语义等价的重写程序,但是生成一个更为快速的版本。

7 代码生成

采用目标架构的指令来实现程序的语义。典型的结果是生成一个目标文件,它会作为连接器的输入。

源码文本

D 源码文本可以是下面各种形式之一:
ASCII
UTF-8
UTF-16BE
UTF-16LE
UTF-32BE
UTF-32LE

UTF-8 是传统的7位 ASCII 的超集。 源代码文档的开始可以是下面的任一个 UTF BOMs(字节序标志Byte Order Marks)之一:( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
格式      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 中没有“双连符”或者“三连符” 。(译注:三连符是一些由 ?? 开头的连续的三字符组合,它包括 ??=,??/,??',??(,??),??!,??<,??>和??-,这些字符将被直接替换为对应的字符,分别为#,,^,[,],|,{,}和~。引入三连符是为了方便的输入这些字符,早期有些键盘不支持它们。双连符同理。显然 Walter 认为这些东西早就过时了。)

源文本从源表示法译码为Unicode字符。源代码文档由 空白、行尾、注释、特殊记号序列、记号等组成,结尾处必须是 文件尾 。

应使用贪心算法将源代码文档分割为记号,也就是词法分析器每次都试图生成一个最长的符号。例如:>> 是一个右移运算符,而不是两个大于运算符。

文件尾

文件的物理结尾( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
\u0000
\u001A

在遇到上述之一时认为文件终止。

行尾

QUOTE:
\u000D
\u000A
\u000D \u000A
文件尾

不允许用反斜线来将一行分为多行,行长度也没有限制。

空白

QUOTE:
WhiteSpace:
    Space
    Space WhiteSpace

Space:
    \u0020
    \u0009
    \u000B
    \u000C

注释

QUOTE:
Comment:
    /* Characters */
    // Characters EndOfLine
    NestingBlockComment

Characters:
    Character
    Character Characters

NestingBlockComment:
    /+ NestingBlockCommentCharacters +/

NestingBlockCommentCharacters:
    NestingBlockCommentCharacter
    NestingBlockCommentCharacter NestingBlockCommentCharacters

NestingBlockCommentCharacter:
    Character
    NestingBlockComment

D 有三种注释:

1 块注释可以跨越多行,但是不能嵌套

2 单行注释在行尾结束

3 嵌套注释可以跨越多行并且可以嵌套

从概念上来说,在记号化之前处理注释。这意味着嵌入的字符串和注释不会影响对注释开始和注释结束的识别:

The contents of strings and comments are not tokenized. Consequently, comment openings occurring within a string do not begin a comment, and string delimiters within a comment do not affect the recognition of comment closings and nested "/+" comment openings. With the exception of "/+" occurring within a "/+" comment, comment openings within a comment are ignored.( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
a = /+ // +/ 1;        // 解析为 'a = 1;'
a = /+ "+/" +/ 1";        // 解析为 'a = " +/1";'
a = /+ /* +/ */ 3;        // 解析为 'a = */ 3;'

注释不能被用作记号连接符,例如 abc/**/def 是两个符号,abc 和 def ,而不是记号 abcdef 。

记号

QUOTE:
Token:
    Identifier
    StringLiteral
    CharacterLiteral
    IntegerLiteral
    FloatLiteral
    Keyword
    /
    /=
    .
    ..
    ...
    &
    &=
    &&
    |
    |=
    ||
    -
    -=
    --
    +
    +=
    ++
    <
    <=
    <<
    <<=
    <>
    <>=
    >
    >=
    >>=
    >>>=
    >>
    >>>
    !
    !=
    !<>
    !<>=
    !<
    !<=
    !>
    !>=
    (
    )
    [
    ]
    {
    }
    ?
    ,
    ;
    :
    $
    =
    ==
    *
    *=
    %
    %=
    ^
    ^=
    ~
    ~=

标志符

QUOTE:
Identifier:
    IdentiferStart
    IdentiferStart IdentifierChars

IdentifierChars:
    IdentiferChar
    IdentiferChar IdentifierChars

IdentifierStart:
    _
    Letter
    UniversalAlpha

IdentifierChar:
    IdentiferStart
    0
    NonZeroDigit

标志符由一个字母、下划线或者一个 unicode 字母开头,后面跟着任意个字母、下划线、数字或者通用字母。通用字母的定义请参考 ISO/IEC 9899:1999(E) 附录 D 。(这是 C99 标准) 标志符长度任意,并且区分大小写。以两个下划线开头的标志符是保留的。

字符串文字量

QUOTE:
字符串文字量:
    所见即所得字符串
    替代所见即所得字符串
    双引号字符串
    转义序列
    十六进制字符串

所见即所得字符串:
    r" 多个所见即所得字符 "

替代所见即所得字符串:
    ` 多个所见即所得字符 `

所见即所得字符:
    字符
    行尾

双引号字符串:
    " 多个双引号字符 "

双引号字符:
    字符
    转义序列
    行尾

转义序列:
    \'
    \"
    \?
    \\
    \a
    \b
    \f
    \n
    \r
    \t
    \v
    \ 文件尾
    \x 十六进制数字 十六进制数字
    \ 八进制数字
    \ 八进制数字 八进制数字
    \ 八进制数字 八进制数字 八进制数字
    \u 十六进制数字 十六进制数字 十六进制数字 十六进制数字
    \U 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字

十六进制字符串:
    x" 多个十六进制字符串字符 "

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

StringLiteral:
    WysiwygString
    AlternateWysiwygString
    DoubleQuotedString
    EscapeSequence
    HexString

WysiwygString:
    r" WysiwygCharacters " Postfix opt

AlternateWysiwygString:
    ` WysiwygCharacters ` Postfix opt

WysiwygCharacters:
    WysiwygCharacter
    WysiwygCharacter WysiwygCharacters

WysiwygCharacter:
    Character
    EndOfLine

DoubleQuotedString:
    " DoubleQuotedCharacters " Postfix opt

DoubleQuotedCharacters:
    DoubleQuotedCharacter
    DoubleQuotedCharacter 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
    \& NamedCharacterEntity ;

HexString:
    x" HexStringChars " Postfixopt

HexStringChars:
    HexStringChar
    HexStringChar HexStringChars

HexStringChar
    HexDigit
    WhiteSpace
    EndOfLine

Postfix
    c
    w
    d

字符串文字量可以是一个双引号字符串、一个所见即所得引号字符串、一个转义序列、或者一个十六进制字符串。
所见即所得引号字符串由‘r"’和‘"’包围起来。所有位于‘r"’和‘"’之间的字符都是字符串的一部分,只有 行尾 除外,他被视作一个‘\n’字符。在 r" " 中没有转义序列:

QUOTE:
r"hello"
r"c:\root\foo.exe"
r"ab\n"            // 由四个字符组成的字符串:'a'、'b'、'\'、'n'

所见即所得字符串的另一种形式是使用反引号‘`’代替双引号。‘`’字符并不是所有的键盘上都有,而且有时在屏幕上难以同另一个常用的字符‘'’区分。尽管‘`’很少使用,当用在包含‘"’的字符串时就会体现出它的价值。( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
`hello`
`c:\root\foo.exe`
`ab\n`            // 由四个字符组成的字符串:'a'、'b'、'\'、'n'

双引号字符串是用‘""’包围起来的字符串。可以使用典型的‘\’记号在其中嵌入转义序列。行尾 被视作一个‘\n’字符。( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
"hello"
"c:\\root\\foo.exe"
"ab\n"            // 由三个字符组成的字符串:'a'、'b'和一个新行符
"ab
"            // 由三个字符组成的字符串:'a'、'b'和一个新行符

转义字符串由‘\’开始,他和其后的字符构成了一个转义字符序列。相邻的转义字符串会被连接在一起:

QUOTE:
\n            // 新行符
\t            // 制表符
\"            // 双引号
\012            // 八进制
\x1A            // 十六进制
\u1234            // wchar 字符
\U00101234        // dchar 字符
\®            //商标 dchar character
\r\n            // 回车换行

除了上面列出的,其他的转义序列都是非法的。
Although string literals are defined to be composed of UTF characters, the octal and hex escape sequences allow the insertion of arbitrary binary data. \u and \U escape sequences can only be used to insert valid UTF characters.
十六进制字符串使用十六进制数据构造字符串:

QUOTE:
x"0A"            // 等价于 "\x0A"
x"00 FBCD 32FD 0A"        // 等价于 "\x00\xFB\xCD\x32\xFD\x0A"

空白和新行符会被忽略,因此可以很方便的格式化。十六进制字符的个数必须是2的倍数。
相邻的字符串应该用 ~ 运算符连接,或者也可以仅仅并置即可:

QUOTE:
"hello " ~ "world" ~ \n    // 构成字符串:'h','e','l','l','o',' ','w','o','r','l','d',新行符

下面的形式都是等价的:

QUOTE:
"ab" "c"
r"ab" r"c"
r"a" "bc"
"a" ~ "b" ~ "c"
\x61"bc"

用可选的词尾字符指定了字符串的类型,而不是从从上下文推断。这在不能从上下方明确推断类型时很有用,比如当重载基于字符串的类型时。词尾字符表示的类型如下:( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
Postfix    Type
c    char[ ]
w    wchar[ ]
d    dchar[ ]

下面是一个例子:

"hello"c    // char[]
"hello"w    // wchar[]
"hello"d    // dchar[]

字符串文字量只读。写字符串文字量并不是总能发现,但会引发未定义行为。

字符文字量

QUOTE:
字符文字量:
    ' 单引号字符 '

单引号字符
    字符
    转义序列

CharacterLiteral:
    ' SingleQuotedCharacter '

SingleQuotedCharacter
    Character
    EscapeSequence

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

整数文字量

QUOTE:
整数文字量:
    整数
    整数 整数后缀

整数:
    十进制数
    二进制数
    八进制数
    十六进制数
    整数

整数后缀:
    L
    u
    U
    Lu
    LU
    uL
    UL

十进制数:
    0
    非零数字
    非零数字 十进制数

二进制数:
    0b 二进制数字
    0B 二进制数字

八进制数:
    0 八进制数字

十六进制数:
    0x 十六进制数字
    0X 十六进制数字

IntegerLiteral:
    Integer
    Integer IntegerSuffix

Integer:
    Decimal
    Binary
    Octal
    Hexadecimal
    Integer

IntegerSuffix:
    L
    u
    U
    Lu
    LU
    uL
    UL

Decimal:
    0
    NonZeroDigit
    NonZeroDigit DecimalDigits

Binary:
    0b BinaryDigits
    0B BinaryDigits

Octal:
    0 OctalDigits

Hexadecimal:
    0x HexDigits
    0X HexDigits

NonZeroDigit:
    1
    2
    3
    4
    5
    6
    7
    8
    9

DecimalDigits:
    DecimalDigit
    DecimalDigit DecimalDigits

DecimalDigit:
    0
    NonZeroDigit
    _

BinaryDigits:
    BinaryDigit
    BinaryDigit BinaryDigits

BinaryDigit:
    0
    1
    _

OctalDigits:
    OctalDigit
    OctalDigit OctalDigits

OctalDigit:
    0
    1
    2
    3
    4
    5
    6
    7
    _

HexDigits:
    HexDigit
    HexDigit HexDigits

HexDigit:
    DecimalDigit
    a
    b
    c
    d
    e
    f
    A
    B
    C
    D
    E
    F
    _

整数可以采用十进制、二进制、八进制或者十六进制。
十进制整数是十进制数字的序列。

二进制整数是二进制数字的序列,以‘0b’为前缀。( 本文出处: http://www.d-programming-language-china.org )

八进制整数是八进制数字的序列,以‘0’为前缀。

十六进制整数是十六进制数字的序列,以‘0x’为前缀,或者使用‘h’作为后缀。

整数可以内嵌 '_' 字符,它们会被忽略。嵌入的 '_' 可以用于格式化较长的文字量,例如作为千位分隔符:

QUOTE:
123_456        // 123456
1_2_3_4_5_6_    // 123456

整数后可以紧跟着一个 'L' 或者一个 'u' 或者两者都有。
整数的类型按照下述规则判断:

QUOTE:
Decimal Literal    Type
0 .. 2147483647    int
2147483648 .. 9223372036854775807    long
Decimal Literal, L Suffix    Type
0L .. 9223372036854775807L    long
Decimal Literal, U Suffix    Type
0U .. 4294967295U    uint
4294967296U .. 18446744073709551615U    ulong
Decimal Literal, UL Suffix    Type
0UL .. 18446744073709551615UL    ulong
Non-Decimal Literal    Type
0x0 .. 0x7FFFFFFF    int
0x80000000 .. 0xFFFFFFFF    uint
0x100000000 .. 0x7FFFFFFFFFFFFFFF    long
0x8000000000000000 .. 0xFFFFFFFFFFFFFFFF    ulong
Non-Decimal Literal, L Suffix    Type
0x0L .. 0x7FFFFFFFFFFFFFFFL    long
0x8000000000000000L .. 0xFFFFFFFFFFFFFFFFL    ulong
Non-Decimal Literal, U Suffix    Type
0x0U .. 0xFFFFFFFFU    uint
0x100000000UL .. 0xFFFFFFFFFFFFFFFFUL    ulong
Non-Decimal Literal, UL Suffix    Type
0x0UL .. 0xFFFFFFFFFFFFFFFFUL    ulong

浮点数文字量

QUOTE:
浮点数文字量:
    浮点数
    浮点数 浮点数后缀
    浮点数 虚数后缀
    浮点数 浮点数后缀 虚数后缀

浮点数:
    十进制浮点数
    十六进制浮点数

浮点数后缀:
    f
    F
    L

虚数后缀:
    i


FloatLiteral:
    Float
    Float FloatSuffix
    Float ImaginarySuffix
    Float FloatSuffix ImaginarySuffix

Float:
    DecimalFloat
    HexFloat

DecimalFloat:
    DecimalDigits .
    DecimalDigits . DecimalDigits
    DecimalDigits . DecimalDigits DecimalExponent
    . Decimal
    . Decimal DecimalExponent
    DecimalDigits DecimalExponent

DecimalExponent
    e DecimalDigits
    E DecimalDigits
    e+ DecimalDigits
    E+ DecimalDigits
    e- DecimalDigits
    E- DecimalDigits

HexFloat:
    HexPrefix HexDigits . HexDigits HexExponent
    HexPrefix . HexDigits HexExponent
    HexPrefix HexDigits HexExponent

HexPrefix:
    0x
    0X

HexExponent
    p DecimalDigits
    P DecimalDigits
    p+ DecimalDigits
    P+ DecimalDigits
    p- DecimalDigits
    P- DecimalDigits

FloatSuffix:
    f
    F
    L

ImaginarySuffix:
    i

浮点数可以使用十进制或者十六进制格式,如同标准 C 一样。
十六进制浮点数以 0x 开头,阶码以 p 或者 P 开头,后面跟着以 2 为底的阶数。( 本文出处: http://www.d-programming-language-china.org )

浮点文字量可以有嵌入的‘_’字符,它们会被忽略。嵌入的‘_’用来格式化冗长的文字量以提高可读性,例如可以将它们用作千位分隔符:

QUOTE:
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 后缀说明这是一个浮点数,L 说明这是一个real扩展格式浮点数。
如果浮点文字量后面跟着 i 或者 I ,那么它就是一个 ireal (虚数)类型。

示例:

QUOTE:
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

如果文字量超出了该类型的表示范围,会被视为错误。如果文字量取整后可以用该类型的有效位数字表示,就不是错误。
复数文字量不是记号,而是在语义分析时用实数和虚数表达式构造的:

QUOTE:
4.5 + 6.2i        // 复数

关键字

关键字是保留的标志符:( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
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

new
null

out
override

package
pragma
private
protected
public

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

特殊记号序列

这些记号按照下面描述替抵成另外的记号:

QUOTE:
Special             Token                Replaced with...
__FILE__        string         literal containing source file name
__LINE__        integer         literal of the current source line number
__DATE__        string         literal of the date of compilation "mmm dd yyyy"
__TIME__        string         literal of the time of compilation "hh:mm:ss"
__TIMESTAMP__    string         literal of the date and time of compilation "www mmm dd hh:mm:ss yyyy"

特殊记号序列由词法分析程序处理,它可以出现在其他记号之间,并且不影响语法分析。
目前只有一个特殊记号序列,#line 。

QUOTE:
特殊记号序列
    # line 整数 行尾
    # line 整数 Filespec 行尾

指定文件
    " 字符 "
SpecialTokenSequence
    # line Integer EndOfLine
    # line Integer Filespec EndOfLine

Filespec
    " Characters "

它会将源代码的行号设置为 整数 的值,将源代码文件名设置为可选的 Filespec 的值,从源码文本的下一行生效。 源码文件名和行号用于打印调试信息,还被符号调试器用于将生成的代码映射回源代码。
例如:

QUOTE:
int #line 6 "foo\bar"
x;            // 这里是文件 foo\bar 的第6行

注意,Filespec 字符串中的反斜杠不会被特殊对待。

( lastupdate:20070421 最新文章请访问http://www.d-programming-language-china.org )

关于一大步成功社区:
yidabu提倡在交流中学习,在分享中提高
收集感兴趣的知识,写下心得,通过网络与别人一起分享
理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的
网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台

TOP

发新话题