package org.drools.lang; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Stack; import org.antlr.runtime.BitSet; import org.antlr.runtime.MismatchedSetException; import org.antlr.runtime.MismatchedTokenException; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.RecognizerSharedState; import org.antlr.runtime.Token; import org.antlr.runtime.TokenStream; import org.drools.compiler.DroolsParserException; import static org.drools.lang.DRLParser.*; /** * This is a class to hold all the helper functions/methods used * by the DRL parser */ public class ParserHelper { public List<DroolsParserException> errors = new ArrayList<DroolsParserException>(); public LinkedList<DroolsSentence> editorInterface = null; public boolean isEditorInterfaceEnabled = false; public boolean lookaheadTest = false; private Stack<Map<DroolsParaphraseTypes, String>> paraphrases = new Stack<Map<DroolsParaphraseTypes, String>>(); // parameters from parser private DRLParser parser = null; private DroolsParserExceptionFactory errorMessageFactory = null; private TokenStream input = null; private RecognizerSharedState state = null; public ParserHelper(DRLParser parser, String[] tokenNames, TokenStream input, RecognizerSharedState state) { this.parser = parser; this.errorMessageFactory = new DroolsParserExceptionFactory( tokenNames, paraphrases ); this.input = input; this.state = state; } public LinkedList<DroolsSentence> getEditorInterface() { return editorInterface; } public void enableEditorInterface() { isEditorInterfaceEnabled = true; } public void disableEditorInterface() { isEditorInterfaceEnabled = false; } public void beginSentence(DroolsSentenceType sentenceType) { if ( isEditorInterfaceEnabled ) { if ( null == editorInterface ) { editorInterface = new LinkedList<DroolsSentence>(); } DroolsSentence sentence = new DroolsSentence(); sentence.setType( sentenceType ); editorInterface.add( sentence ); } } public DroolsSentence getActiveSentence() { return editorInterface.getLast(); } public void emit(List< ? > tokens, DroolsEditorType editorType) { if ( isEditorInterfaceEnabled && tokens != null ) { for ( Object activeObject : tokens ) { emit( (Token) activeObject, editorType ); } } } public void emit(Token token, DroolsEditorType editorType) { if ( isEditorInterfaceEnabled && token != null ) { ((DroolsToken) token).setEditorType( editorType ); getActiveSentence().addContent( (DroolsToken) token ); } } public void emit(boolean forceEmit, int activeContext) { if ( isEditorInterfaceEnabled ) { getActiveSentence().addContent( activeContext ); } } public void emit(int activeContext) { if ( isEditorInterfaceEnabled ) { emit( false, activeContext ); } } public DroolsToken getLastTokenOnList(LinkedList< ? > list) { DroolsToken lastToken = null; for ( Object object : list ) { if ( object instanceof DroolsToken ) { lastToken = (DroolsToken) object; } } return lastToken; } public int getLastIntegerValue(LinkedList< ? > list) { int lastIntergerValue = -1; for ( Object object : list ) { if ( object instanceof Integer ) { lastIntergerValue = (Integer) object; } } return lastIntergerValue; } public String retrieveLT(int LTNumber) { if ( null == input ) return null; if ( null == input.LT( LTNumber ) ) return null; if ( null == input.LT( LTNumber ).getText() ) return null; return input.LT( LTNumber ).getText(); } public boolean validateLT(int LTNumber, String text) { String text2Validate = retrieveLT( LTNumber ); return text2Validate == null ? false : text2Validate.equalsIgnoreCase( text ); } public boolean isPluggableEvaluator(int offset, boolean negated) { String text2Validate = retrieveLT( offset ); return text2Validate == null ? false : DroolsSoftKeywords.isOperator( text2Validate, negated ); } public boolean isPluggableEvaluator(boolean negated) { return isPluggableEvaluator( 1, negated ); } public boolean validateIdentifierKey(String text) { return validateLT( 1, text ); } public boolean validateSpecialID(int index) { return validateLT( index, DroolsSoftKeywords.THIS ) || validateLT( index, DroolsSoftKeywords.SUPER ) || validateLT( index, DroolsSoftKeywords.NEW ) || validateLT( index, DroolsSoftKeywords.CLASS ); } public boolean validateIdentifierSufix() { return validateLT( 1, "[" ) || validateLT( 1, "(" ) || validateLT( 1, "<" ) || (validateLT( 1, "." ) && validateSpecialID( 2 )); } public void checkTrailingSemicolon(String text, Token token) { if ( text.trim().endsWith( ";" ) ) { errors.add( errorMessageFactory .createTrailingSemicolonException( ((DroolsToken) token) .getLine(), ((DroolsToken) token) .getCharPositionInLine(), ((DroolsToken) token) .getStopIndex() ) ); } } public boolean validateNotWithBinding() { if ( input.LA( 1 ) == ID && input.LA( 2 ) == ID && input.LA( 3 ) == COLON ) { return true; } return false; } public boolean validateRestr() { int lookahead = 2; int countParen = 1; while ( true ) { if ( input.LA( lookahead ) == COMMA ) { break; } else if ( input.LA( lookahead ) == LEFT_PAREN ) { countParen++; } else if ( input.LA( lookahead ) == RIGHT_PAREN ) { countParen--; } else if ( input.LA( lookahead ) == EOF ) { break; } if ( countParen == 0 ) { break; } lookahead++; } boolean returnValue = false; int activeIndex = input.index(); lookaheadTest = true; try { input.seek( input.LT( 2 ).getTokenIndex() ); parser.constraint_expression(); returnValue = true; } catch ( RecognitionException e ) { } finally { input.seek( activeIndex ); } lookaheadTest = false; return returnValue; } public String safeSubstring(String text, int start, int end) { return text.substring( Math.min( start, text.length() ), Math.min( Math .max( start, end ), text.length() ) ); } public void reportError(RecognitionException ex) { // if we've already reported an error and have not matched a token // yet successfully, don't report any errors. if ( state.errorRecovery ) { return; } state.errorRecovery = true; errors.add( errorMessageFactory.createDroolsException( ex ) ); } /** return the raw DroolsParserException errors */ public List<DroolsParserException> getErrors() { return errors; } /** Return a list of pretty strings summarising the errors */ public List<String> getErrorMessages() { List<String> messages = new ArrayList<String>( errors.size() ); for ( DroolsParserException activeException : errors ) { messages.add( activeException.getMessage() ); } return messages; } /** return true if any parser errors were accumulated */ public boolean hasErrors() { return !errors.isEmpty(); } /** * Method that adds a paraphrase type into paraphrases stack. * * @param type * paraphrase type */ public void pushParaphrases(DroolsParaphraseTypes type) { Map<DroolsParaphraseTypes, String> activeMap = new HashMap<DroolsParaphraseTypes, String>(); activeMap.put( type, "" ); paraphrases.push( activeMap ); } public Map<DroolsParaphraseTypes, String> popParaphrases() { return paraphrases.pop(); } /** * Method that sets paraphrase value for a type into paraphrases stack. * * @param type * paraphrase type * @param value * paraphrase value */ public void setParaphrasesValue(DroolsParaphraseTypes type, String value) { paraphrases.peek().put( type, value ); } /** * Helper method that creates a string from a token list. * * @param tokenList * token list * @return string */ public String buildStringFromTokens(List<Token> tokenList) { StringBuilder sb = new StringBuilder(); if ( null != tokenList ) { for ( Token activeToken : tokenList ) { if ( null != activeToken ) { sb.append( activeToken.getText() ); } } } return sb.toString(); } /** Overrided this method to not output mesages */ public void emitErrorMessage(String msg) { } // --------------------------------------------------------------------------------- // COPIED FROM: http://www.antlr.org/wiki/display/ANTLR3/Custom+Syntax+Error+Recovery // --------------------------------------------------------------------------------- /** * Use the current stacked followset to work out the valid tokens that * can follow on from the current point in the parse, then recover by * eating tokens that are not a member of the follow set we compute. * * This method is used whenever we wish to force a sync, even though * the parser has not yet checked LA(1) for alt selection. This is useful * in situations where only a subset of tokens can begin a new construct * (such as the start of a new statement in a block) and we want to * proactively detect garbage so that the current rule does not exit on * on an exception. * * We could override recover() to make this the default behavior but that * is too much like using a sledge hammer to crack a nut. We want finer * grained control of the recovery and error mechanisms. */ protected void syncToSet() { // Compute the followset that is in context wherever we are in the // rule chain/stack // BitSet follow = state.following[state._fsp]; //computeContextSensitiveRuleFOLLOW(); syncToSet(follow); } protected void syncToSet(BitSet follow) { int mark = -1; try { mark = input.mark(); // Consume all tokens in the stream until we find a member of the follow // set, which means the next production should be guaranteed to be happy. // while (! memberOfFollowSet( follow ) ) { if (input.LA(1) == Token.EOF) { // Looks like we didn't find anything at all that can help us here // so we need to rewind to where we were and let normal error handling // bail out. // input.rewind(); mark = -1; return; } reportError( new MismatchedSetException(follow, input) ); input.consume(); // Now here, because you are consuming some tokens, yu will probably want // to raise an error message such as "Spurious elements after the class member were discarded" // using whatever your override of displayRecognitionError() routine does to record // error messages. The exact error my depend on context etc. // } } catch (Exception e) { // Just ignore any errors here, we will just let the recognizer // try to resync as normal - something must be very screwed. // e.printStackTrace(); } finally { // Always release the mark we took // if (mark != -1) { input.release(mark); } } } private boolean memberOfFollowSet(BitSet follow) { boolean isMember = follow.member(input.LA(1)); if( input.LA( 1 ) == DRLParser.ID ) { String token = input.LT( 1 ).getText(); isMember = ( DroolsSoftKeywords.IMPORT.equals( token ) || DroolsSoftKeywords.GLOBAL.equals( token ) || DroolsSoftKeywords.FUNCTION.equals( token ) || DroolsSoftKeywords.DECLARE.equals( token ) || DroolsSoftKeywords.RULE.equals( token ) || DroolsSoftKeywords.QUERY.equals( token ) || DroolsSoftKeywords.SALIENCE.equals( token ) || DroolsSoftKeywords.NO.equals( token ) || DroolsSoftKeywords.AGENDA.equals( token ) || DroolsSoftKeywords.TIMER.equals( token ) || DroolsSoftKeywords.ACTIVATION.equals( token ) || DroolsSoftKeywords.AUTO.equals( token ) || DroolsSoftKeywords.DATE.equals( token ) || DroolsSoftKeywords.ENABLED.equals( token ) || DroolsSoftKeywords.RULEFLOW.equals( token ) || DroolsSoftKeywords.DIALECT.equals( token ) || DroolsSoftKeywords.CALENDARS.equals( token ) ); } return isMember; } // --------------------------------------------------------------------------------- // END COPIED FROM: http://www.antlr.org/wiki/display/ANTLR3/Custom+Syntax+Error+Recovery // --------------------------------------------------------------------------------- }