DSL/API/GUI之辩
以ElasticSearch的查询为例,API的例子是ES提供的原生json查询接口,写起来比较复杂:
{
"size":0,
"query": {
"filtered":{
"query": {"match": { "content": "ERROR"}},
"filter":{
"range" : {
"time" : { "gte" : 0, "lte" : 1515470055256}
}
}
}
},
"aggs": {
"_group_count0": {
"terms": {
"field": "group"
},
"aggs":{
"_group_count1":{
"terms":{
"field": "host",
"order":{"_count": "desc"}
}
}
}
}
}
}
若用DSL表达,是这样:
STAT COUNT(*) AS errnum WHERE MATCH(content, “ERROR”) AND time>=time1 AND time<=time2 BY group,host SORT errnum;
这是类SQL的语法,相比API要简单易懂很多。
GUI的例子就是类似日志普通查询的GUI:搜索框+展示栏。
在表述复杂逻辑方面,DSL比GUI做的更好,比如下面的水平统计,用GUI做界面会比较臃肿:
STAT group,COUNT(content) AS total,
COUNT(EVAL(MATCH(content, “ERROR”))) AS errnum,
COUNT(EVAL(MATCH(content, “WARN”))) AS warnnum BY group SORT -total
设计心得
我们要做DSL到json API之间的翻译,首先要构建一个描述json API的中间模型IM,将DSL翻译为IM,再从IM翻译为json。这个IM就是Martin Fowler提到的“语义模型”。
实际中,IM往往是AST(抽象语法树)。
综上,DSL到目标的翻译过程是:
DSL -> 语义模型 -> 目标结构
实践中,我们在做DSL的设计时,步骤建议是这样:
- 写DSL文法
- 根据DSL文法设计并组装语义模型
- 做语义模型到目标结构的翻译。
有时,我们会发现,以API或GUI方式提供语义模型给用户使用也是可以的,不是必须要暴露DSL(DSL可作为一个增强功能的卖点)。本质上,DSL/API/GUI都是语义模型的外在呈现。
java做语法解析一般用antlr,antlr里AST的遍历方法:
- 内嵌遍历器 好处:屏蔽节点实现细节; 坏处:不够灵活,翻译代码分散在各个节点类中,牵一发动全身,不方便维护
- 外部访问者 好处:灵活,翻译代码在一个类里;坏处:要暴露节点实现细节,按节点类型分发需要一些技巧才能避免丑陋的“硬分派”。
- ANTLR3的树文法能自动为我们构建外部访问者,这样避免了手写重复代码。不过ANTLR4已没有这个功能了,我们只能自行从CST(具体语法树)转AST。