Parrot Virtual Machine/Not Quite Perl

Not Quite Perl
Not Quite Perl (NQP) is an implementation of a subset of the Perl 6 language which was originally intended to help bootstrap the implementation of Perl 6. In other words, the Perl 6 developers are writing the Perl 6 compiler in a subset of the Perl 6 language itself. This bootstrapping was accomplished by first writing a small NQP compiler using PIR. After the NQP compiler was completed, programs could then be written in NQP instead of having to write them entirely in PIR.

NQP is not just a tool reserved for use with Perl 6, however. Other languages are using NQP as a light-weight implementation language. A major benefit to NQP is that it does not rely on any external code libraries which would be subject to change over time. Because of its small footprint, however, NQP tends to lack many features of higher-level programming languages, and learning to program without using some common constructs can be challenging at first.

Variables in NQP
Here we are going to discuss some of the basics of NQP programming. Experienced Perl programmers, even programmers who are familiar with Perl 5 but not necessarily Perl 6, will find most of this to be a simple review.

NQP is not perl5 or perl 6. This point cannot be stressed enough. There are a lot of features from Perl that are missing in NQP. Sometimes, this means you need to do some tasks the hard way. In NQP, we use the  operator, which is called the bind operator. Unlike normal variable assignment, bind does not copy the value from one "container" to another. Instead, it creates a link between the two variables, and they are, from that point forward, aliases of the same container. This is similar to the way copying a pointer in C does not copy the data being pointed to.

Variables in NQP typically have one of three basic types: scalars, arrays, and hashes. Scalars are single values, like an integer, a floating point number, or a string. Arrays are lists of scalars that are accessed with an integer index. A hash is a list of scalars that use a string, called a key, for indexing. All variable names have a sigil in front of them. A sigil is a punctuation symbol like "$", "@", or "%" that tells the type of the variable.

Scalar variables have a "$" sigil. The following are examples of scalar values:

$x := 5; $mystring := "string"; $pi := 3.1415;

Arrays use the "@" sigil. We can use arrays like this:

@myarray[1] := 5; @b[2] := @a[3];

Notice that NQP does not have a list context like Perl6 has. This means you can't do a list-assignment, like:

@b := (1, 2, 3); # WRONG! $b := (1, 2, 3); # CORRECT

NQP is designed to be bare-bones, as little as is needed to support development of Perl6. The above line could be written also:

@b[0] := 1; @b[1] := 2; @b[2] := 3;

We'll discuss this in more detail a little bit further down the page. Hashes are prefixed with the "%" sigil:

%myhash{'mykey'} := 7 %mathconstants{'pi'} := 3.1415; %mathconstants{'2pi'} := 2 * %mathconstants{'pi'};

Hashes, for people who aren't familiar with Perl, are also known as Dictionaries (in Python) or associative arrays. Basically, they are like arrays but with string indices instead of integer indices.

Where's My List Context?
As we mentioned before, there is no such thing in NQP as "array context", which Perl 5 programmer might have expected. One of the big features of the Perl language is that it's context-aware, and it treats things differently depending on whether you are in scalar or array context. Without this, it really isn't perl. That's why they call it NQP, because it's perl-ish, but isn't quite perl. In NQP you cannot write either of the following:

Lexical And Global Variables
All variables (hashes, scalars, and arrays) can be declared to be lexical with the keyword "my", or global with the keyword "our". For those readers who have read the sections on PIR, "my" variables correspond to the  directive, and the instructions , and. "our" variables correspond to the  and   instructions. Here's an example:

Likewise, for "our":

NQP Control Constructs
NQP has all the high-level control constructs that are missing in PIR. We have loops and If/Then/Else branches in a way that PIR does not have. Because this is a Perl-like language, the loops that NQP does have are varied and relatively high-level.

Branching Constructs
In terms of branches, we have:

if ($key eq 'foo') { THEN DO SOME FOO STUFF } elsif ($key eq 'bar') { THEN DO THE BAR-RELATED STUFF } else { OTHERWISE DO THIS }
 * If/Then/Else:


 * Unless/Then/Else:

Looping Constructs

 * For:A "For" loop iterates over a list and sets   to the current index, as in perl5. There's no c-style loop with STARTING_POINT and STEP_ACTION in NQP, although there is a similar construct in both Perl 5 and Perl 6. Here is a basic for loop:

for (1,2,3) { Do something with $_ }


 * Translated exactly into this PIR code:

.sub 'for_statement' .param pmc match .local pmc block, past $P0 = match['EXPR'] $P0 = $P0.'item' $P1 = match['block'] block = $P1.'item' block.'blocktype'('sub') .local pmc params, topic_var params = block[0] $P3 = get_hll_global ['PAST'], 'Var' topic_var = $P3.'new'('name'=>'$_', 'scope'=>'parameter') params.'push'(topic_var) block.'symbol'('$_', 'scope'=>'lexical') $P2 = get_hll_global ['PAST'], 'Op' $S1 = match['sym'] past = $P2.'new'($P0, block, 'pasttype'=>$S1, 'node'=>match) match.'result_object'(past) .end

You can also iterate over the keys of a hash like so:

for (keys %your_hash) { DO SOMETHING WITH %your_hash{$_} }

where   creates a list of all of the keys in  , and iterates through this list setting   to hold the current key.


 * While: "While" loops are similar to for loops. In NQP, a while loop looks like this:

while(EXIT_CONDITION) { LOOP_CONTENTS }


 * Which roughly becomes in PIR:

loop_top: if(!EXIT_CONDITION) goto loop_end LOOP_CONTENTS goto loop_top loop_end:


 * Do/While:A "do/while" loop is similar to a while loop except that the condition is tested at the end of the loop and not at the beginning. This means that the loop is always executed at least once, and possibly more times if the condition is not satisfied. In NQP:

do { LOOP_CONTENTS } while(EXIT_CONDITION);


 * In PIR:

loop_top: LOOP_CONTENTS if(!EXIT_CONDITION) goto loop_end goto loop_top loop_end:

Operators
NQP supports a small set of operators for manipulating variables.

The Match Object, Defaults, and Hashes
When a grammar rule matches and the  rule is performed, a special type of hash object called the match object is generated and passed to the associated NQP method. This match object is given the special name. You can name it something different if you like, but you would lose a lot of the power that makes the  variable so special.

Ordinarily when you reference an object in a hash, you would use  curly brackets. For example:

my %hash; %hash{'key'} = "value";

When you want to call a value from a hash reference, you would have to do something even more complex:

$hashref->{'key'} = "value";

However, with the special default match object, you can use  angle brackets instead. So, instead of writing

$/->{'key'}

We can write the less-verbose:

$

The keys of the hash object correspond to the names of the subrules used in the grammar. So, if we had the grammar rule:

rule my_rule { }

Our match object would have the fields:

$ $  $  $

If we have multiples of any one field, such as:

rule my_rule { }

Now,  and   are both two-item arrays. Also, we can extend this behavior to repetition operators in the grammar:

rule my_rule { + * }

Now, both  and   are arrays whose length indicate how many items were matched by each. You can use the + operator or the  function to get the number of items matched.

PIR Actions
NQP isn't the only way for writing action methods to accompany a grammar. It's an attractive tool for a number of reasons, but it isn't the only option. Action methods can also be written in PIR or PASM. This is how the NQP compiler itself is implemented. Here is an example of how a PIR action might look:

.sub 'block' :method .param pmc match .param string key .local pmc past $P0 = get_hll_global ['PAST'], 'Stmts' past = $P0.'new'('node' => match) ...   match.'result_object'(past)  # make $past; .end