Basic Syntax

PIR has a relatively simple syntax. Every line is a comment, a label, a statement, or a directive. Each statement or directive stands on its own line. There is no end-of-line symbol (such as a semicolon in C).


A comment begins with the # symbol, and continues until the end of the line. Comments can stand alone on a line or follow a statement or directive.

    # This is a regular comment. The PIR
    # interpreter ignores this.

PIR also treats inline documentation in Pod format as a comment. An equals sign as the first character of a line marks the start of a Pod block. A =cut marker signals the end of a Pod block.


  This is Pod documentation, and is treated like a
  comment. The PIR interpreter ignores this.



A label attaches a name to a line of code so other statements can refer to it. Labels can contain letters, numbers, and underscores. By convention, labels use all capital letters to stand out from the rest of the source code. It's fine to put a label on the same line as a statement or directive:

    GREET: say "'Allo, 'allo, 'allo."

Labels on separate lines improve readability, especially when outdented:

    say "'Allo, 'allo, 'allo."


A statement is either an opcode or syntactic sugar for one or more opcodes. An opcode is a native instruction for the virtual machine; it consists of the name of the instruction followed by zero or more arguments.

  say "Norwegian Blue"

PIR also provides higher-level constructs, including symbolic operators:

  $I1 = 2 + 5

These special statement forms are just syntactic sugar for regular opcodes. The + symbol corresponds to the add opcode, the - symbol to the sub opcode, and so on. The previous example is equivalent to:

  add $I1, 2, 5


Directives resemble opcodes, but they begin with a period (.). Some directives specify actions that occur at compile time. Other directives represent complex operations that require the generation of multiple instructions. The .local directive, for example, declares a named variable.

  .local string hello


Integers and floating point numbers are numeric literals. They can be positive or negative.

  $I0 = 42       # positive
  $I1 = -1       # negative

Integer literals can also be binary, octal, or hexadecimal:

  $I1 = 0b01010  # binary
  $I2 = 0o72     # octal
  $I3 = 0xA5     # hexadecimal

Floating point number literals have a decimal point and can use scientific notation:

  $N0 = 3.14
  $N2 = -1.2e+4

String literals are enclosed in single or double-quotes.See the section on Strings in Chapter 4 for an explanation of the differences between the quoting types.

  $S0 = "This is a valid literal string"
  $S1 = 'This is also a valid literal string'


PIR variables can store four different kinds of values—integers, numbers (floating point), strings, and objects. Parrot's objects are called PMCs, for "PolyMorphic Container".

The simplest kind of variable is a register variable. The name of a register variable always starts with a dollar sign ($), followed by a single character which specifies the type of the variable -- integer (I), number (N), string (S), or PMC (P) -- and ends with a unique number. You need not predeclare register variables:

  $S0 = "Who's a pretty boy, then?"
  say $S0

PIR also has named variables; the .local directive declares them. As with register variables, there are four valid types: int, num, string, and pmc. You must declare named variables; once declared, they behave exactly the same as register variables.

  .local string hello
  hello = "'Allo, 'allo, 'allo."
  say hello


The .const directive declares a named constant. Named constants are similar to named variables, but the values set in the declaration may never change. Like .local, .const takes a type and a name. It also requires a literal argument to set the value of the constant.

  .const int    frog = 4                       # integer
  .const string name = "Superintendent Parrot" # string
  .const num    pi   = 3.14159                 # floating point

You may use a named constant anywhere you may use a literal, but you must declare the named constant beforehand. This example declares a named string constant hello and prints the value:

  .const string hello = "Hello, Polly."
  say hello


A key is a special kind of constant used for accessing elements in complex variables (such as an array). A key is either an integer or a string; and it's always enclosed in square brackets ([ and ]). You do not have to declare literal keys. This code example stores the string "foo" in $P0 as element 5, and then retrieves it.

  $P0[5] = "foo"
  $S1    = $P0[5]

PIR supports multi-part keys. Use a semicolon to separate each part.

  $P0['my';'key'] = 472
  $I1             = $P0['my';'key']

Control Structures

Rather than providing a pre-packaged set of control structures like if and while, PIR gives you the building blocks to construct your own.PIR has many advanced features, but at heart it is an assembly language. The most basic of these building blocks is goto, which jumps to a named label.This is not your father's goto. It can only jump inside a subroutine, and only to a named label. In this code example, the say statement will run immediately after the goto statement:

    goto GREET
      # ... some skipped code ...
    say "'Allo, 'allo, 'allo."

Variations on the basic goto check whether a particular condition is true or false before jumping:

  if $I0 > 5 goto GREET

You can construct any traditional control structure from PIR's built-in control structures.


A PIR subroutine starts with the .sub directive and ends with the .end directive. Parameter declarations use the .param directive; they resemble named variable declarations. This example declares a subroutine named greeting, that takes a single string parameter named hello:

  .sub 'greeting'
      .param string hello
      say hello

That's All Folks

You now know everything you need to know about PIR. Everything else you read or learn about PIR will use one of these fundamental language structures. The rest is vocabulary.