项目研发过程中经常会需要将业务逻辑外置,需要将业务逻辑和代码分离。一般面对这样的需求有以下几种解决办法:
虽然我们可以使用正则表达式来分析字符串,但是如果我们想从一个字符串或者文本文件中一个单词一个单词的分析,词法分析器是最合适的。一个词法分析器需要对应一个词法规则,设计在词法分析器遵循java和sql的词法,两个差别主要在注释方便,构造函数中需要区分。
public Lexer(String sFormula,int langType){
this.languageType = langType;
setFormula(sFormula);
}
词法分析器的核心函数是 public String getAWord( ); 它返回的内容可能是:
一个操作符。
public String getARawWord(){
int sl = formulaSen.length();
while((startPos < sl ) && (formulaSen.charAt(startPos) == ' ' || formulaSen.charAt(startPos) == 9 || formulaSen.charAt(startPos) == 10|| formulaSen.charAt(startPos) == 13)) startPos++;
if(startPos >= sl) return "";
int bp = startPos;
// 数字
if( (formulaSen.charAt(startPos)>='0' && formulaSen.charAt(startPos)<='9') ||
//m_Formula.charAt(m_iStart)== '.' ||
( ! canAcceptOpt && (formulaSen.charAt(startPos)== '-' || formulaSen.charAt(startPos)== '+' ) ) ){
startPos++;
int nPoints = 0;
while ( startPos < sl && (
( formulaSen.charAt(startPos)>='0' && formulaSen.charAt(startPos)<='9') ||
formulaSen.charAt(startPos)=='.' ))
{
if( formulaSen.charAt(startPos)=='.' ){
nPoints ++;
if (nPoints>1)
break;
}
startPos ++;
}
canAcceptOpt = true;
// 标识符
} else if (( formulaSen.charAt(startPos)>='a' && formulaSen.charAt(startPos)<='z') ||
( formulaSen.charAt(startPos)>='A' && formulaSen.charAt(startPos)<='Z') ||
formulaSen.charAt(startPos)=='_' ||
formulaSen.charAt(startPos)=='@' ){
startPos++;
while ( startPos < sl && (
( formulaSen.charAt(startPos)>='0' && formulaSen.charAt(startPos)<='9') ||
( formulaSen.charAt(startPos)>='a' && formulaSen.charAt(startPos)<='z') ||
( formulaSen.charAt(startPos)>='A' && formulaSen.charAt(startPos)<='Z') ||
formulaSen.charAt(startPos)=='_' || formulaSen.charAt(startPos)=='.' ||
formulaSen.charAt(startPos)=='@' ) )
startPos ++;
canAcceptOpt = true;
}else {
canAcceptOpt = false;
switch(formulaSen.charAt(startPos)){
case '+':
++startPos;
if((startPos<sl) && ((formulaSen.charAt(startPos) == '=') ||
(formulaSen.charAt(startPos) == '+') ) ) startPos ++;
break;
case '-':
++startPos;
if((startPos<sl) && ((formulaSen.charAt(startPos) == '=') ||
(formulaSen.charAt(startPos) == '-') ) ) startPos ++;
break;
case '*':
++startPos;
if((startPos<sl) && ((formulaSen.charAt(startPos) == '*') ||
(formulaSen.charAt(startPos) == '=') ||
(formulaSen.charAt(startPos) == '/') ) ) startPos ++;
break;
case '/':
++startPos;
if((startPos<sl) && ((formulaSen.charAt(startPos) == '=') ||
(formulaSen.charAt(startPos) == '/') ||
(formulaSen.charAt(startPos) == '*') ) ) startPos ++;
break;
case '<':
++startPos;
if((startPos<sl) && ((formulaSen.charAt(startPos) == '=') ||
(formulaSen.charAt(startPos) == '>') ||
(formulaSen.charAt(startPos) == '<') ) ) startPos ++;
break;
case '>':
++startPos;
if((startPos<sl) && ((formulaSen.charAt(startPos) == '=') ||
(formulaSen.charAt(startPos) == '>') ) ) startPos ++;
break;
case ':':
++startPos;
if((startPos<sl) && (formulaSen.charAt(startPos) == '=')) startPos ++;
break;
case '=':
case '!':
++startPos;
if((startPos<sl) && (formulaSen.charAt(startPos) == '=')) startPos ++;
break;
case '|':
++startPos;
if((startPos<sl) && (formulaSen.charAt(startPos) == '|')) startPos ++;
break;
case '&':
++startPos;
if((startPos<sl) && (formulaSen.charAt(startPos) == '&')) startPos ++;
break;
case '\"': //字符串
case '\'': //字符串
canAcceptOpt = true;
startPos ++;
break;
case '.':
++startPos;
while ( startPos < sl &&
( formulaSen.charAt(startPos)>='0' && formulaSen.charAt(startPos)<='9') )
{
startPos ++;
}
break;
case ')':
canAcceptOpt = true;
startPos ++;
break;
default: //"+-*/"
startPos ++;
break;
}
}
String str = formulaSen.substring(bp, startPos );
return str;
}
四则运行包括加减乘除、取模和与或非等等;表达式中的标识符对应的变量可以通过map传递给四则运算,也可以通过一个对象的属性传递。四则运算接口如下:
public static Object calculate(String szExpress,Object varMap) {
...................
}
变量对应的类型可以式任意类型,数字、字符串、日期、数组都可以。因为他们不仅可以传递到四则运行中还可以传递到四则运算的内置函数中。
为了对四则运行进行扩展,引入了内置函数来丰富表达式的功能。内置函数分一下几类:
测试代码
public static void testFormula3() {
Map<String,Object> varMap = new HashMap<>();
varMap.put("a", 10);
varMap.put("b", 4);
String formula = "(a*a-b)/b";
Object s = VariableFormula.calculate(formula, varMap);
System.out.println(StringBaseOpt.castObjectToString(s));
System.out.println("Done!");
}
运行结果
24
Done!