Sunday, November 15, 2015

Day 4 A Forth interpreter in C, more math functions.


For today, we add most of the standard C math library functions to the basic four function calculator oriented interpreter.

#include 
#include 
#include 
#include 
#include 


/*
 day 1 - basic 
 day 2 - tokenizing.
 day 3 - basic function calculator.
 day 4 - more math functions. November 15, 2014.
*/

#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,
             FPI, FE, FABS,FNEG, FEXP, FLN, FLOG10, FSIN, FCOS, FTAN, FSINH, FCOSH, FTANH, 
             FDOWN, FUP,
             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;
    }
    // A simplistic individual brute force string comparisons 
    if (strcmp(tok, "pi")==0) return FPI; 
    if (strcmp(tok, "e")==0) return FE;
    if (strcmp(tok, "abs")==0) return FABS;
    if (strcmp(tok, "neg")==0) return FNEG;
    if (strcmp(tok, "exp")==0) return FEXP;
    if (strcmp(tok, "ln")==0)  return FLN;
    if (strcmp(tok, "log10")==0) return  FLOG10;
    if (strcmp(tok, "sin")==0) return FSIN;
    if (strcmp(tok, "cos")==0) return FCOS;
    if (strcmp(tok, "tan")==0) return FTAN;
    if (strcmp(tok, "sinh")==0) return FSINH;
    if (strcmp(tok, "cosh")==0) return FCOSH;
    if (strcmp(tok, "tanh")==0) return FTANH;

    if (strcmp(tok, "fdown")==0) return FDOWN;
    if (strcmp(tok, "fup")==0) return FUP;
    
    if (strcmp(tok, "quit")== 0) {
       return QUIT;
    };
    if (strcmp(tok, "ecr") == 0) {
       return ECR;
    }
    if (getnumber(tok) == 0) {
       return FNUM;
    };
    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) {
      // basic floating point operations.
      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;
            
            
      // Constants and One argument math functions.      
      case  FPI:   // constant pi
            FSTACK[++FP] = M_PI; break;
      case  FE:    // constant e
            FSTACK[++FP] = exp(1.0); break;
      case  FABS:  FSTACK[FP] = fabs(FSTACK[FP]); break;
      case  FNEG:  FSTACK[FP] = -FSTACK[FP]; break;
      case  FEXP:  FSTACK[FP] = exp(FSTACK[FP]); break;
      case  FLN:   FSTACK[FP] = log(FSTACK[FP]); break;
      case  FLOG10:FSTACK[FP] = log10(FSTACK[FP]); break;
      case  FSIN:  FSTACK[FP] = sin(FSTACK[FP]); break;
      case  FCOS:  FSTACK[FP] = cos(FSTACK[FP]); break;
      case  FTAN:  FSTACK[FP] = tan(FSTACK[FP]); break;
      case  FSINH: FSTACK[FP] = sinh(FSTACK[FP]); break;
      case  FCOSH: FSTACK[FP] = cosh(FSTACK[FP]); break;
      case  FTANH: FSTACK[FP] = tanh(FSTACK[FP]); break;
      case  FUP:   if (FP < MAXFSTACKSIZE-1) FP++; break; 
      case  FDOWN: if (FP) FP--; break;
      case ERROR:
     printf("ERROR\n");
     break;
      case  QUIT:
            printf("quitting...\n");
     exitnow = 1;
     break;
      };
      tok = strtok(NULL, delims);
    } 
    if (exitnow) {
       puts("Thanks for using easyforth!\n");
       break;
    }
  };
  return 0;
};
Here is a simple compilation and execution run.
toto@toto-VirtualBox:~/Projects/forth$ gcc day4-easyforth.c -lm
toto@toto-VirtualBox:~/Projects/forth$ ./a.out 
>>pi 2 / sin .
1.000000>>1.0 exp .
2.718282>>

No comments:

Post a Comment