Parrot Plumage Day 4: First Bones of the Skeleton

geoffrey on 2009-09-14T07:51:51

Day 1 and Day 2/3 got me (mostly) prepared to begin the actual coding for the Parrot Plumage prototype. There were just two things left to figure out: how to access command-line arguments, and how to initialize complex data structures.

The first problem turned out to be relatively simple to solve with a couple more additions to the glue library to initialize @ARGS and $PROGRAM_NAME from the IGLOBALS_ARGV_LIST field of the Parrot interpreter globals.

The second problem (initializing complex data structures) required a couple silly hacks, but I got a decent workaround in the end. Allow me to explain ....

NQP natively can index into hashes, arrays, and complex arrangements of same with relative ease. Want to access an entry in a hash of arrays of hashes, with some fixed keys and some varying keys? No problem. Something like this will do:

$age := %pets{$type}[$num];

The problem is, there's no short and simple way to initialize the %pets hash. NQP doesn't have syntax for hash or array literals, which makes it rather painful to instantiate complex structures. Oh, I suppose you could write a whole bunch of these:

%pets[0] := 5;
%pets[1] := 6;
%pets[0] := 4;
...

but as you can imagine that gets old rather quickly -- and doing the initialization in raw PIR would be even more painful. Luckily, during my previous hack day I'd brought the JSON parser up to current standards, so I used it for a little trick. I defined the data structure I wanted as a JSON string, parsed it into a real data structure using the data_json language that now ships with Parrot, and then ran a fixup routine on the few bits that didn't translate easily.

That last bit might require a little explanation. I could only create data structures this way containing data types the JSON language knows how to represent: numbers, strings, arrays, hashes, booleans, etc. However, there's no way to represent a code reference, and one of the applications I had in mind was a function table for mapping user commands, such as version and install to the subroutines that would implement them.

My solution was to replace the code references with the names of the subroutines I wanted in the JSON text, and then after parsing the JSON run a peculiar bit of inline PIR to replace the names with the real code references. The fixup function in question looks like this:

sub fixup_commands ($commands) {
    Q:PIR{
        $P0 = find_lex '$commands'
        $P1 = iter $P0
      fixup_loop:
        unless $P1 goto fixup_loop_end
        $S0 = shift $P1
        $P2 = $P1[$S0]
        $S1 = $P2['action']
        $P3 = get_hll_global $S1
        $P2['action'] = $P3
        goto fixup_loop
      fixup_loop_end:
    };

    return $commands;
}

The short explanation is that the loop in the above PIR block iterates over each entry in the $commands hash, and for each action key replaces the matching string value (the subroutine name) with the subroutine itself, looked up using the get_hll_global op. And it works!

These problems out of the way, I could finally start writing a prototype of the plumage command line tool. Time eventually ran out on my hacking day, but I managed to implement basic versions of usage, version, and info commands, and a command parser and dispatcher that could be charitably described as "beyond minimalist".

While the first two commands just provide info about plumage itself, the last one is the first command that actually provides real functionality. In particular, info simply loads the metadata for a given project and prints the details for the user.

In order to have some metadata to display with my brand new info command, I took a shot at filling in the fields from the Metadata Proposal manually for a single project. In this case, I chose Blizkost because it's useful, cool, and non-trivial without being insane.

Unsurprisingly, I came across a couple underspecified bits in the metadata proposal, but things mostly seemed to make sense. The result is a tad wordy, but it is after all intended to be generated mostly automatically.

That done, I found myself out of time for this hack day, but things are looking up for implementing an ultra-simple version of the install command next time. Until then, I invite you to browse the repository or just come by #parrot on irc.parrot.org and ping me (japhb). See you there!