Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[pmc]: exporter api changes

~ removed 'add_global' method
~ 'globals' is now getter/setter, matching 'source' and 'destination' method semantics
~ added SYNOPSIS with example code

git-svn-id: https://svn.parrot.org/parrot/trunk@18538 d31e2699-5ff4-0310-a27c-f18f2fbe73fe
  • Loading branch information...
commit abc4db2cab619a98a027ec05dea80b7aa9b1f8c6 1 parent cf259ef
@particle particle authored
Showing with 198 additions and 124 deletions.
  1. +79 −52 src/pmc/exporter.pmc
  2. +119 −72 t/pmc/exporter.t
View
131 src/pmc/exporter.pmc
@@ -6,6 +6,24 @@ $Id$
src/pmc/exporter.pmc - Export globals from one namespace to another
+=head1 SYNOPSIS
+
+You can use Exporter in PIR to import subs from a library. At its simplest:
+
+ .sub main :main
+ load_bytecode 'Test/More.pir'
+ .local pmc exporter, test_ns
+ test_ns = get_namespace ['Test::More']
+ exporter = new 'Exporter'
+ exporter.'import'( test_ns :named('source'), 'plan ok' :named('globals') )
+ plan(1)
+ ok(1, 'exporter has imported the requested functions')
+ .end
+
+Fancier options exist which allow you to import to an alternate namespace,
+or export globals so they can be called using a different name. See
+F<t/pmc/exporter.t> for examples.
+
=head1 DESCRIPTION
Exports globals from one namespace to another. Exporter always uses
@@ -13,7 +31,7 @@ the typed namespace interface, as outlined in
F<docs/pdds/pdd21_namespaces.pod>.
Exporter is not derived from any other PMC, and does not provide any
-standard interface--its interface consists solely of non-vtable methods.
+vtable interface--its interface consists solely of non-vtable methods.
=head2 Structure
@@ -33,7 +51,7 @@ A PMC representing the current namespace is allocated upon initialization.
=item C<globals>
-The globals to export -- a ResizableStringArray.
+The globals to export -- a PMC that implements the hash interface, or Null.
A Null PMC is allocated during initialization.
=cut
@@ -156,7 +174,6 @@ Throws an exception if a non-NameSpace PMC is passed.
"source must be a NameSpace PMC");
return;
}
-
exp->ns_src = src;
}
else {
@@ -204,41 +221,76 @@ Throws an exception if a non-NameSpace PMC is passed.
=item C<PCCMETHOD void
globals(PMC *glb :optional, int got_glb :opt_flag)>
-Accessor for the array of globals to export (C<globals>.)
-Sets the array if C<glb> is passed, otherwise returns the value.
-If C<glb> is a String, it is split on ascii whitespace.
-If C<glb> is a FixedStringArray, it is cloned and set.
+Accessor for the globals to export (C<globals>.)
+Sets the value if C<glb> is passed, otherwise returns the value.
+If C<glb> is a String, it is split on ascii whitespace, and each array member
+is added as a hash key.
+If C<glb> implements the array interface, each member is added as a hash key.
+if C<glb> implements the hash interface, it is assigned to Exporter's
+C<globals> attribute.
+Throws an exception if an unknown PMC type is passed.
=cut
*/
PCCMETHOD void globals(PMC *glb :optional, int got_glb :opt_flag) {
- Parrot_Exporter *exp = PARROT_EXPORTER(SELF);
- PMC *ret_globals;
- STRING *s_str = CONST_STRING(interp, "String");
- STRING *s_arr = CONST_STRING(interp, "array");
+ Parrot_Exporter *exp = PARROT_EXPORTER(SELF);
+ PMC *ret_globals;
+ STRING *s_str = CONST_STRING(interp, "String");
+ STRING *s_arr = CONST_STRING(interp, "array");
+ STRING *s_hash = CONST_STRING(interp, "hash");
if (got_glb) {
- if (PMC_IS_NULL(glb))
- exp->globals = PMCNULL;
- else if (VTABLE_isa(interp, glb, s_str))
- exp->globals = string_split(interp,
- CONST_STRING(interp, " "),
- VTABLE_get_string(interp, glb));
- else if (VTABLE_does(interp, glb, s_arr))
- exp->globals = VTABLE_clone(interp, glb);
- else
- real_exception(interp, NULL, 0,
- "Invalid type %d in globals()", glb->vtable->base_type);
+ STRING * const s_empty = CONST_STRING(interp, "");
+ PMC *temp_globals = pmc_new(interp, enum_class_Hash);
+
+ if (PMC_IS_NULL(glb)) {
+ temp_globals = PMCNULL;
+ }
+ else if (VTABLE_isa(interp, glb, s_str) || (VTABLE_does(interp, glb, s_arr))) {
+ PMC *glb_array;
+ INTVAL n, i;
+
+ if (VTABLE_isa(interp, glb, s_str))
+ glb_array = string_split(interp,
+ CONST_STRING(interp, " "),
+ VTABLE_get_string(interp, glb));
+ else
+ glb_array = glb;
+
+ n = VTABLE_elements(interp, glb_array);
+
+ if (n == 0)
+ temp_globals = PMCNULL;
+
+ for(i = 0; i < n; i++) {
+ STRING * const item = VTABLE_get_string_keyed_int(interp, glb_array, i);
+ VTABLE_set_string_keyed_str(interp, temp_globals, item, s_empty);
+ }
+ }
+ else if (VTABLE_does(interp, glb, s_hash)) {
+ if (VTABLE_elements(interp, glb) == 0)
+ temp_globals = PMCNULL;
+ else
+ temp_globals = glb;
+ }
+ else {
+ real_exception(interp, NULL, 0,
+ "Invalid type %d in globals()", glb->vtable->base_type);
+ return;
+ }
+
+ exp->globals = temp_globals;
}
else {
if (PMC_IS_NULL(exp->globals)) {
PCCRETURN(PMC *PMCNULL);
}
else {
- ret_globals = VTABLE_clone(interp, exp->globals);
- PCCRETURN(PMC *ret_globals);
+ PMC *tmp_globals;
+ tmp_globals = exp->globals;
+ PCCRETURN(PMC *tmp_globals);
}
}
}
@@ -247,37 +299,12 @@ If C<glb> is a FixedStringArray, it is cloned and set.
/*
=item C<PCCMETHOD void
- add_global(PMC *global :optional, int has_global :opt_flag)>
-
-Add C<global> to the array of globals (C<globals>.)
-Sets the array if C<global> is passed, otherwise does nothing.
-
-=cut
-
-*/
-
- PCCMETHOD void add_global(PMC *global :optional, int has_global :opt_flag) {
- Parrot_Exporter *exp = PARROT_EXPORTER(SELF);
- if (has_global) {
- if (PMC_IS_NULL(exp->globals))
- exp->globals = pmc_new(interp, enum_class_ResizableStringArray);
-
- VTABLE_push_string(interp, exp->globals,
- VTABLE_get_string(interp, global));
- }
- }
-
-
-/*
-
-=item C<PCCMETHOD void
import(PMC *dest :optional :named["destination"], int got_dest :opt_flag,
- PMC *src :optional :named["source"], int got_src :opt_flag,
+ PMC *src :optional :named["source"], int got_src :opt_flag,
PMC *globals :optional :named["globals"], int got_globals :opt_flag)>
Import C<globals> from the C<src> namespace to the C<dest> namespace.
-If C<src>, C<dest>, or C<globals> are passed, they will override
-the current value.
+If C<src>, C<dest>, or C<globals> are passed, they override the current value.
C<import> follows the semantics of the C<export_to> method
of the C<NameSpace> PMC. in particular, if a NULL value is passed
for C<globals>, the default set of items will be imported.
@@ -288,7 +315,7 @@ Throws an exception upon error.
*/
PCCMETHOD void import(PMC *dest :optional :named["destination"], int got_dest :opt_flag,
- PMC *src :optional :named["source"], int got_src :opt_flag,
+ PMC *src :optional :named["source"], int got_src :opt_flag,
PMC *globals :optional :named["globals"], int got_globals :opt_flag) {
Parrot_Exporter *exp = PARROT_EXPORTER(SELF);
View
191 t/pmc/exporter.t
@@ -6,7 +6,7 @@ use strict;
use warnings;
use lib qw( . lib ../lib ../../lib );
use Test::More;
-use Parrot::Test tests => 10;
+use Parrot::Test tests => 12;
=head1 NAME
@@ -22,6 +22,7 @@ Tests the Exporter PMC.
=cut
+
# L<PDD17/Exporter PMC>
# TODO fix smartlinks once this is specced
pir_output_is( <<'CODE', <<'OUT', 'new' );
@@ -40,6 +41,7 @@ ok 1 - $P0 = new .Exporter
ok 2 - isa $P0, 'Exporter'
OUT
+
pir_output_is( <<'CODE', <<'OUT', 'source' );
.sub 'test' :main
new $P0, .Exporter
@@ -92,6 +94,7 @@ ok 3 - source() with too many args fails
ok 4 - source() with non-namespace arg throws exception
OUT
+
pir_output_is( <<'CODE', <<'OUT', 'destination' );
.sub 'test' :main
new $P0, .Exporter
@@ -151,6 +154,7 @@ ok 4 - destination() with too many args fails
ok 5 - destination() with non-namespace arg throws exception
OUT
+
pir_output_is( <<'CODE', <<'OUT', 'globals' );
.sub 'test' :main
$P0 = new .Exporter
@@ -167,110 +171,84 @@ pir_output_is( <<'CODE', <<'OUT', 'globals' );
$P0.'globals'($P99)
$P1 = $P0.'globals'()
- $I0 = does $P1, 'array'
- $I99 = $P99
- $I1 = $P1
- unless $I0 == 1 goto nok_2
- unless $I1 == $I99 goto nok_2
- unless $I1 == 0 goto ok_2
- goto ok_2
- nok_2:
+ $I0 = isnull $P1
+ if $I0 goto ok_2
print 'not '
ok_2:
- say 'ok 2 - globals() with args sets globals array (empty array)'
+ say 'ok 2 - globals() with empty array arg sets PMCNULL'
$P99 = push 'Alex'
$P99 = push 'Prince'
$P0.'globals'($P99)
$P1 = $P0.'globals'()
+ $I0 = does $P1, 'hash'
$I99 = $P99
$I1 = $P1
+ unless $I0 == 1 goto nok_3
unless $I1 == $I99 goto nok_3
- unless $I1 == 2 goto nok_3
- $S0 = pop $P1
- unless $S0 == 'Prince' goto nok_3
- $S0 = pop $P1
- unless $S0 == 'Alex' goto nok_3
+ unless $I1 == 2 goto ok_3
+ $I0 = exists $P1['Prince']
+ unless $I0 goto nok_3
+ $I0 = exists $P1['Alex']
goto ok_3
nok_3:
print 'not '
ok_3:
- say 'ok 3 - globals() with args sets globals array (array with two values)'
-
+ say 'ok 3 - globals() with array arg sets globals hash (hash with two keys)'
- $P98 = clone $P99
-
- push_eh ok_4
- $P0.'globals'($P99, $P98)
- clear_eh
+ # create a hash to store globals in
+ $P99 = new .Hash
+ $P0.'globals'($P99)
+ $P1 = $P0.'globals'()
+ $I0 = isnull $P1
+ if $I0 goto ok_4
print 'not '
ok_4:
- say 'ok 4 - globals() with too many args fails'
-
-.end
-CODE
-ok 1 - globals() returns PMCNULL upon Exporter init
-ok 2 - globals() with args sets globals array (empty array)
-ok 3 - globals() with args sets globals array (array with two values)
-ok 4 - globals() with too many args fails
-OUT
+ say 'ok 4 - globals() with empty hash arg sets PMCNULL'
-pir_output_is( <<'CODE', <<'OUT', 'add_global' );
-.sub 'test' :main
- $P0 = new .Exporter
+ $P99['Prince'] = ''
+ $P99['Alex'] = ''
- $P0.'add_global'()
+ $P0.'globals'($P99)
$P1 = $P0.'globals'()
- if null $P1 goto ok_1
+ $I99 = $P99
+ $I1 = $P1
+ unless $I1 == $I99 goto nok_5
+ unless $I1 == 2 goto nok_5
+ $I0 = exists $P1['Prince']
+ unless $I0 goto nok_5
+ $I0 = exists $P1['Alex']
+ unless $I0 goto nok_5
+ goto ok_5
+ nok_5:
print 'not '
- ok_1:
- say 'ok 1 - add_global() with no args does nothing'
+ ok_5:
+ say 'ok 5 - globals() with hash arg sets globals hash (hash with two keys)'
- $P0.'add_global'('foo')
- $P1 = $P0.'globals'()
- $I0 = $P1
- unless $I0 == 1 goto nok_2
- $S0 = $P1[0]
- unless $S0 == 'foo' goto nok_2
- goto ok_2
- nok_2:
- print 'not '
- ok_2:
- say 'ok 2 - add_global() with args adds string to globals array'
- $P0.'add_global'('bar')
- $P1 = $P0.'globals'()
- $I0 = $P1
- unless $I0 == 2 goto nok_3
- $S0 = $P1[0]
- unless $S0 == 'foo' goto nok_3
- $S0 = $P1[1]
- unless $S0 == 'bar' goto nok_3
- goto ok_3
- nok_3:
- print 'not '
- ok_3:
- say 'ok 3 - add_global() with args adds string to globals array (again)'
+ $P98 = clone $P99
- push_eh ok_4
+ push_eh ok_6
$P0.'globals'($P99, $P98)
clear_eh
print 'not '
- ok_4:
- say 'ok 4 - add_global() with too many args fails'
+ ok_6:
+ say 'ok 6 - globals() with too many args fails'
.end
CODE
-ok 1 - add_global() with no args does nothing
-ok 2 - add_global() with args adds string to globals array
-ok 3 - add_global() with args adds string to globals array (again)
-ok 4 - add_global() with too many args fails
+ok 1 - globals() returns PMCNULL upon Exporter init
+ok 2 - globals() with empty array arg sets PMCNULL
+ok 3 - globals() with array arg sets globals hash (hash with two keys)
+ok 4 - globals() with empty hash arg sets PMCNULL
+ok 5 - globals() with hash arg sets globals hash (hash with two keys)
+ok 6 - globals() with too many args fails
OUT
-## TODO import
+
pir_error_output_like( <<'CODE', <<'OUT', 'import - no args' );
.sub 'test' :main
$P0 = new .Exporter
@@ -287,7 +265,7 @@ pir_output_is( <<'CODE', <<'OUT', 'import - same source and destination namespac
.sub 'test' :main
.local pmc exporter, src
- src = get_namespace
+ src = get_namespace
exporter = new .Exporter
exporter.'import'( src :named('source'), src :named('destination'), 'plan ok' :named('globals') )
@@ -309,12 +287,13 @@ CODE
ok 1
OUT
+
pir_output_is( <<'CODE', <<'OUT', 'import - globals as string' );
.sub 'test' :main
load_bytecode 'Test/More.pir'
.local pmc exporter, src
- src = get_namespace ['Test::More']
+ src = get_namespace ['Test::More']
exporter = new .Exporter
exporter.'import'( src :named('source'), 'plan ok' :named('globals') )
@@ -326,6 +305,26 @@ CODE
ok 1
OUT
+
+pir_output_is( <<'CODE', <<'OUT', 'import - globals with source passed separately' );
+.sub 'test' :main
+ load_bytecode 'Test/More.pir'
+ .local pmc exporter, src
+
+ src = get_namespace ['Test::More']
+
+ exporter = new .Exporter
+ exporter.'source'( src )
+ exporter.'import'( 'plan ok' :named('globals') )
+ plan(1)
+ ok(1)
+.end
+CODE
+1..1
+ok 1
+OUT
+
+
pir_output_is( <<'CODE', <<'OUT', 'import - globals as array' );
.sub 'test' :main
load_bytecode 'Test/More.pir'
@@ -346,6 +345,50 @@ CODE
ok 1
OUT
+
+pir_output_is( <<'CODE', <<'OUT', 'import - globals as hash - null + empty string' );
+.sub 'test' :main
+ load_bytecode 'Test/More.pir'
+ .local pmc exporter, src, globals, nul
+
+ nul = new .Null
+ src = get_namespace ['Test::More']
+ globals = new .Hash
+ globals['ok'] = nul
+ globals['plan'] = ''
+
+ exporter = new .Exporter
+ exporter.'import'( src :named('source'), globals :named('globals') )
+ plan(1)
+ ok(1)
+.end
+CODE
+1..1
+ok 1
+OUT
+
+
+pir_output_is( <<'CODE', <<'OUT', 'import - globals as hash - with dest names (latin)' );
+.sub 'test' :main
+ load_bytecode 'Test/More.pir'
+ .local pmc exporter, src, globals
+
+ src = get_namespace ['Test::More']
+ globals = new .Hash
+ globals['plan'] = 'consilium'
+ globals['ok'] = 'rectus'
+
+ exporter = new .Exporter
+ exporter.'import'( src :named('source'), globals :named('globals') )
+ consilium(1)
+ rectus(1)
+.end
+CODE
+1..1
+ok 1
+OUT
+
+
pir_output_is( <<'CODE', <<'OUT', 'import - globals with destination' );
.sub 'test' :main
load_bytecode 'Test/More.pir'
@@ -374,6 +417,10 @@ CODE
ok 1
OUT
+
+# TODO test exporting mmd subs
+
+
# Local Variables:
# mode: cperl
# cperl-indent-level: 4
Please sign in to comment.
Something went wrong with that request. Please try again.