In yesterday’s installment (Testing FizzBuzz in Parrot), I explained a test file for testing multiple Parrot implementations of the FizzBuzz problem. I also promised to show two different ways to solve the problem in Parrot. The test framework requires both approaches to take a single integer describing how many FizzBuzz elements to produce and to return an Array-like PMC containing the FizzBuzz strings.

Pre-populating an Array

The most straightforward approach is to create a new Array PMC and fill it in with the appropriate strings. Despite its procedural naïveté, it’s also the most straightforward application of the algorithm. It all takes place in the procedural function:

    42  .sub 'procedural'
    43      .param int end

The function starts as normal. It takes one parameter; an integer. Right now the code assumes that it will start at position 0 in the created array and loop until the integer counter reaches end.

    45      .local pmc results
    46      results = new .ResizableStringArray
    47  
    48      .local int count
    49      count   = 1
    50
    51      .local int    remainder
    52      .local string element

Lines 45 through 52 declare a couple of interesting variables. I chose to use a ResizableStringArray for ease of use. It would have been just as easy to use a FixedStringArray, but I thought showing the push code later would be clearer. Notice that count, the loop counter, starts at 1. This is important to avoid fencepost errors in the algorithm later.

I declared remainder and element outside of the loop (though there’s no real sense of “outside” in PIR) out of personal preference. I’m not aware of any real scoping issues in Parrot, except that declarations must precede use.

    54    loop_start:
    55      if count > end goto loop_end
    56      element = ''

This is the start of the loop. It tests the counter to see if the loop has run end times. If so, it quits looping. Otherwise, it initializes element to an empty string.

    58    check_fizz:
    59      remainder = count % 3
    60      if remainder goto check_buzz
    61      element .= 'Fizz'

The label in line 58 exists only for clarity. It’s not necessary for the algorithm. Lines 59 checks that the current element number is a multiple of three, using the modulus operator. Line 60 skips ahead if there’s a remainder. Otherwise, line 61 appends the string Fizz to the current value of element.

    63    check_buzz:
    64      remainder = count % 5
    65      if remainder goto add_element
    66      element .= 'Buzz'

No matter what’s in element, line 64 is the next line executed. It checks that the number of the current element is a multiple of five. If not, another goto skips line 66. Line 66 appends the string Buzz to the current element.

I deliberately kept the structure of check_fizz and check_buzz similar to make the code clearer. There are ways to reduce this duplication–perhaps turning this code into a parametrized function–but with only two occurrences, it wasn’t quite worth it.

    68    add_element:
    69      push results, element
    70      inc count
    71      goto loop_start

The loop ends by pushing element, which contains an empty string, Fizz, Buzz, or FizzBuzz, onto the results array. It increments the loop counter and restarts the loop.

    73    loop_end:
    74      .return( results )
    75  .end

When the loop ends, the function returns the entire array.

Besides the lack of control flow beyond goto LABEL, the straightforward algorithm in PIR looks like that in almost any high level language.

Creating a FizzBuzz Array PMC

My second approach was to subclass FixedStringArray and override some of its vtable methods to generate the right data instead of filling up an entire array all at once. This is similar to the tie mechanism in Perl 5, except that it’s pervasive throughout Parrot so that the implementation is much, much better.

The public function is keyed_array:

    77  .sub 'keyed_array'
    78      .param int count
    79  
    80      .local pmc results
    81      results = new [ 'FizzBuzz'; 'Array' ]
    82      results = count
    83  
    84      .return( results )
    85  .end

That’s it; like procedural it takes a single integer parameter, the number of elements requested.

Line 81 creates a new FizzBuzz;Array object. The strange bracket syntax names a class by Key. A Key is a hierarchical name, and it’s useful for nested namespaces.

Line 82 sets the number of elements in the array to count. Parrot turns the act of assigning an integer to a PMC to a method call on the PMC; the method is set_integer_native. This will be important in a moment.

It’s important to show the implementation of FizzBuzz;Array.

    87  .namespace [ 'FizzBuzz'; 'Array' ]
    88  
    89  .sub 'init' :init :load
    90      .local pmc fs_array
    91      .local pmc fb_array
    92  
    93      fs_array = getclass 'FixedStringArray'
    94      fb_array = subclass fs_array, [ 'FizzBuzz'; 'Array' ]
    95  
    96      addattribute fb_array, 'size'
    97  .end

Line 87 starts a new namespace for FizzBuzz;Array. Parrot methods must belong to a namespace with the same name as an object’s class.

Line 89 begins a compilation unit of initialization code. The presence of both :init and :load is a bit of a mish-mash. They execute at different times, depending on whether you invoke this code directly or load it from another piece of code invoked directly. Using both here hurts nothing; it supports both execution models I explained in my previous post.

The most important lines are 93 and 94. Line 93 fetches the Class PMC associated with the built-in FixedStringArray. Line 94 creates a new class named FizzBuzz;Array as a subclass of FixedStringArray. fb_array then contains the new Class PMC.

Finally, line 96 adds an attribute–an instance variable–to the class, named size. I’m sure you can imagine what that indicates.

    99  .sub '__init' :method
   100      .local pmc size
   101      size = new .Integer
   102  
   103      setattribute self, 'size', size
   104  .end

Parrot classes need initializers. Parrot will call the __init method when creating a new PMC. Lines 100 and 101 declare and create a new Integer PMC. Line 103 assigns that PMC to the size attribute.

Why is it an Integer? Attributes can only contain PMCs, not primitive types such as integers, floats, or strings.

Strictly speaking, I don’t need this initializer, but I find it’s helpful to create a PMC with all of the necessary attributes initialized correctly than to check for their existence whenever I want to access those attributes later.

   106  .sub 'set_integer_native' :vtable :method
   107      .param int size
   108  
   109      .local pmc size_pmc
   110      size_pmc = getattribute self, 'size'
   111      size_pmc = size
   112  .end

I mentioned earlier that assigning count to a FizzBuzz;Array ultimately called the set_integer_native vtable method. Because I decided not to create a full array with all of the elements precalculated, I needed to intercept this call and do something different.

The :vtable attribute tells Parrot that this subroutine should override the parent’s vtable method of the same name. The :method attribute tells Parrot that this subroutine expects the invocant in a special register according to Parrot’s calling conventions. It also makes that invocant available through the body of the subroutine in the variable named self.

All vtable methods are methods, so, yes, :vtable should imply :method. It currently doesn’t.

Lines 109 and 110 retrieve the size attribute PMC into a variable. Line 111 assigns the size passed into this subroutine to the PMC. Think of the PMC as a container for this integer value.

It might be tempting to try to assign a primitive integer to the attribute, but that won’t work. As I said before, attributes must be PMCs. You must box a primitive value in a PMC to assign to an attribute slot.

   114  .sub 'get_integer' :vtable :method
   115      .local pmc size_pmc
   116      size_pmc = getattribute self, 'size'
   117  
   118      .local int size
   119      size     = size_pmc
   120      .return( size )
   121  .end

Most set_* methods imply a corresponding get_* method. This get_integer vtable method looks at the size attribute and returns its value. Parrot will call this for code like:

    .local int num_elements
    num_elements = fb_array

That assumes that fb_array is a FizzBuzz;Array object, of course.

   123  .sub 'elements' :vtable :method
   124      .local pmc size_pmc
   125      size_pmc = getattribute self, 'size'
   126  
   127      .local int size
   128      size     = size_pmc
   129      .return( size )
   130  .end

Array-like PMCs have another vtable method called elements. There is currently no easy way in Parrot, to my knowledge, to use the same Parrot subroutine for two different vtable methods. There will be.

Finally, the real work comes from accessing an element of the array.

   132  .sub 'get_string_keyed' :vtable :method
   133      .param int key
   134      inc key

The vtable method called is either get_string_keyed or get_string_keyed_int, depending on some Parrot esoterica. It takes an integer as the key.

Because Parrot arrays start with an index of 0, line 134 increments the key to make the math easier.

   136      .local pmc size_pmc
   137      .local int size
   138      size_pmc = getattribute self, 'size'
   139      size     = size_pmc
   140  
   141      unless key > size goto calculate_string
   142      .return()

Fixed arrays have fixed sizes in Parrot. I decided to return nothing if someone tries to access an element beyond the appropriate size of a FizzBuzz;Array.

Another option is throwing an exception, but I wanted to keep the code simple for this example.

   144    calculate_string:
   145      .local string result
   146      result = ''
   147  
   148      .local int remainder
   149  
   150    check_fizz:
   151      remainder = key % 3
   152      if remainder goto check_buzz
   153      result .= 'Fizz'
   154  
   155    check_buzz:
   156      remainder = key % 5
   157      if remainder goto return_result
   158      result .= 'Buzz'
   159  
   160    return_result:
   161      .return( result )
   162  .end

The rest of the code looks very much like the procedural implementation, except that it doesn’t loop and push the results onto an array. This code calculates the result for the given index and returns it directly. It could be more complex by adding caching, but that would complicate the code and it’s unnecessary.

   164  .sub 'get_string_keyed_int' :vtable :method
   194  .end

Due to not being able to share implementation of vtable methods defined as PIR subroutines, get_string_keyed_int uses the worst possible method of code sharing–copy and paste–to handle the other way of accessing an array value by index. It works though.

Final Thoughts

There are plenty of other ways to solve this problem in Parrot. A fun one is to create separate iterators and access them all simultaneously. Perhaps I’ll do that–or perhaps you will. Despite being an assembly language for a virtual machine still under active development, PIR is reasonably straightforward and very powerful.