我需要根据下面的定义设计一种强制约束的语言。下面的列表是brat的实际注释配置。我的任务是为我们的内部工具设计一种用于类似目的的语言。我打算在python中编写解析器。
[entities]
Drug
DrugClass
Procedure
Therapy
AE
SAE
Disease
[relations]
Equiv Arg1:<ENTITY>, Arg2:<ENTITY>, <REL-TYPE>:symmetric-transitive
BelongsTo Arg1:Drug , Arg2:DrugClass
BelongsTo Arg1:AE , Arg2:AE
BelongsTo Arg1:AE , Arg2:SAE
BelongsTo Arg1:SAE , Arg2:SAE
BelongsTo Arg1:SAE , Arg2:AE
BelongsTo Arg1:Disease , Arg2:Disease
BelongsTo Arg1:Drug , Arg2:Therapy
BelongsTo Arg1:Procedure , Arg2:Therapy
BelongsTo Arg1:Surgery , Arg2:Therapy
<CAUSE>=Drug|DrugClass|Therapy|Procedure
<EV>=AE|SAE
AssociatedWith Arg1:<CAUSE> , Arg2:<EV>
NotAssociatedWith Arg1:<CAUSE> , Arg2:<EV>
Causes Arg1:<CAUSE> , Arg2:<EV>
NotCauses Arg1:<CAUSE> , Arg2:<EV>
HasEffect Arg1:<CAUSE> , Arg2:Disease
HasNoEffect Arg1:<CAUSE> , Arg2:Disease
<OVERLAP> Arg1:<ANY>, Arg2:<ANY>, <OVL-TYPE>:<ANY>
我想到的是以下内容。
[entities]
# subtyping entities which is already present in brat
Entity
Trigger
Drug
DrugClass
Procedure
Therapy
Effect
AE
SAE
Disease
[relations]
# Any subtype of Trigger can be Associated with any subtype of Effect except Disease
AssociatedWith Arg1:Trigger , Arg2:Effect.except(Disease)
NotAssociatedWith Arg1:Trigger , Arg2:Effect.except(Disease)
Causes Arg1:Trigger , Arg2:Effect.except(Disease)
NotCauses Arg1:Trigger , Arg2:Effect.except(Disease)
HasEffect Arg1:Trigger , Arg2:Disease
HasNoEffect Arg1:Trigger , Arg2:Disease
# Equiv relation can exist between Drug and Drug,
# DrugClass and DrugClass and so on,
# but not between different subtypes Drug and AE
Equiv Arg1:Entity* , Arg2: Entity*
BelongsTo Arg1:Entity* , Arg2: Entity*
BelongsTo Arg1:Drug , Arg2:Therapy
BelongsTo Arg1:Procedure , Arg2:Therapy
BelongsTo Arg1:Surgery , Arg2:Therapy
<OVERLAP> Arg1:<ANY>, Arg2:<ANY>, <OVL-TYPE>:<ANY>
是否已有可以执行此操作的语言。我看了Prolog,这看起来像是一种矫枉过正。我想我正在寻找一种语言,它可以处理对象和粒度寻址的分组,从而可以简洁地指定约束。感谢
答案 0 :(得分:1)
更容易思考,这些定义在解析后如何在内存中看起来像 - 然后向后工作。一种简单的解析方法是将每一行视为“方法调用” - 例如“BelongsTo Arg1:Drug”可以看作是对ParserContext对象的隐式“this.BelongsTo”方法调用。因此,您基本上使用ParserContext类“评估”文件,并且文件中的每一行“静默”调用基类上的方法,慢慢构建内存中的方法。当最后一行完成时,内存中的定义也已完成。
在Ruby中,人们不需要围绕方法的parantheses和“this”。隐含(如在大多数其他语言中)。所以,你认为DSL /语言只是Ruby中的普通代码。
让我们来看一个简单的Parser类。
class Context
def new
# initialize some vars
this.entities = {}
end
def Entity(name, ...args)
# save to
self.entities[name] = ...args
end
def BelongsTo(entity, ...args)
# process belongs to with the entity
end
... every DSL feature is a language method ...
... if you want to "zoom-in" or focus on an entity, use blocks
让我们采取以下DSL ......
Entity "Arg1"
BelongsTo "Arg2"
这个简单的读者:
ctx = Context.new
ctx.instance_eval File.read "my dsl file"
因此,仅使用设置为上下文对象的“this”来评估DSL文件,并且DSL中的每一行都只是简单的方法调用。 Ruby中的Gemfile,Rails中的Routes文件,许多ORM定义等 - 一直使用这个技巧。 看起来像DSL一样只是漂亮的代码。作为Ruby上的一个简单示例 - https://github.com/rdsubhas/ruby-blocks/blob/master/Mapping - 您会看到虽然它看起来像DSL,但它实际上只是代码,并且实际上还有 no parser 来解析该代码。语言本身解析它。
这不仅适用于Ruby,每种语言都有自己的“评估”技巧。我相信同样可以使用nodejs(茉莉花/黄瓜测试)。
希望能够回答您的问题。总结一下:
旁注:您展示的DSL看起来很复杂,不容易被商业/产品用户使用。看起来它适用于开发人员或高级用户。所以不要在DSL上设置自我约束,就像它必须有括号,或者它不能有双引号或那种东西。如果这是针对高级用户的,则引号或括号的使用将不再重要。选择一个最省力的人。