There are many approaches to implementing user-entered DSLs. In my application, I wanted a user to enter a DSL and show its result immediately (pseudo real-time) on the screen. I wanted natural text input for the creation of directed graphs with simple statements. For example, I wanted the statement edge a b to create something like this:

There are many ways of doing this, of course, but I went for something simple: using reflection to transform strings into executable code. What I mean is that when someone writes edge a b I really want them to call edge("a", "b") in a certain class.

public void edge(string source, string target)
{
  currentEdge = graph.AddEdge(source, target);
}

With this in mind, I had to implement a simplistic parser that would find the method for me (without paying the cost of reflection). A basic string.Split() got me the constituent parts and I managed to find the right method:

public void AddInstruction(string line)
{
  if (string.IsNullOrEmpty(line)) return;
  foreach (Match m in Regex.Matches(line, "([\"'])(?:\\\\\\1|.)*?\\1"))
    line = line.Replace(m.Value, m.Value.Replace(' ''`'));
  string[] parts = line.Split(' ').Select(p => p.Replace('`'' ').Unquote()).ToArray();
  if (parts.Length < 1) return;
  // having acquired the parts, see if there's a matching method
  MethodInfo mi = null;
  if (cachedMethodInfo.ContainsKey(new KeyValuePair<string,int>(parts[0], parts.Length - 1)))
    mi = cachedMethodInfo[new KeyValuePair<stringint>(parts[0], parts.Length - 1)];
  else
  {
    var methods = GetType().GetMethods().Where(m => m.Name.ToLower() == parts[0]
      && m.GetParameters().Length == (parts.Length - 1));
    if (methods.Any())
    {
      mi = methods.First();
      cachedMethodInfo.Add(new KeyValuePair<stringint>(mi.Name, parts.Length - 1), mi);
    }
  }
  if (mi != null)
  {
    var pars = mi.GetParameters();
    object[] ps = new object[0];
    if (pars.Length == parts.Length - 1)
    {
      // try building a parameter array
      ps = new object[pars.Length];
      for (int i = 0; i < pars.Length; i++)
      {
        var par = pars[i];
        var source = parts[i + 1];
        ps[i] = ConvertString(source, par.ParameterType);
      }
    }
    // parameters ready - call it
    try { mi.Invoke(this, ps); }
    catch (Exception ex) { }
  }
}

You’ll notice that the MethodInfo for a particular method is cached: I keep a Dictionary<KeyValuePair<string,int>,MethodInfo> with the key being a pair of the method name and number of parameters.

Does it work? It sure does, and the evaluations run in real-time: the graph gets re-rendered (and this is GDI+ rendering, mind you) after each keystroke. I am still polishing the details, but here’s an illustration of something more complex:

As I said, this extension to my app is still in the works, but already I feel like I’ve added value to the application. All withoug concerning myself with F#, DLR, MPS and other exotic ways of implementing DSLs. All it took is some C# debugging, and here we are!