PROGRAMMING PARADIGMS

A Tour of AppleScript

Michael Swaine

I'd been using the beta of AppleScript, as well as betas of a couple of third-party products that support AppleScript, so when AppleScript Developer's Toolkit 1.0 shipped this summer, I was eager to talk about it.

And it turns out that there are things to say about Apple's long-awaited system-level scripting software, and reasons to look into it, even if you don't think you'd ever use it, even if you don't think you'll ever use a Macintosh.

AppleScript is Apple's idea of what a scripting system can and ought to be on a 1993-vintage GUI and operating system. That's not the same thing as, say, little languages on UNIX systems, or batch files on DOS systems. But it's also not the same thing as the scripting systems we can expect to see on next-generation object-oriented operating systems. What's intriguing is that AppleScript could serve as a testing ground for such future scripting systems. Believing that object-oriented operating systems and scripting are made for one another, I find this intriguing.

And it should be interesting to follow the fortunes of AppleScript over the next year. It's always interesting to follow Apple's fortunes, if not always satisfying. I recently wrote an upbeat piece for another publication about things that Apple was doing right, and between the time I turned it in and it saw print, Apple announced disappointing profits, replaced its president, threatened to lay off up to 2000 employees, and cut R&D. (In publishing, cycle time is three months and a lot can happen between fetch and execution.) Apple's successes in its early attempts to get third-party developers to embrace the interapplication communication technology that AppleScript relies on, were about as mixed as President Clinton's results in trying to get bills through Congress earlier this year. Now that there's been some success on this front (Apple's, that is), it makes sense to launch AppleScript. But the launch means another campaign to get developers to embrace another Apple technology: There's more to supporting AppleScript than just supporting Apple events, and AppleScript's success is purely a question of how many AppleScript-supporting applications come out, how soon.

What AppleScript Is

The core of AppleScript is an operating system component called the "AppleScript extension." Although actually using AppleScript to do something meaningful involves the support of application developers, installing AppleScript is no more complicated than dropping this file into the appropriate folder. What the AppleScript extension does is to interpret AppleScript commands sent to it and to dispatch Apple events to the appropriate target.

The source from which the AppleScript extension gets its commands and the destination to which it sends its Apple events are where third-party applications come in, but it's not just applications that can talk to AppleScript. The possible sources for AppleScript commands and the possible destinations for Apple events suggest its potential:

The Script Editor. A script editor is another key element of AppleScript, and Apple supplies one. It's not difficult to use, and users will find that writing scripts with it is roughly on the level of writing HyperTalk scripts. But this supplied script editor is not sacrosanct: Third-party developers can write their own and replace it.

An Application. An AppleScript-compatible application can send Apple-Script commands in response to user or internal events. Or it can do its usual tricks in response to Apple events coming in from the outside. An application can be a sender or a receiver.

A Script Application. There's also something called a "script application," an AppleScript script that's been compiled and saved as a double-clickable application. Script applications can be sources or destinations for the dispatching actions of the AppleScript extension. While they are much like real applications, they don't relate to Apple-Script in quite the same way, and so are worth considering separately. Script applications can also act as a kind of script server for scripts and Apple event-savvy applications.

The Finder. Using the Finder as a destination is one of the ways in which AppleScript can drive operating system functions, although technically the Finder is really an application; not yet a scriptable one, but the scriptable Finder is promised real soon now.

The AppleScript Extension and Scripting Additions. Here's another way AppleScript can drive the operating system. A half-dozen core commands are handled by AppleScript itself (that's in addition to its regular duties in interpreting the language: evaluating expressions, resolving references, and so forth).

When you run out the combinations of all these sources and destinations, and when you consider that the source and the destination don't have to be on the same machine or in the same network zone, you can see that "using AppleScript" covers a lot of different scenarios.

What It's Good For

With AppleScript, an application could implement its own internal scripting language by sending messages to itself through AppleScript. In fact, all the actions of an application could be mediated by AppleScript. Menu choices and selection operations, and for that matter, pretty much anything that signals a potential content change, could result in AppleScript code being fired off and Apple events being returned to be handled by handlers that perform the actions.

One application could parasitically drive another application to do things the first application can't do. A word processor could call on a spelling checker whenever a document is closed. A drawing program could call up a 3-D application to add depth to a drawing, rotate it, project a shadow, and send it back as a 2-D drawing again.

A user could set up a batch-job script that starts several applications and pipes a document through them one at a time, making each application massage the document a little before passing it on.

And so on.

Since AppleScript is an extensible language with a replaceable editor, it can present itself to users in a variety of guises, with varying degrees of access to its power. UserLand Frontier and CE Software QuicKeys can both drive AppleScript, but at quite different levels of difficulty and power. Since any (Apple-Script-supporting) application can drive the AppleScript extension and add to the AppleScript vocabulary (there are other ways to extend the vocabulary as well), AppleScript has no fixed interface and no fixed set of capabilities.

Commands

AppleScript is a verbose and easy-going language that will be familiar to HyperTalk scripters. It lets you slip in "the"s and other syntactic sugar to kid yourself into thinking it's English. There are the standard sorts of compound statements: if and repeat, but also some less-common compounds: considering and ignoring. These are chiefly used by AppleScript to control string comparisons; see Example 1.

There's also the tell statement, which is crucial since it specifies the destination for the command(s). It can do so with more precision than implied above, specifying rather minuscule elements of objects as command destinations. The forms in Example 2 do the same thing.

Who actually does the work in these cases is a question. Normally, it's the application that handles the actual command that the AppleScript extension passes it, although there's a situation in which a command may be directed to an application object and handled by a subroutine in a script. There are really only four kinds of AppleScript commands: application commands (handled by applications), AppleScript commands (handled by the AppleScript extension), scripting additions (handled by their own extensions), and user-defined commands (subroutines handled by handlers within a script).

Objects

Commands are sent to and operate on objects. Syntactically, the objects will typically appear as the parameters of commands. There are system objects, which AppleScript knows about (applications, zones, machines) and application objects, which individual applications know about (words and characters, for example). More precisely, all AppleScript-supporting applications in a category like word processing will know about the same basic collection of text-related classes of objects, such as lines, words, characters, and insertion points. Individual applications may know about other classes of objects that no other applications know about. A running application, or any script driving it, will operate on instances of these classes.

Commands typically tell objects to perform actions or return values. Objects can have properties (text objects can have, for example, size, style, font, alignment, and color properties) and elements (elements of a word-processing document would include words, characters, and so on).

There are also what are called "script objects." Like system objects and application objects, script objects can have properties and can respond to commands. These are objects defined within scripts; see Example 3. Elsewhere you could send a command to this object: tell duck to quack.

If applications can define their own commands and objects, how do you know what vocabulary an application supports? A language with an unknown vocabulary is not very useful. The answer is that every AppleScript-supporting application is expected to make its supported vocabulary explicit in a standard way, and the script editor knows how to display this dictionary for you in a convenient and readable manner.

Values

AppleScript supports the more obvious value classes: Boolean, class, integer, real, string, list, and date. It also supports record (defined as a collection of parameter-value pairs), data (uninterpreted binary data), constant (application-defined special values), and reference (a reference to an object).

The use of references to identify application objects is heavily used in AppleScript. You create a value of class reference using the ref operator (also known as a reference to). The value of the first of the references in Example 4(a) is a string; the value of the second is a structure within AppleScript that points to that string.

AppleScript works hard to make sense of references. There are many reference forms that it recognizes. Scripts can easily refer to the first or last or nth or nth-from-last or middle element of any organized object, an arbitrary element, every element, elements in a range, or elements satisfying some Boolean condition. Objects can also be referred to by name, ID, or relative position with respect to another object, where that is meaningful. Also, AppleScript uses tell statements and default object values to make sense of incomplete references, if possible. If you look at Example 2, in every example the object reference for the delete command was incomplete, and AppleScript completed it from default objects specified by one or more enclosing tell commands.

The property reference form is another form of reference. Its syntax is straightforward: name of window 1 references the name property of that window object. Application objects and system objects and script objects can have properties; application objects and script objects can define their own properties: footStyle of myDuck.

It's also possible to refer to properties of records (a record being a collection of properties and their values): phone of {name: "Bill", phone: "555-9861"}.

Variables, like properties, hold values. There are two commands for assigning values to variables: copy and set. These behave identically, copying the source value to the destination variable, except in the cases of lists, records, and script objects. When the source object is one of these types, the set command creates a variable that shares data with the original object, so that when the original changes, the assigned variable changes with it.

There are other differences between properties and variables as value holders. Variables are assumed local unless declared global; the opposite is true for properties, which are persistent. For example, in Example 4(b), the second line shows the syntax for defining a property. The property barks retains its last value when you close the script. It is only reset to 0 when the script is recompiled.

Subroutines

You can write your own handlers, called "subroutines." These can be recursive, as in Example 5(a). They can use positional parameters, as above, or labeled parameters. The labeled parameter form supports two dozen special labels and can be defined using either the word on or the word to. Example 5(b) provides a few examples of first lines of labeled parameter subroutine definitions.

One special kind of handler is the command handler, written in a script but attached to a particular application object. This means that you can assign behavior to objects created by applications. Applications must be written to support attachment in order for this to work.

Another special handler is the try handler, which is used for error handling. Bracketing a block of code by the try command lets you specify error handlers for error messages that may be returned in that block.

AppleScript and Apps

So what does it mean for an application to support AppleScript? There are, first of all, two directions of support: There's the ability to send AppleScript commands and the ability to respond to them. Then there are the three Apple-defined levels of support. An application can be scriptable, meaning that it responds to AppleScript commands. (There are levels of support within this, because there are several classes of commands: required, core, application-suite commands, and an application's own special commands.) An application can be recordable, meaning that users can create scripts by turning on a watch-me mode and performing normal application actions. That's a step beyond scriptable. And an application can be attachable, meaning that users can write scripts, perhaps in the script editor, and attach them to objects that the application knows about, so that the behavior of those objects is controlled by the user's script.

But the big question is, who will support AppleScript? We'll find out over the course of the next year. It'll be interesting to watch.

Example 1: Controlling string comparisons using AppleScript

ignoring case and punctuation
  if "It's" = "its" then beep 1
  considering punctuation
    if "It's" = "its" then beep 2
  end considering
end ignoring

Example 2: The tell statement

tell application "Text Editor"
  delete character 1 of word 4 of line 6 of document "Ridley 3/18"
end tell
tell word 4 of line 6 of document "Ridley 3/18" of application "Text Editor"
  delete character 1
end tell
tell application "Text Editor"
  tell document "Ridley 3/18"
    tell line 6
      tell word 4
        delete character 1
      end tell
    end tell
  end tell
end tell

Example 3: Script objects

script duck
  bill:     yellow
  footCount:    2
  footStyle:    webbed
  to quack
    open bill
    beep
    close bill
  end quack
end duck

Example 4: (a) AppleScript values; (b)

(a)
word 6 of line 4
ref word 6 of line 4
(b)
script dog
  barks: 0
  set barks to barks + 1
  display dialog barks
end dog

Example 5: (a) recursive handlers, or subroutines; (b) labeled parameter subroutine definitions.

(a)
on factorial (n)
  if n = 1 then
    return n
  else
    return factorial (n-1)
  end if
end factorial
(b)
on FahrenheitToCentigrade of temp
to searchFiles of filesToSearch for theString
to lowerCase of charsInString above minChar given sortOrder

Copyright © 1993, Dr. Dobb's Journal