HTML-Selector-XPath-0.02 の疑問個所

miyagawa さん作の HTML-Selector-XPath-0.02 は便利ですね。
(10月8日) 以下取消
以下の疑問点は、CSS3 のセレクタ・ドラフト案 (http://www.w3.org/TR/css3-selectors/#typenmsp)の xmlns の先取り実装でした。CSS2.1 で考えていたので、理解できなかったということがわかりました。お騒がせして申し訳ありませんでした m(_ _)m
なのですが、ソースを見ていたら、element の正規表現が、なぜこうなるのか理解できませんでした。

HTML::Selector::XPath

    element => qr/^([#.]?)([a-z0-9\\*_-]*)((\|)([a-z0-9\\*_-]*))?/i,
    # 途中略
        if ($rule =~ s/$reg->{element}//) {
            # 途中略
            } else {
                $parts[$index] = $5 || $2;
            }
        }

正規表現 $reg->{element} の $2 以降は、字句の W3C CSS2.1 G.2 Lexical Scanner の name のサブセットにマッチすることを意図していると思われます。関係する部分を抜き出してみましょう。

G.2 Lexical Scanner (CSS2.1 Specification)

h           [0-9a-f]
unicode     \\{h}{1,6}(\r\n|[ \t\r\n\f])?
escape      {unicode}|\\[^\r\n\f0-9a-f]
nonascii    [\200-\377]
nmchar      [_a-z0-9-]|{nonascii}|{escape}
name        {nmchar}+

Perl正規表現に直訳してみます。

my $h        = qr/[0-9a-f]/i,
my $unicode  = qr/\\${h}{1,6}(?:\r\n|[\040\t\r\n\f])?/,
my $escape   = qr/(?:${unicode}|\\[^\r\n\f0-9a-f])/i,
my $nonascii = qr/[\200-\377]/,
my $nmchar   = qr/(?:[_a-z0-9-]|${nonascii}|${escape})/i,
my $name     = qr/${nmchar}+/,

これが Perl で記述した CSS2.1 の name のフルセットの正規表現です。'|' が入る余地は escape の中だけです。
(10月8日) 追記
オリジナルのままで構わないという結論になりました。次のように動作します。CSS3 の xmlns|elementName と表現する案があり、その xmlns 部分を読み飛ばすようになっています。

use HTML::Selector::XPath;

for my $selector (
  'foo',          # --> '//foo'
  'bar|foo',      # --> '//foo'
  '|foo',         # --> '//foo'
  '|bar|foo',     # --> '//foo'
  'hoge|fuga|baz|bar|foo', # --> '//foo'
  '.foo',         # --> '//*[contains(concat(' ', @class, ' '), ' foo ')]'
  '.foo|bar',     # --> '//*[contains(concat(' ', @class, ' '), ' foo ')]'
  '.foo|bar|baz', # --> '//baz[contains(concat(' ', @class, ' '), ' foo ')]
  '.foo|bar|baz|fuga|hoge', # --> '//hoge[contains(concat(' ', @class, ' '), ' foo ')]
  '.|foo',        # --> '//*[contains(concat(' ', @class, ' '), '  ')]'
  '#foo',         # --> '//*[@id='foo']'
  '#foo|bar',     # --> '//*[@id='foo']'
  '#foo|bar|baz', # --> '//baz[@id='foo']'
  '#foo|bar|baz|fuga|hoge', # --> '//hoge[@id='foo')]
  '#|foo',        # --> '//*[@id='']'
) {
    print qq{to_xpath('$selector') --> '},
        HTML::Selector::XPath->new($selector)->to_xpath,
        qq{'\n};
}

(10月8日) 以下取り下げます
が、しかし。nonascii はエンコーディングに依存するマルチバイト文字の可能性があります。escape も Perl スクリプトユニコードフラグを立てるのかどうか、他のライブラリ依存があります。扱いが面倒な上に、これと escape をまともな HTML では使いません。ばっさりと無視してしまいましょう。

my $name = qr{[\w-]+};
my $name = qr{[\w\-*]+};  # 自己フォロー:もし、これを使うにしても * は必要

簡単になりました。そこで、冒頭の HTML::Selector::XPath の該当部分を書き改めてみます。

    element => qr/^([#.]?)([\w-]+)/i,

        if ($rule =~ s/$reg->{element}//) {
            # 途中略
            } else {
                $parts[$index] = $2;
            }
        }

これで良いのではないでしょうか。