Every programmer debugs
A bug is a difference between what you meant and what you wrote. Finding one is not a sign you are bad at programming: it is most of programming. The trick is to stop guessing and start looking. This lesson is a toolbox for looking.
1. Read the error
When a script stops, xTalk tells you where and why. The playground highlights the line and explains in plain words; the Script Editor shows the error under the script and puts its line in view. Read the whole message: it usually names the problem exactly.
put "apple,pear,fig" into tFruit
put the number of items of tFruit into tCount
put tCount / (tCount - 3)
Here, line 3 divides by zero, because there are exactly three items. The message says so, and the line number says where.
2. Look at the values: put
The quickest way to see what a script is doing is to make it tell you. Put the values you are unsure of as it runs, with a word to say which is which:
function average3 pA, pB, pC
put pA + pB + pC / 3 into tResult
put "trace: sum part =" && pA + pB && "result =" && tResult
return tResult
end average3
put average3(3, 6, 9)
trace: sum part = 9 result = 12 12
The average of 3, 6 and 9 is 6, not 12. The trace shows the sum is right, so the division is wrong: / goes before +, so only pC was divided. Brackets fix it:
function average3 pA, pB, pC
return (pA + pB + pC) / 3
end average3
put average3(3, 6, 9)
6
In a stack, a put with no container goes to the Message Box (Tools > Message Box), where it replaces what was there. To keep a running log, put each value after msg with a return, or write to a field of your own.
3. Stop and look around: the debugger
Click in the Script Editor's left margin, next to a line, to set a breakpoint: a red dot. When the script reaches that line it pauses, and the Script Editor becomes the debugger. Now you can:
- see every variable of the handler and its value, in the Variables pane at the bottom;
- Step Into the next line (and into any handler it calls), Step Over it, or Step Out of the handler;
- look at the stack trace: which handler called this one, and which called that;
- Run on to the next breakpoint, or Stop.
A script can set a breakpoint too, with the command breakpoint, handy for pausing only in an unusual case:
on mouseUp
repeat with i = 1 to 100
put someCalculation(i) into tValue
if tValue < 0 then breakpoint
end repeat
end mouseUp
function someCalculation pN
return 50 - pN
end someCalculation
4. Test in small pieces
A long handler that does everything is hard to debug, because a wrong answer could come from anywhere in it. Split the work into small functions, and check each with a few values whose answers you know. When one fails, you know exactly where to look:
function initials pName
put empty into tLetters
repeat for each word tWord in pName
put toUpper(char 1 of tWord) after tLetters
end repeat
return tLetters
end initials
command expect pGot, pWanted
if pGot is pWanted then
put "ok" && quote & pGot & quote
else
put "FAILED: got" && pGot && "but wanted" && pWanted
end if
end expect
expect initials("ada lovelace"), "AL"
expect initials("Grace Brewster Hopper"), "GBH"
expect initials(""), ""
ok "AL" ok "GBH" ok ""
Keep checks like these in your stack, and run them again after every change: they catch a new bug in old code the moment you make it.
5. The usual suspects
Most bugs in xTalk are one of a handful. Check these first:
- A misspelled variable, which xTalk reads as its own name, as text. Turn on Variable Checking (Edit > Options > Variable Checking in the Script Editor) and these become errors (Where variables live).
- A misspelled property, which silently becomes a custom property (Properties).
- The wrong order of operations, as above: add brackets.
- A trailing return or space in a value, which makes
issay false: comparethe lengthof the two values. - A message that never arrives: a handler further along the message path caught it and did not
passit. - The wrong itemDelimiter (Chunks), left set to something else by earlier code.
put "yes" & return into tAnswer
put tAnswer is "yes"
put the length of tAnswer
put line 1 of tAnswer is "yes"
false 4 true
Try it yourself
This function should count the words longer than three letters. There are five in the sentence, but it says 0. Add a trace to the loop to find out why, then fix it.
function longWords pText
repeat for each word tWord in pText
put 0 into tCount
if the length of tWord > 3 then add 1 to tCount
end repeat
return tCount
end longWords
put longWords("the quick brown fox jumps over the lazy dog")
0
Show a solution
A trace after the if shows the count each time round: it goes to 1 for each long word, then back to 0 for the next word. The put 0 into tCount is inside the loop, so it starts again every time, and the last word, dog, leaves it at 0. Move it above the loop:
function longWords pText
put 0 into tCount
repeat for each word tWord in pText
if the length of tWord > 3 then add 1 to tCount
end repeat
return tCount
end longWords
put longWords("the quick brown fox jumps over the lazy dog")
5
Words in this lesson
Each one finds every lesson that uses it. The Dictionary has the whole story of each, with its syntax and more examples.