The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

The Parrot intermediate representation (PIR) is the primary way to program Parrot directly. It used to be an overlay on top of the far more primitive Parrot assembly language (PASM). However, PIR and PASM have since diverged semantically in a number of places and no longer are directly related to one another. PIR has many high-level features that will be familiar to programmers, such as basic operator syntax. However, it's still very low-level, and is closely tied to the underlying virtual machine. In fact, the Parrot developers specifically want to keep in that way for a number of reasons. PASM, the Parrot assembly language, is discussed in more detail in CHP-5Chapter 5.

As a convention, files containing pure PIR code generally have a .pir extension. PASM files typically end with .pasm. Compiled Parrot Bytecode (PBC) files have a .pbc extension. We'll talk more about PBC and PASM in later chapters.

PIR is well documented, both in traditional documentation and in instructional code examples. The project documentation in docs/ are good sources for information about the current syntax, semantics, and implementation. The test suite in t/compilers/imcc/ shows examples of proper working code. In fact, the test suite is the definitive PIR resource, because it shows how PIR actually works, even when the documentation may be out of date.

Statements

The syntax of statements in PIR is much more flexible then is commonly found in assembly languages, but is more rigid and "close to the machine" then some higher-level languages like C are. PIR has a very close relationship with the Parrot assembly language, PASM. PASM instructions, with some small changes and caveats, are valid PIR instructions. PIR does add some extra syntactic options to help improve readability and programmability, however. The statement delimiter for both PIR and PASM is a newline \n. Each statement has to be on its own line This isn't entirely true when you consider things like macros and heredocs, but we'll tackle those issues when we come to them., but empty whitespace lines between statements are okay. Statements may also start with a label, for use with jumps and branches. Comments are marked by a hash sign (#), and continue until the end of the line. POD blocks may be used for multi-line documentation. We'll talk about all these issues in more detail as we go.

To help with readability, PIR has some high-level constructs, including symbol operators:

  $I1 = 5                       # set $I1, 5

named variables:

  count = 5

and complex statements built from multiple keywords and symbol operators:

  if $I1 <= 5 goto LABEL        # le $I1, 5, LABEL

We will get into all of these in more detail as we go. Notice that PIR does not, and will not, have high-level looping structures like while or for loops and if/then/else branch structures. Because of these omissions PIR can become a little bit messy and unweildy for large programs. Luckily, there are a large group of high-level languages (HLL) that can be used to program Parrot instead. PIR is used primarily to write the compilers and libraries for these languages.

Variables and Constants

Parrot Registers

PIR code has a variety of ways to store values while you work with them. Actually, the only place to store values is in a Parrot register, but there are multiple ways to work with these registers. Parrot's register names always start with a dollar sign, followed by a single character that shows whether it is an integer (I), numeric (N), string (S), or PMC (P) register, and then the number of the register:

  $S0 = "Hello, Polly.\n"
  print $S0

You can have as many registers of each type as you need, Parrot will automatically allocate new ones for you. The process is transparent, and programmers should never have to worry about it.

Parrot registers are allocated in a linear array, and register numbers are indices into this array. Having more registers means Parrot must allocate more storage space for them, which can decrease memory efficiency and register allocation/fetch performance. In general, it's better to keep the number of registers small. However, the number of the register does not necessarily correspond to the actual storage location where the register data is held. A memory allocator unit translates register names in the form "$S0" into an actual fixed memory location. This allocator can also help to optimize register usage so that existing registers are reused instead of allocating new ones in memory. The short version is that the programmer should never have to worry about register allocation, and should feel free to use as many as she wants. As with any system, it's a good idea to be mindful of the things that might impact performance anyway.

Constants

Parrot has four primary data types: integers, floating-point numbers, strings, and PMCs. Integers and floating-point numbers can be specified in the code with numeric constants.

  $I0 = 42       # Integers are regular numeric constants
  $I1 = -1       # They can be negative or positive
  $I2 = 0xA5     # They can also be hexadecimal
  $I4 = 0b01010  # ...or binary

  $N0 = 3.14     # Numbers can have a decimal point
  $N1 = 4        # ...or they don't
  $N2 = -1.2e+4  # Numbers can also use scientific notation.

String literals are enclosed in single or double-quotes:

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

Strings in double-quotes accept all sorts of escape sequences using backslashes. Strings in single-quotes only allow escapes for nested quotes:

  $S0 = "This string is \n on two lines"
  $S0 = 'This is a \n one-line string with a slash in it'
  

Here's a quick listing of the escape sequences supported by double-quoted strings:

  \xhh        1..2 hex digits
  \ooo        1..3 oct digits
  \cX         control char X
  \x{h..h}    1..8 hex digits
  \uhhhh      4 hex digits
  \Uhhhhhhhh  8 hex digits
  \a, \b, \t, \n, \v, \f, \r, \e, \\, \"

Or, if you need more flexibility, you can use a heredoc:

  $S2 = << "End_Token"

  This is a multi-line string literal. Notice that
  it doesn't use quotation marks. The string continues
  until the ending token (the thing in quotes next to
  the << above) is found. The terminator must appear on
  it's own line, must appear at the beginning of the
  line, and may not have any trailing whitespace.

  End_Token

Strings: Encodings and Charsets

Strings are complicated. It used to be that all that was needed was to support the ASCII charset, which only contained a handful of common symbols and English characters. Now we need to worry about several character encodings and charsets in order to make sense out of all the string data in the world.

Parrot has a very flexible system for handling and manipulating strings. Every string is associated with an encoding and a character set (charset). The default for Parrot is 8-bit ASCII, which is simple to use and is almost universally supported. However, support is built in to have other formats as well.

Double-quoted string constants, like the ones we've seen above, can have an optional prefix specifying the charset or both the encoding and charset of the string. Parrot will maintain these values internally, and will automatically convert strings when necessary to preserve the information. String prefixes are specified as encoding:charset: at the front of the string. Here are some examples:

  $S0 = utf8:unicode:"Hello UTF8 Unicode World!"
  $S1 = utf16:unicode:"Hello UTF16 Unicode World!"
  $S2 = ascii:"This is 8-bit ASCII"
  $S3 = binary:"This is treated as raw unformatted binary"

The binary: charset treats the string as a buffer of raw unformatted binary data. It isn't really a "string" per se because binary data isn't treated as if it contains any readable characters. These kinds of strings are useful for library routines that return large amounts of binary data that doesn't easily fit into any other primitive data type.

Notice that only double-quoted strings can have encoding and charset prefixes like this. Single-quoted strings do not support them.

When two types of strings are combined together in some way, such as through concatenation, they must both use the same character set an encoding. Parrot will automatically upgrade one or both of the strings to use the next highest compatible format, if they aren't equal. ASCII strings will automatically upgrade to UTF-8 strings if needed, and UTF-8 will upgrade to UTF-16. Handling and maintaining these data and conversions all happens automatically inside Parrot, and you the programmer don't need to worry about the details.

Named Variables

Calling a value "$S0" isn't very descriptive, and usually it's a lot nicer to be able to refer to values using a helpful name. For this reason Parrot allows registers to be given temporary variable names to use instead. These named variables can be used anywhere a register would be used normally ...because they actually are registers, but with fancier names. They're declared with the .local statement which requires a variable type and a name:

  .local string hello
  set hello, "Hello, Polly.\n"
  print hello

This snippet defines a string variable named hello, assigns it the value "Hello, Polly.\n", and then prints the value.

The valid types are int, num, string, and pmc or any Parrot class name (like PerlInt or PerlString). It should come as no surprise that these are the same divisions as Parrot's four register types. Named variables are valid from the point of their definition to the end of the current function.

The name of a variable must be a valid PIR identifier. It can contain letters, digits, and underscores, but the first character has to be a letter or underscore. There is no limit to the length of an identifier, especially since the automatic code generators in use with the various high-level languages on parrot tend to generate very long identifier names in some situations. Of course, making huge identifier names could cause all sorts of memory allocation problems or inefficiencies in parsing. Push the limits at your own risk.

Register Allocator

Now's a decent time to talk about Parrot's register allocator. When you use a register like $P5, you aren't necessarily talking about the fifth register in memory. This is important since you can use a $P10000000 without forcing Parrot to allocate an array of ten million registers. Instead Parrot's compiler front-end uses an allocation algorithm which turns each register in the PIR source code into a reference to an actual memory storage location.

The allocator is a type of optimization. It performs a lifetime analysis on the registers to determine when they are being used and when they are not. When a register stops being used for one thing, it can be reused later for a different purpose. Register reuse helps to keep Parrot's memory requirements lower, because fewer unique registers need to be allocated. However, the downside of the register allocator is that it takes more time to execute during the compilation phase.

In some situations it can be helpful to turn the allocator off and avoid expensive optimizations. Such situations are subroutines where there are a small fixed number of registers used, when variables are used throughout the subroutine and should never be reused, or when some kind of pointer reference needs to be made to the register this happens in some NCI calls that take pointers and return values. To turn off the register allocator for certain variables, you can use the :unique_reg modifier:

  .local pmc MyUniquePMC :unique_reg

Notice that :unique_reg shouldn't affect the behavior of Parrot, but instead only changes the way registers are allocated. It's a trade off between using more memory in exchange for less time spent optimizing the subroutine.

PMC variables

PMC registers and variables act much like any integer, floating-point number, or string register or variable, but you have to instantiate a new PMC object before you use it. The new instruction creates a new PMC of a specified type:

  $P0 = new 'PerlString'     # This is how the Perl people do it
  $P0 = "Hello, Polly.\n"
  print $P0

This example creates a PerlString object, stores it in the PMC register $P0, assigns the value "Hello, Polly.\n" to it, and prints it. With named variables the type passed to the .local directive is either the generic pmc or a type compatible with the type passed to new:

  .local PerlString hello    # or .local pmc hello
  hello = new PerlString
  hello = "Hello, Polly.\n"
  print hello

PIR is a dynamic language, and that dynamicism is readily displayed in the way PMC values are handled. Primitive registers like strings, numbers, and integers perform a special action called autoboxing when they are assigned to a PMC. Autoboxing is when a primative scalar type is automatically converted to a PMC object type. There are PMC classes for String, Number, and Integer which can be quickly converted to and from primitive int, number, and string types. Notice that the primative types are in lower-case, while the PMC classes are capitalized. We will discuss PMCs and all the details of their interactions in CHP-11 Chapter 11.

Named Constants

The .const directive declares a named constant. It's very similar to .local, and requires a type and a name. The value of a constant must be assigned in the declaration statement. As with named variables, named constants are visible only within the compilation unit where they're declared. This example declares a named string constant hello and prints the value:

  .const string hello = "Hello, Polly.\n"
  print hello

Named constants function in all the same places as literal constants, but have to be declared beforehand:

  .const int the_answer = 42        # integer constant
  .const string mouse = "Mouse"     # string constant
  .const num pi = 3.14159           # floating point constant

In addition to normal local constants, you can also specify a global constant which is accessible from everywhere in the current code file:

  .globalconst int days = 365

Symbol Operators

PIR has many other symbol operators: arithmetic, concatenation, comparison, bitwise, and logical. All PIR operators are translated into one or more PASM opcodes internally, but the details of this translation stay safely hidden from the programmer. Consider this example snippet:

  .local int sum
  sum = $I42 + 5
  print sum
  print "\n"

The statement sum = $I42 + 5 translates to something like add I16, I17, 5 in PASM. The exact translation isn't too important Unless you're hacking on IMCC or PIRC!, so we don't have to worry about it for now. We will talk more about PASM and its instruction set in Chapter 5.

PIR also provides automatic assignment operators such as +=, -=, and >>=. These operators help programmers to perform common manipulations on a data value in place, and save a few keystrokes while doing them.

A complete list of PIR operators is available in CHP-13 Chapter 13.

Labels

Any line in PIR can start with a label definition like LABEL:, but label definitions can also stand on their own line. Labels are like flags or markers that the program can jump to or return to at different times. Labels and jump operations (which we will discuss a little bit later) are one of the primary methods to change control flow in PIR, so it is well worth understanding.

PIR code can contain both local and global labels. Global labels start with an underscore. The name of a global label has to be unique since it can be called at any point in the program. Local labels start with a letter. A local label is accessible only in the function where it is defined. The name has to be unique within that function, but the same name can be reused in other functions without causing a collision.

  branch L1   # local label
  bsr    _L2  # global label

Labels are most often used in branching instructions, which are used to implement high level control structures by our high-level language compilers.

Compilation Units

Compilation units in PIR are roughly equivalent to the subroutines or methods of a high-level language. Though they will be explained in more detail later, we introduce them here because all code in a PIR source file must be defined in a compilation unit. The simplest syntax for a PIR compilation unit starts with the .sub directive and ends with the .end directive:

  .sub main
      print "Hello, Polly.\n"
  .end

This example defines a compilation unit named main that prints a string.The first compilation unit in a file is normally executed first but you can flag any compilation unit as the first one to execute with the :main marker. The convention is to name the first compilation unit main, but the name isn't critical.

  .sub first
      print "Polly want a cracker?\n"
      end
  .end

  .sub main :main
      print "Hello, Polly.\n"
  .end

This code prints out "Hello, Polly." but not "Polly want a cracker?". This is because the function main has the :main flag, so it is executed first. The function first, which doesn't have this flag is never executed. However, if we change around this example a little:

  .sub first :main
      print "Polly want a cracker?\n"
      end
  .end

  .sub main
      print "Hello, Polly.\n"
  .end

The output now is "Polly want a cracker?". Execution in PIR starts at the :main function and continues until the end of that function only. If you want to do more stuff if your program, you will need to call other functions explicitly.

CHP-04Chapter 4 goes into much more detail about compilation units and their uses.

Flow Control

Flow control in PIR is done entirely with conditional and unconditional branches. This may seem simplistic and primitive, but remember that PIR is a thin overlay on the assembly language of a virtual processor. High level control structures are invariably linked to the language in which they are used, so any attempt by Parrot to provide these structures would work well for some languages but would require all sorts of messy translation in others. The only way to make sure all languages and their control structures can be equally accommodated is to simply give them the most simple and fundamental building blocks to work with. Language agnosticism is an important design goal in Parrot, and creates a very flexible and powerful development environment for our language developers.

The most basic branching instruction is the unconditional branch: goto.

  .sub _main
      goto L1
      print "never printed"
  L1:
      print "after branch\n"
      end
  .end

The first print statement never runs because the goto always skips over it to the label L1.

The conditional branches combine if or unless with goto.

  .sub _main
      $I0 = 42
      if $I0 goto L1
      print "never printed"
  L1: print "after branch\n"
      end
  .end

In this example, the goto branches to the label L1 only if the value stored in $I0 is true. The unless statement is quite similar, but branches when the tested value is false. An undefined value, 0, or an empty string are all false values. Any other values are considered to be true values.

The comparison operators (<, <=, ==, !=, >, >=) can combine with if ... goto. These branch when the comparison is true:

  .sub _main
      $I0 = 42
      $I1 = 43
      if $I0 < $I1 goto L1
      print "never printed"
  L1:
      print "after branch\n"
      end
  .end

This example compares $I0 to $I1 and branches to the label L1 if $I0 is less than $I1. The if $I0 < $I1 goto L1 statement translates directly to the PASM lt branch operation.

The rest of the comparison operators are summarized in CHP-13-SECT-3"PIR Instructions" in Chapter 11.

PIR has no special loop constructs. A combination of conditional and unconditional branches handle iteration:

  .sub _main
      $I0 = 1               # product
      $I1 = 5               # counter

  REDO:                     # start of loop
      $I0 = $I0 * $I1
      dec $I1
      if $I1 > 0 goto REDO  # end of loop

      print $I0
      print "\n"
      end
  .end

This example calculates the factorial 5!. Each time through the loop it multiplies $I0 by the current value of the counter $I1, decrements the counter, and then branches to the start of the loop. The loop ends when $I1 counts down to 0 so that the if doesn't branch to REDO. This is a do while-style loop with the condition test at the end, so the code always runs the first time through.

For a while-style loop with the condition test at the start, use a conditional branch together with an unconditional branch:

  .sub _main
      $I0 = 1               # product
      $I1 = 5               # counter

  REDO:                     # start of loop
      if $I1 <= 0 goto LAST
      $I0 = $I0 * $I1
      dec $I1
      goto REDO
  LAST:                     # end of loop

      print $I0
      print "\n"
      end
  .end

This example tests the counter $I1 at the start of the loop. At the end of the loop, it unconditionally branches back to the start of the loop and tests the condition again. The loop ends when the counter $I1 reaches 0 and the if branches to the LAST label. If the counter isn't a positive number before the loop, the loop never executes.

Any high-level flow control construct can be built from conditional and unconditional branches, because this is the way almost all computer hardware operates at the lowest-level, so all modern programming languages use branching constructs to implement their most complex flow control devices.

Fortunately, libraries of macros have been developed that can implement more familiar syntax for many of these control structures. We will discuss these libraries in more detail in CHP-6 "PIR Standard Library".

Classes and Objects

It may seem more appropriate for a discussion of PIR's support for classes and objects to reside in it's own chapter, instead of appearing in a generic chapter about PIR programming "basics". However, part of PIR's core functionality is it's support for object-oriented programming. PIR doesn't use all the fancy syntax as other OO languages, and it doesn't even support all the features that most modern OO languages have. What PIR does have is support for some of the basic structures and abilities, the necessary subset to construct richer and higher-level object systems.

PMCs as Classes

PMCs aren't exactly "classes" in the way that this term is normally used. They are polymorphic data items that can be one of a large variety of predefined types. As we have seen briefly, and as we will see in more depth later, PMCs have a standard interface called the VTABLE interface. VTABLEs are a standard list of functions that all PMCs implement or, PMCs can choose not to implement each interface explicitly and instead let Parrot call the default implementations.

VTABLES are very strict: There are a fixed number with fixed names and fixed argument lists. You can't just create any random VTABLE interface that you want to create, you can only make use of the ones that Parrot supplies and expects. To circumvent this limitation, PMCs may have METHODS in addition to VTABLES. METHODS are arbitrary code functions that can be written in C, may have any name, and may implement any behavior.

Subclassing PMCs

Existing built-in PMC types can be subclassed to associate additional data and methods with that PMC type. Subclassed PMC types act like their PMC base types, by sharing the same VTABLE methods and underlying data types. However, the subclass can define additional methods and attribute data storage. If necessary new VTABLE interfaces can be defined in PIR and old VTABLE methods can be overriden using PIR. We'll talk about defining methods and VTABLE interface overrides in the next chapter.

Creating a new subclass of an existing PMC class is done using the subclass keyword:

  # create an anonymous subclass
  $P0 = subclass 'ResizablePMCArray'

  # create a subclass named "MyArray"
  $P0 = subclass 'ResizablePMCArray', 'MyArray'

This returns a Class PMC which can be used to create and modify the class by adding attributes or creating objects of that class. You can also use the new class PMC to create additional subclasses:

  $P0 = subclass 'ResizablePMCArray', 'MyArray'
  $P1 = subclass $P0, 'MyOtherArray'

Once you have created these classes, you can create them like normal with the new keyword:

  $P0 = new 'MyArray'
  $P1 = new 'MyOtherArray'

Creating Classes

In addition to subclassing existing PMC types, new classes can be created in PIR from scratch. This is done with the newclass opcode:

  $P0 = newclass 'MyClass'

Attributes

Classes and subclasses can be given attributes in addition to methods, which we will talk about in the next chapter which are named data fields. Attributes are created with the addattribute opcode, and can be set and retrieved with the setattribute and getattribute opcodes respectively:

  $P0 = newclass 'MyClass'
  addattribute $P0, 'First'
  addattribute $P0, 'Second'

  $P1 = new 'MyClass'
  setattribute $P1, 'First', 'First Value'
  setattribute $P1, 'Second', 'Second Value'
  $S0 = getattribute $P1, 'First'
  $S1 = getattribute $P1, 'Second'

Those values added as attributes don't need to be strings, even though both of the ones in the example are. They can be integers, numbers or PMCs too.

Exceptions

Parrot includes a robust exception mechanism that is not only used internally to implement a variety of control flow constructs, but is also available for use directly from PIR code. Exceptions, in as few words as possible, are error conditions in the program. Exceptions are thrown when an error occurs, and they can be caught by special routines called handlers. This enables Parrot to recover from errors in a controlled way, instead of crashing and terminating the process entirely.

Exceptions, like most other data objects in Parrot, are PMCs. They contain and provide access to a number of different bits of data about the error, such as the location where the error was thrown (including complete backtraces), any annotation information from the file, and other data.

Throwing Exceptions

Many exceptions are used internally in Parrot to indicate error conditions. Opcodes such as die and warn throw exceptions internally to do what they are supposed to do. Other opcodes such as div throw exceptions only when an error occurs, such as an attempted division by zero.

Exceptions can also be thrown manually using the throw opcode. Here's an example:

  $P0 = new 'Exception'
  throw $P0

This throws the exception object as an error. If there are any available handlers in scope, the interpreter will pass the exception object to the handler and continue execution there. If there are no handlers available, Parrot will exit.

Exception Attributes

Since Exceptions are PMC objects, they can contain a number of useful data items. One such data item is the message:

  $P0 = new 'Exception'
  $P1 = new 'String'
  $P1 = "this is an error message for the exception"
  $P0["message"] = $P1

Another is the severity and the type:

  $P0["severity"] = 1   # An integer value
  $P0["type"] = 2       # Also an Integer
  

Finally, there is a spot for additional data to be included:

  $P0["payload"] = $P2  # Any arbitrary PMC

Exception Handlers

Exception handlers are labels in PIR code that can be jumped to when an exception is thrown. To list a label as an exception handler, the push_eh opcode is used. All handlers exist on a stack. Pushing a new handler adds it to the top of the stack, and using the pop_eh opcode pops the handler off the top of the stack.

  push_eh my_handler
    # something that might cause an error

  my_handler:
    # handle the error here

Catching Exceptions

The exception PMC that was thrown can be caught using the .get_results() directive. This returns the Exception PMC object that was thrown from inside the handler:

  my_handler:
    .local pmc err
    .get_results(err)

With the exception PMC available, the various attributes of that PMC can be accessed and analyzed for additional information about the error.

Exception Handler PMCs

Like all other interesting data types in Parrot, exception handlers are a PMC type. When using the syntax above with push_eh LABEL, the handler PMC is created internally by Parrot. However, you can create it explicitly too if you want:

  $P0 = new 'ExceptionHandler'
  set_addr $P0, my_handler
  push_eh $P0
  ...
  
  my_handler:
    ...

Rethrowing and Exception Propagation

Exception handlers are nested and are stored in a stack. This is because not all handlers are intended to handle all exceptions. If a handler cannot deal with a particular exception, it can rethrow the exception to the next handler in the stack. Exceptions propagate through the handler stack until it reaches the default handler which causes Parrot to exit.

Annotations

Annotations are pieces of metadata that can be stored in a bytecode file to give some information about what the original source code looked like. This is especially important when dealing with high-level languages. We'll go into detail about annotations and their use in CHP-10Chapter 10.

Annotations are created using the c<.annotation> keyword. Annotations consist of a key/value pair, where the key is a string and the value is an integer, a number, or a string. Since annotations are stored compactly as constants in the compiled bytecode, PMCs cannot be used.

  .annotation 'file', 'mysource.lang'
  .annotation 'line', 42
  .annotation 'compiletime', 0.3456

Annotations exist, or are "in force" throughout the entire compilation unit, or until they are redefined. Creating a new annotation with the same name as an old one overwrites it with the new value. The current hash of annotations can be retrieved with the annotations opcode:

  .annotation 'line', 1
  $P0 = annotations # {'line' => 1}
  .annotation 'line', 2
  $P0 = annotations # {'line' => 2}

Or, to retrieve a single annotation by name, you can write:

  $I0 = annotations 'line'

Annotations in Exceptions

Exception objects contain information about the annotations that were in force when the exception was thrown. These can be retrieved with the 'annotation'() method of the exception PMC object:

  $I0 = $P0.'annotations'('line')  # only the 'line' annotation
  $P1 = $P0.'annotations'()        # hash of all annotations

Exceptions can also give out a backtrace to try and follow where the program was exactly when the exception was thrown:

  $P1 = $P0.'backtrace'()

The backtrace PMC is an array of hashes. Each element in the array corresponds to a function in the current call stack. Each hash has two elements: 'annotation' which is the hash of annotations that were in effect at that point, and 'sub' which is the Sub PMC of that function.

25 POD Errors

The following errors were encountered while parsing the POD:

Around line 3:

Unknown directive: =head0

Around line 5:

A non-empty Z<>

Around line 7:

Deleting unknown formatting code A<>

Around line 37:

A non-empty Z<>

Around line 39:

Deleting unknown formatting code N<>

Around line 81:

A non-empty Z<>

Around line 85:

A non-empty Z<>

Around line 216:

A non-empty Z<>

Around line 218:

Deleting unknown formatting code N<>

Around line 269:

Deleting unknown formatting code N<>

Around line 285:

A non-empty Z<>

Around line 307:

Deleting unknown formatting code A<>

Around line 320:

A non-empty Z<>

Around line 348:

A non-empty Z<>

Around line 362:

Deleting unknown formatting code N<>

Around line 373:

Deleting unknown formatting code A<>

Around line 377:

A non-empty Z<>

Around line 404:

A non-empty Z<>

Around line 453:

Deleting unknown formatting code A<>

Around line 458:

A non-empty Z<>

Around line 526:

Deleting unknown formatting code A<>

Around line 589:

Deleting unknown formatting code A<>

Around line 606:

Deleting unknown formatting code N<>

Around line 662:

Deleting unknown formatting code N<>

Around line 786:

Deleting unknown formatting code A<>