Search code examples
xtextplantuml

DSL with XText for PlantUML


Currently I'm trying to create a DSL for the class diagrams of PlantUML. I'm new to Xtext and I can't get my head around several things. Before I list my problems I show you some parts of my current grammar:

ClassUml:
    {ClassUml}
    '@startuml' umlElements+=(ClassElement)* '@enduml';

ClassElement:
    Class
    | Association;

Class:
    {Class}
    'class' name=ClassName
    (color=ColorTag)?
    ('{' (classContents+=ClassContent)* '}')?;

ClassContent:
    Attribute | Method;

ClassName:
    (ID | STRING);

Attribute:
    {Attribute}
    (visibility=Visibility)? name=ID (":" type=ID)?;

Method:
    {Method}
     (visibility=Visibility)? name=METHID
     (":" type=ID)?;

Association:
    {Association}
    (classFrom=[Class]
    associationType=Bidirectional
    classTo=[Class])
    |
    (classTo=[Class]
    associationType=UnidirectionalLeft
    classFrom=[Class])
    |
    (classFrom=[Class]
    associationType=UnidirectionalRight
    classTo=[Class])
    (':' text+=(ID)*)?;

Bidirectional:
    {Bidrectional}
    ('-' ("[" color=ColorTag "]")? '-'?)
    | ('.' ("[" color=ColorTag "]")? '.'?);

UnidirectionalLeft:
    {UnidirectionalLeft}
    ('<-' ("[" color=ColorTag "]")? '-'?)
    | ('<.' ("[" color=ColorTag "]")? '.'?);

UnidirectionalRight:
    {UnidirectionalRight}
    ((('-[' color=ColorTag "]")|'-')? '->')
    | ((('.[' color=ColorTag "]")|'.')? '.>');

ColorTag:
    (COLOR | HEXCODE);

enum Visibility: 
    PROTECTED='#'
    | PRIVATE='-'
    | DEFAULT='~'
    | PUBLIC='+';

terminal COLOR:
    "#"
    ('red') | ('orange');

terminal HEXCODE:
    "#"
    ('A' .. 'F'|'0' .. '9')('A' .. 'F'|'0' .. '9')('A' .. 'F'|'0' .. '9')
    ('A' .. 'F'|'0' .. '9')('A' .. 'F'|'0' .. '9')('A' .. 'F'|'0' .. '9');

terminal STRING:
    '"' ('\\' . | !('\\' | '"'))* '"';

terminal ID:
    ('a'..'z' | 'A'..'Z' | '_' | '0'..'9' | '\"\"' | '//' | '\\')
    ('a'..'z' | 'A'..'Z' | '_' | '0'..'9' | '\"\"' | '//' | '\\' | ':')*;

I left out the other association types (--*, --o, --|>) because I've defined them in the same way.

Problems
1. The visibility enum '#' isn't working without a separation from the method / attribute name. But all the other cases (+,-,~) are fine, with and without a blank space between.
2. The associations don't seem to work in most cases. I've listed a few examples:

' Working '
Alice -* Bob : Hello
Alice - Bob
Alice .o Bob
Alice <|-[#002211]- Bob
Alice *-[#red]- Bob
Alice -[#000000]-> Bob
Alice .[#red].> Bob

' Not Working '
Alice *-- Bob
Alice --* Bob
Alice .. Bob
Alice -[#ff0022]- Bob
Alice <-- Bob
Alice ..> Bob
Alice -- Bob
  1. I don't know how I can use cross references for classes which were defined by STRING and not ID.

Also I'm guessing the additional terminal for the method name is a weird solution and should be handled differently.


Solution

  • 1) Color should be a parser rule not a terminal rule. Also remove the Hex rule and simply use your changed ID rule.

    Color:
       "#" ('red' | 'orange' | ID);
    

    2) Make sure you to unify the differences, for instance there is a conflict between

    Bidirectional:
      ...
      ('-' ("[" ...;
    

    and

    UnidirectionalRight:
    ((('-[' ...;
    

    a sequence '-[' will always match the latter version. You should create one rule AssociationType and make that work for all cases. Something like this:

    Association:
       {Association}
       (classFrom=[Class | ClassName]
       associationType=AssociationType
       classTo=[Class | ClassName])
       (':' text+=(ID)*)?;
    
    AssociationType:
       {AssociationType} 
       left?='<'? ('-'|'.') ("[" color=Color "]")? ('-'|'.') right?='>'?;
    

    3) You could allow a STRING in the cross references, as well, by using the following syntax for the crossrefs: classFrom=[Class|ClassName]