This page uses CSS, and renders properly in the latest versions of Microsoft Internet Explorer and Mozilla.
This is Frontier: The Definitive Guide by Matt Neuburg, unabridged and unaltered from the January 1998 printing.
It deals with the freeware Frontier 4.2.3; for commercial Frontier 8, look here, and for the inexpensive Radio UserLand, look here. See my Web site for information.
Those wishing an offline or printed copy may purchase the book, which is still in print.
Copyright 1998 O'Reilly & Associates, Inc. ALL RIGHTS RESERVED. Reprinted with permission.
TOP UP: UserTalk Basics NEXT: Data Manipulation

13

Running and Debugging Scripts

A script is just a series of instructions until it actualizes its potential by being executed, also known as running the script. Furthermore, before a UserTalk script can be run, it is always compiled. This chapter describes how compilation and running of scripts takes place. And, since bugs in your script are proverbially as inevitable as death and taxes, you're going to want to isolate and mend those bugs as they crop up; this you can do through Frontier's excellent debugging environment, which this chapter also describes. Finally, we discuss some additional ways in which Frontier helps you figure out what your script does and how to make it do what you want it to.

Compiling

Before a script can be run or debugged, it must be compiled. Compilation involves translation of the script into tokenized form (p-code) by way of the tables in system.compiler . Compilation is extremely rapid; on most machines, the time involved is typically too small to measure accurately (one- or two-sixtieths of a second).

You compile a script by pressing the Compile button in the script's edit window. If you make changes in the window and then press the edit window's Run button or Debug button without pressing Compile, Frontier compiles implicitly and proceeds. However, the compiled version generated by such implicit compilation is not the same as the compiled version generated by explicit compilation by way of the Compile button.

It is the explicit version that is run when a script is called as a verb. For this reason, if you make changes in a script window and then close the window, Frontier will offer to perform explicit compilation.1

The chief reason for this behavior is that a script can be executed without the user initiating it directly - for example, agent scripts run automatically in the background, and scripts can be triggered by commands from other applications. Scripts can call other scripts, so it is perfectly possible that a script object could be called while you are editing it. The script at that moment might be unfinished, buggy or dangerous. Therefore, it is the explicitly compiled version that runs; thus, there will be no danger as long as the script's current state has never been explicitly compiled.

So the existence of an implicitly and an explicitly compiled version of a script is important and useful. But it can also be confusing. If you modify a script, test it with the Run button, and then execute another script which calls it, the modifications may mysteriously fail to work - because you've forgotten to compile the script explicitly. A particularly insidious variant can arise when you press Run in a script that calls itself: initially, the implicitly compiled version runs, but when it calls itself, the script calls the explicitly compiled version. Then, when you try to track down the problem in Debug mode, debugging itself appears to have broken; the text version of the script is out of synch with the explicitly compiled version that is actually being executed.

The moral is: press the Compile button! On the other hand, if the script is one which might be called automatically, don't press the Compile button until the script is in acceptable shape (see Chapter 27, Agents and Hooks).

alt
NOTE Menu item scripts have no Compile button, because the implicitly compiled version is the only version needed - such a script will never be called in an unexpected automatic way.
alt

The Kernel

The token tables that are located at system.compiler.kernel and system.com-piler.language are called the kernel. A verb is truly "built in" to Frontier if it has a token here, because it is implemented in the Frontier application. All other verbs are implemented as scripts.

The programmer should never call the kernel explicitly. Pre-evaluation takes care of routing calls to the verbs in system.compiler.language.builtins (see Chapter 9, Special Evaluation), and scripts are provided which route calls to the other kernel verbs. So, for example, when you use pack(), pre-evaluation translates it to system.compiler.language.builtins.pack; when you use msg(), the reference is resolved to system.verbs.globals.msg and the script there calls the kernel.

Calls to the kernel happen primarily by means of the kernel() verb. The programmer should never use this verb! It works in unusual ways. It must be always the only command in its script. And it has the remarkable property that the parameters to its caller are passed on automatically. For instance, if you examine system.verbs.globals.msg(), you see that it receives a string parameter, but apparently fails to pass it on when it calls the kernel; that's because it's passing it on by a different mechanism.2

Compile Errors

When Frontier compiles a script, it puts up an error dialog if a syntax error prevents compilation. There is a Go To button in this dialog, and if you press it (or hit Enter), Frontier will place the insertion point after the error in the script.

When Frontier does this, it is thinking like a machine, so its idea of where the error is may differ from yours. You may have to hunt back a little to find what a human being would think of as the culprit. For example, if you try to compile the following:

locals (x = 6)

when the error message appears and you press Go To, Frontier places the insertion point after the =, because it is illegal to use this symbol in the parameter list of a verb call. The fact is, of course, that you never intended a verb call: the real problem is that you typed locals for local. Or, if you try to compile:

local (x = target.get()
msg(x)

then when the error message appears and you press Go To, Frontier places the insertion point in the second line, after the left parenthesis; but the problem is in the first line, where a right parenthesis has been omitted.

Script Object Verbs

Several verbs do compilation-related things to script objects as a whole.

To compile a script:

script.compile (addrScriptObject)

Identical to pressing the Compile button in the script object's edit window.

To clear the memory occupied by a script's explicitly compiled version:

script.uncompile (addrScriptObject)

The script will take longer to start executing when next called, but some memory is freed up. Chiefly a method of housekeeping; for an example, see suites.samples.basicStuff.windowFormatter() (discussed in Chapter 24, Windows).

To clear a script's text version:

script.removeSource (addrScriptObject)

The script object becomes henceforward permanently uneditable - indeed, it ceases to be a script object, becoming instead a "compiled code" object - but it can still be called. Useful chiefly for security purposes, as a way to distribute a script no one can read. Since you can't read it either, be sure to keep a normal copy. Called by Remove Source Code in the Script menu.

To learn or specify a script's OSA dialect:

script.getLanguage (addrScriptObject)
script.setLanguage (addrScriptObject, languageString)

Identical to using the language popup menu at the bottom of the script object's edit window. OSA dialects are typically "UserTalk" or "AppleScript". On scripts in non-UserTalk dialects, see Chapter 33, AppleScript.

Running a Script

The following are the ways in which script execution may be initiated:3

  • The user presses the Run button in a script edit window.
  • This causes evaluation of the surface-level commands in the script, which, as we saw in Chapter 5, Handlers and Parameters, may have a different effect from calling the script as a verb.
  • The user enters a UserTalk expression into the Quick Script window and presses its Run button (or hits Enter).
  • The Quick Script window is summoned by choosing Quick Script from the Open menu. It can be resized, but the result area cannot be made to contain more than one line.
  • Quick Script serves as a convenient command-line interface to Frontier. Its Run button causes evaluation of its contents as a UserTalk expression; thus, if the window contains a verb call, the verb will be executed. Another possibility is to enter a script directly into Quick Script; but note that if more than one command is to be entered, the semicolon-and-curly-braces syntax appropriate to a text environment must be used (Chapter 4, What a UserTalk Script Is Like).
  • The result of evaluating the contents of the Quick Script window appears automatically in the result field at the bottom of the window (after first being passed through displayString() - see Chapter 10, Datatypes). If you wish to capture the result of a Quick Script you must include commands to do so in the code itself.
  • The user chooses a menu item (or types its keyboard shortcut) to which a menu item script is attached.
  • This is how all Frontier menus (except for File, Edit, and Window) are implemented. Every menu item has a script; choosing the menu item runs that script. The script runs as when its Run button is pressed, executing its top-level commands; menu item scripts don't have eponymous handlers. The creation and editing of menu item scripts is discussed in Chapter 26, Menus and Suites.
  • The user chooses Run Selection from the Main menu.
  • What happens when the user does this depends upon what is selected at the time.4
  • In a table, if a string object is selected, the string is evaluated; if a script object is selected, the script is called with no parameters.5 The result is displayed both in the result field at the bottom of the table window and in the Main Window.6
  • In a wptext, the currently selected text is evaluated as a UserTalk expression. The result is displayed in the Main Window.7
  • In a menubar, the selected menu item's script is run, just as if the menu item had been chosen from the menu.
  • In a script or outline, the entirety of the current line is evaluated as a UserTalk expression. This works even if the current line is itself a comment. The result of the evaluation is displayed as a comment indented below the originally selected line. If there was already a comment in that position, it is overwritten. The database is full of "one-liners," lines in scripts or outlines intended to be selected and evaluated with Run Selection for testing purposes; see, for example, suites.webBrowser.examples.oneLiners, or the last line of suites.toys.commentDelete.
  • Frontier automatically calls an agent script.
  • Agent scripts are called automatically at intervals which they themselves dictate. See Chapter 27, Agents and Hooks.
  • Frontier automatically calls a "hook" script.
  • Hook scripts are called automatically in response to certain events. See Chapter 27.
  • A desktop script is opened from the Finder.
  • See Chapter 29, Import/Export.
  • Frontier receives a command from another application.
  • See Chapter 34, Driving Frontier from Outside.

Debug Mode

Debug mode is entered by pressing a script edit window's Debug button. Thereupon, Frontier will propose to treat the script as if you had pressed its Run button, but it pauses with the first executable surface-level command selected and awaits further instructions.

A new set of buttons has now appeared at the top of the window, and you proceed by pressing one of them. (I'll describe what they do in a moment.) Frontier remains in Debug mode until either the script terminates naturally or you exit it with the Kill button. The buttons then return to their normal state.

In the heat of debugging, you might start making changes to a script. You should not continue debugging a script which you have accidentally changed, because the script's text may no longer correspond to the code Frontier is executing, and you'll be confused. Exit Debug mode and start debugging again.

During debugging, there may be several script edit windows, and you might press a button in a window that is not active. It might appear that nothing has happened, which can be confusing. What's going on is that the first click in a non-active window just makes the window active; you thought you were pressing the button, but you weren't.

Breakpoints and the Execution Path

A breakpoint is a line of a script that is specially marked: it starts with a "stop" hand icon instead of a triangle. The execution path is the complete train of commands that Frontier actually executes during a debugging session as one script calls another.8

In Debug mode, any time the execution path passes through a breakpointed line, Frontier will pause, open, and bring frontmost the script's edit window, and select the breakpointed line, without yet having executed it.

The presence of a breakpoint has no effect when you are not in Debug mode. The database is full of breakpoints left there by the developers of the scripts, but they do not affect normal execution.

To toggle a line's status between normal command and breakpoint, Command-click its triangle or hand, or choose Toggle Breakpoint from the Script menu. You can set a breakpoint at any time, not just in Debug mode. In Debug mode, it's fine to set and remove breakpoints "on the fly"; for example, if you set a breakpoint in a loop after a couple of iterations of the loop, Frontier will pause there on the next iteration (and then you can remove it if you like).

alt
WARNING An on line, an else line, a bundle line, or a line indicating one of the possible values in a case is not an executable line. Neither is a local or a line subordinate to a local, unless assignment takes place there. You can breakpoint such a line, but Frontier will never pause there. You can breakpoint a comment, but this will have no effect, and the hand will not be visible, until the comment is turned to a command line.
alt

The Debug Mode Buttons

We now describe the behavior of the Debug mode buttons. The following two rules should be borne in mind:

  • 1. Whenever Debug mode pauses, it selects the command it is about to execute.
  • 2. Breakpoints take priority over everything else; in Debug mode, Frontier always pauses before executing a breakpointed line.

The Go button causes the script to continue execution without pausing until either all execution terminates (at which point Frontier leaves Debug mode) or a breakpoint is encountered. The Go button changes to Stop during execution; the Stop button causes a pause.

The Step button causes execution of the currently selected line, pausing before the next line to be executed. If the current line contains a verb call, the execution path enters and returns from the called script or handler without pausing (unless a breakpoint is encountered).

The In button causes the script to continue execution without pausing (unless a breakpoint is encountered) until a verb call is executed (Frontier pauses at the first executable line of the called handler or script) or until the current handler or script terminates.

The Out button causes the script to continue execution without pausing (unless a breakpoint is encountered) until the current handler or script terminates.

The Follow button causes the script to continue execution without pausing (unless a breakpoint is encountered), and each line of any open script through which the execution path passes is selected as it is executed. This makes it possible to watch the execution path, but no windows are opened, nor are windows brought to the front as the execution path passes through them; also, the speed is rather fast, and cannot be changed. The Go button changes to Stop during execution; the Stop button causes a pause.

The Kill button aborts execution and brings Frontier out of Debug mode. You can use Command-period instead.

Variable Values

In Debug mode, the value of local variables can be consulted. This is actually just a consequence of the fact that it is possible to pause. During execution, as Frontier encounters each new scope, it creates a subtable in system.compiler. stack and maintains there the names and values of any local variables created in that scope; when that scope goes out of existence, so does the corresponding subtable. In Debug mode, you can examine these subtables.9

The most convenient way to examine local variable values in Debug mode is to push the Lookup button. If the Lookup button is pressed while a line as a whole is selected, the subtable for that scope opens. If the Lookup button is pressed while the name of a variable is selected, the subtable containing that variable opens; or you can get the same effect by Command-double-clicking the name.

Navigating the subtables by hand is also possible. Jump to system.compiler. stack and find the subtable containing the variable you wish to examine. The name of a subtable tells which scope within what script it represents. It may take some hunting to find a desired variable.

You can take advantage of a pause to examine other parts of the database as well. If the Lookup button is pressed while the name of a database object is selected, that object opens or is revealed; or you can get the same effect by Command-double-clicking the name.

Runtime Errors

When a runtime error is not trapped by a try, Frontier puts up an error dialog.

Even when Frontier is not in Debug mode, this error dialog can be used to help track down the error. Clicking the Go To button (or hitting Enter) opens the script window and puts the selection point at the line that caused the error. Or, holding down the Go To button pops up a menu which shows the calling chain; you can use this information to help figure out what was going on when the error occurred, as well as to navigate to the various handlers and scripts named in the menu.

If Frontier is in Debug mode, exactly the same thing happens, but the compiler's variable stack is not emptied, so you can also examine variable values. However, you can't continue debugging; after all, an error has occurred. So after you've finished examining things, you must exit Debug mode.

Since the compiler stack is emptied when a runtime error occurs not in Debug mode, a useful strategy is then to run the same routine in Debug mode so that variables can be examined when the error occurs again.

alt
TIP The text of the error dialog can be copied to the clipboard by choosing Copy when it is frontmost.
alt

Runtime errors are by nature often pesky to track down, even with all the help Frontier gives you. It can be useful to modify the code temporarily to be more informative. A judicious sprinkling of try...else structures and msg() or dialog.notify() calls can help you work out what was happening when the error occurred.

Desperation

To abort a running operation, hit Command-period. You may have to hit it several times or hold it down.

If you think the database may have been damaged, choose Revert from the File menu, or quit without saving the database. If you're still worried about its integrity, you can check whether Frontier has lost the internal consistency of any tables by choosing UserLand Testing from the Suites menu and then Validate Tables from the Test menu that appears. But this won't tell you if you have overwritten or deleted some important value.

The best policy is to back up the database from time to time. Personally, I also keep a spare copy of a completely clean root downloaded from UserLand's Web site. That way, if I suspect the integrity of the database, I can migrate my personal stuff into the clean root. These matters are all discussed in Chapter 29.

Getting Help from DocServer

DocServer is an online help program included with the Frontier distribution. It contains documentation for two kinds of UserTalk features: (1) punctuation, operators, and keywords; and (2) verbs. It's very useful, though, as of this writing, the information it contains has some slight drawbacks. Not every built-in verb is documented, and important utility verbs and UCMDs are omitted. The documentation for some keywords fails to describe their full syntax. The documentation is sometimes unnecessarily obscure, and occasionally just plain wrong.

Also, the DocServer application is hard to navigate: the Index menu does not include every category of verb; no menu lists other verbs in the present category, and if you don't know the exact name of a verb, there are no good facilities for finding it.

Nonetheless, Frontier and DocServer work tightly together to provide a handy reference, and it is extremely useful to be conversant with the ways in which they do so:

  • In Frontier, if you are looking at the name of a verb in an edit window, Control-double-clicking that name takes you to the DocServer documentation for that verb.10
  • In Frontier, if you are looking at the name of a verb in a script or outline edit window, Control-Option-double-clicking that name copies the verb's parameter information from DocServer to just after the name. Now you have placeholders for the expected parameters and can substitute actual values.
  • In Frontier, choosing DocServer from the Main menu (or hitting Command-=) brings up a dialog into which you can type the name of a verb to jump to its documentation. (You can do the same thing in DocServer by choosing Jump To Verb from the File menu.)
  • In DocServer, choosing Paste Into Frontier from the Frontier menu switches to Frontier and pastes at the insertion point of the frontmost window the name and parameter information of the verb you were just looking at.
  • In DocServer, choosing Open Glue Script from the Frontier menu switches to Frontier and opens the script edit window of the verb you were just viewing.
  • In Frontier, an outline at suites.docs.verbList lists the verbs (along with their parameters) in DocServer. This outline is a splendid way to browse DocServer; remember, Control-double-clicking a verb name takes you to its DocServer documentation. To see the outline, jump to it, or, from DocServer, choose Open Verb List from the Frontier menu. To rebuild the list, so that it reflects the current state of DocServer, choose DocServer Verb List from the Suites menu, and choose Build Whole List from the Frontier menu that appears.

Getting Help from the Database

Exploration of the database is a primary debugging (and learning!) technique. If a verb is implemented as a script, you can study that script. You can jump to any database entry whose name you see in an edit window by Command-double-clicking that name, and you can jump to any database entry whose name you know by choosing Jump from the Open menu. In both cases, partial object references are resolved for you.


1. When Frontier starts up, a script has no explicit compiled version, so Frontier will make one automatically the first time the script is called. Therefore, if you make changes to a script just after startup and close the script window, no compile prompt appears.

2. Also known as "magic."

3. Calling a script object from another script is not included, because by definition the caller is already somehow running; the question is how that might ultimately have been caused.

4. The behavior of Run Selection is implemented at system.verbs.globals.runSelection.

5. If the script requires parameters, an error results.

6. This overwrites anything displayed in the Main Window during the evaluation; you may wish to modify this behavior. A MacBird card (a binary object of type 'CARD') can also be run by selecting it in a table and choosing Run Selection.

7. This overwrites anything displayed in the Main Window during the evaluation; you may wish to modify this behavior.

8. For purposes of debugging, the execution path is never considered to pass through any script which consists solely of a call to kernel().

9. You can change the values of variables in these tables, but you shouldn't: it's dangerous. As Doug Baron has said, "If Frontier supported locked windows these would be the first ones we'd lock."

10. This feature, and the next, are implemented by system.misc.control2click, which is automatically called when you Control-double-click in Frontier.


TOP UP: UserTalk Basics NEXT: Data Manipulation

This is Frontier: The Definitive Guide by Matt Neuburg, unabridged and unaltered from the January 1998 printing.
It deals with the freeware Frontier 4.2.3; for commercial Frontier 8, look here, and for the inexpensive Radio UserLand, look here. See my Web site for information.
Those wishing an offline or printed copy may purchase the book, which is still in print.
Copyright 1998 O'Reilly & Associates, Inc. ALL RIGHTS RESERVED. Reprinted with permission.