要求将前缀运算符(如AND(a,b)
,OR(a,b)
或NOT(a)
)转换为中缀,如下所示:(a && b)
,(a || b)
,(!(a))
我已经编写了下面的代码,但如果表达式不太复杂,它就可以工作。我能够转换:
AND(OR(1<2, OR(3<4, 1<2, FUNC(5<6, 2<3))), 2<3)
来
(((1<2 || ((3<4 || (1<2 || FUNC(5<6, 2<3))))))&& 2<3))
除了那些额外的括号,这个表达式是可以接受但是,当我为表达式运行这个代码有点复杂时,其中有太多函数和括号要么失败要么返回表达式。例如这个expr:
AND(OR((NOT(A != null)), OR(FUNC(3<4, 1==1), 1<2, FUNC(5<6, 2<3))), 2<3)
除了And / Or / Not之外,它应该忽略其他功能。例如,FUNC(5<6, 2<3)
应该输出为FUNC(5<6, 2<3)
,如上例所示。
代码:
public String ConvertToJS(String sExpr, String Operator)
{
//String subExpr[] = sExpr.split(",");
sExpr = sExpr.trim();
String resolved = "";
String resolved2 = "";
if(sExpr.indexOf(",") != -1 || sExpr.indexOf("(") != -1)
{
if((sExpr.indexOf(",") != -1 && sExpr.indexOf("(") != -1 && sExpr.indexOf(",") < sExpr.indexOf("(")) || sExpr.indexOf("(") == -1)
{
if(sExpr.indexOf(",") > 0)
{
if("AND".equalsIgnoreCase(Operator))
return "(" + sExpr.substring(0, sExpr.indexOf(",")) + " && " + ConvertToJS(sExpr.substring(sExpr.indexOf(",")+1, sExpr.length()), Operator) + ")";
else if("OR".equalsIgnoreCase(Operator))
return "(" + sExpr.substring(0, sExpr.indexOf(",")) + " || " + ConvertToJS(sExpr.substring(sExpr.indexOf(",")+1, sExpr.length()), Operator) + ")";
else
return sExpr;
}
else
{
if("AND".equalsIgnoreCase(Operator))
return " && " + ConvertToJS(sExpr.substring(sExpr.indexOf(",")+1, sExpr.length()), Operator) + ")";
else if("OR".equalsIgnoreCase(Operator))
return " || " + ConvertToJS(sExpr.substring(sExpr.indexOf(",")+1, sExpr.length()), Operator) + ")";
else
return sExpr;
}
}
else
{
if(sExpr.indexOf("(") < 2)
{
resolved = sExpr.substring(0, sExpr.indexOf("(")) + "(" + ConvertToJS(sExpr.substring(sExpr.indexOf("(")+1, sExpr.lastIndexOf(")")), "") + ")";
if(sExpr.lastIndexOf(")")< sExpr.length()-1)
resolved += ConvertToJS(sExpr.substring(sExpr.lastIndexOf(")") + 1), Operator);
return resolved;
}
else if(sExpr.indexOf("(") == 2)
{
if(sExpr.substring(0, sExpr.indexOf("(")).equalsIgnoreCase("OR"))
{
resolved = "(" + ConvertToJS(sExpr.substring(sExpr.indexOf("(")+1, sExpr.lastIndexOf(")")), "OR") + ")";
if(sExpr.lastIndexOf(")")< sExpr.length()-1)
resolved += ConvertToJS(sExpr.substring(sExpr.lastIndexOf(")") + 1), Operator);
return resolved;
}
else
{
resolved = sExpr.substring(0, sExpr.indexOf("(")) + "(" + ConvertToJS(sExpr.substring(sExpr.indexOf("(")+1, sExpr.lastIndexOf(")")), "") + ")";
if(sExpr.lastIndexOf(")")< sExpr.length()-1)
resolved += ConvertToJS(sExpr.substring(sExpr.lastIndexOf(")") + 1), Operator);
return resolved;
}
}
else if(sExpr.indexOf("(") == 3)
{
if(sExpr.substring(0, sExpr.indexOf("(")).equalsIgnoreCase("AND"))
{
resolved = "(" + ConvertToJS(sExpr.substring(sExpr.indexOf("(")+1, sExpr.lastIndexOf(")")), "AND") + ")";
if(sExpr.lastIndexOf(")")< sExpr.length()-1)
resolved += ConvertToJS(sExpr.substring(sExpr.lastIndexOf(")") + 1), Operator);
return resolved;
}
else if(sExpr.substring(0, sExpr.indexOf("(")).equalsIgnoreCase("NOT"))
{
resolved = "(" + ConvertToJS(sExpr.substring(sExpr.indexOf("(")+1, sExpr.lastIndexOf(")")), "NOT") + ")";
if(sExpr.lastIndexOf(")")< sExpr.length()-1)
resolved += ConvertToJS(sExpr.substring(sExpr.lastIndexOf(")") + 1), Operator);
return resolved;
}
else
{
resolved = sExpr.substring(0, sExpr.indexOf("(")) + "(" + ConvertToJS(sExpr.substring(sExpr.indexOf("(")+1, sExpr.lastIndexOf(")")), "") + ")";
if(sExpr.lastIndexOf(")")< sExpr.length()-1)
resolved += ConvertToJS(sExpr.substring(sExpr.lastIndexOf(")") + 1), Operator);
return resolved;
}
}
else
{
resolved = sExpr.substring(0, sExpr.indexOf("(")) + "(" + ConvertToJS(sExpr.substring(sExpr.indexOf("(")+1, sExpr.lastIndexOf(")")), "") + ")";
if(sExpr.lastIndexOf(")")< sExpr.length()-1)
resolved += ConvertToJS(sExpr.substring(sExpr.lastIndexOf(")") + 1), Operator);
return resolved;
}
}
}
else
{
if("NOT".equalsIgnoreCase(Operator))
return " !(" + sExpr + ") ";
else
return sExpr;
}
}
答案 0 :(得分:0)
您的实现的问题在于它依赖于在不解析表达式的情况下确定子表达式的结束位置的能力。这不起作用 - 当你调用sExpr.lastIndexOf(")")
时,你会得到整体表达式的结束,而不是你想要递归解析的嵌套子表达式。类似地,当另一个表达式嵌套在您当前正在解析的表达式中时,使用sExpr.indexOf(",")
得到的逗号可能不会破坏正确位置中的表达式。
编写递归下降解析器的技巧(如果你不知道你正在实现的是什么东西被调用)是保持你当前正在读取字符串的位置。这可以通过多种方式完成 - 例如,您可以将String sExpr
字符串传递给Scanner
,而不是传递sExpr
。这样,每个递归调用将完全按照需要读取,并让下一级别继续停止。
像这样配置Scanner
:
Scanner scanner = new Scanner(s);
scanner.useDelimiter("\\s|\\b|(?<=[()])");
从扫描仪获取下一个令牌的方法可能如下所示:
static String next(Scanner scanner) {
String tok;
do {
tok = scanner.next();
} while (scanner.hasNext() && tok.trim().length() == 0);
return tok;
}
现在,您的转化方法可以调用next
传递扫描程序,并在每次需要时获取下一个令牌。当您的递归方法返回时,Scanner
已正确定位,以便先前的调用级别获取下一个标记(demo)。