编写用于从表中选择行的领域特定语言
我正在写一个服务器,预计会有很多不同的人来使用,而我并不一定能和每个人直接联系。这些服务器会在一个集群中相互通信。服务器的一部分功能是从一个可能非常大的表中选择一小部分行。具体选择哪些行需要一些调整,而且很重要的一点是,运行这个集群的人(比如我自己)能够更新选择标准,而不需要每个服务器管理员都去部署一个新版本的服务器。
简单地用Python写这个功能并不是一个好主意,因为没有人会想要安装一个在运行时下载并执行任意Python代码的服务器。
我需要的是一些建议,关于如何以最简单的方式实现一个特定领域的语言来达到这个目标。这个语言需要能够进行简单的表达式计算,还要能查询表的索引,并遍历返回的行。写和读这个语言的简单程度比实现它的简单程度要次要。我也希望不需要写一个完整的查询优化器,所以能够明确指定要查询哪些索引的方案会更理想。
这个语言的接口需要具备类似于App Engine数据存储导出的功能:你可以对表的任何索引进行顺序范围查询(比如小于、大于、范围和相等查询),然后通过任何布尔表达式过滤返回的行。你还可以将多个独立的结果集合并在一起。
我意识到这个问题听起来像是在询问SQL。然而,我并不想要求支持这个数据的数据存储是一个关系数据库,也不想自己去重新实现SQL的复杂性。我处理的只有一个已知结构的单一表,最后也不需要进行连接查询。更简单的东西会更受欢迎。
编辑:扩展描述以澄清一些误解。
9 个回答
“实现一个领域特定语言”
“没人会想安装一个服务器,它在运行时下载并执行任意的Python代码”
我想要一个领域特定语言,但我不想让Python成为那个语言。好吧,那你打算怎么执行这个领域特定语言呢?如果不使用Python,什么样的运行环境是可以接受的呢?
如果我有一个C程序,它恰好嵌入了Python解释器,这样可以吗?
而且——如果Python不是一个可以接受的运行环境——那为什么这个问题会有Python的标签呢?
我觉得我们需要更多的信息。如果下面的内容有任何错误的假设,请告诉我。
首先,正如你自己提到的,已经有一种选择任意表格行的语言,叫做“SQL”。既然你不想重新发明SQL,我假设你只需要从一个固定格式的单一表格中查询数据。
如果是这样的话,你可能不需要实现一种新的语言(虽然这也是一种选择);如果你熟悉面向对象编程,创建一个“过滤器”对象可能会更简单。
更具体地说,可以创建一个“过滤器”集合,里面包含一个或多个选择标准对象。你可以让这些对象继承自一个或多个基础类,这些基础类代表不同的选择类型(比如范围、少于、精确匹配、类似等)。一旦这些基础类建立好,你就可以为每一列创建特定的继承版本。最后,根据你想支持的查询复杂度,你需要实现一些连接的逻辑,以处理不同标准之间的与(AND)、或(OR)和非(NOT)关系。
如果你愿意,可以创建一个简单的图形界面来加载这个集合;如果你没有其他想法,可以参考Excel中的过滤功能。
最后,将这个集合的内容转换成相应的SQL语句,并传递给数据库应该是很简单的。
不过,如果你追求简单性,并且你的用户懂SQL,你可以直接让他们输入WHERE子句的内容,然后程序自动构建其余的查询。从安全的角度来看,如果你的代码控制了选择的列和FROM子句,并且数据库权限设置得当,同时对用户输入的字符串进行一些合理性检查,这将是一个相对安全的选择。
构建一个可以被Python解释的领域特定语言(DSL)。
第一步:创建运行时的类和对象。这些类会把所有的循环和SQL语句,以及算法处理的逻辑都封装在它们的方法里。你会大量使用命令模式和策略模式来构建这些类。大多数东西都是一个命令,选项和选择则是可以插拔的策略。可以看看Apache Ant的任务API,这是个很好的例子。
第二步:验证这个对象系统是否真的有效。确保设计简单且完整。你的测试会构建命令和策略对象,然后执行顶层的命令对象。命令对象会完成实际的工作。
到这个时候,你基本上就完成了。你的运行时只是由上面提到的对象配置而成。[这并不像听起来那么简单。需要小心定义一组可以实例化的类,然后让它们“相互交流”以完成你的应用工作。]
需要注意的是,你所拥有的只需要声明就可以了。程序式编程有什么问题呢?一旦你开始用程序式元素来写DSL,你会发现你需要越来越多的功能,直到你写出了一个不同语法的Python。这可不好。
而且,程序式语言的解释器确实很难写。执行状态和引用范围的管理都很麻烦。
你可以使用原生Python,这样就不用担心“走出沙盒”了。实际上,这也是你进行单元测试的方式,使用一个简短的Python脚本来创建你的对象。Python将成为你的DSL。
["等等,你可能会说,如果我只是用Python作为DSL,人们就可以执行任意的东西。"这取决于PYTHONPATH和sys.path里的内容。可以查看site模块,了解如何控制可用的内容。]
声明式DSL是最简单的。这完全是一个表示的练习。一段仅仅设置一些变量值的Python代码是很不错的。这就是Django所使用的。
你可以使用ConfigParser作为表示你运行时对象配置的语言。
你也可以使用JSON或YAML作为表示你运行时对象配置的语言。现成的解析器完全可以使用。
你也可以使用XML。虽然设计和解析比较困难,但它也能很好地工作。人们很喜欢它。这就是Ant和Maven(以及很多其他工具)用声明式语法来描述过程的方式。我不推荐它,因为它太啰嗦,麻烦。我建议直接使用Python。
或者,你可以大胆尝试,发明自己的语法并编写自己的解析器。