Adding Variables to the Parser


All programming languages, many calculators, and spreadsheets use variables to store values for later use. Before the parser can be used for such applications, it needs to be expanded to include variables. To accomplish this, you need to add several things to the parser. First, of course, are the variables themselves. As stated earlier, we will use the letters A through Z for variables. The variables are stored in an array inside the Parser class. Each variable uses one array location in a 26-element array of doubles. Therefore, add the following field to the Parser class:

 double[] vars = new double[26];

You will also need to add the following Parser constructor, which initializes the variables:

 public Parser() {   // Initialize the variables to zero.   for(int i=0; i < vars.Length; i++)      vars[i] = 0.0; }

The variables are initialized to 0.0 as a courtesy to the user. However, in specialized applications of the parser, you could initialize these variables to different values.

You will also need a method to look up the value of a given variable. Because the variables are named A through Z, they can easily be used to index the array vars by subtracting the ASCII value for A from the variable name. The method FindVar( ), shown here, accomplishes this:

 // Return the value of a variable. double FindVar(string vname) {   if(!Char.IsLetter(vname[0])){     SyntaxErr(Errors.SYNTAX);     return 0.0;   }   return vars[Char.ToUpper(vname[0])-'A']; }

As this method is written, it will actually accept long variable names, such as A12 or test, but only the first letter is significant. You can change this feature to fit your needs.

You must also modify the Atom( ) method to handle both numbers and variables. The new version is shown here:

 // Get the value of a number or variable. void Atom(out double result) {   switch(tokType) {     case Types.NUMBER:       try {         result = Double.Parse(token);       } catch (FormatException) {         result = 0.0;         SyntaxErr(Errors.SYNTAX);       }       GetToken();       return;     case Types.VARIABLE:       result = FindVar(token);       GetToken();       return;     default:       result = 0.0;       SyntaxErr(Errors.SYNTAX);       break;   } }

Technically, these additions are all that is needed for the parser to use variables correctly; however, there is no way for these variables to be assigned a value. To enable a variable to be given a value, the parser needs to be able to handle the assignment operator, which is =. To implement assignment, we will add another method, EvalExp1( ), to the Parser class. This method will now begin the recursive-descent chain. This means that it, not EvalExp2( ), must be called by Evaluate( ) to begin parsing the expression. EvalExp1( ) is shown here:

 // Process an assignment. void EvalExp1(out double result) {   int varIdx;   Types ttokType;   string temptoken;   if(tokType == Types.VARIABLE) {     // save old token     temptoken = String.Copy(token);     ttokType = tokType;     // Compute the index of the variable.     varIdx = Char.ToUpper(token[0]) - 'A';     GetToken();     if(token != "=") {       PutBack(); // return current token       // restore old token -- not an assignment       token = String.Copy(temptoken);       tokType = ttokType;     }     else {       GetToken(); // get next part of exp       EvalExp2(out result);       vars[varIdx] = result;       return;     }   }   EvalExp2(out result); }

EvalExp1( ) needs to look ahead to determine whether an assignment is actually being made. This is because a variable name always precedes an assignment, but a variable name alone does not guarantee that an assignment expression follows. That is, the parser will accept A = 100 as an assignment, but is also smart enough to know that A/10 is not an assignment. To accomplish this, EvalExp1( ) reads the next token from the input stream. If it is not an equal sign, the token is returned to the input stream for later use by calling PutBack( ), shown here:

 // Return a token to the input stream. void PutBack() {   for(int i=0; i < token.Length; i++) expIdx--; }

After making all the necessary changes, the parser will now look like this:

 /*    This module contains the recursive-descent    parser that recognizes variables. */ using System; // Exception class for parser errors. class ParserException : ApplicationException {   public ParserException(string str) : base(str) { }   public override string ToString() {     return Message;   } } class Parser {   // Enumerate token types.   enum Types { NONE, DELIMITER, VARIABLE, NUMBER };   // Enumerate error types.   enum Errors { SYNTAX, UNBALPARENS, NOEXP, DIVBYZERO };   string exp;    // refers to expression string   int expIdx;    // current index into the expression   string token;  // holds current token   Types tokType; // holds token's type   // Array for variables.   double[] vars = new double[26];   public Parser() {     // Initialize the variables to zero.     for(int i=0; i < vars.Length; i++)        vars[i] = 0.0;   }   // Parser entry point.   public double Evaluate(string expstr)   {     double result;     exp = expstr;     expIdx = 0;     try {       GetToken();       if(token == "") {         SyntaxErr(Errors.NOEXP); // no expression present         return 0.0;       }       EvalExp1(out result); // now, call EvalExp1() to start       if(token != "") // last token must be null         SyntaxErr(Errors.SYNTAX);       return result;     } catch (ParserException exc) {       // Add other error handling here, as desired.       Console.WriteLine(exc);       return 0.0;     }   }   // Process an assignment.   void EvalExp1(out double result)   {     int varIdx;     Types ttokType;     string temptoken;     if(tokType == Types.VARIABLE) {       // save old token       temptoken = String.Copy(token);       ttokType = tokType;       // Compute the index of the variable.       varIdx = Char.ToUpper(token[0]) - 'A';       GetToken();       if(token != "=") {         PutBack(); // return current token         // restore old token -- not an assignment         token = String.Copy(temptoken);         tokType = ttokType;       }       else {         GetToken(); // get next part of exp         EvalExp2(out result);         vars[varIdx] = result;         return;       }     }     EvalExp2(out result);   }   // Add or subtract two terms.   void EvalExp2(out double result)   {     string op;     double partialResult;     EvalExp3(out result);     while((op = token) == "+" || op == "-") {       GetToken();       EvalExp3(out partialResult);       switch(op) {         case "-":           result = result - partialResult;           break;         case "+":           result = result + partialResult;           break;       }     }   }   // Multiply or divide two factors.   void EvalExp3(out double result)   {     string op;     double partialResult = 0.0;     EvalExp4(out result);     while((op = token) == "*" ||            op == "/" || op == "%") {       GetToken();       EvalExp4(out partialResult);       switch(op) {         case "*":           result = result * partialResult;           break;         case "/":           if(partialResult == 0.0)             SyntaxErr(Errors.DIVBYZERO);           result = result / partialResult;           break;         case "%":           if(partialResult == 0.0)             SyntaxErr(Errors.DIVBYZERO);           result = (int) result % (int) partialResult;           break;       }     }   }   // Process an exponent.   void EvalExp4(out double result)   {     double partialResult, ex;     int t;          EvalExp5(out result);     if(token == "^") {       GetToken();       EvalExp4(out partialResult);       ex = result;       if(partialResult == 0.0) {         result = 1.0;         return;       }       for(t=(int)partialResult-1; t > 0; t--)         result = result * (double)ex;     }   }   // Evaluate a unary + or -.   void EvalExp5(out double result)   {     string op;     op = "";     if((tokType == Types.DELIMITER) &&         token == "+" || token == "-") {       op = token;       GetToken();     }     EvalExp6(out result);     if(op == "-") result = -result;   }   // Process a parenthesized expression.   void EvalExp6(out double result)   {     if((token == "(")) {       GetToken();       EvalExp2(out result);       if(token != ")")         SyntaxErr(Errors.UNBALPARENS);       GetToken();     }     else Atom(out result);   }   // Get the value of a number or variable.   void Atom(out double result)   {     switch(tokType) {       case Types.NUMBER:         try {           result = Double.Parse(token);         } catch (FormatException) {           result = 0.0;           SyntaxErr(Errors.SYNTAX);         }         GetToken();         return;       case Types.VARIABLE:         result = FindVar(token);         GetToken();         return;       default:         result = 0.0;         SyntaxErr(Errors.SYNTAX);         break;     }   }    // Return the value of a variable.   double FindVar(string vname)   {     if(!Char.IsLetter(vname[0])){       SyntaxErr(Errors.SYNTAX);       return 0.0;     }     return vars[Char.ToUpper(vname[0])-'A'];   }   // Return a token to the input stream.   void PutBack()   {     for(int i=0; i < token.Length; i++) expIdx--;   }   // Handle a syntax error.   void SyntaxErr(Errors error)   {     string[] err = {       "Syntax Error",       "Unbalanced Parentheses",       "No Expression Present",       "Division by Zero"     };     throw new ParserException(err[(int)error]);   }   // Obtain the next token.   void GetToken()   {     tokType = Types.NONE;     token = "";     if(expIdx == exp.Length) return; // at end of expression     // skip over whitespace     while(expIdx < exp.Length &&           Char.IsWhiteSpace(exp[expIdx])) ++expIdx;     // trailing whitespace ends expression     if(expIdx == exp.Length) return;     if(IsDelim(exp[expIdx])) { // is operator       token += exp[expIdx];       expIdx++;       tokType = Types.DELIMITER;     }     else if(Char.IsLetter(exp[expIdx])) { // is variable       while(!IsDelim(exp[expIdx])) {         token += exp[expIdx];         expIdx++;         if(expIdx >= exp.Length) break;       }       tokType = Types.VARIABLE;     }     else if(Char.IsDigit(exp[expIdx])) { // is number       while(!IsDelim(exp[expIdx])) {         token += exp[expIdx];         expIdx++;         if(expIdx >= exp.Length) break;       }       tokType = Types.NUMBER;     }   }   // Return true if c is a delimiter.   bool IsDelim(char c)   {     if((" +-/*%^=()".IndexOf(c) != -1))       return true;     return false;   } }

To try the enhanced parser, you can use the same program that you used for the simple parser. Using the enhanced parser, you can now enter expressions like these:

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




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net