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 中,见下文)。
- 更有趣的是其后果:指向未解析名称的位置的引用会发生什么情况?在这里,我们必须推迟引用解析过程,因为我们无法知道名称在解析时是否匹配...
我有一个简单的示例模型,我想在其中为未使用 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 中,见下文)。
- 更有趣的是其后果:指向未解析名称的位置的引用会发生什么情况?在这里,我们必须推迟引用解析过程,因为我们无法知道名称在解析时是否匹配...