Here's an evil C state-machine-based time conversion routine.  It
walks through the text backwards and when it hits a digit, it fires
the state machine off.  If it matches, the result gets copied into the
buffer, otherwise it continues the char-by-char copy until the next
candidate for a match comes around.  I imagine it finishes its job
before the perl, ruby, or javascript runtimes even get a chance to
start. ;) It did take quite a bit longer to write and debug, though.

I make no claims about the quality of the code; I did it in a hurry
and don't have any more time to put into it.  It does pass a lot more
test cases than I included in the source, though.

                --Levi

#include <stdio.h>
#include <string.h>

char *ritoa(int n, char *s) {
  int i;
  i = 0;
  do {
    *(s--) = "0123456789"[n % 10];
  } while ( n /= 10 );
  return s;
}


/*
   A state machine to convert strings from hours:minutes:seconds format
   to seconds
*/

enum {
  SECOND_OR_FRAC,
  SECOND,
  SECOND_2,
  MINUTE,
  MINUTE_2,
  HOUR,
  HOUR_2,
  FINISHED
};

#define CASE_DIGIT case '0': case '1': case '2': case '3': case '4': \
                   case '5': case '6': case '7': case '8': case '9'

#define ACCUM(x) acc += ((x)-'0') * placeval; placeval *= 10
#define ACCUM2(x) if (placeval > 10) goto reject; ACCUM(x)

#define RESET() acc = 0; placeval = 1

#define STORE_FRAC() frac += acc; RESET()
#define STORE_SEC() if (acc > 60 || placeval != 100) goto reject; \
		    seconds += acc; RESET()
#define STORE_MIN() if (acc > 60 || placeval != 100) goto reject; \
                    seconds += (acc*60); RESET()
#define STORE_HOUR() seconds += (acc * 3600); RESET()

char *convert(char *str, char *begin, char **out) {
  int state = SECOND_OR_FRAC;
  int acc = 0;
  int frac = 0;
  int seconds = 0;
  int placeval = 1;
  char *p = str;
  char *q;

  while (state != FINISHED) {
    switch (state) {
    case SECOND_OR_FRAC:
      switch (*p) {
      CASE_DIGIT:	ACCUM(*p); break;
      case '.':		STORE_FRAC(); state = SECOND; break;
      case ':':		STORE_SEC(); state = MINUTE; break;
      default:		STORE_SEC(); state = FINISHED;
      }
      if (p == begin) {	STORE_SEC(); state = FINISHED; }
      break;
    case SECOND:
      switch (*p) {
      CASE_DIGIT:	ACCUM2(*p); state = SECOND_2; break;
      default:		goto reject;
      }
      if (p == begin) {	STORE_SEC(); state = FINISHED; }
      break;
    case SECOND_2:
      switch (*p) {
      CASE_DIGIT:	ACCUM2(*p); break;
      case '.':		goto reject;
      case ':':		STORE_SEC(); state = MINUTE; break;
      default:		STORE_SEC(); state = FINISHED;
      }
      if (p == begin) {	STORE_SEC(); state = FINISHED; }
      break;
    case MINUTE:
      switch (*p) {
      CASE_DIGIT:	ACCUM2(*p); state = MINUTE_2; break;
      default:		goto reject;
      }
      if (p == begin) {	STORE_MIN(); state = FINISHED; }
      break;
    case MINUTE_2:
      switch (*p) {
      CASE_DIGIT:	ACCUM2(*p); break;
      case '.':		goto reject;
      case ':':		STORE_MIN(); state = HOUR; break;
      default:		STORE_MIN(); state = FINISHED;
      }
      if (p == begin) {	STORE_MIN(); state = FINISHED; }
      break;
    case HOUR:
      switch (*p) {
      CASE_DIGIT:	ACCUM(*p); state = HOUR_2; break;
      default:		goto reject;
      }
      if (p == begin) {	STORE_HOUR(); state = FINISHED; }
      break;
    case HOUR_2:
      switch (*p) {
      CASE_DIGIT:	ACCUM(*p); break;
      case '.':		goto reject;
      case ':':		goto reject;
      default:		STORE_HOUR(); state = FINISHED;
      }
      if (p == begin) {	STORE_HOUR(); state = FINISHED; }
      break;
    }
    p--;
  }

  if (frac) {
    q = ritoa(frac, *out);
    *(q--) = '.';
    q = ritoa(seconds, q);
  } else {
    q = ritoa(seconds, *out);
  }
  *out = q;

  return p+1;

 reject:
  return 0;
}

void convert_expression(char *str, char *begin, char **out) {
  char *p;
  int done = 0;
  while (str >= begin) {
    /* seek a digit, copying from str to *out */
    switch (*str) {
    CASE_DIGIT: 
      p = convert(str, begin, out);
      if (p) {
	str = p;
	if (p == begin) str--;
      }	else {
	/* seek something that isn't a deformed time pattern */
	done = 0;
	while (!done) {
	  switch (*str) {
	  CASE_DIGIT:
	  case '.':
	  case ':':
	    *((*out)--) = *(str--); break;
	  default:
	    done = 1;
	  }
	}
      }
      break;
    default:
      *((*out)--) = *(str--);
    }
  }
}

int main(void) {
  char output[10000] = {};
  char test[] = "23 +12 /14:25:35.124 - 16*125/cat";

  char *p;

  p = output + 9999;
  *(p--) = 0;

  convert_expression(test + strlen(test) - 1, test, &p);

  printf("%s\n", p+1);
}
/*
PLUG: http://plug.org, #utah on irc.freenode.net
Unsubscribe: http://plug.org/mailman/options/plug
Don't fear the penguin.
*/

Reply via email to