
On 3/18/2010 12:12 PM, Manjunath Kudlur wrote:
Hello,
I want to create a DSL where users are allowed to create "free" variables and then use them are parameters and local variables in their mini-program. For example, I want to have something like
int_ a,b,c;
Trouble. These all have the same type.
Program(&a,b) [c = b*2, a = c]
The parameter with the '&' prefix represents output, the ones without represent input. So, Program(&a, b)[...] should behave as a function object with prototype void foo(int&, const int), and I should be able to do the following :
int ra; BOOST_AUTO(pr, Program(&a,b) [c = b*2, a = c]); pr(ra, 4);
I am seeking some advice on implementing the "evaluator" for this language. In particular, where do I store the values corresponding to these free variables? To draw parallels with Boost.Phoenix, in phoenix, arg1, arg2.. etc are uniquely typed, and the evaluator just creates a fusion::vector<...> with the values passed in, and argN evaluation just returns fusion::at_c<N>(env.args()). In my case, I could create a unique ID for each free variable, and create a std::map from ID to value, and use that as the environment. Evaluator of the free variables can then look up that map to find values.
Tricky. I can imagine a scheme where, as you build the program expression, you walk the expression and keep a map<void*,int> from parameters addresses (e.g. int_*) to monotonically increasing slot numbers; as you go, you can replace the parameters with their slot numbers. You end up with a program where each "a" is replaced with "slot(0)", each "b" with "slot(2)" and each "c" with "slot(3)" (where "slot" is some type that wraps a runtime int). Later, when evaluating the program, the parameters to the program are put in a std::vector, and the evaluator knows to evaluate "slot" terminals by indexing into the vector. Handling out parameters requires special handling, as would handling expressions where not all parameters have the same type. This is essentially the same as what you describe above, except the expensive mapping is done by the code the builds the program rather than the code that evaluates it. If you're not wedded to the use of BOOST_AUTO, you could do the mapping during a single tree traversal when you assign the program to, say, a boost::function with the right signature.
But I consider this too heavy weight. In contrast, the fusion::vector<...> would incur very little overhead during run time. I will be grateful for any suggestions on other ways to implement the evaluator.
Hope the above gets you moving in the right direction, -- Eric Niebler BoostPro Computing http://www.boostpro.com