Re: Context, wrappers, and rules

2004-12-17 Thread Leopold Toetsch
Sam Ruby wrote:
Leopold Toetsch wrote:
You'd need in pyclass.pmc:
  PMC* get_repr() {
 return  pmc_new_string( ... class ...)
  }

In this case, there is a Python specific default.  In other cases, there 
are other Python specific behaviors like mapping not found to an 
exception or to None.  In short, I don't see wrappers going away.
Yes, I already said that some methods might need either a wrapper or 
just simpler the class implementes the method.

The problem with you code is: you call again VTABLE_find_method and 
then, if a method is found, run_meth_fromc_args. This is already done by 
Parrot eventually, you are already in PyClass.__repr__. No redispatch is 
needed, just return a string.

get_repr is overloadable.  Being overloadable is the norm rather than 
the exception in Python.
Yes, of course. But that's part of the dispatch that Parrot is doing, 
not part of *each* method in PyClass or such. If get_repr was overloaded 
then Parrot runs the user code.

Again:
STRING* get_repr() {
PMC *repr = VTABLE_find_method(INTERP, SELF, REPR);
if (repr) {
PMC *temp = Parrot_run_meth_fromc_args(INTERP, repr, SELF, REPR
This part is done in the run-core. The HLLs part is to install a 
function with add_method that is found if needed.

* Parrot knows common type names (Integer, Float, String, Bool, ...) 
of the supported languages

The alternative (which is supported today) is that callers pass in a PMC 
which defines a morph method which maps common type names to language 
specific alternatives.
Well and this scheme doesn't match overloaded methods. So we'd have two 
different calling conventions, which of course would need glue code to 
make them compatible. We don't need that glue code or wrapper, if 
overloaded function syntax matches the internal C implementation.

The advantage of what exists today is that adding a new language does 
not require any changes to Parrot.  The caller defines the mapping.
Such a mapping is easily extendible. New languages aren't added 
silently, Parrot needs to know a few bits about the HLL.

* all dispatch is based on VTABLE_find_method

Just to be clear, in the cases where a wrapper is needed (which I would 
argue is the majority of cases), this works out to be: a call to 
VTABLE_find_method for the wrapper which in turn uses VTABLE_find_method 
to access the real logic.
No. VTABLE_find_method locates the very method that should be run. Being 
it a C function or a user provided subroutine. So when e.g. 
Parrot_PyInt_divide_PyInt is called, you know exactly the class (in case 
of MMD both classes). As you know that the base class is Integer, you 
can just call the baseclass function directly, if you desire so.
A user can't insert another __bases__ in core classes, so that's save.

For overloaded methods again the pair add_method/find_method is doing 
the job. If the result of find_method is a Sub PMC, the run-core runs 
the function.

Again, just to be clear: this hinges on dual assumptions: (1) that 
wrappers aren't needed in the majority of cases,
Yes. Basic mathematical functions are just the same in Python or Perl6 
or whatever.

... and (2) every time 
someone gets or sets a method, a mapping can be done from language 
defined names to Parrot conventions.
Of course. How else would you be able to overload add Px, Py, Pz if 
you can't detect that certain manipulations of attributes have that effect?

Note that in Python, all attributes may potentially be a method.
Yes. Your implementation of find_method has to cope with it. You need 
that anyway.

- Sam Ruby
leo


Re: Context, wrappers, and rules

2004-12-16 Thread Leopold Toetsch
Sam Ruby wrote:
Leopold Toetsch wrote:

Only function calls / returns may change the context. A simple opcode 
like get_repr isn't allowed to do that.

The question isn't about what opcodes are or are not allowed to do.  The 
question is what VTABLE_* functions are or are not allowed to do.  Can 
VTABLE_invoke change the context?  How about VTABLE_get_string?
Well, I think it's all answered in the two quoted lines. Cinvoke can 
change the context, but the next opcode after invoke has again the 
original context, because of the function return restores the context.

All other methods aren't allowed to change the context.
If you look at the current delegate PMC, every VTABLE call is translated 
to a call of a method.  Are such methods allowed to change the context?
The delegate vtables or MMD wrappers call a method. That returns with 
the passed in return continuation. So all the wrapped methods behave 
exactly as if an opcode would have done the job. So no.

If things invoked by runops are not allowed to change the context, then 
runops should throw an internal exception if the context changes.
Well, as already said, that would need a check for each opcode and we 
don't do that. It's like a C compiler should check the stack pointer 
after each execute hardware opcode.

On the other hand, if runops can't change the context, then why is 
runops_args careful to capture the context prior to calling runops?
Crunops is a low-level function that awaits all properly setup. Around 
that are more function that e.g. create a return continuation and do 
argument or returnvalue passing. Just use one of these functions as 
delegate.c does.

Again, what are the rules?
Simple: plain opcodes don't change the context. A function call does, 
but the return has to restore the context. Ovleroading doesn't change 
anything here.

But before you implement more and more methods I'd rather have 
inheritance and method calling conventions fixed. 

How I chose to invest my time is up to me. 
Of course. You have promised a summary ;)
The reason why I am proceeding this way is that I found the previous 
discussions on topics like object, classes, and metaclasses to be 
frustrating.  I want the next round to be based on specifics.
I was very specific. Parrot (or the runcore, the engine) does method 
dispatch (when all is fixed and adjusted).

  add Px, Py, Pz
is equivalent to a method call:
  Px = Py.__add(Pz)
The class (of Py) is queried via VTABLE_find_method() if it can do the 
add, which will eventually cause more find_method calls to find a 
matching or best suited MMD method. After the method is found, it'll be 
called, being it a C function or an overloaded piece of code.

That'll cost some cycles on the first execution of this one opcode. The 
next one on that bytecode location is running 30%-70% faster as the 
current static MMD lookup.

The signature of an overloaded function for above is
   .sub myadd
  .param left
  .param right
  ...
 .return(dest)
.end
myadd can be added via add_method() to the class (as __add) or 
Parrot picks up an existing __add in the appropriate namespace (which 
will call add_method()). A class system can provide it's own 
add_method/find_method pair or use Parrot's scheme.

The same basics holds for all overloadable operations, being it 
currently vtable or MMD methods.

 I want to be able to point at a specific wrapper and ask the question: 
how could it be done better?
You need a wrapper, if the Parrot implementation of the method for that 
class isn't matching Python semantics, e.g. to adjust return results. Or 
you just install your own method, e.g. for __repr. Almost all basic 
methematical, logical, bitwise, ... operations should just have a 
reasonable implementation in core, and all HLLs will just use that.

Parrot_PyClass_get_repr is one such wrapper.  How could it be done 
better?  If you make a specific suggestion there, I'll either adopt it 
or produce a test case as a counter example.
You'd need in pyclass.pmc:
  PMC* get_repr() {
 return  pmc_new_string( ... class ...)
  }
A method that returns the string representation of a PyClass object.
That's not a wrapper but an implementation for one specific class. No 
wrapper for other classes or a dispatch through run_meth_fromc_args is 
needed.

Nothing more will be needed, *when* all is fixed:
* we return PMCs normally (other types are optimizations)
* the methods create new PMCs - no LHS PMC is passed in
* all overloadable MMDs and vtables are registered as NCI methods
  like the current METHOD syntax in .pmc files.
* Parrot knows common type names (Integer, Float, String, Bool, ...) of 
the supported languages
* all dispatch is based on VTABLE_find_method

That's a simple scheme and should be well suited for all current target 
languages - IMHO.

- Sam Ruby
leo


Re: Context, wrappers, and rules

2004-12-16 Thread Sam Ruby
Leopold Toetsch wrote:

Parrot_PyClass_get_repr is one such wrapper.  How could it be done 
better?  If you make a specific suggestion there, I'll either adopt it 
or produce a test case as a counter example.
You'd need in pyclass.pmc:
  PMC* get_repr() {
 return  pmc_new_string( ... class ...)
  }
Just to be clarify, this is from the header comment in pyclass.pmc:
These are the vtable functions for the Python Class base class (i.e.,
methods you would expect to see on python objects).
For reference, here is the current definition of get_repr.
%%%
/*
=item CSTRING *get_repr()
Return the representation of this object.
=cut
*/
STRING* get_repr() {
PMC *repr = VTABLE_find_method(INTERP, SELF, REPR);
if (repr) {
PMC *temp = Parrot_run_meth_fromc_args(INTERP, repr, SELF, 
REPR,
PP, SELF);
return VTABLE_get_string(INTERP, temp);
}
else {
STRING *res;

res = string_from_cstring(INTERP, , 0);
res = string_append(INTERP, res,
VTABLE_name(INTERP, VTABLE_get_class(INTERP, SELF)), 0);
res = string_append(INTERP, res,
const_string(INTERP,  instance at ), 0);
res = string_append(INTERP, res,
Parrot_sprintf_c(INTERP, %#x, (INTVAL) SELF), 0);
res = string_append(INTERP, res,
const_string(INTERP, ), 0);
return res;
}
}
%%%
In this case, there is a Python specific default.  In other cases, there 
are other Python specific behaviors like mapping not found to an 
exception or to None.  In short, I don't see wrappers going away.

A method that returns the string representation of a PyClass object.
That's not a wrapper but an implementation for one specific class. No 
wrapper for other classes or a dispatch through run_meth_fromc_args is 
needed.

Nothing more will be needed, *when* all is fixed:
* we return PMCs normally (other types are optimizations)
* the methods create new PMCs - no LHS PMC is passed in
* all overloadable MMDs and vtables are registered as NCI methods
  like the current METHOD syntax in .pmc files.
get_repr is overloadable.  Being overloadable is the norm rather than 
the exception in Python.

* Parrot knows common type names (Integer, Float, String, Bool, ...) of 
the supported languages
The alternative (which is supported today) is that callers pass in a PMC 
which defines a morph method which maps common type names to language 
specific alternatives.  Take a look at PyObject.pmc where I substitute 
PyLong for BigInt.

The advantage of what exists today is that adding a new language does 
not require any changes to Parrot.  The caller defines the mapping.

* all dispatch is based on VTABLE_find_method
Just to be clear, in the cases where a wrapper is needed (which I would 
argue is the majority of cases), this works out to be: a call to 
VTABLE_find_method for the wrapper which in turn uses VTABLE_find_method 
to access the real logic.

That's a simple scheme and should be well suited for all current target 
languages - IMHO.
Again, just to be clear: this hinges on dual assumptions: (1) that 
wrappers aren't needed in the majority of cases, and (2) every time 
someone gets or sets a method, a mapping can be done from language 
defined names to Parrot conventions.

Note that in Python, all attributes may potentially be a method.
- Sam Ruby


Re: Context, wrappers, and rules

2004-12-16 Thread Sam Ruby
Leopold Toetsch wrote:
On the other hand, if runops can't change the context, then why is 
runops_args careful to capture the context prior to calling runops?
Crunops is a low-level function that awaits all properly setup. Around 
that are more function that e.g. create a return continuation and do 
argument or returnvalue passing. Just use one of these functions as 
delegate.c does.
I *am* using Parrot_run_meth_fromc_args, just like delegate.c does.
Single stepping through the debugger, I find that interpreter-ctx.bp is 
changed by Parrot_Sub_invoke, line 299.  The value is not changed after 
that point, and still retains this new value when control returns to 
Parrot_get_repr_s_p.

- Sam Ruby


Context, wrappers, and rules (was: cvs commit: parrot/ops pmc.ops)

2004-12-16 Thread Sam Ruby
Leopold Toetsch wrote:
Sam Ruby wrote:
Before this line is executed,
  (gdb) p interpreter-ctx.bp
  $1 = (struct parrot_regs_t *) 0x40b6bd88
After the above line is executed:
  (gdb) p interpreter-ctx.bp
  $2 = (struct parrot_regs_t *) 0x40b6bae8
Then is obviously your implementation of get_repr broken.
Two questions come to mind:
1) Should this call to run_meth change the context? 
Yes because a subroutine is run. But returning from it via a (return-) 
continuation restores the context and the register frame pointer or it 
ought to.

... In this case, repr happens to be a Closure based on some 
assumptions I made early on in this process that I need to revisit.  
Arguably, this is a bug, but in the general case, can one always 
assume that the context does not change?
Only function calls / returns may change the context. A simple opcode 
like get_repr isn't allowed to do that.
The question isn't about what opcodes are or are not allowed to do.  The 
question is what VTABLE_* functions are or are not allowed to do.  Can 
VTABLE_invoke change the context?  How about VTABLE_get_string?

If you look at the current delegate PMC, every VTABLE call is translated 
to a call of a method.  Are such methods allowed to change the context?

What are the rules?
Note: if the context is not supposed to change as a result of runops, 
perhaps an internal exception should be thrown.
We can't check that on each opcode. That just must not happen :)
I mustn't have been clear.
If things invoked by runops are not allowed to change the context, then 
runops should throw an internal exception if the context changes.

On the other hand, if runops can't change the context, then why is 
runops_args careful to capture the context prior to calling runops?

Again, what are the rules?
But before you implement more and more methods I'd rather have 
inheritance and method calling conventions fixed. Parrot should call the 
approriate method if it exists in the class namespace (if find_method 
did return something).
How I chose to invest my time is up to me.  If all that comes of it is 
the few minor contributions I have made to the core of Parrot... then I 
will be OK with that.

In most cases, I think I could adapt to a change in a matter of minutes. 
   You've seen me clone classes like BigInt and Complex wholesale, only 
to gut them back once I had some partial ability to inherit MMD methods.

In the worst case, adapting to a major change would be a matter of days. 
 Again, my time to burn.  Also, if you note, I am only writing the 
minimum necessary to pass the given set of tests.  That way there is 
less for me to change when things change.

The reason why I am proceeding this way is that I found the previous 
discussions on topics like object, classes, and metaclasses to be 
frustrating.  I want the next round to be based on specifics.

In particular, I don't want to have to respond to statements like It 
doesn't buy us anything, if we force all languages to create wrappers.. 
 I want to be able to point at a specific wrapper and ask the question: 
how could it be done better?

Parrot_PyClass_get_repr is one such wrapper.  How could it be done 
better?  If you make a specific suggestion there, I'll either adopt it 
or produce a test case as a counter example.

- Sam Ruby