如何在Perl API中自省正则表达式

11

我正在处理一些需要序列化Perl正则表达式的代码,包括任何正则表达式标志。只支持一部分标志,因此我需要检测正则表达式对象中是否存在不支持的标志,例如/u

当前版本的代码实现如下:

static void serialize_regex_flags(buffer *buf, SV *sv) {
  char flags[] = {0,0,0,0,0,0};
  unsigned int i = 0, f = 0;
  STRLEN string_length;
  char *string = SvPV(sv, string_length);

然后手动逐个字符处理字符串以查找标志。

问题在于,正则表达式标志的字符串化已经改变(我想是在 Perl 5.14 中),例如从 (?i-xsm:foo) 改为 (?^i:foo),这使得解析变得很痛苦。

我可以检查 perl 的版本,或者编写解析器来处理两种情况,但某些东西告诉我一定有更好的内省方法可用。

2个回答

6
在Perl中,你可以使用re::regexp_pattern
 my $re = qr/foo/i;
 my ($pat, $mods) = re::regexp_pattern($re);
 say $pat;   # foo
 say $mods;  # i

regexp_pattern的源代码中可以看出,API中没有获得该信息的函数,因此建议您也从XS中调用该函数。 Perlcall介绍了如何从C语言中调用Perl函数。下面是未经测试的代码:
/* Calls re::regexp_pattern to extract the pattern
 * and flags from a compiled regex.
 *
 * When re isn't a compiled regex, returns false,
 * and *pat_ptr and *flags_ptr are set to NULL.
 *
 * The caller must free() *pat_ptr and *flags_ptr.
 */

static int regexp_pattern(char ** pat_ptr, char ** flags_ptr, SV * re) {
   dSP;
   int count;
   ENTER;
   SAVETMPS;
   PUSHMARK(SP);
   XPUSHs(re);
   PUTBACK;
   count = call_pv("re::regexp_pattern", G_ARRAY);
   SPAGAIN;

   if (count == 2) {
      /* Pop last one first. */
      SV * flags_sv = POPs;
      SV * pat_sv   = POPs;

      /* XXX Assumes no NUL in pattern */
      char * pat   = SvPVutf8_nolen(pat_sv); 
      char * flags = SvPVutf8_nolen(flags_sv);

      *pat_ptr   = strdup(pat);
      *flags_ptr = strdup(flags);
   } else {
      *pat_ptr   = NULL;
      *flags_ptr = NULL;
   }

   PUTBACK;
   FREETMPS;
   LEAVE;

   return *pat_ptr != NULL;
}

使用方法:

SV * re = ...;

char * pat;
char * flags;
regexp_pattern(&pat, &flags, re);

谢谢,@ikegami。我能够以你的C代码为起点得到我需要的东西。需要注意的一件事是返回值必须按相反的顺序弹出(所以flags_sv首先弹出而不是第二个)。 - friedo

3
use Data::Dump::Streamer ':util';
my ($pattern, $flags) = regex( qr/foo/i );
print "pattern: $pattern, flags: $flags\n";
# pattern: foo, flags: i

但如果你想限制更多最近的功能,那么仅仅检查/u是远远不够的,你需要做更多的工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接