develooper Front page | perl.perl5.porters | Postings from August 2001

Custom Ops

Thread Next
From:
Simon Cozens
Date:
August 25, 2001 10:58
Subject:
Custom Ops
Message ID:
20010825174509.A5752@netthink.co.uk
Enjoy.
Simon

--- pod/perlguts.pod~	Thu Aug 23 23:39:53 2001
+++ pod/perlguts.pod	Sat Aug 25 11:36:16 2001
@@ -2357,6 +2357,50 @@
 
 =back
 
+=head1 Custom Operators
+
+Custom operator support is a new experimental feature that allows you do
+define your own ops. This is primarily to allow the building of
+interpreters for other languages in the Perl core, but it also allows
+optimizations through the creation of "macro-ops" (ops which perform the
+functions of multiple ops which are usually executed together, such as
+C<gvsv, gvsv, add>.) Currently, this feature must be enabled with the C
+flag C<-DPERL_CUSTOM_OPS>.
+
+Enabling the feature will create a new op type, C<OP_CUSTOM>. The Perl
+core does not "know" anything special about this op type, and so it will
+not be involved in any optimizations. This also means that you can
+define your custom ops to be any op structure - unary, binary, list and
+so on - you like.
+
+It's important to know what custom operators won't do for you. They
+won't let you add new syntax to Perl, directly. They won't even let you
+add new keywords, directly. In fact, they won't change the way Perl
+compiles a program at all. You have to do those changes yourself, after
+Perl has compiled the program. You do this either by manipulating the op
+tree using a C<CHECK> block and the C<B::Generate> module, or by adding
+a custom peephole optimizer with the C<optimize> module.
+
+When you do this, you replace ordinary Perl ops with custom ops by
+creating ops with the type C<OP_CUSTOM> and the C<pp_addr> of your own
+PP function. This should be defined in XS code, and should look like
+the PP ops in C<pp_*.c>. You are responsible for ensuring that your op
+takes the appropriate number of values from the stack, and you are
+responsible for adding stack marks if necessary.
+
+You should also "register" your op with the Perl interpreter so that it
+can produce sensible error and warning messages. Since it is possible to
+have multiple custom ops within the one "logical" op type C<OP_CUSTOM>,
+Perl uses the value of C<< o->op_ppaddr >> as a key into the
+C<PL_custom_op_descs> and C<PL_custom_op_names> hashes. This means you
+need to enter a name and description for your op at the appropriate
+place in the C<PL_custom_op_names> and C<PL_custom_op_descs> hashes.
+
+Forthcoming versions of C<B::Generate> (version 1.0 and above) should
+directly support the creation of custom ops by name; C<Opcodes::Custom> 
+will provide functions which make it trivial to "register" custom ops to
+the Perl interpreter.
+
 =head1 AUTHORS
 
 Until May 1997, this document was maintained by Jeff Okamoto
--- dump.c~	Thu Aug 23 23:02:36 2001
+++ dump.c	Thu Aug 23 23:04:01 2001
@@ -381,7 +381,7 @@
 	PerlIO_printf(file, "    ");
     PerlIO_printf(file,
 		  "%*sTYPE = %s  ===> ",
-		  (int)(PL_dumpindent*level-4), "", PL_op_name[o->op_type]);
+		  (int)(PL_dumpindent*level-4), "", OP_NAME(o));
     if (o->op_next) {
 	if (o->op_seq)
 	    PerlIO_printf(file, "%d\n", o->op_next->op_seq);
--- embed.pl~	Thu Aug 23 23:18:14 2001
+++ embed.pl	Thu Aug 23 23:21:09 2001
@@ -2248,6 +2248,10 @@
 Ap	|void	|sys_intern_clear
 Ap	|void	|sys_intern_init
 #endif
+#if defined(PERL_CUSTOM_OPS)
+Ap	|char *	|custom_op_name|OP* op
+Ap	|char *	|custom_op_desc|OP* op
+#endif
 
 #if defined(PERL_OBJECT)
 protected:
--- intrpvar.h~	Thu Aug 23 23:35:20 2001
+++ intrpvar.h	Sat Aug 25 11:37:08 2001
@@ -487,6 +487,10 @@
 
 PERLVAR(Isavebegin,     bool)	/* save BEGINs for compiler	*/
 
+#ifdef PERL_CUSTOM_OPS
+PERLVAR(Icustom_op_names, HV*)  /* Names of user defined ops */
+PERLVAR(Icustom_op_descs, HV*)  /* Descriptions of user defined ops */
+#endif
 /* New variables must be added to the very end for binary compatibility.
  * XSUB.h provides wrapper functions via perlapi.h that make this
  * irrelevant, but not all code may be expected to #include XSUB.h. */
--- op.c~	Thu Aug 23 22:51:28 2001
+++ op.c	Sat Aug 25 11:42:32 2001
@@ -72,7 +72,7 @@
 S_no_fh_allowed(pTHX_ OP *o)
 {
     yyerror(Perl_form(aTHX_ "Missing comma after first argument to %s function",
-		 PL_op_desc[o->op_type]));
+		 OP_DESC(o)));
     return o;
 }
 
@@ -94,7 +94,7 @@
 S_bad_type(pTHX_ I32 n, char *t, char *name, OP *kid)
 {
     yyerror(Perl_form(aTHX_ "Type of arg %d to %s must be %s (not %s)",
-		 (int)n, name, t, PL_op_desc[kid->op_type]));
+		 (int)n, name, t, OP_DESC(kid)));
 }
 
 STATIC void
@@ -1133,7 +1133,7 @@
     case OP_GETLOGIN:
       func_ops:
 	if (!(o->op_private & (OPpLVAL_INTRO|OPpOUR_INTRO)))
-	    useless = PL_op_desc[o->op_type];
+	    useless = OP_DESC(o);
 	break;
 
     case OP_RV2GV:
@@ -1502,7 +1502,7 @@
 		      ? "do block"
 		      : (o->op_type == OP_ENTERSUB
 			? "non-lvalue subroutine call"
-			: PL_op_desc[o->op_type])),
+			: OP_DESC(o))),
 		     type ? PL_op_desc[type] : "local"));
 	return o;
 
@@ -1964,7 +1964,7 @@
 	     type != OP_PUSHMARK)
     {
 	yyerror(Perl_form(aTHX_ "Can't declare %s in \"%s\"",
-			  PL_op_desc[o->op_type],
+			  OP_DESC(o),
 			  PL_in_my == KEY_our ? "our" : "my"));
 	return o;
     }
@@ -5416,7 +5416,7 @@
 	    break;
 	default:
 	    Perl_croak(aTHX_ "%s argument is not a HASH or ARRAY element or slice",
-		  PL_op_desc[o->op_type]);
+		  OP_DESC(o));
 	}
 	op_null(kid);
     }
@@ -5521,14 +5521,14 @@
 	    (void) ref(kid, o->op_type);
 	    if (kid->op_type != OP_RV2CV && !PL_error_count)
 		Perl_croak(aTHX_ "%s argument is not a subroutine name",
-			   PL_op_desc[o->op_type]);
+			   OP_DESC(o));
 	    o->op_private |= OPpEXISTS_SUB;
 	}
 	else if (kid->op_type == OP_AELEM)
 	    o->op_flags |= OPf_SPECIAL;
 	else if (kid->op_type != OP_HELEM)
 	    Perl_croak(aTHX_ "%s argument is not a HASH or ARRAY element",
-		       PL_op_desc[o->op_type]);
+		       OP_DESC(o));
 	op_null(kid);
     }
     return o;
@@ -5806,7 +5806,7 @@
 		    }
 		    else if (kid->op_type == OP_READLINE) {
 			/* neophyte patrol: open(<FH>), close(<FH>) etc. */
-			bad_type(numargs, "HANDLE", PL_op_desc[o->op_type], kid);
+			bad_type(numargs, "HANDLE", OP_DESC(o), kid);
 		    }
 		    else {
 			I32 flags = OPf_SPECIAL;
@@ -5874,7 +5874,7 @@
 	}
 	o->op_private |= numargs;
 	if (kid)
-	    return too_many_arguments(o,PL_op_desc[o->op_type]);
+	    return too_many_arguments(o,OP_DESC(o));
 	listkids(o);
     }
     else if (PL_opargs[type] & OA_DEFGV) {
@@ -5886,7 +5886,7 @@
 	while (oa & OA_OPTIONAL)
 	    oa >>= 4;
 	if (oa && oa != OA_LIST)
-	    return too_few_arguments(o,PL_op_desc[o->op_type]);
+	    return too_few_arguments(o,OP_DESC(o));
     }
     return o;
 }
@@ -5985,7 +5985,7 @@
 
     kid = cLISTOPo->op_first->op_sibling;
     if (!kid || !kid->op_sibling)
-	return too_few_arguments(o,PL_op_desc[o->op_type]);
+	return too_few_arguments(o,OP_DESC(o));
     for (kid = kid->op_sibling; kid; kid = kid->op_sibling)
 	mod(kid, OP_GREPSTART);
 
@@ -6490,7 +6490,7 @@
     scalar(kid);
 
     if (kid->op_sibling)
-	return too_many_arguments(o,PL_op_desc[o->op_type]);
+	return too_many_arguments(o,OP_DESC(o));
 
     return o;
 }
@@ -7082,6 +7082,44 @@
     }
     LEAVE;
 }
+
+#ifdef PERL_CUSTOM_OPS
+char* custom_op_name(pTHX_ OP* o)
+{
+    IV  index = PTR2IV(o->op_ppaddr);
+    SV* keysv;
+    HE* he;
+
+    if (!PL_custom_op_names) /* This probably shouldn't happen */
+        return PL_op_name[OP_CUSTOM];
+
+    keysv = sv_2mortal(newSViv(index));
+
+    he = hv_fetch_ent(PL_custom_op_names, keysv, 0, 0);
+    if (!he)
+        return PL_op_name[OP_CUSTOM]; /* Don't know who you are */
+
+    return SvPV_nolen(HeVAL(he));
+}
+
+char* custom_op_desc(pTHX_ OP* o)
+{
+    IV  index = PTR2IV(o->op_ppaddr);
+    SV* keysv;
+    HE* he;
+
+    if (!PL_custom_op_descs)
+        return PL_op_desc[OP_CUSTOM];
+
+    keysv = sv_2mortal(newSViv(index));
+
+    he = hv_fetch_ent(PL_custom_op_descs, keysv, 0, 0);
+    if (!he)
+        return PL_op_desc[OP_CUSTOM];
+
+    return SvPV_nolen(HeVAL(he));
+}
+#endif
 
 #include "XSUB.h"
 
--- opcode.pl~	Thu Aug 23 22:29:43 2001
+++ opcode.pl	Thu Aug 23 23:10:39 2001
@@ -65,6 +65,16 @@
 
 START_EXTERN_C
 
+#ifdef PERL_CUSTOM_OPS
+#define OP_NAME(o) (o->op_type == OP_CUSTOM ? custom_op_name(o) : \\
+                    PL_op_name[o->op_type])
+#define OP_DESC(o) (o->op_type == OP_CUSTOM ? custom_op_desc(o) : \\
+                    PL_op_desc[o->op_type])
+#else
+#define OP_NAME(o) PL_op_name[o->op_type]
+#define OP_DESC(o) PL_op_desc[o->op_type]
+#endif
+
 #ifndef DOINIT
 EXT char *PL_op_name[];
 #else
@@ -130,7 +140,7 @@
 END
 
 for (@ops) {
-    print "\tMEMBER_TO_FPTR(Perl_pp_$_),\n";
+    print "\tMEMBER_TO_FPTR(Perl_pp_$_),\n" unless $_ eq "custom";
 }
 
 print <<END;
@@ -291,6 +301,7 @@
 
 for (@ops) {
     next if /^i_(pre|post)(inc|dec)$/;
+    next if /^custom$/;
     print PP "PERL_PPDEF(Perl_pp_$_)\n";
     print PPSYM "Perl_pp_$_\n";
 }
@@ -887,3 +898,5 @@
 # Control (contd.)
 setstate	set statement info	ck_null		s;
 method_named	method with known name	ck_null		d$
+
+custom		unknown custom operator	ck_null		0 
--- pp_ctl.c~	Thu Aug 23 23:02:02 2001
+++ pp_ctl.c	Thu Aug 23 23:02:20 2001
@@ -1195,27 +1195,27 @@
 	case CXt_SUBST:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting substitution via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    break;
 	case CXt_SUB:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting subroutine via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    break;
 	case CXt_FORMAT:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting format via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    break;
 	case CXt_EVAL:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting eval via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    break;
 	case CXt_NULL:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting pseudo-block via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    return -1;
 	case CXt_LOOP:
 	    if (!cx->blk_loop.label ||
@@ -1330,27 +1330,27 @@
 	case CXt_SUBST:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting substitution via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    break;
 	case CXt_SUB:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting subroutine via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    break;
 	case CXt_FORMAT:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting format via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    break;
 	case CXt_EVAL:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting eval via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    break;
 	case CXt_NULL:
 	    if (ckWARN(WARN_EXITING))
 		Perl_warner(aTHX_ WARN_EXITING, "Exiting pseudo-block via %s",
-			PL_op_name[PL_op->op_type]);
+			OP_NAME(PL_op));
 	    return -1;
 	case CXt_LOOP:
 	    DEBUG_l( Perl_deb(aTHX_ "(Found loop #%ld)\n", (long)i));
--- pp_sys.c~	Thu Aug 23 23:06:56 2001
+++ pp_sys.c	Thu Aug 23 23:08:49 2001
@@ -2150,7 +2150,7 @@
     if (SvPOK(argsv)) {
 	if (s[SvCUR(argsv)] != 17)
 	    DIE(aTHX_ "Possible memory corruption: %s overflowed 3rd argument",
-		PL_op_name[optype]);
+		OP_NAME(PL_op));
 	s[SvCUR(argsv)] = 0;		/* put our null back */
 	SvSETMAGIC(argsv);		/* Assume it has changed */
     }
--- run.c~	Thu Aug 23 23:04:46 2001
+++ run.c	Thu Aug 23 23:05:18 2001
@@ -67,7 +67,7 @@
     CV *cv;
     SV *sv;
     STRLEN n_a;
-    Perl_deb(aTHX_ "%s", PL_op_name[o->op_type]);
+    Perl_deb(aTHX_ "%s", OP_NAME(o));
     switch (o->op_type) {
     case OP_CONST:
 	PerlIO_printf(Perl_debug_log, "(%s)", SvPEEK(cSVOPo_sv));
--- sv.c~	Thu Aug 23 23:00:28 2001
+++ sv.c	Thu Aug 23 23:06:36 2001
@@ -540,7 +540,7 @@
 {
     if (PL_op)
 	Perl_warner(aTHX_ WARN_UNINITIALIZED, PL_warn_uninit,
-		    " in ", PL_op_desc[PL_op->op_type]);
+		    " in ", OP_DESC(PL_op));
     else
 	Perl_warner(aTHX_ WARN_UNINITIALIZED, PL_warn_uninit, "", "");
 }
@@ -1616,7 +1616,7 @@
     case SVt_PVFM:
     case SVt_PVIO:
 	Perl_croak(aTHX_ "Can't coerce %s to integer in %s", sv_reftype(sv,0),
-		   PL_op_desc[PL_op->op_type]);
+		   OP_DESC(PL_op));
     }
     (void)SvIOK_only(sv);			/* validate number */
     SvIVX(sv) = i;
@@ -1727,7 +1727,7 @@
     case SVt_PVFM:
     case SVt_PVIO:
 	Perl_croak(aTHX_ "Can't coerce %s to number in %s", sv_reftype(sv,0),
-		   PL_op_name[PL_op->op_type]);
+		   OP_NAME(PL_op));
     }
     SvNVX(sv) = num;
     (void)SvNOK_only(sv);			/* validate number */
@@ -1807,7 +1807,7 @@
     if (PL_op)
 	Perl_warner(aTHX_ WARN_NUMERIC,
 		    "Argument \"%s\" isn't numeric in %s", tmpbuf,
-		PL_op_desc[PL_op->op_type]);
+		OP_DESC(PL_op));
     else
 	Perl_warner(aTHX_ WARN_NUMERIC,
 		    "Argument \"%s\" isn't numeric", tmpbuf);
@@ -3355,7 +3355,7 @@
 			if (first && ch > 255) {
 			    if (PL_op)
 				Perl_warner(aTHX_ WARN_UTF8, "Wide character in byte %s",
-					   PL_op_desc[PL_op->op_type]);
+					   OP_DESC(PL_op));
 			    else
 			        Perl_warner(aTHX_ WARN_UTF8, "Wide character in byte");
 			    first = 0;
@@ -3370,7 +3370,7 @@
 		else {
 		    if (PL_op)
 		        Perl_croak(aTHX_ "Wide character in %s",
-				   PL_op_desc[PL_op->op_type]);
+				   OP_DESC(PL_op));
 		    else
 		        Perl_croak(aTHX_ "Wide character");
 		}
@@ -3597,7 +3597,7 @@
     case SVt_PVIO:
 	if (PL_op)
 	    Perl_croak(aTHX_ "Bizarre copy of %s in %s", sv_reftype(sstr, 0),
-		PL_op_name[PL_op->op_type]);
+		OP_NAME(PL_op));
 	else
 	    Perl_croak(aTHX_ "Bizarre copy of %s", sv_reftype(sstr, 0));
 	break;
@@ -6760,7 +6760,7 @@
     else {
 	if (SvTYPE(sv) > SVt_PVLV && SvTYPE(sv) != SVt_PVFM) {
 	    Perl_croak(aTHX_ "Can't coerce %s to string in %s", sv_reftype(sv,0),
-		PL_op_name[PL_op->op_type]);
+		OP_NAME(PL_op));
 	}
 	else
 	    s = sv_2pv_flags(sv, lp, flags);
--- utf8.c~	Thu Aug 23 22:58:48 2001
+++ utf8.c	Thu Aug 23 23:09:10 2001
@@ -428,7 +428,7 @@
 
 	    if (PL_op)
 		Perl_warner(aTHX_ WARN_UTF8,
-			    "%s in %s", s,  PL_op_desc[PL_op->op_type]);
+			    "%s in %s", s,  OP_DESC(PL_op));
 	    else
 		Perl_warner(aTHX_ WARN_UTF8, "%s", s);
 	}

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About