Xtext/ANTLR: 如何解决这个错误?以下令牌定义永远无法匹配...?

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

我做了一个语法,编辑器没有显示任何错误,当我select 'Generate XText Artifacts',我得到以下错误:

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 仅在以下片段中使用:

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

在每个规则的开头(除了 MidiChannel' 有一个关键字 ('Time', 'Note', 'CC', 'Aftertouch', 'PrgCh',所以我希望以下规则都是独一无二的。

而MIDI_CHANNEL的定义是:

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

我该如何解决这个错误?

完整语法如下:

我做过的测试

  1. 将规则更改为不同的名称并写出数字:

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

    结果:相同的错误但名称不同 (MIDI_CHANNEL_NUMBER)

  2. 删除了对 MIDI_CHANNEL_NUMBER 的引用(因此从不使用该规则):

    结果:错误仍然存​​在。我没想到这个,因为它无处使用。

  3. 从列表中删除数字 10 到 16

    结果:错误仍然存​​在。

  4. 删除(和)

    结果:错误仍然存​​在。

  5. 将值更改为 'x' 和 'y'

    结果:错误消失。但我不想要值 x 和 y,而是 1 到 16。

完整语法

// 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');

规则MIDI_VALUEDMX_VALUEMIDI_CHANNEL 相互重叠。

可能的解决方案

  1. 使用 INT + 验证器(对所有这些)
  2. 使用像 MIDI_CHANNEL: INT 这样的数据类型规则(无终端关键字)+ 值转换器
  3. 使用不重叠的终端规则和数据类型规则MIDI_CHANNEL: TERMINAL1|TERMINAL2| ....