Improve this page Github へのログインが必要です。 簡単な修正は、ここから fork、オンライン編集、pull request ができます。 大きな修正については、 通常の clone で行って下さい。 Page wiki 関連するWikiページを参照・編集 English このページの英語版(原文)

字句の構成

Dでは、 字句解析は構文解析や意味解析とは独立しています。 字句解析器は文法に従ってソーステキストをトークンに分解しますが、 Dの字句解析の文法は、 特殊な規則をできる限り減らしてあるので、 正確で高速な解析器が簡単に書けるようになっています。 各トークンは、 C や C++に慣れた人にはわかりやすいものです。

ソースコードのテキスト

D のソースコードは以下のいずれかの形式のテキストファイルです: UTF-8 は昔ながらの 7-bit ASCII のスーパーセットです。 以下の UTF BOM (Byte Order Mark) がソースの先頭に置かれていても構いません:

UTF Byte Order Mark
形式BOM
UTF-8EF BB BF
UTF-16BEFE FF
UTF-16LEFF FE
UTF-32BE00 00 FE FF
UTF-32LEFF FE 00 00
ASCIIBOMなし

ソースファイルが 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には三種類のコメントがあります:

  1. ネストしないブロックコメント
  2. 行コメント
  3. ネストできるブロックコメント

文字列とコメントの中身はトークン分けされません。従って、 文字列中に埋め込まれたコメント開始記号ではコメント扱いになりませんし、 コメント内に埋め込まれた引用符では文字列扱いになりません。 "/+" コメントの中の "/+" をのぞき、 コメント中のコメント開始記号も 単純に無視されます。

a = /+ // +/ 1;    // 'a = 1;' として解析される
a = /+ "+/" +/ 1"; // 'a = " +/ 1";' として解析される
a = /+ /* +/ */ 3; // 'a = */ 3;' として解析される
コメントを間に挟むと、トークンは分割されます。例えば abc/**/def は2つのトークン abcdef です。 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別名
cimmutable(char)[]string
wimmutable(wchar)[]wstring
dimmutable(dchar)[]dstring
"hello"c  // string
"hello"w  // wstring
"hello"d  // dstring

文字列はまず UTF-8 の文字列として読み込まれ、 postfix 文字を見て whar や dchar に変換されます。

文字列リテラルは読み取り専用です。文字列リテラルへの書き込みは、 全てが検出されるとは限りませんが、未定義動作となります。

デリミタ指定文字列

デリミタ指定文字列では、いろいろな形式のデリミタを使用することができます。 デリミタは、文字を使う場合も識別子を使う場合も、 余計な空白なしで " の直後に続ける必要があります。 終了デリミタも、 " の直前に空白なしで置くようにします。 nesting delimiter はネスト可能で、 以下のいずれかの文字を使用できます:

Nesting Delimiters
開始デリミタ終了デリミタ
[]
()
<>
{}
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進リテラルの型
10進リテラル
0 .. 2_147_483_647int
2_147_483_648 .. 9_223_372_036_854_775_807Llong
10進リテラル, 接尾辞L
0L .. 9_223_372_036_854_775_807Llong
10進リテラル, 接尾辞U
0U .. 4_294_967_296Uuint
4_294_967_296U .. 18_446_744_073_709_551_615ULulong
10進リテラル, 接尾辞UL
0UL .. 18_446_744_073_709_551_615ULulong
非10進リテラル
0x0 .. 0x7FFF_FFFFint
0x8000_0000 .. 0xFFFF_FFFFuint
0x1_0000_0000 .. 0x7FFF_FFFF_FFFF_FFFFlong
0x8000_0000_0000_0000 .. 0xFFFF_FFFF_FFFF_FFFFulong
非10進リテラル, 接尾辞L
0x0L .. 0x7FFF_FFFF_FFFF_FFFFLlong
0x8000_0000_0000_0000L .. 0xFFFF_FFFF_FFFF_FFFFLulong
非10進リテラル, 接尾辞U
0x0U .. 0xFFFF_FFFFUuint
0x1_0000_0000UL .. 0xFFFF_FFFF_FFFF_FFFFULulong
非10進リテラル, 接尾辞UL
0x0UL .. 0xFFFF_FFFF_FFFF_FFFFULulong

浮動小数点数リテラル

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 で始まり、 指数部は pP のうしろに 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 のうちどれか一つを付けることができます。 fF は値が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 文字列の中では特別扱いされないことにご注意下さい。