假设需要语法来解析以下模板:
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. ;
所以它有效,但它看起来很难看,如果我有两个以上的部分,它会很快变得无法管理?有什么建议吗?
答案 0 :(得分:0)
这样的东西?通常,您将在稍后的处理步骤中仅强制执行每个项目中的一个。否则,如你所见,语法变得笨拙。
grammar PRL;
report
: REPORT
BEGIN
query
END
;
query
: QUERY
BEGIN
body_decl*
END
;
body_decl :
aggregation_decl dimension_decl
| dimension_decl aggregation_decl;
答案 1 :(得分: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();};
然后,检查AST中的decl*
是否有重复项。
但是如果你真的想在解析过程中这样做,你需要抓住decl
的左侧并在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
时,将抛出异常。在这种情况下,只要集合已经包含某个key
,它就会计算为false。