OddThinking

A blog for odd things and odd thoughts.

Rational 1000: Staged Compilation

This is one of a short series of nostalgic reminiscences about the Rational 1000.

One of the more interesting concepts on a Rational 1000 was the way it pushed your code through stages on the way to compilation.

Stage 1: Raw Text

In the first stage, it was raw text. Not much to say really.

Stage 2: Formatted Text

Periodically (generally several times a minute!), while writing code, you would hit the Format key. The Format key would format your code, according to the project-standard.

To the compiler geeks: behind the scenes it would build a concrete syntax tree, and then pretty-print it. From what I could see it was a pure tree (no semantic links or context).

Generally, the formatter’s parser was very forgiving. There was very little that would stop it in its tracks. If it encountered an error (like an if with no then, it would automatically include the missing parts of the statement, leaving placeholders for you to fill in your code. You could even jump between, and overtype, the placeholders quickly. Ada is a verbose language; the Format key was a key time-saver for a lazy typist.

What’s more, at least half of the wasteful arguments about coding style instantly disappeared. The formatter was common across the project, and running it was a necessary step to getting code to compile. It was impossible to break a lot of the coding standards that coding reviews still get bogged down on today.

If I recall correctly, it even smashed-case to follow a fixed style.

I have some other ideas here about avoiding coding-style arguments, but I will discuss them later.

Stage 3: Semanticized Code

The next button was the Semanticize button. I never liked the term “semanticize” but I learned to live with it.

The Semanticize button did an implicit Format (if it was required). It then did a full parse, checking that the code was semantically correct, and highlighting any errors.

Ada is a bondage-and-discipline language. It is strongly typed, by just about every definition. Developers would spend plenty of time in this phase, until the parser gave the go ahead and lifted the velvet rope to let the code past.

Once the code was semanticized, some other features would become available.

Command completion was a key one, and one that I missed for many years after leaving this project. That is, you could type the beginning of an identifier name, and hit the Complete button to get a list of choices of matching identifiers. You could type the name of a package, and hit Complete to get a list of choices of identifiers in that package. You could type the name of a function, and hit Complete to have placeholders appear for each of the parameters.

Ada supports named association of parameters. That is, you can write copy(from=> sourceFile, to=>destinationFile); or copy(to=>destinationFile, from=>sourceFile); without anyone getting confused. I think is a Good ThingTM – i.e. having the option supported is great. It was particular useful here in providing hints to the developer about how to fill in the parameters.

The only problem with the Complete command was that it was slooooow! It would sometimes take over 30 seconds to complete. I often said at the time that the Complete command would be great if it was ten times faster. I would probably demand 500 times faster these days.

Stage 3: Installed Code

The parse graph tree created by the Semanticize button was both temporary and one-sided.

The next button in the sequence was the Install button.

The Install button did an implicit Semanticize (if it was required). It then stored the parse graph tree to permanent storage.

I think it also modified the parse-trees of other installed code to link back to this one. That way, for example, you could see what code referenced a particular definition very quickly. By quickly here, I am probably talking about only a 30 second delay!

Once code was in the installed state, other code could start to reference it. For example, the Semanticize function or the Complete function would only find referenced definitions if they were in the Installed state (or greater).

Once in the Installed state, the parse tree (in the form of a DIANA tree) was also available for meta-programming tools to manipulate.

Installing marked the text as read-only. You would have to “uninstall” it (also known as Demote or Edit) to make further changes. Any code that depended on it would also need to be uninstalled.

Stage 4: Generated Code

The final stage was to generate code.

The Generate Code button did an implicit Install (if it was required). It then produced the object code for the appropriate target platform.

For the cross-compiling that we did, this was terribly slow. (We didn’t do clean build of the project each night – it took days to compile the whole project from scratch.) Doing early testing directly on the Rational 1000 was always preferable for many reasons; avoiding unnecessary cross-compiles was a key one.

Ada didn’t have an explicit linking phase. I don’t recall the details of how you would mark a function main program to produce an executable, but it wasn’t actually required when you were compiling natively to the Rational 1000. It could execute any arbitrary function from the “command line”.

Conclusions

This has mainly been a description of how an IDE worked 14 years ago.

I’ve expressed a few minor points of view here and there (e.g. praising the benefits of a project-standard pretty-printer, and named association of parameters).

My stronger opinions are actually on the benefits of a Complete button (which didn’t appear in a reliable form in other, popular IDEs for many years later), and the usage of the concrete syntax tree, rather than text, as the primary artifact of the IDE. I’ll probably blog more about the latter, later.

Note: This essay has largely been from memory from over 11 years ago when I last used a Rational 1000. It is somewhat likely that I have made some errors in the details – especially in the behind-the-scenes work by the compiler – but I think the overall flow and concepts are intact.


Comments

  1. Wikipedia doesn’t even have a stub for the Rational 1000. At least, it’s not linked from the Rational page.

    Sounds like an opportunity for fame and fortune to me. OK maybe just fame. OK maybe neither.

  2. Sounds much like a less animate, more static version of the Smalltalk image concept.

  3. Aristotle,

    I’ve been pondering this.

    I only know the Smalltalk image concept in theory. I can see the overlap – the idea of promoting an object into the namespace of objects that the environment knows about.

    It has quite a different feel for me though. There was no concept of cloning a copy of the image for later.

    The binding was generally early, rather than late. (I can’t state clearly why I think this is important. It just goes to the different “vibe”.) If you wanted to edit a library, all the files that used the library had to be demoted back to edit mode too, and then re-installed.

    [Actually, it had the ability to demote individual lines of code, so that it only needed to recompile part of the file. In principle this was much faster! In practice, it was very finicky and finished up to be faster to recompile the lot.]

  4. Of course there is a huge difference! It’s what I meant by “less animate.”

    Late binding allows compiling methods as soon as you finish writing them. There is never actually a source file as such in a Smalltalk image, all the code exists as running, live bytecode and only turns into text as you browse the system to look at methods. (James Robertson explains this better.)

Leave a comment

You must be logged in to post a comment.