AppleScript
|
AppleScript is a scripting language devised by Apple Computer, and built into Mac OS. More generally, AppleScript is the word used to designate the Mac OS scripting interface, which is meant to operate in parallel with the graphical user interface.
Contents |
History
The AppleScript project was an outgrowth of the HyperCard project. HyperCard had an English language-based scripting language called HyperTalk which could be used for embedding logic and behavior into a HyperCard stack. Apple engineers recognized that a similar type of scripting language could be designed to be used with any application, and the AppleScript project was born.
AppleScript required extensive upgrades to the Mac OS to work (see below for details). Much of the underlying technology changes were made part of the massive System 7 release, notably the key Apple Events concept. As such, Apple Events were vying for developer attention along with many other new technologies introduced at the same time (balloon help, publish and subscribe, etc.). Apple Events were likely the most difficult of the System 7 technologies to implement, requiring a re-write of major portions of the "low level" code in an application. Apple's own application framework, MacApp, did not directly support Apple Events for some time.
AppleScript itself did not emerge until years later. Originally Apple had an agreement with Dave Winer to use his Frontier product as a scripting engine, but for a variety of reasons the agreement fell apart. Apple announced their own scripting engine to developers in January 1992, and publicly that May. However it wasn't until 1994 that the system was finally shipped. Up to this point there was little reason for developers to support Apple Events, so when AppleScript was initially released there was little software that supported it. It was not until the mid-1990s that there was any sort of expectation of support, one of the first to do so in a widespread fashion was Microsoft Office, which remains the high point of "scriptability" to this day.
The AppleScript project was killed and restarted several times over its history before release. After released and a few bug patches, AppleScript was once again ignored within Apple. However by 2000 the market had finally found a strong niche for the system, using it to automate complex pre-printing tasks that required several pieces of software to complete. After a chorus of "why aren't you updating AppleScript" led Apple to realize the product was not only in-use, but essential to one of their major markets, it has gone on to receive major attention for the first time in a decade.
The move to Mac OS X and its Cocoa frameworks has made AppleScript come into its own. Cocoa applications are scriptable with no effort on the part of the developer, and "well scriptable" for the cost of writing a text file. AppleScript Studio, released with Mac OS X 10.2, allows users to build entire applications using AppleScript and Cocoa objects.
Basic concepts
AppleScript was designed to be used primarily as a scripting language to control other applications. As such, it depends on the ability to send messages between applications. Unfortunately the Mac OS application runtime did not include such a feature, and had only a rudimentary event model that could specify a small and fixed number of low-level events like "key was pressed" or "mouse was clicked". The application itself was responsible for decoding these low-level events into meaningful high-level user actions, like "select cut from the Edit menu". In many cases the code for the two was mixed together, the code handling a mouse click simply running the code directly for simple tasks like Quit.
AppleScript, however, was based entirely on high-level user actions, and needed a considerably richer messaging system. To support this need the AppleScript team dramatically extended the Mac OS with the addition of Apple Events. Apple Events were based on the existing event model, using unused bit masks to identify a new "high-level event" (Apple Events being one type of high-level event). Additional information about the event would then be sent in via the same channel; the application was responsible for knowing that the following events were actually data. From a programming standpoint the already-complex event handling system became considerably more complex, typically with one "chain" of code handling the original low-level events, and a second chain to handle the new high-level ones when they were recognized.
In order to provide full access to the functionality of your application, the two event handlers would both have to call into the same work functions in code. However, in many cases developers had already tied those functions directly into the event chain. Disentangling the code was often annoyingly difficult. Additionally it was important that the work methods be able to run without any UI being present, but in an even larger number of cases developers used the UI as storage -- for instance, it was common to keep track of a list of windows, instead of documents. Adding Apple Event support often meant an almost complete re-write of the application, not to add new functionality or the new event handling chain, but to cleanly separate the program and its objects from the events that called it, a task Apple referred to as factoring. This represented a major expansion on the original Macintosh programming model, and it was some time before the system was fully supported, either by Apple or 3rd party developers.
Several technical problems made the model as a whole difficult to manage. Notably, the cooperative multitasking employed on the Macintosh meant that either the script engine or the target application could be running at any one point in time. In order to control another application, the script engine had to decode part of a script, turn those instructions into a stream of Apple Events, place them on the application's event stack one at a time, and then call an expensive context switch to give time to the application. Once the application took over the process was reversed, pulling apart the messages, decoding them into internal actions, and then switching back once the code had run. There was no real way to make the system run any faster, spending more time in the script engine improved script performance, but at the cost of stealing time from the application. For this reason (among others) AppleScript on "classic" Mac OS is excruciatingly slow. To address this problem, at least to some degree, the script engine could be run inside the target application, which Apple referred to as attachability. This avoided the context switches, although not the problem of wrapping messages and then unwrapping them again in order to be processed. Attachability was not a widely used solution, even though it was not all that difficult to add such support to an application.
Under the Mac OS X system the Apple Events model is dramatically simpler to implement, at least for Cocoa applications. For one, Cocoa applications are fully factored in exactly the fashion Apple Events require. The runtime is based on a UI that sends messages, in the form of strings, into a single dispatch queue. and from there calls your work methods. Unlike the "classic" Mac OS, under Cocoa one writes the work methods only, and the frameworks handle the events of any sort, that is, all programs are properly factored automatically. Another major advantage is that Cocoa objects are presented to the outside world (other applications and even machines) in a standardized format that anyone can examine directly. Under this system AppleScript is much "thinner"; the script engine decodes the script, translates object names from human-readable to their internal format, and then calls those methods on the target application directly. Combined with a full preemptive multitasking kernel, AppleScript under Mac OS X is considerably faster and almost trivial to implement.
The natural language metaphor
Apple Events are a way to send messages into applications, AppleScript is a particular language designed to send Apple Events. In keeping with the Mac OS tradition of ease-of-use, the AppleScript language is designed on the natural language metaphor, just as the graphical user interface is designed on the desktop metaphor. AppleScript programs are generally readable by anyone, and editable by most. The language is based largely on HyperCard's HyperTalk language, extended to refer not only to the HyperCard world of cards and stacks, but theoretically to any document. To this end, the AppleScript team introduced the Apple Event Object Model (AEOM), which specified the objects any particular application "knew".
Generally, AEOM defined a number of objects, like "document" or "paragraph", and the actions that could be done to them, like "cut" and "close". The system also defined ways to refer to properties of objects, so one could refer to the "third paragraph of the document 'Good Day'", or the "color of the last word of the front window". AEOM uses an application dictionary to associate the Apple Events with human-readable terms, allowing the translation back and forth between human-readable AppleScript and bytecode Apple Events. To discover what elements of a program are scriptable, dictionaries for supported applications may be viewed. (In the Xcode and Script Editor applications, this is under File → Open Dictionary.)
To designate which application is meant to be the target of such a message, AppleScript uses a "tell" construct:
tell application "Microsoft Word" to quit
The concept of an object hierarchy is expressed using nested prepositional phrases:
pixel 7 of row 3 of TIFF image "my bitmap"
which in another programming language might be expressed as sequential function calls:
getTIFF("my bitmap").getRow(3).getPixel(7);
Many applications do not store pointers directly into anything but the lowest level objects; for instance, a word processor almost certainly has an object that returns all of the characters in a document, but may not have objects representing pages, paragraphs or documents as a whole. Since the authors intended AppleScript to be added to existing applications without too much redesign, they also provided a second way to write accessors known as iterators. Using iterators requires the developer only to keep track of the current location of an object, and provide a method to find the "next one". For instance, paragraph objects could be supported with a single pointer to the current paragraph, and a method that finds the next one by looking for a return character. Using this system, the application would decode the above script internally into something like:
getTIFF("my bitmap"), loop(3) { getNextRow, loop(7) { getNextPixel } }
Of course this method of access is much slower. Remember that the only channel for communications between the script and application is the event queue: using iterators as in the above example would require 11 "get the next..." calls into the application, each one causing an expensive context switch. This is yet another reason for poor performance.
Note the similarities between the AEOM model and the considerably more recent DOM system used in XML. Both decompose a document into a hierarchy of objects, and offer the programmer a standardized iterative method to access the contents. Differences between the systems lie primarily in the user-level syntax, with AppleScript introducing a number of different ways to refer to any particular object. For instance, AppleScript includes syntax for ordinal counting, "the first paragraph", as well as cardinal, "paragraph one". Likewise the numbers themselves can be referred to as text or numerically, "five", "fifth" and "5" are all supported. This syntax is entirely external, however, the translation between these terms in the script and the internal "object(5)" format is direct, and already done in AppleScript for its own purposes.
Under Mac OS X the utility of the AEOM is debatable. It would seem that the AppleScript system could replace the AEOM with the DOM largely "in place" without anyone noticing. The syntax of the scripts would be identical, although the name-mapping files might have to be changed. Yet with the DOM any number of existing 3rd party tools could be used to access document content without modification. Some of these tools are tremendously powerful, and the tight integration of the DOM and web-based technologies represents an obvious "win".
AppleScript on its own
AppleScript need not depend on other applications. For very simple tasks, AppleScript can be used for self-contained applets. For instance, the code:
set pix to 72 set answer to text returned of (display dialog "Enter in the number of inches" default answer "1") display dialog answer & "in = " & (answer * pix) & "px"
Brings up a dialog box requesting a number of inches from the user. This number is then converted to pixels on a system that uses 72 pixels per inch. A second dialog box is brought up displaying the result.
The problem with this is that, unlike other scripting languages like Perl, AppleScript is very poor in built-in facilities, so such scripts are of limited usefulness and often run quite slowly. The lack of native functionality outside of application commands has caused long-time scripters to spend enormous amounts of time creating "vanilla" algorithms for tasks that are trivial in other high-level scripting languages, such as deleting an item from a list or changing the case of a string. The main power of AppleScript derives crucially from its ability to orchestrate the abilities of scriptable applications.
AppleScript Studio
With Mac OS X, AppleScript has grown well beyond its humble beginnings. AppleScript Studio is a development environment, which comes free with Mac OS X, which uses AppleScript as the primary programming language, in conjunction with the Cocoa-based ProjectBuilder framework used to construct graphical user interfaces.
With Mac OS X v10.3 ("Panther") AppleScript Studio and ProjectBuilder have been rolled into the Xcode integrated development environment. AppleScript Studio lets you build a user interface in a drag-and-drop fashion (similar to Visual Basic) and then "run" the user interface to see what the forms and menus looks like.
Panther also comes with Script Editor, which is a minimalist editor for compiling and running AppleScripts. A nice feature of this editor is that if you right-click (control-click) on the editing area you get a pop-up menu with a large range of options for script fragments to paste into your code. This is an excellent feature for people learning to write AppleScript. From that menu you can also open up the directory where these scripts are kept, and have a look at them. You can also add your own scripts (although you need to restart Script Editor for these changes to show up in the pop up menu. Using these features, you can turn Script Editor into a minimalist, but extensible, Java IDE, for example.
AppleScript dialects
For a short time, AppleScript supported the idea of multiple dialects, which included English, French, Japanese (kanji), Japanese (romaji), and Italian. Terminology was made available for each dialect, such that the AppleScript compiler could compile and decompile scripts written in any dialect to any other dialect. While the project was a technical success, few application developers provided terminology in multiple languages, which meant that scripts were a confusing mix of languages in most cases. Additionally, changing the words and not the grammar left the scripts in a bizarre form similar to engrish. Support for multiple dialects was dropped by Apple in Mac OS 8.5.
AppleScript language essentials
- basic data types are string, integer, real, list, record and object
- different types can coexist in a list, including nested lists
- records are lists of key-value pairs
- standard control flow with if / else constructions and repeat loops
- variables are instantiated when used, and are not strictly typed
- script objects can encapsulate methods and data
- script objects can inherit behavior from a parent script
- 'tell' construct used to identify target of message
- applications can define terminology at runtime
- runtime compiling possible using 'run script' construct
- persistence possible using 'store script' and 'load script'
Applets and Droplets
It was possible to save an AppleScript script as an executable file or applet. If the script had a run handler:
on run ... end run
then this would be invoked when the user double-clicked the applet.
If, furthermore, the script had an open handler:
on open of SomeItems .... end open
then the applet became a droplet: if the user dragged and dropped some filesystem items on the executable, it would be launched and the open handler would be invoked with a list of references to those items.
Open Scripting Architecture
An important aspect of the AppleScript implementation was the Open Scripting Architecture (OSA). Apple did not want to antagonize the vendors of existing third-party scripting/automation products such as QuicKeys and UserLand Frontier, so it created the OSA as a way for these other languages to be handled on an equal status with AppleScript. Thus, AppleScript was implemented as a scripting component, and the basic specs for interfacing such components to the OSA were public, allowing other developers to add their own such components to the system. Public client APIs for loading, saving and compiling scripts would work the same for all such components, which also meant that applets and droplets could hold scripts in any of those scripting languages.
One of the most interesting features of the architecture, the ability to write language syntax extensions, also turned out to be a significant liability of the language. Because an extension (called a "scripting addition", or "osax" for "Open Scripting Architecture Extension") could change the basic grammar of AppleScript, it became difficult to predict how the AppleScript compiler would behave. Indeed, one of the most common complaints about AppleScript is the complexity of its syntax. However, the basic language independent of these extensions is quite simple and comparable to the complexity of other programming languages, while retaining its natural language metaphor. At one time, Apple developers had considered referring to osax as "osex". In plural, they are often joking called osaxen, often capitalized as "OSAXen". The coiner's of the term, Jon Pugh and Donald Olson consider the proper spelling to use all lowercase letters, "osax" and "osaxen".
See also
- Sal Soghoian — AppleScript product manager
- William Cook (http://www.cs.utexas.edu/users/wcook/), Donn Denman, Warren Harris, Kurt Piersol, Jens Alfke (http://www.mooseyard.com/Jens/) — AppleScript designers
Books
- Computer-Books.us (http://www.computer-books.us/applescript.php) — Online AppleScript books