Contents1使用 Antlr 解析配置文件小实现闲话编程Contents在纠结了一阵子甚至等等配置文件的格式后,还是决定使用实现了一种我自定义的格式的解析。这个格式是这个样子的// Here is some commentshared { 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};};其实是一个形式的文本文件,支持的基本类型有:字符串、布尔值、整数、小数、数组。定义的方法类似于或者语言,string _baseUrl = "http://localhost:8080"前面会限定数据类型。如果要定义数组,则用bool sslArray = [true, false];这种形式。然后使用区分不同的配置块。因为可能有些相同的配置会重名,这样我们利用不同的去区分就好了。考虑到有些配置中需要共同的变量的使用,所以定义了一个的,这个是写死的,其他中只能引用中的变量。字符串连接使用..操作符。这样可以组装字符串。详细的实现可以在的子项目中找到。 Contents2另外还实现了一个类似的路由定义文件的解析,长这个样子的controllers admin{package com.riguz.forks.demo.controllerUserControllerFileController}controllers {package com.riguz.forks.demo.adminUserController->AdminUserControllerPostController}filters {package com.riguz.forks.demo.filtersAuthorizationFilterNocsrfFilter}routes admin {+AuthorizationFilterget /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 {+NocsrfFilterget /posts PostUserController.getPosts()get /posts/:id PostUserController.getPost(id: String)}routes guest {+NocsrfFilterget /posts PostUserController.getPosts()get /posts/:id PostUserController.getPost(id: String)}这个文件的解析也在上面的中可以找到实现。通过可以很方便的把类似这样的文件解析出来,你甚至可以实现自己的领域语言。在实现过程中,遇到过一些问题,来说下问题吧。首先是提供了和两种方式,起初使用来实现但是感觉比较麻烦,而使用则可以直接通过返回值来取得解析结果。我们解析一个文件的时候,是自顶向下的,一个个的去解析的,比如我们的配置文件的语法定义如下:script : shared? scope* Contents3 EOF ;其中又是这样的shared : SHARED LBRACE (property SEMI)* RBRACE SEMI ;也就是说shared { k=v...} ;这样的形式,然后又开始到了property : type NAME ASSIGN expression #basicProperty | type NAME ASSIGN LBRACK expression? (COMMA expression)* RBRACK #arrayProperty ;这样层层往下来看。然后解析的时候也是一样,我们首先有一个顶层的解析器: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) { ... }这个负责解析语法文件中定义的块,然后解析里面的: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); });这样又实现一个去解析就好了。详细的实现就不多贴代码了。另外一个问题是,对于错误的处理,我们在哪一步做?比如bool s = "123";这是错误的,我们其实可以在定义的时候就避免这种错误来,但写起来会麻烦一些。目前的实现是在中去对逻辑进行判断的,前面只做语法检查就可以了。参考