Skip to Content
Skip to Table of Contents

← Previous Article Next Article →

ATPM 8.08
August 2002

Columns

Segments

How To

Report

Extras

Reviews

Download ATPM 8.08

Choose a format:

Roll Your Own

by Charles Ross, cross@atpm.com

Go with the Flow

Welcome back to Roll Your Own, where we take you through the beginnings (and eventually, the more advanced topics) of programming your Macintosh using AppleScript. I apologize for missing last month, but our family was a bit busy with a new addition, Tiana Lia Ross, born June 10, 2002, 10:44 AM, 6 lb. 1 oz., 19.5 in. Mommy, Daddy and big brother Kieran are happy to have the little one to hold at last.

Last time we covered variables and how they help to store information in your program. This time, we’ll delve into the topic of flow control, which allows the computer to make decisions for you.

Before we get into the meat of this month’s column, a word or two about learning AppleScript syntax: I’ve mentioned in earlier columns that this series isn’t meant to go into the details of AppleScript, so we’re going to go through some of the topics here rather quickly. We’ll cover the general programming topic in detail, but only scratch the surface when it comes to all of the ways AppleScript allows you to implement that topic. If you’re interested into the details of the AppleScript language, a great reference is AppleScript in a Nutshell, by Bruce W. Perry from O’Reilly & Associates. O’Reilly is a great technical publisher with many offerings on a wide range of computer topics, including about a dozen books dedicated to the Mac. (In case you’re wondering if this is a self-serving plug, rest easy, my book is not being published by O’Reilly.) In addition to a print version, AppleScript in a Nutshell is also available online for a monthly fee through Safari Tech Books Online. Safari allows you to subscribe to technical references by paying a monthly fee. The fee can be as little as $9.95 per month for 5 points (most books are worth 1 point). I’ve been subscribing to the service for about four months now, and have found it to be a great tool.

If you aren’t able to either purchase AppleScript in a Nutshell or a Safari subscription, Apple does provide a free manual which explains all of the AppleScript syntax called The AppleScript Language Guide, available both as a group of Web page and as a PDF document. Although it’s not as well written as AppleScript in a Nutshell, since this reference is free and available on the Web, when I suggest taking a look at the deeper AppleScript documentation, I’ll provide links to the appropriate sections of this book.

Back to Programming…

Flow control is the ability in a program to jump from one line of code to another without executing the lines between the two. For the most part, when a program runs, it executes each line in turn, performing the actions in sequence. But often you want to execute some lines of code out of order or conditionally. Other times you want the same code to execute repeatedly with slight variations. Both of these cases are why programming languages provide flow control. AppleScript makes use of a number of commands that allow us to control whether some statements are executed, and when.

Truthfully, we’ve already seen an example of flow control. In our prior sample applications we had a repeat loop. The repeat loop allows us to execute one or more lines of code as many times as necessary. But there are other kinds of flow control tools provided by AppleScript.

The first, and probably most useful, statement that we’ll cover is the if statement. The if statement is followed by a boolean test. The test expression needs to return a boolean value. (Remember, a boolean value is one that is either true or false.) If the test turns out to be true, then the code after the if statement is executed. Type the following code into Script Editor and run it.

(6 / 2) is equal to 3

When you run the above program, the word true should appear in your result window. That’s because the line (6 / 2) is equal to 3, is a boolean expression, which in this case returns true. Change the program to read as follows:

2 is equal to 3

The line is still a boolean expression, but this time the expression is false, so running the program will show false in the result window.

Here’s the simplest example of an if statement:

if (6 / 2) is equal to 3 then beep

Here we are testing to see if the expression (6 / 2) has a value of 3. If it does, then the system will beep. If the expression had evaluated to false, then the above line of code would do nothing.

Many programming languages have an equivalence between boolean values and numbers. For instance, FileMaker Pro and C both use 0 to indicate false and any positive value to indicate true. The following code in FileMaker Pro would work perfectly:

Set Field [ myField, 1 ]
If [ myField ]
  Beep
End If

This code will work because FileMaker interprets a 1 as true. AppleScript doesn’t work this way. Similar code in AppleScript will produce an error

set myVar to 1
if myVar then beep

AppleScript provides a number of comparison operators that programmers can use to build test expressions, and many of these operators are equivalent. If you don’t like to type as much, you could have written our first if statement program as follows:

if (6 / 2) = 3 then beep

Here’s a list of the comparison operators that AppleScript provides. As you can see, many of them are equivalent to each other, so I’ve grouped the synonyms together. Check out your AppleScript reference for a detailed description of each of these operators.

=
equals
equal
is
equal to
is equal to
≠
is not
isn't
is not equal
is not equal to
isn't equal
isn't equal to
doesn't equal
<
is less than
less than
comes before
is not greater than or equal to
is not greater than or equal
isn't greater than or equal to
isn't greater than or equal
<=
≤
is less than or equal to
is less than or equal
less than or equal to
is not greater than
isn't greater than
does not come after
doesn't come after
>
is greater than
greater than
comes after
is not less than or equal to
is not less than or equal
isn't less than or equal to
isn't less than or equal
>=
≥
is greater than or equal to
is greater than or equal
greater than or equal to
is not less than
isn't less than
does not come before
doesn't come before
begins with
begin with
starts with
start with
ends with
end with
contains
does not contain
is contained by
is in
is not contained by
is not in
isn't contained by
and
or
not

If your desire is to write programs that are readable by the average person, you would probably use the English versions of the operators. Some of the operators are only available in English versions, such as contains and begins with. Most of these operators are pretty self explanatory, such as the = and the <= operators, and the synonyms for them make sense.

Those last three operators listed, and, or and not, are of great assistance in building complex boolean expressions. Often the test you wish to make is more complex than can be expressed with a single operator. Perhaps you wish to perform an action only if var1 is greater than var2 and var3 is equal to var1. By using the and operator, you can accomplish this sort of test:

set var1 to "Chuck"
set var2 to "Adam"
set var3 to var1
(var1 is greater than var2) and (var3 is equal to var1)
  -- result:  true

Make a practice of using parentheses when building complex boolean expressions like this. In this case, the expression will work without the parentheses, but they certainly help make it clear exactly what is being tested.

The and operator returns true only if both of the tests on either side of it return true. If either one returns false, so does the entire operation.

set x to true
set y to false
x and x
  -- result:  true
x and y
  -- result:  false
y and x
  -- result:  false
y and y
  -- result:  false

The or operator, on the other hand, will return true if either of the expressions it is comparing are true.

set x to true
set y to false
x or x
  -- result:  true
x or y
  -- result:  true
y or x
  -- result:  true
y or y
  -- result:  false

Lastly, we have the not operator. Whereas all of the operators we’ve seen so far work on two expressions, the not operators works on only one. It simply reverses the value of a boolean.

set x to true
set y to false
not x
  -- result:  false
not y
  -- result:  true

We’ve covered a lot of material so far, and before you continue, I would recommend that you experiment with the operators available to create boolean expressions. Set some variables and compare them, then see what happens in the result window. It is important that you thoroughly understand how to build boolean expressions, because you’ll be needing them throughout your programming career. Even if you later decide to change languages, the logic of using these boolean operators will be useful in whichever language you later tackle. Because of how important this will be to your future programming life, take the time to make sure you understand what we’ve gone over so far before continuing.

Don’t worry, I’ll wait…

Welcome back! OK, now that we have a solid foundation of what comparison expressions are, we can use them to tell our computer to make decisions. We’ve already checked out a simple if statement, and now we’re going to build on that. Remember from above that our simple if statement looked like this:

if (6 / 2) is equal to 3 then beep

This takes the form of

if [test] then [statement]

This is as simple an if statement as you can get. You need at least this much for it to work, but you can provide more to increase its power. The first thing you might notice is that only one statement is being executed. Usually we want a number of statements to execute if the test proves to be true. In such a case, our if statement is going to look like this:

if [test] then
  [statements]
end if

We can place as many statements between the if and end if statements as we like. They will only execute if the test evaluates to true.

if (6 / 2) is 3 then
  beep
  display dialog myVariable
  beep
end if

Often if a condition is true you want one set of statements to execute and if not then a different set of statements. AppleScript provides the else command for just such occasions. It takes the form:

if [test] then
  [statements]
else
  [statements]
end if

One set of those statements will execute, never both and never none.

set myVariable to (6 / 2)
if myVariable is 3 then
  beep
  display dialog myVariable
  beep
else
  display dialog "myVariable is not 3"
end if

From here it just gets even more complex. Let’s say that if a condition is false, then you only want to execute the else block of statements if a different condition is true. We have the else if statement to cover such ground.

set myVariable to 5
if myVariable is 3 then
  beep
  display dialog myVariable
  beep
else if myVariable is 5
  display dialog "myVariable is 5"
end if

That second statement block, display dialog "myVariable is 5" will only execute if myVariable actually is 5. If it is 3, the second if never even gets evaluated. You can have as many of these if…else if…else if…end if blocks as you need, and when we get to building user interfaces and need to check which button was clicked by the user, such statements come in quite handy.

By using nested ifs and else ifs, you can create as complex a test as needed for your program to know what to do. Be careful, however. Be sure to document your code well, as many nested ifs are difficult to decipher later. One way I use comments in my code to help me with nested ifs is to place a comment at the end of the end if statement like this:

if myVariable is 3 then
  beep
  display dialog myVariable
  beep
end if -- myVariable is 3

The comment after the end if statement makes it clear which if block the end if is closing. Read the AppleScript documentation on if statements for more detailed information on the if statement.

Another kind of control statement is the repeat statement. The repeat statement allows you to execute a block of code over and over again however many times you need to. We saw the repeat statement in previous columns when we wrote a program to sum up all the values to a given point. As a refresher, here is the program that used it:

display dialog "Please enter a positive number:" —
  default answer "" buttons {"OK"} default button 1
set theNumber to the text returned of result
set sum to 0
repeat with i from 1 to theNumber
  set sum to sum + i
end repeat
-- Report the results of the process to the user.
display dialog "The sum of the first five number is " & —
  sum & "." buttons {"OK"} default button 1

The repeat block of this program executes the number of times entered by the user. This is just one version of the repeat loop, and it takes the form of

repeat with [variable] from [integer] to [integer]
  [statements]
end repeat

This block of code will set the variable to the first integer. Each time through the loop, the variable will be incremented by 1. If after incrementing the variable it is greater than the second integer, the loop doesn’t execute and the program continues with the code found after the end repeat statement.

Like the if statement, the repeat statement comes in a number of flavors, including the simpler:

repeat
  [statements]
end repeat

This code simply executes forever, unless one of the statements within the repeat block has an exit repeat statement. Be very careful with this simple version of the repeat loop. It’s easy to forget to have the exit repeat statement or to have code which never executes the exit repeat statement, and then you get an infinite loop. The only way to stop an infinite loop is to force the program to quit or, if the program is running within Script Editor, to press Command-Period. Here’s a simple example of such a repeat block.

set i to 1
repeat
  display dialog i
  if i is equal to 10 then exit repeat
  set i to i + 1
end repeat

This code is equivalent to:

repeat with i from 1 to 10
  display dialog i
end repeat

The fact that these two code fragments are equivalent brings up a good point. There is almost always more than one way to accomplish your programming task. Which repeat loop you use in this case is perhaps a matter of preference, although to me the second one is simpler and easier to understand.

Another version of the repeat statement is the repeat while flavor:

repeat while [booleanExpression]
  [statements]
end repeat

This version will execute the statements within the repeat block until the booleanExpression evaluates to false. If the booleanExpression is false to begin with, the statements in the loop won’t execute at all. Using this version to perform the same loop as above, we would have:

set i to 1
repeat while i is less than or equal to 10
  display dialog i
  set i to i + 1
end repeat

Similar to repeat while is repeat until, which will execute the loop until the boolean expression evaluates to true. We’ll see an example of this type of loop when we improve our program later.

There’s one version of the repeat loop that is specific to lists. Remember that a list is a collection of objects surrounded by braces and separated by commas, such as {1, 3, 5, 10}. It is often useful to do something with each item in the list, and there is a version of the repeat loop to handle this. It takes the form

repeat with [variable] in [list]
  [statements]
end repeat

Since most of our example repeat loops have had the same output, we’ll continue that theme with this one.

set myList to {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
repeat with i in myList
  display dialog i
end repeat

Like our prior loops, this one displays ten dialog boxes with the first ten numbers in them, only this time we are iterating through a list instead of counting.

Before we leave the syntax of flow control and begin improving our program to sum numbers, we need to cover one more command, the try…on error…end try block. Like repeat and if, it has many different forms which are covered in the AppleScript documentation. For now we’re only going to cover the simplest form, which looks like this:

try
  [statements]
on error
  [statements]
end try

There are many times when you’ll be programming and you know that under certain conditions the statements you’re going to execute will produce an error. Using a try block you can capture the error and handle it yourself rather than having AppleScript choke on it. Here’s an example:

try
  set myVar to "AppleScript" as number
  beep
on error
  display dialog "Can't coerce that string into a number."
end try
display dialog "This is outside the try block."

What the above block does is first attempt to coerce the string “AppleScript” into a number. Well, this isn’t something that AppleScript can do, so an error is produced. If we had tried to execute the statement outside of a try block, AppleScript would have halted execution of the program and displayed an error message. By enclosing the attempt in a try block we can handle the error ourselves and then act appropriately, allowing the program to continue. With the program above, the final line of code will execute even if there is an error.

One thing to take note of is that the above code will never beep. When the error is detected, execution of the try block stops at that point and continues after the on error statement.

You can also execute the on error block by manually raising an error within the try block, as you’ll see in our sample program.

The try block is quite useful in catching user errors and handling them elegantly rather than having the user have to put up with a system error. We’ll go into more detail with it in a future column, but for now we’ll use the simple version of it in our updated program.

Now that we have all this syntax under our belts, let’s put it to use. If you remember our program that sums up numbers, the first thing it does is get a number from the user. But what if the user doesn’t enter a number? What if instead of entering “5", they enter “five”? As our program currently stands, it would crash because it can’t count to “five.”

Here’s the edited version that checks for the type of data input by the user. It adds not only an if statement, but an additional repeat loop. Because the complexity of the program has increased, I’m beginning the practice of including comments with every line of my full sample programs. Remember that the ¬ character is entered by typing Option-Return in Script Editor and indicates that the following line is a continuation of the current line.

-- Get the number to sum up to from the user.
display dialog "Please enter a positive number:" default answer ¬
  "" buttons {"OK"} default button 1
-- Get the data entered by the user into the dialog's field
set theNumber to the text returned of result
-- Assume that invalid data was entered
set isValidEntry to false
-- Repeat the following block of statements until we've made 
-- sure that the user has entered valid data.
repeat until isValidEntry
  -- Attempt to coerce the data entered by the user into a number.
  try
    -- If a non-numeric entry was made by the user the 
    -- following statement will produce an error.
    set theNumber to theNumber as number
    -- If the data entered by the user is a number but
    -- isn't an integer, manually produce an error.
    if class of theNumber is not integer then error
    -- If no error was produced, set the isValidEntry
    -- variable to true so that we can exit the loop.
    set isValidEntry to true
  on error
    -- If an error was produced, alert the user and ask for
    -- valid data.
    display dialog "That is not a valid entry. " & ¬
      "Please enter a positive integer:" default answer ¬
      "" buttons {"OK"} default button 1
    -- Get the data entered by the user.
    set theNumber to text returned of result
  end try -- set theNumber to theNumber as number
end repeat -- until isValidEntry
-- Initialize sum to 0.
set sum to 0
-- Sum up the numbers.
repeat with i from 1 to theNumber
  set sum to sum + i
end repeat -- with i from 1 to theNumber
-- Report the results of the process to the user.
display dialog "The sum of the first ¬
  " & theNumber & " numbers is " & sum & ¬
  "." buttons {"OK"} default button 1

Wow, our program is getting more complex (and better) every time! Here’s what it is doing.

As before, the first command prompts the user to enter a number. We then immediately store what the user entered into a variable. Next we initialize another variable called isValidentry to false. We only want to sum up the numbers if the user enters an integer, so we use this variable to test if that has occurred.

Next we enter a repeat loop. Since the repeat loop says repeat until isValidEntry, and isValidEntry is false to begin with (because that’s what we set it to), we know that this repeat loop will execute at least once.

As soon as we enter the loop, we begin a try block. The first line of the try block attempts to coerce the user’s entry into a number. If the user entered something like 5 or 6.3, this will work fine and the execution continues. If the user entered their name or today’s date, or anything but a number, the line set theNumber to theNumber as number will produce an AppleScript error, and execution of the program will hop over to the on error section.

If we passed the first line successfully, we know that theNumber is in fact a number, but it still needs to be an integer for our sum to work. So we use an if statement to check the class of theNumber, and if it isn’t an integer, we manually call the error, the execution of the try portion halts and moves to the on error portion.

If both of the first two statements in the try block execute successfully without producing an error, then we set the isValidEntry variable to true so that the repeat loop won’t execute again. The on error portion of the program will only execute if an error was produced by one of the first statements in the try portion. If we get to this part of the program, then the user either didn’t enter a number or the number wasn’t an integer. We inform the user that there was a problem with her entry the last time and again ask for an integer, storing the data entered by the user into the variable theNumber again. Since isValidEntry hasn’t been set to true in this case, we go back up to the repeat loop and begin the process of testing the user entry again. The repeat loop ensures that before we continue, and attempt to sum up the numbers, we have a valid number to sum up to. The rest of the program is the same as before with the added comments.

Finishing Up

Well, we’ve covered a lot of material today. We learned how to build simple and complex boolean, expressions, how to conditionally execute part of the program, how to capture errors and how to loop through code repeatedly. Next time we’re going to go into more detail with the try block by testing for the type of error that occurred, and we’ll cover a more general programming topics, such as algorithms and flowcharts. As always, feel free to e-mail me with questions or suggestions. Until then, happy programming!

Also in This Series

Reader Comments (2)

Madoline Augustus · February 5, 2003 - 22:48 EST #1
Very helpful! I can't wait for this column to get into the meat of AppleScript. The programming info here is great for beginners. Excellent work.
Jeff Gustafson · October 31, 2003 - 18:46 EST #2
Excellent tutorial. I can't wait for your Applescript Studio book to be released. I have seen all the books and these articles are very good--better than most on Applescript.

Add A Comment





 E-mail me new comments on this article