Java 字符串表达式解析技巧?
在今年的假期里,我感到很无聊,于是随机决定为Java编写一个简单的列表理解/过滤库(我知道有一些很棒的,我只是想自己编写它) 对于此列表:Java 字符串表达式解析技巧?,java,parsing,lambda,list-comprehension,Java,Parsing,Lambda,List Comprehension,在今年的假期里,我感到很无聊,于是随机决定为Java编写一个简单的列表理解/过滤库(我知道有一些很棒的,我只是想自己编写它) 对于此列表: LinkedList<Person> list = new LinkedList<Person>(); list.add(new Person("Jack", 20)); list.add(new Person("Liz", 58)); list.add(new
LinkedList<Person> list = new LinkedList<Person>();
list.add(new Person("Jack", 20));
list.add(new Person("Liz", 58));
list.add(new Person("Bob", 33));
基本上,你需要的是一个表达式。这是编译器理论中的一个重要主题,所以任何关于编译器的书都会涉及这个主题。在正式的语法术语中,它看起来像这样:
condition : orAtom ('||' orAtom)+ ;
orAtom : atom ('&&' atom)+ ;
atom : '(' condition ')'
| expression ;
expression : value OPER value ;
value : VARIABLE | LITERAL '
VARIABLE : (LETTER | '_') (LETTER | DIGIT | '_')* ;
LITERAL : NUMBER
| STRING ;
NUMBER : '-'? DIGIT+ ('.' DIGIT+)? ;
STRING : '"' . CHAR* . '"' '
CHAR : ('\\' | '\"' | .) + ;
LETTER : 'a'..'z' | 'A'..'Z' ;
DIGIT : '0'..'9' ;
OPER : '>' | '>=' | '<' | '<=' | '=' | '!=' ;
因为这是在代码中说明表达式树,应该更容易实现。要添加到cletus answer中,首先需要定义语法 下面的表达式grammer在大多数情况下都非常有效,不幸的是,正常的递归下降不允许您在每次生产中首先定义递归部分。这将导致递归调用生产方法,直到出现堆栈溢出 orexpr ::= orexpr '|' andexpr | andexpr andexpr ::= andexpr '&' comparison | comparison comparison ::= addexpr compareOp addexpr | addexpr addexpr ::= addexpr '+' mulexpr | addexpr '-' mulexpr | mulexpr mulexpr ::= mulexpr '*' value | mulexpr '/' value | mulexpr '%' value | value value ::= integer | float | variable | quotation | '(' orexpr ')' orexpr::=orexpr'|'andexpr |andexpr andexpr::=andexpr'&'比较 |比较 比较::=加法器比较加法器 |加法器 addexpr::=addexpr'+'mulexpr |addexpr'-'mulexpr |mulexpr mulexpr::=mulexpr'*'值 |mulexpr“/”值 |mulexpr“%”值 |价值观 值::=整数 |浮动 |变数 |报价单 |“('orexpr')” 正常递归下降需要您定义mulexpr,例如: mulexpr ::= value '*' mulexpr | value '/' mulexpr | value '%' mulexpr mulexpr::=值'*'mulexpr |值“/”mulexpr |值“%”mulexpr 但是这个语法的问题是表达式树的构建方式将使您的操作顺序完全相反 折衷:在上面写的原始语法上使用反向递归下降法。从右向左解析表达式。从右到左建造你的树。这将维持行动的秩序 在递归下降中,通常为每个产品编写一个解析方法。parseOr()方法可能如下所示: private MyExpression parseOr(MyScanner scanner) { MyExpression expression = null; MyExpression rightExpr = parseAnd(scanner); Token token = scanner.getCurrentToken(); if (token.hasValue("|") { expression = new MyExpression(); expression.setOperator(OR); Token nextToken = scanner.getNextToken(); // remember, this is scanning in reverse MyExpression leftExpression = parseOr(scanner); expression.setLeft(leftExpression); expression.setRight(rightExpression); } else { expression = rightExpression; } return expression; } 专用MyExpression解析器(MyScanner扫描仪){ MyExpression表达式=null; MyExpression rightExpr=parseAnd(扫描仪); 令牌=scanner.getCurrentToken(); if(token.hasValue(“|”){ expression=newmyexpression(); 表达式.setOperator(OR); 令牌nextToken=scanner.getNextToken();//记住,这是反向扫描 MyExpression leftExpression=parser(扫描器); setLeft(leftExpression); 表达式.setRight(rightExpression); } 否则{ 表达式=右表达式; } 返回表达式; }
谢谢大家给我的提示。我觉得大部分都超出了我的需要,所以我最终注册了它,以便将内容放入可管理的组中,我可以轻松地用20-30行代码解析 我的字符串LambdaExpression接口几乎和fluent接口一样工作,只是一两个小错误
我可能会继续开发它,只是为了好玩,但它显然效率太低,无法真正使用,因为它大约90%是基于反射的。
“x=>x.Age>=21&x.Age我不打算编写一个提供程序将我的实用程序链接到关系数据库,尽管这可能很有趣。你到底想让我详细说明什么?@Chii:我想,"x=>x.Age>=21&x.Age我想这是我要研究的领域。多谢各位,至少我现在有了一个起点。我不是说我当前的实现已经完成,但它可以处理我迄今为止所能处理的任何事情。不过,它需要在效率方面做一些工作。我真的认为减少冗长的唯一方法是o我很满意的一点是通过字符串实现(它将被解析为与我现在使用的完全相同的底层表达式结构)。
List results = from(source)
.where(var("x").greaterThan(25), var("x").lessThan(50))
.select("field1", "field2");
orexpr ::= orexpr '|' andexpr
| andexpr
andexpr ::= andexpr '&' comparison
| comparison
comparison ::= addexpr compareOp addexpr
| addexpr
addexpr ::= addexpr '+' mulexpr
| addexpr '-' mulexpr
| mulexpr
mulexpr ::= mulexpr '*' value
| mulexpr '/' value
| mulexpr '%' value
| value
value ::= integer
| float
| variable
| quotation
| '(' orexpr ')'
mulexpr ::= value '*' mulexpr
| value '/' mulexpr
| value '%' mulexpr
private MyExpression parseOr(MyScanner scanner) {
MyExpression expression = null;
MyExpression rightExpr = parseAnd(scanner);
Token token = scanner.getCurrentToken();
if (token.hasValue("|") {
expression = new MyExpression();
expression.setOperator(OR);
Token nextToken = scanner.getNextToken(); // remember, this is scanning in reverse
MyExpression leftExpression = parseOr(scanner);
expression.setLeft(leftExpression);
expression.setRight(rightExpression);
}
else {
expression = rightExpression;
}
return expression;
}