Hi, I'm trying to find a way to create "usable" MGraph file when using the mgx.exe MGrammar executor. Let me sketch my situation. I have the following types declared in MSchema (Note: all samples are hand-edited from the original to simplify the situtation. I hope I didn't mess thing up)
| moduleTraphicControl.Models |
| { |
| typeLamp |
| { |
| Id:Integer32=AutoNumber(); |
| Name:Text; |
| Color:Text; |
| TraphicLight:TraphicLight?; |
| }whereidentityId; |
|
| Lamps:Lamp*whereitem.TraphicLightinTraphicLights; |
| |
| typeTraphicLight |
| { |
| Id:Integer32=AutoNumber(); |
| Name:Text; |
| }whereidentityId; |
| |
| TraphicLights:TraphicLight*; |
| } |
|
|
I compiled this into a TraphicControlModel.mx image and inserted it into the Repository. To create a model instance I used the following:
| moduleTraphicControl.Models |
| { |
| TraphicLights{ |
| TF1{ |
| Name="light1", |
| .Lamps |
| { |
| { |
| Color="Red", |
| Name="r1", |
| TraphicLight=TraphicLights.TF1 |
| }, |
| { |
| Color="Orange", |
| Name="o1", |
| TraphicLight=TF1 |
| } |
| } |
| } |
| } |
| } |
|
This works to insert the MGraph into the existing model defined in the MSchema snippet. I compiled it into an TraphicControlInstance.mx image and ran mx.exe on it with a reference to the previousTraphicControlModel.mx file. Next, I created a DSL using MGrammar and want to produce output in an M file that represents approximately the same model instance. My first attempt was this:
| moduleTraphicControlLibrary |
| { |
| languageTraphicControlLanguage |
| { |
| interleaveWhitespace='\r'|''|'\n'; |
| syntaxMain=p:TraphicLight+=>TraphicLights{valuesof(p)}; |
| |
| //Useliteralsinsideofrule |
| syntaxTraphicLight="light"i:Identifierl:light+"end-light"=> |
| id(i){ |
| Name{i}, |
| id(".Lamps"){valuesof(l)}}; |
| |
| //syntaxIdent=n:Identifier=>id("bla"){n}; |
| |
| //Orusenamedtokens |
| syntaxlight=Lightname:IdentifierColonc:Color |
| =>id(name){Color=c,Name=name}; |
| |
| tokenColon=":"; |
| tokenLight="lamp"; |
| tokenColor="Red"|"Green"|"Orange"; |
| tokenDigit="0".."9"; |
| tokenNumber=Digit#1..2; |
| |
| tokenIdentifier=IdentifierStartIdentifierCharacter+; |
| tokenIdentifierCharacter=Letter|Digit; |
| tokenLetter='a'..'z'|'A'..'Z'; |
| tokenIdentifierStart='_'|Letter; |
| } |
| } |
That together with a DSL example of:
| lightlight1 |
| lampr1:Red |
| lampo1:Orange |
| lampg1:Green |
| end-light |
gives the following output in the MGrammar Preview Mode window:
| TraphicLights{ |
| light1{ |
| Name{ |
| "light1" |
| }, |
| .Lamps{ |
| r1{ |
| Color{ |
| "Red" |
| }, |
| Name{ |
| "r1" |
| } |
| }, |
| o1{ |
| Color{ |
| "Orange" |
| }, |
| Name{ |
| "o1" |
| } |
| }, |
| g1{ |
| Color{ |
| "Green" |
| }, |
| Name{ |
| "g1" |
| } |
| } |
| } |
| } |
| } |
Seems like it is nearly there, but I am having trouble getting the "light1" label for the TraphicLight instance referenced in the TraphicLight association of each Lamp, something like
| ... |
| r1{ |
| Color{ |
| "Red" |
| }, |
| Name{ |
| "r1" |
| }, |
| TraphicLight=TraphicLights.light1 |
| }, |
| ... |
The label of the TraphicLight instance does not matter to me (or MGrammar I assume). When I compile the grammar into an mgx image and run mgx.exe on the sample file it creates an MGraph file alright, but not one that can be compiled and inserted into the Repository just yet.
So, my questions are these:
- Am I on the right track here?How does one create a MGraph.m file that I can compile (m.exe with a reference to the mx of the compiled MSchema) and insert into the Repository (mx.exe) without having to change anything after running the DSL sample through my mgx file of the grammar?
- How to solve the referencing of previous defined entities?
- Are there other ways to work with previously defined entities? I believe that you can also take the .Lamps of the MGraph instance of the TraphicLight instance and put them at the "root" level of the MGraph tree. But does this fit well with the MGrammar parsing?
- When does one need to resort to DynamicParser et al. to do the work?
Any input is appreciated. Thanks. Alex
(Edited a little to clarify some more) - Edited byAlex ThissenMVPMonday, February 16, 2009 7:10 PMClarification
-
| | Alex Thissen |
Alex,
Here's a sample grammar that should work with your text file and produce MGraph that conforms to the modified schema I posted above:
| moduleTraphicControlLibrary |
| { |
| languageTraphicControlLanguage |
| { |
| interleaveWhitespace='\r'|''|'\n'; |
| syntaxMain=p:TraphicLight+=>TraphicLights{valuesof(p)}; |
| |
| //Useliteralsinsideofrule |
| syntaxTraphicLight="light"i:Identifierl:light+"end-light"=> |
| id(i){ |
| Name{i}, |
| Lamps{{valuesof(l)}}}; |
| |
| //syntaxIdent=n:Identifier=>id("bla"){n}; |
| |
| //Orusenamedtokens |
| syntaxlight=Lightname:IdentifierColonc:Color |
| =>id(name){Color=c,Name=name}; |
| |
| tokenColon=":"; |
| tokenLight="lamp"; |
| tokenColor="Red"|"Green"|"Orange"; |
| tokenDigit="0".."9"; |
| tokenNumber=Digit#1..2; |
| |
| tokenIdentifier=IdentifierStartIdentifierCharacter+; |
| tokenIdentifierCharacter=Letter|Digit; |
| tokenLetter='a'..'z'|'A'..'Z'; |
| tokenIdentifierStart='_'|Letter; |
| } |
| } |
|
To answer your questions point-by-point:
Q: Am I on the right track here? How does one create a MGraph .m file that I can compile (m.exe with a reference to the mx of the compiled MSchema) and insert into the Repository (mx.exe) without having to change anything after running the DSL sample through my mgx file of the grammar? A:At this point, you need to have the parent contain the child collection in the schema (rather than having the child reference the parent in the schema). Q: How to solve the referencing of previous defined entities? A:As far as I know, the parent/child approach I've shown is the only way to reference one entity from another via MGrammar. This reference is an implicit reference by containment (the parent contains the child and thereby is linked to/references the child). Creating lables in MGrammar is supported but using lables to reference another entity is currently not supported in MGrammar. Q: Are there other ways to work with previously defined entities? A: I believe that you can also take the .Lamps of the MGraph instance of the TraphicLight instance and put them at the "root" level of the MGraph tree. But does this fit well with the MGrammar parsing? As far as I know, this "parent contains children" approach is the only way to reference any other entities in MGrammar. Q: When does one need to resort to DynamicParser et al. to do the work? A:If you don't want to modify your MGrammar or your MSchema to make them play well together, you'd need to use DynamicParser or some other technique. One other technique would be to create two different schemas: an "import" schema that plays well with the grammar and can accept its output directly, and another "real" schema that is structured the way you prefer. You'd need some kind of tool to move from the one schema to the other, and that's probably where DynamicParser could come in. I've even tried using SQL scripts to move data from an import schema to a real schema; that works, but it is a bit tedious.
I realize that this process is a bit cumbersome right now, and it is not well-documented. I'll file a bug internally to get better documentation on the MGrammar/MSchema integration, and we are working hard to make the two integrate better in the future.
David - Proposed As Answer bydmatsonMSFTTuesday, February 17, 2009 9:48 PM
- Marked As Answer byAlex ThissenMVPWednesday, February 18, 2009 9:32 AM
-
| | dmatson | Yes, you are correct about the options at the moment. I would tend to take the latter approach as well.
I don't really know how the integration will look at this point; it's still in development. If I get more information, I'll post it here.
David - Marked As Answer byKraig BrockschmidtMSFT, ModeratorWednesday, February 25, 2009 6:11 PM
-
| | dmatson | I got the following answer from Don Box: “Our goal is to remove the distinctions between the function bodies in today’s MSchema and the right-hand-sides of grammar rules in today’s MGrammar. This includes a consistent set of operators and functions for use in both contexts as well as the ability to type-ascribe (a la today’s MSchema) the results. The corresponding design and implementation work is ongoing.�/span>
David - Marked As Answer byKraig BrockschmidtMSFT, ModeratorWednesday, February 25, 2009 6:11 PM
-
| | dmatson | Alex, This is a great question, and I've actually run into this scenario myself when trying to use MGrammar. Short answer: MGrammar and MSchema are not yet fully integrated, and so using the "best" schema won't necessarily work together with the "best" grammar. At least in some scenarios, you have to modify things a bit on one side or the other to make the two play nicely together. We are working hard to make this experience better, but for now there are a couple of extra hoops to jump through. Yourtraffic light/lamp sample is a great example of one of these issues: parent/child relationships. The way your schema is defined is great if you're just using MSchema. Here's the generic idea of the pattern you're currently using:
| typeParent |
| { |
| Id:Integer32=AutoNumber(); |
| Name:Text; |
| }whereidentity(Id); |
|
| Parents:Parent*; |
|
| typeChild |
| { |
| Id:Integer32=AutoNumber(); |
| Parent:Parent; |
| Name:Text; |
| }whereidentity(Id) && Parent in Parents; |
|
| Children:Child*; | However, this pattern unfortunately does not work well with MGrammar. The label approach is actually something I tried myself, but unfortunately that doesn't work at the moment. (Currently there's no way to create unique lables in MGrammer and then reference these lables in other nodes.) So, if you have a parent/child relationship in MSchema and you want to populate both sides from MGrammar output, you'll need to use a different pattern:
| typeParent |
| { |
| Id:Integer32=AutoNumber(); |
| Name:Text; |
| MyChildren:Child*; |
| }whereidentity(Id)&&MyChildren<=Children; |
|
| Parents:Parent*; |
|
| typeChild |
| { |
| Id:Integer32=AutoNumber(); |
| Name:Text; |
| }whereidentity(Id); |
|
| Children:Child*; | In you're specific example, here's the modified schema you would need:
| moduleTraphicControl.Models |
| { |
| typeLamp |
| { |
| Id:Integer32=AutoNumber(); |
| Name:Text; |
| Color:Text; |
| }whereidentityId; |
| |
| Lamps:Lamp*; |
| |
| typeTraphicLight |
| { |
| Id:Integer32=AutoNumber(); |
| Name:Text; |
| Lamps:Lamp*; |
| }whereidentityId; |
| |
| TraphicLights:TraphicLight*whereitem.Lamps<=Lamps; |
| } |
| Note that this approach does create an many-to-many table in SQL to join parent to child (or TrafficLight to Lamp). Stay tuned; I'll work on getting a modified MGrammar to populate this schema shortly. David | | dmatson |
Alex,
Here's a sample grammar that should work with your text file and produce MGraph that conforms to the modified schema I posted above:
| moduleTraphicControlLibrary |
| { |
| languageTraphicControlLanguage |
| { |
| interleaveWhitespace='\r'|''|'\n'; |
| syntaxMain=p:TraphicLight+=>TraphicLights{valuesof(p)}; |
| |
| //Useliteralsinsideofrule |
| syntaxTraphicLight="light"i:Identifierl:light+"end-light"=> |
| id(i){ |
| Name{i}, |
| Lamps{{valuesof(l)}}}; |
| |
| //syntaxIdent=n:Identifier=>id("bla"){n}; |
| |
| //Orusenamedtokens |
| syntaxlight=Lightname:IdentifierColonc:Color |
| =>id(name){Color=c,Name=name}; |
| |
| tokenColon=":"; |
| tokenLight="lamp"; |
| tokenColor="Red"|"Green"|"Orange"; |
| tokenDigit="0".."9"; |
| tokenNumber=Digit#1..2; |
| |
| tokenIdentifier=IdentifierStartIdentifierCharacter+; |
| tokenIdentifierCharacter=Letter|Digit; |
| tokenLetter='a'..'z'|'A'..'Z'; |
| tokenIdentifierStart='_'|Letter; |
| } |
| } |
|
To answer your questions point-by-point:
Q: Am I on the right track here? How does one create a MGraph .m file that I can compile (m.exe with a reference to the mx of the compiled MSchema) and insert into the Repository (mx.exe) without having to change anything after running the DSL sample through my mgx file of the grammar? A:At this point, you need to have the parent contain the child collection in the schema (rather than having the child reference the parent in the schema). Q: How to solve the referencing of previous defined entities? A:As far as I know, the parent/child approach I've shown is the only way to reference one entity from another via MGrammar. This reference is an implicit reference by containment (the parent contains the child and thereby is linked to/references the child). Creating lables in MGrammar is supported but using lables to reference another entity is currently not supported in MGrammar. Q: Are there other ways to work with previously defined entities? A: I believe that you can also take the .Lamps of the MGraph instance of the TraphicLight instance and put them at the "root" level of the MGraph tree. But does this fit well with the MGrammar parsing? As far as I know, this "parent contains children" approach is the only way to reference any other entities in MGrammar. Q: When does one need to resort to DynamicParser et al. to do the work? A:If you don't want to modify your MGrammar or your MSchema to make them play well together, you'd need to use DynamicParser or some other technique. One other technique would be to create two different schemas: an "import" schema that plays well with the grammar and can accept its output directly, and another "real" schema that is structured the way you prefer. You'd need some kind of tool to move from the one schema to the other, and that's probably where DynamicParser could come in. I've even tried using SQL scripts to move data from an import schema to a real schema; that works, but it is a bit tedious.
I realize that this process is a bit cumbersome right now, and it is not well-documented. I'll file a bug internally to get better documentation on the MGrammar/MSchema integration, and we are working hard to make the two integrate better in the future.
David - Proposed As Answer bydmatsonMSFTTuesday, February 17, 2009 9:48 PM
- Marked As Answer byAlex ThissenMVPWednesday, February 18, 2009 9:32 AM
-
| | dmatson | Thanks David, for the very elaborate explanation and filing it as a bug.
So, if I get this correctly, atm it is chosing between making consessions to your model/schema or more work (DynamicParser or the import schema workaround). I think I would rather go for the latter, as changing the model probably impacts a lot more. The work from those changes is presumably more than doing the extra work now (and discard it once the MSchema/MGrammar integration is better).
Any concrete ideas on how the integration will manifest itself? Will it include "type-safety" for the output of the MGrammar transformation? | | Alex Thissen | Yes, you are correct about the options at the moment. I would tend to take the latter approach as well.
I don't really know how the integration will look at this point; it's still in development. If I get more information, I'll post it here.
David - Marked As Answer byKraig BrockschmidtMSFT, ModeratorWednesday, February 25, 2009 6:11 PM
-
| | dmatson | I got the following answer from Don Box: “Our goal is to remove the distinctions between the function bodies in today’s MSchema and the right-hand-sides of grammar rules in today’s MGrammar. This includes a consistent set of operators and functions for use in both contexts as well as the ability to type-ascribe (a la today’s MSchema) the results. The corresponding design and implementation work is ongoing.�/span>
David - Marked As Answer byKraig BrockschmidtMSFT, ModeratorWednesday, February 25, 2009 6:11 PM
-
| | dmatson | Yes, type-ascription and/or conversion functions would be very useful for this. I am very curious to see how this will manifest itself in the language(s). | | Alex Thissen |
|