Friday, March 14, 2014

A new Forth interpreter written in C


/
/*
File      forth-and-c.c
Author    Ernesto P. Adorio
          ernesto.adorio@gmail.com
Desc
This simple forth interpreter reads from the command line based on the following
prescription.

loop:
  read
  eval
  print

This version uses double floating point numbers for ease in applications.
Version   0.0.1 march 12, 2014
Compile   gcc -std=gnu99 forth-and-c.c  
*/

#include 
#include 
#include 
#include 



/* Constants */
char* ByeMessage = "Thanks for trying out forth-and-c.\n";
char* WelcomeMessage="Welcome to Forth-and-C Version 0.0.1, March 12, 2014.\n";
    
/* Global interpreter variables. */



#define BUFFMAXLEN 1024 
#define EOI '\0'
 char BUFF[BUFFMAXLEN];
 char * tokenstart, *tokenend = BUFF;
 
 #define MAXSTACKDEPTH 10
 union {
   double f;
   long int i;
 }OPSTACK[MAXSTACKDEPTH];
 int opindex = -1;


char PROMPT[32]= ">> ";

/* token system */
double fnum;  /* number read */
enum tokentype  { FPLUS, FMINUS, FDIV, FMUL, DOT,   BYE,  EMIT,   CR, EOL, FNUM, UNDEFINED};
char* symbols[] ={  "+",  "-",    "/",  "*",  ".",  "bye","emit", "cr" };
int NSYMBOLS=8;

char* errmssgs[] = {"NOERROR", "OVERFLOW", "UNDERFLOW"};
enum errorcodes {NOERROR, OVERFLOW, UNDERFLOW};
int error = NOERROR;


int lex(void)
{  
   /* process token pointed to by token start */
   if (*tokenstart==EOI) {
     return EOL;
   }
   for (int i = 0; i < NSYMBOLS; i++){
      if (strcmp(tokenstart, symbols[i]) == 0){
         return i;
      }   
   }
   
   /* Pure Forth assumes a number only after trying other token types */
   fnum = strtod(tokenstart,NULL);
   return FNUM;
   
   /* Error at this stage! */
   return UNDEFINED;
}p
  
  
int parse(void)
/* Parsing system, splits input line into tokens */
{
   tokenstart = tokenend;
   /* Find location of next space character. */ 
   while (! isspace(*tokenend) &&  *tokenend !=EOI)tokenend++;
   if (*tokenend != EOI){
      *tokenend++='\0';
      return 1;
   } else {
     /* reset tokenend */
     return EOL;
   } 
}


int execute(int toktype)
{
  switch(toktype) {
    case FNUM:if (opindex < MAXSTACKDEPTH) {
                 OPSTACK[++opindex].f = fnum;
              } 
       break;
    case DOT: if (opindex >= 0){
                 printf("%f ", OPSTACK[opindex--].f);
       } else {
  error= UNDERFLOW;
       }; 
       break;
    case FPLUS:  
              OPSTACK[--opindex].f += OPSTACK[opindex + 1].f;
              break;
    case FMINUS:
              OPSTACK[--opindex].f -= OPSTACK[opindex + 1].f;
              break;
    case FMUL:
              OPSTACK[--opindex].f *= OPSTACK[opindex + 1].f;
              break;
    case FDIV:
              OPSTACK[--opindex].f /= OPSTACK[opindex + 1].f;                    
              break;
    case EMIT:putchar( (int) (OPSTACK[opindex--].f));
       break;
    case CR:  /* carriage return */
              putchar(10); putchar(13);
              break;
    default:  break;                  
  };    
};


int main()
{
   int ntokens = 0;
   /* print welcome.*/
   printf("%s", WelcomeMessage);
   
   while (1) {
      /* read input */
      printf("%s", PROMPT);
      fgets(BUFF, BUFFMAXLEN, stdin);
      
      /*reset tokenizer */
      tokenend=BUFF;              
      while (parse()!= EOL) {
 ntokens ++;
 int tokentype = lex();
 // printf("%d: %s %d\n",ntokens, tokenstart, tokentype);
 execute(tokentype);
 
 if (tokentype == BYE) {
   printf("%s", ByeMessage);
  
   return (0);
 }  
 if (error) {
   printf("%s", errmssgs[error]);
   switch(error){
     case UNDERFLOW:opindex= -1; break ;
     case OVERFLOW: opindex= MAXSTACKDEPTH - 1; break;
     default: break;
   }  
   error = 0;
 }
      }
   }
}


We have decided to rewrite our Forth interpreter written in C. Default data type are double floating point numbers and this allows you to use it for processing simple arithmetic problems.

Compile the above using the command line invocation

gcc -std=gnu99 forth-and-c.c
Then run the program by typing ./a.out Type the expression 4 3 * 1 - . You should get the answer 11.