Thursday, November 12, 2015

Day 2: A forth interpreter in C, tokenizing


On our second day, we analyze each token, determine if it is a number or an operator.


/

#include 
#include 
#include 
#include 

/*
 day 2 - tokenizing.
*/

#define MAXBUFFSIZE 128
char    BUFF[MAXBUFFSIZE];   // input string buffer.

char delims[] = " \t\n";
double fval   = 0;

enum  Dtypes{ERROR, FNUM, FADD,FSUB, FMUL, FDIV, FPRINT, QUIT};

int  lexer(char* tok)
{
    int  n = strlen(tok);
    char c = *tok;
    if (n == 1){
       if (c == '+') return FADD;
       if (c == '-') return FSUB;
       if (c == '*') return FMUL;
       if (c == '/') return FDIV;
       if (c == '.') return FPRINT;
    }
    if (getnumber(tok) == 0) {
       return FNUM;
    };
    if (strcmp(tok, "quit")== 0) {
       return QUIT;
    }
    return ERROR;
}


int checknumber(char *s)
{
  /* Description
     Returns 0 if s is a double real number, otherwise nonzero value.
     It is assumed that leading and trailing white spaces are removed from s.
     1 illegal character after '.'
     2 illegal starting character.
     3 illegal character after 'e'
     4 expected a '\0'
     5 empty string.
   */
  char* t = s;
  /* empty string */
  if (*t == '\0') return 5; 
  /* starts with an optional sign? */
  if ((*t == '-') || (*t == '+'))t++;
  // block of digits starting with a digit 
  if (isdigit(*t)) { /* starts with a digit? */
  while (isdigit(*t)) t++;
  if (*t == '.'){ /* a block starting with a .? */
    t++;
    if (*t == '\0') return 0;  // a number ending with a '.'
    while (isdigit(*t)) t++;
  }
  }else if (*t == '.'){ /* a number starting with a .? */
 t++;
 if (! isdigit(*t)) return 1;
 while (isdigit(*t)) t++;
  } else return 2;  /* illegal starting value! */
    
  /* optional exponent */
  if (tolower(*t) == 'e') {
 t++;
 if ((*t == '-') || (*t == '+')) {
    t++;
 }
 if (!isdigit(*t))return 3;
 while isdigit(*t) t++;
  }
  /* illegal character */
  if (*t != '\0') return 4;
  return 0;
};


int getnumber(char *s){
   int i = checknumber(s);
   if (i==0) {
      fval = atof(s);
   }
   return i;
};

int main() {
  int i =0;
  int exitnow = 0;
  while (1) {
    fputs(">>", stdout);
    fgets(BUFF, MAXBUFFSIZE-1, stdin);
    char *tok = strtok(BUFF, delims);
    while (tok)  {
      printf("%d %s", ++i, tok);
      if (tok == NULL) break;
     
      // process token.
      int dtype = lexer(tok);
      switch(dtype) {
      case  FNUM:
            printf("FNUM %f\n",fval);
            break;
      case  FADD:
      case  FSUB:
      case  FMUL:
      case  FDIV:
      case  FPRINT:
            printf("[%s]\n", tok);  
     break;
      case  QUIT:
            printf("quitting...\n");
     exitnow = 1;
     break;
      case ERROR:
     printf("ERROR\n");
     break;
      };
      tok = strtok(NULL, delims);
    } 
    if (exitnow) {
       puts("Thanks for using easyforth!\n");
       break;
    }
  };
  return 0;
};



Here is a compilation-run of our recent program version.

toto@toto-VirtualBox:~/Projects/forth$ gcc day2-easyforth.c
toto@toto-VirtualBox:~/Projects/forth$ ./a.out
>>-1.2345 320 7 + * - 
1 -1.2345FNUM -1.234500
2 320FNUM 320.000000
3 7FNUM 7.000000
4 +[+]
5 *[*]
6 -[-]
>>quit
7 quitquitting...
Thanks for using easyforth!

In the next installment we add a simple working calculator.


No comments:

Post a Comment