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 )
's也是如此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/63 2014-08-17 12:34:41.952692723 -0700
+++ /dev/fd/62 2014-08-17 12:34:41.952692723 -0700
@@ -7 +7 @@
-6 <1> entersub[t6] lKS/TARG <-- "l" for list context
+6 <1> entersub[t6] vKS/TARG <-- "v" for void context
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);
}
它声明 subvoid
使用get_cvn
. (子永远不会被定义。) 中的代码Void.pm
会将子导出到调用词法范围。
它告诉 Perl 调用void
遵循用户定义的语法,使用cv_set_call_parser
.
它告诉 Perlvoid
在使用cv_set_call_checker
.
当 Perl 遇到对 的调用时void
,用户定义的解析器提取一个术语 using parse_termexpr
,然后将该术语的上下文更改为void
using op_contextualize
。
之后,检查器从操作码树中删除对的调用void
,同时将其参数(术语)留在后面。