递归下降的表达式解析器(C#)
使用方法:10-2*3 得到结果 6。
ExpressionParser.cs
using System;
namespace IBMS.Text.Script
{
Field#region Field
/**//// <summary>
/// Parser of syntax
/// </summary>
public class ExpressionParser
{
/**//// <summary>
/// Threse are the token types
/// </summary>
public enum TokenTypes
{
/**//// <summary>
/// Not defined type of token
/// </summary>
None,
/**//// <summary>
/// Delimiter token type
/// </summary>
Delimiter,
/**//// <summary>
/// Variable token type
/// </summary>
Variable,
/**//// <summary>
/// Number token type
/// </summary>
Number,
}
/**//// <summary>
/// These are the types of syntax errors
/// </summary>
public enum SyntaxErrors
{
/**//// <summary>
/// All syntax error
/// </summary>
Synatx,
/**//// <summary>
/// Bracket error
/// </summary>
UnBalparens,
/**//// <summary>
/// No expression error
/// </summary>
NoExpression,
/**//// <summary>
/// Divide by zero error
/// </summary>
DivByZero,
}
/**//// <summary>
/// This token indicates end-of-expression
/// </summary>
readonly private string EOE;
/**//// <summary>
/// refers to expression string
/// </summary>
private string exp;
/**//// <summary>
/// Current index into the expression
/// </summary>
private int expIdx;
/**//// <summary>
/// Holds current token
/// </summary>
private string token;
/**//// <summary>
/// Holds current token's type
/// </summary>
private TokenTypes tokType;
#endregion
Public Mothord#region Public Mothord
/**//// <summary>
/// Constructor
/// </summary>
public ExpressionParser()
{
EOE= "\0";
}
/**//// <summary>
/// Parsor entry point
/// </summary>
/// <param name="expstr">expression string</param>
/// <returns>result of the expression</returns>
public double Evaluate(string expstr)
{
double result = 0.0;
exp =expstr;
expIdx = 0;
GetToken();
if( token == EOE)
{
HandleError(SyntaxErrors.NoExpression); //No expression present
}
//Parse and evaluate the expression
result = EvalExp2();
if( token != EOE)
{
HandleError(SyntaxErrors.Synatx);
}
return result;
}
#endregion
Private Methord#region Private Methord
/**//// <summary>
/// Add or ssubtract two terms;
/// </summary>
/// <returns>Evaluated result</returns>
private double EvalExp2()
{
char op;
double result;
double partialResult;
result = EvalExp3();
while( ((op=token[0]) ) == '+' || op == '-')
{
GetToken();
partialResult = EvalExp3();
switch(op)
{
case '+':
result += partialResult;
break;
case '-':
result -= partialResult;
break;
}
}
return result;
}
/**//// <summary>
/// Multiply or divide two factors.
/// </summary>
/// <returns>Evaluated result</returns>
public double EvalExp3()
{
char op;
double result;
double partialResult;
result = EvalExp4();
while( (op= token[0]) == '*'|| op == '/' || op =='%')
{
GetToken();
partialResult = EvalExp4();
switch(op)
{
case '*':
result *=partialResult;
break;
case '/':
if(partialResult == 0.0)
{
HandleError(SyntaxErrors.DivByZero);
}
result /= partialResult;
break;
case '%':
if(partialResult == 0.0)
{
HandleError(SyntaxErrors.DivByZero);
}
result %= partialResult;
break;
}
}
return result;
}
/**//// <summary>
/// Process an exponent
/// </summary>
/// <returns></returns>
private double EvalExp4()
{
double result;
double partialResult;
double ex;
int t;
result = EvalExp5();
if(token =="^" )
{
GetToken();
partialResult = EvalExp4();
ex = result;
if(partialResult == 0.0 )
{
result = 1.0;
}
else
{
for(t=(int)partialResult-1; t>0; t--)
{
result *= ex;
}
}
}
return result;
}
//Evaluate a unary + or -
private double EvalExp5()
{
double result;
string op;
op="";
if( (tokType == TokenTypes.Delimiter) && ( token =="+" || token =="-") )
{
op = token;
GetToken();
}
result = EvalExp6();
if(op == "-") result = - result;
return result;
}
private double EvalExp6()
{
double result;
if(token=="(")
{
GetToken();
result = EvalExp2();
if(token!=")")
{
HandleError(SyntaxErrors.UnBalparens);
}
GetToken();
}
else
{
result = Atom();
}
return result;
}
/**//// <summary>
/// Get the value of a number
/// </summary>
/// <returns></returns>
private double Atom()
{
double result = 0.0;
switch(tokType)
{
case TokenTypes.Number:
try
{
result = Double.Parse(token);
}
catch(InvalidCastException)
{
HandleError( SyntaxErrors.Synatx );
}
GetToken();
break;
default:
HandleError( SyntaxErrors.Synatx );
break;
}
return result;
}
/**//// <summary>
/// Handle an error
/// </summary>
/// <param name="error">index of error</param>
void HandleError(SyntaxErrors error)
{
String [] err = new string[]
{
"Syntax Error",
"Unabalanced Parentheses",
"No Expression Present",
"Division By Zero"
};
throw new ExpressionParserException(err[(int)error]);
}
/**//// <summary>
/// Obtain the next token
/// </summary>
private void GetToken()
{
tokType = TokenTypes.None;
token = "";
//Check for end of expression.
if( expIdx == exp.Length)
{
token = EOE;
return;
}
//Skip over white space
while( expIdx <exp.Length && char.IsWhiteSpace( exp[expIdx] ) ) ++expIdx;
//Trailing whitespace ends expression.
if( expIdx == exp.Length ) {
token = EOE;
return;
}
if( IsDelimiter(exp[expIdx] ) ) //Is operator
{
token+=exp[expIdx];
expIdx++;
tokType = TokenTypes.Delimiter;
}
else if (Char.IsLetter(exp[expIdx])) //Is variable
{
while(!IsDelimiter( exp[expIdx]) )
{
token += exp[expIdx];
expIdx ++;
if(expIdx >= exp.Length ) break;
}
tokType = TokenTypes.Variable;
}
else if( char.IsDigit(exp[expIdx])) //Is a number
{
while( !IsDelimiter(exp[expIdx]))
{
token += exp[expIdx];
expIdx ++;
if( expIdx >= exp.Length ) break;
}
tokType = TokenTypes.Number;
}
else
{
token = EOE;
return;
}
}
/**//// <summary>
/// Check whether c is a delimiter
/// </summary>
/// <param name="c">the checked char</param>
/// <returns>Return true if c is a delimiter, else return false</returns>
private static bool IsDelimiter(char c)
{
if( (" +-/*%^=()".IndexOf(c) !=-1 ))
{
return true;
}
return false;
}
#endregion
}
}
namespace IBMS.Text.Script
{
Field#region Field
/**//// <summary>
/// Parser of syntax
/// </summary>
public class ExpressionParser
{
/**//// <summary>
/// Threse are the token types
/// </summary>
public enum TokenTypes
{
/**//// <summary>
/// Not defined type of token
/// </summary>
None,
/**//// <summary>
/// Delimiter token type
/// </summary>
Delimiter,
/**//// <summary>
/// Variable token type
/// </summary>
Variable,
/**//// <summary>
/// Number token type
/// </summary>
Number,
}
/**//// <summary>
/// These are the types of syntax errors
/// </summary>
public enum SyntaxErrors
{
/**//// <summary>
/// All syntax error
/// </summary>
Synatx,
/**//// <summary>
/// Bracket error
/// </summary>
UnBalparens,
/**//// <summary>
/// No expression error
/// </summary>
NoExpression,
/**//// <summary>
/// Divide by zero error
/// </summary>
DivByZero,
}
/**//// <summary>
/// This token indicates end-of-expression
/// </summary>
readonly private string EOE;
/**//// <summary>
/// refers to expression string
/// </summary>
private string exp;
/**//// <summary>
/// Current index into the expression
/// </summary>
private int expIdx;
/**//// <summary>
/// Holds current token
/// </summary>
private string token;
/**//// <summary>
/// Holds current token's type
/// </summary>
private TokenTypes tokType;
#endregion
Public Mothord#region Public Mothord
/**//// <summary>
/// Constructor
/// </summary>
public ExpressionParser()
{
EOE= "\0";
}
/**//// <summary>
/// Parsor entry point
/// </summary>
/// <param name="expstr">expression string</param>
/// <returns>result of the expression</returns>
public double Evaluate(string expstr)
{
double result = 0.0;
exp =expstr;
expIdx = 0;
GetToken();
if( token == EOE)
{
HandleError(SyntaxErrors.NoExpression); //No expression present
}
//Parse and evaluate the expression
result = EvalExp2();
if( token != EOE)
{
HandleError(SyntaxErrors.Synatx);
}
return result;
}
#endregion
Private Methord#region Private Methord
/**//// <summary>
/// Add or ssubtract two terms;
/// </summary>
/// <returns>Evaluated result</returns>
private double EvalExp2()
{
char op;
double result;
double partialResult;
result = EvalExp3();
while( ((op=token[0]) ) == '+' || op == '-')
{
GetToken();
partialResult = EvalExp3();
switch(op)
{
case '+':
result += partialResult;
break;
case '-':
result -= partialResult;
break;
}
}
return result;
}
/**//// <summary>
/// Multiply or divide two factors.
/// </summary>
/// <returns>Evaluated result</returns>
public double EvalExp3()
{
char op;
double result;
double partialResult;
result = EvalExp4();
while( (op= token[0]) == '*'|| op == '/' || op =='%')
{
GetToken();
partialResult = EvalExp4();
switch(op)
{
case '*':
result *=partialResult;
break;
case '/':
if(partialResult == 0.0)
{
HandleError(SyntaxErrors.DivByZero);
}
result /= partialResult;
break;
case '%':
if(partialResult == 0.0)
{
HandleError(SyntaxErrors.DivByZero);
}
result %= partialResult;
break;
}
}
return result;
}
/**//// <summary>
/// Process an exponent
/// </summary>
/// <returns></returns>
private double EvalExp4()
{
double result;
double partialResult;
double ex;
int t;
result = EvalExp5();
if(token =="^" )
{
GetToken();
partialResult = EvalExp4();
ex = result;
if(partialResult == 0.0 )
{
result = 1.0;
}
else
{
for(t=(int)partialResult-1; t>0; t--)
{
result *= ex;
}
}
}
return result;
}
//Evaluate a unary + or -
private double EvalExp5()
{
double result;
string op;
op="";
if( (tokType == TokenTypes.Delimiter) && ( token =="+" || token =="-") )
{
op = token;
GetToken();
}
result = EvalExp6();
if(op == "-") result = - result;
return result;
}
private double EvalExp6()
{
double result;
if(token=="(")
{
GetToken();
result = EvalExp2();
if(token!=")")
{
HandleError(SyntaxErrors.UnBalparens);
}
GetToken();
}
else
{
result = Atom();
}
return result;
}
/**//// <summary>
/// Get the value of a number
/// </summary>
/// <returns></returns>
private double Atom()
{
double result = 0.0;
switch(tokType)
{
case TokenTypes.Number:
try
{
result = Double.Parse(token);
}
catch(InvalidCastException)
{
HandleError( SyntaxErrors.Synatx );
}
GetToken();
break;
default:
HandleError( SyntaxErrors.Synatx );
break;
}
return result;
}
/**//// <summary>
/// Handle an error
/// </summary>
/// <param name="error">index of error</param>
void HandleError(SyntaxErrors error)
{
String [] err = new string[]
{
"Syntax Error",
"Unabalanced Parentheses",
"No Expression Present",
"Division By Zero"
};
throw new ExpressionParserException(err[(int)error]);
}
/**//// <summary>
/// Obtain the next token
/// </summary>
private void GetToken()
{
tokType = TokenTypes.None;
token = "";
//Check for end of expression.
if( expIdx == exp.Length)
{
token = EOE;
return;
}
//Skip over white space
while( expIdx <exp.Length && char.IsWhiteSpace( exp[expIdx] ) ) ++expIdx;
//Trailing whitespace ends expression.
if( expIdx == exp.Length ) {
token = EOE;
return;
}
if( IsDelimiter(exp[expIdx] ) ) //Is operator
{
token+=exp[expIdx];
expIdx++;
tokType = TokenTypes.Delimiter;
}
else if (Char.IsLetter(exp[expIdx])) //Is variable
{
while(!IsDelimiter( exp[expIdx]) )
{
token += exp[expIdx];
expIdx ++;
if(expIdx >= exp.Length ) break;
}
tokType = TokenTypes.Variable;
}
else if( char.IsDigit(exp[expIdx])) //Is a number
{
while( !IsDelimiter(exp[expIdx]))
{
token += exp[expIdx];
expIdx ++;
if( expIdx >= exp.Length ) break;
}
tokType = TokenTypes.Number;
}
else
{
token = EOE;
return;
}
}
/**//// <summary>
/// Check whether c is a delimiter
/// </summary>
/// <param name="c">the checked char</param>
/// <returns>Return true if c is a delimiter, else return false</returns>
private static bool IsDelimiter(char c)
{
if( (" +-/*%^=()".IndexOf(c) !=-1 ))
{
return true;
}
return false;
}
#endregion
}
}
ExpressionParserException.cs
using System;
namespace IBMS.Text.Script
{
/**//// <summary>
/// Exception class for parsor errors
/// </summary>
public class ExpressionParserException:Exception
{
/**//// <summary>
/// Constructor
/// </summary>
/// <param name="str">error message</param>
public ExpressionParserException(string str)
:base(str)
{
}
}
}
namespace IBMS.Text.Script
{
/**//// <summary>
/// Exception class for parsor errors
/// </summary>
public class ExpressionParserException:Exception
{
/**//// <summary>
/// Constructor
/// </summary>
/// <param name="str">error message</param>
public ExpressionParserException(string str)
:base(str)
{
}
}
}