Serious First Steps In UserTalk Scripting
by Matt Neuburg
Author of the book Frontier: The Definitive Guide
We are almost ready to put our knowledge to work for us, developing a serious and useful UserTalk program. But before we do this, we should know how to enlarge our knowledge independently of this tutorial.Clearly a tutorial like this can't come close to covering every UserTalk verb; so how will you know, as you develop your own programs, what verbs exist and what can be done with them?
Also, in this chapter we will say something about working with scripts in general, since they have a number of features we have not yet studied explicitly.
The primary reference source for information about built-in UserTalk keywords and verbs remains DocServer. Originally, DocServer was an independent application which interacted closely with Frontier to function as a sort of online reference.Now, DocServer is being rewritten as a set of downloadable Web pages:
http://www.scripting.com/docServer/default.htmlThis provides a valuable compendium, explaining what each parameter of a verb needs to be, and often giving useful examples and warnings. However, it lacks the wonderful integration with Frontier that the old DocServer used to have; and various users have contributed third-party utility scripts, in an effort to restore some semblance of that integration.
Even so, DocServer has some limitations, of which the most significant is that it simply lists the verbs in what amounts to alphabetical order. This is great if you know the name of a verb in advance and want to know some details about it, but if you have in mind an action and want to know what verb you might use to make it happen, chasing around through DocServer can be very frustrating.
To help with this problem, I wrote workspace.aLittleHelp, a Frontier outline providing a general reference arranged in how-to order, and later turned it into a book, Frontier: The Definitive Guide. (Hmm, could this be the first book based on a Frontier outline?) Unfortunately, neither has yet been updated for Frontier 5; but they might prove helpful anyway. (See links at my home page.)
There is also a large body of official Frontier documentation in the form of Web pages. These are somewhat oddly ordered, and in spots are incomplete or outdated, but they are being actively updated, and are definitely very useful.I find that the best way to bring order to such online docs is to download the Web pages and read them from your hard disk with a browser. If you grab a shareware utility such as UltraFind from the nets, you can quickly search the Web pages as textfiles.
Another important place to get information about UserTalk verbs is from the database itself -- naturally enough, since a verb is a database entry.There's a shortcut for finding out more about a database entry whose name you see in front of you: command-double-click the name of a database entry to jump to it.
That includes verbs, and many important verbs are scripts, so you can learn a lot by studying them. For example, earlier we learned of a verb which is actually a script for writing a string to textfile; scripts of this sort exemplify important techniques that provide the reader with valuable training.
You can learn quite a bit, too, by browsing the database generally and just reading scripts at random, trying to understand them.
Part of what helps you in studying a script is its comments which explain what it is doing. Use of comments is an art, but it is widely agreed among programmers that it is important to provide comments, both for others and for yourself, later, when you are trying to reconstruct why you wrote a program the way you did.In UserTalk, comments are of two types: either the whole line is a comment, or the line starts out as UserTalk code and then has a comment appended to it.
To append a comment to a line of UserTalk, type option-backslash [alt-keypad-0171] (the left guillemot or "chevron" character); this character and everything after it will be considered a comment. Or, if that's too much trouble, just type two forward slashes in a row.
To make a whole line a comment, select the line or any text within it and hit command-backslash [ctl-backslash]; this toggles the status of the line between comment and code. Or, when you create a new line, instead of hitting return, hit shift-return [shift-enter] and the new line will be a comment.
One of the advantages of having a whole line be a comment is that all lines in the bundle indented from it will also be comments, so a multi-line comment can be collapsed into one.
As you develop a program, you are sure to make mistakes, or to have areas of doubt where you're not sure the program is doing what you expect it to.One technique for dealing with this situation is to insert, at key locations, calls to verbs that display information, such as "msg" or the various dialog verbs, to give you an indication of what part of the program is executing at any given moment.
But you can also watch the movement of the program in detailed slow-motion, and obtain information about the value of any variable at any given moment, by running the program in Debug mode.
To learn about Debug mode, let's use it with workspace.fileLister. Bring the workspace.fileLister window to the front and press the Debug button.
The window enters Debug mode, with a new set of buttons across the top; and the first executable line (the call to the fileLister handler, namely the last line of the script) is selected, ready to execute. (Lines are selected before they have actually executed.) Now, if you hit the Go button, the whole script will execute, and the window leaves Debug mode. Try it.
The Go button executes until the end of the script or until a line previously marked as a "breakpoint" is encountered. To mark a line as a breakpoint, command-option-click its triangle [ctl-alt-click], or select the line and hit command-k [ctl-k]. You don't have to be in Debug mode to do this. Try it with the "fileloop" line: the triangle turns into a hand indicating that this is a place to stop.
Press the Debug button and then the Go button; the script executes normally until it reaches the "fileloop" line, at which point it stops with that line selected.
We are now paused in the middle of executing our script. Any time you're stopped in a script in Debug mode, you can examine (and, if you wish, change) the value of local variables.
There are several ways to do this, but the simplest is to select the name of the variable and then hit the Lookup button. Try this with the variable "whatFolder": select its name, perhaps by double-clicking the occurrence of its name in the "local" line where it is declared, and hit the Lookup button. A window opens that looks something like this:
This window comes from inside Frontier's compiler stack. What it actually shows is all the variables being tracked at this level of execution, which, as the name of the parent subtable tells us, is level 2. Why is it level 2? Because level 1, the top level of our script, is simply the call to the fileLister() handler; once we start performing that call and are inside the fileLister() handler, we are one level deeper than that.
If you like, you can also look at the level 1 variables now (not that there's anything particular to see).
At first you may not understand everything you see in the stack window, but you can see that the stuff in the level 2 subtable makes sense: the variable "whatFolder" has received the name of whatever folder you selected when the dialog came up, and other variables have no value because at this point in the script nothing has happened to give them one.
Now do this: adjust the windows so you can see both the stack window and workspace.fileLister. Bring workspace.fileLister to the front and hit the Go button. At first, it seems nothing has happened; but look at the level 2 subtable and you'll see it has changed: "theFile" now has a value.
When you pressed Go, the "fileloop" was executed once (we looped until we hit the breakpoint again); thus, "theFile" was assigned its first file pathname, and this now appears in the compiler stack window.
Press Go repeatedly to see "theFile" change until the routine ends; or, if you like, remove the breakpoint from the fileloop line and press Go to continue execution to the end of the routine. (Or, if you're sick of the whole thing, hit the Kill button to abort execution and leave Debug mode.)
So now we've seen that you can use the compiler stack window not only to examine variable values but also to watch them change as the script proceeds.
By the way, it does no harm to leave breakpoints in your scripts; they have no effect when the script is run in the ordinary way. Breakpoints are meaningful only when you're in Debug mode. There are breakpoints lying around all over the database, but they don't affect ordinary execution.
The other thing Debug mode is most frequently used for is to step, one command at a time, through the script.This is a little tricky because the Step button alone may not do what you want. Try it: enter Debug mode, remove any breakpoints you've previously set, and hit the Step button. The script appears to execute normally, right to the end. That's because the Step button moves to the next command at the same level, and there is no next command at this level; once we've performed the call to the fileLister() handler, we're done.
To step from the call to the fileLister() handler to the first command within the fileLister() handler, you have to use the In button. So try it: enter Debug mode, and hit the In button. Now you can repeatedly hit the Step button to watch the routine progress one command at a time. It gets a little boring when it starts going round and round the "fileloop", so you can hit Go if you've had enough.
You don't have to be right on a call to a handler or verb in order to use the In button. Basically, the In button works like the Go button, except that as soon as we come to a command at a deeper level, we pause there; similarly, the Out button works like the Go button, except that we pause as soon as we come to a command at a shallower level.
When you first attempt to run a script, Frontier will catch any linguistic ("syntax") errors in your use of UserTalk before starting to execute, and will put up a dialog telling you if it finds one.You can then press the Go To button to cause a spot near the error to be selected, though in a few cases it can still be hard to find.
Once your script is running, there can still be errors (called "runtime" errors); again, Frontier will stop and put up an error dialog, and at this point you can press the Go To button to see the line where the error occurred.
You can also hold down the Go To button to get a popup menu showing you (and letting you navigate) the current nest of embedded handler calls; this can be helpful in complex situations for understanding what the program was up to when the error happened.
Remember, you're either in Debug mode or you're not; if you start a script running in the ordinary way, you can't suddenly drop into Debug mode and take advantage of its wonderful features. If you encounter a runtime error and you want to debug in Debug mode, you need to begin all over again, this time starting the initial script with the Debug button. Thanks to breakpoints, it's no trouble to start in Debug mode and then fast-forward to where the problem seems to be.
Let's now review the chief ways in which UserTalk commands can be caused to execute. We're already familiar with two: open a script and press the Run button; and, call a script from another script. What others are there?Here are some of the main ones.
You can select a script as an entry in the table it lives in and choose Main: Run Selection (command-slash [ctl-slash]). But this calls the script with no parameters, so it only works if the script requires no parameters.
You can type (or paste) a command into the Quick Script window and hit the Run button. (The Quick Script window can be summoned with command-semicolon [ctl-semicolon].)
In the Quick Script window, you have left the realm of outlines, so you can't enter a multi-line script in the same way as you do an outline. To see this, go into workspace.fileLister, select the "fileloop" line, and hit Copy.
Now go to Quick Script and Paste. Notice the transformation. The lineation and indentation here are just for ease of reading; what actually shows the structure of the lines is the use of curly braces and semicolons.
Basically, every indented bundle must have curly braces around it, and every command that doesn't have a curly brace after it must have a semicolon after it. Bear this in mind if you write scripts of any length in the Quick Script window.
By the way, nothing stops you from using the same braces-and-semicolon notation here and there in an outline. For example, it would be perfectly legal, and more to the taste of many UserTalk stylists, to rewrite workspace.fileLister like this:
on fileLister () local (whatFolder) if not file.getFolderDialog("Pick a folder to list:", @whatFolder) {return} whatFolder = string.replaceAll (whatFolder, "\\\\", "\\") new (outlineType, @workspace.fileOutline); target.set (@workspace.fileOutline) local (theFile) fileloop (theFile in whatFolder, 1) op.insert(theFile, down) edit (@workspace.fileOutline) fileLister ()Finally, you can attach a script to a menu item and choose that menu item. This technique is particularly valuable for utility scripts that you write. Indeed, there is a menu with your initials which you are actually expected to modify.
Menu items are modified through a menubar edit window, which actually edits something called a menubar object in the datase. The quickest way to edit a menubar is to option-choose [alt-choose] a menu-item. This brings up the menubar edit window, and now you can see an item's script by double-clicking its triangle.
Or, back in the real menubar (at the top of the screen), shift-option-choose [shift-alt-choose] a menu item to see its script instantly. (This feature was originally my suggestion, so now that UserLand has gone to the trouble of implementing it, you'd better use it a lot!)
If I want to access a utility script from a menu item, I often put the utility script in the workspace table and then make the menu item's script a simple call to the utility script; that way, later editing of the utility script is easier because I don't have to open the menu item's script at all.
All text is by Matt Neuburg, phd, matt@tidbits.com.
For information about the book Frontier: The Definitive Guide, see my home page:
http://www.tidbits.com/matt
All text copyright Matt Neuburg, 1997 and 1998. ALL RIGHTS RESERVED.
No one else has any right to copy or reproduce in
any form, including electronic. You may download this material but you may not post it for others to
see or distribute it to others without explicit permission from the author.
Downloadable versions at http://www.ojai.net/matt/downloads/scriptingTutorial.hqx and http://www.ojai.net/matt/downloads/scriptingTutorial.zip.
Please do not confuse this tutorial with a certain other Frontier 5 tutorial based upon my earlier work.
This page created with
Frontier, 2/11/2000; 6:59:03 PM.