1

わかりました、すでに投稿した質問の数に基づいて、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

文字列の周りや演算子の周りなどからスペースを削除すると機能します。

また、誰かが文法に他の愚かさを見た場合は、喜んで聞いてください.

ジェイソン

4

1 に答える 1

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ため、構文解析はギャグです。

' 'from parser ruleを削除した後dateOp、上記の入力により、予想どおり次のトークンが生成されます。

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

' 'fromdateOpを削除することがあなたの文法で許容されるかどうかはわかりません。スペースを明示的にテストする必要がある場合は、空白テストをレクサーに移動するためにできることを書き直すことを検討してください。あるいは、パーサーは、次のトークンが非表示の WS トークンであるかどうかを確認するために先を見越すことができます。ただし、手始めに、できるだけクリーンアップしdateOpて、どこに着陸するかを確認することをお勧めします.

于 2013-03-02T05:55:58.257 に答える