Stories
Slash Boxes
Comments

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

drhyde (1683)

drhyde
  (email not shown publicly)
http://www.cantrell.org.uk/david
Submitted by drhyde on 2008.05.12 13:51
At the user's request, CPANdeps has sprouted another tentacle. There's now a ticky box which, if enabled, makes it also fetch MANIFEST files for the entire dependency tree, and highlight any modules (actually distributions) that don't look like they're pure perl. The heuristic is simple. We assume that a distribution is pure perl unless it contains a file that ends in .xs, .c or .h, or it depends on Inline::*. But if there is any file in the distro whose name contains the string 'pureperl' (case-insensitive, naturally), then we assume that the distribution contains a pure perl version as well as the C-ish version.

If there's any other things I should look for, please use the "report bugs" link on the website.

  How useful closures are! 2008-02-24 16:23 drhyde

Submitted by drhyde on 2008.02.24 16:23
drhyde writes "As some of you may be aware, I'm working on a pure-perl Z80 emulator. Why? <shrug> Why not! And we can't go letting the 6502 boys have all the fun!

It also makes a very good example of how useful closures can be. Here's just one example.

The Z80 is an 8 bit processor, with several 8 bit registers. But it also has some 16 bit "reigster pairs", which are made up of two 8 bit registers that can be treated as a single larger register. For example, the 8 bit register B and C can be combined to make a 16 bit register BC. Any operation that changes B or C also changes BC, and vice versa. Now, to implement them, I could have lots of hairy logic so that any instruction that operates on BC actually operates on both B and C. But that would lead to lots of duplication of code, for all the register pairs, and for all the instructions that operate on 'em.

I already have a ::Register8 class for an 8 bit register, and a Register16 class for real 16 bit registers such as the Program Counter (a register that points at the next instruction to execute) which store a value and have get() and set() methods. So, I thought, instead of putting lots of hairy logic in the instructions themselves, I could move it into the registers so I'd only have to write it once. The Register* classes already have a 'value' field for storing the register's current value. So I changed Register16 to have either a value field or a pair of 'get' and 'set' fields, depending on how the register was initialised in perl. If it has a value field, then get() and set() simply operate on that. But if instead it has get/set fields, then get() and set() call those code-refs instead.

So now, I can create a 16 bit register pair like this ...

my $BC = CPU::Emulator::Z80::Register16->new(
  get => sub {
    return 256 * $self->register('B')->get() +
                 $self->register('C')->get()
  },
  set => sub {
    my $value = shift;
    $self->register('B')->set($value >>8);
    $self->register('C')->set($value & 0xFF);
  }
);

and then as far as implementing the actual Z80 instructions goes, I can just get() and set() sixteen bit registers with a single method call, instead of doing all the multiplication, shifting, bitmasking and so on every time. Note that I use a variable $self inside the two anonymous subroutines without declaring it with my. This isn't a bug. That is the $self in the code that creates the anonysubs. It has nothing to do with whatever $self might be inside a Register16 class's get() or set() method.

But there are actually several register-pairs, so I went one step further. Instead of repeating that chunk of code several times, once for each pair, I did this:

$AF = _derive_register16($self, qw(A F));
$BC = _derive_register16($self, qw(B C));
$DE = _derive_register16($self, qw(D E));

...

sub _derive_register16 {
  my($self, $high, $low) = @_;
  return CPU::Emulator::Z80::Register16->new(
    get => sub {
      return 256 * $self->register($high)->get() +
                   $self->register($low)->get()
    },
    set => sub {
      my $value = shift;
      $self->register($high)->set($value >>8);
      $self->register($low)->set($value & 0xFF);
    },
  );
}

In this case, I pass $self and the names of two 8 bit registers to the _derive_register16 function. That function then creates and returns a Register16 now "closing over" 3 variables.

Closures rock. By combining objects and closures I have ended up writing a lot less code. And by reducing the amount of duplication in my code, when I inevitably find a bug, I'll have just one place to fix it, instead of having to remember all the hundred places where I twiddle register pairs. If you ever find yourself writing the same code over and over again just with minimally different data, this is a useful technique.

You can see what I'm up to on this project by looking at the CVS repository.

"

  Please try to break our code! 2007-10-19 09:31 drhyde

Submitted by drhyde on 2007.10.19 9:31
Following on from Devel::CheckOS I have been working with David Golden on Devel::CheckLib. It's similar to Devel::CheckOS, in that it will let module authors specify dependencies which aren't just other perl modules - if they need, eg, libjpeg, then they can use this module in Makefile.PL / Build.PL to make it a pre-req, and if it's missing then the CPAN testers will SHUT THE HELL UP ABOUT IT and not annoy the author.

It's still in something of a state of flux so not ready for real-world use yet, but we're at the stage where we need lots of people to beat it to hell and back. Please try to run the tests on as many platforms as possible, with as many perls as possible, and in particular using perls built with as many compilers and linkers as possible. Send the results - both successes and failures - to me by email along with the output from perl -V (that's a capital V).

We're particularly interested in results from people whose perl was built with something other than gcc or Microsoft C, as we don't have easy access to the proprietary Sun/SGI/DEC etc compilers or Borland C.

The more alert of you will have noticed that there is a bootstrapping problem in using this from within a Makefile.PL - relax, it will come with a script to bundle itself in an inc/ directory.

  Please try to break our code! 2007-10-19 09:31

Journal by drhyde on 2007.10.19 9:31

Following on from Devel::CheckOS I have been working with David Golden on Devel::CheckLib. It's similar to Devel::CheckOS, in that it will let module authors specify dependencies which aren't just other perl modules - if they need, eg, libjpeg, then they can use this module in Makefile.PL / Build.PL to make it a pre-req, and if it's missing then the CPAN testers will SHUT THE HELL UP ABOUT IT and not annoy the author.

It's still in something of a state of flux so not ready for real-world use yet, but we're at the stage where we need lots of people to beat it to hell and back. Please try to run the tests on as many platforms as possible, with as many perls as possible, and in particular using perls built with as many compilers and linkers as possible. Send the results - both successes and failures - to me by email along with the output from perl -V (that's a capital V).

We're particularly interested in results from people whose perl was built with something other than gcc or Microsoft C, as we don't have easy access to the proprietary Sun/SGI/DEC etc compilers or Borland C.

The more alert of you will have noticed that there is a bootstrapping problem in using this from within a Makefile.PL - relax, it will come with a script to bundle itself in an inc/ directory.

  Devel::CheckOS and Devel::AssertOS released 2007-10-01 05:26 drhyde

Submitted by drhyde on 2007.10.01 5:26
Last night I released Devel::CheckOS and Devel::AssertOS. They are a wrapper around $^O with some extra functionality. For example, where you can check $^O's value to see if you're running on a Linux system, you can use these modules to check for OS 'families' as well, such as whether you're running on a Unix system.

Devel::CheckOS provides functions which return true or false depending on whether there's a match. Devel::AssertOS dies when you try to use it if there's no match.

The intention is that authors will use these modules whenever they write platform-specific code so that it will die with an appropriate message, which will be picked up by CPAN testers, and make them SHUT THE HELL UP when they try to do stupid things like test Solaris::Foo on Linux or VMS::Bar on Windows.

Journal by drhyde on 2007.10.01 5:26

Last night I released Devel::CheckOS and Devel::AssertOS. They are a wrapper around $^O with some extra functionality. For example, where you can check $^O's value to see if you're running on a Linux system, you can use these modules to check for OS 'families' as well, such as whether you're running on a Unix system.

Devel::CheckOS provides functions which return true or false depending on whether there's a match. Devel::AssertOS dies when you try to use it if there's no match.

The intention is that authors will use these modules whenever they write platform-specific code so that it will die with an appropriate message, which will be picked up by CPAN testers, and make them SHUT THE HELL UP when they try to do stupid things like test Solaris::Foo on Linux or VMS::Bar on Windows.

  CPAN dependencies and test results checker 2007-08-04 13:43 drhyde

Submitted by drhyde on 2007.08.04 13:43
After a discussion on the perl-qa list, and being blessed with a few hours free time, I wrote a shiny thing, which, given a module name on the CPAN, will find all its dependencies, and their dependencies yea even unto the Nth generation, and display a nice shiny report on their CPAN-testers results.

I expect it to be useful for authors trying to decide what to depend on (you don't want to depend on stuff that itself has fragile dependencies) and also for people trying to figger out why the hell some random module won't install.

To see half the CPAN, try pointing it at Angerwhale.

Journal by drhyde on 2007.08.04 13:43

After a discussion on the perl-qa list, and being blessed with a few hours free time, I wrote a shiny thing, which, given a module name on the CPAN, will find all its dependencies, and their dependencies yea even unto the Nth generation, and display a nice shiny report on their CPAN-testers results.

I expect it to be useful for authors trying to decide what to depend on (you don't want to depend on stuff that itself has fragile dependencies) and also for people trying to figger out why the hell some random module won't install.

To see half the CPAN, try pointing it at Angerwhale.

update: it now calculates the likelihood of a trouble-free install, and has been made much faster by having a local copy of the CPAN testers database and moving to mod_perl