antlrgrammardslxtextmodel-driven-development

Xtext/ANTLR: How to fix this error? The following token definition can never be matched prior...?


I have made a grammar and the editor does not show any error, when I select 'Generate XText Artifacts', I get the following error:

error(208): ../mestra.dmxlightshow/src-gen/mestra/parser/antlr/internal/InternalDmxLightShow.g:3668:1: The following token definitions can never be matched because prior tokens match the same input: RULE_MIDI_CHANNEL error(208): ../mestra.dmxlightshow.ide/src-gen/mestra/ide/contentassist/antlr/internal/InternalDmxLightShow.g:10741:1: The following token definitions can never be matched because prior tokens match the same input: RULE_MIDI_CHANNEL

MIDI_CHANNEL / MidiChannel is only used in the following fragments:

MidiNoteTrigger:
    'Note' onOff=ON_OFF 'Channel' mc=MidiChannel ('Note' note=MidiNote | 'NoteRange' noteRange=MidiNoteRange) velocity=MIDI_VALUE;

MidiCcTrigger:
    'CC' 'Channel' mc=MidiChannel 'Number' ccNumber=(MIDI_VALUE) ('Value' value=MidiValue | 'ValueRange' valueRange=MidiValueRange);

MidiAftertouchTrigger:
    'Aftertouch' 'Channel' mc=MidiChannel ('Value' value=MidiValue | 'ValueRange' valueRange=MidiValueRange);

MidiProgramChangeTrigger:
    'PrgChg' 'Channel' mc=MidiChannel 'Bank' bank=MidiValue 'Program' program=MidiValue;

MidiChannel: 
    channel=('OMNI' | MIDI_CHANNEL);

At the start of each rule (except MidiChannel' there is a keyword ('Time', 'Note', 'CC', 'Aftertouch', 'PrgCh', so I hoped following rules would be all unique.

And the definition of MIDI_CHANNEL is:

terminal MIDI_CHANNEL:
    ('1' '0'..'6') |
    (    '0'..'9');

How can I fix this error?

The full grammar is below:

TESTS I DID

  1. Changing the rule into a different name and written out numbers:

    terminal MIDI_CHANNEL_NUMBER: ('1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11' | '12' | '13' | '14' | '15' | '16');

    Result: same error with different name (MIDI_CHANNEL_NUMBER)

  2. Removed the reference to MIDI_CHANNEL_NUMBER (so the rule is never used):

    Result: ERROR IS STILL PRESENT. I did not expect this as it is nowhere used.

  3. Removing numbers 10 to 16 from the list

    Result: Error is still present.

  4. Removed ( and )

    Result: Error is still present.

  5. Changing values to 'x' and 'y'

    Result: Error disappears. But I don't want values x and y, but 1 to 16.

FULL GRAMMAR

// Grammar 
grammar mestra.DmxLightShow with org.eclipse.xtext.common.Terminals

generate dmxLightShow 'http://www.DmxLightShow.mestra'

// Main structure

Mestra:
    'songs:'    songs   +=Song+ 
    'triggers:' triggers+=RuleTrigger+ 
    'commands:' commands+=Command+;

// Song structure

Song:
    'song' name=ID ':'
      'bank' bank=MIDI_VALUE 'program' program=MIDI_VALUE ';'
      rules=Rules
      ('order'      sequenceRefs+=[Sequence] (',' sequenceRefs+=[Sequence])* ';'
       'sequences:' sequences   += Sequence+)?;

Sequence:
    'sequence' name=ID ':'
       rules=Rules
      ('order'  stepRefs+=[Step] (',' stepRefs+=[Step])* ';'
       'steps:' steps   += Step+)?;

Step:
    'step' name=ID  ':'
       rules=Rules;

// Rules

Rules: 
    {Rules} rules+=Rule*;

Rule:
    'rule' (ruleTriggers=RuleTriggers ':')? ruleCommands=RuleCommands ';';

RuleTriggers:
    triggerRefs+=[RuleTrigger] (',' triggerRefs+=[RuleTrigger])*;

RuleCommands:
    commandsRefs+=[Command] (',' commandsRefs+=[Command])*;

// Rule Triggers

RuleTrigger:
    name=ID type=(/* DmxRuleTrigger | */ MidiRuleTrigger) ';';

// DmxRuleTrigger: // Not supported

MidiRuleTrigger:
    type=(MidiTimeTrigger | MidiNoteTrigger | MidiCcTrigger | MidiAftertouchTrigger | MidiProgramChangeTrigger) ';';

MidiTimeTrigger:
    'Time' time=Time;

MidiNoteTrigger:
    'Note' onOff=ON_OFF 'Channel' mc=MidiChannel ('Note' note=MidiNote | 'NoteRange' noteRange=MidiNoteRange) velocity=MIDI_VALUE;

MidiCcTrigger:
    'CC' 'Channel' mc=MidiChannel 'Number' ccNumber=(MIDI_VALUE) ('Value' value=MidiValue | 'ValueRange' valueRange=MidiValueRange);

MidiAftertouchTrigger:
    'Aftertouch' 'Channel' mc=MidiChannel ('Value' value=MidiValue | 'ValueRange' valueRange=MidiValueRange);

MidiProgramChangeTrigger:
    'PrgChg' 'Channel' mc=MidiChannel 'Bank' bank=MidiValue 'Program' program=MidiValue;

MidiChannel: 
    channel=('OMNI' | MIDI_CHANNEL);

MidiValue:
    value=MIDI_VALUE;

MidiValueRange:
    start=MIDI_VALUE '-' end=MIDI_VALUE;

MidiNote: 
    'Note' note=MIDI_NOTE;

MidiNoteRange: 
    'NoteRange' start=MIDI_NOTE '-' end=MIDI_NOTE;

Time:
    'Time' time=INT type=('ms' | 's' );

// Commands

Command:
    name=ID type=(DmxCommand /* | MidiCommand */ ) ';';

// MidiCommand: // Not supported

DmxCommand:
    parGroup=ParGroup dmxSubCommands=DmxSubCommands ';';

ParGroup:
    (parGroup=  'AllGroupsAll' | 
                {ParGroup} 'AllGroupsCenter' |
                {ParGroup} 'AllGroupsAllExceptEgoRisers' |
                {ParGroup} 'AllGroupsLeft' |
                {ParGroup} 'AllGroupsRight' |
                {ParGroup} 'LedBarAll' |
                {ParGroup} 'LedBarCenter' |
                {ParGroup} 'LedBarLeft' |
                {ParGroup} 'LedBarRight' |
                {ParGroup} 'DrumsAll' |
                {ParGroup} 'DrumsLeft' |
                {ParGroup} 'DrumsRight' |
                {ParGroup} 'EgoRisersAll' |
                {ParGroup} 'EgoRisersLeft' |
                {ParGroup} 'EgoRisersRight' |
                {ParGroup} 'FrontAll' |
                {ParGroup} 'FontCorners' |
                {ParGroup} 'FrontMiddle' |
                {ParGroup} 'FrontInner' |
                {ParGroup} 'FrontOuter' |
                {ParGroup} 'FrontLeft1Inside' |
                {ParGroup} 'FrontLeft2' |
                {ParGroup} 'FrontLeft3' |
                {ParGroup} 'FrontLeft4Outside' |
                {ParGroup} 'FrontLeftAll' |
                {ParGroup} 'FrontLeftInner' |
                {ParGroup} 'FrontLeftOuter' |
                {ParGroup} 'BannerAll' |
                {ParGroup} 'BannerLeft' |
                {ParGroup} 'BannerRight' |
                {ParGroup} 'FrontRight1Inside' |
                {ParGroup} 'FrontRight2' |
                {ParGroup} 'FrontRight3' |
                {ParGroup} 'FrontRight4Outside' |
                {ParGroup} 'FrontRightAll' |
                {ParGroup} 'FrontRightInner' |
                {ParGroup} 'FrontRightOuter');

DmxSubCommands:
    {DmxSubCommands} 
    (mode=DmxModeSubCommand)? 
    (preset=DmxPresetSubCommand)?  
    (delayTime=DmxDelayTimeSubCommand)? 
    (strobeTime=DmxStrobeTimeSubCommand)? 
    (stepNumber=DmxStepNumberSubCommand)? 
    (hold=DmxHoldSubCommand)? 
    (once=DmxOnceSubCommand)? 
    (DefaultColor=DmxDefaultColorSubCommand)? 
    (AlternateColor=DmxAlternateColorSubCommand)?;

DmxModeSubCommand:
    'Mode' DmxModeSubCommandData;

DmxModeSubCommandData:
    type=('trigger' | 'loop' | 'once' | 'restart');

DmxPresetSubCommand:
    'Preset' DmxPresetSubCommandData;

DmxPresetSubCommandData:
    presetName=                          'def2alt' |
               {DmxPresetSubCommandData} 'alt2def' |
               {DmxPresetSubCommandData} 'switch_def_alt' |
               {DmxPresetSubCommandData} 'def2act' |
               {DmxPresetSubCommandData} 'actual2def' |
               {DmxPresetSubCommandData} 'switch_def_actual' |
               {DmxPresetSubCommandData} 'alt2actual' |
               {DmxPresetSubCommandData} 'actual2alt' |
               {DmxPresetSubCommandData} 'switch_alt_actual' |
               {DmxPresetSubCommandData} 'solid' |           
               {DmxPresetSubCommandData} 'dual_colors_def_alt' |
               {DmxPresetSubCommandData} 'dual_colors_alt_def' |
               {DmxPresetSubCommandData} 'chase_left2right' |
               {DmxPresetSubCommandData} 'chase_right2left' |
               {DmxPresetSubCommandData} 'switch_left_right_left' |
               {DmxPresetSubCommandData} 'switch_right_left_right' |
               {DmxPresetSubCommandData} 'fade_alt2def' |
               {DmxPresetSubCommandData} 'fade_def2alt' |
               {DmxPresetSubCommandData} 'fade_def_alt_def' |
               {DmxPresetSubCommandData} 'fade_alt_def_alt' |
               {DmxPresetSubCommandData} 'fade_chase_left2right' |
               {DmxPresetSubCommandData} 'fade_chase_right2left' |
               {DmxPresetSubCommandData} 'fade_chase_left_right_left' |
               {DmxPresetSubCommandData} 'fade_chase_right_left_right' |
               {DmxPresetSubCommandData} 'rainbow_no_fade_left2right' |
               {DmxPresetSubCommandData} 'rainbow_no_fade_right2left' |
               {DmxPresetSubCommandData} 'rainbow_fade_left2right' |
               {DmxPresetSubCommandData} 'rainbow_fade_right2left';

DmxDelayTimeSubCommand:
    'DelayTime' time=Time;

DmxStrobeTimeSubCommand:
    'StrobeTime' time=Time;

DmxStepNumberSubCommand:
    'StepNumber' (last='Last' | value=INT);

DmxHoldSubCommand:
    'Hold' onOff=ON_OFF;

DmxOnceSubCommand:
    'Once' onOff=ON_OFF;

DmxDefaultColorSubCommand:
    'DefaultColor' color=DmxColor;

DmxAlternateColorSubCommand:
    'AlternateColor' color=DmxColor;

DmxColor: 
    ShortDmxColor | LongDmxColor;

ShortDmxColor:
    {ShortDmxColor} (intensity='I')? (red='R')? (green='G')? (blue='B')? (white='W')?;

LongDmxColor:
    intensity=DMX_VALUE red=DMX_VALUE green=DMX_VALUE blue=DMX_VALUE (white=DMX_VALUE)?;

// Terminals

terminal MIDI_VALUE: 
    ('1' '2'      '0'..'7') |
    ('1' '0'..'1' '0'..'9') |
    (    '1'..'9' '0'..'9') |  
    (             '0'..'9');

terminal DMX_VALUE: 
    ('2' '5'      '0'..'5') |
    ('2' '0'..'4' '0'..'9') |
    ('1' '0'..'9' '0'..'9') |
    (    '1'..'9' '0'..'9') |  
    (             '0'..'9');

terminal MIDI_CHANNEL:
    ('1' '0'..'6') |
    (    '0'..'9');

terminal MIDI_NOTE: 
    ('C' | 'D' | 'E' | 'F' | 'G' | 'A' | 'B') 
    ('b' | '#') 
    ('0'..'9') | ('1' '0'..'1');

terminal ON_OFF: 
    ('ON' | 'OFF');

Solution

  • the rules MIDI_VALUE and DMX_VALUE and MIDI_CHANNEL overlap with each other.

    possible solutions

    1. use INT + validator (for all of them)
    2. use a datatyperule like MIDI_CHANNEL: INT (no terminal keyword) + a valueconverter
    3. use terminal rules that dont overlap and datatype rules MIDI_CHANNEL: TERMINAL1|TERMINAL2| ....