Working with variables in the analyzer using the example of the C language

Lecture



In all programming languages, many calculators and spreadsheets, variables are provided to keep values ​​for future use. In order for the parser from the previous example to have this feature, it is necessary to make some additions to it. First, these are, of course, the variables themselves. As mentioned above, the analyzer will recognize only variables with names from A to Z. (However, if you wish, you can get rid of this restriction.) Each variable is stored in one cell of an array of 26 double- type elements. Therefore, the following fragment must be added to the source code of the analyzer:

  double vars [26] = {/ * 26 user variables, AZ * /
   0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
   0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
   0.0, 0.0, 0.0, 0.0, 0.0, 0.0
 };

As you noticed, for the convenience of the user, all variables are initialized with zeros.

In addition, you will need a procedure to get the value of the specified variable. Since variable names are letters from A to Z, they can be used to index the vars array by subtracting the ASCII code of the letter A from the variable name. The following function is find_var () , which returns the value of a variable:

  / * Get the value of a variable.  * /
 double find_var (char * s)
 {
   if (! isalpha (* s)) {
     serror (1);
     return 0;
   }
   return vars [toupper (* token) - 'A'];
 }

This function is written so that it takes names of any length, but only the first character is significant. This limit can be changed to suit your needs.

It is also necessary to modify the atom () function so that it processes both numbers and variables. Below is the new version:

  / * Getting the value of a number or variable.  * /
 void atom (double * answer)
 {
   switch (tok_type) {
     case VARIABLE:
       * answer = find_var (token);
       get_token ();
       return;
     case NUMBER:
       * answer = atof (token);
       get_token ();
       return;
     default:
       serror (0);
   }
 }

From a technical point of view, this is all that is required for the analyzer to correctly process variables. However, there is no way to assign values ​​to these variables. Often this is done outside the analyzer, but in the analyzer you can consider the equal sign as a sign of an assignment operation and make the processing of this sign a part of the analyzer. This can be achieved in several ways. One of them is to add to the analyzer the eval_exp1 () function shown below:

  / * Assignment processing.  * /
 void eval_exp1 (double * result)
 {
   int slot, ttok_type;
   char temp_token [80];

   if (tok_type == VARIABLE) {
     / * save old lexeme * /
     strcpy (temp_token, token);
     ttok_type = tok_type;

     / * calculate variable index * /
     slot = toupper (* token) - 'A';

     get_token ();
     if (* token! = '=') {
       putback ();  / * return the current variable * /
       / * restore old star lexeme is not an assignment * /
       strcpy (token, temp_token);
       tok_type = ttok_type;
     }
     else {
       get_token ();  / * get the next part of the expression * /
       eval_exp2 (result);
       vars [slot] = * result;
       return;
     }
   }

   eval_exp2 (result);
 }

As you can see, this function has to look ahead to determine whether assignment is actually performed. This is due to the fact that the variable name is always in front of the assignment operator, but the mere existence of the variable name does not guarantee that the assignment will follow. In other words, the analyzer will perceive the expression A = 100 as an assignment, and he can determine that A / 10 is not. To do this, the eval_exp1 () function reads the next token from the input stream. If this token is not an equal sign, it is returned using the putback () function to the input stream for later use:

  / * Return the token to the input stream.  * /
 void putback (void)
 {
   char * t;

   t = token;
   for (; * t; t ++) prog--;
 }

Below is the full text of the improved analyzer:

  / * This module contains a recursive downward
    variable parser
 * /

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

 #define DELIMITER 1
 #define VARIABLE 2
 #define NUMBER 3

 extern char * prog;  / * pointer to the expression being analyzed * /
 char token [80];
 char tok_type;

 double vars [26] = {/ * 26 user variables, AZ * /
  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
  0.0, 0.0, 0.0, 0.0, 0.0, 0.0
 };

 void eval_exp (double * answer), eval_exp2 (double * answer);
 void eval_exp1 (double * result);
 void eval_exp3 (double * answer), eval_exp4 (double * answer);
 void eval_exp5 (double * answer), eval_exp6 (double * answer);
 void atom (double * answer);
 void get_token (void), putback (void);
 void serror (int error);
 double find_var (char * s);
 int isdelim (char c);

 / * Analyzer entry point.  * /
 void eval_exp (double * answer)
 {
   get_token ();
   if (! * token) {
     serror (2);
     return;
   }
   eval_exp1 (answer);
   if (* token) serror (0);  / * last lexeme must be zero * /
 }

 / * Assignment processing.  * /
 void eval_exp1 (double * answer)
 {
   int slot;
   char ttok_type;
   char temp_token [80];

   if (tok_type == VARIABLE) {
     / * keep old lexeme * /
     strcpy (temp_token, token);
     ttok_type = tok_type;
     / * calculate variable index * /
     slot = toupper (* token) - 'A';

     get_token ();
     if (* token! = '=') {
       putback ();  / * return the current token * /
       / * restore the old token is not an assignment * /
       strcpy (token, temp_token);
       tok_type = ttok_type;
     }
     else {
       get_token ();  / * get the next part of the expression * /
       eval_exp2 (answer);
       vars [slot] = * answer;
       return;
     }
   }
   eval_exp2 (answer);
 }

 / * Addition or subtraction of two terms.  * /
 void eval_exp2 (double * answer)
 {
   register char op;
   double temp;

   eval_exp3 (answer);
   while ((op = * token) == '+' || op == '-') {
     get_token ();
     eval_exp3 (& temp);
     switch (op) {
       case '-':
         * answer = * answer - temp;
         break;
       case '+':
         * answer = * answer + temp;
         break;
     }
   }
 }

 / * Multiplication or division of two factors.  * /
 void eval_exp3 (double * answer)
 {
   register char op;
   double temp;

   eval_exp4 (answer);
   while ((op = * token) == '*' || op == '/' || op == '%') {
     get_token ();
     eval_exp4 (& temp);
     switch (op) {
       case '*':
         * answer = * answer * temp;
         break;
       case '/':
         if (temp == 0.0) {
           serror (3);  /* division by zero */
           * answer = 0.0;
         } else * answer = * answer / temp;
         break;
       case '%':
         * answer = (int) * answer% (int) temp;
         break;
     }
   }
 }

 / * Exponentiation * /
 void eval_exp4 (double * answer)
 {
   double temp, ex;
   register int t;

   eval_exp5 (answer);
   if (* token == '^') {
     get_token ();
     eval_exp4 (& temp);
     ex = * answer;
     if (temp == 0.0) {
       * answer = 1.0;
       return;
     }
     for (t = temp-1; t> 0; --t) * answer = (* answer) * (double) ex;
   }
 }

 / * Calculate unary + and -.  * /
 void eval_exp5 (double * answer)
 {
   register char op;

   op = 0;
   if ((tok_type == DELIMITER) && * token == '+' || * token == '-') {
     op = * token;
     get_token ();
   }
   eval_exp6 (answer);
   if (op == '-') * answer = - (* answer);
 }

 / * Handle the expression in brackets.  * /
 void eval_exp6 (double * answer)
 {
   if ((* token == '(')) {
     get_token ();
     eval_exp2 (answer);
     if (* token! = ')')
       serror (1);
     get_token ();
   }
   else atom (answer);
 }

 / * Get the value of a number or variable.  * /
 void atom (double * answer)
 {
   switch (tok_type) {
     case VARIABLE:
       * answer = find_var (token);
       get_token ();
       return;
     case NUMBER:
       * answer = atof (token);
       get_token ();
       return;
     default:
       serror (0);
   }
 }

 / * Return the token to the input stream.  * /
 void putback (void)
 {
   char * t;

   t = token;
   for (; * t; t ++) prog--;
 }

 / * Displays a syntax error message.  * /
 void serror (int error)
 {
   static char * e [] = {
       "Syntax error",
       "Unbalanced brackets",
       "No Expression",
       "Division by zero"
   };
   printf ("% s \ n", e [error]);
 }

 / * Getting the next lexeme.  * /
 void get_token (void)
 {
   register char * temp;

   tok_type = 0;
   temp = token;
   * temp = '\ 0';

   if (! * prog) return;  / * end of expression * /

   while (isspace (* prog)) ++ prog;  / * skip spaces,
                   tab characters and blank lines * /

   if (strchr ("+ - * /% ^ = ()", * prog)) {
     tok_type = DELIMITER;
     / * go to the next character * /
     * temp ++ = * prog ++;
   }
   else if (isalpha (* prog)) {
     while (! isdelim (* prog)) * temp ++ = * prog ++;
     tok_type = VARIABLE;
   }
   else if (isdigit (* prog)) {
     while (! isdelim (* prog)) * temp ++ = * prog ++;
     tok_type = NUMBER;
   }

   * temp = '\ 0';
 }

 / * Return value is TRUE if c is a delimiter.  * /
 int isdelim (char c)
 {
   if (strchr ("+ - / *% ^ = ()", c) || ​​c == 9 || c == '\ r' || c == 0)
     return 1;
   return 0;
 }

 / * Get the value of a variable.  * /
 double find_var (char * s)
 {
   if (! isalpha (* s)) {
     serror (1);
     return 0.0;
   }
   return vars [toupper (* token) - 'A'];
 }

To demonstrate the operation of this analyzer, you can use the main () function that was used to demonstrate the operation of a simple analyzer. The advanced analyzer allows you to enter expressions like the following:

  A = 10/4
 A - B
 C = A * (F - 21) 

Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Algorithms

Terms: Algorithms