Rand Stats

CompUnit::Util

github:LLFourn

CompUnit::Util Build Status

Utility functions for introspecting CompUnits and re-exporting their symbols.

CompUnit::Util contains set of utilities to introspect CompUnit stuff and a bunch of compile time symbol manipulation tools. Its main purpose is to encapsulate the compiler hacks needed to implement features like 're-exporting' which don't exist in rakudo yet.

The API should now be stable enough to use.

warning this module relies on unspec'd rakudo internals and can (and has and will) break without warning.

CompUnit Utilities

CompUnit introspection utilities.

Apart from load none of the routines here will load a compunit. All parameters named $handle are converted to a CompUnit::Handle automatically. If the $handle you pass is not a defined CompUnit or CompUnit::Handle, &find-loaded will be used to search for an loaded compunit matching it.

warning because of RT #127302, you should be very careful about manipulating CompUnit and CompUnit::Handle objects within BEGIN blocks. CompUnit::Handles cannot be serialized at the moment. For example,

use CompUnit::Util :load;
BEGIN load('SomeModule');

should be written as

use CompUnit::Util :load;
BEGIN {
    load('SomeModule');
    Nil;
}

load

(Str:D $short-name,*%opts --> CompUnit:D)

use CompUnit::Util :load;
my CompUnit $cu = load('Test');
# or even
my $cu = load('MyModule', version => v3);

Loads a compunit by name. All named arguments to CompUnit::DependencySpecification are accepted (other than short-name which is the positional argument). At the moment load is just short for:

$*REPO.need(CompUnit::Dependencyspecification.new(:short_name<MyModule>));

find-loaded

($match --> CompUnit)

use CompUnit::Util :find-loaded;
need SomeModule;
my CompUnit $some-module = find-loaded('SomeModule');

Searches all the CompUnit::Repositorys until it finds a loaded compunit matching $match. Returns a failure otherwise.

all-loaded

use CompUnit::Util :all-loaded;
.note for all-loaded;

Returns all loaded CompUnits.

at-unit

($handle,Str:D $key)

use CompUnit::Util :at-unit;
say at-unit('CompUnit::Util','$=pod');

Gets a symbol from the UNIT scope of the compunit. If you want to do this at compile time while a compunit is loading see get-unit.

unit-to-hash

($handle)

use CompUnit::Util :unit-to-hash;
my %unit := unit-to-hash('SomeModule');

returns a Hash representing the UNIT:: of the module.

capture-import

($handle, *@pos, *%named --> Hash:D)

use CompUnit::Util :capture-import;
need SomeModule;
my %symbols = capture-import('SomeModule',:tag);

Attempts to simulate a use statement. Returns a hash of all the symbols the compunit would export if it were used.

WHO Utilities

set-in-WHO

($WHO,$key,$value)

use CompUnit::Util :who;
my package Example {};
BEGIN set-in-WHO(Example.WHO,'Foo::Bar::$Baz','win');

say Example::Foo::Bar::<$Baz>; #-> win

Convenience routine for setting a symbol's value inside a package that might not exist yet. Only useful outside the compunit being compiled.

descend-WHO

($WHO,Str:D $path)

use CompUnit::Util :who;
my package Example {};
BEGIN set-in-WHO(Example.WHO,'Foo::Bar::Baz','win');
BEGIN note descend-WHO(Example.WHO,'Foo::Bar::Baz'); #-> win

Convenience routine for getting a symbol's value with a path from a Stash like .WHO. Only useful outside the currently compiling compunit (where you can just use the normal syntax).

Re-Exporting

The following routines provide re-exporting which is not yet implemented in rakudo.

re-export

($handle)

use CompUnit::Util :re-export;
need SomeModule;
BEGIN re-export('SomeModule');
# This compunit will now export everything that SomeModule does

Merges the EXPORT package from $handle into the present UNIT::EXPORT.

this routine can only be called at BEGIN time

re-exporthow

($handle)

use CompUnit::Util :re-export;
need SomeModule;
BEGIN re-exporthow('SomeModule');
# This compunit now exports SomeModule's custom declarators

Merges the EXPORTHOW from $handle into the present UNIT::EXPORTHOW. UNIT::EXPORTHOW will be created if it doesn't exist but it won't clobber it if it does.

this routine can only be called at BEGIN time warning Using this is throwing up warnings for some reason.

steal-export-sub

($handle)

use CompUnit::Util :re-export;
need SomeModule;
BEGIN steal-export-sub('SomeModule');
# This compunit now has the same &EXPORT as SomeModule

Sets UNIT::<&EXPORT> to $handle's &EXPORT.

this routine can only be called at BEGIN time

steal-globalish

($handle)

use CompUnit::Util :re-export,:load;
BEGIN steal-globalish(load('SomeModule'));
# This compunit now has everything in SomeModule in it's globalish

Merges the GLOBALish from $handle intot he present UNIT::GLOBALish.

This is the least interesting of all the re-exports, and if you've already done need SomeModule; you won't need it. But it's here for completeness. The above example should be the same as this anyway:

BEGIN require ::('SomeModule');

this routine can only be called at BEGIN time

re-export-everything

($handle)

use CompUnit::Util :re-export;
BEGIN re-export-everything('SomeModule');
# use [this-module]; should now do the same thing as use SomeModule;

A convenience method for calling all the other functions under re-export functions with the same argument.

this routine can only be called at BEGIN time

Symbol setting

The following routines manipulate the symbols of the compunit being compiled. They are probably of most use inside an &EXPORT sub or in a trait.

They each take a map of symbol names to values and install them in different places.

note: These subs will overwrite existing symbols without warning.

Inserts name/value pairs into the present UNIT::EXPORT under $tag.

set-unit

(Str:D $path,Mu $value)

# like is export, but prefixes the the exported name with 'fun-'
sub trait_mod:<is>(Routine:D $r, :$export-fun!) {
    my $exported-name = '&fun-' ~ $r.name;
    set-unit("EXPORT::DEFAULT::{$exported-name}",$r);
    {};}

Inserts the $value at $path in UNIT of the currently compiling compunit.

this routine can only be called at BEGIN time

set-lexpad

(Str:D $path,Mu $value)

The same as set-unit but inserts the $value into the lexical scope being compiled.

this routine can only be called at BEGIN time

Symbol Getting

get-unit

(Str:D $path)

use CompUnit::Util :get-symbols;
sub foo is export { };
BEGIN note get-unit('EXPORT::DEFAULT::&foo') === &foo; #-> True

get-lexpad

(Str:D $path)

The same as get-unit but looks for the symbol in the lexpad being compiled.

get-lexical

(Str:D $name)

Like get-lexpad but does a full lexical lookup. At the moment it can only take a single $name with no ::.

Dispatcher Manipulation

These routines help you construct multi dispatchers candidate by candidate in a procedural manner. Useful when you want to construct a trait that adds a multi candidate each time it's called. Parameters marked $multi can be any Routine:D. If you pass a dispatcher it will just use it as the dispatcher or die if you are trying to push onto an existing dispatcher.

If you try and push a non-multi/dispatcher onto an empty slot it will not vivify one for you.

push-multi

(Routine:D $target where { .is_dispatcher },Routine:D $candidate)

Adds the $candidate onto $target.

use CompUnit::Util :push-multi;
multi foo('one') { 'one' }
multi foo('two') { 'two' }

&foo.&push-multi(sub ('three') { "win" });
say foo('three') #-> "win"

note This is NYI in rakudo. The design docs says that protos should have a .push method. see S06.

push-unit-multi

(Str:D $path,Routine:D $mutli)

## lib/SillyModule.pm6
use CompUnit::Util :push-multi
# exports the multi under a sub named after its first letter
sub trait_mod:<is>(Routine:D $r,:$one-letter-export!) {
    my $exported-name = '&' ~ $r.name.comb[0];
    push-unit-multi("EXPORT::DEFAULT::{$exported-name}",$r);
}

multi bar(Str) is one-letter-export { say "bar" }
multi baz(Int) is one-letter-export { say "baz" }

...
use SillyModule;

b("string"); #-> bar
b(1) #-> baz

Takes $multi and pushses it onto a dispatcher located at $path. If one doesn't exist it will be created. You can pass a proto instead of a multi but only when $path is empty (ie only the first time). It will become the dispatcher for any further calls.

this routine can only be called at BEGIN time

push-lexpad-multi

(Str:D $path,Routine:D $mutli)

The same as push-unit-multi but pushes onto a symbol in the lexical scope currently being compiled.

this routine can only be called at BEGIN time

push-lexical-multi

(Str:D $name,Routine:D $mutli)

The smart version of push-lexpad-multi. If it doesn't find a dispatcher in the current lexpad it will do a lexical lookup for one of the same $name. If it finds one it clones it, installs it in the current lexpad and pushes $multi onto it. Like get-lexical, it can't take a $name with :: in it.

this routine can only be called at BEGIN time