3.4. Compiling and Decompiling
To compile your AppleScript code is a necessary intermediate step between editing it and running it. This section explains what this means, and then discusses some of the implications for you, the AppleScript programmer.
To compile code means to transform it from editable text to a form that is understood by some engine that will run the code. The particular form depends upon the nature of the particular engine. At one end of the spectrum, the engine might be the computer's central processing unit (CPU), in which case the code is turned from text to machine-language instructions; that, for example, is how Fortran and C work. At the other end of the spectrum, one can postpone compilation until the program is actually running and a line of code is encountered; for example, in some implementations of BASIC , such as the one that came with the original Apple II, the runtime engine accepts pure text, compiling and executing that text one line at a time. A language that works this way is said to be interpreted.
In the early days of computers, most language implementations fell into one camp or the other, being either compiled or interpreted. These days, however, many popular scripting languages (such as Perl, Python, and Java) are implemented through a compromise technique where the entire program is initially compiled into an intermediate representation, which is then fed to a runtime engine that accepts and executes it a chunk at a time. In effect, such languages are compiled, then interpreted. AppleScript works this way.
Like those other scripting languages, AppleScript code is compiled into bytecode , meaning that, roughly speaking, the nouns and verbs of the original text are translated into a sort of compressed, coded equivalent, called tokens . These tokens are meaningful to the AppleScript scripting component's runtime engine (and illegible to everyone else). The runtime engine interprets the bytecode, parsing whatever tokens it meets along its path of execution, accumulating them into chunks, and translating these chunks further, as necessary, in order to execute them.
There is sometimes a prejudice against interpreted languages as being slow. It is true that the compiled code must be processed further while being run before it can really be run, but this need not make the language particularly slowno one complains, for example, that Perl is slow. The AppleScript runtime engine, however, probably does run a good deal slower than it should. Whether you'll perceive this slowness in practice depends on the nature of the script and on the speed of your computer; with today's fast computers, the observable bottleneck will typically be the time required to send Apple events to the target application and for that application to respond, more than the overall speed of the AppleScript runtime engine. (Tips for improving script speed appear in Chapter 22.)
The AppleScript compiler is what's called a single-pass compiler; this is a fairly simple-minded approach to compilation, but it helps to ensure that your script has a certain level of legality and overall consistency before runtime. (Of course, even a legal, successfully compiled script could still choke at runtime, but this would be for a different kind of reason; you'll see many examples over the course of this book.) Consequently, the runtime engine has less work to do, and is therefore smaller and faster, than if AppleScript were not compiled at all.
Also, the use of compilation makes AppleScript a better language. For instance, the following AppleScript code is legal:
sayHowdy( ) on sayHowdy( ) display dialog "Howdy" end sayHowdy
In this code, we call a handler, sayHowdy( ), before we've defined it. If AppleScript code were not compiled intially, this would not be possible, because upon encountering this reference to a handler it has not yet met, the runtime engine simply wouldn't know what to do.
A curious feature of AppleScript is that it not only compiles scripts, it decompiles them, turning compiled bytecode back into human-readable text. Decompilation is not an unknown procedure in the computer world, but it's usually a kind of hacker tool (used, for example, to steal someone else's code); I don't know any other computer language where decompilation is a routine behavior of the language implementation itself.
AppleScript decompiles on two main occasions: right after you compile a script, and when you open a compiled script file for display in a script editor application. The procedure in both cases is essentially the same. When a compiled script is displayed to a human user, it is pretty-printed, with indentation to reflect the script's structure, and different kinds of word shown in different fonts, sizes, styles, and colors. This pretty-printing , as we have seen, is performed by the AppleScript scripting component, not by the script editor application (the script editor application merely asks the AppleScript component for the pretty-printed script and displays it). The AppleScript scripting component generates the pretty-printed version of the script, not from the original text, but from the compiled bytecode. In other words, in order to generate the pretty-printed text, it decompiles the bytecode.
This behavior has some curious consequences. The pretty-printed code that you see when you compile your text in a script editor application is not, you understand, a colorful version of your text; it's a completely different text, supplied by decompiling the compiled script's bytecode. Therefore, some of the words constituting your script may actually differ before and after compilation. For example, suppose you type this code into a script editor application:
tell app "Finder" to get ref disk 1
Compile that code. Now your code is displayed like this:
tell application "Finder" to get a reference to disk 1
The reason is that some AppleScript terms have abbreviations that are acceptable for purposes of compilation, but the scripting component substitutes, at compile time, a token signifying the canonical form of the term; thus, when it decompiles the bytecode, what you see is the canonical form.
AppleScript may even, in the course of decompilation, rebreak your code's lineation. AppleScript allows you to break long lines into shorter lines through the use of a continuation character (); at compile time AppleScript will sometimes undo your attempts to use this feature, removing your continuation characters or putting them somewhere else. If I compile this code:
do shell script "echo 'hi'"
AppleScript rebreaks it like this:
do shell script
The reason is that the bytecode contains no information about whitespace, so the new formatting imposed by the decompilation process may not correspond to your original whitespace. This looks like a trivial annoyance, but if the line were longer it wouldn't be so trivial. Fortunately, the current version of the Script Editor wraps long lines, so the problem is far less disruptive than it was in the past because there isn't much need to use the continuation character any more.
(Some more examples of this odd behavior of AppleScript's will appear in "Abbreviations and Synonyms" in Chapter 5 and elsewhere, including "Variable Names" in Chapter 7.)