This is part 4 of Parrot Babysteps, my ongoing Parrot PIR tutorial.
We have learned a reasonable amount so far. We know how to write fairly trivial applications using Parrot Intermediate Representation. We could probably write a simple formula calculator that gets input from the user, ensures that the content is valid, and presents the results of applying user input to the formula.
It would be nice to write more ambitious programs, though. It would be painful - maybe even impossible - to create a modern program using only the tools and opcodes we have learned so far.
We can start examining PMCs by writing a version of our hypotenuse calculator from a few steps ago that has command line arguments.
Command Line Arguments
How do we tell Parrot that our program accepts command line arguments, though?
We need some way to show that our
:main sub is ready to take parameters.
Turns out that’s actually pretty easy.
.param directive is used at the start of a subroutine to indicate
that the subroutine will accept a parameter and place it in the named
# example-04-01.pir .sub 'main' :main .param pmc argv .end
argv is a ResizableStringArray:
an ordered collection of strings.
We can use the
elements opcode to find out how many arguments were passed to the file.
# example-04-02.pir .sub 'main' :main .param pmc argv .local int argument_count .local string description argument_count = elements argv description = "I was called with " $S0 = argument_count description .= $S0 description .= " arguments" say description .end
Try it out.
$ parrot example-04-02.pir hey there I was called with 3 arguments
Three? Let’s look at the arguments individually and see if we can figure this out.
shift opcode lets us pull the first item from an array. This shrinks
argv array by one as it shifts the rest of its contents over to fill
the empty space, but it’s not a concern for us right now.
# example-04-03.pir .sub 'main' :main .param pmc argv .local int argument_count .local string this_argument .local string description GET_ARG: argument_count = argv if argument_count <= 0 goto END this_argument = shift argv description = "This argument: " . this_argument say description goto GET_ARG END: .end
We can use this code to look at our program arguments one at a time.
bash-3.2$ parrot example-04-03.pir hey there This argument: example-04-03.pir This argument: hey This argument: there
Oh, right. The program name is the first argument. That is not unusual, especially in some lower level languages. I should have remembered.
Calculating a Hypotenuse
Let’s take what we’ve learned about handling the command line and apply it to our hypotenuse calculator.
# example-04-04.pir .sub 'main' :main .param pmc argv .local int argument_count .local string program_name .local num a .local num b .local num c .local num a_squared .local num b_squared .local num c_squared .local string error_message program_name = shift argv argument_count = elements argv if argument_count != 2 goto BAD_ARG_COUNT a = shift argv b = shift argv a_squared = a * a b_squared = b * b say a_squared say b_squared c_squared = a_squared + b_squared c = sqrt c_squared say c goto END BAD_ARG_COUNT: error_message = "Exactly two arguments required" say error_message goto END END: .end
First we shift the first item off of
argv because we know for sure that it’s
going to be the program name. Then we check to make sure that the user has
provided us with two arguments that we can use for
b. Not having two
arguments is an error, so the program branches to displaying an error and
quitting. When the argument count is right, the program shifts the
b then uses them to calculate and display the
Oh yeah - sooner or later you’re going to see this error message from Parrot:
error:imcc:syntax error, unexpected '=', expecting '(' ('=') in file 'example-04-04.pir' line 24
What did line 24 look like?
c_squared = a_squared + b_squared
It toook me a while to realize that I had never
.local num c_squared. Unfortunately, Parrot’s error messages aren’t
quite as descriptive as Perl’s. Perl has had a lot more time to figure out how
to gently explain a user’s error to him, though.
bash-3.2$ parrot example-04-04.pir 12 23 144 529 25.9422435421457 bash-3.2$
Parrot has many special opcodes for dealing with PMCs and soon we’ll be looking at more of them. I am especially interested in the opcodes that allow us to use arrays and other collection types.
Yes, this has been a very quick step. Maybe we didn’t learn a whole lot,
but there’s a little bit of new stuff in there. We
did learn how to add command line handling by using
.param to tell Parrot that
:main method accepts parameters. We learned that for
:main, the parameter
is a particular PMC - something called a ResizableStringArray. The
removes the first item in a ResizableStringArray and lets us use it in a variable.
We also saw that we can use the
members opcode to get the number of members in