使用TypePal编写语义规则

huangapple go评论103阅读模式
英文:

Writing Semantic Rules with TypePal

问题

我正在使用Rascal MPL设计数据建模的DSL,以下是我的语法规范的一部分:

语法 声明
    = @可折叠 实体: "entity" 实体标识符 名称  "{" 字段+ 字段 "}"
    ;
    
语法 字段 
    = 字段: 标识符 名称 ":" 类型 类型约束? 约束
    | 单向引用: 标识符 名称 "->" 类型 类型标识符
    | 双向引用: 标识符 名称 "->" 类型 类型标识符 "inverse" 标识符 引用 "::" 标识符 属性
    ;

实体 雇员  {
   雇佣日期 : 日期
   工资率 : 货币
   上司 -> 经理 逆经理::下属
}

实体 经理 {
   下属 -> 集合<雇员> 逆雇员::上司
}

我已经使用TypePal实现了很多类型检查,但以下是我的问题:
如何强制执行规则,即在雇员实体的属性 上司 中必须存在对应的属性 下属,反之亦然,对于经理实体中的属性。
谢谢。

英文:

I am using Rascal MPL to design a DSL for data modeling here is a snippet of my grammar specification:

syntax Declaration
    = @Foldable entity: &quot;entity&quot; EntityId name  &quot;{&quot; Field+ fields “}”
;

syntax Field 
   =  field: Id name &quot;:&quot; Type t Constraints? constraint
   | uniReference: Id name &quot;-\&gt;&quot; Type typ
   | biReference: Id name &quot;-\&gt;&quot; Type typ &quot;inverse&quot; Id ref &quot;::&quot; Id attr
;

entity Employee  {
   hireDate : Date
   payRate : Currency
   boss -&gt; Manager inverse Manager::subordinates
}

entity Manager {
   subordinates -&gt; Set&lt;Employee&gt; inverse Employee::boss
}

I have implemented a good deal of the Typechecking with TypePal but here is my problem:
How do I enforce the the rule that for attribute boss in Employee entity there must be a corresponding attribute
subordinates in the Manager entity and vice versa.
Thanks

答案1

得分: 1

有趣的问题,你的问题可以相对容易地解决。秘密武器是 useViaType,它用于检查另一种类型中定义的类型,比如结构声明或者在你的情况下,实体声明。

实现这一点的基本 collect 规则如下:

void collect(current: (Declaration) `entity &lt;Id name&gt;  { &lt;Field+ fields&gt; }`, Collector c){
    c.define("&lt;name&gt;", entityId(), current, defType(entityType("&lt;name&gt;")));
    c.enterScope(current);
    collect(fields, c);
    c.leaveScope(current);
}

void collect(current: (Field) `&lt;Id name&gt; -&gt; &lt;Type typ&gt; inverse &lt;Id ref&gt; :: &lt;Id attr&gt;`, Collector c){
    c.define("&lt;name&gt;", fieldId(), current, defType(typ));
    c.use(ref, {entityId()});
    c.useViaType(ref, attr, {fieldId()});
    collect(typ, ref, attr, c);
}

第一个规则创建一个单独的作用域,用于包围当前实体声明中的字段声明。

第二个规则:

  • 使用了 ref
  • 通过 ref 的类型使用了 attr

为了方便起见,我已经将解决方案(稍微简化了一些)放在了 TypePal 存储库中的一个单独示例中,参见 https://github.com/usethesource/typepal/tree/master/src/examples/dataModel

给定(错误的)输入:

entity Employee  {
   boss -&gt; Manager inverse Manager::subordinates
}

entity Manager {
   subordinates -&gt; Set&lt;Employee&gt; inverse Employee::bos
}

现在你将收到以下错误消息:

error("No definition found for field `bos` in type `Employee`",
|project://typepal/src/examples/dataModel/example1.dm|(139,3,<6,51>,<6,54>))

bos 替换为 boss,错误将消失。

希望这能帮助你完成你的项目。

对问题的回应:关于类型的额外检查可能如下所示:

void collect(current: (Field) `&lt;Id name&gt; -&gt; &lt;Type typ&gt; inverse &lt;Id ref&gt; :: &lt;Id attr&gt;`, Collector c){
    c.define("&lt;name&gt;", fieldId(), current, defType(typ));
    c.use(ref, {entityId()});
    c.useViaType(ref, attr, {fieldId()});
    c.require("check inverse", current, [attr],
        void(Solver s){
            field_type = s.getType(typ);
            attr_type = s.getType(attr);
            ref_type = s.getType(ref);

            if(setType(elm_type) := field_type){
                s.requireEqual(elm_type, ref_type, error(attr, "Field type %t does not match reference type %t", typ, ref)); 
            } else {
                s.requireEqual(ref_type, field_type, error(attr, "Field type %t should be equal to reference type %t", field_type, ref_type));
            }
        });
    collect(typ, ref, attr, c);
}

你可能需要根据你的具体需求进行调整。我已经在 TypePal 存储库中更新了示例。

英文:

Interesting question, your problem can be solved rather easily. The secret weapon is useViaType that is used to check a type that is defined in another type such as, for instance, a structure declaration or as in your case an entity declaration.

The essential collect rules to achieve this are as follows:

void collect(current: (Declaration) `entity &lt;Id name&gt;  { &lt;Field+ fields&gt; }`, Collector c){
    c.define(&quot;&lt;name&gt;&quot;, entityId(), current, defType(entityType(&quot;&lt;name&gt;&quot;)));
    c.enterScope(current);
        collect(fields, c);
    c.leaveScope(current);
}

void collect(current: (Field) `&lt;Id name&gt; -\&gt; &lt;Type typ&gt; inverse &lt;Id ref&gt; :: &lt;Id attr&gt;`, Collector c){
    c.define(&quot;&lt;name&gt;&quot;, fieldId(), current, defType(typ));
    c.use(ref, {entityId()});
    c.useViaType(ref, attr, {fieldId()});
    collect(typ, ref, attr, c);
}

The first rule creates a separate scope to surround the field declarations in the current entity declaration.

The second rule:

  • uses ref
  • uses attr via the type of ref.

For your convenience I have placed the solution to (a slightly simplified version of) your problem as a separate example in the TypePal repository, see https://github.com/usethesource/typepal/tree/master/src/examples/dataModel

Given the (erroneous) input:

entity Employee  {
   boss -&gt; Manager inverse Manager::subordinates
}

entity Manager {
   subordinates -&gt; Set&lt;Employee&gt; inverse Employee::bos
}

Your will now get the following error message:

error(&quot;No definition found for field `bos` in type `Employee`&quot;,
|project://typepal/src/examples/dataModel/example1.dm|(139,3,&lt;6,51&gt;,&lt;6,54&gt;))

Replace bos by boss and the error will disappear.

I hope this will help you to complete your project.

Response to question: an extra check on the types could look like this:

void collect(current: (Field) `&lt;Id name&gt; -\&gt; &lt;Type typ&gt; inverse &lt;Id ref&gt; :: &lt;Id attr&gt;`, Collector c){
    c.define(&quot;&lt;name&gt;&quot;, fieldId(), current, defType(typ));
    c.use(ref, {entityId()});
    c.useViaType(ref, attr, {fieldId()});
    c.require(&quot;check inverse&quot;, current, [attr],
        void(Solver s){
            field_type = s.getType(typ);
            attr_type = s.getType(attr);
            ref_type = s.getType(ref);
            
            if(setType(elm_type) := field_type){
                s.requireEqual(elm_type, ref_type, error(attr, &quot;Field type %t does not match reference type %t&quot;, typ, ref)); 
            } else {
                s.requireEqual(ref_type, field_type, error(attr, &quot;Field type %t should be equal to reference type %t&quot;, field_type, ref_type));
            }
        });
    collect(typ, ref, attr, c);
}

You may want to adapt this to your specific needs. I have updated the example in the TypePal repo.

huangapple
  • 本文由 发表于 2023年6月19日 03:15:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76502168.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定