IMCC.pm is still a "ball of mud" at the moment, but context propagation has been moved to Context.pm. Next for refactoring is symbol handling.

Context should not be propagated during code generation, as the context propagation pass handles this. This rule is broken for hyper-operators, since I can't think of a good way to handle them using context.

Code is generated by a depth-first recursive traversal of the op tree. Each node type should define a val function to be called by its parent node. This function should gather values from child nodes (by calling their val functions), then emit the code for the node's operation (using P6C::Compiler::code). Code is appended to the current function in the order in which it is generated, so subnodes must be evaluated in the proper order.

val should return one of the following:

Node types that can act as lvalues should define an assign function that takes an unevaluated rvalue tree and a context structure. This function should return a PMC register or array ref (like val) if (like an assignment) it serves as both an lvalue and an rvalue.

External interface ^

If P6C::IMCC is imported with the ":external" flag, it will define the following interface, used by the driver:


Initialize or reset compiler state. This should be called before generating any code. init destroys all functions and globals, resets the current function, and reinitializes builtins.


Compile a tree based at $top, but do not emit any code.


Emit IMCC code on standard output, including a header that calls main, and the code for any builtin functions (see P6C::Builtins). emit will fail if you have not defined main.

Internals ^

If P6C::IMCC is imported with the ":all" flag, it exports an internal interface.

The compiler maintains a "current function" (could be generalized to "current scope") in which code is emitted, locals are declared, and symbol lookups begin. The following functions manipulate the current function context.


Append IMCC code $x to the current function.

add_function($name, [$sub_def])

Create a new function stub for $name. If $name exists, it will be overwritten. In such cases, a warning will be omitted unless the {'weak'} flag is set.


Return true if function $name is defined (i.e. not just "declared").


Return true if a stub exists for $name, even if it has no code.

$oldfunc = set_function($name)

Set the code insertion point to the end of function $name, returning the name of the previously active function. Function $name should exist before this is called.

Name lookup ^

This is a primitive symbol table. Which is okay, since Parrot doesn't have stashes yet. Hopefully the interface will be useful when things get more complicated.

$name = globalvar($var)

Lookup a global variable.

add_globalvar($var [, $type])

Declare global variable $var. Warns if $var is already defined. $var will be initialized to a new PMC of type $type (or PerlUndef if type is not given) before main is called.

$name = localvar($var)

Find local variable $var, returning its IMCC name.

add_localvar($var, $type)

Declare local variable $var of type $type. Warns if $var is already defined. If $type is a PMC type, $var will automatically be initialized.

$name = paramvar($var)

Find parameter $var.

$name = findvar($var)

($name, $isglobal) = findvar($var)

Find variable $var, a P6C::variable, returning a PMC register containing its value. Currently findvar looks at the active function's parameters, then locals, then globals (which don't exist, so it won't find anything there). Returns undef if the variable is not found. $isglobal is currently unused.


Push a scope within the current function.


Pop a scope from the current function.


Mangle any kind of variable, function, or operator name.

Convert names like $foo to _SV_foo. If the name is prefixed with an equals sign, then just pass the rest of the string through untouched (so the mangled form of "=$#@!!" is "$#@!!".

Labels ^

Note that the "labels" here aren't necessarily simple addresses in the code; while this may sometimes be the case, creating some labels may involve taking a continuation, and jumping to labels may involve throwing an exception and unwinding the call stack.

XXX: Labels and try/CATCH currently use different mechanisms, contrary to Apocalypse 4. Exceptions are implemented with continuations, and are therefore much more expensive than labels, which use simple jumps. Eventually, either continuations will have to become much lighter-weight, or the compiler will have to determine when a jump is sufficient, and when a continuation or exception is required. This implementation means that you can't mix gotos and exceptions without Bad Things happening.

The name argument is a label name, and may be undefined for typed loop labels (e.g. "next"). The type argument should be one of the following:








Label handling functions:

declare_label(name = $name, type => $type)>

Declare a label in the current scope. Either name or type may be omitted.

emit_label(name = $name, type => $type)>

Emit code for a label in the current scope. Either name or type may be omitted.

goto_label(name = $name, type => $type)>

Branch to the appropriate version of a label. Either name or type may be omitted.


Do something intensely interesting that is extremely important for you to understand.

Topic ^


Sets the topic to $x until the next call to set_topic, or until the end of the current scope, whichever is first. Note that $x is a variable, not a value.


Get the current topic variable.

Temporary names ^


Generate a unique identifier. If $str is given, include it as part of the identifier.


Generate an uninitialized temporary register.


Generate an uninitialized local variable with the given type and name.


Generate a unique label containing $str.


Create a new temporary register to hold a value of type $type, which should be "int", "num", "str", or some PMC type. If $type is a PMC type, the register will be initialized with a new value. If $type is omitted, it default to PerlUndef.


Generate a new local variable initialized with the default value for its type.

Code generation functions ^

The following functions generate useful and common pieces of code.

gen_counted_loop($counter, $body)

Generate a counted loop using $counter as the repetition count. The loop will iterate over values between 0 and $counter - 1, inclusive. $counter will be used as the iteration variable, so it can be used in indexing expressions in the loop body.

scalar_in_context($val, $ctx)

Emit the code to return a scalar $val in the right way for context $ctx. In array context, that means create a single-element array containing the scalar.

primitive_in_context($val, $primitive_type, $ctx)

array_in_context($val, $ctx)

Convert an array to the appropriate type for the given context. In tuple context, pull out as many values as needed from the array. In scalar context, return the length of the array.

tuple_in_context(\@vals, $ctx)

Convert a tuple to the appropriate type for the given context. In tuple context, pad with PerlUndefs or truncate the array. In array context, construct an array out of all of the values in the tuple. In scalar context, return the first element in the tuple.


Convert an undefined value to the appropriate type for the given context. For example, an undefined value is an empty PerlArray. For most types, it is a PerlUndef object.


Emit code to evaluate each item in @$vals, which are assumed to be in list context. The results are concatenated into a single array, whose name is returned.

do_append_array($dest, $src)

Append one array to another. I would vastly prefer having an 'append' opcode, but that can wait.

P6C::rule ^

A node representing a rule.

val() : rule -> match obj

Generate code defining a rule

Rules take several arguments: mode - 0 means try to match rule 1 means backtrack into rule rx_pos - position within input string to start matching rx_input - input string rx_stack - backtracking state stack params - rule parameters array

and return two values: rx_pos - position after applying rule (to match or backtrack) status - 1 for success, 0 for failure

TODO: Rather than returning a status code, this really ought to just invoke the appropriate continuation.

TODO: It would also be nice to have two different entry points rather than the hokey mode param.


P6C::IMCC::prefix for prefix operators (function calls, return statements, if/for/while/given, etc.)