Friday, November 13, 2015

Day 3: A Forth interpreter in C, basic four function calculator.


It is day 3 for our Forth interpreter in C. To show that we wrote already has capabilities to compute simple arithmetic we add an evaluator routines inside the main function to the current code.


#include 
#include 
#include 
#include 

/*
 day 2 - tokenizing.
 day 3 - 11.13.2015 basic function calculator.
*/

#define MAXBUFFSIZE 128
#define MAXFSTACKSIZE 32

double FSTACK[MAXFSTACKSIZE];
int    FP = 0;                // index to FSTACK;

char    BUFF[MAXBUFFSIZE];   // input string buffer.

char   delims[] = " \t\n";   // delimiters.
double fval     = 0.0;


enum  Dtypes{ERROR, FNUM, FADD,FSUB, FMUL, FDIV, FPRINT, 
             ECR,  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;
    }
    if (strcmp(tok, "ecr") == 0) {
       return ECR;
    }
    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:
            FSTACK[++FP]= fval; break;
      case  FADD:
            FP--; FSTACK[FP]+= FSTACK[FP+1]; break;  
      case  FSUB:
            FP--; FSTACK[FP]-= FSTACK[FP+1]; break;  
      case  FMUL:
            FP--; FSTACK[FP]*= FSTACK[FP+1]; break;  
      case  FDIV:
            FP--; FSTACK[FP]/= FSTACK[FP+1]; break;  
      case  FPRINT:
            printf("%f", FSTACK[FP]); break;
      case  ECR: // prints new line
            printf("\n"); 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;
};

As a test, we compute, in infix notation, (12 + 34) * -45 + 120) / 456.

toto@toto-VirtualBox:~/Projects/forth$ ./a.out
>>12 34 + -45 * 120 + 456 / .
-4.276316>>quit
quitting...
Thanks for using easyforth!

Using a scientific calculator, gives the result 4276315789.

No comments:

Post a Comment