我正在使用 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 实体中的下属,反之亦然。 谢谢
有趣的问题,你的问题很容易解决。秘密武器是
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 存储库中的示例。