PL_keyword_plugin で、Perl の lexer と re2c を組み合わせる

さいきん PL_keyword_plugin というのをよく耳にするので、ためしにつかってみました。

p5-php-embed

たとえば

use PHP::Embed;

$a = <?php print "


か";
?>;
print $a, "\n";

こんなふうに PHP をインラインで Perl にまぜたりできる。

じつは、内部的には PHP を pipe でよびだす PHP::Embed::run を実行する OP をかいているだけなので、PHP をインストールして、パスをとおしておく必要があります。

Perl の lexer にはくせがあって、Perl_lex_next_chunk() という関数をよびだすと、つぎのチャンクを PL_parser->linestr によみこんで、PL_parser->bufptr や PL_parser->bufend を更新するようになっているんですが、チャンクというものの単位が次の改行文字までの 1 行なのと、かつ LEX_KEEP_PREVIOUS フラグをわたさない限り PL_parser->linestr のなかみがぜんぶ入れかえられるようになっているので、この関数をよびだしている Perl_yylex() もそういう前提でかかれている。

なので、今回みたいに外部の lexer といっしょにつかうのは、toke.c にパッチを当てないとむりでした。

http://github.com/moriyoshi/p5-php-embed/blob/master/perl-5.11.2-funky.patch.diff

上のパッチは、"<?" ではじまっているトークンも word として強引にあつかうようにする変更もふくんでいるのでわかりづらいですが、

@@ -4653,8 +4653,12 @@
 		PL_bufend = s; */
 	    }
 #else
-	    *s = '\0';
-	    PL_bufend = s;
+	    if (*s == '#') {
+		s = (char*)memchr(s, '\n', PL_bufend - s);
+		if (!s)
+		    s = PL_bufend;
+	    }
+	    s++;
 #endif
 	}
 	goto retry;

この部分が肝心。