てきとうなメモ

本の感想とか技術メモとか

Perlの識別子の最大長はなぜ251文字なのか

YAPC::Asia Tokyo 2013のPerl◯×クイズで、251文字までということを知ったので、なんで255文字でなく251文字なのかなと思ってちょっと調べた。

結論からいうと実装上の問題っぽい。でも、253文字にもできるんじゃないかなという気がした。

識別子の長さ制限がついたのは5.004からで、perl5004deltaのNew Diagnosticsに以下の記述がある。

Identifier too long
(F) Perl limits identifiers (names for variables, functions, etc.)
to 252 characters for simple names, somewhat more for compound
names (like $A::B). You've exceeded Perl's limits. Future
versions of Perl are likely to eliminate these arbitrary
limitations

というわけでperl5.004のtoke.c(字句解析器のソース)を読む。(最近のtoke.cも読んだんだけども古い方が読みやすい。メインのロジックはほぼ同じだし)

識別子をとってきている部分はscan_ident関数で

static char *
scan_ident(s, send, dest, destlen, ck_uni)
register char *s;
register char *send;
char *dest;
STRLEN destlen;
I32 ck_uni;
{
    register char *d;
    register char *e;
    ...
    d = dest;
    e = d + destlen - 3;        /* two-character token, ending NUL */
    if (isDIGIT(*s)) {
        while (isDIGIT(*s)) {
            if (d >= e)
                croak(ident_too_long);  // ここでIdentifier too longとなる
            *d++ = *s++;
        }    
    }    
    else {
        for (;;) {
            if (d >= e)
                croak(ident_too_long); // ここでIdentifier too longとなる
            if (isALNUM(*s))
                *d++ = *s++;
            else if (*s == '\'' && isIDFIRST(s[1])) {
                *d++ = ':'; 
                *d++ = ':';
                s++;
            }
            else if (*s == ':' && s[1] == ':') {
                *d++ = *s++;
                *d++ = *s++;
            }
            else
                break;
        }
    }
    *d = '\0';

sは元の文字列でdestがトークンをコピーする先の文字配列を指すポインタ、destlenはコピー先の長さ。tokenのコピー先の配列の長さは256なんだけども、この関数を呼ぶ段階で既にsigilが入っていてdestは1バイト進み、destlenは255になっている。

            if (d >= e)
                croak(ident_too_long); // ここでIdentifier too longとなる

となっているのだが、eは番兵みたいなもので、ここまでいってしまうとIdentifier too longをcroakする。

で、eは

    e = d + destlen - 3;        /* two-character token, ending NUL */

destlenは255で-3しているので252文字になった時にcroakすることになる。よって251文字までになる。

で、なんで-3しているかなのだけども、コメントにあるように1文字は文字列の最後のNUL文字用で、2文字はトークンの先読み用かなと。先読みというのは、先読み時に長さチェックしてからコピーするのではなく、コピーした後にe以上の位置になっていればcroakする。そのため、コピー先の配列が実際に許容する識別子の長さより長く必要となり、より手前でエラーにしてしまわないといけない。で、2文字としているのは、$a::bの::の部分を先読みして一度にコピーしているので2文字ということなのだろう。先に長さチェックしてコピーした方がわかりやすい気がするけども、perl5.004の頃なので性能とか考えたのかなと。

ただ、NUL文字は正常終了時にくっつけるだけじゃないのとか、'::'用に2文字余分にとっているのけど、'>='でeとの比較しているので1文字分は既に考慮されているんじゃないのとか考えると、253文字までいけそうな気がするんだけどね。