2

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

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
}

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 Answers1

1

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 <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);
}

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 -> Manager inverse Manager::subordinates
}

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

Your will now get the following error message:

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

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) `<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);
}

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

Paul Klint
  • 1,418
  • 7
  • 12
  • Thanks this is very helpful I have gone through all the examples but couldn't find a solution so its great you even added it to the TypePal examples. I still have one more question though how do I enforce the rule that boss should be of type Employee and subordinates should be of type Manager – Benni Katchy Jun 19 '23 at 23:26
  • See the above extension of the answer, and please upvote it ;-) – Paul Klint Jun 20 '23 at 11:28
  • Thanks that almost did it. I now have a way to check that the attribute exist in the inverse side but I still can't confirm it's type is the correct type. Is there a way to get the type of enclosing Entity so I can enforce the constraint that not only should the attribute exist in the inverse side but that it most have the correct type. – Benni Katchy Jun 20 '23 at 17:24