This chapter discusses the process of creating, storing, running and debugging UserTalk scripts.
Where do I put my Scripts?
Like everything else in the Frontier environment, scripts reside in the Object Database. Although your scripts can be put almost anywhere, it's best to stay organized right from the start.
The Workspace table is an excellent place to store scripts that you're working on. There's a command in the Open menu that opens this table; it even has a single keystroke, Cmd-T.
We recommend putting most your scripts here, unless there is a compelling reason to do otherwise.
The Frontier website contains additional documentation, including step-by-step instructions for taking advantage of specific features. If the instructions are specific about where to put your scripts, please follow them. For example, many of the new web publishing features depend on scripts that you add to a specific table within the "user" table.
Structuring a Script
The most important thing to remember about the structure of a script in a script editing window is that indentation is significant. Just as the Pascal language uses begin/end and the C language uses curly braces, UserTalk uses outline indentation to delimit the beginning and end of various blocks and structures. Loops and conditional branches should be arranged so that the lines you expect to execute repeatedly (using "loop", "for" or "while") or under some defined circumstance (with "if" or "case") are indented under the line that defines the structure. Each of these language elements is explained in DocServer.
A script is a special type of outline in Frontier. Whether reviewing your own script or reading someone else's, you can collapse groups of lines to provide a high-level view of the script's structure. You can also expand certain portions that you wish to view in detail. The ability to expand and collapse is one of the most powerful aspects of using an outline as a scripting tool.
In addition to the standard loops & branches, UserTalk has many language elements that make use of indentation, including comments, "local", "with" and "bundle." Comments are covered next; the other items appear later in the chapter.
Comments in Scripts
Comments are easy to include in your scripts and we encourage you to use them extensively. They don't affect the speed of your scripts, take up relatively little room in the Object Database and make it much easier to maintain and share your scripts.
Frontier supports two kinds of comments: line-ending comments and outline comments.
A line-ending comment generally appears on the same line as executable code. Click at the end of a line then type Frontier's comment character « (hold down the Option key and press the backslash key -- located just above the Return key on most keyboards) and the text of your comment. When the compiler encounters a line-ending comment, it stops parsing the line. (In case you are curious, the comment character is variously known as the "chevron", "left guillemot", "left angle quote" or "double angle bracket.")
To convert a line of your script into an outline comment, hold down the Command key and press backslash. The item marker changes into a «, converting this line and all lines indented under it as comments. If the headings below a comment heading are collapsed, the comment character is emboldened. You can toggle the outline comment off and on by selecting "Toggle Comment" in the "Script" menu, or using the keyboard equivalent noted above. To enter a new line that is automatically a full-line comment, press Shift-Return.
Figure 3-1 shows a script fragment that demonstrates all of these facts about comments in UserTalk scripts.
Figure 3-1. Script Fragment Showing Comment Usage
Frontier is quite intelligent about how it deals with comments and executable lines of code. For example, if you enter a line of executable code and then indent it under a comment line, it will turn into a comment. If you move that line back to its previous level, it "remembers" that it is executable and toggles itself out of comment mode.
Frontier scripts are outlines
As we saw in the previous chapter, Frontier scripts are edited as outlines. Use tab to indent a line and shift-tab to move it back. Double-click on the item marker on the left to expand or collapse any lines that are at a lower "level" -- i.e. indented underneath. Click on the item marker to select a line at the lowest level (i.e. with no lines indented underneath). Cut, copy and paste work as expected. Click to select a line at a higher level; cut, copy and paste now work on the whole "block". Click on a "summit" line (i.e. one that is at the far left), choose "Select All" from the edit menu, then copy; you will get the entire script. These basics should get you started; the outline-based editor is covered in more detail in Chapter 7, The Frontier Environment.
Indentation is used to group statements. For example, here's a simple "if" statement, properly formatted:
if answer >= 6
msg ("Big family!")
msg ("Not such a big family!")
This script fragment looks at a variable called answer. If it holds a value of 6 or larger, the script displays one message; if the number is less than 6, it displays a different message. This example shows one line indented under "if" and one under "else"; UserTalk supports any number of lines in either block.
When a block includes multiple lines, it's often convenient to collapse them to hide the detailed behavior. Users who don't want or need to understand detailed levels of processing in a script can look only at the level in which they're interested. Comments can summarize the result of the actions that are hidden. Here is the same script fragment, collapsed and with added comments:
Here's a simple "for" loop, properly formatted:
Only the second line, window.msg (i), is indented under the "for" loop header, so it's the only line that gets executed 100 times. Note that window.msg () is similar to the msg () verb, but displays into the message area at the lower left corner of the script window (i.e. the window that holds this particular script) instead of into the main Frontier window.
Here's an example of a "local" block, with a formatting error:
The "local" keyword defines variables as local to the script; see Chapter 4 for details on variables. Only lines that consist of variable names or variable assignments may be indented as part of a "local" block. Because the structure of a script as reflected in its levels of indentation is significant, the msg (x + y / z) line is a syntax error. If you try to compile this script, Frontier will present an error dialog. Simply click on the Go To button and the cursor will be placed on the line that contains the error. To fix this script, type shift-tab; the msg line will move out one level where it belongs.
Turning a Script Into a Verb
You may think of "verbs" as functions that are built into Frontier, and "scripts" as functions that you write. In reality, there is no essential difference! Many of Frontier's verbs are implemented as scripts, and all of them live in the Object Database. As seen in Chapter 2, scripts that you create in your Workspace table can be called from the Quick Script window, a menu script or from any script anywhere in the Object Database.
Nevertheless, there is a recommended way to design your scripts so that they can be called from other scripts. This design is required when your script accepts parameters, so it's a good idea to get in the habit of using it all the time. Here are the details.
Since a script is stored in the Object Database, it must have a name. The name should suggest or describe what the script does. Inside the script, UserTalk uses the "on" keyword followed by the exact name of the script to define what actions will be performed when the script is executed. One way to understand "on" is to think of the script definition as "on the occurance of this script name and (optionally) these parameters, perform the following actions". Some scripting languages refer to verbs or scripts as "handlers", and the definition as an "on handler".
Here's an example. Go to your Workspace table (Cmd-T from the Open menu). In the Table menu, select "New Script". Type "demoFormat" and click OK. Open the script and enter a single line:
on demoFormat ()
Don't forget the parentheses! When you create a script that has parameters, they will go between the parentheses; but parentheses are still required for scripts that have no parameters.
As we shall see when we cover debugging later in this chapter, there is one slight complication to this structure. Even after you add executable lines indented under the "on" line, clicking the "Run" button will just give an error. Why? The script contains only a definition of a verb, and the definition can't be executed directly.
Fortunately, there's a very easy solution. You can call your new verb (script) from anywhere in Frontier -- including from inside the script itself! If you are still following along, enter the following, using Tab and Shift-Tab to indent and outdent:
on demoFormat ()
dialog.notify ("Hello " + user.name + "!")
Click the "Run" button. It works! It doesn't do much, but it no longer gives an error. Again, this form is optional for simple verbs, but required for scripts that have parameters.
Menubar scripts should not be structured with an "on" definition. Their purpose is to call other scripts; they are executed from the menu but cannot otherwise be called by scripts.
The "on" keyword is also used to define local scripts, i.e. scripts that live inside another script. These scripts must be defined before they are called, so they are often placed at the very beginning of the script. They can also occur in almost any UserTalk "block" structure (covered in the next chapter). The return keyword is used to return control and send a value back to the main script. Parentheses are optional. Here's a simple example.
on demoFormat ()
msg ("part of the main script")
on squareIt (n)
msg (squareIt (3)) «result will be 9
Compiling a UserTalk Script
All UserTalk scripts exist in two forms. The first is source code that you create, view and edit. The second is an internal "compiled" version, a compact "tokenized" format that executes very quickly.
In general, Frontier creates the compiled version automatically when necessary, so you don't have to worry about it. If you make a change in an open script window and close it without clicking the Compile (or Run) button, Frontier will if you want your changes to be compiled (see Figure 3-2).
Figure 3-2. Dialog Confirming Desire to Automatically Compile Script Changes
Unless you are an advanced script writer and have a specific reason for leaving the compiled version as is, just click on the "Yes" button in this dialog. The circumstances under which you would wish to say "No" are rare.
Note that Frontier's compiler is quite fast. When you click on Compile, you might think that nothing happened!
When you first create a script, the most common way to run it is to click on the "Run" button. Remember: scripts that consist of an "on" definition that matches the name of the script must include the name of the script as an executable line at the end of the script.
When you click "Run", Frontier first tries to compile the script. If there is a syntax error, it will present an error dialog. (Tip: if you select Copy while the error dialog is in front, the text of the error message will be put onto the clipboard.)
Click the GoTo button in the error window; the cursor will be placed on the offending line. (If you are missing a quote mark, the cursor may appear on the following line.) To just check the syntax without running the script, click the "Compile" button. The "Debug" button is covered in the next section.
Any script can be treated as a "verb" and called from other places in the Object Database. In these cases, it's important to use the correct syntax for calling the script. The format for calling any script or verb is essentially the same: the "category" or location if necessary, the script name, parameters enclosed in parentheses -- or a pair of empty parentheses if there are no parameters.
Scripts can be run using this complete name, as follows:
- from the Quick Script window
- from another script
- from a menu item
- in an outline window or Frontier "word processing" window
Most of these choices where covered earlier; the last item is new. Here's an example. Select "Open Notepad" from the "Open" menu (or type Cmd-Y). Scroll to the end (if necessary) and type return to create a new line, then type the following:
Leave the cursor on that line and select "Run Selection" from the "Main" menu -- or use the keyboard equivalent of Cmd-/. Your message appears in the main window and the returned value of the msg verb is placed as a comment on a new line indented below the line that contains your script. The Notepad or any other outline can be used like the Quick Script window, with the added advantage of keeping track of which scripts you ran and what result they returned. (The result of "msg" isn't very interesting but results are very important for many verbs.)
msg ("Outlines are cool!")
In an outline, "Run Selection" really means to run the entire line (whether or not any of it is selected). "Run Selection" also works in Frontier's word processing windows, though the complete name must be selected and the result appears in the main window.
Even after you fix syntax errors so that your script runs, it may not do what you intend. Frontier includes a powerful debugger to help pinpoint the source of the problem. Click the "Debug" button. Figure 3-3 shows the new buttons that appear.
Figure 3-3. Script-Execution Window Buttons
From left to right, these buttons permit you to do the following things while executing a script:
- "Step" enables you to walk through the execution of the script a statement at a time. When you click the button, it will run the current line and move the bar cursor (selection) to the next line. You can look at entries in the Object Database, examine variables in the program and perform other debugging tasks as needed before clicking "Step" again.
- "In" allows you to "dive" into a script that is being called by the script you are debugging. Click on this button while the cursor is positioned on a line in your script that calls a local or external script. If it's an external script, Frontier will open it in an editing window and position the cursor at its first executable line. You are still in debugging mode on the called script.
- "Out" has the effect of canceling one click of the "In" button. Essentially, it tells Frontier, "Continue executing until you're out of this script and back at the level from which this script was called."
- "Follow" highlights each line of your script as it is executed but does not stop. A common use of "Follow" is to watch for lines or groups that are being skipped when you thought they should be executed. You can also watch the script and click "Stop" to pause the script when needed. (The debugger is pretty quick, so you may be better off setting a breakpoint, discussed below.)
- "Go" will run the script to completion -- unless you click "Stop" to pause. The "Go" button changes to "Stop" while the script is running.
- "Kill" terminates execution of the script immediately.
- "Lookup" permits you to examine the values of variables used in your script while the script is paused.
Figure 3-4 shows an example of using "Lookup". The script in the top window is paused in single-step mode. When the user clicks "Lookup", Frontier opens the temporary location in the Object Database where the script's local variables are stored while the script is running. In this case, the variable "answer" has a value of 3. As shown in the bottom window, the table looks like any other Frontier table. For complex scripts with many levels of testing, there are a whole series of tables. While a script is paused, you can navigate these tables just like you navigate any other table in Frontier.
Figure 3-4. Sample Variable Lookup
The "Lookup" button can also be used to jump to other places in the Object Database. Select any variable or function in the text of the script you are debugging and click "Lookup"; Frontier will jump to that item if it exists. [Note: you can do this kind of lookup even if a script isn't being debugged by holding down the Cmd-key while double-clicking on a name.]
There are times when stepping through a script a line at a time is too tedious to be of much value. For such situations, Frontier includes the ability to define a breakpoint on nearly any statement in your UserTalk scripts. When Frontier encounters a breakpoint during a "Debug" run, it stops processing unconditionally; all of the other circumstances of stepping, and moving into and out of subordinate scripts we discussed earlier are subordinate to this point. If you click "Run" instead of "Debug", or if you run the script from anywhere else in the Object Database, breakpoints will be ignored.
You set and clear breakpoints by selecting the line of interest and then choosing "Toggle Breakpoint" from the Script menu -- or pressing the Command-K keyboard equivalent. You can also Command-click on the line's item marker to toggle a breakpoint. When you set a breakpoint on a line, the leader character changes from a triangular shape to a hand:
You can also use other approaches to debugging scripts, such as displaying in the Main Window messages indicating what part of a script is executing or the value of some variable you wish to monitor without interrupting script execution.
If you have some object-oriented or other highly structured programming experience, the idea of designing code for reusability is not new to you.
At the heart of Frontier is a highly reusable set of verbs, each focused on a particular task. You use these verbs over and over again in your scripts. You don't have to write the code for each verb in every script where you want to accomplish the particular task of that verb.
You should design your scripts with the same goal in mind: each script performs a single task that can be isolated. When you need the same function in the future, you can re-use the code by calling (running) the earlier script from your new script.
There are two key benefits to "factoring" your code into single-purpose scripts. First, you can create new scripts more quickly because you can take advantage of code that already exists instead of writing it from scratch each time. Second, it is much easier to maintain your scripts. When you need to fix or improve the code that accomplishes a certain function, you can change it in one place. Every script that depends on it automatically gets the benefit of the new code.
It is also beneficial to minimize dependence on specific locations in the Object Database, sometimes by isolating this information within a single script. For example, if you're designing a suite that always opens a particular outline object for editing by the user, write one script whose job is to open that outline. Then, call this script from any other scripts that need to open the outline, rather than having them open the object directly. While this approach is slightly less efficient, it makes your suite easier to maintain. If you change the location or name of the outline, you only have to change one line of code; the other scripts will continue to work as expected.
© Copyright 1996-97 UserLand Software. This page was last built on 5/7/97; 1:07:05 PM.
It was originally posted on 9/24/96; 9:36:17 AM.
Internet service provided by Conxion.