contributing to tcl/parrot ^

a brief overview on how to help out, if you're interested. In general, it's ok to send patches for tcl to the RT system for anything that isn't "BIG STUFF" - those, please bounce off the MAINTAINERS first. We'd prefer diff -u patches, but are happy to take complete files as well.

Since partcl is bundled with the parrot source, svn access is handled via the same mechanism as for parrot itself.

BIG STUFF ^

Writing Builtins

partcl is a compiler. Given a section of tcl code, it translates that code into an AST (in the form of a Tcl* object from lib/*). Once a section of code has been translated, the object's compile method is invoked, which then generates PIR which can be compiled by parrot.

The commands themselves have (well, some of them do) inlineable (compiled) versions: These are in lib/builtins, and all new development should go here. There are many old, runtime-only versions that exist in lib/commands. All *new* development should occur in lib/builtins. Minor patches to things in lib/commands are ok, though.

When you define an inline-able builtin, tcl.pl will automatically generate a version similar in those in lib/commands/ that invokes the inline'able version, compiles it, invokes it, and returns the result immediately. The compiler will fall back to this version as necessary, you don't have to worry about it.

When writing inlined builtins, there are two variants: those generated automatically by running tools/gen_inline.pl, and those that are handrolled.

Templates for inlined builtins look a lot like a perl hash that defines the various arguments, options, and code for the builtin. By switching to this declarative scheme, we hope to elminate a lot of the bookkeeping code, as well as simplify generations. While we're using perl to generate the code, this is a one time cost at build time: at run time, we are using PIR. (Which in turn generates PIR itself).

Handrolled inlined builtins take two args: a TclCompiler, and a single container PMC with the various args. The container PMC has objects generated from parser that need to be compiled themselves before you can rely on their values. Your return value is simply the compiler object. You should only use this method for generating builtins if a builtin cannot be defined using a template, and even then, extending the template parser is probably the right answer.

(This is as opposed to the interpreted style in lib/commands, which takes a variable number of already compiled args at runtime. Our fallback method bundles these up, passing in a starting register of 0, and uses the resulting PIR to generate a .sub immediately and then invoke it. The runtime fallback is always going to be necessary for HLL interoperability.)

Remember, every arg to your builtin is potentially variable and may change at runtime. You can check the type of the object at compile time. However, if it's constant, then you can optimize: for example, we could optimize [puts -nonewline "whee\n"] down to, basically, {print "whee\n"}... however, we still have to be able to deal with [set a -nonewline; puts $a "whee\n"]. So, your first pass should probably do no optimizations. If you see a potential for optimization, however, at least add an XXX comment so we can get back to it later. At this point, I'd rather spend time working on new features than making existing features faster.

The results of calling the inlined code will be used by lib/tclcommand.pir. The inlined code is further wrapped in a protective conditional which will skip the inlined version if it can no longer be trusted (that is, if [proc] or [rename] has been invoked), and will fall back to the interpreted version as necessary. (And throw an exception if the command is not found at runtime.).

speed

We're currently slow, compared to tclsh. It's not worth worrying about this in terms of specific numbers until we can run tcltest natively. That said, any patches that improve speed without harming maintainability will of course be applied.

features

We're currently missing some things that require support from parrot. See hacks.pod for a list. In general, though, a lot of what we need to do is possible with parrot.

If you're looking for something to todo, see TODO one level up.

DOCUMENTATION ^

pod

Every PIR .sub that's defined should probably have some POD to go along with it to document the arguments and return values. Only exceptions to this should be subs which correspond directly to Tcl builtins -- those are already documented elsewhere.

big picture docs.

Are the docs in docs/ useful? Could use someone to proof them, and verify that there are no missing chunks (if missing, write them, or get them added to the TODO), and that they are coherent.

PIR ^

Missing Commands

Every builtin command corresponds to an appropriately named file in src/builtins or runtime/builtins - Each of these subs takes some number of PMCs as arguments. For those commands that take a fixed number of parameters, we declare them with .param. For those that take a variable number, we use the foldup opcode.

If the return value would be TCL_OK, then simply .return the value from the sub. For any other return type, use one of the macros in src/returncodes.pir: .throw, etc. the value can be any simple register or a PMC, the calling conventions will autobox as necessary.

Before adding new functionality, add a test (or a test in an existing) file in t/ - tests for puts, for example, go in t/cmd_puts.t - we use the Test::Harness framework, via Parrot::Test.

Our final goal will be to pass (most of) the tcl test suite: run make tcl-test to checkout the latest version of of the tcl test suite and run it. Warning: slow...

Long term goal is remove any tests in t/ that are testing things that are already tested in the official tcl suite. Partcl's checked in test suite should just be checking partcl-specific functionaliity.


parrot