4

假设需要语法来解析以下模板:

1. REPORT
2. BEGIN
3.   QUERY
4.   BEGIN
5.     AGGREGATION: day
6.     DIMENSION: department
7.   END
8. END

其中第 5 行和第 6 行是可选的,并且 2 行的顺序无关紧要。如何在我的语法文件中指定它?下面是我的解决方案(见第 12 行):

1. grammar PRL;
2. report
3.  : REPORT
4.      BEGIN 
5.          query
6.      END
7.  ;
8.
9. query
10.  : QUERY 
11.     BEGIN
12.         (aggregation_decl dimension_decl | dimension_decl aggregation_decl)? 
13.     END
14. ;

所以它可以工作,但它看起来很丑,如果我有两个以上的部分,它会很快变得难以管理吗?有什么建议吗?

4

2 回答 2

0

像这样的东西?通常,您会强制在以后的处理步骤中仅存在每个项目中的一个。否则,如您所见,语法会变得笨拙。

grammar PRL;
report
  : REPORT
      BEGIN 
          query
      END
  ;

query
  : QUERY 
     BEGIN
       body_decl* 
     END
 ;

body_decl :
   aggregation_decl dimension_decl
 | dimension_decl aggregation_decl;
于 2012-06-26T03:49:53.820 回答
0

正如亚当已经提到的:这通常是在解析器创建某种(抽象)解析树之后完成的。您只需收集所有类型的声明,如下所示:

grammar PRL;

report
 : REPORT BEGIN query END
 ;

query
 : QUERY BEGIN decl* END
 ;

decl
 : NAME ':' NAME
 ;

REPORT : 'REPORT';
BEGIN  : 'BEGIN';
END    : 'END';
QUERY  : 'QUERY';
NAME   : ('a'..'z' | 'A'..'Z')+;

SPACE  : (' ' | '\t' | '\r' | '\n')+ {skip();};

decl*之后,检查您的 AST中是否有重复项。

但是如果你真的想在解析过程中这样做,你需要抓住左侧decl并将它们添加到 a 中Set,当你偶然发现重复时,抛出一个谓词异常:

grammar PRL;

@parser::header {
  import java.util.Set;
  import java.util.HashSet;
}

report
 : REPORT BEGIN query END
 ;

query
 : QUERY BEGIN unique_decls END
 ;

unique_decls
@init{Set<String> set = new HashSet<String>();}
 : (decl {set.add($decl.key)}?)*
 ;

decl returns[String key]
 : k=NAME ':' NAME {$key = $k.text;}
 ;

REPORT : 'REPORT';
BEGIN  : 'BEGIN';
END    : 'END';
QUERY  : 'QUERY';
NAME   : ('a'..'z' | 'A'..'Z')+;

SPACE  : (' ' | '\t' | '\r' | '\n')+ {skip();};

{set.add($decl.key)}?称为Validating Semantic Predicates将在其中的代码 ( set.add($decl.key)) 计算为时引发异常false。在这种情况下,只要集合已经包含某个 ,它就会评估为 false key

于 2012-06-26T07:58:59.820 回答