.local string perl6_source
.local pmc perl6_compiler
perl6_compiler = compreg 'Perl6'
$P0 = perl6_compiler(perl6_source)
However, pdd21_namespaces.pod says that compilers have
methods such as 'parse_name', 'get_namespace', and 'load_library'
(see the section titled "Compiler PMC API").
Recognizing that much of the details about compilers are still to
be specced, the naive version of my question is: "How do we get
those API methods (and possibly other compiler-specific methods)
attached to the compiler sub?"
Or, in claiming that compilers have an API, should we instead
say that the canonical compilation sequence is to use compreg
to obtain a compiler object (not an invokable sub), and then
compile the source via a 'compile' method on the compiler object?
For example:
perl6_compiler = compreg 'Perl6'
$P0 = perl6_compiler.'compile'(perl6_source)
In asking the above questions I'm purposely avoiding, because Parrot
doesn't seem to support it yet, the possibility that the object
obtained via compreg is both invokable in its own right (like a sub)
and has methods attached to it. I'm simply curious as to how we
conceptually model "compilers" in Parrot -- are they really like
subroutines or are they more traditional 'objects' that provide
a method-based interface for invoking a compilation.
Opinions welcome. Personally I think I favor the "a compiler is
an object with a 'compile' method" model, and that C<compreg> gives
us back a compiler object as opposed to a subroutine-like thing.
Thanks in advance,
Pm
Would it not be possible to support both? A compiler compiles, so it
seems natural to write quick-and-dirty compilers as simple
subroutines. Also for wrappers around other compiler codes, they still
could be useful if they compile/execute, while there is not a
full-blown implementations in terms of the Parrot compiler API .
The usage patterns could be something like:
.local string perl6_source
.local pmc perl6_compiler
perl6_compiler = compreg 'Perl6'
$P0 = perl6_compiler(perl6_source)
(Yes, the same as Patrick's first snippet.) And then
perl6_compiler = compreg 'Perl6', OBJ # OBJ is some constant
$P0 = perl6_compiler.'compile'(perl6_source)
"compreg S" would be short for "compreg S, SUB" or something (and it
could be always required). While "compreg S, OBJ" may blow with a "non
implemented feature for some compilers". It would be easy for object
compilers to provide a sub implementation. But on the other hand it
could also be easy for subroutine compilers to provide an object
implementation with the help of a wrapper object (with not very useful
methods besides 'compile').
My $0.02 cents.
Adriano Ferreira.
Sure, it's possible to support both -- we can even handle both
types within the existing Parrot framework. I think I'm basically
asking which model will be considered the "Parrot standard"?
> The usage patterns could be something like:
>
> .local string perl6_source
> .local pmc perl6_compiler
> perl6_compiler = compreg 'Perl6'
> $P0 = perl6_compiler(perl6_source)
>
> (Yes, the same as Patrick's first snippet.) And then
>
> perl6_compiler = compreg 'Perl6', OBJ # OBJ is some constant
> $P0 = perl6_compiler.'compile'(perl6_source)
We could do this now without requiring an additional parameter
to C<compreg>, by adding suffixes to the compiler name. For example:
$P0 = compreg 'Perl6_sub' # get subroutine view of compiler
$P0 = compreg 'Perl6_obj' # get object
But I find the suffix (or the use of an extra parameter) a bit
overblown. I'd rather come up with a standard API for compilers
that supports all of this, and then individual compiler subs can
deviate from that standard if it's really appropriate.
I've also just written a HLLCompiler base class (I would've called
it 'Compiler', but that name is currently taken in Parrot) that
makes it easy to wrap a sub into a compiler object. Thus
registering a new compiler becomes:
load_bytecode 'Parrot/HLLCompiler.pbc'
.local pmc compile_object, compile_sub
compile_object = new [ 'HLLCompiler' ]
compile_sub = get_global 'name_of_compile_sub'
compile_object.'compsub'(compile_sub)
compreg 'MyCompiler', compile_object
Using the compiler is then:
.local pmc mycompiler
mycompiler = compreg 'MyCompiler'
$P0 = mycompiler.'compile'(source)
Thanks for the excellent comments!
Pm
Also for the record from the weekly meeting (which was actually today,
just a very long today): Yes, compilers are objects and compilation is a
method call. The compiler for TGE tree grammars is implemented this way,
and it's a very usable interface.
We might want to resurrect the 'compile' opcode as an indirect syntax
for making the 'compile' method call.
> In asking the above questions I'm purposely avoiding, because Parrot
> doesn't seem to support it yet, the possibility that the object
> obtained via compreg is both invokable in its own right (like a sub)
> and has methods attached to it.
Possible, but agreed that it's more complex than we need, without enough
added gain to be worth it.
Allison
For the record, it was decided (Allison++) during today's
#parrotsketch meeting that the convention would be to have
the 'compreg' opcode return an object with a 'compile' method,
as opposed to returning an invokable sub.
Of course, individual language implementors may choose to
defy convention, and for the forseeable future the "PIR"
and "PASM" compilers that come with Parrot will continue
to be subroutine-like. All of this just means that
callers to compreg need to know what they are getting back
or else figure it out at runtime.
I'm hoping to formalize some of the details for creating
compilers into a "compilers pdd" at some not-too-distant date.
Comments and suggestions welcomed. At the moment we have
the following:
- There's a HLLCompiler class (loadlib 'Parrot/HLLCompiler.pbc')
that can be used to quickly create compiler objects.
- To register a new compiler using HLLCompiler:
load_bytecode 'Parrot/HLLCompiler.pbc'
.local pmc compile_sub, mycompiler
## get the compilation subroutine
compile_sub = get_global 'compile'
## create a new compiler object
mycompiler = new [ 'HLLCompiler' ]
## register the language and compiler subroutine
mycompiler.'register'('MyCompiler', compile_sub)
- To perform a compile:
mycompiler = compreg 'MyCompiler'
$P0 = mycompiler.'compile'('...source code...')
In addition, HLLCompiler provides a 'command_line' method
for acting as a standalone compiler; thus a .pir/.pbc can
simply have its :main sub delegate control directly
to HLLCompiler. For example:
.sub main :main
.param pmc args
.local pmc mycompiler
mycompiler = compreg 'MyCompiler'
mycompiler.'command_line'(args)
.end
When invoked in this manner, compiler object will compile
and execute any source file given on the command line, or
enter an interactive mode if no source file is given.
The result of the compilation can be controlled by the
--target command line (assuming the compilers involved
support this):
--target=parse # output parse tree
--target=past # output ast (PAST)
--target=post # output opcode tree (POST)
--target=pir # output PIR
HLLCompiler also understands '--encoding' (for languages that
have specific character encoding requirements), and '--output'
to specify a file where the output should be placed.
Comments and other feedback are greatly appreciated.
Thanks!
Pm
Our messages crossed in the mail.
> We might want to resurrect the 'compile' opcode as an indirect syntax
> for making the 'compile' method call.
Maybe, but I can't see that this is worthy of a special opcode
(and presumably a vtable slot?). There's just not a lot of
difference between:
$P0 = compile mycompiler, code # compile opcode
$P0 = mycompiler.'compile'(code) # Parrot convention
Another advantage of using (true) method calls is that
it's easy to pass options and additional arguments to the
compiler:
$P0 = mycompiler.'compile'(code, 'target'=>'parse')
Pm
The main reason is to have a consistent interface that can be used to
compile code whether that code is PASM, PIR, or one of the HLLs. But, a
lot depends on how the Compiler PDD develops. It seems likely that the
best solution will be to have the PASM and PIR compilers act like
standard compiler objects.
Allison
Please don't. Opcodes are very limited re calling conventions. Mehthods are by
far more flexible when it comes to pass arguments to compilers.
And compiling a piece of code isn't one of the fast operations a CPU can do,
it doesn't warrant an dedicated opcode.
leo
I believe we've been through this conversation before. I don't mean
coding a completely different opcode, I just mean using the opcode
syntax to make (a standard form of) the method call.
And, I do think making the PASM and PIR compilers capable of being used
as standard compiler objects is a superior solution.
Allison
Yes. But abstracting opcodes syntax to some method isn't implemented
generally. It can be done, as experiments in src/builtin.c are showing. But I
don't see any good reason to implement new features with some opcode syntax,
when it'll be a method call anyway.
> And, I do think making the PASM and PIR compilers capable of being used
> as standard compiler objects is a superior solution.
We currently can't pass any arguments to PASM/PIR compilers. You can't change
trace or debug options for "eval". This is a serious limitation, which needs
a flexible solution e.g. passing named args. Some syntactic sugar opcode
syntax, which first has to be parsed as opcode, then be converted to a method
call does really not help here - sorry.
> Allison
leo
Hrm... I think you missed my point, which was that enabling method-call
syntax on the PASM/PIR compilers was a better solution than pushing
consistency via opcode syntax.
We're in violent agreement again. :)
Allison