sub void(&) { $_[0]->(); () }
say context();
say scalar context();
say void { context() };
更高级的代码可以给我们提供更好的语法:
use syntax qw( void );
say context();
say scalar context();
say void context();
顺便提一下,以下内容表明scalar
不太像函数,而更像是编译时指令:
$ diff -u0 \
<( perl -MO=Concise,-exec -Msyntax=void -E'say f()' 2>&1 ) \
<( perl -MO=Concise,-exec -Msyntax=void -E'say scalar f()' 2>&1 )
--- /dev/fd/63 2014-08-17 12:34:29.124827443 -0700
+++ /dev/fd/62 2014-08-17 12:34:29.128827401 -0700
@@ -7 +7 @@
-6 <1> entersub[t6] lKS/TARG <-- "l" for list context
+6 <1> entersub[t7] sKS/TARG <-- "s" for scalar context
同样适用于use syntax qw( void )
中的void
关键字:
$ diff -u0 \
<( perl -MO=Concise,-exec -Msyntax=void -E'say f()' 2>&1 ) \
<( perl -MO=Concise,-exec -Msyntax=void -E'say void f()' 2>&1 )
+++ /dev/fd/62 2014-08-17 12:34:41.952692723 -0700
@@ -7 +7 @@
-6 <1> entersub[t6] lKS/TARG <
+6 <1> entersub[t6] vKS/TARG <
如何使用use syntax qw( void );
Syntax::Feature::Void的Void.xs
才是真正发挥作用的部分,以下是其关键行:
STATIC OP* parse_void(pTHX_ GV* namegv, SV* psobj, U32* flagsp) {
return op_contextualize(parse_termexpr(0), G_VOID);
}
STATIC OP* ck_void(pTHX_ OP* o, GV* namegv, SV* ckobj) {
return remove_sub_call(o);
}
BOOT: {
const char voidname[] = "Syntax::Feature::Void::void";
CV* const voidcv = get_cvn_flags(voidname, sizeof(voidname)-1, GV_ADD);
cv_set_call_parser(voidcv, parse_void, &PL_sv_undef);
cv_set_call_checker(voidcv, ck_void, &PL_sv_undef);
}
使用 get_cvn
声明了一个名为 sub void
的子程序。(该子程序从未被定义。)Void.pm
中的代码将把该子程序导出到调用词法作用域。
使用 cv_set_call_parser
,告诉 Perl 调用 void
遵循用户定义的语法。
使用 cv_set_call_checker
,告诉 Perl 在编译后需要操作 void
的调用。
当 Perl 遇到对 void
的调用时,用户定义的解析器使用 parse_termexpr
提取一个项,然后使用 op_contextualize
更改该项的上下文为 void
。
之后,检查器从操作码树中删除对 void
的调用,同时留下其参数(该项)。