【问题标题】:String expression parsing tips?字符串表达式解析技巧?
【发布时间】:2010-01-17 07:54:34
【问题描述】:

我在今年的假期里感到无聊,随机决定为 Java 编写一个简单的列表理解/过滤库(我知道那里有一些很棒的库,我只是想自己写一个) )。

对于这个列表:

LinkedList<Person> list = new LinkedList<Person>();
            list.add(new Person("Jack", 20));
            list.add(new Person("Liz", 58));
            list.add(new Person("Bob", 33));

语法是:

Iterable<Person> filtered = Query.from(list).where(
    Condition.ensure("Age", Op.GreaterEqual, 21)
    .and(Condition.ensure("Age", Op.LessEqual, 50));

我知道它很难看,但是如果我使用静态导入并使用较短的方法名称,它会变得非常简洁。

以下语法是最终目标:

Iterable<Person> list2 = Query.from(list).where("x=> x.Age >= 21 & x.Age <= 50");

显然表达式解析不是我最擅长的领域,我在解析嵌套/多个条件时遇到了麻烦。有人知道我可能会觉得有用的一些资源/文献吗?

我目前只有一个条件表达式从 String lambda 语法成功解析:"x=&gt; x.Name == Jack"。我的底层表达式结构相当稳固,可以轻松处理任意数量的嵌套,问题只是从字符串中解析表达式。

谢谢

只是为了好玩,这里有一点关于幕后表达式结构如何工作的见解(显然我可以在下面的示例中指定'op.GreaterEqual'等......但我想演示如何它对任何数量的嵌套都很灵活):

Condition minAge1 = Condition.ensure("Age", Op.Equal, 20);
Condition minAge2 = Condition.ensure("Age", Op.Greater, 20);
Expression minAge = new Expression(minAge1, Express.Or, minAge2);
Expression maxAge = Condition.ensure("Age", Op.Equal, 50).or(Condition.ensure("Age", Op.Less, 50));
Expression ageExpression = new Expression(minAge, Express.And, maxAge);

Condition randomException = Condition.ensure("Name", Op.Equal, "Liz");
Expression expressionFinal = new Expression(ageExpression, Express.Or, randomException);

【问题讨论】:

  • "x=&gt; x.Age &gt;= 21 &amp; x.Age &lt;= 50" 不太适合我:你能详细说明一下吗? &amp; 前面有三个表达式,这与普通的 sql 样式子句有很大不同。
  • 我不打算编写提供程序来将我的实用程序链接到关系数据库,尽管这可能很有趣。你到底要我详细说明什么?
  • @Chii:我认为,"x=> x.Age >=21 & x.Age ' 运算符右侧的表达式。

标签: java parsing lambda list-comprehension


【解决方案1】:

基本上,您需要的是 recursive descent parser 用于表达式。这是编译器理论中的一个重要主题,因此任何有关编译器的书籍都将涵盖该主题。在正式的语法术语中,它看起来像这样:

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       : '>' | '>=' | '<' | '<=' | '=' | '!=' ;

上面的语法(大部分)是ANTLR 形式,这是我最熟悉的。

解析布尔或算术表达式是处理解析时的经典介绍性主题,因此您应该能够找到大量关于它的文献。如果您想追求 ANTLR(因为您使用的是 Java),我强烈建议您阅读 The Definitive ANTLR Reference: Building Domain-Specific Languages

如果这一切看起来有点矫枉过正,而且有点难以接受,那么你可能是对的。这是一个很难开始的话题。

您的另一种选择是不创建任意字符串表达式,而是使用流畅的界面(就像您正在做的那样):

List results = from(source)
  .where(var("x").greaterThan(25), var("x").lessThan(50))
  .select("field1", "field2");

因为这是在代码中说明表达式树,应该更容易实现。

【讨论】:

  • 我想这就是我要研究的领域。非常感谢,至少我现在有了一个起点。
  • 我并不是说我当前的实现已经完成,但它可以处理我迄今为止所能提供的任何东西。不过,它需要在效率方面做一些工作。我真的认为将冗长减少到我满意的程度的唯一方法是通过字符串实现(将被解析为我现在使用的完全相同的底层表达式结构)。
【解决方案2】:

要添加到 cletus 答案,您首先要定义您的语法。

以下表达式语法在大多数情况下工作得很好,不幸的是,正常的递归下降不允许您在每个产生式中首先定义递归部分。这将导致您递归调用生产方法,直到出现堆栈溢出。

orexpr ::= orexpr '|'和表达式 |和表达式 andexpr ::= andexpr '&' 比较 |比较 比较 ::= addexpr compareOp addexpr | addexpr addexpr ::= addexpr '+' mulexpr | addexpr '-' 多路数 |多路复用器 mulexpr ::= mulexpr '*' 值 | mulexpr '/' 值 | mulexpr '%' 值 |价值 值 ::= 整数 |漂浮 |多变的 |引述 | '(' orexpr ')'

正常递归下降需要您定义 mulexpr,例如:

mulexpr ::= value '*' |值 '/' |值 '%'

但是这个语法的问题是,表达式树的构建方式是你的操作顺序都是相反的。

妥协:在上面写的原始语法上反向使用递归下降。从右到左解析表达式。从右到左构建你的树。它将保留操作的顺序。

在递归下降中,您通常为每个产生式编写一个解析方法。 parseOr() 方法可能如下所示:

私人 MyExpression parseOr(MyScanner 扫描仪) { MyExpression 表达式 = null; MyExpression rightExpr = parseAnd(scanner); 令牌令牌 =scanner.getCurrentToken(); if (token.hasValue("|") { 表达式 = 新的 MyExpression(); 表达式.setOperator(OR); 令牌 nextToken =scanner.getNextToken(); // 记住,这是反向扫描 MyExpression leftExpression = parseOr(scanner); 表达式.setLeft(leftExpression); 表达式.setRight(rightExpression); } 别的 { 表达式 = 右表达式; } 返回表达式; }

【讨论】:

    【解决方案3】:

    感谢所有提示。我认为其中的大部分内容超出了我的需要,所以我最终将其进行了正则表达式,以便将内容放入可管理的组中,我可以轻松地在 20-30 行代码中解析。

    我已经得到了字符串 LambdaExpression 接口几乎以及流畅的接口,只是一两个小错误。

    我可能会继续开发它只是为了好玩,但它显然太低效而无法真正使用,因为它大约 90% 是基于反射的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 2016-04-29
      • 2015-06-20
      • 2012-08-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多