Messing Up on Purpose

Before we can really explore the Debugger, we need to deliberately create an error condition; that is, we have to write some Lingo that will malfunction when it runs. That’s actually rather hard to do, because the basic errors you might think of making are all tested for by Director itself. It won’t let you make obvious mistakes, in other words, so you need to do something subtle.

Start with a new Director movie and, in frame 1, place a go to the frame script in the script channel. Then use the #field tool in the Tool Palette to make two new #field sprites on the Stage. Name the #field members "num1" and "num2" in the Cast window. Use the Field tab in the Property Inspector (PI) to set their types to editable, and then create a button labeled "Divide".

Now create a new behavior for the Divide button:
 

on mouseUp me
 
 
nFirstNum = value( member("num1").text )
nSecondNum = value( member("num2").text )

ALERT "The result was" && string ( nFirstNum / nSecondNum )

END mouseUp


Here’s what this code does:

nFirstNum = value( member("num1").text )

First we get whatever text has been typed into the #field sprite named "num1" in the Cast, and then we try to convert it to a number with the value keyword.

nSecondNum = value( member("num2").text )

Here we’re doing the same thing with the text typed into "num2".

ALERT "The result was" && string ( nFirstNum / nSecondNum )

And finally we divide the two numbers, convert that result to a string so that it will be treated as text once more, and put the result into an ALERT box. (This is a very simple program, of course.)

Now click the Play button and enter 1 for the first number and 0 for the second, and then click your Divide button. You will get a script error saying you cannot divide by zero (which is true; in mathematics division by zero is undefined, or an operation that yields no known result). Don’t click Cancel or Script… though; instead click Debug…. You should get a Debugger window.

There are three panes to this window. In the upper-left corner is the stack of handlers you’ve passed through to arrive at this point. Since the mouseUp handler was the only one used here, that’s the only handler listed.

To the right is a list of all the variables currently in use in this handler. As you can see, the Debugger is showing you the values for nFirstNum and nSecondNum.

Note: The me variable refers to the behavior script itself. We’ll be discussing me in depth in Part 4. Underneath you can see the Lingo you entered, complete with an arrow pointing to the ALERT line. This is the place where your program stopped working. Something is happening in this line of Lingo that Director does not like, and by looking at the list of variables you can see that the problem is the value for nSecondNum, which is zero.

Well, that’s a little obvious, but it’s a quick introduction to how you can get a peek into the way your program believes it should be running. There are more effective uses for the Debugger, which we’ll be covering in the very next section.

Stepping Through Code

Sometimes you might not know exactly where something is going wrong, and so you need to deliberately invoke the Debugger to see what’s happening in your program.

For instance, click Director’s Stop button. (Note that when you do so, everything in the Debugger disappears.) Open a new Movie script and enter the following:
 

on DivideNumbers n1, n2
 
 
nResult = n1 / n2

ALERT "The result was" && string ( nResult )

END DivideNumbers


This, of course, is not very different from the code you wrote for the Divide button, by which you may infer we are going to change that script a little and call this handler from the button’s behavior, passing along the two numbers entered in the #field sprites as parameters:
 

on mouseUp me
 
 
nFirstNum = value( member("num1").text )
nSecondNum = value( member("num2").text )

DivideNumbers( nFirstNum, nSecondNum )

END mouseUp


Now click Play again and enter 1 for the first number and

6 + 3 * ( —2 )

for the second, paying attention to the parentheses. Click Divide. Well, you got the same script error as before, because 6 + 3 * (—2) is zero, again, and since Director calculates mathematical operations when it takes the value of a field, you’re still dividing 1 by 0.

However, you will notice that there are now two names listed in the handler stack in the upper-left pane of the Debugger, the bottommost one being your DivideNumbers handler. As the arrow in the Debugger shows, the place where the program stopped working was with the line

nResult = n1 / n2

inside that handler. And as you can also see, n2 has a value of 0.

Of course, you know already why this is happening; but you can actually step back through your handlers to see where those values came from.

To do that, click the mouseUp handler listed in the upper-left pane of the Debugger window. What happens? Director does two things:

Here again you can see that nSecondNum turned out to be zero, which is how n2 got its zero value when the variable was passed to it in the DivideNumbers call from the mouseUp script.

You can also step ahead into the DivideNumbers handler once more, and as you can see from this example, the Debugger lets you roam through the previous handlers that ran before the one that malfunctioned. In this way, you can see not only the place where the actual script error occurred, but also the events leading up to it­which often will tell you what went wrong, and where.

Okay, click the Stop button again and notice that, once more, the Debugger clears. Then click Play again and enter 1 for the first number as before, but 7 / 3 for the second, and then click Divide.

Now that was probably unexpected. Director told you the result was 0!

Why? Let’s see if we can use the Debugger to find out.

Open your button’s behavior script and click in the gray bar on the left-hand side just under the mouseUp handler. A red dot should appear there.

This is a breakpoint, or place in your Lingo where you are deliberately telling Director that you want it to pause and open the Debugger window. In other words, you want the program to show you at that point what’s going on, so you set a breakpoint to cause that to happen.

Now click Play again and, with 1 for the first number and 7 / 3 for the second, click Divide.

As you can see, the Debugger window becomes active at that point, with the mouseUp script listed in the stack pane in the upper-left and the variable list to the right. Note that me has a value, but that nFirstNum and nSecondNum are defined in the Debugger as <Void>. This simply means that they don’t have any values assigned to them yet.

Note also that there are some green buttons below the stack list that have become active: a downward-pointing arrow with a line above it, another arrow like that but pointing to the lower right, and a third solid arrow pointing straight down.

Left to right, these arrows allow you to

We want to step through this Lingo to try to figure out why Director thinks that 1 divided by 7/3 is 0, so click the leftmost step button.

When you do that, you see that the variable nFirstNum receives a value of 1, which is exactly what should happen according to the Lingo you entered.

Then, when you click the step button again, you see that nSecondNum gets a value of 2. Two? That’s not right. Seven divided by three is two and one-third, or 2.333…. So it’s obvious that there’s one thing that needs fixing in our code already.

Note: You’ll have to click the step button twice here to see the changes made to nFirstNum and nSecondNum. This is because Director is first reading the text contained in your #field sprites before assigning the values to the variables. But that still doesn’t answer the question of why Director gives us a final answer of 0, so we need to follow the program into the DivideNumbers handler.

With the break arrow pointing to the line that reads

DivideNumbers( nFirstNum, nSecondNum )

click the diagonal arrow, which is the Step Into Script arrow.

Once you do that, you’ll see that the Debugger has carried you into the DivideNumbers handler­you’ve jumped into it, following the Lingo in the same order that Director itself follows it. You can see that the handler stack in the upper left now has the DivideNumbers handler listed, and that to the right n1 and n2 are shown with values of 1 and 2, respectively. nResult shows a value of <Void>, which you remember means it just hasn’t been given a value yet.

So now if you click the step button, you see that the division operation takes place, but that nResult’s value is 0, which is strange, because one divided by two is one-half, or 0.5.

Now we’ve found what is happening­our numbers are not working as decimals­but the larger question of why is still unanswered.

It’s happening because Director defaults to integer math operations when performing many different mathematical functions, which means that while 7 divided by 3 is 2 1/3 (or 2.333…), Director truncates the trailing decimal, giving us an incorrect result of 2.

What’s worse, Director now assumes that this 2 value is meant to be an integer. So when it divides 1 by 2, it assumes the result is supposed to be an integer too, and 0.5 gets truncated to zero.

In order to fix this, we must explicitly tell Director that we want it to perform math calculations with decimals by using the float keyword. This forces Director to work with decimals, even if it would normally work with integers only.

To change things, we need to rewrite some of our code, beginning with the Divide button’s script:
 

on mouseUp me
 
 
fFirstNum = float ( value( member("num1").text ) )
fSecondNum = float ( value( member("num2").text ) )

DivideNumbers( fFirstNum, fSecondNum )

END mouseUp


Here you can see we’ve added the keyword float to the value operations we’re getting for our variables. We’ve also changed their prefixes from n to f, to indicate we’re not working with integers any more, but rather floating-point numbers.

Then we need to rework the DivideNumbers handler a little too:
 

on DivideNumbers f1, f2
 
 
fResult = float ( f1 / f2 )

ALERT "The result was" && string ( fResult )

END DivideNumbers


Technically, this second float specification is redundant, since the two variables that have been passed into this handler are already floating-point numbers. It’s been included here because it’s not a bad practice, when you’re writing Lingo scripts, to specify the format you want a given variable to have as Director manipulates and displays it. After all, as we’ve just learned, if we don’t do so, unexpected or weird things can happen.

Note too that we’ve changed the n prefixes here to the letter f, just as we did in the mouseUp script, and for the same reasons.

Now does it work? Go back into your mouseUp handler and make sure the breakpoint is still in place, then close the script window and click the Play button. By the time you’re through seeing the value for fSecondNum being set, you’ll see that it seems to be working, so you can simply click the third arrow (the run script arrow) in the Debugger’s window, which will cause the rest of your Lingo to execute. And there you are; you learn that the result of your calculations is 0.5, which is still not what we expected.

Now, 1 divided by 2.333… is not, in fact, 0.5; if you do this in the Message window:
 

-- Welcome to Director --
put 1.0 / 2.333


You see the result is less than one-half:
 

-- 0.4286


Run your program again and pay attention to the value given to fSecondNum as it appears in the Debugger. Though the variable is given a decimal, the division operation on the equation in the field is being handled before the float keyword, which means that we’re getting the integer value of 7 / 3, or 2.0­not the 2.333… we actually want.

Here the Debugger has shown us a fundamental error in the way the program operates, demonstrating that it’s not really our Lingo that is at fault any more, but rather a basic hitch in the way Director handles math. Unfortunately for this one, there is no easy fix, because there’s no simple way to force the text the user enters into floating-point values as it is being entered.