Added Global.nqp, with tests, to handle export/use and global variables.
[kakapo:kakapo.git] / src / Global.nqp
1 module Global;
2
3 _ONLOAD(); 
4
5 sub _ONLOAD() {
6         if our $onload_done { return 0; }
7         $onload_done := 1;
8
9 }
10
11 =sub export(*@symbols, :$as?, :@tags?)
12
13 Adds a list of symbols - either String names or Subs - to one or more export 
14 groups.
15
16 If a String is passed to identify the symbol, then the String will be the export
17 name of the symbol.
18
19 The symbol is added to all of the export groups named in C<@tags>.  This allows
20 definition of partially overlapping tag sets, by adding the common symbols to
21 multiple tags:
22     Global::export('c1', 'c2', 'c3', :tags('A', 'B'));
23     Global::export('a1', 'a2', :tags('A'));     # A include a1, a2, c1, c2, c3
24     Global::export('b1', :tags('B'));           # B includes b1, c1, c2, c3
25
26 If no tags are specified, the tag 'DEFAULT' is used. (This is the same tag used
27 by C<import> when no other tags are specified.)
28
29 The option C<:as($name)> can only be used with a single symbol. In this case,
30 the symbol - which in this case may be an object, or the String name of an 
31 object - is added to the specified export tags under the C<$name> given. (This
32 can be used to export dynamically created objects, or to export some other 
33 module's code under your own name.)
34
35 =cut
36
37 sub export($symbol, *@symbols, :$as?, :$namespace?, :@tags?) {
38         unless @symbols { @symbols := Array::empty(); }
39         @symbols.unshift($symbol);
40         if ! Parrot::defined(@tags) { @tags := Array::new('DEFAULT'); }
41         elsif Parrot::isa(@tags, 'String') { @tags := Array::new(@tags); }
42         
43         my $source_nsp := Parrot::defined($namespace)
44                 ?? $namespace
45                 !! Parrot::caller_namespace(2);
46         
47         if Parrot::isa($source_nsp, 'String') {
48                 $source_nsp := Parrot::get_namespace($source_nsp);
49         }
50
51         my $export_nsp := $source_nsp.make_namespace('EXPORT');
52         
53         @tags.push('ALL');
54         
55         for @tags {
56                 my $tag_nsp := $export_nsp.make_namespace(~ $_);
57                 
58                 if Parrot::defined($as) {
59                         my $export_sym := $symbol;
60                         if Parrot::isa($export_sym, 'String') {
61                                 $export_sym := $source_nsp.get_sym($export_sym);
62                         }
63                         
64                         $tag_nsp{$as} := $export_sym;
65                 }
66                 else {
67                         $source_nsp.export_to($tag_nsp, @symbols);
68                 }
69         }
70 }
71
72 =sub register_global($name, $object, :$namespace?)
73
74 Registers a symbol C<$name> in the Global:: namespace, bound to C<$object>.
75
76 This function is used to create global variables. The C<:namespace()> option
77 may be specified to use another namespace in preference to Global.
78
79 The intended usage pattern is that the Global namespace serves as a D<Registry>
80 for locating shared objects and services.
81
82 =cut
83
84 sub register_global($name, $object, :$namespace?) {
85         unless $namespace { $namespace := 'Global'; }
86
87         my $nsp := Parrot::get_namespace($namespace);
88         $nsp{$name} := $object;
89         export($name, :namespace($namespace));
90 }
91
92 =sub use($module?, :import('TAG', ...)?, :symbols('name', ...)?)
93
94 Imports global symbols into the caller's namespace. If neither C<:import> nor
95 C<:symbols> are specified, C<:import('DEFAULT')> is assumed.
96
97 The strings given to C<:import> are tag names. The C<DEFAULT> tag is one 
98 of two special tag names known to the system. Otherwise, each module may 
99 define its own tagging scheme. (The other predefined tag is C<ALL>.)
100
101 If C<:symbols> are specified, specific symbol names may be imported. The 
102 symbols must be in the target module's C<ALL> export group, as this is where
103 they are looked up. (This will normally be true, unless the same name has been
104 used for different exports in different TAGS. In which case, don't do that.)
105
106 If no C<$from> module is specified, the default is the Global:: module itself. 
107 This is a shortcut for defining global variables, in conjunction with the
108 C<register_global> function. (q.v.)
109
110 =cut
111
112 sub use($module?, :@import?, :@symbols?) {
113         if ! Parrot::defined($module) { $module := Parrot::caller_namespace(); }
114         elsif Parrot::isa($module, 'String') { $module := Parrot::get_hll_namespace($module); }
115         if ! Parrot::defined(@import) { @import := Array::empty(); }
116         elsif Parrot::isa(@import, 'String') { @import := Array::new(@import); }
117         if ! Parrot::defined(@symbols) { @symbols := Array::empty(); }
118         elsif Parrot::isa(@symbols, 'String') { @symbols := Array::new(@symbols); }
119
120         if +@import == 0 && +@symbols == 0 {
121                 @import.push('DEFAULT');
122         }       
123
124         my $export_nsp := $module.make_namespace('EXPORT');
125         my $target_nsp := Parrot::caller_namespace(2);
126
127         if @import {
128                 for @import {
129                         my $source_nsp := $export_nsp.make_namespace(~ $_);
130                         $source_nsp.export_to($target_nsp, $source_nsp.keys);
131                 }
132         }
133         
134         if +@symbols {
135                 $export_nsp{'ALL'}.export_to($target_nsp, @symbols);
136         }
137 }