6

我正在用 python 编写游戏,并决定为地图数据文件创建一个 DSL。我知道我可以用正则表达式编写自己的解析器,但我想知道是否有现有的 python 工具可以更容易地做到这一点,比如 PHP 引擎中使用的 re2c。

一些额外的信息:

  • 是的,我确实需要 DSL,即使我不需要,我仍然想要在项目中构建和使用 DSL。
  • DSL 只包含数据(声明性的?),它不会被“执行”。大多数行看起来像:

    SOMETHING: !abc @123 #xyz/123

    我只需要读取数据树。

4

8 回答 8

12

我一直对pyparsing印象深刻。作者 Paul McGuire 在python 列表/comp.lang.python上很活跃,并且对任何有关它的查询都非常有帮助。

于 2008-12-04T01:02:53.107 回答
6

这是一种非常有效的方法。

abc= ONETHING( ... )
xyz= ANOTHERTHING( ... )
pqr= SOMETHING( this=abc, that=123, more=(xyz,123) )

声明性的。易于解析。

和...

它实际上是 Python。一些类声明,工作就完成了。DSL 实际上是类声明。

重要的是 DSL 只是创建对象。定义 DSL 时,首先必须从对象模型开始。稍后,您围绕该对象模型添加了一些语法。您不是从语法开始,而是从模型开始。

于 2008-12-04T01:09:56.947 回答
4

是的,有很多——太多了——解析工具,但标准库中没有。

据我所见,PLY 和 SPARK 很受欢迎。PLY就像 yacc,但你用 Python 做所有事情,因为你用文档字符串编写语法。

就个人而言,我喜欢解析器组合器的概念(取自函数式编程),我也非常喜欢pyparsing:您可以直接在 python 中编写语法和操作,而且很容易上手。我最终生成了我自己的带有动作的树节点类型,而不是使用它们的默认ParserElement类型。

否则,您还可以使用现有的声明性语言,例如YAML

于 2008-12-04T00:59:31.600 回答
2

我在工作中编写了类似的东西来读取 SNMP 通知定义并从中自动生成 Java 类和 SNMP MIB 文件。使用这个小 DSL,我可以编写 20 行规范,它会生成大约 80 行 Java 代码和一个 100 行 MIB 文件。

为了实现这一点,我实际上只是使用了直接的 Python 字符串处理(split()、切片等)来解析文件。我发现 Python 的字符串功能足以满足我的大多数(简单)解析需求。

除了其他人提到的库之外,如果我正在编写更复杂的东西并且需要适当的解析功能,我可能会使用支持 Python(和其他语言)的ANTLR 。

于 2008-12-04T20:08:42.280 回答
2

DSL 是个好东西,所以你不需要为自己辩护 :-) 但是,你考虑过内部 DSL 吗?与外部(解析的)DSL 相比,它们有很多优点,至少值得考虑。将 DSL 与本机语言的强大功能混合在一起确实可以为您解决很多问题,而且 Python 在内部 DSL 方面也不是很差,而且with声明很方便。

于 2008-12-05T06:10:45.340 回答
2

对于您所描述的“小语言”,我使用简单的拆分、shlex(请注意 # 定义注释)或正则表达式。

>>> line = 'SOMETHING: !abc @123 #xyz/123'

>>> line.split()
['SOMETHING:', '!abc', '@123', '#xyz/123']

>>> import shlex
>>> list(shlex.shlex(line))
['SOMETHING', ':', '!', 'abc', '@', '123']

以下是一个示例,因为我不知道您在寻找什么。

>>> import re
>>> result = re.match(r'([A-Z]*): !([a-z]*) @([0-9]*) #([a-z0-9/]*)', line)
>>> result.groups()
('SOMETHING', 'abc', '123', 'xyz/123')
于 2009-11-02T12:00:47.817 回答
1

在声明性 python 的行中,我编写了一个名为“bpyml”的辅助模块,它可以让你以一种更 XML 结构化的方式在 python 中声明数据,而不需要详细的标签,它也可以转换为 XML 或从 XML 转换,但它是有效的 python。

https://svn.blender.org/svnroot/bf-blender/trunk/blender/release/scripts/modules/bpyml.py

示例使用 http://wiki.blender.org/index.php/User:Ideasman42#Declarative_UI_In_Blender

于 2011-11-18T10:15:42.693 回答
1

这是一个更简单的方法来解决它

如果我可以使用新的运算符扩展 python 语法以在语言中引入新的功能怎么办?例如,<=>用于交换两个变量值的 new 运算符。

我怎样才能实现这种行为?AST 模块来了。最后一个模块是处理抽象语法树的便捷工具。这个模块最酷的地方在于它允许我编写生成树然后将其编译为 python 代码的 python 代码。

假设我们想将超集语言(或类似 Python 的语言)编译为 Python:

从 :

    a <=> b

至:

    a , b = b , a
  1. 我需要将我的“python like”源代码转换为令牌列表。所以我需要一个分词器,一个 Python 源代码的词法扫描器。标记模块

  2. 我可以使用相同的元语言来定义新的“类 Python”语言的语法,然后构建抽象语法树AST的结构

为什么要使用 AST?

  1. 在评估不受信任的代码时,AST 是一个更安全的选择
  2. 在执行代码之前操作
from tokenize import untokenize, tokenize, NUMBER, STRING, NAME, OP, COMMA
import io
import ast

s = b"a <=> b\n" # i may read it from file
b = io.BytesIO(s)
g = tokenize(b.readline)
result = []
for token_num, token_val, _, _, _ in g:
    # naive simple approach to compile a<=>b to a,b = b,a
    if token_num == OP and token_val == '<=' and next(g).string == '>':
        first  = result.pop()
        next_token = next(g)
        second = (NAME, next_token.string)
        result.extend([
            first,
            (COMMA, ','),
            second,
            (OP, '='),
            second,
            (COMMA, ','),
            first,
        ])
    else:
        result.append((token_num, token_val))

src = untokenize(result).decode('utf-8')
exp = ast.parse(src)
code = compile(exp, filename='', mode='exec')


def my_swap(a, b):
    global code
    env = {
        "a": a,
        "b": b
    }
    exec(code, env)
    return env['a'], env['b']

print(my_swap(1,10))

其他使用 AST 的模块,其源代码可能是有用的参考:

  • textX-LS:用于描述形状集合并为我们绘制的 DSL。

  • pony orm:您可以使用 Python 生成器和 lambdas 编写数据库查询,并转换为 SQL 查询字符串——pony orm 在后台使用 AST

  • osso:基于角色的访问控制框架处理权限。

于 2021-10-30T13:29:19.373 回答