使用 TypePal 编写语义规则

问题描述 投票:0回答:1

我正在使用 Rascal MPL 设计用于数据建模的 DSL,这里是我的语法规范的片段:

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

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

entity Employee  {
   hireDate : Date
   payRate : Currency
   boss -> Manager inverse Manager::subordinates
}

entity Manager {
   subordinates -> Set<Employee> inverse Employee::boss
}

我已经使用 TypePal 实现了大量的类型检查,但这是我的问题: 如何强制执行以下规则:对于 Employee 实体中的 attribute boss 必须有相应的 attribute Manager 实体中的下属,反之亦然。 谢谢

typechecking rascal
1个回答
1
投票

有趣的问题,你的问题很容易解决。秘密武器是

useViaType
,用于检查在另一种类型中定义的类型,例如结构声明或在您的情况下是实体声明。

实现这一目标的基本

collect
规则如下:

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

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

第一条规则创建一个单独的范围来包围当前实体声明中的字段声明。

第二条规则:

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

为了您的方便,我已将您的问题的解决方案(稍微简化的版本)作为单独的示例放置在 TypePal 存储库中,请参阅 https://github.com/usethesource/typepal/tree/master/src/examples/数据模型

鉴于(错误的)输入:

entity Employee  {
   boss -> Manager inverse Manager::subordinates
}

entity Manager {
   subordinates -> Set<Employee> 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) `<Id name> -\> <Type typ> inverse <Id ref> :: <Id attr>`, Collector c){
    c.define("<name>", 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 存储库中的示例。

© www.soinside.com 2019 - 2024. All rights reserved.