Missing External Referents

AppleScript is a little language, leaving it up to externals such as scriptable applications and scripting additions to extend the vocabulary of the language as needed. A consequence of this architecture is that at various crucial moments during the life of a script, such as when it is compiled, decompiled, or executed, AppleScript will look for externals, and may complain if it can't find them. This section talks what happens on these occasions.

When a script is compiled, AppleScript needs each application targeted by that script, so that it can obtain its dictionary to verify the legality of the terms the script is using and to translate those terms into Apple events in bytecode. If it can't find a targeted application, it will present a dialog asking you, the human user, to locate it. At this point, one of three things can happen:

What consitutes "the correct application" is any application whose dictionary defines the terminology AppleScript is trying to verify. To see what I mean, let's start with code like this:

get disk 1

If that's all your script says, it won't compile at all, because the term disk isn't part of the AppleScript language. You can use it this way only while targeting a scriptable application that extends the AppleScript language to include a class called disk. Now, suppose we pretend there is such an application, making up a name for this application when in fact we have no application by that name. Let's call it NoSuchApp.

tell application "NoSuchApp" to get disk 1

When you try to compile this, you're presented with a dialog listing every application, and posing the question: "Where is NoSuchApp?" AppleScript has realized that although the term disk is meaningless in the AppleScript language proper, it might be meaningful in the context of the application NoSuchApp. AppleScript therefore attempts to find the application. (I do not know the exact steps used in the search.) But NoSuchApp can't be found , because there's no such app. So AppleScript turns to you for help. If you cannot at this moment locate NoSuchApp—if, for example, you cancel out of the dialog—the script will simply not compile.

Instead of cancelling, you can nominate an application as NoSuchApp—any application, even if it isn't called NoSuchApp. This makes sense because the problem might merely be that the name has changed or you've typed it wrong. For example, you could claim that NoSuchApp is Address Book (just select Address Book in the dialog, and click Choose). However, this does not fool AppleScript for long, because it immediately turns to Address Book's dictionary and investigates its vocabulary. AppleScript quickly concludes you were lying, because Address Book doesn't know what a disk is after all—and the script still won't compile.

Now suppose we try again to compile, and this time, when AppleScript asks us about NoSuchApp, we tell it that NoSuchApp is the Finder. The script now compiles successfully, because the Finder does know what a disk is. Also, when the script is decompiled for display, you'll find that the name "NoSuchApp" has changed to "Finder"; I explained earlier, under "Decompiling," how that sort of thing happens.

I'll talk about how you specify a target application in your script, and how to do so in such a way that AppleScript stands the best chance of just compiling the script without asking for human assistance, in "Tell" and "Using Terms From" in Chapter 19, and in "Application" in Chapter 13.

Decompilation takes place when you open a compiled script file for editing; the bytecode of the compiled script file is illegible, so it must be decompiled in order to be displayed (and pretty-printed). When a compiled script file is decompiled, AppleScript needs each application targeted by that script, but for the opposite of the reason why this happens at compile time. When a script is compiled, AppleScript needs a dictionary so that it can translate the English-like AppleScript code into Apple events in the bytecode. When a script is decompiled, AppleScript needs a dictionary so that it can translate the Apple events of the bytecode into English-like AppleScript code.

Once again, AppleScript starts by trying to find the application on its own. And again, I do not know the exact steps involved here, but experimentation shows that if the application is actually running at decompile time or is present on disk and its name has not changed, AppleScript usually finds it easily. The compiled script also contains an alias to the application, which means that even if you change the application's name, or move it to a different folder on the same volume, the compiled script should continue to keep track of it.

Nevertheless, there can be circumstances under which AppleScript cannot find the application on its own—perhaps you moved the application to a different volume, or the script is being opened on a different machine where the application is missing or has another name—so when you try to open the script in your script editor application, you'll get the dialog asking you to locate it. At this point, one of three things can happen:

The third possibility is surprising. Recall that when you try to compile a script, if AppleScript asks you to locate an application and you locate some other application, one that doesn't resolve the terms used, AppleScript knows you're lying, and compilation fails. But when you decompile a script, if AppleScript asks you to locate an application and you locate some other application, decompilation succeeds. This can be quite useful, because it means you can open a script for editing, and even read most of it, even if you're missing one of the applications needed to run it.

For example, consider this script targeting Eudora:

tell application "Eudora"
    get subject of message 1 of mailbox "Trash"
end tell

Let's say we compile this script and send it to a friend who doesn't have Eudora. Our friend tries to open the script in a script editor application. AppleScript can't find Eudora, so it asks our friend where it is. At this point, let's say our friend nominates another application—let's say it's the Finder. The script opens, but the terms subject, message, and mailbox aren't defined in the Finder's dictionary, so some raw four-letter Apple event codes are displayed (and the Finder is substituted for Eudora as the targeted application):

tell application "Finder"
    get «class euSu» of «class euMS» 1 of «class euMB» "Trash"
end tell

Our friend can now read and edit the script. Those things in funny double angle-brackets are actually raw four-letter codes from the Apple event stored in the bytecode. (See "What an Apple Event Looks Like," earlier in this chapter; raw four-letter codes are further discussed in Chapter 20.) The script is already compiled, so now it is possible to run it—though it probably won't run properly, because the Finder will complain when it is sent an Apple event it doesn't understand.

When a compiled script file is executed directly without opening it for editing—as, for instance, in a script runner—it is not decompiled, and so it does not face quite the same issues as it does during decompilation. AppleScript in this case doesn't look for an application until the script is already running and it encounters code referring to that application. Thus, a targeted application may be missing, but if the code referring to it is never actually executed, there is no problem.

So, for example, this compiled script file runs perfectly well in a script runner on a machine without Eudora:

if 1 = 2 then
    tell application "Eudora"
        get subject of message 1 of mailbox "Trash"
    end tell
else
    display dialog "No problem!"
end if

The reason is that because of the branching code (and because 1 is not 2) the material about Eudora is never encountered, so the issue of where Eudora is and what all those terms mean never even arises.

If, on the other hand, such material is encountered, and the AppleScript scripting component can't find the target application on its own, what happens depends upon the context. Sometimes the dialog appears asking you to locate the application (as, for example, in BBEdit); but sometimes it doesn't (under Apple's Script Menu, for example, the script will simply fail silently). Furthermore, having helped the AppleScript scripting component find the application, you'd like that information to be saved back into the script so that next time the script will run without assistance. Again, sometimes this happens and sometimes it doesn't, depending upon the context. (If all this inconsistency seems confusing and annoying, that's because it is.)

A run-only script obviously presents a special case, because it can't be modified. Thus, if a run-only script, while executing, encounters a reference to a targeted application that AppleScript can't locate on its own, then even if the dialog asking for the application appears, and even if the user can locate the application (in which case the script will then continue executing), this information cannot be saved back into the script; the next time the script runs, the dialog will appear again.

An applet contains a compiled script, and when the applet is launched, the compiled script runs without being decompiled, so you might think it would behave just like a compiled script that is executed directly. You'd be wrong. Instead, when an applet is launched, all its external referents are sought then and there, just as if you were compiling the script.

If an applet, as it launches, asks you to locate a missing application and you do locate it, the applet may proceed to run correctly. But knowledge of the missing applications location is not written into the applet, so the same thing will happen next time the applet is launched. The only solution is to open the applet for editing (if it hasn't been saved as run-only) and fix it in a script editor application.

A puzzle therefore arises of how to permit an applet, if it contains code that might never be executed and that targets an application that might be missing, to run at all—in other words, how to make an applet behave like a compiled script file in this regard, postponing the question of where an application is until the script runs and code referring to that application is actually encountered. This puzzle used to be rather difficult to solve, but with the invention of the terms block (one of the few major linguistic changes in the history of AppleScript), there is in fact a rather slick workaround. I'll explain what it is in "Using Terms From" in Chapter 19.

Up to now we've been talking just about missing applications. But what if what's missing is a scripting addition? AppleScript can't present any kind of dialog pointing out that such-and-such a scripting addition is missing, because it has no idea where the troublesome terminology was supposed to come from. This is because you don't target a scripting addition; you just use its terminology directly as if it were part of the language. So if the scripting addition isn't there, then this terminology isn't part of the language, and that's all AppleScript knows about what the problem is. You're talking illegally, but AppleScript doesn't know why. So what happens? It depends on what you're trying to do:

You're trying to compile the script.

The script simply won't compile. In the absence of the scripting addition that defines a certain term, that term is illegal, and a script that uses illegal terminology can't be compiled.

You're trying to decompile the script.

All other things being equal, you'll succeed. The terminology from the missing scripting addition is shown in raw four-letter form; AppleScript can't find the scripting addition's dictionary to resolve it back into English-like AppleScript code, so it just shows you the Apple event that's stored in the compiled script's bytecode. This makes sense, because if AppleScript refused to decompile the script merely because it contains an unknown Apple event, you'd probably never be able to read the script again, since AppleScript has no way of knowing or informing you of what the problem is—namely, that a certain scripting addition is missing. Of course, now that you've seen the raw four-letter code of the Apple event, you probably have no way of knowing what the problem is either. Even if you can guess that a scripting addition is missing, how can you know what scripting addition it is, unless you've got a list of all the raw four-letter codes of all possible scripting additions? (This is just one of many ways in which scripting additions are troublesome.)

You're trying to execute the script.

The script will run until it comes to the offending Apple event, and then things will probably choke. That's because this Apple event is going to be sent either to some target application or to AppleScript itself, and either way it isn't going to be defined in the repertory, so an error message will come back. Unless the script handles this error, execution will come to a stop and the error message will be displayed; it may well include the four-letter code of the Apple event (for all the good that's going to do you).