字句の構成
Dでは、 字句解析は構文解析や意味解析とは独立しています。 字句解析器は文法に従ってソーステキストをトークンに分解しますが、 Dの字句解析の文法は、 特殊な規則をできる限り減らしてあるので、 正確で高速な解析器が簡単に書けるようになっています。 各トークンは、 C や C++に慣れた人にはわかりやすいものです。ソースコードのテキスト
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 | BOMなし |
ソースファイルが BOM で始まらない場合は、 最初の2文字は必ず U0000007F 以下でなければなりません。
二連文字(digraph)や三連文字(trigraph)はDには存在しません。
ソーステキストは Unicode Character の列へとデコードされ、 解釈されます。Character は、以下に分類されます: WhiteSpace, EndOfLine, Comment, SpecialTokenSequence, Token, そして最後に EndOfFile。
トークンへ分解する際には、最長一致戦略を用います。つまり、
字句解析器はできるだけ長いトークンを探そうとします。
例えば
>>
は右シフトトークンであって、
2個の不等号トークンとは解釈しません。この規則の例外は、
浮動小数点数リテラルとして読めるトークンに挟まれた .. です。
例えば 1..2 は、.. が先頭の
1 と空白で区切られているかのように解釈されます。
文字集合
Character
任意の Unicode 文字
ファイル終端
EndOfFile:
物理的なファイル終端
\u0000
\u001A
どれかが一番最初に出現した点をソースの終端とします。
行終端
EndOfLine:
\u000D
\u000A
\u000D \u000A
\u2028
\u2029
EndOfFile
バックスラッシュで行を分けることはできません。
1行の文字数に制限はありません。
空白
WhiteSpace:
Space
Space WhiteSpace
Space:
\u0020
\u0009
\u000B
\u000C
コメント
Comment:
BlockComment
LineComment
NestingBlockComment
BlockComment
/* Characters */
LineComment
// Characters EndOfLine
NestingBlockComment:
/+ NestingBlockCommentCharacters +/
NestingBlockCommentCharacters:
NestingBlockCommentCharacter
NestingBlockCommentCharacter NestingBlockCommentCharacters
NestingBlockCommentCharacter:
Character
NestingBlockComment
Characters:
Character
Character Characters
Dには三種類のコメントがあります:
- ネストしないブロックコメント
- 行コメント
- ネストできるブロックコメント
文字列とコメントの中身はトークン分けされません。従って、 文字列中に埋め込まれたコメント開始記号ではコメント扱いになりませんし、 コメント内に埋め込まれた引用符では文字列扱いになりません。 "/+" コメントの中の "/+" をのぞき、 コメント中のコメント開始記号も 単純に無視されます。
a = /+ // +/ 1; // 'a = 1;' として解析される
a = /+ "+/" +/ 1"; // 'a = " +/ 1";' として解析される
a = /+ /* +/ */ 3; // 'a = */ 3;' として解析される
コメントを間に挟むと、トークンは分割されます。例えば
abc/**/def
は2つのトークン abc と def です。
1つの abcdef ではありません。
トークン
Token:
Identifier
StringLiteral
CharacterLiteral
IntegerLiteral
FloatLiteral
Keyword
/
/=
.
..
...
&
&=
&&
|
|=
||
-
-=
--
+
+=
++
<
<=
<<
<<=
<>
<>=
>
>=
>>=
>>>=
>>
>>>
!
!=
!<>
!<>=
!<
!<=
!>
!>=
(
)
[
]
{
}
?
,
;
:
$
=
==
*
*=
%
%=
^
^=
^^
^^=
~
~=
@
=>
#
識別子
Identifier:
IdentifierStart
IdentifierStart IdentifierChars
IdentifierChars:
IdentifierChar
IdentifierChar IdentifierChars
IdentifierStart:
_
Letter
UniversalAlpha
IdentifierChar:
IdentifierStart
0
NonZeroDigit
識別子はアルファベットか _、または普遍基本文字(Universal Alpha)で始まり、
アルファベット、
_、数字、普遍基本文字の列が後ろに続いたものです。
普遍基本文字は ISO/IEC 9899:1999(E) Appendix D. (C99標準です) に
Universal character names for identifiers として定義されています。
識別子は大文字小文字を区別します。長さに制限はありません。
__ (2個のアンダースコア)で始まる識別子は処理系側に予約されています。
String Literals
StringLiteral:
WysiwygString
AlternateWysiwygString
DoubleQuotedString
HexString
DelimitedString
TokenString
WysiwygString:
r" WysiwygCharacters " StringPostfixopt
AlternateWysiwygString:
` WysiwygCharacters ` StringPostfixopt
WysiwygCharacters:
WysiwygCharacter
WysiwygCharacter WysiwygCharacters
WysiwygCharacter:
Character
EndOfLine
DoubleQuotedString:
" DoubleQuotedCharacters " StringPostfixopt
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 " StringPostfixopt
HexStringChars:
HexStringChar
HexStringChar HexStringChars
HexStringChar:
HexDigit
WhiteSpace
EndOfLine
StringPostfix:
c
w
d
DelimitedString:
q" Delimiter WysiwygCharacters MatchingDelimiter "
TokenString:
q{ Tokens }
文字列リテラルの種類としては、二重引用符やWYSIWYG引用符で囲まれた 文字列、エスケープシーケンス、 デリミタ指定文字列、トークン文字列、 16進文字列があります。
どの文字列リテラルの中でも EndOfLine は単一の \n という文字と解釈されます。
WYSIWYG文字列
WYSIWYG "what you see is what you get" 文字列は r" と " で囲みます。 r" と " の間の全ての文字は 文字列として扱われます。 この中ではエスケープシーケンスは使用できません。
r"hello"
r"c:\root\foo.exe"
r"ab\n" // 4文字
// 'a', 'b', '\', 'n'
WYSIWYG文字には、バッククォート ` を使った表現もあります。 この ` 文字はキーボードによっては存在しなかったり、 フォントによっては普通の引用符 ' と区別できないことがあります。 このため ` が使用されることはまれですが、" を含む文字列を書くには便利です。
`hello`
`c:\root\foo.exe`
`ab\n` // 4文字
// 'a', 'b', '\', 'n'
二重引用符文字列
二重引用符 "" の中では、おなじみの \ を使った記法で エスケープシーケンスを使うことができます。"hello"
"c:\\root\\foo.exe"
"ab\n" // 3文字の文字列
// 'a', 'b', 改行
"ab
" // これも3文字の文字列
// 'a', 'b', 改行
16進文字列
16進文字列は、16進データを使って文字列リテラルを記述する方法です。 16進データは有効なUTF文字である必要はありません。
x"0A" // "\x0A" と同じ
x"00 FBCD 32FD 0A" //
// "\x00\xFB\xCD\x32\xFD\x0A" と同じ
空白や改行文字は無視されるので、
16進データを整形して並べることができます。
16進文字の個数は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"
追加の StringPostfix 文字によって、 文字列リテラルの型を文脈から推論させるのではなく、明示的に指定できます。 これは、あいまいなく型を推論することが不可能なとき、例えば 文字列の型によるオーバーロードがあるときなどに役に立ちます。 postfixと対応する型はそれぞれ:
Postfix | 型 | 別名 |
---|---|---|
c | immutable(char)[] | string |
w | immutable(wchar)[] | wstring |
d | immutable(dchar)[] | dstring |
"hello"c // string
"hello"w // wstring
"hello"d // dstring
文字列はまず UTF-8 の文字列として読み込まれ、 postfix 文字を見て whar や dchar に変換されます。
文字列リテラルは読み取り専用です。文字列リテラルへの書き込みは、 全てが検出されるとは限りませんが、未定義動作となります。
デリミタ指定文字列
デリミタ指定文字列では、いろいろな形式のデリミタを使用することができます。 デリミタは、文字を使う場合も識別子を使う場合も、 余計な空白なしで " の直後に続ける必要があります。 終了デリミタも、 " の直前に空白なしで置くようにします。 nesting delimiter はネスト可能で、 以下のいずれかの文字を使用できます:
開始デリミタ | 終了デリミタ |
---|---|
[ | ] |
( | ) |
< | > |
{ | } |
q"(foo(xxx))" // "foo(xxx)"
q"[foo{]" // "foo{"
デリミタとして識別子を使用する場合、 最初の識別子の直後に改行を続けます。また、 最後の閉じ識別子としては先頭の識別子と同じものを 行頭に書きます:
writefln(q"EOS
This
is a multi-line
heredoc string
EOS"
);
T先頭の識別子の直後の改行は文字列には含まれませんが、 最後の閉じ識別子の直前の改行は 文字列に含まれます。閉じ識別子は、 行の左端にその識別子だけの行として配置される必要があります。
それ以外の場合は、 マッチするデリミタはデリミタ文字と完全に一致します:
q"/foo]/" // "foo]"
q"/abc/def/" // error
トークン文字列
トークン文字列は q{ で始まり } で終わります。この間にはDで有効なトークンのみを記述することが出来ます。 トークン { と } はネストできます。 表現する文字列は、括弧の中に記述されたトークンの、 コメントも含めた全文字の列となります。
q{foo} // "foo"
q{/*}*/ } // "/*}*/ "
q{ foo(q{hello}); } // " foo(q{hello}); "
q{ __TIME__ } // " __TIME__ " になる。つまり、時刻で置き換えられることはない
q{ __EOF__ } // エラー。__EOF__ はトークンではなくファイル終端
文字リテラル
CharacterLiteral:
' SingleQuotedCharacter '
SingleQuotedCharacter:
Character
EscapeSequence
文字リテラルは、1文字分の文字かエスケープシーケンスです。
一重引用符 ' ' でくくられます。
整数リテラル
IntegerLiteral:
Integer
Integer IntegerSuffix
Integer:
DecimalInteger
BinaryInteger
HexadecimalInteger
IntegerSuffix:
L
u
U
Lu
LU
uL
UL
DecimalInteger:
0
NonZeroDigit
NonZeroDigit DecimalDigitsUS
BinaryInteger:
BinPrefix BinaryDigits
BinPrefix:
0b
0B
HexadecimalInteger:
HexPrefix HexDigitsNoSingleUS
NonZeroDigit:
1
2
3
4
5
6
7
8
9
DecimalDigits:
DecimalDigit
DecimalDigit DecimalDigits
DecimalDigitsUS:
DecimalDigitUS
DecimalDigitUS DecimalDigitsUS
DecimalDigitsNoSingleUS:
DecimalDigit
DecimalDigit DecimalDigitsUS
DecimalDigitsUS DecimalDigit
DecimalDigitsNoStartingUS:
DecimalDigit
DecimalDigit DecimalDigitsUS
DecimalDigit:
0
NonZeroDigit
DecimalDigitUS:
DecimalDigit
_
BinaryDigitsUS:
BinaryDigitUS
BinaryDigitUS BinaryDigitsUS
BinaryDigit:
0
1
BinaryDigitUS:
BinaryDigit
_
OctalDigits:
OctalDigit
OctalDigit OctalDigits
OctalDigitsUS:
OctalDigitUS
OctalDigitUS OctalDigitsUS
OctalDigit:
0
1
2
3
4
5
6
7
OctalDigitUS:
OctalDigit
_
HexDigits:
HexDigit
HexDigit HexDigits
HexDigitsUS:
HexDigitUS
HexDigitUS HexDigitsUS
HexDigitsNoSingleUS:
HexDigit
HexDigit HexDigitsUS
HexDigitsUS HexDigit
HexDigit:
DecimalDigit
HexLetter
HexLetter:
a
b
c
d
e
f
A
B
C
D
E
F
_
整数は10進,2進,8進,16進のどれかの記法で表記します。
10進整数は、0,1,..,9の並びです。
2進整数 は ‘0b’ で始まる0,1の並びです。
C 形式の8進整数リテラルは、十進表記と不注意で混ぜてしまう可能性が非常に高いものです。 これは文字列リテラルの中でのみ完全にサポートされています。 D では、コンパイル時に解釈される8進リテラルは std.conv.octal テンプレートとして提供しており、octal!167 のように使います。
16進整数は、 ‘0x’ で始まる 0,1,..,9,A,..,F,a,..,f の並びです。
整数の途中に文字 ‘_’ を入れることができます。 この文字は単に無視されます。 長いリテラルの整形に、例えば1000単位の区切りとしてなどで役に立ちます。
123_456 // 123456
1_2_3_4_5_6_ // 123456
整数の後ろに ‘L’ または ‘u’ や ‘U’、あるいはその両方が続くことがあります。 小文字の ‘l’ は使えません。
整数の型は次のように決定されます:
10進リテラル | 型 |
---|---|
0 .. 2_147_483_647 | int |
2_147_483_648 .. 9_223_372_036_854_775_807L | long |
10進リテラル, 接尾辞L | 型 |
0L .. 9_223_372_036_854_775_807L | long |
10進リテラル, 接尾辞U | 型 |
0U .. 4_294_967_296U | uint |
4_294_967_296U .. 18_446_744_073_709_551_615UL | ulong |
10進リテラル, 接尾辞UL | 型 |
0UL .. 18_446_744_073_709_551_615UL | ulong |
非10進リテラル | 型 |
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 |
非10進リテラル, 接尾辞L | 型 |
0x0L .. 0x7FFF_FFFF_FFFF_FFFFL | long |
0x8000_0000_0000_0000L .. 0xFFFF_FFFF_FFFF_FFFFL | ulong |
非10進リテラル, 接尾辞U | 型 |
0x0U .. 0xFFFF_FFFFU | uint |
0x1_0000_0000UL .. 0xFFFF_FFFF_FFFF_FFFFUL | ulong |
非10進リテラル, 接尾辞UL | 型 |
0x0UL .. 0xFFFF_FFFF_FFFF_FFFFUL | ulong |
浮動小数点数リテラル
FloatLiteral:
Float
Float Suffix
Integer ImaginarySuffix
Integer FloatSuffix ImaginarySuffix
Integer RealSuffix ImaginarySuffix
Float:
DecimalFloat
HexFloat
DecimalFloat:
LeadingDecimal .
LeadingDecimal . DecimalDigits
DecimalDigits . DecimalDigitsNoSingleUS DecimalExponent
. DecimalInteger
. DecimalInteger DecimalExponent
LeadingDecimal DecimalExponent
DecimalExponent
DecimalExponentStart DecimalDigitsNoSingleUS
DecimalExponentStart
e
E
e+
E+
e-
E-
HexFloat:
HexPrefix HexDigitsNoSingleUS . HexDigitsNoSingleUS HexExponent
HexPrefix . HexDigitsNoSingleUS HexExponent
HexPrefix HexDigitsNoSingleUS HexExponent
HexPrefix:
0x
0X
HexExponent:
HexExponentStart DecimalDigitsNoSingleUS
HexExponentStart:
p
P
p+
P+
p-
P-
Suffix:
FloatSuffix
RealSuffix
ImaginarySuffix
FloatSuffix ImaginarySuffix
RealSuffix ImaginarySuffix
FloatSuffix:
f
F
RealSuffix:
L
ImaginarySuffix:
i
LeadingDecimal:
DecimalInteger
0 DecimalDigitsNoSingleUS
浮動小数点数は、10進または16進表記が可能です。
16進浮動小数は 0x で始まり、 指数部は p か P のうしろに 2 の指数を表す数を続けたものです。
浮動小数点数リテラルの途中に文字 ‘_’ を入れることができ、 この文字は単に無視されます。 長いリテラルを整形して読みやすくするのに役立ちます。 例えば、1000単位の区切りとしてなど:
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 は値がfloat型であることを示し、 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
Iリテラルが型の表現可能な範囲を超えていると、 エラーになります。 リテラルの示す値の下位桁が丸められるのはエラーではありません。
複素数リテラルはトークンではなく、意味解析によって 実数と虚数トークンが組み合わせられます。
4.5 + 6.2i // 複素数 (phased out)
予約語
以下が予約された識別子です:Keyword:
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
immutable
import
in
inout
int
interface
invariant
ireal
is
lazy
long
macro
mixin
module
new
nothrow
null
out
override
package
pragma
private
protected
public
pure
real
ref
return
scope
shared
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
__FILE__
__LINE__
__gshared
__thread
__traits
特殊トークン
特殊トークンは、 以下の表に従って別のトークンへと置換されます:
特殊トークン | 置換後... |
---|---|
__DATE__ | コンパイルした日にちの文字列リテラル "mmm dd yyyy" |
__EOF__ | このトークンの出現をファイルの終端として扱う |
__TIME__ | コンパイルした時刻の文字列リテラル "hh:mm:ss" |
__TIMESTAMP__ | コンパイルした日時の文字列リテラル "www mmm dd hh:mm:ss yyyy" |
__VENDOR__ | コンパイラベンダ名文字列。例えば "Digital Mars D" |
__VERSION__ | コンパイラのバージョン番号を表す整数。例えば 2001 |
特殊トークン列
SpecialTokenSequence:
# line IntegerLiteral EndOfLine
# line IntegerLiteral Filespec EndOfLine
Filespec:
" Characters "
特殊トークン列は、トークン列のどの位置にでも登場でき、 字句解析器によって処理されます。 構文解析には影響しません。
現在の所、特殊トークン列は #line 一つだけです。
これによって、次の行の行番号は IntegerLiteral に、 ファイル名は(もしあれば) Filespec に設定されます。 ソースファイル名と行番号は、 エラーメッセージや デバッガ用のシンボルとして使用されています。
例:
int #line 6 "foo\bar"
x; // ここがファイル foo\bar の6行目扱いになる
バックスラッシュ文字は Filespec 文字列の中では特別扱いされないことにご注意下さい。