parrotcode: Parrot Namespaces | |
Contents | Documentation |
docs/pdds/pdd21_namespaces.pod - Parrot Namespaces
$Revision$
get_namespace
opcode takes a multidimensional hash key or an array of name stringsA High Level Language, such as Perl, Python, or Tcl, in contrast to PIR, which is a low-level language.
The current namespace at runtime is the namespace associated with the currently executing subroutine. PASM assigns each subroutine a namespace when compilation of the subroutine begins. Don't change the associated namespace of a subroutine unless you're prepared for weird consequences.
(PASM also has its own separate concept of current namespace which is used to initialize the runtime current namespace as well as determine where to store compiled symbols.)
Namespaces are denoted in Parrot as simple strings, multidimentional hash keys, or arrays of name strings.
A namespace may appear in Parrot source code as the string "a"
or the key ["a"]
.
A nested namespace "b" inside the namespace "a" will appear as the key ["a"; "b"]
.
There is no limit to namespace nesting.
Parrot's target languages have a wide variety of namespace models. By implementing an API and standard conventions, it should be possible to allow interoperability while still allowing each one to choose the best internal representation.
get_root_namespace
opcode.
For example: $P0 = get_root_namespace
tcl
namespace. This eliminates any accidental collisions between languages._tcl
namespace.Most languages leave their symbols plain, which makes lookups quite straightforward. Others use sigils or other mangling techniques, complicating the problem of interoperability.
Parrot namespaces assist with interoperability by providing two interface subsets: the untyped interface and the typed interface.
Each HLL may, when working with its own namespace objects, use the untyped interface, which allows direct naming in the native style of the namespace's HLL.
This interface consists of the standard Parrot hash interface, with all its keys, values, lookups, deletions, etc. Just treat the namespace like a hash. (It probably is one, really, deep down.)
The untyped interface also has one method:
get_name
$P1 = $P2.get_name()
When a given namespace's HLL is either different from the current HLL or unknown, an HLL should generally use only the language-agnostic namespace interface. This interface isolates HLLs from each others' naming quirks. It consists of add_foo()
, find_foo()
, and del_foo()
methods, for values of "foo" including "sub" (something executable), "namespace" (something in which to find more names), and "var" (anything).
NOTE: The job of the typed interface is to bridge naming differences, and only naming differences. Therefore: 1) It does not enforce, nor even notice, the interface requirements of "sub" or "namespace": e.g. execution of add_sub("foo", $P0)
does not automatically guarantee that $P0 is an invokable subroutine; and 2) it does not prevent overwriting one type with another.
add_namespace
$P1.add_namespace($S2, $P3)
add_sub
$P1.add_sub($S2, $P3)
add_var
$P1.add_var($S2, $P3)
del_namespace
, del_sub
, del_var
$P1.del_namespace($S2)
$P1.del_sub($S2)
$P1.del_var($S2)
find_namespace
, find_sub
, find_var
$P1 = $P2.find_namespace($S3)
$P1 = $P2.find_sub($S3)
$P1 = $P2.find_var($S3)
our $A
and our @A
. (Well, you might want to consider not exporting variables at all, but that's a style issue.)export_to
$P1.export_to($P2, $P3)
use tcl:Some::Module 'c*'
. This operation would import all the commands that start with 'c' from the given Tcl namespace into the current Perl namespace. This is so because, regardless of whether 'c*' is a Perl 6 style export pattern, it is a valid Tcl export pattern.export_to
implementations will restrict themselves to using the typed interface on the target namespace. However, they may also decide to check the type of the target namespace and, if it turns out to be of a compatible type, to use same-language shortcuts.parse_name
$P1 = $P2.parse_name($S3)
a.b.c
' to ['a','b','c']
, while a Perl compiler would turn 'a::b::c
' into the same result. Meanwhile, due to Perl's sigil rules, '$a::b::c
' would become ['a','b','$c']
.get_namespace
$P1 = $P2.get_namespace($P3)
get_namespace
retrieves the base namespace for the HLL. It returns a namespace PMC on success and a null PMC on failure.get_root_namespace
followed by zero or more calls to <namespace>.get_namespace(). However, any compiler is free to cheat if it doesn't get caught, e.g. to use the untyped namespace interface if the language doesn't mangle namespace names.load_library
$P1.load_library($P2, $P3)
perl5.load_library(["Some", "Module"], null)
.Some information must be available about subroutines to implement the correct behavior about namespaces.
get_namespace
$P1 = $P2.get_namespace()
The namespace opcodes all have 3 variants: one that operates from the currently selected namespace (i.e. the namespace of the currently executing subroutine), one that operates from the HLL root namespace (identified by "hll" in the opcode name), and one that operates from the true root namespace (identified by "root" in the name).
set_namespace
set_namespace [key], $P1
set_hll_namespace [key], $P1
set_root_namespace [key], $P1
set_namespace $P1, $P2
set_hll_namespace $P1, $P2
set_root_namespace $P1, $P2
del_namespace
get_namespace
$P1 = get_namespace
$P1 = get_hll_namespace
$P1 = get_root_namespace
$P1 = get_namespace [key]
$P1 = get_hll_namespace [key]
$P1 = get_root_namespace [key]
$P1
. $P1 = get_namespace $P2
$P1 = get_hll_namespace $P2
$P1 = get_root_namespace $P2
$P1
. $P0 = get_hll_namespace ["Foo"; "Bar"]
$P1 = split "::", "Foo::Bar"
$P0 = get_hll_namespace $P1
make_namespace
$P1 = make_namespace [key]
$P1 = make_hll_namespace [key]
$P1 = make_root_namespace [key]
$P1
. If the namespace already exists, only retrieve it. $P1 = make_namespace $P2
$P1 = make_hll_namespace $P2
$P1 = make_root_namespace $P2
$P1
. If the namespace already exists, only retrieve it.get_global
$P1 = get_global $S2
$P1 = get_hll_global $S2
$P1 = get_root_global $S2
$P1 = get_global [key], $S2
$P1 = get_hll_global [key], $S2
$P1 = get_root_global [key], $S2
$P1 = get_global $P2, $S3
$P1 = get_hll_global $P2, $S3
$P1 = get_root_global $P2, $S3
set_global
set_global $S1, $P2
set_hll_global $S1, $P2
set_root_global $S1, $P2
set_global [key], $S1, $P2
set_hll_global [key], $S1, $P2
set_root_global [key], $S1, $P2
set_global $P1, $S2, $P3
set_hll_global $P1, $S2, $P3
set_root_global $P1, $S2, $P3
In order to make this work, Parrot must somehow figure out what type of namespace PMC to create.
The default namespace PMC will implement Parrot's current behavior.
This Perl:
#!/usr/bin/perl
package Foo;
$x = 5;
should map roughly to this PIR:
.HLL "Perl5", "perl5_group"
.namespace [ "Foo" ]
.sub main :main
$P0 = new 'PerlInt'
$P0 = 5
set_global "$x", $P0
.end
In this case, the main
sub would be tied to Perl 5 by the .HLL
directive, so a Perl 5 namespace would be created.
Consider the following Perl 5 program:
#!/usr/bin/perl
$a = 'x';
${"Foo::$a"} = 5;
The Foo::
namespace is created at run-time (without any optimizations). In these cases, Parrot should create the namespace based on the HLL of the PIR subroutine that calls the store function.
.HLL "Perl5", "perl5_group"
.sub main :main
# $a = 'x';
$P0 = new 'PerlString'
$P0 = "x"
set_global "$a", $P0
# ${"Foo::$a"} = 5;
$P1 = new PerlString
$P1 = "Foo::"
$P1 .= $P0
$S0 = $P1
$P2 = split "::", $S0
$S0 = pop $P2
$S0 = "$" . $S0
$P3 = new 'PerlInt'
$P3 = 5
set_global $P2, $S0, $P3
.end
In this case, set_global
should see that it was called from "main", which is in a Perl 5 namespace, so it will create the "Foo" namespace as a Perl 5 namespace.
Perl 6 may wish to be able to access the namespace as a hash with sigils. That is certainly possible, even with subroutines and methods. It's not important that a HLL use the typed namespace API, it is only important that it provides it for others to use.
So Perl 6 may implement get_keyed
and set_keyed
VTABLE slots that allow the namespace PMC to be used as a hash. The find_sub
method would, in this case, append a "&" sigil to the front of the sub/method name and search in the internal hash.
Since functions and variables overlap in Python's namespaces, when exporting to another HLL's namespace, the Python namespace PMC's export_to
method should use introspection to determine whether x
should be added using add_var
or add_sub
. $I0 = does $P0, "Sub"
may be enough to decide correctly.
Since Python's subroutines and namespaces are just variables (the namespace collides there), the Python PMC's find_var
method may return subroutines as variables.
Perl:
#!/usr/bin/perl6
sub foo {...}
%Foo::{"&bar"} = &foo;
PIR:
.sub main :main
$P0 = get_name "&foo"
$P1 = get_namespace ["Foo"]
# A smart perl6 compiler would emit this,
# because it knows that Foo is a perl6 namespace:
$P1["&bar"] = $P0
# But a naive perl6 compiler would emit this:
$P1.add_sub("bar", $P0)
.end
.sub foo
...
.end
Perl 5:
#!/usr/bin/perl
use tcl:Some::Module 'w*'; # XXX - ':' after HLL may change in Perl 6
write("this is a tcl command");
PIR (without error checking):
.sub main :main
.local pmc tcl
.local pmc ns
tcl = compreg "tcl"
ns = new 'Array'
ns = 2
ns[0] = "Some"
ns[1] = "Module"
null $P0
tcl.load_library(ns, $P0)
$P0 = tcl.get_namespace(ns)
$P1 = get_namespace
$P0.export_to($P1, 'w*')
"write"("this is a tcl command")
.end
None.
None.
None.
|