Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH] to support mere pmcs as keys

7 views
Skip to first unread message

Stéphane Payrard

unread,
Feb 9, 2004, 2:21:26 PM2/9/04
to perl6-i...@perl.org, Leopold Toetsch, Dan Sugalski

The implementation of the methods key_* in keys.c imposed
to the PMCs to be of type Key. I don't' see the interest
for atomic keys that could be mere PMCs.

This concretely means that one can write the following and save a
intermediate register:

P3 = PO[P1]

instead of:

P3 = new P2, .Key
P2 = P1
P3 = PO[P1]

or instead of:

S0 = P1
P3 = P0[S0] # if the key is used as a string


Patch includes code and test.
Affects src/key.c and t/pmc/key.t.

--
stef

--- src/key.c.old 2004-01-27 01:01:16.000000000 +0100
+++ src/key.c 2004-02-09 18:11:47.000000000 +0100
@@ -283,21 +283,27 @@
key_integer(struct Parrot_Interp *interpreter, PMC *key)
{
PMC *reg;
+ int flags = PObj_get_FLAGS(key);
+ int key_flags = flags & KEY_type_FLAGS;

- switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
- case KEY_integer_FLAG:
- return key->cache.int_val;
- case KEY_integer_FLAG | KEY_register_FLAG:
- return interpreter->int_reg.registers[key->cache.int_val];
- case KEY_pmc_FLAG:
- return VTABLE_get_integer(interpreter,
- key->cache.pmc_val);
- case KEY_pmc_FLAG | KEY_register_FLAG:
- reg = interpreter->pmc_reg.registers[key->cache.int_val];
- return VTABLE_get_integer(interpreter, reg);
- default:
- internal_exception(INVALID_OPERATION, "Key not an integer!\n");
- return 0;
+ if(key_flags) {
+ switch (key_flags) {
+ case KEY_integer_FLAG:
+ return key->cache.int_val;
+ case KEY_integer_FLAG | KEY_register_FLAG:
+ return interpreter->int_reg.registers[key->cache.int_val];
+ case KEY_pmc_FLAG:
+ return VTABLE_get_integer(interpreter,
+ key->cache.pmc_val);
+ case KEY_pmc_FLAG | KEY_register_FLAG:
+ reg = interpreter->pmc_reg.registers[key->cache.int_val];
+ return VTABLE_get_integer(interpreter, reg);
+ default:
+ internal_exception(INVALID_OPERATION, "Key not an integer!\n");
+ return 0;
+ }
+ } else {
+ return VTABLE_get_integer(interpreter, key);
}
}

@@ -313,22 +319,29 @@
FLOATVAL
key_number(struct Parrot_Interp *interpreter, PMC *key)
{
+
PMC *reg;
+ int flags = PObj_get_FLAGS(key);
+ int key_flags = flags & KEY_type_FLAGS;

- switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
- case KEY_number_FLAG:
- return key->cache.num_val;
- case KEY_number_FLAG | KEY_register_FLAG:
- return interpreter->num_reg.registers[key->cache.int_val];
- case KEY_pmc_FLAG:
- return VTABLE_get_number(interpreter,
- key->cache.pmc_val);
- case KEY_pmc_FLAG | KEY_register_FLAG:
- reg = interpreter->pmc_reg.registers[key->cache.int_val];
- return VTABLE_get_number(interpreter, reg);
- default:
- internal_exception(INVALID_OPERATION, "Key not a number!\n");
- return 0;
+ if (key_flags) {
+ switch (key_flags) {
+ case KEY_number_FLAG:
+ return key->cache.num_val;
+ case KEY_number_FLAG | KEY_register_FLAG:
+ return interpreter->num_reg.registers[key->cache.int_val];
+ case KEY_pmc_FLAG:
+ return VTABLE_get_number(interpreter,
+ key->cache.pmc_val);
+ case KEY_pmc_FLAG | KEY_register_FLAG:
+ reg = interpreter->pmc_reg.registers[key->cache.int_val];
+ return VTABLE_get_number(interpreter, reg);
+ default:
+ internal_exception(INVALID_OPERATION, "Key not a number!\n");
+ return 0;
+ }
+ } else {
+ return VTABLE_get_number(interpreter, key);
}
}

@@ -345,21 +358,29 @@
key_string(struct Parrot_Interp *interpreter, PMC *key)
{
PMC *reg;
+ int flags = PObj_get_FLAGS(key);
+ int key_flags = flags & KEY_type_FLAGS;

- switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
- case KEY_string_FLAG:
- return key->cache.string_val;
- case KEY_string_FLAG | KEY_register_FLAG:
- return interpreter->string_reg.registers[key->cache.int_val];
- case KEY_pmc_FLAG:
- return VTABLE_get_string(interpreter,
- key->cache.pmc_val);
- case KEY_pmc_FLAG | KEY_register_FLAG:
- reg = interpreter->pmc_reg.registers[key->cache.int_val];
- return VTABLE_get_string(interpreter, reg);
- default:
- internal_exception(INVALID_OPERATION, "Key not a string!\n");
- return 0;
+ if (key_flags) {
+ switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
+ case KEY_integer_FLAG:
+ string_from_int(interpreter, key->cache.int_val);
+ case KEY_string_FLAG:
+ return key->cache.string_val;
+ case KEY_string_FLAG | KEY_register_FLAG:
+ return interpreter->string_reg.registers[key->cache.int_val];
+ case KEY_pmc_FLAG:
+ return VTABLE_get_string(interpreter,
+ key->cache.pmc_val);
+ case KEY_pmc_FLAG | KEY_register_FLAG:
+ reg = interpreter->pmc_reg.registers[key->cache.int_val];
+ return VTABLE_get_string(interpreter, reg);
+ default:
+ internal_exception(INVALID_OPERATION, "Key not a string!\n");
+ return 0;
+ }
+ } else {
+ return VTABLE_get_string(interpreter, key);
}
}

@@ -403,7 +424,10 @@
PMC *
key_next(struct Parrot_Interp *interpreter, PMC *key)
{
- return PMC_data(key);
+ if (PObj_get_FLAGS(key) & KEY_type_FLAGS)
+ return PMC_data(key);
+ return (PMC*) NULL;
+
}

/*

--- t/pmc/key.t.old 2004-02-09 19:41:22.000000000 +0100
+++ t/pmc/key.t 2004-02-09 20:06:41.000000000 +0100
@@ -1,6 +1,6 @@
#! perl -w

-use Parrot::Test tests => 2;
+use Parrot::Test tests => 3;
use Test::More;

output_is(<<'CODE', <<'OUT', 'traverse key chain');
@@ -64,3 +64,58 @@
ok 1
123
OUT
+
+my $qr = "0\n0.00*\n\n\n" x 3;
+$qr = qr|$qr|;
+
+output_like(<<'CODE', $qr, 'mere PMCs used as keys');
+ new P0, .Array
+ set P0, 1
+
+ new P1, .PerlInt
+ set P1, 0
+ set I2, P0[P1]
+ print I2
+ print "\n"
+ set N2, P0[P1]
+ print N2
+ print "\n"
+ set S2, P0[P1]
+ print S2
+ print "\n"
+ set P2, P0[P1]
+ print P2
+ print "\n"
+
+ new P1, .PerlString
+ set P1, "0"
+ set I2, P0[P1]
+ print I2
+ print "\n"
+ set N2, P0[P1]
+ print N2
+ print "\n"
+ set S2, P0[P1]
+ print S2
+ print "\n"
+ set P2, P0[P1]
+ print P2
+ print "\n"
+
+ new P1, .PerlNum
+ set P1, 0.0
+ set I2, P0[P1]
+ print I2
+ print "\n"
+ set N2, P0[P1]
+ print N2
+ print "\n"
+ set S2, P0[P1]
+ print S2
+ print "\n"
+ set P2, P0[P1]
+ print P2
+ print "\n"
+ end
+CODE
+

Stéphane Payrard

unread,
Feb 9, 2004, 3:48:09 PM2/9/04
to perl6-i...@perl.org, Leopold Toetsch, Dan Sugalski
The get_*_keyed methods were missing from PerlArray. Inheriting from Array did not
cut it when accessing elements beyond the array end.

The patch adds the missing methods. They really are a cut and paste
from the Array methods. They access the corresponding get_*_keyed_int() methods,
or the get_pmc_keyed() methods for the next part of a composite key.

--
stef


--- classes/perlarray.pmc.old 2004-02-09 13:57:19.000000000 +0100
+++ classes/perlarray.pmc 2004-02-09 18:16:14.000000000 +0100
@@ -110,6 +110,20 @@
return SUPER(key);
}

+ INTVAL get_integer_keyed (PMC* key) {
+ INTVAL ix;
+ PMC* nextkey;
+ PMC* box;
+
+ if (!key) return 0;
+ ix = key_integer(INTERP, key);
+ nextkey = key_next(INTERP, key);
+ if (nextkey == NULL) return SELF.get_integer_keyed_int(ix);
+ box = SELF.get_pmc_keyed_int(ix);
+ if (box == NULL) box = undef(INTERP);
+ return VTABLE_get_integer_keyed(INTERP, box, nextkey);
+ }
+
FLOATVAL get_number_keyed_int (INTVAL key) {
if (key >= DYNSELF.elements() || key < -DYNSELF.elements()) {
if (PARROT_WARNINGS_test(interpreter,
@@ -123,6 +137,22 @@
return SUPER(key);
}

+ FLOATVAL get_number_keyed (PMC* key) {
+ INTVAL ix;
+ PMC* nextkey;
+ PMC* box;
+
+ if (!key) return 0;
+ ix = key_integer(INTERP, key);
+ nextkey = key_next(INTERP, key);
+ if (nextkey == NULL) return SELF.get_number_keyed_int(ix);
+
+ box = SELF.get_pmc_keyed_int(ix);
+ if (box == NULL) box = undef(INTERP);
+ return VTABLE_get_number_keyed(INTERP, box, nextkey);
+ }
+
+
STRING* get_string () {
return string_from_int(INTERP, DYNSELF.elements());
}
@@ -140,6 +170,22 @@
return SUPER(key);
}

+ STRING* get_string_keyed (PMC* key) {
+ INTVAL ix;
+ PMC* nextkey;
+ PMC* box;
+
+ if (!key) return 0;
+ ix = key_integer(INTERP, key);
+ nextkey = key_next(INTERP, key);
+ if (nextkey == NULL) return SELF.get_string_keyed_int(ix);
+
+ box = SELF.get_pmc_keyed_int(ix);
+ if (box == NULL) box = undef(INTERP);
+ return VTABLE_get_string_keyed(INTERP, box, nextkey);
+ }
+
+
PMC* get_pmc_keyed_int (INTVAL key) {
if (key >= DYNSELF.elements() || key < -DYNSELF.elements()) {
if (PARROT_WARNINGS_test(interpreter,
@@ -153,4 +199,20 @@
return SUPER(key);
}

+ PMC* get_pmc_keyed (PMC* key) {
+ INTVAL ix;
+ PMC* nextkey;
+ PMC* box;
+
+ if (!key) return 0;
+ ix = key_integer(INTERP, key);
+ nextkey = key_next(INTERP, key);
+ if (nextkey == NULL) return SELF.get_pmc_keyed_int(ix);
+
+ box = SELF.get_pmc_keyed_int(ix);
+ if (box == NULL) box = undef(INTERP);
+ return VTABLE_get_pmc_keyed(INTERP, box, nextkey);
+ }
+
+
}

diff -u t/pmc/perlarray.t.old t/pmc/perlarray.t
--- t/pmc/perlarray.t.old 2004-02-09 20:59:15.000000000 +0100
+++ t/pmc/perlarray.t 2004-02-09 21:46:23.000000000 +0100


@@ -1,6 +1,6 @@
#! perl -w

-use Parrot::Test tests => 26;
+use Parrot::Test tests => 27;
use Test::More;

my $fp_equality_macro = <<'ENDOFMACRO';
@@ -1376,5 +1376,33 @@
/
OUTPUT

+my $qr = "0\n0.00*\n\n";
+$qr=qr|$qr|;
+
+
+output_like(<<'CODE', $qr, "Fetching beyond the end of an array");
+# with pmcs, exercise the get_*_int() as well
+ new P0, .PerlArray
+ new P1, .Key


+ set P1, 0
+

+ set I1, P0[P1]
+ print I1
+ print "\n"
+
+ set N1, P0[P1]
+ print N1
+ print "\n"
+
+ set S1, P0[P1]
+ print S1
+ print "\n"
+
+ set P1, P0[P1]
+ print P1


+ print "\n"
+ end
+CODE
+

1;

Leopold Toetsch

unread,
Feb 9, 2004, 3:52:28 PM2/9/04
to st...@payrard.net, perl6-i...@perl.org
Stéphane Payrard <st...@payrard.net> wrote:

> The implementation of the methods key_* in keys.c imposed
> to the PMCs to be of type Key. I don't' see the interest
> for atomic keys that could be mere PMCs.

> This concretely means that one can write the following and save a
> intermediate register:

> P3 = PO[P1]

> instead of:

> P3 = new P2, .Key
> P2 = P1
> P3 = PO[P1]

This doesn't parse ;) You mean something like:

P2 = new Key
assign P2, P1
P3 = P0[P2]

or for short

I0 = P1
P3 = P0[I0]

> or instead of:

> S0 = P1
> P3 = P0[S0] # if the key is used as a string

So both would be:

P3 = PO[P1]

And that's the problem. An INT or STRING key has a definite type. A
plain (scalar) PMC doesn't have a type per se. You can extract a string
or and int out of a PerlInt or a PerlString and vv.

So the usage of the key isn't always clear. Sometimes the aggregate
might know, that it only takes INT keys (like Array). But what about
e.g. OrderedHash that takes both kind of keys and does different
things, depending on the key type? Or a Hash that takes int keys?

> Patch includes code and test.

Doesn't look bad though. But these kind of changes needs probably still
more thoughts and tests.

OTOH, we could use this idea, if we set the KEY_<type>_FLAG on
asingment to the PMC:

new P1, PerlUndef
P1 = 1 # has now KEY_integer_FLAG *and* is usable as a key for
P3 = P0[P1]

or

new P1, PerlUndef
P1 = "key" # has now KEY_string_FLAG *and* is usable as a key for
P3 = P0[P1] # P0 ias a hash-like thingy
new P1, PerlUndef

and additionally:

P1 = "42"
set_key_type P1, .Key_integer_FLAG
P3 = P0[P1] # array access with integer key 42
inc P1 # "43" ... still a string (BTW unimplemented inc ;)

This would need a little change (and slow down[1]) for key_next. But it
would make plain scalar PMCs as usable as keys as your patch does -
AFAIK for now :)

Your patch would slow down keyed access little, while above is more
general and with some extra cycles for setting the flag.

I can imagine, that this flags could also help Ponie to implement Sv?Ok
flags.

Comments?

leo

[1] check for non-NULL pmc->pmc_ext

Stéphane Payrard

unread,
Feb 9, 2004, 4:55:40 PM2/9/04
to perl6-i...@perl.org
Le Mon, Feb 09, 2004 at 09:52:28PM +0100, le valeureux mongueur Leopold Toetsch a dit:

> Stéphane Payrard <st...@payrard.net> wrote:
>
> > The implementation of the methods key_* in keys.c imposed
> > to the PMCs to be of type Key. I don't' see the interest
> > for atomic keys that could be mere PMCs.
>
> > This concretely means that one can write the following and save a
> > intermediate register:
>
> > P3 = PO[P1]
>
> > instead of:
>
> > P3 = new P2, .Key
> > P2 = P1
> > P3 = PO[P1]
>
> This doesn't parse ;) You mean something like:
>
> P2 = new Key
> assign P2, P1
> P3 = P0[P2]

Yes, sorry, I have trouble (among other things) to go from IMC syntax
and back to PASM. One of my points is that one should not create a key or
do an explicit intermediate operation if he wants to use a regular pmc
as a key.

>
> or for short
>
> I0 = P1
> P3 = P0[I0]
>
> > or instead of:
>
> > S0 = P1
> > P3 = P0[S0] # if the key is used as a string
>
> So both would be:
>
> P3 = PO[P1]
>
> And that's the problem. An INT or STRING key has a definite type. A
> plain (scalar) PMC doesn't have a type per se. You can extract a string
> or and int out of a PerlInt or a PerlString and vv.
>
> So the usage of the key isn't always clear. Sometimes the aggregate
> might know, that it only takes INT keys (like Array). But what about
> e.g. OrderedHash that takes both kind of keys and does different
> things, depending on the key type? Or a Hash that takes int keys?

It is up to the accessed pmc to decide what to do with the subscript.
On the other hand, in some cases, it does need some extra information.
Because the type of the pmc or of its value may not help.
Example : two-way hashes (supposing that keys and values are one to
one).

In keeping with the spirit of Perl, I propose setting the flag in
the subscript pmc in the access operation by adding some syntax to
imcc:

P3 = PO[P1] # regular keyed access without flag

# set a flag in P1 before doing the equivalent of C<P3 = PO[P1]>
P3 = P0{P1}

The C<get_*_key()> would test the flag to do the proper operation.

To compare it with your proposition below: even if the flag lies in
the subscript pmc, I think it is more a property of the operation than
of the subscript pmc. It does not specify the type of a value. That's
why I want to bundle the flag setting with the access operation.
Anyway, a flag avoids to create a new set of keyed methods.

My scheme needs new syntax to specify to the accessed pmc what is the
required operation, but it is familiar to Perl programmers and avoids
to learn yet another assembler statement, the explicit setting of the
flag. I agree that the explicit flag setting is still a progress
because it does not need a extra register (like the creation of an
explicit Key did).


>
> > Patch includes code and test.
>
> Doesn't look bad though. But these kind of changes needs probably still
> more thoughts and tests.
>
> OTOH, we could use this idea, if we set the KEY_<type>_FLAG on
> asingment to the PMC:
>
> new P1, PerlUndef
> P1 = 1 # has now KEY_integer_FLAG *and* is usable as a key for
> P3 = P0[P1]
>
> or
>
> new P1, PerlUndef
> P1 = "key" # has now KEY_string_FLAG *and* is usable as a key for
> P3 = P0[P1] # P0 ias a hash-like thingy
> new P1, PerlUndef
>
> and additionally:
>
> P1 = "42"
> set_key_type P1, .Key_integer_FLAG
> P3 = P0[P1] # array access with integer key 42
> inc P1 # "43" ... still a string (BTW unimplemented inc ;)
>
> This would need a little change (and slow down[1]) for key_next. But it
> would make plain scalar PMCs as usable as keys as your patch does -
> AFAIK for now :)
>
> Your patch would slow down keyed access little, while above is more
> general and with some extra cycles for setting the flag.

Anyway, with the flag (your scheme or mine), we save a register
which is a very scarce ressource.

>
> I can imagine, that this flags could also help Ponie to implement Sv?Ok
> flags.
>
> Comments?
>
> leo
>
> [1] check for non-NULL pmc->pmc_ext

--
stef

Leopold Toetsch

unread,
Feb 9, 2004, 6:53:43 PM2/9/04
to st...@payrard.net, perl6-i...@perl.org
Hi Stef,

Stéphane Payrard <st...@payrard.net> wrote:

> Le Mon, Feb 09, 2004 at 09:52:28PM +0100, le valeureux mongueur
> Leopold Toetsch a dit:

Trop long d'introduction. Pas de valeur.

[ precise summary snipped ]

> P3 = PO[P1] # regular keyed access without flag

> # set a flag in P1 before doing the equivalent of C<P3 = PO[P1]>
> P3 = P0{P1}

It was already discussed (a long time ago, when current Key PMC went
into CVS) that we might need a different syntax do differenciate
in an instruction like:

P3 = Phash_like[Pkey] # Phash_like is an aggregate that does both ...

between integer or string keyed access.

> The C<get_*_key()> would test the flag to do the proper operation.

> To compare it with your proposition below: even if the flag lies in
> the subscript pmc, I think it is more a property of the operation than
> of the subscript pmc. It does not specify the type of a value. That's
> why I want to bundle the flag setting with the access operation.
> Anyway, a flag avoids to create a new set of keyed methods.

I think that concept isnn't generally suitable for compilers. Perl6 has
%P0[$key} and @P0[$key]. This syntax imposes some type restriction (or
coercion) on the key and defines the operation.
But with Perl5 syntax you are lost: The compiler wouldn't know, what to
emit.

There are of course more complicated examples:

P3 = P0[Pa; Pb; Pc] # intermdiate aggregates have to know ...

[ more compiler writers opinions needed ]

leo

Stéphane Payrard

unread,
Feb 10, 2004, 2:26:09 PM2/10/04
to perl6-i...@perl.org, perl6-l...@perl.org, Leopold Toetsch
On Tue, Feb 10, 2004 at 12:53:43AM +0100, Leopold Toetsch wrote:
> Hi Stef,

Hi Leopold,

I cross-post to perl6-internals because I am not sure about the
exact semantic of hash and array access in Perl6.

>
> Stéphane Payrard <st...@payrard.net> wrote:
>
> > Le Mon, Feb 09, 2004 at 09:52:28PM +0100, le valeureux mongueur
> > Leopold Toetsch a dit:
>
> Trop long d'introduction. Pas de valeur.
>
> [ precise summary snipped ]
>
> > P3 = PO[P1] # regular keyed access without flag
>
> > # set a flag in P1 before doing the equivalent of C<P3 = PO[P1]>
> > P3 = P0{P1}
>
> It was already discussed (a long time ago, when current Key PMC went
> into CVS) that we might need a different syntax do differenciate
> in an instruction like:
>
> P3 = Phash_like[Pkey] # Phash_like is an aggregate that does both ...
>
> between integer or string keyed access.
>
> > The C<get_*_key()> would test the flag to do the proper operation.
>
> > To compare it with your proposition below: even if the flag lies in
> > the subscript pmc, I think it is more a property of the operation than
> > of the subscript pmc. It does not specify the type of a value. That's
> > why I want to bundle the flag setting with the access operation.
> > Anyway, a flag avoids to create a new set of keyed methods.
>
> I think that concept isnn't generally suitable for compilers. Perl6 has
> %P0[$key} and @P0[$key]. This syntax imposes some type restriction (or
> coercion) on the key and defines the operation.

I am not sure that Perl6 syntax imposes any type restriction on keys.
If so, it would probably invalidate my example of two-way hash accessed either
by C<$value = @a{$key> or C<$key = @a[$value]> with $key and $value
being non Key pmcs.

The two notations of "hash access" and "array access" or the
explicit setting of a flag denote two ways of accessing a pmc.
The possible types of the keys and the semantic of the access
operation is up to the pmc. And in a sense this is not new
because whatever semantic can be implemented with ties in Perl5.
In most cases but not all, the array (resp. hash ) access will
involve a subscript type that will be coerced to an integer
(resp. string).

About the flags used: they cannot be KEY_integer_FLAG and
KEY_string_FLAG because they are private flags that may have
other meanings in not Key pmcs. But PObj_access_as_hash (or
PObj_acts_as_String) would do. When zero the access is an
"array" access.

About the overhead of the added C code: if we choose to support
indexed access thru subscript pmcs that are not of type Key, we
cannot avoid it. But explicitly creating Key pmcs, or converting
pmcs to string or int for the sake of a keyed access is an
overhead too. So I don't see why supporting not key pmc would be
so much a problem.


> But with Perl5 syntax you are lost: The compiler wouldn't know, what to
> emit.
>
> There are of course more complicated examples:
>
> P3 = P0[Pa; Pb; Pc] # intermdiate aggregates have to know ...

In the case of composite keys, all the component but the last must
be keys. Indeed the flag should be set in each atomic component.

>
> [ more compiler writers opinions needed ]

>
> leo

--
stef

0 new messages