Does Applescript have a function equivalent to Javascript’s or PHP’s eval()?
The closest I’ve been able to come is the run script command. However, this doesn’t appear to work on user defined subroutines. Given the following script
-- will display the dialog
test("This is a test")
-- will also display the dialog
run script ("display dialog "this is a test"")
--throws an error
--<> doesn't understand the test message
run script ("test("This is a test")")
on test(theString)
display dialog theString
end test
The first two commands perform as expected, the third throws an error.
I’m not an expert on this, but I think I know what’s wrong with the third example. It’s trying to run a subroutine that is NOT part of the script. When you do a “run script” command you are asking AppleScript to run an independent unit separate from the current script.
I don’t know if there’s anyway to get the “inner script” to recognize a subroutine (handler, in AppleScript-ese) outside of its scope.
What does the eval() function in php do, and why is it better than simply calling the function name manually?
Hmm… thanks to Matt Neuberg’s excellent book, I figured out a way to do what you are trying. Basically, you can pass an argument into the script so that it can refer back to your scope.
run script ("on run {inArgs}
(topLevel of inArgs)'s test("This is a test")
end") with parameters {topLevel:me}
on test(theString)
display dialog theString
end test
Not sure if this is the best way to do this but I hope it helps!
Nice one redsweater. Here’s another way but it only runs when the script is saved as an application. Here I named the script app “MyScriptApp”.
run script (“property parent: application “MyScriptApp”
t(“This is a test”)”)
on t(theString)
display dialog theString
end t
EDITTED: this gets the name:
set my_name to (name of (info for (path to me)))
set p_text to “property parent: application “” & my_name & “”” & return
run script p_text & “t(“This is a test”)”
Actually, you’re probably better off specifying the complete path, not just the name of the application in case you have more than one application with the same name (this also saves a few steps as you don’t have to get info for the app):
Also, using redsweater’s method, I would modify it a bit like so:
Because ‘run script’ is an osax, what gets passed to the ‘run {inArgs}’ handler is a copy of the main script, not a reference to it. That means your sub-script can’t modify properties in the main script. (This may not always be a disadvantage, mind you.) It also makes it slower; though ‘run script’ is pretty slow anyway, and this is only a problem if you’re calling it a lot.
‘run script’ may blow up when passed very large, complex values due to brittleness and internal limitations in the AppleScript interpreter.
To get around these problems, use ‘run script’ to compile and return a script object, then call that object’s handler(s) locally, e.g.:
on test(s)
display dialog s
end test
set scriptObj to run script "
script
on doSomething(mainScript)
mainScript's test("This is a test")
end doSomething
end script"
scriptObj's doSomething(me)
Best solution, of course, is to avoid evals whenever possible. The number of times you genuinely need an eval() is considerably smaller than the number of times you only think you need it. Aside from the security implications there’s usually simpler, non-hokey ways of achieving the same ends in-code.