(Sorry about the title. Don’t you hate it when you’ve got a syntax question and you don’t even know what to google to find out what it means…?)

draegtun dropped by recently to offer suggestions for tuning up some of my Perl code… and some of his code appeared to do some strange things. (Thanks for the help, as always….) Maybe it was something to do with closures and maybe it was operating directly on the internal function table of Perl. So I decided to mess with it some before rolling it out to my mini-project. I’m sure draegtun ‘s a nice guy and all, but I remember all to well the poor wannabe lisp hacker that picked up code for formatting his hard drive via some random use-group advice…. You just can’t be too careful…. 😉 Yeah, this is silly… but this is what passes for understanding code snippets before plugging them in….

Anyways, flipping through the Camel book I quickly came across a similar example in the section on closures. I couldn’t find out what the weird * sigil meant even after flipping around in the index. Logically, Perl is a postmodern language. Therefore the * sigil means the same thing as it did in Kurt Vonnegut’s Breakfast of Champions. Obviously, applying the * to a scalar shoves its associated reference up Perl’s hind end. Perl’s DWIM features should cause it to psychically intuit which internal table you meant it to go to based on the type of reference you’re passing…. (Well, I figure if I can think like Larry, then learning Perl will be a lot easier. That’s my theory, anyway. Let’s see if this is right….)

#/usr/bin/perl use strict; use warnings; sub hotwire { my $number = 42; my $string = "foo"; my $subname = "blarney"; no strict 'refs'; *$subname = sub { print "Yes, it's $number and $string.

"; }; } hotwire(); my $number = 12; my $string = "bar"; blarney();

Okay, this is fun…. Now, we know from the opening chapter of Intermediate Perl that you can use eval as a substitute for the type of error-catching try/catch block activity you might have seen in other languages. Of course, as in other languages, using eval for what it’s supposed to be used is generally stupid and a form of language abuse. However we feel it is our divine right to abuse whatever language we have at our disposal, so let’s just check to see that we can load evaluated text into our environment as a function:

sub loadme { my ($name, $code) = @_; no strict 'refs'; *$name = eval $code; } loadme("noway", 'sub { print "yadda yadda yadda

"; }'); noway();

Wow… it works! Woo-hoo! Okay…. Let’s try adding in our own syntactic sugar. We’ll create a function and we’ll search for a pattern and replace it with our function call:

sub check { my ($a, $b, $c) = @_; return "$a...$b...$c..."; } sub loadme2 { my ($name, $code) = @_; $code =~ s/(\w+)~(\w+)~(\w+)/check('$1','$2','$3')/g; #print "$code

"; no strict 'refs'; *$name = eval $code; } loadme2("yesway", 'sub { my $a = 99; print try~this~out, "

", $a, "

"; }'); yesway();

Dumb dumb dumb! There are surely better ways to accomplish this! But yeah, it’s nice to know that (in a pinch) we can do evil nasty things like this…. Let’s try one more variation that loads the text in from a file. Also, let’s store our reference in a scalar variable instead of hotwiring the package’s table of functions….

open TEXTFILE, "< loadthis"; my $file; while (<TEXTFILE>) { $file .= $_; } close TEXTFILE; loadme2("crazy", $file); crazy(); sub loadme3 { my ($code) = @_; $code =~ s/(\w+)~(\w+)~(\w+)/check('$1','$2','$3')/g; return eval $code; } my $C = loadme3($file); print "Now we'll try running the code from a scalar...

"; $C->();

There we go!

Now… this is evil… this is wrong… this is bogus and brain dead. But but by marrying this to our table parsing code, we can create our own Frankenstein style scripting language. We already can create specialized data structures can initialize them with data parsed from a pseudo-DSL. We can customize these structures with perl code tacked on however we like. This can be basic event and override code like you see in GUI frameworks. I figure that the code is already interpreted for us into compiled-ish code when the references are read in, so you won’t see any real performance hit when you call these things– they should be indistinguishable from any other sub. Just look for things to slow down at startup as you add more and more of these to the script files. (Yeah, I know. I need to learn about hardware and assembly language– and I need to go pick up a comp sci degree, too– I’ll get around to it sometime. Right now I just want something that works….)

Back in Lisp we made the mistake of trying to create an all-new language that ran on top of Lisp. This was bad because we couldn’t take advantage of Lisp’s language features in our hacked/custom dsl-thing. (We should have gone with the grain of the language and modified Lisp slightly so that it became our lisp-ish DSL by itself.) In Blub, we hacked someone’s custom evaluator to go even further to craft our own language from scratch. This proved to be an unsustainable amount of work beyond a certain level of complexity. With Perl, we are compromising by creating special language-interfaces for initializing data structures… and then switching over to (maybe) slightly-abused Perl code where it suits. There’s better ways to do this, but this may be sufficient for a certain class of problems….

Probably what we’re doing is rolling our own Meta-object protocol. (After a fashion, anyway.) That’s not necessarily a complete waste of time: now maybe if we study it, we’ll have a use for it….

Share this: Twitter

Facebook

Like this: Like Loading... Related