umlet 的标记语言规范是什么?

What is the markup language specification for umlet?

我一直在整个网站、Internet 上寻找,当然还有 SO,但似乎找不到 umlet 中使用的标记语言的描述或规范。

在示例序列图中例如:

title: sample
_alpha:A~id1_|_beta:B~id2_|_gamma:G~id3_
id1->>id2:id1,id2
id2-/>id1:async Msg.
id3->>>id1:id1,id3
id1.>id3:id1,id3:async return Msg
id1->id1:id1:self
iframe{:interaction frame
id2->id3:id1,id3:async Msg.
iframe}

->、--> 等很明显,但是冒号是做什么的?

为什么需要下划线等。好问的人想知道,因为这看起来像一个有用的素描工具。

找到 an annotated script(从 14.2 开始似乎无效):

//UML2 style titles are optional.
title:sequence diagram
//the participating objects are separated using the pipe symbol "|". Their names may be underlined using underscores.
_alpha:A_|_beta:B_|_gamma:G_
//The following line describes a (synchonuous) message originating from the first object sent to the second object, while both objects are active.
1->>2:1,2
//This message is asynchronous being sent from the second object to the first object. The messages is named.
2-/>1:async Msg.
//A message from the third object to the first object; both objects are active.
3->>>1:1,3
//A named asynchronous return message in UML2 style (using a stick arrow head).
1.>3:1,3:async return Msg
//This is a self call of the first object.
1->1:1:self
//Interaction frames may be used to show optional or conditional blocks within sequence diagrams.
iframe{:interaction frame
2->3:2,3:async Msg.
iframe}

RE: BNF,来源中有umlet-elements/src/main/javacc/SequenceAllInOneParser.jj

options {
  static              = false;
  IGNORE_CASE         = false;
  JAVA_UNICODE_ESCAPE = true;
  JDK_VERSION        = "1.6";
  // Not yet detected as a valid option by the Eclipse plugin, but works nonetheless. This is essential for GWT compatible generated code.
  JAVA_TEMPLATE_TYPE = "modern";  
}


PARSER_BEGIN(SequenceAllInOneParser)
package com.baselet.element.facet.specific.sequence_aio.gen;

import org.slf4j.Logger;import org.slf4j.LoggerFactory;

import com.baselet.control.enums.LineType;
import com.baselet.element.facet.specific.sequence_aio.Lifeline;
import com.baselet.element.facet.specific.sequence_aio.Message.ArrowType;
import com.baselet.element.facet.specific.sequence_aio.SequenceDiagramBuilder;
import com.baselet.element.facet.specific.sequence_aio.SequenceDiagramException;

public class SequenceAllInOneParser {
    private static final Logger log = LoggerFactory.getLogger(SequenceAllInOneParser.class);

    private boolean autoTick;

    /**
    * Replaces "\\" with "\" in the output so that any further specified match replace pair can't match the replaced "\".
    * @param matchReplacePairs
    * @return the string with the replacements
    */
    public static String backslashReplace(String input, String... matchReplacePairs) {
        if (matchReplacePairs.length % 2 == 1) {
            throw new IllegalArgumentException("matchReplacePairs must have an even number of elements.");
        }
        String split = "\\";
        StringBuilder strBuilder = new StringBuilder(input.length());
        int firstIndex = 0;
        // use the indexOf function instead of a split, because split uses regex and regex is not 100% supported by GWT
        int foundIndex = input.indexOf(split, firstIndex);
        while (firstIndex < input.length()) {
            int lastIndex = foundIndex == -1 ? input.length() : foundIndex;
            String tmp = input.substring(firstIndex, lastIndex);
            for (int j = 0; j < matchReplacePairs.length - 1; j += 2) {
                tmp = tmp.replace(matchReplacePairs[j], matchReplacePairs[j + 1]);
            }
            strBuilder.append(tmp);
            if (foundIndex != -1) {
                strBuilder.append('\');
            }
            firstIndex = lastIndex + split.length();
            foundIndex = input.indexOf(split, firstIndex);
        }
        return strBuilder.toString();
    }

    /**
     * Small data container to pass all informations.
     */
    private class MessageArrowInfo {
        boolean fromLeftToRight;
        LineType lineType;
        ArrowType arrowType;
    }

    private class InteractionConstraint {
        private String lifelineId = null;
        private String text = "";
    }

    private class LifelineInterval {
        private String startId;
        private String endId;
    }
}
PARSER_END(SequenceAllInOneParser)

/* defines input to be ignored */
< * > SKIP:
{
    " "
    | "\t"
}

< DIAGRAM_DEF_TEXT > SKIP:
{
    "\n" : DEFAULT
    | "\r\n" : DEFAULT
    | "\r" : DEFAULT
}

< DEFAULT > TOKEN:
{
    < DIAGRAM_OPTION_OVERRIDE_ID :"overrideIds=" >
    | < DIAGRAM_OPTION_AUTO_TICK: "autoTick=" >
    | < TRUE: "true" >
    | < FALSE: "false" >
    | < DIAGRAM_TITLE: "title=" > : DIAGRAM_DEF_TEXT
    | < DIAGRAM_DESC: "desc=" > : DIAGRAM_DEF_TEXT
    | < LIFELINE_DEFINITIONS: "obj=" > : LIFELINE_DEF
}

< DIAGRAM_DEF_TEXT > TOKEN:
{
    < DIAGRAM_DEFINITION_TEXT:
        (
            ~ ["\","\r","\n"]
            | ("\" ["\", "n"] )
        )+> : DIAGRAM_DEF_TEXT
}

< LIFELINE_DEF > TOKEN :
{
    < LL_DEF_NEW_LINE: "\n" | "\r\n" | "\r" > : DEFAULT
    | < LIFELINE_DEF_DELIMITER: "|" >
    /*| < LIFELINE_TITLE_DELIMITER: "~" > integrated in the title */
    | < LIFELINE_ACTOR: "ACTOR" >
    | < LIFELINE_ACTIVE: "ACTIVE" >
    | < LIFELINE_CREATED_LATER :"CREATED_LATER" >
    | < LIFELINE_EXEC_SPEC_FROM_START: "EXECUTION" >
    | < LIFELINE_TITLE: /* since | and ~ are special characters which delimit the title they need to be escaped */
        ( 
            ~["~","\","\n","\r","|"] 
            | ("\" ["\","~","|","n"])
        )+
        "~" >
}

< DEFAULT > TOKEN :
{
    < TEXT_DELIMITER: ":" > : DIAGRAM_SEQ_TEXT
    | < LIST_DELIMITER: "," >
    | < OPEN_CURLY_BRACKET: "{" >
    | < CLOSE_CURLY_BRACKET: "}" >
    | < COMMAND_DELIMITER: ";" >
    | < T_DASH: "-" >
    | < T_DOT: "." >
    | < T_DDOT: ".." >
    | < T_EQ: "=" >
    | < MESSAGE_ARROW_LEFT_OPEN: "<" >
    | < MESSAGE_ARROW_LEFT_FILLED: "<<<" >
    | < MESSAGE_ARROW_RIGHT_OPEN: ">" >
    | < MESSAGE_ARROW_RIGHT_FILLED: ">>>" >
    | < MESSAGE_DURATION_INC: "+" >
    | < LOST: "lost" >
    | < FOUND: "found" >
    | < GATE: "gate" >
    | < START_COREGION: "coregionStart=" >
    | < END_COREGION: "coregionEnd=" >
    | < INVARIANT: "invariant=" >
    | < STATE_INVARIANT: "stateInvariant=" >
    | < EXEC_SPEC_START: "on=" >
    | < EXEC_SPEC_END: "off=" >
    | < LL_DESTROY: "destroy=" >
    | < REF: "ref=" >
    | < CONTINUATION: "continuation=" >
    | < TEXT_ON_LIFELINE: "text=" >
    | < TICK: "tick=" >
    | < COMBINED_FRAGMENT: "combinedFragment=" > : CF_OPERATOR
    | < INTERACTION_CONSTRAINT: "constraint=" >
    | < UNSIGNED_INT_CONSTANT: ( ["0" - "9"] ) + >
    | < DEFAULT_NEW_LINE: "\n" | "\r\n" | "\r" >
}

< LIFELINE_DEF, DEFAULT > TOKEN :
{
    < LIFELINE_ID: ["a"-"z","A"-"Z"] (["a"-"z","A"-"Z","0"-"9"])* >
}

< DIAGRAM_SEQ_TEXT > TOKEN :
{
    < TEXT_UNTIL_NEXT_COMMAND : ( /* since ; is special characters which delimit the text it must be escaped */ 
            ~["\","\n","\r",";"] 
            | ("\" ["\",";","n"])
        )+ > : DEFAULT
}

< CF_OPERATOR > TOKEN :
{
    < COMBINED_FRAGMENT_OPERATOR: /* since ; and ~ are special characters which delimit the operator they need to be escaped */
        ( 
            ~["~","\","\n","\r",";"] 
            | ("\" ["\","~","|","n",";"])
        )+
        "~" > : DEFAULT
}

/* general Tokens */

/* skip comments */
< * > SKIP :
{
    <"//"(~["\n","\r"])*>
}

< * > TOKEN :
{
    <LAST_LINE_COMMENT: "//"(~["\n","\r"])*>
}


/**
 * The main function which parses the whole diagram.
 * Line comments are skipped (see Tokens)
 */
SequenceDiagramBuilder start() :
{
    String titleText = "";
    String descText = "";
    SequenceDiagramBuilder diagram  = new SequenceDiagramBuilder();
    autoTick = true;
}
{
    (
        (
            titleText=DiagramTitle() /* NL skip and change state to DEFAULT */
            | descText = DiagramDescription() /* NL skip and change state to DEFAULT */
            | Option(diagram) < DEFAULT_NEW_LINE >
            | LifelineDefinitions(diagram) /* NL part of the nonterminal */

        )+//diagram definition as new NT followed by NL alternating with sequence so that the sequence= can be removed
        Sequence(diagram)
    )

    (< LAST_LINE_COMMENT >)?
    <EOF>
    {
        diagram.setTitle(titleText);
        diagram.setText(descText);
        return diagram;
    }
}

String DiagramTitle() :
{
    String text = "";
}
{
    < DIAGRAM_TITLE >
    (
        < DIAGRAM_DEFINITION_TEXT > { text = backslashReplace(token.image, "\n","\n");}
    )?
    {return text;}
}

String DiagramDescription() :
{
    String desc = "";
}
{
    < DIAGRAM_DESC >
    < DIAGRAM_DEFINITION_TEXT > { desc = backslashReplace(token.image, "\n","\n");}
    {return desc;}
}

/**
 * Options for the whole diagram
 */
void Option(SequenceDiagramBuilder diagram) :
{
    boolean overrideIds;
}
{
    < DIAGRAM_OPTION_OVERRIDE_ID > overrideIds = booleanConstant()
    {
        diagram.setOverrideDefaultIds(overrideIds);
    }
    | < DIAGRAM_OPTION_AUTO_TICK > autoTick = booleanConstant()
}

/**
 * Defines all Lifelines
 */
void LifelineDefinitions(SequenceDiagramBuilder diagram) :
{}
{
    < LIFELINE_DEFINITIONS > LifelineDef(diagram) (< LIFELINE_DEF_DELIMITER > LifelineDef(diagram))* < LL_DEF_NEW_LINE >
}

/**
 * Defines one Lifeline, the id can't be LIFELINE_ACTOR, LIFELINE_ACTIVE
 * or LIFELINE_CREATED_LATER because these are keywords.
 */
void LifelineDef(SequenceDiagramBuilder diagram) :
{
    String name = "";
    String id = null;
    boolean createdOnStart = true;
    Lifeline.LifelineHeadType headType = Lifeline.LifelineHeadType.STANDARD;
    boolean execSpecFromStart = false;
}
{
    name = LifelineDefTitleText() (id=LifelineId())?
    (
        < LIFELINE_ACTOR > { headType = Lifeline.LifelineHeadType.ACTOR; }
        | < LIFELINE_ACTIVE > { headType = Lifeline.LifelineHeadType.ACTIVE_CLASS; }
        | < LIFELINE_CREATED_LATER > { createdOnStart = false; }
        | < LIFELINE_EXEC_SPEC_FROM_START > { execSpecFromStart = true; }
    ) *
    {
        if("lost".equals(id) || "found".equals(id)) {
            throw new SequenceDiagramException("'lost' and 'found' are keywords and can not be used as lifeline identifiers.");
        }
        diagram.addLiveline(name, id, headType, createdOnStart, execSpecFromStart);
    }
}

/** can could be multiple lines */
String LifelineDefTitleText() :
{}
{
    < LIFELINE_TITLE >
    {
        /* remove trailing ~ and handle the escaping of \, |, n and ~ */
        return backslashReplace(token.image.substring(0, token.image.length() - 1), "\n", "\n", "\~", "~", "\|", "|");
    }
}

/**
 * Can't be one of the following, because these are keywords in the lifeline definition!
 * < LIFELINE_ACTOR: "ACTOR" >
 * < LIFELINE_ACTIVE: "ACTIVE" >
 * < LIFELINE_CREATED_LATER :"CREATED_LATER" >
 */
String LifelineId() :
{}
{
    < LIFELINE_ID >
    {
        return token.image;
    }
}

void Sequence(SequenceDiagramBuilder diagram) :
{}
{
    (
        SequenceTick(diagram) < DEFAULT_NEW_LINE >
        | (
            SequenceElement(diagram)
            ( LOOKAHEAD(2) < COMMAND_DELIMITER > SequenceElement(diagram))*
            (< COMMAND_DELIMITER >)?
            < DEFAULT_NEW_LINE >
            { if(autoTick) { diagram.tick(); } }
        )
        | < DEFAULT_NEW_LINE >
    )*
}

void SequenceTick(SequenceDiagramBuilder diagram) :
{
    int tickCount = 1;
}
{
    < TICK > (tickCount = unsignedIntConstant())?
    { diagram.tick(tickCount); }
}

void SequenceElement(SequenceDiagramBuilder diagram) :
{}
{
    (
        MessageOrGeneralOrderingOrText(diagram) /* Message, GeneralOrdering have a common prefix for more clarity a new nonterminal was created*/
        | Coregion(diagram)
        | DestroyLL(diagram)
        | ExecutionSpecification(diagram)
        | StateInvariant(diagram)
        //| (diagram) //general ordering, TimeConstraint TimeObservation and DurationConstraint Duration Observation missing
        | InteractionUse(diagram)
        | Continuation(diagram)
        | CombinedFragment(diagram)
    )
}

void MessageOrGeneralOrderingOrText(SequenceDiagramBuilder diagram) :
{}
{
    /*
     * (Message and GeneralOrdering) A common prefix is: <LIFELINE_ID> "." <LIFELINE_ID> "<"
     * All three have <LIFELINE_ID > as common prefix
     * therefore use LOOKAHEAD(2) if it is a text, if not use a LOOKAHEAD(5) to determine if it is a message or a general ordering
     */
    LOOKAHEAD(2) TextOnLifeline(diagram)
    | LOOKAHEAD(5) Message(diagram)
    | GeneralOrdering(diagram)
}

void CombinedFragment(SequenceDiagramBuilder diagram) :
{
    LifelineInterval interval = new LifelineInterval();
    String operator = "";
    String id = null;
}
{
    (
        < COMBINED_FRAGMENT >
        (
            < COMBINED_FRAGMENT_OPERATOR >
            { operator = backslashReplace(token.image.substring(0, token.image.length() - 1), "\n", "\n", "\;", ";", "\~", "~"); }
        )
        [
            id = LifelineId() 
            [interval = LifelineInterval()]
        ]
        { diagram.beginCombinedFragment(interval.startId, interval.endId, id, operator); }
    )
    | (
        < T_DASH > < T_DASH > (< T_EQ > id=LifelineId())?
        { diagram.endCombinedFragment(id); }
    )
    | (
        < T_DDOT > (< T_EQ > id=LifelineId())?
        { diagram.endAndBeginOperand(id); }
    )
}

void Message(SequenceDiagramBuilder diagram) :
{
    String leftLifelineId = null;
    String rightLifelineId = null;
    String leftLifelineLocalId = null;
    String rightLifelineLocalId = null;
    MessageArrowInfo messageArrowInfo;
    String msgText = "";
    int lostCount = 0;
    int foundCount = 0;
    int gateCount = 0;
    int duration = 0;
}
{
    (
        (
            leftLifelineId = LifelineId() [LOOKAHEAD(2)< T_DOT > leftLifelineLocalId = LifelineId()]
            | < LOST > { leftLifelineId = "lost"; lostCount++; }
            | < FOUND > { leftLifelineId = "found"; foundCount++; }
            | < GATE > { leftLifelineId = "gate"; gateCount++; }
        )
        messageArrowInfo =  MessageArrow()
        (
            rightLifelineId = LifelineId() [< T_DOT > rightLifelineLocalId = LifelineId()]
            | < LOST > { rightLifelineId = "lost"; lostCount++; }
            | < FOUND > { rightLifelineId = "found"; foundCount++; }
            | < GATE > { rightLifelineId = "gate"; gateCount++; }
        )
        (duration = MessageDuration())?
        (msgText = TextUntilNewLine())?
    )
    {
        if(lostCount + foundCount + gateCount > 1) {
            throw new SequenceDiagramException("Error: 'lost', 'found' and 'gate' can only occur once per message.");
        }
        String send;
        String receive;
        String sendLocalId;
        String receiveLocalId;
        if(messageArrowInfo.fromLeftToRight) {
            send = leftLifelineId;
            receive = rightLifelineId;
            sendLocalId = leftLifelineLocalId;
            receiveLocalId = rightLifelineLocalId;
        }
        else {
            send = rightLifelineId;
            receive = leftLifelineId;
            sendLocalId = rightLifelineLocalId;
            receiveLocalId = leftLifelineLocalId;
        }
        if(gateCount > 0) {
            if(duration != 0) {
                throw new SequenceDiagramException("Error: a messages with a gate can only have a duration of 0, but the duration was " + duration + ".");
            }
            if(send.equals("gate")) {
                diagram.addSendGateMessage(receive, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, receiveLocalId);
            }
            else {
                diagram.addReceiveGateMessage(send, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, sendLocalId);
            }
        }
        else if(send.equals("lost")) {
            throw new SequenceDiagramException("Error: 'lost' can only be on the receiving end of a message.");
        }
        else if(send.equals("found")) {
            if(duration != 0) {
                throw new SequenceDiagramException("Error: 'lost' and 'found' messages can only have a duration of 0, but the duration was " + duration + ".");
            }
            diagram.addFoundMessage(receive, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, receiveLocalId);
        }
        else
        {
            if(receive.equals("lost")) {
                if(duration != 0) {
                    throw new SequenceDiagramException("Error: 'lost' and 'found' messages can only have a duration of 0, but the duration was " + duration + ".");
                }
                diagram.addLostMessage(send, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, sendLocalId);
            }
            else if(receive.equals("found")) {
                throw new SequenceDiagramException("Error: 'found' can only be on the sending end of a message.");
            }
            else {
                diagram.addMessage(send, receive, duration, msgText, messageArrowInfo.lineType, messageArrowInfo.arrowType, sendLocalId, receiveLocalId);
            }
        }
    }
}

MessageArrowInfo MessageArrow():
{
    MessageArrowInfo messageArrowInfo = new MessageArrowInfo();
}
{
        (
            (
                messageArrowInfo.lineType = MessageArrowLineType()
                (
                    < MESSAGE_ARROW_RIGHT_OPEN > { messageArrowInfo.arrowType = ArrowType.OPEN; }
                    | < MESSAGE_ARROW_RIGHT_FILLED >  { messageArrowInfo.arrowType = ArrowType.FILLED; }
                )
            )
            {
                messageArrowInfo.fromLeftToRight = true;
            }
            | (
                (
                    < MESSAGE_ARROW_LEFT_OPEN > { messageArrowInfo.arrowType = ArrowType.OPEN; }
                    | < MESSAGE_ARROW_LEFT_FILLED >  { messageArrowInfo.arrowType = ArrowType.FILLED; }
                )
                messageArrowInfo.lineType = MessageArrowLineType()
            )
            {
                messageArrowInfo.fromLeftToRight = false;
            }
        )
        { return messageArrowInfo; }
}

LineType MessageArrowLineType() :
{
    LineType lineType;
}
{
    (
        < T_DASH > { lineType = LineType.SOLID; }
        | < T_DOT >  { lineType = LineType.DASHED; }
    )
    {
        return lineType;
    }
}

int MessageDuration() :
{
    int duration;
}
{
    (
        (
            < MESSAGE_DURATION_INC > { duration = 1; }
            (
                duration = unsignedIntConstant() 
                | (< MESSAGE_DURATION_INC >  { duration++; })*
            )
        )
        | (
            < T_DASH > { duration = -1; }
            (
                duration = unsignedIntConstant() { duration = -duration; }
                | (< T_DASH >  { duration--; })*
            )
        )
    )
    {
        return duration;
    }
}

void GeneralOrdering(SequenceDiagramBuilder diagram):
{
    String leftLifelineId = null;
    String rightLifelineId = null;
    String leftLifelineLocalId = null;
    String rightLifelineLocalId = null;
    boolean leftEarlier;
}
{
    leftLifelineId = LifelineId()
    < T_DOT >
    leftLifelineLocalId = LifelineId()
    (
        < MESSAGE_ARROW_RIGHT_OPEN > { leftEarlier = true; }
        | < MESSAGE_ARROW_LEFT_OPEN > { leftEarlier = false; }
    )
    rightLifelineId = LifelineId()
    < T_DOT >
    rightLifelineLocalId = LifelineId()
    {
        if(leftEarlier) {
            diagram.addGeneralOrdering(leftLifelineId, leftLifelineLocalId, rightLifelineId, rightLifelineLocalId);
        }
        else {
            diagram.addGeneralOrdering(rightLifelineId, rightLifelineLocalId, leftLifelineId, leftLifelineLocalId);
        }
    }
}

void Coregion(SequenceDiagramBuilder diagram) :
{
    String lifelineId;
    boolean start;
}
{
    (
        < START_COREGION > { start = true; }
        | < END_COREGION > { start = false; }
    )
    lifelineId = LifelineId()
    {
        diagram.addCoregion(lifelineId, start);
    }
}

void DestroyLL(SequenceDiagramBuilder diagram) :
{
    String lifelineId;
}
{
    < LL_DESTROY > 
    lifelineId = LifelineId()
    {
        diagram.destroyLifeline(lifelineId);
    }
}

void ExecutionSpecification(SequenceDiagramBuilder diagram) :
{
    String lifelineId;
    boolean on;
}
{
    (
        < EXEC_SPEC_START > { on = true; }
        | < EXEC_SPEC_END > { on = false; }
    )
    lifelineId = LifelineId()
    {
        diagram.changeExecutionSpecification(lifelineId, on);
    }
    ( (< LIST_DELIMITER >)? lifelineId = LifelineId()
        {
            diagram.changeExecutionSpecification(lifelineId, on);
        }
    )*
}

void StateInvariant(SequenceDiagramBuilder diagram) :
{
    String lifelineId;
    String text = "";
    boolean stateStyle;
}
{
    (
        < INVARIANT > { stateStyle = false; }
        | < STATE_INVARIANT > { stateStyle = true; }
    ) 
    lifelineId = LifelineId()
    (text = TextUntilNewLine())?
    {
        diagram.addStateInvariant(lifelineId, text, stateStyle);
    }
}

void TextOnLifeline(SequenceDiagramBuilder diagram) :
{
    String lifelineId;
    String text = "";
}
{
    [< TEXT_ON_LIFELINE >]
    lifelineId = LifelineId()
    text = TextUntilNewLine() /* text is mandatory, if not lookahead with message and general ordering would not work*/ 
    {
        diagram.addTextOnLifeline(lifelineId, text);
    }
}

String TextUntilNewLine() :
{
}
{
    < TEXT_DELIMITER >
    < TEXT_UNTIL_NEXT_COMMAND >
    {
        return backslashReplace(token.image, "\n", "\n", "\;", ";");
    }
}

void InteractionUse(SequenceDiagramBuilder diagram):
{
    LifelineInterval interval;
    String text = "";
}
{
    (
        < REF >
        interval = LifelineInterval()
        (text = TextUntilNewLine())?
    )
    {
        diagram.addInteractionUse(interval.startId, interval.endId, text);
    }
}

void Continuation(SequenceDiagramBuilder diagram):
{
    LifelineInterval interval;
    String text = "";
}
{
    (
        < CONTINUATION >
        interval = LifelineInterval()
        (text = TextUntilNewLine())?
    )
    {
        diagram.addContinuation(interval.startId, interval.endId, text);
    }
}

/**
 * @return the start and end of the interval.
 */
LifelineInterval LifelineInterval():
{
    LifelineInterval interval = new LifelineInterval();
}
{
    interval.startId = LifelineId()
    (< LIST_DELIMITER >)?
    interval.endId = LifelineId()
    {
        return interval;
    }
}

boolean booleanConstant() :
{ boolean value;}
{
    (
        <FALSE>  { value = false;}
        | <TRUE> { value = true;}
    ) {return value; }
}

int unsignedIntConstant() :
{}
{
    <UNSIGNED_INT_CONSTANT> {
        int value;
        try {
            value = Integer.parseInt(token.image);
        } catch(NumberFormatException e) {
            // only digits are accepted by the gramer, so the only reason for a NumberFormatException should be that the number is too big for int
            throw (ParseException) new ParseException("Error: The string '" + token.image + "' couldn't be parsed as integer. The most probable reason is that the number is too big.").initCause(e);
        }
        return value;
    }
}