首页
随机
最近更改
特殊页面
社群首页
参数设置
关于WHY42
免责声明
WHY42
搜索
用户菜单
登录
欢迎来到Riguz的小站!这是一个私人wiki,用来记录一些我的笔记。
查看“︁Blog:使用 Antlr 解析配置文件”︁的源代码
←
Blog:使用 Antlr 解析配置文件
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
在纠结了一阵子 yml,ini,xml甚至 lua 等等 配置文件的格式后,还是决定使用antlr实现了一种我自定义的格式的解析。 这个格式是这个样子的: <syntaxhighlight lang="lua"> // Here is some comment shared { string _baseUrl = "http://localhost:8080"; string domain = "riguz.com"; bool ssl = false; int version = 19; int subVersion = 25; float number = 19.25; string urls = ["http://localhost:8080", "http://riguz.com:8080"]; string domains = ["riguz.com", "dr.riguz.com"]; bool sslArray = [true, false]; int versionArray = [19, 25]; float numberArray = [18.01, 19.25, 20.23]; }; scope dev_db { string url = ${domain} .. ":3306/mysql"; string user = "lihaifeng"; int connections = 10; string password = "iikjouioqueyjkajkqq=="; string domains = ${domains}; }; </syntaxhighlight> 其实是一个k-v形式的文本文件,支持的基本类型有:字符串、布尔值、整数、小数、数组。定义的方法类似于Java或者C语言, <pre> string _baseUrl = "http://localhost:8080" </pre> 前面会限定数据类型。如果要定义数组,则用 <pre> bool sslArray = [true, false]; </pre> 这种形式。 然后使用scope区分不同的配置块。因为可能有些相同的配置会重名,这样我们利用不同的scope去区分就好了。考虑到有些配置中需要共同的变量的使用,所以定义了一个shared的scope,这个是写死的scope,其他scope中只能引用shared scope中的变量。 字符串连接使用```..```操作符。这样可以组装字符串。详细的实现可以在[https://github.com/soleverlee/forks/tree/master/config/src/main forks的子项目config]中找到。 另外还实现了一个类似Play! Framework的路由定义文件的解析,长这个样子的: <syntaxhighlight lang="lua"> controllers admin{ package com.riguz.forks.demo.controller UserController FileController } controllers { package com.riguz.forks.demo.admin UserController->AdminUserController PostController } filters { package com.riguz.forks.demo.filters AuthorizationFilter NocsrfFilter } routes admin { +AuthorizationFilter get /users UserController.getUsers() get /users/:id UserController.getUser(id: Long) post /users UserController.createUser() get /users/:id/files/*name FileUserController.getFile(id: Long, name: String) } routes guest { +NocsrfFilter get /posts PostUserController.getPosts() get /posts/:id PostUserController.getPost(id: String) } routes guest { +NocsrfFilter get /posts PostUserController.getPosts() get /posts/:id PostUserController.getPost(id: String) } </syntaxhighlight> 这个文件的解析也在上面的git中可以找到实现。通过Antlr可以很方便的把类似这样的文件解析出来,你甚至可以实现自己的领域语言。在实现过程中,遇到过一些问题,来说下问题吧。 首先是Antlr提供了Listener和Visitor两种方式,起初使用Listener来实现但是感觉比较麻烦,而使用Visitor则可以直接通过返回值来取得AST解析结果。我们解析一个文件的时候,是自顶向下的,一个个的去解析的,比如我们的配置文件的antlr语法定义如下: <syntaxhighlight lang="lua"> script : shared? scope* EOF ; </syntaxhighlight> 其中shared又是这样的 <syntaxhighlight lang="lua"> shared : SHARED LBRACE (property SEMI)* RBRACE SEMI ; </syntaxhighlight> 也就是说 ```shared { k=v...} ;```这样的形式,然后又开始到了property: <syntaxhighlight lang="lua"> property : type NAME ASSIGN expression #basicProperty | type NAME ASSIGN LBRACK expression? (COMMA expression)* RBRACK #arrayProperty ; </syntaxhighlight> 这样层层往下来看。然后解析的时候也是一样,我们首先有一个顶层的解析器: <syntaxhighlight lang="java"> public class ScriptVisitor extends CfParserBaseVisitor<Map<String, ScriptVisitor.Scope>> { private static final Logger logger = LoggerFactory.getLogger(ScriptVisitor.class); @Override public Map<String, Scope> visitScript(CfParser.ScriptContext ctx) { ... } </syntaxhighlight> 这个Visitor负责解析语法文件中定义的script块,然后解析里面的scope: <syntaxhighlight lang="java"> ScopeVisitor scopeVisitor = new ScopeVisitor(context); ctx.scope().forEach(scopeContext -> { logger.debug("Visit scope:{}", scopeContext.getText()); Scope scope = scopeContext.accept(scopeVisitor); scopes.put(scope.name, scope); }); </syntaxhighlight> 这样又实现一个ScopeVisitor去解析scope就好了。详细的实现就不多贴代码了。 另外一个问题是,对于错误的处理,我们在哪一步做?比如```bool s = "123";```这是错误的,我们其实可以在定义grammar的时候就避免这种错误来,但写起来会麻烦一些。目前的实现是在Visitor中去对逻辑进行判断的,前面只做语法检查就可以了。 参考: * http://jakubdziworski.github.io/java/2016/04/01/antlr_visitor_vs_listener.html
返回
Blog:使用 Antlr 解析配置文件
。