Sign In/My Account | View Cart  
advertisement


Listen Print

Parrot : Some Assembly Required
by Simon Cozens | Pages: 1, 2

Some Parrot Programs

Now let's look at a few simple Parrot programs to give you a feel for the language.

Displaying the Time

This little program displays the Unix epoch time every second: (or so)

        set I3, 3000000
REDO:   time I1
        print I1
        print "\n"
        set I2 0
SPIN:   inc I2
        le I2, I3, SPIN, REDO

First, we set integer register 3 to contain 3 million - that's a completely arbitrary number, due to the fact that Parrot averages a massive 6 million ops per second on my machine. Then the program consists of two loops. The outer loop stores the current Unix time in integer register 1, prints it out, prints a new line and resets register 2 to zero. The inner loop increments register 2 until it reaches the 3 million we stored in register 3. When it is no longer less than (or equal to) 3 million, we go back to the REDO of the outer loop. In essence, we're just spinning around a busy loop to waste some time. This is only because Parrot doesn't currently have a "sleep" op; we'll see how to implement one later.

How do we run this? First, we need to assemble this into Parrot bytecode, with the assemble.pl provided. So, copy the assembler to a file showtime.pasm, and inside your Parrot directory, run:

      perl assemble.pl showtime.pasm > showtime.pbc
    

(.pbc is the file extension for Parrot bytecode.)

Finding a Fibonacci number

The Fibonnaci series is defined like this: take two numbers, 1 and 1. Then repeatedly add together the last two numbers in the series to make the next one: 1, 1, 2, 3, 5, 8, 13, and so on. The Fibonacci number fib(n) is the n'th number in the series. Here's a simple Parrot assembler program that finds the first 20 Fibonacci numbers:

# Some simple code to print some Fibonacci numbers
# Leon Brocard <acme@astray.com>

        print   "The first 20 fibonacci numbers are:\n"
        set     I1, 0
        set     I2, 20
        set     I3, 1
        set     I4, 1
REDO:   eq      I1, I2, DONE, NEXT
NEXT:   set     I5, I4
        add     I4, I3, I4
        set     I3, I5
        print   I3
        print   "\n"
        inc     I1
        branch  REDO
DONE:   end

This is the equivalent code in Perl:

        print "The first 20 fibonacci numbers are:\n";
        my $i = 0;
        my $target = 20;
        my $a = 1;
        my $b = 1;
        until ($i == $target) {
           my $num = $b;
           $b += $a;
           $a = $num;
           print $a,"\n";
           $i++;
        }
    

(As a fine point of interest, one of the shortest and certainly the most beautiful ways of printing out a Fibonacci series in Perl is perl -le '$b=1; print $a+=$b while print $b+=$a'.)

Jako

So much for programming in assembler; let's move on and look at a medium-level language - Jako. Jako was written by Gregor Purdy who obviously got sick (as a parrot) of programming in assembler. Jako looks a little bit like C and a little bit like Perl, and it can do anything you can do in Parrot assembler, but a little tidier. Let's try that Fibonacci program again:

        print("The first 20 fibonacci numbers are:\n");
        var int i = 0;
        var int target = 20;
        var int a = 1;
        var int b = 1;
        var int num;
        while (i != target) {
           num = b;
           b += a;
           a = num;
           print("$a\n");
           i++;
        }

Notice how similar this is to the Perl version? I stripped away the $ sigils, replaced the Perlish until with a more common while, replaced my with var int and I was nearly done.

The Jako compiler, jakoc, ships with Parrot in the little_languages subdirectory:

 % perl little_languages/jakoc fib.jako > fib.pasm
 % perl assemble.pl fib.pasm > fib.pbc
 % ./test_prog fib.pbc
The first 20 fibonacci numbers are:
1
2
3
...

Where Next?

Parrot is obviously developing very rapidly, and we still have a long way to go before we are ready for a compiler to this platform. This section is for those who are interested in helping us take Parrot further.

Adding Operations

The first thing we need is a lot more operations; but this needs to be carefully thought out, and all new proposals for operations should be checked by Dan Sugalski, the Parrot designer.

That said, adding operations to Parrot is actually simple. Let's add the sleep operator we were complaining about earlier.

To add an operator to the Parrot core, we need to edit two files: opcode_table, which contains the description of operations and their arguments, and basic_opcodes.ops, which contains the C implementation for our opcodes.

Although we've been able to say print "String" and print I3 to print a register, Parrots ops are not polymorphic - this is some trickery carried out by the assembler. Those two operations would be implemented by two different ops, print_sc to print a string constant, and print_i to print an integer register. So we'll add two ops sleep_i to sleep for the number of seconds determined by the contents of an integer register, and sleep_ic, to sleep for a constant number of seconds. Each op has one argument. At the top of opcode_table there is a list of argument types:

# Revised arg types:
#      i       Integer constant
#      I       Integer register
#      n       Numeric constant
#      N       Numeric register
#      s       String constant?
#      S       String register
#      D       Destination

So sleep_ic has 1 argument, i, and sleep_i has 1 argument, I. Let's add these into opcode_table in the "Miscellaneous and debugging ops" category:

 # Miscellaneous and debugging ops
 
 time_i              1                   I
 print_i             1                   I
 print_ic            1                   i
 time_n              1                   N
 print_n             1                   N
 print_nc            1                   n
+sleep_i             1                   I
+sleep_ic            1                   i
    

And now we need to implement them by adding to basic_opcodes.ops. The format of this file is a little funny; it's C, which is preprocessed by a Perl program. The C functions should be declared AUTO_OP, which means that the preprocessor will automatically work out the return address of the next op in the bytecode stream. (branch operators need special treatment, and as such are MANUAL_OPs) Parameters are denoted by P1, P2 and so on - they aren't actual parameters to the C function, but are pulled out of the bytecode stream. Finally, we can access register n by saying INT_REG(n), NUM_REG(n) and so on.

So let's do the constant sleep op first. We want to take the first parameter, P1, and pass it to sleep:

AUTO_OP sleep_ic {
     sleep(P1);
}

That was easy. The second op is only slightly more complex. We have to use INT_REG to retrieve the contents of the register:

AUTO_OP sleep_i {
     sleep(INT_REG(P1));
}

All that's missing is a test suite (see t/op/basic.t for an example) and some documentation (we need to add entries to docs/parrot_assembly.pod) and we've added our instructions to the Parrot CPU. The assembler will automatically determine whether we're sleeping for a constant time or a variable time, and will dispatch the right op when we just say sleep. Now we can rewrite the showtime code a little more neatly - or rather, you can, as a nice little exercise!

Vtable Datatypes

The next major thing that Parrot needs to implement is PMCs; these are almost like Perl 5's SVs, only more so. A PMC is an object of some type, which can be instructed to perform various operations. So when we say

      inc P1
    

to increment the value in PMC register 1, the increment method is called on the PMC - and it's up to the PMC how it handles that method.

PMCs are how we plan to make Parrot language-independent - a Perl PMC would have different behavior from a Python PMC or a Tcl PMC. The individual methods are function pointers held in a structure called a vtable, and each PMC has a pointer to the vtable which implements the methods of its "class." Hence a Perl interpreter would link in a library full of Perl-like classes and its PMCs would have Perl-like behavior.

We've already designed the vtables and the structure of PMCs, but sitting down and implementing them is one of the priorities for Parrot development that will make it truly useful.

More Todos

There are a huge number of things we want to do with Parrot: We need, for instance, to create some I/O operators to make programs actually interesting; we want to create a range of string functions to deal with various encodings and convert between them; we want more documentation; we really, really need more tests; we want to check Parrot's portability to various platforms; and finally, there are more ops that we need to implement.

Mastering Algorithms with PerlMastering Algorithms with Perl
By Jon Orwant, Jarkko Hietaniemi & John Macdonald
1st Edition August 1999
1-56592-398-7, Order Number: 3987
701 pages, $34.95

Getting Involved

We have a good number of people working on Parrot, but we could always use a few more. To help out, you'll need a subscription to the perl6-internals mailing list, where all the development takes place; you should keep up to date with the CVS version of Parrot - if you want to be alerted to CVS commits, you can subscribe to the cvs-parrot mailing list. CVS commit access is given to those who taken responsibility for a particular area of Parrot, or who often commit high-quality patches.

A useful Web page is cvs.perl.org, which reminds you how to use CVS and allows you to browse the CVS repository; the code page is a summary of this information and other resources about Parrot.

So don't delay - pick up a Parrot today!