The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

docs/compiler_faq.pod - Parrot FAQ for compiler writers

General Questions

Which C compilers can I use with Parrot?

Whoa, there--you're looking at the wrong FAQ. This document is for people writing compilers that target Parrot.

To answer your question, though, Parrot should theoretically work with any C89-compliant C compiler, although some features require gcc. See the README files in the root directory for more information about building Parrot.

How can I implement a compiler to use as a compiler object from within Parrot? (For example, with the compile op.)

Define a sub that takes as input a string, and returns something invokable. The easiest way to create something invokable at the moment is to use the builtin PIR or PASM compilers.

See languages/tcl/tcl.pir_template's .sub _tcl_compile as an example.

How do I embed source locations in my code for debugging?

You can do this using either the setfile and setline opcodes or with C-like #line comments:

  #line 27 "my_source.file"

Simply set the source file name or line number whenever it changes. But note that currently (Parrot 0.3.0) both are ignored in the lexer.

Subroutines

How do I generate a sub call in PIR?

If you have a fixed-length parameter list, IMCC makes this blindingly easy:

   $P0( $P1, $P2, $P3 )

where $P0 is the function object, and $P1, $P2, and $P3 are its parameters. You can also use a function's label in place of the object:

   somefunctionlabel( $P1, $P2, $P3 )

You can also get return value(s):

  ($P1,$P2) = $P0( $P1, $P2, $P3 )

If the function name might collide with a Parrot opcode, quote it:

   i = "new"(42)

How do I generate a method call in PIR?

Similar to function calls, just append . to the object and the method:

  ret_val = some_obj."some_meth"(arg)

The method name may also be a string variable representing a method name:

  .local string m
  m = "bold"
  curses_obj.m()

How do I locate or create a subroutine object

There are several ways to achieve this, depending on the location of the subroutine.

If the sub is in the same compilation unit use a Sub constant:

  .const .Sub foo = "foo"

The subroutine object is available in PASM too with a similar syntax:

  .const .Sub P2 = "foo"      # any P register will do
  ...
  .pcc_sub foo:               # denote a Sub constant

If the PIR compiler finds a "foo" function during compiling a file, then the syntax:

  foo()

gets translated to above constant declaration.

A more dynamic way is:

  .local pmc foo
  foo = find_name "foo"

This searches for a subroutine "foo" in the current lexical pad, in the current namespace, in the global, and in the builtin namespace in that order. This opcode is generated, if foo() is used, but the compiler can't figure out, where the function is.

If the subroutine is in a different namespace, use the find_global opcode:

  foo = find_global "Foo", "foo"

This searches the sub "foo" in the "Foo" namespace.

How do I create a Closure or Coroutine

Both carry dynamic state, therefore you use one of the above ways to locate the sub object, then you just clone it to get a distinct copy of it:

  .local pmc coro
  coro = find_name "my_coro"
  coro = clone coro

Any subroutine that contains a .yield directive is automatically created as a Coroutine PMC:

  .sub my_coro             # automagically a Coroutine PMC
     ...
     .yield (result)
     ...
  .end

How do I generate a tail call in PIR?

  .sub foo
      # do something
      .return bar(42)           # tail call sub bar
  .end

  .sub bar
      # ...
  .end

The sub bar will return to the caller of foo.

How do I generate a sub call with a variable-length parameter list in PIR?

If you have a variable amounts of arguments in an array, you can pass all items of that array with the :flat directive.

  ar = new .ResitablePMCArray
  push ar, "arg 1\n"
  push ar, "arg 2\n"
  ...
  foo(ar :flat)
  ...

How to I retrieve the contents of a variable-length parameter list being passed to me?

Use a slurpy array:

  .sub mysub
    .param pmc argv      :slurpy
    .local int argc
    argc = argv
    ...

If you have a few fixed parameters too, you can use a slurpy array to get the rest of the arguments

  .sub mysub
    .param pmc arg0
    .param pmc arg1
    .param pmc varargs   :slurpy
    .local int num_varargs
    num_varargs = varargs
    ...

How do I pass optional arguments

Use the :optional and :opt_flag pragmas:

  .sub foo
     .param pmc arg1       :optional
     .param int has_arg1   :opt_flag
     .param pmc arg2       :optional
     .param int has_arg2   :opt_flag

     if has_arg1 goto got_arg1
     ...

How do I create nested subroutines?

PIR doesn't support nested subroutines. You have to emit subroutines one by one. If lexicals of the outer subroutine are visible inside the nested sub, you have to include the outer pad depth in new_pad opcodes.

Variables

How do I fetch a variable from the global namespace?

There are two possible ways. Either use the special PIR syntax:

    $P0 = global "name_of_the_global"

or the find_global op:

    find_global $P0, "name_of_the_global"

How can I delete a global

You can retrieve the namespace hash and use the delete opcode. Nested namespace names have a NULL char prepended to their name.

  .sub main :main
    $P0 = new .Integer
    store_global "foo", $P0
    store_global "Bar", "baz", $P0
    # ...
    .include "interpinfo.pasm"
    .local pmc ns, Bar_ns
    ns = interpinfo .INTERPINFO_NAMESPACE_ROOT
    delete ns["foo"]                # delete from top level
    Bar_ns = ns["\0Bar"]            # get Bar namespace
    delete Bar_ns["baz"]
    $P0 = find_global "Bar", "baz"
    print "never\n"
  .end

How do I use lexical pads to have both a function scope and a global scope?

To create lexical variables, you'll need to keep track of how deeply nested each block of code is. Say you have some HLL code like so:

    # depth 0
    lexical $bar
    $foo = 3       # global foo
    $bar = 5       # bar at depth 0
    {
      # depth 1
      lexical $foo
      $foo = 5     # foo at depth 1
      {
         # depth 2
         lexical $bar
         $foo = 7  # also foo at depth 1
         $bar = 2  # bar at depth 2
      }
      {
         # depth 2, again
         lexical $foo
         $foo = 11 # foo at depth 2
      }
    }

When you are building your program tree, have each block reference its parent block, note its depth, and keep a list of all its lexical variables. At the opening of each block (that has lexical variables), emit code to push a lexical pad:

    new_pad n

where n is the lexical depth of the block. After the first new_pad 0, you can also use:

    new_pad -1

to create a new pad with a nesting of outer + 1.

At any point that you exit a block, you should emit

    pop_pad

This isn't necessary if you leave a block by invoking a continuation (by returning from a subroutine, for instance), as the continuation will automatically put the lexical pad stack back the way it was when the continuation was created.

When you need to figure out how to access a certain variable, simply look at the topmost block and work your way down the tree until you finds a block that declares lexical variables. Then take the lexical depth of the block in which you found it and emit some code like so:

    find_lex $P0, 2, "foo"

where 2 is the depth the variable was found at, foo is the name of the variable, and $P0 is a PMC register in which to store the variable.

Note that, by convention (and confusing IMCC syntax), variables, not direct values, are stored. So to assign to this lexical variable, you would say:

    find_lex $P0, 2, "foo"
    assign $P0, some_value

instead of:

    store_lex 2, "foo", some_value

You will still need to do a store_lex at some point (probably at the start of the block in which it is declared) to create the variable in the first place. Put a Undef PMC in it or something.

If, on the other hand, you never find said lexical variable (or if a block declares that variable to be global, or whatever other tricks your compiler likes to do), you might assume it to be a global, which you can access much the same way:

    find_global $P0, "bar"
    assign $P0, value_to_store

How can I delete a lexical

You can peek_pad the current pad and use delete.

  .sub main :main
    new_pad 0
    $P0 = new .Integer
    store_lex -1, "foo", $P0
    .local pmc pad
    pad = peek_pad
    delete pad["foo"]                # delete from current pad
    $P0 = find_lex "foo"
    print "never\n"
  .end

How do I resolve a variable name?

Use find_name:

    $P0 = find_name "$x"
    find_name $P0, "foo"    # same thing

This will find the name foo in lexical, global or builtin namespace, in that order, and store it in $P0.

How do I fetch a variable from the current lexical pad?

Use lexical depth -1:

    find_lex $P0, -1, "foo"

How do I fetch a variable from any nesting depth?

Omit the depth parameter:

    find_lex $P0, "foo"

This finds a foo variable at any depth starting from the top.

How can I produce more efficient code for lexicals?

Use numeric indices instead of variable names for lexical retrieval. Lexicals of one pad depth are numbered from 0..n-1 in the order of their declaration.

    find_lex $P0, -1, 0       # get first lexical from top pad
    find_lex $P1, -2, 3       # get forth lexical from next inner pad
    ...

Modules, Classes, and Objects

How do I create a module?

How do I create a class?

With the newclass op:

    newclass $P0, "Animal"

How do I add instance variables/attributes?

Each class knows what attribute its object can have. You can add attributes to a class (not to individual objects) like so:

    addattribute $P0, "legs"

How do I add instance methods to a class?

Methods are declared as functions in the class namespace with the method keyword appended to the function declaration:

  .namespace [ "Animal" ]

  .sub __init method
    $P0 = new .Integer
    setattribute self, "legs", $P0
    ...
  .end
  .sub run method
    ...
  .end

How do I access attributes?

Attributes can be access by a short name, fully qualified name or by index.

  $P0 = getattribute self, "legs"
  assign $P0, 4                   # set attribute's value

or

  $P0 = getattribute self, "Animal\0legs"
  assign $P0, 4                   # set attribute's value

or

  .local int offs
  offs = classoffset "Animal"
  $I0 = offs + 0                  # 1st attribute
  $P0 = getattribute self, $I0
  $I0 = offs + 1                  # 2nd attribute
  $P0 = getattribute self, $I0

When should I use properties vs. attributes?

Properties aren't inherited. If you have some additional data that don't fit into the classes hierarchy, you could use properties.

How do I create a class that is a subclass of another class?

You first have to get the class PMC of the class you want to subclass. Either you use the PMC returned by the newclass op if you created the class, or use the getclass op:

    getclass $P0, "Animal"

Then you can use the subclass op to create a new class that is a subclass of this class:

    subclass $P1, $P0, "Dog"

This stores the newly created class PMC in $P1.

How do I create a class that has more than one parent class?

First, create a class without a parent class using newclass (or with only one subclass, see previous question). Then add the other parent classes to it. Please refer to the next question for an example.

How do I add another parent class to my class?

If you have a class PMC (created with newclass or by subclass), you can add more parent classes to it with the addparent op:

    getclass $P1, "Dog"
    subclass $P2, $P1, "SmallDog"
    getclass $P3, "Pet"
    addparent $P2, $P3  # make "SmallDog" also a "Pet"

How can I specify the constructor of a class?

Just define a method named __init in the namespace if the class.

    newclass $P0, "Dog"         # create a class named Dog
    # ...

    .namespace ["Dog"]

    .sub __init method
      # ...

Or you can specify the constructor method by setting the BUILD property of the class PMC:

    newclass $P0, "Dog"         # create a class named Dog
    new $P1, .String            # create a string
    set $P1, "initialise"       # set it to the name of the constructor method
    setprop $P0, "BUILD", $P1   # set the BUILD property

How do I instantiate a class?

First, you have to retrieve the type id of class you want to instantiate:

    find_type $I0, "Dog"

Then, you can create an instance of Dog with the new op:

    new $P0, $I0    # creates a Dog object and stores it in register $P0

or for short:

    new $P0, "Dog"

During the new opcode the constructor is called.

How can I pass arguments to an constructor?

You can pass only a single argument to a constructor. By convention, a hash PMC is passed to the constructor that contains the arguments as key/value pairs:

    new $P0, .Hash
    set $P0["greeting"], "hello"
    set $P0["size"], 1.23

    find_type $I0, "Alien"
    new $P1, $I0, $P0           # create an Alien object and pass
                                # the hash to the constructor

How do I add module/class methods?

How do I access module/class variables?

Exceptions

How do I throw an exception in PIR?

Create an Exception object and throw it!

    $P0 = new .Exception
    throw $P0

Not too hard, is it?

How do I throw an exception with an error message in PIR?

    $P0 = new .Exception
    $P0["_message"] = "something happened"
    throw $P0

How do I catch an exception in PIR?

Use push_eh to push an exception handler onto the stack.

    push_eh handler
    $P0 = new .Exception         # or any other code ...
    throw $P0                   # ... that might throw
    clear_eh
    exit 0

  handler:
    print "Exception caught!\n"
    exit 1

How do I access the error message of an exception I've caught?

P5 is the register used for the Exception object.

    push_eh handler
    $P0 = new .Exception
    $P0["_message"] = "something happened"
    throw $P0
    clear_eh
    exit 0

  handler:
    print "Exception: "
    $S0 = P5["_message"]
    print $S0
    print "\n"
    exit 1

Misc

How can I access a programs environment.

Create a new Env PMC and access it like a hash.

    .local pmc e
    e = new .Env
    $P0 = e['USER']      # lt

How can I access parrot's configuration.

    .include "iglobals.pasm"
    .local pmc interp, cfg
    interp = getinterp
    cfg = interp[.IGLOBALS_CONFIG_HASH]
    $S0 = cfg['VERSION']     "0.3.0"

See config_lib.pasm for all the keys in the config hash - or iterate over the config hash.