Metaprogramming capability has been in the .NET Framework since day 1. So it’s no surprise that the .NET Micro Framework has some basic metaprogramming capabilities, too. Most of the introductory examples you see for working in the .NET Micro Framework involve blinking an LED. So, why not blink out “Hello world” on an LED in Morse code? And because we can, why not do it in metaprogramming style?

The basic metaprogamming functionality that we’ll take advantage of in this little application is the multicast delegate. Morse code letters can be expressed as a series of dots and dashes. The dots are short signals that are 1/3 the length of the dashes. And there are rules for intra-letter spacing, intra-word spacing and extra-word spacing. The word “HELLO” might be expressed as four dots or “....” for H, a single dot “.” for E, the sequence “.-..” for L and so on. With the appropriate spacing in between, of course. The goal is to blink the LED on my Netduino to emit the correct Morse code sequence for a string.

private Hashtable _letters = new Hashtable();
public Morse(OutputPort led)
{
  _letters.Add(' ', "*");
  _letters.Add('A', ".-");
  _letters.Add('B', "-...");
  _letters.Add('C', "-.-.");
  // remainder omitted for brevity

The first step is to insert each dot-dash sequence into a dictionary keyed by each letter of the alphabet. The .NET Micro Framework 4.1 doesn’t support generic collection classes so we have to use an old-fashioned Hashtable. That’s OK because I’ll take advantage of the weak typing in the Hashtable to demonstrate memoizing my dynamic Morse code sequences in the next section. Now that I’ve got the letters and their dot-dash sequences in a dictionary, now I need some functions for the dot, dash and space parts.

private int dotLength = 200;
private void dash()
{
  _led.Write(true);
  Thread.Sleep(dotLength * 3);
  _led.Write(false);
}

private void dot()
{
  _led.Write(true);
  Thread.Sleep(dotLength);
  _led.Write(false);
}

private void intraletterspace()
{
  Thread.Sleep(dotLength);
}

private void intrawordspace()
{
  Thread.Sleep(dotLength * 3);
}

private void extrawordspace()
{
  // this is actually 7 but we'll borrow
  // the intraword space from the end of
  // the last word
  Thread.Sleep(dotLength * 4);
}

public delegate void seq();

Notice the seq delegate type declared here? The various dot, dash and space functions shown here will be assembled into a seq delegate depending on their formulations found in the _letters Hashtable. The function to do the conversion from string to multicast delegate looks like:

private seq ConvertToDelegate(string letterSeq)
{
  seq del = null;
  bool addIntraWordSpace = true;
  foreach (char c in letterSeq.ToCharArray())
  {
    switch (c)
    {
      default:
        break;
      case '*':
        del += extrawordspace;
        addIntraWordSpace = false;
        break;
      case '.':
        if (del == null)
          del = dot;
        else
        {
          del += intraletterspace;
          del += dot;
        }
        break;
      case '-':
        if (del == null)
          del = dash;
        else
        {
          del += intraletterspace;
          del += dash;
        }
        break;
    }
  }
  if (addIntraWordSpace)
    del += intrawordspace;
  return del;
}

The conversion function evaluates each dot, dash or space in the source string and assembles a seq delegate that invokes each of the appropriate functions in order. Isn’t metaprogramming fun? We’re actually treating each symbol in the string like an instruction in a little programming language and converting them into function calls. The delegate type, which is really a MulticastDelegate from the Framework, does all the hard work. When we use the += operator to add a new function call, the MulticastDelegate adds a new function to the list of those to be invoked. The last step is to invoke the sequences as letters are encountered in a source string like “Hello world”.

public void Emit(string text)
{
  if (text == null)
    return;
  text = text.Trim().ToUpper();
  if (text.Length == 0)
    return;

  foreach (char c in text.ToCharArray())
  {
    char actual = c;
    if (!_letters.Contains(c))
      actual = ' ';
    object s = _letters[actual];
    if (s is string)
    {
      // memoize the multicast delegate for each letter
      _letters[actual] = s = ConvertToDelegate(s as string);
    }
    (s as seq)();
  }
}

The code is simple enough. It just iterates over the characters in an input string, converts the associated sequence characters to seq delegates and invokes them. However, like any good metaprogramming application, we always look for chances to optimize the code generation steps. They are the most expensive so caching the results of our little “dot-dash compiler” is really important. The weak typing of the Hashtable makes this easy enough. When a new letter is encountered during emission of the phrase, it’s compiled into a seq delegate and cached in the Hashtable in place of the original string (source code) that was used to create it. A bit of reflection is used to determine whether or not the object in the value part of the Hashtable is a string (my dot-dash style source code) or a seq delegate that can actually be invoked to flash the LED on my Netduino. Once a letter has been compiled, it never has to be compiled again while the application is running.

The single C# source code file for this fun, little application can be downloaded here.