$S0 = obj.'_meth'(arg1, arg2)
but this also works:
.pcc_begin
.arg "hello"
.arg "\n"
.invocant obj
.meth_call "_meth"
.result $S0
.pcc_end
There's a similar construct for return values.
leo
The basic problem is inconsistency. For hand-written code the current
PIR method call syntactic sugar is mildly annoying. (It'd be nice to
safely get rid of the quotes around the method name.) For generated code
it's a completely different syntax tree node. Opcode syntax like so:
opcode $S0, obj, arg1, arg2
can be represented with a simple parent node with a sequence of child nodes.
The syntax tree for methods (whether you use the syntactic sugar or the
long form) is more like a parent node (a "method"), with one invocant
child and two composite children for the argument list and return list,
respectively. To generate the PIR, you need unroll the composite
children in a way that's contextually dependent on other nodes further
up the tree.
And when some common language constructs are opcodes and some are method
calls, the burden of deciding which kind of syntax a particular
construct should use falls to the compiler writer. There are various
ways to implement it: a lookup table, a chunk of hard-coded PIR, etc.
But they all boil down to added complexity.
This isn't to say that method calls are "bad", but they are more complex
to work with. You want more features to be implemented only as methods
and to eliminate the opcodes. But there is real value in keeping the
opcode syntax for the common cases.
Allison
> The basic problem is inconsistency. For hand-written code the current
> PIR method call syntactic sugar is mildly annoying. (It'd be nice to
> safely get rid of the quotes around the method name.)
Not easily:
obj.'foo'() # a methodname constant
.local string bar
bar = get_some_meth() # or bar = 'get_some_meth'()
obj.bar() # a method variable
But:
obj.foo() # still a methodname constant
# unless there's a variable 'foo'
To be on the safe side, method (and function) names *should* be quoted. I
don't think that this is inconsistent.
> And when some common language constructs are opcodes and some are method
> calls, the burden of deciding which kind of syntax a particular
> construct should use falls to the compiler writer. There are various
> ways to implement it: a lookup table, a chunk of hard-coded PIR, etc.
> But they all boil down to added complexity.
Well, when I write x86 or ppc JIT code, the burden of deciding, which syntax
to use falls on the compiler write that is me. While we could provide an
introspection API to available opcodes (and args) it still doesn't help much.
The compiler (writer) has to know, what a particular opcode is doing.
When I write code which calls some library function, I've to lookup the docu
and check the function name and the argument it takes. And I've to decide, if
I have an opcode available and use it, or if I've to call a library function.
This is just the usual job when creating a compiler. Introspection would have
little benefit IMHO:
interp = getinterp
info = interp.get_op_info() # hypothetical
$I0 = exists info['gcd'] # do we have a 'gcd' opcode
cl = getclass 'Integer'
$I0 = can cl, 'gcd'
While a compiler could use introspection or tables or whatever, these still
wouldn't help to figure out the semantics of a particular operation.
> Allison
leo
Is there a reason that you would want to conflate method names and
variables used as a method name? If not, why not change the syntax
slightly so that method names in a variable are uniquely identified?
Here's a suggestion:
obj.foo() # a methodname constant
.local string bar
bar = get_some_meth()
obj.$bar() # a method variable
The same could be used for ordinary subroutines:
.local string mysub
mysub = "foo"
$mysub() # calls the foo subroutine
-Scott
--
Jonathan Scott Duff <du...@pobox.com>
Exactly, change the most common case (of a method call by bare name) to
be the unmarked case, and use some additional marking on the less common
case of calling a method by a string name or method object. I wouldn't
use '$' to mark the string lookup because it's too confusing with the
temporary register variables ($S0, etc). But some other syntactic sugar
would work. This is clumsy, but then, it's also rare.
obj.{bar}() # a string method name
obj.{$S1}()
Allison
> Exactly, change the most common case (of a method call by bare name) to
> be the unmarked case, and use some additional marking on the less common
> case of calling a method by a string name or method object. I wouldn't
> use '$' to mark the string lookup because it's too confusing with the
> temporary register variables ($S0, etc). But some other syntactic sugar
> would work. This is clumsy, but then, it's also rare.
>
> obj.{bar}() # a string method name
> obj.{$S1}()
That's not bad; it reminds me a bit of Perl and Tcl.
To push a little more the other direction, is it possible for the compiler to
detect symbol and method name conflicts? It's only the collision that makes
a case ambiguous, right?
-- c
I'm not sure what's meant by "a string method name" above, but
I'd look at it as:
.local string abc
obj.'abc'() # call 'abc' method of obj
obj.abc() # always the same as above
obj.{abc}() # call method indicated by abc symbol
obj.{S0}() # call method indicated by S0
obj.$S0() # call method indicated by $S0
Having obj.abc() always mean obj.'abc'() seems to me like it's
most in line with what PIR-authors expect.
As noted in the last instance, I don't know that we need a
separate obj.{$S0}() case since the dollar sign is sufficient
to distinguish exactly what was meant. But there's also an
argument in favor of the consistency of {...} always meaning
"evaluate this" as opposed to "treat this as literal" given
by the bareword and 'abc' forms.
> To push a little more the other direction, is it possible for the
> compiler to detect symbol and method name conflicts?
> It's only the collision that makes a case ambiguous, right?
I don't think that the compiler always knows at compile time
what method names are available for any given object, so detecting
"collisions" could be problematic.
However, it could certainly detect when a bareword symbol has
been used as a method name and warn about it, requiring the use of an
explicit obj.{symbol}() or obj.'symbol'() form to disambiguate it.
Pm
> > To push a little more the other direction, is it possible for the
> > compiler to detect symbol and method name conflicts?
> > It's only the collision that makes a case ambiguous, right?
> I don't think that the compiler always knows at compile time
> what method names are available for any given object, so detecting
> "collisions" could be problematic.
Oh no, of course not.
> However, it could certainly detect when a bareword symbol has
> been used as a method name and warn about it, requiring the use of an
> explicit obj.{symbol}() or obj.'symbol'() form to disambiguate it.
Yes, that is what I meant. If there's a symbol in the current compilation
unit with the same name as a bareword method call, require disambiguation.
Otherwise, assume it's a bareword method call.
-- c
I'm just quoting the relevant pieces here and add some comments below:
> obj.'abc'() # call 'abc' method of obj
> obj.abc() # always the same as above
> obj.{abc}() # call method indicated by abc symbol
This makes a lot of sense, and there are simple rules for the syntax:
* use {symbol}, if the thing isa symbol
* use 'name', if the 'name' contains non-identifier characters
(of if unsure, of if you are a compiler ;-)
* else you also might use bare word syntax
That should be it to deal with all that:
obj.S0() # emit warning but call 'S0' method
Rational: if bare <abc> isa identifier here, then C<S0> too.
obj.$S0() # illegal
leo
You got it, I meant "a method name stored in a string variable/register".
> .local string abc
>
> obj.'abc'() # call 'abc' method of obj
> obj.abc() # always the same as above
> obj.{abc}() # call method indicated by abc symbol
> obj.{S0}() # call method indicated by S0
> obj.$S0() # call method indicated by $S0
>
> Having obj.abc() always mean obj.'abc'() seems to me like it's
> most in line with what PIR-authors expect.
Yup.
Allison
The latter two are the same rules as the rules for declaring
methods/subs, which makes for nice consistency.
> That should be it to deal with all that:
>
> obj.S0() # emit warning but call 'S0' method
>
> Rational: if bare <abc> isa identifier here, then C<S0> too.
I can see the value of this warning for the transition, but long-term we
really don't want the code to be littered with warnings for deprecated
features. It'd be pretty simple to write a script that combs the .pir
files in the repository for any instance of a method name that looks
like a register name.
> obj.$S0() # illegal
Is '$' a valid identifier character? If so, then it's legal, but just
treated as part of the string name of the method. If not (which seems
more likely), then it's illegal anyway, so no special case is needed.
Allison
Yep.
> > That should be it to deal with all that:
> >
> > obj.S0() # emit warning but call 'S0' method
> >
> > Rational: if bare <abc> isa identifier here, then C<S0> too.
>
> I can see the value of this warning for the transition, but long-term we
> really don't want the code to be littered with warnings for deprecated
> features. It'd be pretty simple to write a script that combs the .pir
> files in the repository for any instance of a method name that looks
> like a register name.
Indeed. I'm pretty sure that there isn't one test nor other .pir in the tree
using this syntax. The rules are clear enough: C<S0> is parsed as IDentifier
in that case, which means:
obj.S0() := obj.'S0'()
That is: no warning needed at all.
> > obj.$S0() # illegal
>
> Is '$' a valid identifier character?
No. Just a PIR temp variable signature, which makes the thingy a variable or
synmbol.
> If so, then it's legal, but just
> treated as part of the string name of the method. If not (which seems
> more likely), then it's illegal anyway, so no special case is needed.
I wanted just to stress the illegality of it as it was differently used in a
previous mail.
> Allison
leo
Why not handle this like we handle subroutines? That is, why don't we
have a find_method opcode that returns a bound method? That simplifies
parsing for IMCC and makes PIR a little simpler.
obj.'abc'() # call 'abc' method of obj
obj.abc() # same as above
$P0 = find_method obj, abc # get bound method indicated by abc symbol
$P0() # actually call it
--
Matt Diephouse
http://matt.diephouse.com
> Why not handle this like we handle subroutines? That is, why don't we
> have a find_method opcode that returns a bound method? That simplifies
> parsing for IMCC and makes PIR a little simpler.
> obj.'abc'() # call 'abc' method of obj
> obj.abc() # same as above
> $P0 = find_method obj, abc # get bound method indicated by abc symbol
> $P0() # actually call it
Does that make emitting PASM a lot more difficult? All of a sudden, invoking
$P0 means that $P0 has to know that it stores something in the appropriate
invocant PMC register.
I suppose that means creating a CurriedInvocantMethodCall PMC that does all of
this magic in invoke(). That's not awful, but....
-- c