Eval in AS3: Tips for Executing Dynamic Actionscript
It's not too often that one needs to execute a block of Actionscript which is unknown at compile time. However, in recent weeks I found myself working on a project that allows the user to enter Actionscript code to translate data values to style properties, not unlike translating HTML using CSS.
A more common use for this sort of thing might be a plotting program in which users enter formulas that they would like to see graphed.
Without too much searching you'll probably find that the best tool for this is the AS3 Eval Library at Hurlant. There is no eval method in AS3 but this project, in version 0.3 at time of writing, makes a great substitute.
Still, there were some additional things I wanted from the library and a quick wrapper class helped me achieve them. Here's what I was looking for:
- Some way to handle execution errors
- An event indicating that execution is complete
- A clean way to pass data to and from the scope of the evaluation
var evaluator:Evaluator = Evaluator.getInstance(); evaluator.addEventListener( Event.COMPLETE, evaluatorCompleteHandler, false, 0, true); evaluator.addEventListener( ErrorEvent.ERROR, evaluatorErrorHandler, false, 0, true); var expression:String = "data.value3 = data.value + data.value2;"; var data:Object = { value: 42, value2: 127, value3: null }; evaluator.eval(expression, data);
package { import com.hurlant.eval.ByteLoader; import com.hurlant.eval.Evaluator; import flash.display.Loader; import flash.events.ErrorEvent; import flash.events.Event; import flash.events.EventDispatcher; import flash.system.LoaderContext; import flash.utils.ByteArray; public class Evaluator extends EventDispatcher { protected static var _currentID:int = 0; protected static var _evaluatorCache:Array = new Array(); protected var _id:int; protected var _data:Object; public function Evaluator() { _data = new Object(); } public function get id():int { return _id; } public static function getInstance(id:int=-1):Evaluator { if (id >= 0) { return _evaluatorCache[String(id)]; } var evaluator:Evaluator = new Evaluator(); id = evaluator._id = _currentID++; _evaluatorCache[String(id)] = evaluator; return evaluator; } public function get data():Object { return _data; } public function eval(expression:String, data:Object=null):void { _data = data; // wrap the expression in a function to tighten the scope a bit expression = "function __eval(evaluator:Evaluator, data:Object)\n" + "{\n" + "\ttry\n" + "\t{\n" + "\n" + expression + "\n\n" + "\t}\n" + "\tcatch (e:Error)\n" + "\t{\n" + "\t\t__evaluator.handleError(e);\n" + "\t}\n" + "}\n" + "\n" + "var evaluator:Evaluator = Evaluator.getInstance('" + _id + "');\n" + "var data:Object = evaluator.get data();\n" + "__eval(evaluator, data);\n" + "evaluator.handleComplete();\n"; var evaluator:com.hurlant.eval.Evaluator = new com.hurlant.eval.Evaluator(); var bytes:ByteArray = evaluator.eval(expression); bytes = ByteLoader.wrapInSWF([bytes]); var context:LoaderContext = null var loader:Loader = new Loader(); loader.loadBytes(bytes, context); } public function handleError(e:Error):void { var message:String = e.errorID + "," + e.name + "," + e.message; dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, message)); } public function handleComplete():void { dispatchEvent(new Event(Event.COMPLETE)); } } }
Note that version 0.3 doesn't support packages so the Evaluator needs to be a top-level class.
Delicious
Digg
Reddit
Facebook
Google
Yahoo
Technorati

Comments
What compiler do you use for this to work? All I seem to end up with is a lot of syntax errors that stem from the Hurlant source.
Now that you mention it, I think I did have to go through the source and correct a few errors. There are also a lot of untyped variables in there but I've left those alone for now.
Any chance of sharing your corrected source? Would be very much appreciated :)
Hi again, Tor,
I just spent a bit of time trying to get through the errors being tossed up by the Flash compiler. I had managed to get it working in Flex but never in Flash—looks like the latter really doesn't want to cooperate.
Removing the question marks after several of the data types helped things but now the compiler is having trouble finding the file, com.hurlant.eval.ast.Ast. If anybody has managed to fix this, let us know!
Daniel
That's what I did as well. I also tried to comment out the line referring to the Ast abstract class, but what you end up with then is a bunch of "3596: Duplicate variable definition"
Hmm, somehow the rest of my comment disappeared from the last one. I'll add it again here:
I believe the Flash compiler follows a more strict pattern than the Flex compiler. I'm not sure though, but I /think/ I recall reading about it when I looked at alternatives to the CS3 compiler.
Regardless, like Daniel said, if anybody has a solution it would be greatly appreciated if you shared it :)
Post new comment