A Validating Edit Control

Syntax Check

In this case, I parse the floating point number using (a subset of) the syntax specified for the atof function. (I don't accept D or d as exponent indicators).

[whitespace] [sign] [digits] [.digits] [{ e | E} [sign] digits]

(Actually, I don't let the user type the whitespace in, but we'll talk about that below). The parsing is done via a Finite State Machine (FSM) which takes the current state and the current character and decodes against a table indicating the next state. The table is encoded as a sequence of case statements inside a switch. In each match, I can do one or more of the following

  • "Eat" the character, removing it from the input stream, or leaving it for the next state to process
  • Set the next state
  • Set an indicator as to whether the string is complete, incomplete, or erroneous.

This is encoded as shown (partially) below. To set the "indicator", I set a brush value to the pointer to a predefined brush. If the brush ever gets set to the "error" indicator, the loop ends.

  
    int state = S0;
for(int i = 0; brush != &errorBrush && i < s.GetLength();)
   { /* scan string */
    TCHAR ch = s[i];
    switch(MAKELONG(state, ch))
       { /* states */
        case MAKELONG(S0, _T(' ')):
        case MAKELONG(S0, _T('\t')):
           i++;
          continue;
        case MAKELONG(S0, _T('+')):
        case MAKELONG(S0, _T('-')):
           i++;
           brush = &partialBrush;
           state = IPART;
           continue;
      case MAKELONG(S0, _T('0')):
           ¤           ¤           ¤
      case MAKELONG(S0, _T('9')):
           state = IPART;
           continue;
      case MAKELONG(S0, _T('.')):
           i++;
           state = FPART;
           brush = &partialBrush;
           continue;
      case MAKELONG(S0, _T('E')):
      case MAKELONG(S0, _T('e')):
           i++;
           state = ESIGN;
           brush = &partialBrush;
          continue;
      case MAKELONG(IPART, _T('0')):
           ¤           ¤           ¤
      case MAKELONG(IPART, _T('9')):
           i++;
           brush = &OKBrush;
           continue;
      case MAKELONG(IPART, _T('.')):
           i++;
           brush = &OKBrush;
           state = FPART;
           continue;
      case MAKELONG(IPART, _T('e')):
      case MAKELONG(IPART, _T('E')):
           i++;
           brush = &partialBrush;
           state = ESIGN;
           continue;
      case MAKELONG(FPART, _T('0')):
      case MAKELONG(FPART, _T('9')):
           i++;
           brush = &OKBrush;
           continue;
       case MAKELONG(FPART, _T('e')):
       case MAKELONG(FPART, _T('E')):
           i++;
           brush = &partialBrush;
           state = ESIGN;
           continue;
      case MAKELONG(ESIGN, _T('+')):
      case MAKELONG(ESIGN, _T('-')):
           i++;
           brush = &partialBrush;
           state = EPART;
           continue;
      case MAKELONG(ESIGN, _T('0')):
      case MAKELONG(ESIGN, _T('1')):
           ¤           ¤           ¤
      case MAKELONG(ESIGN, _T('9')):
           state = EPART;
           continue;
      case MAKELONG(EPART, _T('0')):
           ¤           ¤           ¤
      case MAKELONG(EPART, _T('9')):
           i++;
           brush = &OKBrush;
           continue;
      default:
           brush = &errorBrush;
           continue;
     } /* states */
   } /* scan string */

To absorb a character, I just increment the pointer (i++). You can create a similar table to parse a date, time, or any other field you can define.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Debugging is anticipated with distaste, performed with reluctance, and bragged about forever.” - Dan Kaminsky