ANTLR:在空白处理方面破坏了政策

时间:2013-03-01 15:24:33

标签: java antlr

好吧,根据我已发布的问题数量,我经常感觉自己是最愚蠢的人,但在这里我再次请求帮助。

我最终试图重写一个现有的策略来简化它,只是让“简化”的策略决定轰炸空白,这应该发送到HIDDEN频道(skip()不能正常工作)。它可能只是无序的Lexer令牌,但我很难过(也许我对如何指定顺序没有很好的理解)。

无论如何,这是整个(有点消毒)的政策:

grammar ValidatingPolicy;

options {
  language = Java;
    backtrack = true; 
}

// package and imports for the parser
@parser::header {
package org.jason.manager.impl;

import org.jason.manager.RecognitionRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
}

// package and imports for the lexer
@lexer::header {
package org.jason.manager.impl;

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

// member functions and fields for the parser
@parser::members {

 private static final Logger log = LoggerFactory.getLogger(ValidatingPolicyParser.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);
 }
}

// member functions and fields for the lexer
@lexer::members {
  private static final Logger log = LoggerFactory.getLogger(ValidatingPolicyLexer.class);
}

// validate a group of SHOW constructs
showGroup
  : show+ EOF
  ;

// validate a construct WITHOUT show (MINQ, MOS, etc)
noShow
  : simpleIfStatement+ EOF
  ;

// validate a SHOW construct (COMP or ELIG validation)
show
  : SHOW STRING FOR simpleIfStatement+
  ;

// handle an if statement
simpleIfStatement
  // basic if statement
  : IF chainedOperation THEN operationGroup (ELSE operationGroup)? ENDIF
  // if statement with recursive if statement in THEN or ELSE block
  | IF chainedOperation THEN simpleIfStatement (ELSE simpleIfStatement)? ENDIF
  | operationGroup
  ;

// aggregate multiple operations. When evaluated, there is an implicit AND
// when there are multiple groups
operationGroup
  : chainedOperation+
  ;

// chain an operation together optionally with AND/OR
chainedOperation
  @init {
    log.info("Entered chainedOperation");
  }
  : operation (AND operation | OR operation)*
  ;

// aggregate into a single rule that can be referenced up the chain
operation
  @init {
    log.info("Entered operation");
  }
  // legal operation
  : (booleanLogical | stringLogical | integerLogical | dateLogical | datePeriodLogical)
  ;

// LOGICAL OPERATIONS
// Logical operators do not have a pass through, but may have limits
// on which particular operators can be used

// compare DATE/DATE_FIELD to DATE/DATE_FIELD
dateLogical
  @init {
    log.info("Entered dateLogical");
  }
  : dateOp (EQ|NE|LT|LE|GT|GE) dateOp
  ;

// compare DATE_PERIOD/DATE_PERIOD_CONSTANT/DATE_PERIOD_FIELD
datePeriodLogical
  @init {
    log.info("Entered datePeriodLogical");
  }
  : datePeriodOp (EQ|NE|LT|LE|GT|GE) datePeriodOp
  ;

// compare INTEGER_FIELD/INTEGER
integerLogical
  @init {
    log.info("Entered integerLogical");
  }
  : integerOp (EQ|NE|LT|LE|GT|GE) integerOp
  ;

// compare BOOLEAN_FIELD/BOOLEAN_CONSTANT
booleanLogical
  : booleanOp (EQ|NE) booleanOp
  ;

// compare STRING_FIELD/STRING
stringLogical
  : stringOp (EQ|NE|LT|LE|GT|GE) stringOp
  {
    System.out.println("stringLogical: matched rule 1");
  }
  ;

dateOp
  @init {
    log.info("Entered dateOp");
  }
  // pass through if no math op needs to be performed
  : DATE_FIELD|DATE|DATE_CONSTANT
  // match a legal math op
  | DATE_FIELD|DATE|DATE_CONSTANT ((PLUS|MINUS) DATE_FIELD|DATE|DATE_CONSTANT|DATE_PERIOD_FIELD|DATE_PERIOD_CONSTANT (' ' DATE_PERIOD_CONSTANT)*)*
  ;

datePeriodOp
  // pass through if no math op needs to be performed
  : DATE_PERIOD_FIELD|DATE_PERIOD_CONSTANT
  // match a legal math op
  | DATE_PERIOD_FIELD ((PLUS|MINUS) DATE_FIELD|DATE|DATE_CONSTANT|DATE_PERIOD_FIELD|DATE_PERIOD_CONSTANT+)*
  ;

integerOp
  @init {
    log.info("Entered integerOp");
  }
  // pass through if no math op needs to be performed
  : INTEGER_FIELD | INTEGER
  // match a legal math op
  | INTEGER_FIELD (PLUS|MINUS INTEGER_FIELD|INTEGER)*
  ;

// booleanOp, stringOp, and waiverOp don't do anything since + and - ops are not
// supported for them
booleanOp
  : BOOLEAN_FIELD | BOOLEAN_CONSTANT
  ;

stringOp
  : STRING_FIELD | STRING
  ;

// these items are not directly referenced by parser rules, so they
// can be fragments

fragment DIGIT: ('0'..'9');
fragment DATE: ;
fragment DATE_PERIOD_CONSTANT: DIGIT+ ' '+ (YEAR | MONTH | WEEK | DAY);
YEAR: ('YEAR'|'YEARS');
MONTH: ('MONTH'|'MONTHS');
WEEK: ('WEEK'|'WEEKS');
DAY: ('DAY'|'DAYS');

DATE_FIELD:('DOB'|'TEST_DATE');
DATE_PERIOD_FIELD:('EMPLOYMENT_PERIOD');
BOOLEAN_FIELD:('CERTIFIED');
INTEGER_FIELD:('AGE'|'OPTION');
STRING_FIELD:('STATE'|'UF_USERID'|'USER_LEVEL');

// various tokens can't be fragments since they are directly referenced by parser rules
COMMENT_START: ';';
BOOLEAN_CONSTANT: ('TRUE'|'FALSE'|'"Y"'|'"N"');
DATE_CONSTANT:('TODAY'|'YESTERDAY'|'TOMMOROW');
SHOW: 'SHOW';
FOR: 'FOR';
IF: 'IF';
THEN: 'THEN';
ELSE: 'ELSE';
ENDIF: 'ENDIF';
AND: 'AND';
OR: 'OR';
EQ: '=';
NE: '<>';
LT: '<';
LE: '<=';
GT: '>';
GE: '>=';
NOT: 'NOT';
HAS: 'HAS';
PLUS: '+';
MINUS: '-';

// Commented ifs seem to take more than one line, even if comments are
// only supposed to be a single line
COMMENTED_IF: COMMENT_START WS* IF (options {greedy=false;} : .)* ENDIF '\r\n'
{
  log.info("Lexer: matched COMMENTED IF" + getText());
  $channel=HIDDEN;
  //skip();
};

// Handle an empty comment such as "; "
EMPTY_COMMENT: COMMENT_START WS* '\r\n'
{
  log.info("Lexer: matched EMPTY_COMMENT: " + getText());
  $channel=HIDDEN;
};

// Handle a single-line comment. Policies often end with a comment, so be ready for it
SINGLE_COMMENT: COMMENT_START ~('\r'|'\n')*  (('\r\n')+| EOF)
{
  log.info("Lexer: matched SINGLE_COMMENT: " + getText());
  $channel=HIDDEN;
};

INTEGER
  // Bart Kiers on SO helped me with this one, basically handle a date period such as
  // 4 WEEKS, 1 YEAR 6 MONTHS 2 WEEKS 8 DAYS, etc
 : (DATE_PERIOD_CONSTANT)=> DATE_PERIOD_CONSTANT ((' '+ DATE_PERIOD_CONSTANT)=> ' '+ DATE_PERIOD_CONSTANT)*
   {
      // manually switch the type from INTEGER to DATE_PERIOD_CONSTANT
     $type=DATE_PERIOD_CONSTANT;
     log.info("Matched DATE_PERIOD_CONSTANT: " + getText());
   }
 | DIGIT+
   {
      // match a 6-digit or 8-digit date format (20120101 or 201201)
     if ($text.matches("(19|20|21)[0-9]{2}[0-1]\\d{3}") || $text.matches("(19|20|21)\\d{2}(0[1-9]|1[0-2])")) {
      log.info("Matched DATE pattern: " + getText());
       $type = DATE;
     } else {
      log.info("Matched INTEGER: " + getText());
     }
   }   
 ;

STRING
  : '"' ID  (' ' ID)* '"'
  ;

ID: ('A'..'Z'|'a'..'z'|DIGIT|','|'!'|'?'|':')+;

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

“show”构造应该如下所示:

SHOW "DOES NOT MEET AGE REQUIREMENTS FOR EMPLOYMENT" FOR
  AGE < 18

SHOW "TOO YOUNG FOR CERTIFICATION IN KY" FOR
   IF STATE="KY" THEN AGE > 21 ENDIF

当我删除空格时,例如在字符串周围,或从操作符周围删除

另外,如果有人在语法中看到任何其他愚蠢的话,我会很高兴听到它们。

杰森

1 个答案:

答案 0 :(得分:1)

您的词法分析器在隐式未命名的词法分析器规则中匹配空格。解析器规则dateOp中引用了此词法分析器规则:

dateOp
  //...
  // pass through if no math op needs to be performed
  : DATE_FIELD|DATE|DATE_CONSTANT
  // match a legal math op
  | DATE_FIELD|DATE|DATE_CONSTANT 
     ((PLUS|MINUS) DATE_FIELD|DATE|DATE_CONSTANT|DATE_PERIOD_FIELD|DATE_PERIOD_CONSTANT 
       (' ' DATE_PERIOD_CONSTANT)* //<--- ' ' becomes a new lexer rule
     )*
  ;

它的行为类似于普通的词法分析器规则,因此使用此输入:

SHOW "DOES NOT MEET AGE REQUIREMENTS FOR EMPLOYMENT" FOR
  AGE < 18

词法分子产生这些代币:

[SHOW : SHOW] [' ' :  ] [STRING : "DOES NOT MEET AGE REQUIREMENTS FOR EMPLOYMENT"] 
[' ' :  ] [FOR : FOR] [INTEGER_FIELD : AGE] [' ' :  ] [LT : <] [' ' :  ] 
[INTEGER : 18] 

请注意[' ' : ]令牌。这些是工作中隐含的词法分析规则。解析器不期望dateOp规则之外的这些令牌,因此解析gags。

从解析器规则' '中删除dateOp后,上面的输入会生成以下标记,如预期的那样:

[SHOW : SHOW] [STRING : "DOES NOT MEET AGE REQUIREMENTS FOR EMPLOYMENT"] 
[FOR : FOR] [INTEGER_FIELD : AGE] [LT : <]
[INTEGER : 18] 

我不知道从语法中删除' ' dateOp是否可以接受。如果需要明确测试空间,请考虑重写可以将空白区域测试向下移动到词法分析器中。或者,解析器可以向前看以查看下一个令牌是否是隐藏的WS令牌。不过,对于初学者,我建议尽可能地清理dateOp并查看事情的发生地点。