RFC 5322 のコメント

電子メールのヘッダ中で、 利用しようと思えば利用できるものの、 利用されるのが稀な記法の一つに、 入れ子のコメント、 およびコメント中でのエスケープがあります。 コメントは丸括弧で囲み、 コメントの中にコメントを入れ子にすることができます。 さらに、 コメント中で丸括弧をバックスラッシュでエスケープすることもできます。 その上、 CRLF による改行が許されており、 行頭に 1 つ以上の空白文字をおきます。 ABNF の生成規則 CFWS で、 一連の空白・改行・コメントを定義してあります。

RFC 5322: 3.2.2. Folding White Space and Comments

CFWS     = (1*([FWS] comment) [FWS]) / FWS
comment  = "(" *([FWS] ccontent) [FWS] ")"
ccontent = ctext / quoted-pair / comment
FWS      = [*WSP CRLF] 1*WSP
WSP      = %d9 / %d32
ctext    = %d33-39 / %d42-91 / %d93-126
quoted-pair = "\" (VCHAR / WSP)
VCHAR    = %d33-126

生成規則 CFWS の開始記号はタブ・空白・左丸括弧・改行のいずれかです。 文字列イテレータ pos にこれらがあると真を返す関数を is_first_cfws としましょう。

#include <string>

bool
is_first_cfws (std::string::const_iterator pos, std::string::const_iterator eos)
{
    if (pos >= eos) {
        return false;
    }
    int const c = pos[0];
    return '\t' == c || '\r' == c || '(' == c || ' ' == c;
}

続いて、 文字列イテレータ pos から生成規則 CFWS で生成可能な部分文字列を読み飛ばす skip_cfws を作ります。 この関数は、 読み飛ばしに成功したら、 スキップした後の文字列イテレータを返し、 途中で失敗したら、 文字列の末尾イテレータ eos を返します。 左丸括弧でコメントに入ると、 入れ子の深さをカウンタで追跡し、 深さがゼロに戻った時点でコメントを扱う状態から抜け出します。 開始状態は S2 で、 成功終了状態は S1、 失敗状態 S0 です。 RULE 配列は決定性状態遷移機械の遷移行列で、 先頭から状態 S2 の、 次は状態 S3 の遷移ベクタを並べています。 各遷移ベクタは下から 4 ビットごとに入力記号に対する遷移先になっています。 入力記号に対するビット・シフト量を変数 c へ求めて、 右シフトして遷移先を得ます。 コメント中の丸括弧は決定性状態遷移機械の状態 S6 へ常に遷移し、 状態 S6 にいる間にカウンタで丸括弧の入れ子を追跡します。

std::string::const_iterator
scan_cfws (std::string::const_iterator pos, std::string::const_iterator eos)
{
    static const unsigned long RULE[8] = {
        0x00060430UL, // S2: WSP S3 | CR S4 | '(' S6
        0x10060430UL, // S3: WSP S3 | CR S4 | '(' S6 | #
        0x00005000UL, // S4: LF S5
        0x00000030UL, // S5: WSP S3
        0x67660860UL, // S6: WSP S6 | CR S8 | '(' S6 | ')' S6 | '\\' S7 | ctext S6
        0x66660060UL, // S7: WSP S6 | '(' S6 | ')' S6 | '\\' S6 | ctext S6
        0x00009000UL, // S8: LF S9
        0x00000060UL, // S9: WSP S6
    };
    int level = 0;    // comment nesting level
    unsigned long state = 2UL;
    --pos;
    while (state > 1) {
        ++pos;
        int const octet = pos < eos ? static_cast<unsigned char> (pos[0]) : 0;
        int const c = ' ' == octet ? 4 : '\t' == octet ? 4
            : '\r' == octet ?  8 : '\n' == octet ? 12
            : '('  == octet ? 16 : ')'  == octet ? 20
            : '\\' == octet ? 24
            : (' ' < octet && 0x7f != octet) ? 28
            : 0;
        if (state <= 3UL && '(' == octet) {
            level = 1;
        }
        else if (6UL == state) {
            level = ')' == octet ? level - 1
                  : '(' == octet ? level + 1
                  : level;
        }
        state = (RULE[state - 2UL] >> c) & 15UL;
        if (6UL == state && 0 == level) {
            state = 3UL;
        }
    }
    return 1UL == state ? pos : eos;
}