ANTLR3语法吃整数

时间:2012-12-19 16:37:15

标签: java antlr

首先,我承认:这可能是你生命中最丑陋的语法之一(具有讽刺意味的名字叫SimplePolicy.g)。它作为一个简单的语法启动,但我最终需要更详细的异常,所以我最终做了多个异常扩展RecognitionException。然后用RuntimeException来包装它,这样我就可以摆脱解析器方法了。

该语法应该验证一组一个或多个IF / THEN / ELSE?/ ENDIF语句。不幸的是,当我尝试将其中一个INTEGER_FIELD标记与INTEGER标记进行比较时,INTEGER标记已完全从TokenInputStream中消失。我已经尝试过println()来弄清楚发生了什么,没有运气。

以下是失败的示例:

IF AGE< 21然后 WT> 150 ENDIF

似乎正在发生的是输入字符串是好的,但是当我通过CharStream将它提供给Lexer时,整数常量消失,AGE和WT被比较消失。

这是一个单元测试:

@Test
public void ifStatement_legalPolicy2_passes() {
    String policy = "IF AGE < 21 THEN "+ 
                    "WT > 150 "+ 
                    "ENDIF";


    CharStream charStream = new ANTLRStringStream(policy);
    SimplePolicyLexer lexer = new SimplePolicyLexer(charStream);
    TokenStream tokenStream = new CommonTokenStream(lexer);
    System.out.println(tokenStream.toString());
    SimplePolicyParser parser = new SimplePolicyParser(tokenStream);    

    try {
        parser.ifStatement();
        parser.reset();
    } catch (RecognitionRuntimeException e) {
        RecognitionException e1 = e.getRecognitionException();
        fail("Legal policy should not have thrown exception. Exception thrown: " + e1.getClass().getName() + ". Line: " + e1.line + ", Column: " + e1.charPositionInLine);
    } catch (RecognitionException e) {
    }       


}

这是令牌流的字符串输出:

13:14:14.978 DEBUG m.a.k.m.impl.SimplePolicyParser - ifStatement: IF AGE <  THEN WT >  ENDIF

我为这个语法的丑陋道歉,但如果有人能告诉我为什么会死,我会非常感激。

grammar SimplePolicy;

options {
  language = Java;
  backtrack = true;
}


@header {
package com.manager.impl;

import com.manager.RecognitionRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;


@lexer::header {
package com.manager.impl;

}

@lexer::members {

}

@parser::members {

 private static final Logger log = LoggerFactory.getLogger(SimplePolicyParser.class);

 @Override
 protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException {
   throw new MismatchedTokenException(ttype, input);
 }

  @Override
 public Object recoverFromMismatchedSet(IntStream input, RecognitionException e, BitSet follow) throws RecognitionException {
   throw e;
 }

 @Override
 public String getErrorMessage(RecognitionException e, String[] tokenNames) {
  // wrap in a runtime exception to escape ANTLR's dungeon
  throw new RecognitionRuntimeException(e);
 }

}

@rulecatch {
    catch (RecognitionException e) {
           System.out.println(getErrorHeader(e));
      //System.out.println(getErrorMessage(e,tokenNames));
        throw new RecognitionRuntimeException(e);
    }
}

// evaluate multiple show statements
policyGroupWithShow
  : show (show)* EOF
  | ifStatement+ EOF
  {
    // nope, this isn't legal
    ShowExpectedException ex = new ShowExpectedException();
    ex.line = 1;
    ex.charPositionInLine=0;
    throw ex;
  }
  ;

policyGroupWithoutShow
  : ifStatement (ifStatement)* EOF
  | show+ EOF
  {
    // not legal
    UnexpectedSymbolOrConstructionException ex = new UnexpectedSymbolOrConstructionException();
    ex.line = 1;
    ex.charPositionInLine = 0;
    throw ex;
  }
  ;

//policyGroup
//  : show (show)* EOF
//  ;


// evaluate a single SHOW statement
show
  //: ifStatement
  : SHOW STRING FOR ifStatement // good
  // missing for (FOR expected)
  | SHOW expr1a=STRING ifStatement // missing for
  {
    int nextTokenPosition = expr1a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(nextTokenPosition + 1);

    ShowWithoutForException ex = new ShowWithoutForException();
    ex.line = expr1a.getLine();
    ex.charPositionInLine = token.getCharPositionInLine();
    throw ex;

  }
  // missing show (SHOW expected)
  | expr1b=STRING FOR ifStatement
  {
    int tokenPosition = expr1b.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition);
    ForWithoutShowException ex = new ForWithoutShowException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  ;


ifStatement
  @init {
    log.debug("ifStatement: " + input.toString());
  }
  //: IF logical THEN logical+ (ELSE logical+)? ENDIF
  : IF operation 'THEN' expression+ ENDIF // good
  {
    log.debug("Matched [IF logical THEN expression+ ENDIF]");
  }
  | IF logical THEN expression+ ELSE expression+ ENDIF
  {
    log.debug("Matched [IF logical THEN expression+ ELSE expression+ ENDIF]");
  }
  | logical expr1a=THEN expression* (ELSE expression*)? ENDIF // missing if
  {
    int tokenPosition = expr1a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition);
    MissingIfException ex = new MissingIfException();
    ex.line = token.getLine();
    ex.charPositionInLine = 0;
    throw ex;
  }
  // missing THEN (THEN clause is missing)
  | expr1b=IF logical expression+ (ELSE expression+)? ENDIF
  {
    int tokenPosition = expr1b.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);
    MissingThenClauseException ex = new MissingThenClauseException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getCharPositionInLine();
    throw ex;
  }
  // missing ELSE or ENDIF (ENDIF Expected)
  | IF logical expr1c=THEN expression+
  {
    String inputText = input.toString();
    if (inputText.indexOf("ELSE") < 0 && inputText.indexOf("ENDIF") < 0) {
      // best I can do is get the line/column from the THEN keyword
        MissingElseOrEndifException ex = new MissingElseOrEndifException();
        ex.line = expr1c.getLine();
        ex.charPositionInLine = expr1c.getCharPositionInLine();
        throw ex;   
    }
  }
  // missing comparison for IF (rule expected)
  | IF expr1d=THEN expression* ENDIF
  {
    int tokenPosition = expr1d.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition - 1);

    RuleExpectedException ex = new RuleExpectedException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStopIndex();
    throw ex;
  }
  // missing body of then (unexpected symbol or construction)
  | IF logical a=THEN b=ENDIF
  {
    int tokenPosition1 = a.getTokenIndex();
    int tokenPosition2 = b.getTokenIndex();
    CommonToken tokenA = (CommonToken) input.get(tokenPosition1);
    CommonToken tokenB = (CommonToken) input.get(tokenPosition2);

    UnexpectedSymbolOrConstructionException ex = new UnexpectedSymbolOrConstructionException();
    //MissingThenBodyException ex = new MissingThenBodyException();
    if (tokenA.getLine() == tokenB.getLine()) {
      ex.line = tokenA.getLine();
      ex.charPositionInLine = tokenA.getCharPositionInLine();
    } else {
      ex.line = tokenA.getLine() + 1;
      ex.charPositionInLine = 0;
    }
    throw ex;
  }
  // missing body of ELSE (Unexpected symbol or construction)
  | IF logical THEN expression+ ELSE expr3e=ENDIF
  {

    UnexpectedSymbolOrConstructionException ex = new UnexpectedSymbolOrConstructionException();
    ex.line = expr3e.getLine();
    ex.charPositionInLine = expr3e.getCharPositionInLine();
    throw ex;
  }
  // body of IF missing (Body of IF clause is missing)
  | IF expr3f=ENDIF
  {
    int tokenPosition = expr3f.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition);

    MissingIfBodyException ex = new MissingIfBodyException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getCharPositionInLine();
    throw ex;
  }
  | expression // expression should just pass through
  ;


expression
  @init {
    log.debug("Expression: " + input.toString());
  }
  : logical
  ;

// deal with AND/OR operator
logical
  @init {
    log.debug("Logical:" + input.toString());
  }
 : operation (AND operation | OR operation)*
 ;

operation
  @init {
    log.debug("Operation:" + input.LT(1) + input.LT(2) + input.LT(3));
  }
  // good rules
  //: ';' ~('/r/n'|EOF)*
  : DATE_FIELD (EQ|NE|LT|LE|GT|GE) (DATE_FIELD|DATE|DATE_CONSTANT)
  {

    log.info("Matched STRING_FIELD (EQ|NE) (STRING_FIELD|STRING) field operation: " + input.toString());
  }
  | DATE_FIELD (PLUS|MINUS) (DATE_FIELD|DATE|DATE_CONSTANT|DATE_PERIOD_FIELD|datePeriod)
  {
    log.info("Matched DATE_FIELD (PLUS|MINUS) (DATE_FIELD|DATE|DATE_CONSTANT|DATE_PERIOD_FIELD|datePeriod) field operation: " + input.toString());
  }
  | DATE_PERIOD_FIELD (EQ|NE|LT|LE|GT|GE) (DATE_PERIOD_FIELD|DATE_PERIOD_CONSTANT)
  {
    log.info("Matched DATE_PERIOD_FIELD (EQ|NE|LT|LE|GT|GE) (DATE_PERIOD_FIELD|DATE_PERIOD_CONSTANT) field operation: " + input.toString());
  }
  | DATE_PERIOD_FIELD (PLUS|MINUS) (DATE_PERIOD_FIELD|datePeriod|DATE_FIELD|DATE_CONSTANT|DATE)
  {
    log.info("Matched DATE_PERIOD_FIELD (PLUS|MINUS) (DATE_PERIOD_FIELD|datePeriod|DATE_FIELD|DATE_CONSTANT|DATE) field operation: " + input.toString());
  }
  | STRING_FIELD (EQ|NE) (STRING_FIELD|STRING)
  {
  log.info("Matched STRING_FIELD (EQ|NE) (STRING_FIELD|STRING) field operation: " + input.toString());
  }
  | INTEGER_FIELD (EQ|NE|LT|LE|GT|GE) test=inAllFields
  {
    Boolean result = $inAllFields.result;
    Integer type = $inAllFields.tokenType;

    //int testType = test.getType();
    if (type != INTEGER && type != INTEGER_FIELD) {

        IncompatibleTypeException ex = new IncompatibleTypeException();
        //ex.line = test.getLine();
        //ex.charPositionInLine = test.getStartIndex();
        throw ex;
    }
  }
  | INTEGER_FIELD (EQ|NE|LT|LE|GT|GE) test1=(INTEGER_FIELD|INTEGER)
  {
    log.info(test1.getText());
    log.info("Matched INTEGER_FIELD (EQ|NE|LT|LE|GT|GE) (INTEGER_FIELD|INTEGER) field operation: " + input.toString());
  }
  | BOOLEAN_FIELD (EQ|NE) (BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
  log.info("Matched BOOLEAN_FIELD (EQ|NE) (BOOLEAN_FIELD|BOOLEAN_CONSTANT) field operation: " + input.toString());
  }
  | COMMENT
  {
  log.info("Matched COMMENT field operation: " + input.toString());

  }
  // specify bad rules
  // defined fields with no operation. The expression will be null if datePeriod isn't split off
  | (INTEGER_FIELD|INTEGER) (PLUS|MINUS|EQ|NE|LT|LE|)
  {
    System.out.println("Will it work?");
  }
  | (datePeriod)|e1=(DATE_PERIOD_FIELD|DATE_FIELD|STRING_FIELD|INTEGER_FIELD|WAIVER_FIELD|BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
    RelationalOperatorOrNotExpectedException ex = new RelationalOperatorOrNotExpectedException();
    if (e1 == null) {
        ex.line = 1;
        ex.charPositionInLine = 0;
    } else {
      ex.line = e1.getLine();
      ex.charPositionInLine = e1.getCharPositionInLine();
    }
    throw ex;
  }
  | e2=(INTEGER|DATE|DATE_CONSTANT)
  {
    RuleCannotBeginWithNumberOrDateException ex = new RuleCannotBeginWithNumberOrDateException();
    ex.line = e2.getLine();
    ex.charPositionInLine = e2.getCharPositionInLine();
    throw ex;
  }
  // attempt to compare a date field to a different field type. Incompatable Type exception
  | DATE_FIELD operator1a=(EQ|NE|LT|LE|GT|GE) (DATE_PERIOD_FIELD|datePeriod|STRING_FIELD|STRING|INTEGER_FIELD|INTEGER|WAIVER_FIELD|BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
    int tokenPosition = operator1a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 2);

    IncompatibleTypeException ex = new IncompatibleTypeException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  // attempt to add/sub illegal field types to date (Relational Operator or NOT expected)
  | DATE_FIELD operator1b=(PLUS|MINUS)
      (STRING_FIELD|STRING|INTEGER_FIELD|WAIVER_FIELD|BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
    int tokenPosition = operator1b.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition+1);

    RelationalOperatorOrNotExpectedException ex = new RelationalOperatorOrNotExpectedException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex; 
  }
  // attempting to add an int to a date field makes it assume it's a bad date period
  | DATE_FIELD operator1c=(PLUS|MINUS) expr1c=(INTEGER)
  {
   int tokenPosition = operator1c.getTokenIndex();
   CommonToken token = (CommonToken) input.get(tokenPosition + 1);

   MissingYearsMonthsWeeksDaysException ex = new MissingYearsMonthsWeeksDaysException();
   ex.line = token.getLine();
   ex.charPositionInLine = token.getStartIndex();
   throw ex;
  }
  // attempt to compare a String field to something besides a String field or String constant
  | STRING_FIELD operator2a=(EQ|NE) ~(STRING_FIELD|STRING)
  {
    int tokenPosition = operator2a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    IncompatibleTypeException ex = new IncompatibleTypeException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  | expr2b=STRING_FIELD operator2b=(LT|LE|GT|GE) (STRING_FIELD|STRING)
  {
    int tokenPosition = operator2b.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    UnexpectedSymbolOrConstructionException ex = new UnexpectedSymbolOrConstructionException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  | stringExpr3=STRING
  {
    RuleCannotBeginWithAStringException ex = new RuleCannotBeginWithAStringException();
    ex.line = stringExpr3.getLine();
    ex.charPositionInLine = stringExpr3.getCharPositionInLine();
    throw ex;
  }
  // attempt to compare a number field to something besides a num field or int constant

  | INTEGER_FIELD operator3a=(EQ|NE|LT|LE|GT|GE) expr3a2=(DATE_FIELD|DATE|STRING_FIELD|STRING|DATE_PERIOD_FIELD|WAIVER_FIELD|BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
    Boolean value = $inAllFields.result;
    //System.out.println("Expr text: " + expr3a2.getText());
    int tokenPosition = operator3a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    IncompatibleTypeException ex = new IncompatibleTypeException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  // attempt to perform a PLUS/MINUS on a number field with something besides a num field or constant int
  | INTEGER_FIELD operator3b=(PLUS|MINUS)
    (DATE_FIELD|DATE|STRING_FIELD|STRING|DATE_PERIOD_FIELD|datePeriod|WAIVER_FIELD|BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
    int tokenPosition = operator3b.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    IncompatibleTypeException ex = new IncompatibleTypeException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex; 
  }
  // attempt to compare date period to something besides a date period field or date period constant
  | DATE_PERIOD_FIELD operator4a=(EQ|NE|LT|LE|GT|GE)
    (INTEGER_FIELD|INTEGER|DATE_FIELD|DATE|DATE_CONSTANT|STRING_FIELD|STRING|BOOLEAN_FIELD|BOOLEAN_CONSTANT|WAIVER_FIELD)
  {
    int tokenPosition = operator4a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    IncompatibleTypeException ex = new IncompatibleTypeException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  | DATE_PERIOD_FIELD operator5a=(PLUS|MINUS) (STRING_FIELD|STRING|INTEGER_FIELD|WAIVER_FIELD|BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
    int tokenPosition = operator5a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    RelationalOperatorOrNotExpectedException ex = new RelationalOperatorOrNotExpectedException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  | DATE_PERIOD_FIELD operator5b=(PLUS|MINUS) INTEGER
  {
    int tokenPosition = operator5b.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    MissingYearsMonthsWeeksDaysException ex = new MissingYearsMonthsWeeksDaysException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  | WAIVER_FIELD operator6a=(HAS|EQ|NE) ~(STRING)
  {
    int tokenPosition = operator6a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    IncompatibleTypeException ex = new IncompatibleTypeException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  | WAIVER_FIELD operator6b=(PLUS|MINUS|LT|LE|GT|GE)  STRING
  {
    int tokenPosition = operator6b.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    UnexpectedSymbolOrConstructionException ex = new UnexpectedSymbolOrConstructionException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  | BOOLEAN_FIELD operator7a=(EQ|NE) ~(BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
    int tokenPosition = operator7a.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    IncompatibleTypeException ex = new IncompatibleTypeException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex; 
  }
  | BOOLEAN_FIELD operator7b=(PLUS|MINUS|LT|LE|GT|GE) (DATE_FIELD|DATE|DATE_CONSTANT|DATE_PERIOD_FIELD|datePeriod|STRING_FIELD|STRING|INTEGER_FIELD|WAIVER_FIELD|BOOLEAN_FIELD|BOOLEAN_CONSTANT)
  {
    int tokenPosition = operator6b.getTokenIndex();
    CommonToken token = (CommonToken) input.get(tokenPosition + 1);

    UnexpectedSymbolOrConstructionException ex = new UnexpectedSymbolOrConstructionException();
    ex.line = token.getLine();
    ex.charPositionInLine = token.getStartIndex();
    throw ex;
  }
  ;

// this rule checks that a value is valid. It's not in the validation
// chain
atom
  : datePeriod
  | DATE_FIELD
  | INTEGER_FIELD
  | STRING_FIELD
  | DATE_PERIOD_FIELD
  | WAIVER_FIELD
  | BOOLEAN_FIELD
  | INTEGER
  {
    System.out.println("Matched atom INTEGER");
  }
  | DATE
  | DATE_CONSTANT
  | BOOLEAN_CONSTANT
  | STRING
  //| LPAREN expression RPAREN
  ;

datePeriod
  : (DATE_PERIOD_CONSTANT)+
  ;

// "subatomic" rules, meant to feed the atom rule understandable values



fragment DIGIT: ('0'..'9');

DATE_FIELD:('DOB'|'TEST_DATE');
DATE_PERIOD_FIELD:('AFS');
BOOLEAN_FIELD:('CERTIFIED'|'OVERRIDE');
INTEGER_FIELD:('AGE'|'HT'|'WT');
STRING_FIELD:('CURR_LOC'|'LANG_1'|'LANG_2''USER_LEVEL');
WAIVER_FIELD:('WAIVER');

DATE: /* empty */ {System.out.println("Matched DATE");};

INTEGER: DIGIT+
//INTEGER:('0'..'9')+
{
  System.out.println("Matched INTEGER: " + $text);
  //$type = INTEGER;
    // dynamically change the type when we match the date regular expression
  if ($text.matches("(19|20|21)[0-9]{2}[0-1]\\d{3}")) {
  //if ($text.matches("(19|20|21)[0-9]{2}[0-1]\\d{3}")) {
    System.out.println("Matched date pattern");
    $type = DATE;
  }

}
;
DATE_PERIOD_CONSTANT: ((INTEGER ' ' YEAR)|(INTEGER ' ' MONTH)|(INTEGER ' ' WEEK)|(INTEGER ' ' DAY))
{
  System.out.println("Matched DATE_PERIOD_CONSTANT");
};
DATE_CONSTANT:('TODAY'|'YESTERDAY'|'TOMMOROW');
BOOLEAN_CONSTANT:('TRUE'|'FALSE'|'"Y"'|'"N"');
IF: 'IF';
THEN: 'THEN';
ELSE: 'ELSE';
ENDIF: 'ENDIF';
AND: 'AND';
OR: 'OR';
YEAR: ('YEAR'|'YEARS');
MONTH: ('MONTH'|'MONTHS');
WEEK: ('WEEK'|'WEEKS');
DAY: ('DAY'|'DAYS');

STRING: '"' ID  (' ' ID)* '"' {System.out.println("Matched STRING");};
//  {
//    // strip the quotes once we match this token
//    setText(getText().substring(1, getText().length()-1));
//  }
//  ;

EQ: '=' {System.out.println("Matched EQ");};
NE: '<>';
LT: '<';
LE: '<=';
GT: '>';
GE: '>=';
HAS: 'HAS';
LPAREN: '(';
RPAREN: ')';
PLUS: '+';
MINUS: '-';
SHOW: 'SHOW';
FOR: 'FOR';
ID: ('A'..'Z'|'a'..'z'|'0'..'9'|','|'!'|'?'|':')+ {System.out.println("Matched ID: " + $text);};

COMMENT: ';' ~('\r'|'\n')* {skip();};
WS: (' '+|'\r'|'\n'|'\t') {$channel = HIDDEN;};

1 个答案:

答案 0 :(得分:4)

每当你的词法分析器“看到”一个数字后跟一个空格时,它就会尝试构造一个DATE_PERIOD_CONSTANT。改为使其成为解析器规则:

date_persiod_constant
 : INTEGER (YEAR | MONTH | WEEK | DAY)
 ;

(当然,删除DATE_PERIOD_CONSTANT

此外,您的DATE规则不匹配。我知道您需要此规则来更改INTEGER规则的类型,但永远不会创建(可能)不匹配的词法分析器规则。改为DATE fragment

fragment DATE : ;

即使它是fragment,您仍然可以在解析器规则中使用DATE。通过将其设为fragment,您只是指示词法分析器永远不会自己创建DATE个令牌,但知道词法分析器可以生成此类令牌,​​因为当INTEGER看起来像日期时,您正在更改INTEGER : DIGIT+ ( (' '+ (YEAR | MONTH | WEEK | DAY))=> ' '+ (YEAR | MONTH | WEEK | DAY) {$type = DATE_PERIOD_CONSTANT;} )? { if ($text.matches("(19|20|21)[0-9]{2}[0-1]\\d{3}")) { $type = DATE; } } ; fragment DATE_PERIOD_CONSTANT : ; 的类型。

修改

  

有没有办法将它作为词法分析器规则?我真的希望日期常量发出一个我以后可以使用的令牌。

当然,这是可能的。不漂亮,但可能:

DIGIT+

这样做是在匹配一个或多个数字YEAR之后,它会强制词法分析器在字符流中向前看以查看是否有一个或多个空格后跟MONTH,{ {1}},WEEKDAY,谓词(' '+ (YEAR | MONTH | WEEK | DAY))=>,如果是,则会继续并与之匹配(并且您将类型从INTEGER更改为{{ 1}})。

编辑II

  

最后一个问题:DATE期间可能是“1年4个月2天”,而不仅仅是“1年”。尝试在规则中添加+不成功。我可以再请求一个帮助,以使该规则按照我需要的方式运作吗?

这样的事情应该这样做(但同样,解析器规则可能更合适!):

DATE_PERIOD_CONSTANT