LessonsExpert: BeyondLesson 50

Code that runs code

Build xTalk as text and run it: do, value, calling handlers by a name worked out at run time, merge with code in it, and when not to.

  • Expert
  • Lesson 50 of 83
  • About 10 minutes
  • 8 examples

do: run text as statements

xTalk can run xTalk it has just put together. do takes text and runs it as statements, one line or many, as if they were written where the do is, with the same variables:

ExampleRun it in the playground
put "put 6 * 7" into tCode
do tCode
put 5 into tA
do "put tA * 2 into tB" & return & "add 1 to tB"
put tB
Output
42
11

Text that is not xTalk is an error when the do runs it, which a try catches like any other (Handling errors):

ExampleRun it in the playground
try
   do "put 1 +"
catch tError
   put "That was not a statement xTalk could run."
end try
Output
That was not a statement xTalk could run.

value: work out an expression

value is do for expressions: it works out the text as an expression and returns its value. With an object as its second parameter, the expression is worked out as if in that object's script, so me and its handlers are the object's:

ExampleRun it in the playground
put value("3 + 4 * 2")
put value("the length of" && quote & "hello" & quote)

create button "Greeter"
set the script of it to "function greet pWho" & return & \
      "return " & quote & "Hello, " & quote & " & pWho" & return & "end greet"
put value("greet(" & quote & "Ada" & quote & ")", button "Greeter")
Output
11
5
Hello, Ada

Calling a handler by its name

Most of the time, what you want is not to run any code at all, but to call a handler whose name you work out as the program runs: the handler for a menu item, a command typed by the user, a step in a list. send and dispatch take the message as text, so the name can come from anywhere. dispatch leaves handled or unhandled in it, so a name with no handler is easy to spot:

ExampleRun it in the playground
on sayHello
   put "hello"
end sayHello

on sayGoodbye
   put "goodbye"
end sayGoodbye

repeat for each item tWhat in "Hello,Goodbye,Thanks"
   dispatch "say" & tWhat to me
   if it is "unhandled" then put "nobody says" && tWhat
end repeat
Output
hello
goodbye
nobody says Thanks

For a function, dispatch function calls it with the parameters after with, and leaves what it returned in the result:

ExampleRun it in the playground
function double pN
   return pN * 2
end double

function square pN
   return pN * pN
end square

repeat for each item tName in "double,square"
   dispatch function tName to me with 9
   put tName && "of 9 is" && the result
end repeat
Output
double of 9 is 18
square of 9 is 81

merge with code in it

merge (Formatting) also runs code written between <? and ?>: whatever the code returns takes its place. It suits a template with a decision in it:

ExampleRun it in the playground
put 3 into tCount
put merge("<?return tCount * 2?> eggs")
put merge("<?if tCount > 2 then return " & quote & "Lots of" & quote & " else return " & quote & "A few" & quote & "?> eggs")
Output
6 eggs
Lots of eggs

Never run text you did not write

do, value and merge run whatever they are given, with all the power of your program: they can delete files, send data anywhere, or change any object. Never give them text that came from outside the program (a field the user typed in, a file, a web page) as it is. A calculator that works out what the user types must first make sure it is only a sum:

ExampleRun it in the playground
function calculate pExpression
   if not matchText(pExpression, "^[0-9+*/(). -]+$") then
      return "not a sum I will work out"
   end if
   try
      return value(pExpression)
   catch tError
      return "not a sum I can work out"
   end try
end calculate

put calculate("(2 + 3) * 4")
put calculate("1 / 8")
put calculate("delete file myNotes")
put calculate("2 +")
Output
20
0.125
not a sum I will work out
not a sum I can work out

The pattern (Patterns) lets through only digits, the four signs of arithmetic, brackets, points and spaces, so nothing the user types can be a command. Check what is let in, not what is kept out: a list of forbidden words always misses one.

Try it yourself

Write a tiny command interpreter: for each line of add 5, add 10, times 3, shout and add 1, call the handler named cmd followed by the line's first word (so cmdAdd, cmdTimes), with the line's second word as its parameter. The handlers change a running total. A line with no handler puts unknown command: and the line. Put the total at the end.

Show a solution
ExampleRun it in the playground
local sTotal

on cmdAdd pN
   add pN to sTotal
end cmdAdd

on cmdTimes pN
   multiply sTotal by pN
end cmdTimes

put 0 into sTotal
put "add 5" & return & "add 10" & return & "times 3" & return & "shout" & return & "add 1" into tProgram
repeat for each line tLine in tProgram
   dispatch ("cmd" & word 1 of tLine) to me with word 2 of tLine
   if it is "unhandled" then put "unknown command:" && tLine
end repeat
put "total:" && sTotal
Output
unknown command: shout
total: 46

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.