Showing posts with label solution. Show all posts
Showing posts with label solution. Show all posts

Tuesday, April 8, 2008

Output of Episode 9's Game of Life

In Episode 9, the source code for John Conway's Game of Life was posted. If you don't feel like doing exercises or just want to see what it looks like without doing any trouble, here's what it looks like (this is life generation 9).
-----------------------------------------
-----------------------------------------
-----------------------------------------
-----O-----------------------------------
----OO-----------------------------------
--OO--O----------------------------------
-----OO----------------------------------
--OOOOO------------------OOO-------------
------------------------O---O------------
-----------------------O-----O-----------
----------------------O---O---O----------
----------------------O--O-O--O----------
----------------------O---O---O----------
-----------------------O-----O-----------
------------------------O---O------------
-------------------------OOO-------------
-----------------------------------------
-----------------------------------------
-----------------------------------------
-----------------------------------------
-----------------------------------------

Life - generation: 9
But really, it doesn't compare to seeing this program run on Parrot :-)

Update: The sources for Squaak have been added to the Parrot repository. Update your local copy today, run Configure, build Squaak, and run "../../parrot squaak.pbc examples/life.sq".

Solutions to the PCT Tutorial Exercises

Below you can find links to the solutions to the exercises of the PCT tutorial.
Episodes 1 and 2 didn't have any exercises.

  1. Episode 3
  2. Episode 4
  3. Episode 5
  4. Episode 6
  5. Episode 7
  6. Episode 8
  7. Episode 9

Solutions to the Exercises of Episode 8

1. We've shown how to implement keyed variable access for arrays, by implementing the action method for index. The same principle can be applied to keyed access for hashtables. Implement the action method for key.
method key($/) {
my $key := $( $<expression> );

make PAST::Var.new( $key, :scope('keyed'),
:vivibase('Hash'),
:viviself('Undef'),
:node($/) );
}
2. Implement the action methods for array_constructor and hash_constructor. Use a PAST::Op node and set the pasttype to 'call'. Use the "name" attribute to specify the names of the subs to be invoked (e.g., :name("!array") ). Note that all hash fields must be passed as named arguments. Check out PDD26 for doing this, and look for a "named " method.
method named_field($/) {
my $past := $( $<expression> );
my $name := $( $<string_constant> );
## the passed expression is in fact a named argument,
## use the named() accessor to set that name.
$past.named($name);
make $past;
}

method array_constructor($/) {
## use the parrot calling conventions to
## create an array,
## using the "anonymous" sub !array
## (which is not a valid Squaak name)
my $past := PAST::Op.new( :name('!array'),
:pasttype('call'),
:node($/) );
for $<expression> {
$past.push($($_));
}
make $past;
}

method hash_constructor($/) {
## use the parrot calling conventions to
## create a hash, using the "anonymous" sub
## !hash (which is not a valid Squaak name)
my $past := PAST::Op.new( :name('!hash'),
:pasttype('call'),
:node($/) );
for $<named_field> {
$past.push($($_));
}
make $past;
}
3. We'd like to add a little bit of syntactic sugar for accessing hashtable keys. Instead of writing foo{"key"}, I'd like to write foo.key. Of course, this only works for keys that do not contain spaces and such. Add the appropriate grammar rule (call it "member") that enables this syntax, and write the associated action method. Make sure this member name is converted to a string.
Hint: use a PAST::Val node for the string conversion.
rule postfix_expression {
| <key> {*} #= key
| <member> {*} #= member
| <index> {*} #= index
}

rule member {
'.' <identifier>
{*}
}

method member($/) {
my $member := $( $<identifier> );
## x.y is syntactic sugar for x{"y"},
## so stringify the identifier:
my $key := PAST::Val.new( :returns('String'),
:value($member.name()),
:node($/) );

## the rest of this method is the same
## as method key() above.
make PAST::Var.new( $key, :scope('keyed'),
:vivibase('Hash'),
:viviself('Undef'),
:node($/) );
}

Solutions to the Exercises of Episode 7

1. Currently, Squaak only has grammar rules for integer and string constants, not floating point constants. Implement this grammar rule. A floating-point number consists of zero or more digits, followed by a dot and at least one digit, or, at least one digit followed by a dot and any number of digits. Examples are: 42.0, 1., .0001. There may be no whitespace between the individual digits and the dot. Make sure you understand the difference between a "rule" and a "token".
token float_constant {
[
| \d+ '.' \d*
| \d* '.' \d+
]
{*}
}
2. Implement the missing operators: (binary) "-", "<=", ">=", "==", "!=", "/", "%", "or"

For sake of completeness (and easy copy-paste for you), here's the list of operator declarations as I wrote them for Squaak:
rule expression is optable { ... }

proto 'infix:or' is precedence('1')
is pasttype('unless') { ... }
proto 'infix:and' is tighter('infix:or')
is pasttype('if') { ... }

proto 'infix:<' is tighter('infix:and') { ... }
proto 'infix:<=' is equiv('infix:<') { ... }
proto 'infix:>' is equiv('infix:<') { ... }
proto 'infix:>=' is equiv('infix:<') { ... }
proto 'infix:==' is equiv('infix:<') { ... }
proto 'infix:!=' is equiv('infix:<') { ... }

proto 'infix:+' is tighter('infix:<')
is pirop('n_add') { ... }
proto 'infix:-' is equiv('infix:+')
is pirop('n_sub') { ... }

proto 'infix:..' is equiv('infix:+')
is pirop('n_concat') { ... }

proto 'infix:*' is tighter('infix:+')
is pirop('n_mul') { ... }
proto 'infix:%' is equiv('infix:*')
is pirop('n_mod') { ... }
proto 'infix:/' is equiv('infix:*')
is pirop('n_div') { ... }

proto 'prefix:not' is tighter('infix:*')
is pirop('n_not') { ... }
proto 'prefix:-' is tighter('prefix:not')
is pirop('n_neg') { ... }

proto 'term:' is tighter('prefix:-')
is parsed(&term) { ... }

Thursday, April 3, 2008

Solutions to the Exercises in Episode 6

Without further ado, the solution to the exercise in Episode 6:

1. By now you should have a good idea on the implementation of scope in Squaak. We haven't implemented the for-statement yet, as it needs proper scope handling to implement. Implement this. Check out Episode 3 for the BNF rules that define the syntax of the for-statement. When implementing it, you will run into the same issue as we did when implementing subroutines and parameters. Use the same trick for the implementation of the for-statement.

First, let us look at the BNF of the for-statement:
for-statement ::= 'for' for-init ',' expression [step]
'do'
block
'end'

step ::= ',' expression

for-init ::= 'var' identifier '=' expression
It's pretty easy to convert this to Perl 6 rules:
rule for_statement {
'for' <for_init> ',' <expression> <step>?
'do' <statement>* 'end'
{*}
}

rule step {
',' <expression>
{*}
}

rule for_init {
'var' <identifier> '=' <expression>
{*}
}
Pretty easy huh? Let's take a look at the semantics. A for-loop is just another way to write a while loop, but much easier in certain cases. This:
for var <ident> = <expr1>, <expr2>, <expr3> do
<statement>*
end
corresponds to:
do
var <ident> = <expr1>
while <ident> <= <expr2> do
<statement>*
<ident> = <ident> + <expr3>
end
end
If <expr3> is absent, it defaults to the value "1". Note that the step expression (expr3) should be positive; the loop condition contains a "<=" operator. When you specify a negative step expression, the loop variable will only decrease in value, which will never make the loop condition false (unless it overflows, but that's a different issue; this might even raise an exception in Parrot; this I do not know). Allowing negative step expressions introduces more complexity, which I felt was not worth the trouble for this tutorial language.

Note that the loop variable <ident> is local to the for loop; this is expressed in the equivalent while loop by the surrounding do/end pair: a new do/end pair defines a new (nested) scope; after the "end" keyword, the loop variable is no longer visible.

Let's implement the action method for the for-statement. As was mentioned in the exercise description, we're dealing with the same situation as with subroutine parameters. In this case, we're dealing with the loop variable, which is local to the for-statement. Let's check out the rule for for_init:
method for_init($/) {
our $?BLOCK;
our @?BLOCK;

## create a new scope here, so that we can
## add the loop variable
## to this block here, which is convenient.
$?BLOCK := PAST::Block.new( :blocktype('immediate'),
:node($/) );
@?BLOCK.unshift($?BLOCK);

my $iter := $( $<identifier> );
## set a flag that this identifier is being declared
$iter.isdecl(1);
$iter.scope('lexical');
## the identifier is initialized with this expression
$iter.viviself( $( $<expression> ) );

## enter the loop variable into the symbol table.
$?BLOCK.symbol($iter.name(), :scope('lexical'));

make $iter;
}

So, just as we created a new PAST::Block for the subroutine in the action method for parameters, we create a new PAST::Block for the for-statement in the action method that defines the loop variable. (Guess why we made for-init a subrule, and didn't put in "var <ident&gt = <expression>" in the rule of for-statement). This block is the place to live for the loop variable. The loop variable is declared, initialized using the viviself attribute, and entered into the new block's symbol table. Note that after creating the new PAST::Block object, we put it onto the stack scope.

Now, the action method for the for statement is quite long, so I'll just embed my comments, which makes reading it easier.
method for_statement($/) {
our $?BLOCK;
our @?BLOCK;
First, get the result object of the for statement initialization rule; this is the PAST::Var object, representing the declaration and initialization of the loop variable.
    my $init := $( $<for_init> );
Then, create a new node for the loop variable. Yes, another one (besides the one that is currently contained in the PAST::Block). This one is used when the loop variable is updated at the end of the code block (each iteration). The difference with the other one, is that it doesn't have the isdecl flag, and it doesn't have a viviself clause, which would result in extra instructions checking whether the variable is null (and we know it's not, because we initialize the loop variable).
    ## cache the name of the loop variable
my $itername := $init.name();
my $iter := PAST::Var.new( :name($itername),
:scope('lexical'),
:node($/) );
Now, retrieve the PAST::Block node from the scope stack, and push all statement PAST nodes onto it.
    ## the body of the loop consists of the statements written by the user and
## the increment instruction of the loop iterator.

my $body := @?BLOCK.shift();
$?BLOCK := @?BLOCK[0];
for $<statement> {
$body.push($($_));
}
If there was a step, we use that value; otherwise, we use assume a default step size of "1".
Negative step sizes won't work, but if you Feel Lucky, you could go ahead and try. It's not that hard, it's just a lot of work, and I'm too lazy for that now.... ehm, I mean, I leave it as the proverbial exercise to the reader.
    my $step;
if $<step> {
my $stepsize := $( $<step>[0] );
$step := PAST::Op.new( $iter, $stepsize, :pirop('add'), :node($/) );
}
else { ## default is increment by 1
$step := PAST::Op.new( $iter,
:pirop('inc'),
:node($/) );
}

The incrementing of the loop variable is part of the loop body, so add the incrementing statement to $body.
    $body.push($step);
The loop condition uses the "<=" operator, and compares the loop variable with the maximum value that was specified.
    ## while loop iterator <= end-expression
my $cond := PAST::Op.new( $iter, $( $<expression> ),
:name('infix:<=') );

Now we have the PAST for the loop condition and the loop body, so now create a PAST to represent the (while) loop.
    my $loop := PAST::Op.new( $cond, $body,
:pasttype('while'),
:node($/) );

Finally, the initialization of the loop variable should go before the loop itself, so create a PAST::Stmts node to do this:
    make PAST::Stmts.new( $init, $loop,
:node($/) );
}

Wow, we've done it! This was a good example of how to implement a non-trivial statement type using PAST.

Solutions to the Exercises in Episode 5

1. In this episode, we changed the action method for the TOP rule; it is now invoked twice, once at the beginning of the parse, once at the end of the parse. The block rule, which defines a block to be a series of statements, represents a new scope. This rule is used in for instance if-statement (the then-part and else-part), while-statement (the loop body) and others. Update the parse action for block so it is invoked twice; once before parsing the statements, during which a new PAST::Block is created and stored onto the scope stack, and once after parsing the statements, during which this PAST node is set as the result object. Make sure $?BLOCK is always pointing to the current block. In order to do this exercise correctly, you should understand well what the shift and unshift methods do, and why we didn't implement methods to push and pop, which are more appropriate words in the context of a (scope) stack.

Keeping the Current block up to date
Sometimes we need to access the current block's symbol table. In order to be able to do so, we need a reference to the "current block". We do this by declaring a package variable called "$?BLOCK", declared with "our" (as opposed with "my"). This variable will always point to the "current" block. As blocks can nest, we use a "stack", on which newly created blocks are stored.
Whenever a new block is created, we assign this to $?BLOCK, and store it onto the stack, so that the next time a new block is created, the "old" current block isn't lost. Whenever a scope is closed, we pop off the current block from the stack, and restore the previous "current" block.

Why unshift/shift and not push/pop?
When we're talking about stacks, it would seem logical to talk about stack operations such as "push" and "pop". Instead, we use the operations "unshift" and "shift". If you're not a Perl programmer (such as myself), these names might not make sense. However, it's pretty easy. Instead of pushing a new object onto the "top" of the stack, you unshift objects onto this stack. Just see it as an old school bus, with only one entrance (at the front of the bus). Pushing a new person means taking the first free seat when entering, while unshifting a new person means everybody moves (shifts) one place to the back, so the new person can sit in the front seat. You might think this is not as efficient (more stuff is moved around), but that's not really true (actually: I guess (and certainly hope) the shift and unshift operations are implemented more effectively than the bus metaphor; I don't know how it is implemented).

So why unshift/shift, and not push/pop? When restoring the previous "current block", we need to know exactly where it is (what position). It would be nice to be able to always refer to the "first passenger on the bus", instead of the last person. We know how to reference the first passenger (it's on seat no. 0 (it was designed by an IT guy)); we don't really know what is the seat no. of the last person: s/he might sit in the middle, or at the back.

I hope it's clear what I mean here... otherwise, have a look at the code, and try to figure out what's happening:
method block($/, $key) {  
our $?BLOCK;
our @?BLOCK;
if $key eq 'open' {

$?BLOCK := PAST::Block.new(
:blocktype('immediate'),

:node($/) );
@?BLOCK.unshift($?BLOCK);
}
else {
my $past := @?BLOCK.shift();
$?BLOCK := @?BLOCK[0];

for $<statement> {
$past.push( $( $_ ) );
}
make $past;
}
}

Monday, March 31, 2008

Solutions to the Exercises in Episode 3

By now, you may have finished the PCT tutorial. If you felt too lazy to do the exercises or if you want to see what solution I had in mind, here are the solutions to the exercises in Episode 3 (Episode 1's exercise was discussed at the end of Episode 2, and the latter didn't have any coding assignments).

1. Rename the names of the action methods according to the name changes we made on the grammar rules. So, "integer" becomes "integer_constant", "value" becomes "expression", and so on.
I assume you don't need any help with this.
2. Look at the grammar rule for statement. A statement currently consists of an assignment. Implement the action method "statement" to retrieve the result object of this assignment and set it as statement's result object using the special make function. Do the same for rule primary.
method statement($/) {
make $( $<assignment> );
}
Note that at this point, the rule statement doesn't define different #= keys for each type of statement, so we don't declare a parameter $key. This will be changed later.
method primary($/) {
make $( $<identifier> );
}
3. Write the action method for the rule identifier. As a result object of this "match", a new PAST::Var node should be set, taking as name a string representation of the match object ($/). For now, you can set the scope to 'package'. See "pdd26: ast" for details on PAST::Var nodes.
method identifier($/) {
make PAST::Var.new( :name(~$/),
:scope('package'),
:node($/) );
}
4. Write the action method for assignment. Retrieve the result objects for "primary" and for "expression", and create a PAST::Op node that binds the expression to the primary. (Check out pdd26 for PAST::Op node types, and find out how you do such a binding).
method assignment($/) {
my $lhs := $( $<primary> );
my $rhs := $( $<expression> );
$lhs.lvalue(1);
make PAST::Op.new( $lhs, $rhs,
:pasttype('bind'),
:node($/) );
}
Note that we set the lvalue flag on $lhs. See PDD26 for details on this flag.

5. Run your compiler on a script or in interactive mode. Use the target option to see what PIR is being generated on the input "x = 42".
.namespace
.sub "_block10"
new $P11, "Integer"
assign $P11, 42
set_global "x", $P11
.return ($P11)
.end
The first two lines of code in the sub create an object to store the number 42, the third line stores this number as "x". The PAST compiler will always generate an instruction to return the result of the last statement, in this case $P11.