Scriptability: A Bare-Bones Introduction
Kevin C. Killion, Stone House Systems, Inc.



Scriptability:
A Bare-Bones Introduction

by Kevin C. Killion

© Copyright, 1996, Stone House Systems, Inc. All Rights Reserved.

Set aside your IM, AE Registry, and tech notes for the moment. Starting fresh, let's review the bare essentials you need to start making your app scriptable. Simple things should be simple, complex things should be possible, so let's ignore the rampant excesses of the AppleScript spec for now. We will introduce scripting with a simplified explanation of the concepts, and follow this up with implementation of a minimal, but very useful initial set of scripting functions. As an added bonus, we'll consider how to design your implementation for accessibility from BASIC as well as AppleScript.

Download the complete RTF document (stuffed binary)


Contents





Introduction

An unprecedented number of new sets of APIs have been added to MacOS in the last few years. As of this writing, some 35+ different add-on technologies are provided by Apple in the MacOS SDK. Some of these technologies give developers the ability to implement exciting new features with a minimum of new coding effort. The Thread Manager, for example, enables an entirely different approach for handling long processing times without delaying the user, and it does so with only a few simple API calls. QuickTime has been another unqualified hit: simple to implement, and yet an incredible crowd-pleaser with customers.

Other new technologies released by Apple have not been so successful. Take AOCE: Many of the features it made possible were hard to explain, or passed minimal real benefits through to the end user. Nonetheless, documentation was vast and dense. It was hardly a surprise (to many developers at least) when Apple would say (in the January 5, 1996 issue of MacWeek) that it was "confusing to use" and that the entire API would be thrown out.

We might make an attempt to categorize MacOS add-on APIs with a cost/benefit approach. Every developer will have a different view, of course, depending on his or her own interests and target markets. For myself, I'd view the MacOS APIs this way:

This leads to a kind of technological triage. Some technologies are no-brainers: if an application can make any use at all of QuickTime, it should. Other toolkits are easy to dismiss: the documentation and methods for QuickDraw GX, for example, are so turgid and involved that only a high-end graphics program is likely to benefit.

For many developers, AppleScript belongs firmly in the upper right quadrant: high benefit at a high cost. AppleScript offers tantalizing power and flexibility, and could lead an application into a collaborative world in which the whole is much greater than the sum of the parts. On the other hand, the task of making an app scriptable appears daunting and formidable. Dave Mark says, "Many people (myself included) are intimidated the first time they try to take on the Apple Event Manager. In many ways, this task is as complex as when you first learned to work with the Mac Toolbox."

Why Implement Scripting?

To start taking AppleScript seriously, we must first reinvigorate ourselves with the potential that AppleScript offers.

  1. At the very least, AppleScript serves as a macro language that is far more powerful than most any scratch-baking scripting facility that a developer is likely to dream up on his or her own.

  2. The principal language offered by Apple's scripting facilities leaves a powerful first impression: Who would not be charmed by a programming language that looks so much like English?

  3. AppleScript crosses application borders. It goes where no application-based macro language can go: into another application.

  4. It adds panache: scriptability is a recognizable marketing advantage. Even if a customer or prospect has no clue about what AppleScript looks like or what it's for, they probably perceive that having it is a good thing.

    Beyond these, AppleScript provides the developer with additional benefits, which may not be as obvious:

  5. Scriptability invests the user with a sense of personal participation, and that can only be good for long-term commitment to a product. Just ask your six-year-old: the favorite toys are the ones that offer lots of play value.

  6. That sense of empowerment and entitlement not only adds your customer to your development team, but to your sales team as well. An "enlisted" customer can generate powerful positive word-of-mouth advertising.

  7. AppleScript is an avenue for addressing outlier requests. I have often had key customers request some unusual feature as their "gotta have" top priority, even though other customers would see such a feature as worthless menu clutter. AppleScript provides the perfect solution.

  8. Scripting reduces conflicting pressures on development, by giving users the power to implement new features themselves.

  9. Ofttimes, merely having that power is satisfying to customers, even if it isn't used immediately! One of the great strengths of Microsoft Excel is the perception that one can do anything with it, no matter how convoluted; whether someone would ever actually undertake that effort is a question that is easily overlooked.

  10. Scripting can be a profit center. A customers may place high value on having a custom report or process developed, made possible and practical by AppleScript.


So, Why Not Implement Scripting?

It isn't too hard to understand why developers have ignored AppleScript in droves, despite the inherent appeal of what it could deliver.

Technical Obstacles to Scripting

  1. The sheet vastness of the documentation: "Inside Macintosh: Interapplication Communication" is a thousand pages of intense and cross-dependent text. The "registry", without which we are warned not to proceed, adds more desk ballast.

  2. No high-level interface: One might argue that the paucity of simplified, high-level API calls may be the single greatest technical deficiency of the Mac toolkit in general versus its modern competition. AppleScript is no exception. In its simplest description, a scripting facility should provide a developer with the ability to expose the "objects" of an application and make them available for manipulation by an external process. This kind of basic implementation should be possible with a reduced set of simplified calls. (It is true that some level of scripting support has been built into some class libraries. However, since each such class library is developed independently, there is little portability between them. Of course, other class libraries and procedural projects are left out in the cold.)

  3. Lack of a "start here" subset: Implementing the four "required" AppleEvent handlers gets developers on a roll. At the point, development comes to a screeching stop in the face of the massive documentation. There is no sense given that there is any valid subset of calls and features with which to get started on implementing the juicier parts of scripting. (As we will see, it is possible to start with a decent, manageable subset.)

  4. Minimal sample code: As of this writing, the developer CDs offer only three examples of implementing scripting in a program.

  5. Spaghetti sample code: "Quill" was an early piece of sample code that was not only unfinished (pretty scary) but which suggested unbelievable complexity nonetheless.

  6. Aren't-I-wonderful: In technical writing, awe of the AppleScript architecture too often gets in the way of clear exposition of what a developer needs to do. Rather than identifying specific development steps, writing has often dwelled on terminology and theory.

  7. The hidden power of the AppleScript language: The syntax of the language does not appear (on first blush, at least) to have much of a one-for-one correspondence with the code that must be built in the app. Thus, the purpose of each code element remains elusive. ("Uh, you mean I have to 'install' an 'object accessor'? What for?")

  8. Disinterest in the AppleScript language: Some developers think AppleScript is too cutesy to take seriously. Other developers (myself included) feel that AppleScript has more serious problems as well.

  9. The 'aete' resource: This messy and convoluted resource is the linchpin of scripting, and dealing with it cannot be avoided.


Marketing Obstacles to Scripting

  1. The promise of collaborative applications only works if there is someone to talk to. With the conspicuous and embarrassing exception of Microsoft applications, a comparatively small portion of MacOS apps offer serious scriptability.

  2. The promise of a common scripting language is often not borne out. The president of one well-known Mac publisher says that "There isn't just one AppleScript. There is AppleScript for Excel, AppleScript for ClarisWorks, and so on. Each variation sports its own terminology and set of available verbs. Even products within a category, such as word processors, can have markedly differing scripting dictionaries."

  3. There is no guarantee that the effort involved in adding scriptability will pay out in terms of customer satisfaction. Part of this is becauseÍ

  4. The portion of Mac users who do any kind of AppleScripting today is vanishingly small. In my own survey of my major customers, who in turn have thousands of end users and a number of technical support groups, I failed to find a lead on even a single person who had any experience at all with AppleScript.

  5. The challenge of mastering AppleScript: With few books for support and no similar prior experience to build upon (with the exception of HyperTalk for a few folks), learning AppleScript is not simple. Although many users already have experience with BASIC, which in many respects is a far more powerful and flexible language, this experience doesn't help much when learning AppleScript's rather unique approach. Moreover, the allure of an "English" syntax soon dissolves into the reality of stilted, run-on sentences with peculiar structures.

  6. Dictionary shock: From my own experience, I have seen how customers react with a glazed expression when first seeing scripting dictionaries.

  7. Support costs: While scriptability may eventually deliver benefits to you and your customers, the initial learning hump might impact your short-range support costs. When users run into trouble writing AppleScript statements, who else do they have to call but you? With so few sources for AppleScript information or expertise (contrasted with BASIC, for example), there is simply nowhere else to turn.


Does This Sound Like You?

The growth of AppleScript and scripting has languished over that cost benefit issue: The benefits are irrefutable, but the cost of development is often seen as insurmountable. But if implementation can be made manageable and affordable, the scriptability becomes a much more viable option.

AppleScript divides Mac developers into two camps: the mystical and the mystified. Some developers have bought into scripting completely, and are aglow with fervor for the Object Model, the OSL, the terminology resources and the natural language interface. The rest of this article is not for this enlightened group.

This article is intended for those who remain perplexed by scripting. I intend to provide a "start here" approach for adding scripting to your application. If your perspective of scripting matches the outline presented in the text up to now, keep reading.


"Scripting" versus "AppleScript"

A key step is to understand the difference between "scripting" and "AppleScript". When you make your app scriptable, you are doing nothing that has anything inherently to do with AppleScript! AppleScript is merely a language provided to access the scripting facilities of the system and applications.

The features and strengths of AppleScript often bear little relation to the code you write to support scripting. Moreover, there is nothing to prevent other languages from being developed to drive scripting. In fact, provision for such languages is built into the Mac scripting toolkits. Some folks love AppleScript, but not everyone. If you're in the latter group, then consider scripting anyway and hope that alternative scripting languages emerge.


Objects and Properties

Every application deals with a set of "things". An accounting system has clients, accounts, transactions, reports and many other elements. A model of some industrial process may have inputs, processing stages and outputs. A transportation management system may have parcels, containerized shipments, sources, destinations, warehouses and vehicles. (I'm bored to tears with Apple's fixation on text and pictures, so I like to use other examples.)

Of course, there is nothing novel about the concept of objects and properties. As programmers, we are used to the distinction between methods and instance variables in OOP. Even users have some sense of the distinction; for 12 years now they have dealt with screen "objects" and manipulated their "properties" through dialogs and commands.

(The original Xerox workstations crystallized this philosophy with a specific "Props" button on the keyboard. The user selected a screen object, and pressed Props to access a dialog to change the properties of an object. As time goes on, the brilliance of the Xerox design becomes even more evident. Much of what we admire in OpenDoc already was fundamental on the Xerox workstations of 1981!)

It's important to note that a crucial way of identifying the objects in an application is to tell whether they can be created or removed. A shipment can be removed from our transportation system, but we cannot "remove" its color. The shipment is an object, color is one of that object's properties.

Of the tasks we envision enabling through scripting, an enormous portion involve reading and setting the values of these properties. With just these two actions, the user can create custom reports, transfer selected information to other applications, tweak settings to improve some measure of performance, and do many other things.


The Role of Context

Six-year-old on phone: "Do you want to come and play at my house?"
Classmate: "Where's your house?"
Six-year-old (puzzled): "Here!"

User: "Tell me the weight of the shipment."
Computer: "Which shipment?"
User: "This one!"

In an interactive, live program, direct pointing can identify the "thing" to be discussed. But that obviously isn't sufficient for a batch-style, hands-off operation. Moreover, there are plenty of situations in which a script may wish to talk to elements that aren't even part of the visible interface.

Consequently, "this one" is not a sufficient direction to a target. To identify this target, a descriptive address is needed, and it's natural to do that in terms of the context within a larger entity.

Parent of classmate: "Where's your house?"
Parent of six-year-old: "We're the third house on the block, on the block nearest the school, on the east side of the town."

Computer: "Which shipment?"
User: "The one stamped '47', in Bin C, on the upper shelf, of the truck that left at 10 am"

These addresses identify the object of interest with a series of successively larger contexts. Note that each context is an object in itself: house, block, side of town, the town, bins, shelves, trucks ˝ these are the objects, the things, of their respective applications. And each step in establishing context describes how one object is referenced with respect to another.


"Context" vs. "Containment"

Mac scripting documentation describes this relationship as containment, which is a very useful and descriptive name. However, we must quickly make note of what containment is not:

  1. "Containment" is not exclusive: a cell in a spreadsheet is contained by a row but it's also contained by a column.

  2. "Containment" does not necessarily mean physical enclosure: In our transportation program, a roadside weigh station might be used as a context within which to identify the tenth truck weighed today. The weigh station does not "contain" the truck in any English sense of the word, but it does provide a context for identification.

  3. "Containment" does not necessarily correspond to subclassing in a class library: "box" and "letter" may be subclasses of "parcel" within our app, but we could conceivable desire to identify and handle boxes and letters in entirely different ways in our user scripting facility.

  4. "Containment" does not necessarily involve visible entities on the screen. More generally, scripting certainly should be available even if there are no visible entities on screen at all; we might desire to "operate" a faceless application.

  5. "Containment" does not necessarily correspond to supervisory relationships in a class library: Clicking on a client name may involve a class library's handling of a large pane as supervising the subpane containing the client name, and a document supervising the large pane. For our scripting purposes, "containment" may instead refer to the client's position on a list of clients, which isn't involved in the class bureaucracy at all.


So, while "containment" is a handy term in our scripting efforts, be aware that, more than anything else, it is merely a way to establish context for identification of objects.


Are Properties "Contained"?

A parcel does not "contain" a weight, a spreadsheet cell does not "contain" a font size, and a client does not "contain" a name. Nonetheless, when we understand that the role of "containment" references in scripting has more to do with context and identification than it does with enclosure, certain similarities do emerge.

"Change the color." "Of what?" "Of the third row."

In this example, reference to the object identifies, in a very real sense, the context of the value to be changed. Understanding this subtlety will make it easier to understand the syntactic similarity of property references and object references in both AppleScript and other scripting languages. Later on, this will also help to explain why the same user routine winds up being useful for identification ("resolution") of both objects and properties.


A Make-Believe Scripting Language

To get our heads around where scripting is positioned, and why AppleScript sentences look like they do, let's do a thought experiment.

Let's imagine that AppleScript doesn't exist. Then imagine that we were given the task of designing a scripting language from scratch. Further suppose that we buy into the concepts of:

- objects as the entities within an application
- objects have properties to be examined and set
- objects are identified within contexts of other objects

Now, what structures might we place into our new language to implement these concepts?

To illustrate the task, let's suppose we have a simple tabular document, with a number of table rows that can be colored individually.


First, it seems natural to allow language variables to refer to properties of the objects in our application. A first try at devising such a scheme in BASIC, for example, might involve calling some subroutine that retrieves the needed value.

mycolor = GetValue("color") ' "color" is a pre-arranged keyword


We immediately see that we can't talk about color without talking about of what, so we also need some way to denote context. Let's next imagine another available subroutine called GetObject that provides a reference to an object, when we ask for a specific entry on a list. We then could expand our GetValue routine to use this reference as a context. We then have:

thisrow = GetObject("row", 4) ' get the fourth row 
                              ' ("row" is a pre-arranged keyword)
mycolor = GetValue(thisrow, "color") ' get its color


We're making progress, but to get that row, we need to reference its context. A row only has meaning within the context of a document. In turn, the document is only one of several documents that may be open, but the context there isn't as clear. We might decide to denote this "top level" or "global" context with "NIL". Then:

thisdoc = GetObject(NIL, "doc", 1) ' get the first document
thisrow = GetObject(thisdoc, "row", 4) 
                         ' from the doc, get the fourth row
mycolor = GetValue(thisrow, "color") ' from this row, get its color


In a very simplistic form, note that all of our references constitute a series of pairwise operations:

- within the "global" context, identify a document
- within the document context, identify a row
- within the row context, reference the color property

Now let's play language designer. Rather than clutter our programming with lots and lots of subroutine calls, let's devise some new syntax elements to streamline the coding.

Since everything we've done involves these pairs, we might quickly devise a syntax that combines the two elements of context and a "contained" object or property. A simple dot may serve this purpose:

{ context} .{ contained item}

For convenience, we might allow skipping any top-level "NIL" reference. With these changes, the previous listing could then be simplified as:

thisdoc = doc(1) ' get the first document
thisrow = thisdoc.row(4) ' from the doc, get the fourth row
mycolor = thisrow.color ' from this row, get its color


Again, the pairwise nature of these references is evident. But for the user's convenience, we could allow these statements to be strung together, so that the intermediate variables aren't required:

mycolor = doc(1).row(4).color ' get color of 4th row of doc #1


While this new, crisp syntax looks more complex, it's easy to imagine that processing of it still relies on those good ol' pairwise steps. (And, lo and behold, what we have just devised is not an imaginary language! With small variations, this is the syntax used in Microsoft's Visual Basic family of languages.)

Now let's take a look at another language, namely AppleScript:

get the color of row 4 of document 1


Again, it's a long statement, but the fundamental notion is that processing this will involve a series of ever-finer context resolutions.


Prerequisites for Coding

Although this is intended to be a "bare bones" introduction, we will require a few prerequisites before getting into the coding.

First, we will assume that you have already implemented the four "required" AppleEvents, namely, Open Application, Open Documents, Print Documents, and Quit Application. Supporting these events are already well-documented, and are comparatively straightforward. If by some chance you have not implemented the required events, start there. They are very well described, complete with code examples, in "Inside Macintosh: Interapplication Communication", pages 4-11 through 4-20.

As a by-product of implementing the required events, we'll also assume you have some notion of the idea of AppleEvents acting as containers to transport "clumps" of data. Since you needed to "install" your "handlers" for these required events, we'll also assume that you're familiar with those buzzwords.

To initialize the scripting facilities for your program, you must call the AEObjectInit routine in your application's initialization:

err := AEObjectInit;


Of course, you should always check the result of this and all other calls that return an error parameter. In this text, we will skip that for clarity. However, error handling is implemented in the listing accompanying this article.


Set Data and Get Data

We are now ready to implement the first two crucial "handlers" to enable scripting. The "Set Data" and "Get Data" AppleEvents are the messengers that allow a user to retrieve and change properties of application objects. They are the heart of scripting.

In concept, their function is very simple. Let's start with the Set Data event (see figure, below). After you have created and installed your handler routine for Set Data, your application is prepared to receive these events. Each Set Data event contains two parameters: First, the parameter identified as "keyDirectObject" contains a reference to the property that is to be changed. Second, a parameter labelled "keyAEData" contains the new value for the property.


Figure 1: The Set Data event


Our code for this event involves retrieving these two parameters, figuring out what internal variable in our program corresponds to the property that has been specified, and then changing that variable's value. (For a complete implementation example of code used in this paper, see the listing, "Scripting.p". This sample listing is partially based on Apple's "Quill" example.)

Handling the Get Data event is similar (see figure, below). In this case, the AppleEvent that has been received has only one parameter, a reference to the object and property of interest. This is exactly the same as for the Set Data event. The big difference is that when we access the corresponding variable in our program, we retrieve its existing value, and send it back to the calling application. We do this by packaging up this value as a parameter and attaching it to the reply AppleEvent.


Figure 2: The Get Data event


So far, it hardly sounds difficult at all, does it?

We tell the system that we have Set Data and Get Data handlers by installing them as follows in our application's initialization area:

err := AEInstallEventHandler(kAECoreSuite, kAESetData, 
	@HandleSetData, 0, FALSE)
err := AEInstallEventHandler(kAECoreSuite, kAEGetData, 
	@HandleGetData, 0, FALSE)


Like all AppleEvent handlers, the calling sequence for our routines consists of an AppleEvent being received, a possible reply event, and a reference constant:

FUNCTION HandleSetData (theAppleEvent: AppleEvent; 
                        reply: AppleEvent; 
                        refCon: LongInt): OSErr;


The reference constant makes it possible to handle several different kinds of events with the same handler. For example, you could conceivably have a single routine that handles both Set Data and Get Data by installing the same routine for both functions, and telling them apart with that constant. However, for code clarity, I'd recommend ignoring the refCon and coding each handler separately.

Within each of these handlers, we retrieve the direct object (the object and property to be accessed) as follows:

err := AEGetParamDesc(theAppleEvent, keyDirectObject, 
	typeWildCard, myDirObj);


We now have the reference to the property to be retrieved or set, contained in the variable myDirObj. This variable is declared as type AEDesc, which has mystical trappings to it but which really is nothing more than a handle with a 4-character label (a DescType) on it:

TYPE 
	AEDesc = RECORD
		descriptorType: DescType;
		dataHandle: Handle;
		END;


Ah, but what's in that handle? For initial scripting efforts, it is sufficient to state that this handle contains an encoded representation of the entire specification. For our figures, that specification is "font of row 1 of document 1", in a compact binary form. (No, it is not the literal text of this specification.)

This is where the fun starts. It might be possible to parse the contents of this handle ourselves, determining the structure and successive "containments" implied. Mull that over for a while, and think about how you would do that. Besides the effort involved in decoding and parsing the message, we would have to do a great deal of bookkeeping along the way.

Now recall our earlier lengthy discussion about context and containment. We stressed that every step of the resolution involved a pairing of a context and an object or property to be isolated from within that context. What if we could reduce the problem of identifying that direct object into a series of simpler requests of the context/object form?

HARD: decipher "font of row 1 of document 1"
SIMPLER: Do each of the following:
- find document 1
- find row 1 within it
- find the font of that

This process of breaking down a complex specifier into a reference of a specific object or property is called "resolution". The Mac scripting architecture makes resolution much easier by handling the bookkeeping chore for us. To do this, we are given a function called AEResolve. We call AEResolve, and hand it the complex specification. The system will make additional calls back to our application, consisting of a series of simpler resolution questions, like those listed immediately above. When AEResolve returns, we are given a reference (of our own design and in terms of the application's internals) that uniquely identifies the target object or property.

The sheer power of this approach is that AppleScript and the scripting engines within the system contain a great deal of leverage to manipulate complex expressions on their own, requiring our application to deal with only these simple context/object pairing questions.

If you've been paying close attention, you've noticed two leaps of faith in the above discussion. First, we said that the system calls back to the application to ask these pairing questions, but we have not yet said how. Second, we said that AEResolve returns a reference to some internal variable that is to be retrieved or set, but we have not yet said where that comes from. We will cover those omissions in the next sections.

For now, let's continue to operate on faith. We call AEResolve, and we retrieve a spec for the application internal to be accessed. This is another AEDesc, called newDesc in this sample. newDesc's handle contains a structure that is our own device for referring to goodies in the application. We extract this with a simple BlockMove, placing the result in a structured variable we call myToken:

err := AEResolve(myDirObj, kAEIDoMinimum, newDesc);
BlockMove(newDesc.dataHandle^, @myToken, myTokenSize);


The few statements we've examined so far are identical in both Set Data and Get Data. Now, these two handlers will diverge. Consider what each must do:

Set Data:
1. Retrieve the value specified for the application internals
2. Attach it to the outgoing "reply" AppleEvent

Get Data:
1. Extract the desired new value from the incoming AppleEvent
2. Set the correct application internal to this value

There is an obvious symmetry to setting and getting data elements. Wherever possible, it's desirable to handle such similar functions with the same code, to avoid them getting out of sync and to avoid largely redundant code. For example, many programmers like to have a single I/O routine for both reading and saving documents.

We will do the same here, bottlenecking both our Set Data and Get Data procedures through one routine. In each case, the bottleneck routine is passed our "token" reference, and the relevant AppleEvent (either the incoming or outgoing one).

For our Set Data handler, we give our handler the token and the incoming AppleEvent:

VAR 
	direction: (doSet, doGet);

{ ...} 
direction := doSet;
err := DoTransferProperty(direction, myToken, theAppleEvent);

The Get Data handler is passed the token and the outgoing reply AppleEvent:

VAR 
	direction: (doSet, doGet);

{ ...} 
direction := doGet;
err := DoTransferProperty(direction, myToken, reply);


We are now very close to having our application take its first steps towards scriptability. All we need to do now in our code is fill in the steps we've taken on faith. These missing steps are:

1) Devise a means to uniquely identify variables in our system, using "tokens".
2) Provide the callback routine that AEResolve will use to ask the context/object questions, returning a specific token.
3) Implement the DoTransferProperty routine.

Besides these code changes, there is one more step: we must create an 'aete' resource.


Defining "Tokens"

Much of the existing document and sample code for scripting correctly points out that the contents and usage of tokens are totally up to you. Unfortunately, they often then go on to define very convoluted token strategies for sake of illustration.

I have found that to get started with scripting, a very straightforward token design is really quite sufficient.

The tokens passed between the system and your application consist of AEDesc's which, as we've said, are nothing more than handles with four-character labels. What you put in that label, and what you put in that handle are up to you.

It is not inconceivable that a very simple application may be able to define all of its objects and their properties with a coding that uses the label alone, and leaves the handle at NIL. Suppose we have only the app itself, and a single tabular document with no more than 99 rows and columns (identify them as "A", "R", and "C"). Further suppose that no object has more than 9 unique properties, and we will let "0" represent the object itself. Then, we could use codings like this:

R021 = the first property of row 2
R020 = row 2 itself
A001 = property #1 of the application (some global setting)


That just shows how simple a token strategy may be. A more realistic token design will create a structured record identifying the object or property. In the accompanying listing, this is the token definition used:

TYPE
	MyTokenType = RECORD
		myTokenCode: DescType; 
		theObject: CObject;
		subReference: longint;
		isAProperty: Boolean;
		propertyCode: DescType;
		END;


Here is how I use each of these fields in this record:

myTokenCode: this is just a simple code to identify the kind of object being discussed. The code used here is only meaningful to the application itself, of course, so it can be anything.

theObject: this is a handy place to store the handle containing the most relevant object. For example, from TCL, this field could be set to the actual object of concern.

subReference: I defined this field for convenient referral of application elements which are not "objects" in the class hierarchy themselves. For example, I may have an object "client" which possesses an array of values representing billing dates. With theObject referring to Smith Company and subReference 3, I can resolve down to the third billing date for this client. If subReference=0, the token refers to the object itself.

isAProperty: This same token structure is used for both an object and the property it refers to. For an object, isAProperty=FALSE, for a property, it's TRUE.

propertyCode: another four-character code, of my own device, identifying the needed property, if isAProperty=TRUE.

I have found that this simple token design satisfies all of the needs I have encountered for basic scripting capability. Remember -- the specifics of the design are totally up to you.


Move on to part 2...

Return to contents...


Copyright ©1996 Kevin C. Killion, Stone House Systems, Inc.

Web page by Bill Catambay
Updated: 22-July-1997