用高階的語言寫 parser 總是大家心中的痛,面對一個簡單的 String 「1 + 2 * 5 - log(4 / tan(3/4))」,若是自己硬幹一個 parser 實在痛苦到不行。原本我已經打定主意來學 lex & yacc,準備自己來實作了,好在終於讓我找到 java 的 library XD。不過話說回來,似乎我自己蒐尋的時候沒下對關鍵字啊...
這套 library 叫 Java Math Expression Parser,簡稱 JEP,官網點此進入。看一下它的 feature,嗯...,該有的都有了。不過卡到一個問題,這個玩意實在有夠貴!試用版只能支援 50 個 expression,似乎夠了?但是只能用在教育方面,而實際要買的話,這玩意要 600 美金。XD 以後我也來考慮寫些 library來賣錢好了...。好在估狗大神萬能,在 3.x 版本之前的 2.4.x 版本是 GPL 授權的,馬上在 sourceforge 找到!這裡可以下載。這下子終於可以安心的用 JEP 來開發程式囉。

話說它的使用方法真是相當簡單,一開始先宣告一個 JEP 物件,然後呼叫此物件的 addStandardFunction() 這個 method,就可以呼叫 parseExpression() 開始 parse,然後再用 getValue() 取得值。簡單來說會長這樣:(記得 import org.nfunk.jep.*)

JEP parser = new JEP();
String expr = "1 + 2 + 3";
parser.addStandardFunction();
parser.parseExpression(expr);
// handling errors while parsing
double value = parser.getValue();
// handling errors during evaluation

值得注意的是,在 JEP 中,預設的 data type 會是 double,若想採用其他的 data type 可以自己呼叫 API 來轉換。而error 的部分可以呼叫 parser.hasError(),若是 true 就表示有 error。

2.4 版本的 JEP 比起商業化後的 3.x 版本就缺了一些功能,比如說我想要以 2 為底的 log,也就是一般常見的 lg,在 JEP 3.x 有支援,2.4 就沒有了,因此我想要自己加一個 function 進去,這要怎麼做呢?首先我們用一個 class,並 extend PostfixMathCommand,接下來就可以開始了。

import org.nfunk.jep.*;
import org.nfunk.jep.function.*;
import java.util.Stack;

public class Lg extends PostfixMathCommand {

public Lg() {
numberOfParameters = 1;
}

public void run(Stack inStack) throws ParseException {
checkStack(inStack);
Object param = inStack.pop();
if (param instanceof Double) {
double r = ((Double)param).doubleValue();
r = Math.log(r) / Math.log(2);
inStack.push(new Double(r));
} else {
throw new ParseException("invalid parameter type");
}
}
}

而在原本的地方要用,就用 addFunction() 這個 method,例如 parser.addFunction("lg", new Lg()),如此就可以搞定了。
創作者介紹

zwai 小窩

zwai 發表在 痞客邦 PIXNET 留言(0) 人氣()