java语法 and or_java – 动态解析逻辑运算 – AND,OR,循环条件

这是完整的解决方案,不包括第三方库,如ANTLR或JavaCC.请注意,虽然它是可扩展的,但其功能仍然有限.如果要创建更复杂的表达式,最好使用语法生成器.

首先,让我们编写一个tokenizer,将输入字符串拆分为标记.这是令牌类型:

private static enum TokenType {

WHITESPACE, AND, OR, EQUALS, LEFT_PAREN, RIGHT_PAREN, IDENTIFIER, LITERAL, EOF

}

令牌类本身:

private static class Token {

final TokenType type;

final int start; // start position in input (for error reporting)

final String data; // payload

public Token(TokenType type, int start, String data) {

this.type = type;

this.start = start;

this.data = data;

}

@Override

public String toString() {

return type + "[" + data + "]";

}

}

为了简化标记化,我们创建一个regexp,它从输入字符串中读取下一个标记:

private static final Pattern TOKENS =

Pattern.compile("(\\s+)|(AND)|(OR)|(=)|(\\()|(\\))|(\\w+)|\'([^\']+)\'");

请注意,它有许多组,每个TokenType有一个组,顺序相同(首先是WHITESPACE,然后是AND,依此类推).最后是tokenizer方法:

private static TokenStream tokenize(String input) throws ParseException {

Matcher matcher = TOKENS.matcher(input);

List tokens = new ArrayList<>();

int offset = 0;

TokenType[] types = TokenType.values();

while (offset != input.length()) {

if (!matcher.find() || matcher.start() != offset) {

throw new ParseException("Unexpected token at " + offset, offset);

}

for (int i = 0; i < types.length; i++) {

if (matcher.group(i + 1) != null) {

if (types[i] != TokenType.WHITESPACE)

tokens.add(new Token(types[i], offset, matcher.group(i + 1)));

break;

}

}

offset = matcher.end();

}

tokens.add(new Token(TokenType.EOF, input.length(), ""));

return new TokenStream(tokens);

}

我正在使用java.text.ParseException.这里我们应用正则表达式匹配器直到输入结束.如果它在当前位置不匹配,我们抛出异常.否则,我们会查找找到的匹配组并从中创建一个令牌,忽略WHITESPACE令牌.最后,我们添加一个EOF标记,表示输入的结束.结果作为特殊的TokenStream对象返回.这是TokenStream类,它将帮助我们进行解析:

private static class TokenStream {

final List tokens;

int offset = 0;

public TokenStream(List tokens) {

this.tokens = tokens;

}

// consume next token of given type (throw exception if type differs)

public Token consume(TokenType type) throws ParseException {

Token token = tokens.get(offset++);

if (token.type != type) {

throw new ParseException("Unexpected token at " + token.start

+ ": " + token + " (was looking for " + type + ")",

token.start);

}

return token;

}

// consume token of given type (return null and don't advance if type differs)

public Token consumeIf(TokenType type) {

Token token = tokens.get(offset);

if (token.type == type) {

offset++;

return token;

}

return null;

}

@Override

public String toString() {

return tokens.toString();

}

}

所以我们有一个标记器,hoorah.您现在可以使用System.out.println进行测试(tokenize(“Acct1 =’Y’AND(Acct2 =’N’或Acct3 =’N’)”));

现在让我们编写解析器,它将创建表达式的树状表示.首先是所有树节点的接口Expr:

public interface Expr {

public boolean evaluate(Map data);

}

它唯一的方法用于评估给定数据集的表达式,如果数据集匹配则返回true.

最基本的表达式是EqualsExpr,它类似于Acct1 =’Y’或’Y’= Acct1:

private static class EqualsExpr implements Expr {

private final String identifier, literal;

public EqualsExpr(TokenStream stream) throws ParseException {

Token token = stream.consumeIf(TokenType.IDENTIFIER);

if(token != null) {

this.identifier = token.data;

stream.consume(TokenType.EQUALS);

this.literal = stream.consume(TokenType.LITERAL).data;

} else {

this.literal = stream.consume(TokenType.LITERAL).data;

stream.consume(TokenType.EQUALS);

this.identifier = stream.consume(TokenType.IDENTIFIER).data;

}

}

@Override

public String toString() {

return identifier+"='"+literal+"'";

}

@Override

public boolean evaluate(Map data) {

return literal.equals(data.get(identifier));

}

}

toString()方法仅用于获取信息,您可以将其删除.

接下来我们将定义SubExpr类,它是EqualsExpr或括号中更复杂的东西(如果我们看到括号):

private static class SubExpr implements Expr {

private final Expr child;

public SubExpr(TokenStream stream) throws ParseException {

if(stream.consumeIf(TokenType.LEFT_PAREN) != null) {

child = new OrExpr(stream);

stream.consume(TokenType.RIGHT_PAREN);

} else {

child = new EqualsExpr(stream);

}

}

@Override

public String toString() {

return "("+child+")";

}

@Override

public boolean evaluate(Map data) {

return child.evaluate(data);

}

}

接下来是AndExpr,它是由AND运算符连接的一组SubExpr表达式:

private static class AndExpr implements Expr {

private final List children = new ArrayList<>();

public AndExpr(TokenStream stream) throws ParseException {

do {

children.add(new SubExpr(stream));

} while(stream.consumeIf(TokenType.AND) != null);

}

@Override

public String toString() {

return children.stream().map(Object::toString).collect(Collectors.joining(" AND "));

}

@Override

public boolean evaluate(Map data) {

for(Expr child : children) {

if(!child.evaluate(data))

return false;

}

return true;

}

}

为简洁起见,我在toString中使用Java-8 Stream API.如果您不能使用Java-8,可以使用for循环重写它或完全删除toString.

最后我们定义OrExpr,它是由OR连接的一组AndExpr(通常OR的优先级低于AND).它与AndExpr非常相似:

private static class OrExpr implements Expr {

private final List children = new ArrayList<>();

public OrExpr(TokenStream stream) throws ParseException {

do {

children.add(new AndExpr(stream));

} while(stream.consumeIf(TokenType.OR) != null);

}

@Override

public String toString() {

return children.stream().map(Object::toString).collect(Collectors.joining(" OR "));

}

@Override

public boolean evaluate(Map data) {

for(Expr child : children) {

if(child.evaluate(data))

return true;

}

return false;

}

}

最后的解析方法:

public static Expr parse(TokenStream stream) throws ParseException {

OrExpr expr = new OrExpr(stream);

stream.consume(TokenType.EOF); // ensure that we parsed the whole input

return expr;

}

因此,您可以解析表达式以获取Expr对象,然后根据CSV文件的行对其进行评估.我假设您能够将CSV行解析为Map< String,String>.这是用法示例:

Map data = new HashMap<>();

data.put("Acct1", "Y");

data.put("Acct2", "N");

data.put("Acct3", "Y");

data.put("Acct4", "N");

Expr expr = parse(tokenize("Acct1 = 'Y' AND (Acct2 = 'Y' OR Acct3 = 'Y')"));

System.out.println(expr.evaluate(data)); // true

expr = parse(tokenize("Acct1 = 'N' OR 'Y' = Acct2 AND Acct3 = 'Y'"));

System.out.println(expr.evaluate(data)); // false

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值