textX:如何使用 ObjectProcessors 生成对象名称?

textX: How to generate object names with ObjectProcessors?

我有一个简单的示例模型,我想在其中为未使用 as <NAME> 命名的 Position 规则的对象生成名称。这是必需的,以便我以后可以使用内置的 FQN 范围提供程序找到它们。

我的想法是在 position_name_generator 对象处理器中执行此操作,但只有在解析整个模型后才会调用它。我真的不明白这样做的原因,因为那时我需要 Project 中的 Position 对象,对象已经创建,对象处理器仍然不会被调用。

另一个想法是在 Position.location 的自定义范围提供程序中执行此操作,然后首先生成名称,然后使用内置的 FQN 查找 Location 对象。虽然这会起作用,但我认为这很老套,我宁愿避免它。

解决此问题的 textX 方法是什么?

(请注意,这只是一个小示例。实际上,相当大且复杂的模型需要类似的功能。无法使用生成的名称更改此行为,因为这是一项要求。 )

import textx


MyLanguage = """
    Model
        :   (locations+=Location)*
            (employees+=Employee)*
            (positions+=Position)*
            (projects+=Project)*
        ;

    Project
        :   'project' name=ID
            ('{'
                ('use' use=[Position])*
            '}')?
        ;

    Position
        :   'define' 'position' employee=[Employee|FQN] '->' location=[Location|FQN] ('as' name=ID)?
        ;

    Employee
        :   'employee' name=ID   
        ;

    Location
        :   'location' name=ID
            ( '{'
                (sub_location+=Location)+
            '}')?
        ;

    FQN
        :   ID('.' ID)*
        ;

    Comment:
      /\/\/.*$/
    ;                
"""

MyCode = """
    location Building
    {
        location Entrance
        location Exit
    }

    employee Hans
    employee Juergen

    // Shall be referred to with the given name: "EntranceGuy"
    define position Hans->Building.Entrance as EntranceGuy 
    // Shall be referred to with the autogenerated name: <Employee>"At"<LastLocation>
    define position Juergen->Building.Exit                  

    project SecurityProject
    {
        use EntranceGuy
        use JuergenAtExit
    }
"""


def position_name_generator(obj):
    if "" == obj.name:
        obj.name = obj.employee.name + "At" + obj.location.name


def main():
    meta_model = textx.metamodel_from_str(MyLanguage)
    meta_model.register_scope_providers({
        "Position.location": textx.scoping.providers.FQN(),
    })

    meta_model.register_obj_processors({
        "Position": position_name_generator,
    })

    model = meta_model.model_from_str(MyCode)
    assert model, "Could not create model..."


if "__main__" == __name__:
    main()

解决这个问题的 textx 方法是什么...

您描述的用例是根据其他模型元素定义对象的名称,包括对其他模型元素的引用。这目前不是我们测试套件和 textx 文档中包含的任何测试和用例的一部分。

对象处理器在模型构建期间的定义阶段执行(参见 http://textx.github.io/textX/stable/scoping/#using-the-scope-provider-to-modify-a-model)。在所描述的设置中,它们在参考解析之后执行。由于名称 defined/deduced 本身是引用解析所必需的,因此此处不能使用 对象处理器 (即使我们允许控制何时执行对象处理器,在范围之前或之后分辨率,所描述的设置仍然不起作用)。

考虑到模型加载的动态性(参见 http://textx.github.io/textX/stable/scoping/#using-the-scope-provider-to-modify-a-model),解决方案位于范围提供程序中(正如您所建议的)。在这里,我们允许控制引用解析的顺序,以便推迟对自定义过程命名的对象的引用,直到需要引用 deduce/define 名称解析。

可能的解决方法

https://github.com/textX/textX/pull/194 (with an attached issue https://github.com/textX/textX/issues/193) 中讨论了如何解决您的用例的初步草图。此 textx PR 包含一个 scoping.py 版本,您可能会用于您的项目(只需复制并重命名模块)。 full-fledged 解决方案可能是 textx TEP-001 的一部分,我们计划在其中使 end-user.

的范围更加可控

解决这个绝对有趣的问题向我揭示了 textx 框架的新方面。

  • 名称取决于模型内容(涉及未解析的引用)。根据我们的参考解析逻辑,此名称解析可以推迟(在参考 PR 中,见下文)。
  • 更有趣的是其后果:指向未解析名称的位置的引用会发生什么情况?在这里,我们必须推迟引用解析过程,因为我们无法知道名称在解析时是否匹配...

您的示例已包括在内:https://github.com/textX/textX/blob/analysis/issue193/tests/functional/test_scoping/test_name_resolver/test_issue193_auto_name.py