java實現遞歸下降的表達式解析器
本算法用java代碼實現數值表達式的解析。
1、數值表達式的組成:
- 數字
- 運算符+、-、*、/、^、%、=
- 圓括號(、)
- 變量
其中^運算符表示求冪運算符(如10^2=100),=是賦值運算符。
2、本解析器必須滿足的約束條件:
1)、所有變量都是單個字母(從A到Z的26個字母),字母不區分大小;
2)、假定所有的數字都是double類型,可以方便地修改解析器從而處理其他類型的值。
3、該算法將表達式被視為遞歸的數據結構,表達式定義的規則:
表達式-->項 [+項] [-項]
項-->因數 [*因數] [/因數]
因數-->變量、數字或者表達式
4、表達式的分解
例如表達式:A * B - (C + 10)
包括如下這些獨立的元素:A、*、B、-、(、C、+、10、),這些元素叫做標識符(token),表示表達式中一個不可再分的獨立單元。
5、代碼
[java] view plaincopy
- <span style="font-size:16px;"><span style="font-family:'Microsoft YaHei';">package com.mmq.expression;
- /**
- * @use 表達式解析器,支持變量
- * @ProjectName stuff
- * @Author <a href="mailto:mhmyqn@126.com">mumaoqiang</a></br>
- * @Date 2011-11-22 下午12:03:57 </br>
- * @FullName com.mmq.expression.Parser.java </br>
- * @JDK 1.6.0 </br>
- * @Version 1.0 </br>
- */
- public class Parser {
- //These are the token types 標識符類型常量
- /**未定義的標識符*/
- final int NONE = 0;
- /**分隔符(包括運算符和括號)*/
- final int DELIMITER = 1;
- /**變量*/
- final int VARIABLE = 2;
- /**數值*/
- final int NUMBER = 3;
- //These are the types of the syntax errors 語法錯誤類型常量
- /**所有導致非正則表達式的錯誤*/
- final int SYNTAX = 0;
- /**括號不對稱錯誤*/
- final int UNBALPARENS = 1;
- /**沒有表達式*/
- final int NOEXP = 2;
- /**除數為0*/
- final int DIVBYZERO = 3;
- /**
- * This token indicates end-of-expression 表達式結束標識符
- * 標志解析器已達到表達式結尾
- */
- final String EOE = "\0";
- /**refers to expression string 表達式字符串*/
- private String exp;
- /**current index into the expression 表達式中當前標識符的索引*/
- private int expIdx;
- /**holds current token 當前標識符*/
- private String token;
- /**holds token's type 標識符類型*/
- private int tokType;
- /**Array for variables 變量數組*/
- private double vars[] = new double[26];
- /**
- * Parser entry point 解析器入口
- * @param expstr 表達式字符串
- * @return 表達式的值
- * @throws ParserException
- */
- public double evaluate(String expstr) throws ParserException {
- double result;
- exp = expstr;
- expIdx = 0;
- getToken();
- if(token.equals(EOE)){
- handleErr(NOEXP);//no expression present
- }
- //Parse and evaluate the expression
- result = assignment();
- if(!token.equals(EOE)){//last token must be EOE
- handleErr(SYNTAX);
- }
- return result;
- }
- /**
- * Process an assignment 處理賦值語句
- * @return
- * @throws ParserException
- */
- private double assignment() throws ParserException {
- double result;
- int varIdx;
- int ttokType;
- String temptoken;
- if(tokType == VARIABLE){
- //save old token
- temptoken = new String(token);
- ttokType = tokType;
- //Compute the index of a variable.
- varIdx = Character.toUpperCase(token.charAt(0)) - 'A';
- getToken();
- if(!token.equals("=")){
- putBack();//return current token
- //restore old token -- not an assignment
- token = new String(temptoken);
- tokType = ttokType;
- } else {
- getToken();//
- result = addOrSubtract();
- vars[varIdx] = result;
- return result;
- }
- }
- return addOrSubtract();
- }
- /**
- * Add or subtract two terms. 兩個數值加或減
- * @return
- * @throws ParserException
- */
- private double addOrSubtract() throws ParserException {
- char op;
- double result;
- double partialResult;
- result = multiplyOrDivide();
- while((op = token.charAt(0)) == '+' || op == '-'){
- getToken();
- partialResult = multiplyOrDivide();
- switch(op){
- case '-' :
- result = result - partialResult;
- break;
- case '+' :
- result = result + partialResult;
- break;
- }
- }
- return result;
- }
- /**
- * Multiply or divide two factors. 兩個數值乘、除或取模
- * @return
- * @throws ParserException
- */
- private double multiplyOrDivide() throws ParserException {
- char op;
- double result;
- double partialResult;
- result = exponent();
- while((op = token.charAt(0)) == '*' || op == '/' || op == '%'){
- getToken();
- partialResult = exponent();
- switch(op){
- case '*' :
- result = result * partialResult;
- break;
- case '/' :
- if(partialResult == 0.0){
- handleErr(DIVBYZERO);
- }
- result = result / partialResult;
- break;
- case '%' :
- if(partialResult == 0.0){
- handleErr(DIVBYZERO);
- }
- result = result % partialResult;
- break;
- }
- }
- return result;
- }
- /**
- * Process an exponent. 指數運算
- * @return
- * @throws ParserException
- */
- private double exponent() throws ParserException {
- double result;
- double partialResult;
- double ex;
- int t;
- result = unary();
- if(token.equals("^")){
- getToken();
- partialResult = exponent();
- ex = result;
- if(partialResult == 0.0){
- result = 1.0;
- } else {
- for(t = (int)partialResult - 1; t > 0; t-- ){
- result = result * ex;
- }
- }
- }
- return result;
- }
- /**
- * Evaluate a unary + or -. 一元取正或一元取負運算
- * @return
- * @throws ParserException
- */
- private double unary() throws ParserException {
- double result;
- String op = "";
- //檢查標識符是否為一元取正或一元取負運算符
- if(tokType == DELIMITER && token.equals("+") || token.equals("-")){
- op = token;
- getToken();
- }
- result = parenthesize();
- if(op.equals("-")){
- result = -result;
- }
- return result;
- }
- /**
- * Process a parenthesized expression. 處理括號表達式
- * @return
- * @throws ParserException
- */
- private double parenthesize() throws ParserException {
- double result;
- //如果讀取的是括號時遞歸調用addOrSubtract()
- if(token.equals("(")){
- getToken();
- result = addOrSubtract();
- if(!token.equals(")")){
- handleErr(UNBALPARENS);
- }
- getToken();
- } else {
- result = atom();
- }
- return result;
- }
- /**
- * Get the value of a number or variable. 獲取一個數值字符串或變量的值
- * @return
- * @throws ParserException
- */
- private double atom() throws ParserException {
- double result = 0.0;
- switch(tokType){
- case NUMBER :
- try {
- result = Double.parseDouble(token);
- } catch (NumberFormatException e) {
- handleErr(SYNTAX);
- e.printStackTrace();
- }
- getToken();
- break;
- case VARIABLE :
- result = findVar(token);
- getToken();
- break;
- default :
- handleErr(SYNTAX);
- break;
- }
- return result;
- }
- /**
- * Return the value of a variable. 獲取變量的值
- * @param vname
- * @return
- * @throws ParserException
- */
- private double findVar(String vname) throws ParserException {
- if(!Character.isLetter(vname.charAt(0))){
- handleErr(SYNTAX);
- return 0.0;
- }
- return vars[Character.toUpperCase(vname.charAt(0)) - 'A'];
- }
- /**
- * Return a token to the input stream.
- * 當標識為'='時,返回到標識符之前的索引位置
- */
- private void putBack(){
- if(token == EOE){
- return;
- }
- for(int i = 0; i < token.length(); i++){
- expIdx--;
- }
- }
- /**
- * Handle an error. 錯誤處理
- * @param error
- * @throws ParserException
- */
- private void handleErr(int error) throws ParserException {
- String[] err = {
- "Unbalanced Parentheses",
- "No Expression Parent",
- "Division by zero"
- };
- throw new ParserException(err[error]);
- }
- /**
- * 獲得表達式中的下一個標識符
- */
- private void getToken(){
- tokType = NONE;
- token = "";
- //Check for end of expression
- if(expIdx == exp.length()){
- token = EOE;
- return;
- }
- //Skip over white space
- while(expIdx < exp.length() && Character.isWhitespace(exp.charAt(expIdx))){
- ++expIdx;
- }
- //Trailing whitespace ends expression
- if(expIdx == exp.length()){
- token = EOE;
- return;
- }
- if(isDelim(exp.charAt(expIdx))){//is operator
- token += exp.charAt(expIdx);
- expIdx++;
- tokType = DELIMITER;
- } else if(Character.isLetter(exp.charAt(expIdx))){//is variable
- while(!isDelim(exp.charAt(expIdx))){
- token += exp.charAt(expIdx);
- expIdx++;
- if(expIdx >= exp.length()){
- break;
- }
- }
- tokType = VARIABLE;
- } else if(Character.isDigit(exp.charAt(expIdx))){//is number
- while(!isDelim(exp.charAt(expIdx))){
- token += exp.charAt(expIdx);
- expIdx++;
- if(expIdx >= exp.length()){
- break;
- }
- }
- tokType = NUMBER;
- } else {
- token = EOE;
- return;
- }
- }
- /**
- * Return true if c is a delimiter. 判斷是不是一個分隔符
- */
- private boolean isDelim(char c){
- if(" +-*/%^=()".indexOf(c) != -1){
- return true;
- }
- return false;
- }
- }</span></span>
異常處理類:
[java] view plaincopy
- <span style="font-size:16px;"><span style="font-family:'Microsoft YaHei';">package com.mmq.expression;
- public class ParserException extends Exception {
- private static final long serialVersionUID = 8930730209321088339L;
- String errStr;
- public ParserException(String str) {
- this.errStr = str;
- }
- public String toString() {
- return errStr;
- }
- }
- </span></span>
測試代碼:
[java] view plaincopy
- <span style="font-size:16px;"><span style="font-family:'Microsoft YaHei';">package com.mmq.expression;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- public class ParserDemo {
- public static void main(String[] args) throws IOException {
- String expr;
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- Parser p = new Parser();
- System.out.println("Enter an empty expression to stop.");
- for(;;){
- System.out.print("Enter expression: ");
- expr = br.readLine();
- if("".equals(expr)){
- break;
- }
- try {
- System.out.println("Result: " + p.evaluate(expr));
- System.out.println();
- } catch (ParserException e) {
- System.out.println(e);
- }
- }
- }
- }
- </span></span>
測試輸出如下:
Enter an empty expression to stop.
Enter expression: a=10
Result: 10.0
Enter expression: b=5
Result: 5.0
Enter expression: a+(b-2)/3-5
Result: 6.0
Enter expression:
Enter expression: a=10
Result: 10.0
Enter expression: b=5
Result: 5.0
Enter expression: a+(b-2)/3-5
Result: 6.0
Enter expression: