/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drools.compiler.lang; import org.antlr.runtime.CommonToken; import org.antlr.runtime.MismatchedTokenException; import org.antlr.runtime.MissingTokenException; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.Token; import org.antlr.runtime.TokenStream; import org.antlr.runtime.UnwantedTokenException; import org.drools.compiler.lang.api.AbstractClassTypeDeclarationBuilder; import org.drools.compiler.lang.api.AccumulateDescrBuilder; import org.drools.compiler.lang.api.AnnotatedDescrBuilder; import org.drools.compiler.lang.api.AnnotationDescrBuilder; import org.drools.compiler.lang.api.AttributeDescrBuilder; import org.drools.compiler.lang.api.AttributeSupportBuilder; import org.drools.compiler.lang.api.BehaviorDescrBuilder; import org.drools.compiler.lang.api.CEDescrBuilder; import org.drools.compiler.lang.api.CollectDescrBuilder; import org.drools.compiler.lang.api.ConditionalBranchDescrBuilder; import org.drools.compiler.lang.api.DeclareDescrBuilder; import org.drools.compiler.lang.api.DescrBuilder; import org.drools.compiler.lang.api.EntryPointDeclarationDescrBuilder; import org.drools.compiler.lang.api.EnumDeclarationDescrBuilder; import org.drools.compiler.lang.api.EnumLiteralDescrBuilder; import org.drools.compiler.lang.api.EvalDescrBuilder; import org.drools.compiler.lang.api.FieldDescrBuilder; import org.drools.compiler.lang.api.ForallDescrBuilder; import org.drools.compiler.lang.api.FunctionDescrBuilder; import org.drools.compiler.lang.api.GlobalDescrBuilder; import org.drools.compiler.lang.api.ImportDescrBuilder; import org.drools.compiler.lang.api.NamedConsequenceDescrBuilder; import org.drools.compiler.lang.api.PackageDescrBuilder; import org.drools.compiler.lang.api.ParameterSupportBuilder; import org.drools.compiler.lang.api.PatternContainerDescrBuilder; import org.drools.compiler.lang.api.PatternDescrBuilder; import org.drools.compiler.lang.api.QueryDescrBuilder; import org.drools.compiler.lang.api.RuleDescrBuilder; import org.drools.compiler.lang.api.TypeDeclarationDescrBuilder; import org.drools.compiler.lang.api.WindowDeclarationDescrBuilder; import org.drools.compiler.lang.descr.AndDescr; import org.drools.compiler.lang.descr.AttributeDescr; import org.drools.compiler.lang.descr.BaseDescr; import org.drools.compiler.lang.descr.ConditionalElementDescr; import org.drools.compiler.lang.descr.EntryPointDeclarationDescr; import org.drools.compiler.lang.descr.EnumDeclarationDescr; import org.drools.compiler.lang.descr.ExistsDescr; import org.drools.compiler.lang.descr.FunctionDescr; import org.drools.compiler.lang.descr.GlobalDescr; import org.drools.compiler.lang.descr.ImportDescr; import org.drools.compiler.lang.descr.NotDescr; import org.drools.compiler.lang.descr.OrDescr; import org.drools.compiler.lang.descr.PackageDescr; import org.drools.compiler.lang.descr.RuleDescr; import org.drools.compiler.lang.descr.TypeDeclarationDescr; import org.drools.compiler.lang.descr.WindowDeclarationDescr; import org.drools.core.util.StringUtils; import org.kie.internal.builder.conf.LanguageLevelOption; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; public class DRL5Parser extends AbstractDRLParser implements DRLParser { private final DRL5Expressions exprParser; public DRL5Parser(TokenStream input) { super(input); this.exprParser = new DRL5Expressions( input, state, helper ); } protected LanguageLevelOption getLanguageLevel() { return LanguageLevelOption.DRL5; } /* ------------------------------------------------------------------------------------------------ * GRAMMAR RULES * ------------------------------------------------------------------------------------------------ */ protected final PackageDescr compilationUnit(PackageDescrBuilder pkg) throws RecognitionException { try { // package declaration? if ( input.LA( 1 ) != DRL5Lexer.EOF && helper.validateIdentifierKey( DroolsSoftKeywords.PACKAGE ) ) { String pkgName = packageStatement( pkg ); pkg.name( pkgName ); if ( state.failed ) return pkg.getDescr(); } // statements while ( input.LA( 1 ) != DRL5Lexer.EOF ) { int next = input.index(); if ( helper.validateStatement( 1 ) ) { statement( pkg ); if ( state.failed ) return pkg.getDescr(); if ( next == input.index() ) { // no token consumed, so, report problem: resyncToNextStatement(); } } else { resyncToNextStatement(); } if ( input.LA( 1 ) == DRL5Lexer.SEMICOLON ) { match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return pkg.getDescr(); } } } catch ( RecognitionException e ) { helper.reportError( e ); } catch ( Exception e ) { helper.reportError( e ); } finally { helper.setEnd( pkg ); } return pkg.getDescr(); } private void resyncToNextStatement() { helper.reportError( new DroolsMismatchedSetException( helper.getStatementKeywords(), input ) ); do { // error recovery: look for the next statement, skipping all tokens until then input.consume(); } while ( input.LA( 1 ) != DRL5Lexer.EOF && !helper.validateStatement( 1 ) ); } /** * Parses a package statement and returns the name of the package * or null if none is defined. * * packageStatement := PACKAGE qualifiedIdentifier SEMICOLON? * * @return the name of the package or null if none is defined */ public String packageStatement( PackageDescrBuilder pkg ) throws RecognitionException { String pkgName = null; try { helper.start( pkg, PackageDescrBuilder.class, null ); match( input, DRL5Lexer.ID, DroolsSoftKeywords.PACKAGE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return pkgName; pkgName = qualifiedIdentifier(); if ( state.failed ) return pkgName; if ( state.backtracking == 0 ) { helper.setParaphrasesValue( DroolsParaphraseTypes.PACKAGE, pkgName ); } if ( input.LA( 1 ) == DRL5Lexer.SEMICOLON ) { match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return pkgName; } } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( PackageDescrBuilder.class, pkg ); } return pkgName; } /** * statement := importStatement * | globalStatement * | declare * | rule * | ruleAttribute * | function * | query * ; * * @throws RecognitionException */ public BaseDescr statement( PackageDescrBuilder pkg ) throws RecognitionException { BaseDescr descr = null; try { if ( helper.validateIdentifierKey( DroolsSoftKeywords.IMPORT ) ) { descr = importStatement( pkg ); if ( state.failed ) return descr; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.GLOBAL ) ) { descr = globalStatement( pkg ); if ( state.failed ) return descr; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.DECLARE ) ) { descr = declare( pkg ); if ( state.failed ) return descr; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.RULE ) ) { descr = rule( pkg ); if ( state.failed ) return descr; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.QUERY ) ) { descr = query( pkg ); if ( state.failed ) return descr; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.FUNCTION ) ) { descr = function( pkg ); if ( state.failed ) return descr; } else if ( helper.validateAttribute( 1 ) ) { descr = attribute( pkg ); if ( state.failed ) return descr; } } catch ( RecognitionException e ) { helper.reportError( e ); } catch ( Exception e ) { helper.reportError( e ); } return descr; } /* ------------------------------------------------------------------------------------------------ * IMPORT STATEMENT * ------------------------------------------------------------------------------------------------ */ /** * importStatement := IMPORT (FUNCTION|STATIC)? qualifiedIdentifier (DOT STAR)? * * @return * @throws RecognitionException */ public ImportDescr importStatement( PackageDescrBuilder pkg ) throws RecognitionException { ImportDescrBuilder imp = null; try { imp = helper.start( pkg, ImportDescrBuilder.class, null ); // import match( input, DRL5Lexer.ID, DroolsSoftKeywords.IMPORT, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; String kwd; if ( helper.validateIdentifierKey( kwd = DroolsSoftKeywords.FUNCTION ) || helper.validateIdentifierKey( kwd = DroolsSoftKeywords.STATIC ) ) { // function match( input, DRL5Lexer.ID, kwd, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } // qualifiedIdentifier String target = qualifiedIdentifier(); if ( state.failed ) return null; if ( input.LA( 1 ) == DRL5Lexer.DOT && input.LA( 2 ) == DRL5Lexer.STAR ) { // .* match( input, DRL5Lexer.DOT, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return null; match( input, DRL5Lexer.STAR, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return null; target += ".*"; } if ( state.backtracking == 0 ) imp.target( target ); } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( ImportDescrBuilder.class, imp ); } return (imp != null) ? imp.getDescr() : null; } /* ------------------------------------------------------------------------------------------------ * GLOBAL STATEMENT * ------------------------------------------------------------------------------------------------ */ /** * globalStatement := GLOBAL type ID * * @return * @throws RecognitionException */ public GlobalDescr globalStatement( PackageDescrBuilder pkg ) throws RecognitionException { GlobalDescrBuilder global = null; try { global = helper.start( pkg, GlobalDescrBuilder.class, null ); // 'global' match( input, DRL5Lexer.ID, DroolsSoftKeywords.GLOBAL, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; // type String type = type(); if ( state.backtracking == 0 ) global.type( type ); if ( state.failed ) return null; // identifier Token id = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.IDENTIFIER_TYPE ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { global.identifier( id.getText() ); helper.setParaphrasesValue( DroolsParaphraseTypes.GLOBAL, id.getText() ); } } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( GlobalDescrBuilder.class, global ); } return (global != null) ? global.getDescr() : null; } /* ------------------------------------------------------------------------------------------------ * DECLARE STATEMENT * ------------------------------------------------------------------------------------------------ */ /** * declare := DECLARE * | (ENTRY-POINT) => entryPointDeclaration * | (WINDOW) => windowDeclaration * | (TRAIT) => typeDeclaration (trait) * | (ENUM) => enumDeclaration * | typeDeclaration (class) * END * * @return * @throws RecognitionException */ public BaseDescr declare( PackageDescrBuilder pkg ) throws RecognitionException { BaseDescr declaration = null; try { DeclareDescrBuilder declare = helper.start( pkg, DeclareDescrBuilder.class, null ); // 'declare' match( input, DRL5Lexer.ID, DroolsSoftKeywords.DECLARE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( helper.validateIdentifierKey( DroolsSoftKeywords.ENTRY ) ) { // entry point declaration declaration = entryPointDeclaration( declare ); } else if( helper.validateIdentifierKey( DroolsSoftKeywords.WINDOW ) ) { // window declaration declaration = windowDeclaration( declare ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.TRAIT ) ) { // trait type declaration // 'trait' match( input, DRL5Lexer.ID, DroolsSoftKeywords.TRAIT, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; declaration = typeDeclaration( declare, true ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.ENUM ) ) { match( input, DRL5Lexer.ID, DroolsSoftKeywords.ENUM, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; declaration = enumDeclaration( declare ); } else { // class type declaration declaration = typeDeclaration( declare, false ); } } catch ( RecognitionException re ) { reportError( re ); } return declaration; } /** * entryPointDeclaration := ENTRY-POINT stringId annotation* END * * @return * @throws RecognitionException */ public EntryPointDeclarationDescr entryPointDeclaration( DeclareDescrBuilder ddb ) throws RecognitionException { EntryPointDeclarationDescrBuilder declare = null; try { declare = helper.start( ddb, EntryPointDeclarationDescrBuilder.class, null ); match( input, DRL5Lexer.ID, DroolsSoftKeywords.ENTRY, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; match( input, DRL5Lexer.MINUS, null, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; match( input, DRL5Lexer.ID, DroolsSoftKeywords.POINT, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; String ep = stringId(); if ( state.failed ) return null; if( state.backtracking == 0 ) { declare.entryPointId( ep ); } while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( declare ); if ( state.failed ) return null; } match( input, DRL5Lexer.ID, DroolsSoftKeywords.END, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( EntryPointDeclarationDescrBuilder.class, declare ); } return (declare != null) ? declare.getDescr() : null; } /** * windowDeclaration := WINDOW ID annotation* lhsPatternBind END * * @return * @throws RecognitionException */ public WindowDeclarationDescr windowDeclaration( DeclareDescrBuilder ddb ) throws RecognitionException { WindowDeclarationDescrBuilder declare = null; try { declare = helper.start( ddb, WindowDeclarationDescrBuilder.class, null ); String window = ""; match( input, DRL5Lexer.ID, DroolsSoftKeywords.WINDOW, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; Token id = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return null; window = id.getText(); if( state.backtracking == 0 ) { declare.name( window ); } while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( declare ); if ( state.failed ) return null; } lhsPatternBind( declare, false ); match( input, DRL5Lexer.ID, DroolsSoftKeywords.END, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( WindowDeclarationDescrBuilder.class, declare ); } return (declare != null) ? declare.getDescr() : null; } /* * typeDeclaration := [ENUM] qualifiedIdentifier * annotation* * enumerative+ * field* * END * * @return * @throws RecognitionException */ public EnumDeclarationDescr enumDeclaration( DeclareDescrBuilder ddb ) throws RecognitionException { EnumDeclarationDescrBuilder declare = null; try { declare = helper.start( ddb, EnumDeclarationDescrBuilder.class, null ); // type may be qualified when adding metadata String type = qualifiedIdentifier(); if ( state.failed ) return null; if ( state.backtracking == 0 ) declare.name( type ); while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( declare ); if ( state.failed ) return null; } while ( input.LA( 1 ) == DRL5Lexer.ID ) { int next = input.LA( 2 ); if ( next == DRL5Lexer.LEFT_PAREN || next == DRL5Lexer.COMMA || next == DRL5Lexer.SEMICOLON ) { enumerative( declare ); if ( state.failed ) return null; } if ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); } else { match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); break; } } //boolean qualified = type.indexOf( '.' ) >= 0; while ( //! qualified && input.LA( 1 ) == DRL5Lexer.ID && ! helper.validateIdentifierKey( DroolsSoftKeywords.END ) ) { // field* field( declare ); if ( state.failed ) return null; } match( input, DRL5Lexer.ID, DroolsSoftKeywords.END, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( TypeDeclarationDescrBuilder.class, declare ); } return (declare != null) ? declare.getDescr() : null; } /** * typeDeclaration := [TYPE] qualifiedIdentifier (EXTENDS qualifiedIdentifier)? * annotation* * field* * END * * @return * @throws RecognitionException */ public TypeDeclarationDescr typeDeclaration( DeclareDescrBuilder ddb, boolean isTrait ) throws RecognitionException { TypeDeclarationDescrBuilder declare = null; try { declare = helper.start( ddb, TypeDeclarationDescrBuilder.class, null ); declare.setTrait(isTrait); if( helper.validateIdentifierKey( DroolsSoftKeywords.TYPE ) ) { // 'type' match( input, DRL5Lexer.ID, DroolsSoftKeywords.TYPE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } // type may be qualified when adding metadata String type = qualifiedIdentifier(); if ( state.failed ) return null; if ( state.backtracking == 0 ) declare.name( type ); if ( helper.validateIdentifierKey( DroolsSoftKeywords.EXTENDS ) ) { match( input, DRL5Lexer.ID, DroolsSoftKeywords.EXTENDS, null, DroolsEditorType.KEYWORD ); if ( !state.failed ) { // Going for type includes generics, which is a no-no (JIRA-3040) String superType = qualifiedIdentifier(); declare.superType( superType ); while ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); superType = qualifiedIdentifier(); declare.superType( superType ); } } } while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( declare ); if ( state.failed ) return null; } //boolean qualified = type.indexOf( '.' ) >= 0; while ( //! qualified && input.LA( 1 ) == DRL5Lexer.ID && !helper.validateIdentifierKey( DroolsSoftKeywords.END ) ) { // field* field( declare ); if ( state.failed ) return null; } match( input, DRL5Lexer.ID, DroolsSoftKeywords.END, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( TypeDeclarationDescrBuilder.class, declare ); } return (declare != null) ? declare.getDescr() : null; } /** * enumerative := ID ( LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN )? */ private void enumerative( EnumDeclarationDescrBuilder declare ) { EnumLiteralDescrBuilder literal = null; String lit = null; try { Token enumLit = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.IDENTIFIER ); lit = enumLit.getText(); if ( state.failed ) return; } catch ( RecognitionException re ) { reportError( re ); } try { literal = helper.start( declare, EnumLiteralDescrBuilder.class, lit ); if ( input.LA( 1 ) == DRL5Lexer.LEFT_PAREN ) { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; boolean more; do { int first = input.index(); exprParser.conditionalExpression(); if ( state.failed ) return; if ( state.backtracking == 0 && input.index() > first ) { // expression consumed something String arg = input.toString( first, input.LT( -1 ).getTokenIndex() ); literal.constructorArg( arg ); } more = input.LA( 1 ) == DRL5Lexer.COMMA; if ( more ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); } } while ( more ); match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( FieldDescrBuilder.class, literal ); } } /** * field := label fieldType (EQUALS_ASSIGN conditionalExpression)? annotation* SEMICOLON? */ private void field( AbstractClassTypeDeclarationBuilder declare ) { FieldDescrBuilder field = null; String fname = null; try { fname = label( DroolsEditorType.IDENTIFIER ); if ( state.failed ) return; } catch ( RecognitionException re ) { reportError( re ); } try { field = helper.start( declare, FieldDescrBuilder.class, fname ); // type String type = type(); if ( state.failed ) return; if ( state.backtracking == 0 ) field.type( type ); if ( input.LA( 1 ) == DRL5Lexer.EQUALS_ASSIGN ) { // EQUALS_ASSIGN match( input, DRL5Lexer.EQUALS_ASSIGN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; int first = input.index(); exprParser.conditionalExpression(); if ( state.failed ) return; if ( state.backtracking == 0 && input.index() > first ) { // expression consumed something String value = input.toString( first, input.LT( -1 ).getTokenIndex() ); field.initialValue( value ); } } while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( field ); if ( state.failed ) return; } if ( input.LA( 1 ) == DRL5Lexer.SEMICOLON ) { match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( FieldDescrBuilder.class, field ); } } /* ------------------------------------------------------------------------------------------------ * FUNCTION STATEMENT * ------------------------------------------------------------------------------------------------ */ /** * function := FUNCTION type? ID parameters(typed) chunk_{_} * * @return * @throws RecognitionException */ public FunctionDescr function( PackageDescrBuilder pkg ) throws RecognitionException { FunctionDescrBuilder function = null; try { function = helper.start( pkg, FunctionDescrBuilder.class, null ); // 'function' match( input, DRL5Lexer.ID, DroolsSoftKeywords.FUNCTION, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( input.LA( 1 ) != DRL5Lexer.ID || input.LA( 2 ) != DRL5Lexer.LEFT_PAREN ) { // type String type = type(); if ( state.failed ) return null; if ( state.backtracking == 0 ) function.returnType( type ); } // name Token id = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { function.name( id.getText() ); helper.setParaphrasesValue( DroolsParaphraseTypes.FUNCTION, "\"" + id.getText() + "\"" ); } // arguments parameters( function, true ); if ( state.failed ) return null; // body String body = chunk( DRL5Lexer.LEFT_CURLY, DRL5Lexer.RIGHT_CURLY, -1 ); if ( state.failed ) return null; if ( state.backtracking == 0 ) function.body( body ); } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( FunctionDescrBuilder.class, function ); } return (function != null) ? function.getDescr() : null; } /** * parameters := LEFT_PAREN ( parameter ( COMMA parameter )* )? RIGHT_PAREN * @param statement * @param requiresType * @throws RecognitionException */ private void parameters( ParameterSupportBuilder< ? > statement, boolean requiresType ) throws RecognitionException { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; if ( input.LA( 1 ) != DRL5Lexer.RIGHT_PAREN ) { parameter( statement, requiresType ); if ( state.failed ) return; while ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; parameter( statement, requiresType ); if ( state.failed ) return; } } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } /** * parameter := ({requiresType}?=>type)? ID (LEFT_SQUARE RIGHT_SQUARE)* * @param statement * @param requiresType * @throws RecognitionException */ private void parameter( ParameterSupportBuilder< ? > statement, boolean requiresType ) throws RecognitionException { String type = "Object"; if ( requiresType ) { type = type(); if ( state.failed ) return; } int start = input.index(); match( input, DRL5Lexer.ID, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return; while ( input.LA( 1 ) == DRL5Lexer.LEFT_SQUARE ) { match( input, DRL5Lexer.LEFT_SQUARE, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; match( input, DRL5Lexer.RIGHT_SQUARE, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } int end = input.LT( -1 ).getTokenIndex(); if ( state.backtracking == 0 ) statement.parameter( type, input.toString( start, end ) ); } /* ------------------------------------------------------------------------------------------------ * QUERY STATEMENT * ------------------------------------------------------------------------------------------------ */ /** * query := QUERY stringId parameters? annotation* lhsExpression END * * @return * @throws RecognitionException */ public RuleDescr query( PackageDescrBuilder pkg ) throws RecognitionException { QueryDescrBuilder query = null; try { query = helper.start( pkg, QueryDescrBuilder.class, null ); // 'query' match( input, DRL5Lexer.ID, DroolsSoftKeywords.QUERY, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( helper.validateIdentifierKey( DroolsSoftKeywords.WHEN ) || helper.validateIdentifierKey( DroolsSoftKeywords.THEN ) || helper.validateIdentifierKey( DroolsSoftKeywords.END ) ) { failMissingTokenException(); return null; // in case it is backtracking } String name = stringId(); if ( state.backtracking == 0 ) query.name( name ); if ( state.failed ) return null; if ( state.backtracking == 0) { helper.emit( Location.LOCATION_RULE_HEADER ); } if ( speculateParameters( true ) ) { // parameters parameters( query, true ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } else if ( speculateParameters( false ) ) { // parameters parameters( query, false ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( query ); if ( state.failed ) return null; } if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } if (input.LA( 1 ) != DRL5Lexer.EOF ){ lhsExpression( query != null ? query.lhs() : null ); } match( input, DRL5Lexer.ID, DroolsSoftKeywords.END, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; helper.emit( Location.LOCATION_RHS ); } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( QueryDescrBuilder.class, query ); } return (query != null) ? query.getDescr() : null; } private boolean speculateParameters( boolean requiresType ) { state.backtracking++; int start = input.mark(); try { parameters( null, requiresType ); // can never throw exception } catch ( RecognitionException re ) { System.err.println( "impossible: " + re ); re.printStackTrace(); } boolean success = !state.failed; input.rewind( start ); state.backtracking--; state.failed = false; return success; } /* ------------------------------------------------------------------------------------------------ * RULE STATEMENT * ------------------------------------------------------------------------------------------------ */ /** * rule := RULE stringId (EXTENDS stringId)? annotation* attributes? lhs? rhs END * * @return * @throws RecognitionException */ public RuleDescr rule( PackageDescrBuilder pkg ) throws RecognitionException { RuleDescrBuilder rule = null; try { rule = helper.start( pkg, RuleDescrBuilder.class, null ); // 'rule' match( input, DRL5Lexer.ID, DroolsSoftKeywords.RULE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( helper.validateIdentifierKey( DroolsSoftKeywords.WHEN ) || helper.validateIdentifierKey( DroolsSoftKeywords.THEN ) || helper.validateIdentifierKey( DroolsSoftKeywords.END ) ) { failMissingTokenException(); return null; // in case it is backtracking } String name = stringId(); if ( state.failed ) return null; if ( state.backtracking == 0 ) { rule.name( name ); helper.setParaphrasesValue( DroolsParaphraseTypes.RULE, "\"" + name + "\"" ); helper.emit( Location.LOCATION_RULE_HEADER ); } if ( helper.validateIdentifierKey( DroolsSoftKeywords.EXTENDS ) ) { // 'extends' match( input, DRL5Lexer.ID, DroolsSoftKeywords.EXTENDS, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; String parent = stringId(); if ( state.backtracking == 0 ) rule.extendsRule( parent ); if ( state.failed ) return null; } if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_RULE_HEADER ); } while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( rule ); if ( state.failed ) return null; } attributes( rule ); if ( helper.validateIdentifierKey( DroolsSoftKeywords.WHEN ) ) { lhs( rule ); } else { // creates an empty LHS rule.lhs(); } rhs( rule ); match( input, DRL5Lexer.ID, DroolsSoftKeywords.END, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } catch ( RecognitionException re ) { reportError( re ); } finally { helper.end( RuleDescrBuilder.class, rule ); } return (rule != null) ? rule.getDescr() : null; } /** * stringId := ( ID | STRING ) * @return * @throws RecognitionException */ private String stringId() throws RecognitionException { if ( input.LA( 1 ) == DRL5Lexer.ID ) { Token id = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return null; return id.getText(); } else if ( input.LA( 1 ) == DRL5Lexer.STRING ) { Token id = match( input, DRL5Lexer.STRING, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return null; return StringUtils.unescapeJava( safeStripStringDelimiters( id.getText() ) ); } else { throw new MismatchedTokenException( DRL5Lexer.ID, input ); } } /** * attributes := (ATTRIBUTES COLON?)? [ attribute ( COMMA? attribute )* ] * @param rule * @throws RecognitionException */ private void attributes( RuleDescrBuilder rule ) throws RecognitionException { if ( helper.validateIdentifierKey( DroolsSoftKeywords.ATTRIBUTES ) ) { match( input, DRL5Lexer.ID, DroolsSoftKeywords.ATTRIBUTES, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return; if ( input.LA( 1 ) == DRL5Lexer.COLON ) { match( input, DRL5Lexer.COLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } } if ( helper.validateAttribute( 1 ) ) { attribute( rule ); if ( state.failed ) return; while ( input.LA( 1 ) == DRL5Lexer.COMMA || helper.validateAttribute( 1 ) ) { if ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } attribute( rule ); if ( state.failed ) return; } } } /** * attribute := * salience * | enabled * | ( NO-LOOP * | AUTO-FOCUS * | LOCK-ON-ACTIVE * | REFRACT * ) BOOLEAN? * | ( AGENDA-GROUP * | ACTIVATION-GROUP * | RULEFLOW-GROUP * | DATE-EFFECTIVE * | DATE-EXPIRES * | DIALECT * ) STRING * | CALENDARS STRING (COMMA STRING)* * | TIMER ( DECIMAL | chunk_(_) ) * | DURATION ( DECIMAL | chunk_(_) ) * * The above syntax is not quite how this is parsed, because the soft keyword * is determined by look-ahead and passed on to one of the x-Attribute methods * (booleanAttribute, stringAttribute, stringListAttribute, intOrChunkAttribute) * which will actually gobble the tokens. * * @return */ public AttributeDescr attribute( AttributeSupportBuilder< ? > as ) { AttributeDescr attribute = null; try { if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_RULE_HEADER_KEYWORD ); } if ( helper.validateIdentifierKey( DroolsSoftKeywords.SALIENCE ) ) { attribute = salience( as ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.ENABLED ) ) { attribute = enabled( as ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.NO ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.LOOP ) ) { attribute = booleanAttribute( as, new String[]{DroolsSoftKeywords.NO, "-", DroolsSoftKeywords.LOOP} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.AUTO ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.FOCUS ) ) { attribute = booleanAttribute( as, new String[]{DroolsSoftKeywords.AUTO, "-", DroolsSoftKeywords.FOCUS} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.LOCK ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.ON ) && helper.validateLT( 4, "-" ) && helper.validateLT( 5, DroolsSoftKeywords.ACTIVE ) ) { attribute = booleanAttribute( as, new String[]{DroolsSoftKeywords.LOCK, "-", DroolsSoftKeywords.ON, "-", DroolsSoftKeywords.ACTIVE} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.REFRACT ) ) { attribute = booleanAttribute( as, new String[]{ DroolsSoftKeywords.REFRACT } ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.AGENDA ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.GROUP ) ) { attribute = stringAttribute( as, new String[]{DroolsSoftKeywords.AGENDA, "-", DroolsSoftKeywords.GROUP} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.ACTIVATION ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.GROUP ) ) { attribute = stringAttribute( as, new String[]{DroolsSoftKeywords.ACTIVATION, "-", DroolsSoftKeywords.GROUP} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.RULEFLOW ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.GROUP ) ) { attribute = stringAttribute( as, new String[]{DroolsSoftKeywords.RULEFLOW, "-", DroolsSoftKeywords.GROUP} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.DATE ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.EFFECTIVE ) ) { attribute = stringAttribute( as, new String[]{DroolsSoftKeywords.DATE, "-", DroolsSoftKeywords.EFFECTIVE} ); attribute.setType( AttributeDescr.Type.DATE ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.DATE ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.EXPIRES ) ) { attribute = stringAttribute( as, new String[]{DroolsSoftKeywords.DATE, "-", DroolsSoftKeywords.EXPIRES} ); attribute.setType( AttributeDescr.Type.DATE ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.DIALECT ) ) { attribute = stringAttribute( as, new String[]{DroolsSoftKeywords.DIALECT} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.CALENDARS ) ) { attribute = stringListAttribute( as, new String[]{DroolsSoftKeywords.CALENDARS} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.TIMER ) ) { attribute = intOrChunkAttribute( as, new String[]{DroolsSoftKeywords.TIMER} ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.DURATION ) ) { attribute = intOrChunkAttribute( as, new String[]{DroolsSoftKeywords.DURATION} ); } if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_RULE_HEADER ); } } catch ( RecognitionException re ) { reportError( re ); } return attribute; } /** * salience := SALIENCE conditionalExpression * @throws RecognitionException */ private AttributeDescr salience( AttributeSupportBuilder< ? > as ) throws RecognitionException { AttributeDescrBuilder< ? > attribute = null; try { // 'salience' match( input, DRL5Lexer.ID, DroolsSoftKeywords.SALIENCE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { attribute = helper.start( (DescrBuilder< ? , ? >) as, AttributeDescrBuilder.class, DroolsSoftKeywords.SALIENCE ); } boolean hasParen = input.LA( 1 ) == DRL5Lexer.LEFT_PAREN; int first = input.index(); if ( hasParen ) { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } String value = conditionalExpression(); if ( state.failed ) return null; if ( hasParen ) { match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } if ( state.backtracking == 0 ) { if ( hasParen ) { value = input.toString( first, input.LT( -1 ).getTokenIndex() ); } attribute.value( value ); attribute.type( AttributeDescr.Type.EXPRESSION ); } } finally { if ( attribute != null ) { helper.end( AttributeDescrBuilder.class, attribute ); } } return attribute != null ? attribute.getDescr() : null; } /** * enabled := ENABLED conditionalExpression * @throws RecognitionException */ private AttributeDescr enabled( AttributeSupportBuilder< ? > as ) throws RecognitionException { AttributeDescrBuilder< ? > attribute = null; try { // 'enabled' match( input, DRL5Lexer.ID, DroolsSoftKeywords.ENABLED, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { attribute = helper.start( (DescrBuilder< ? , ? >) as, AttributeDescrBuilder.class, DroolsSoftKeywords.ENABLED ); } boolean hasParen = input.LA( 1 ) == DRL5Lexer.LEFT_PAREN; int first = input.index(); if ( hasParen ) { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } String value = conditionalExpression(); if ( state.failed ) return null; if ( hasParen ) { match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } if ( state.backtracking == 0 ) { if ( hasParen ) { value = input.toString( first, input.LT( -1 ).getTokenIndex() ); } attribute.value( value ); attribute.type( AttributeDescr.Type.EXPRESSION ); } } finally { if ( attribute != null ) { helper.end( AttributeDescrBuilder.class, attribute ); } } return attribute != null ? attribute.getDescr() : null; } /** * booleanAttribute := attributeKey (BOOLEAN)? * @param key * @throws RecognitionException */ private AttributeDescr booleanAttribute( AttributeSupportBuilder< ? > as, String[] key ) throws RecognitionException { AttributeDescrBuilder< ? > attribute = null; try { StringBuilder builder = new StringBuilder(); for ( String k : key ) { if ( "-".equals( k ) ) { match( input, DRL5Lexer.MINUS, k, null, DroolsEditorType.KEYWORD ); // part of the keyword if ( state.failed ) return null; } else { match( input, DRL5Lexer.ID, k, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } builder.append( k ); } if ( state.backtracking == 0 ) { attribute = helper.start( (DescrBuilder< ? , ? >) as, AttributeDescrBuilder.class, builder.toString() ); } String value = "true"; if ( input.LA( 1 ) == DRL5Lexer.BOOL ) { Token bool = match( input, DRL5Lexer.BOOL, null, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; value = bool.getText(); } if ( state.backtracking == 0 ) { attribute.value( value ); attribute.type( AttributeDescr.Type.BOOLEAN ); } } finally { if ( attribute != null ) { helper.end( AttributeDescrBuilder.class, attribute ); } } return attribute != null ? attribute.getDescr() : null; } /** * stringAttribute := attributeKey STRING * @param key * @throws RecognitionException */ private AttributeDescr stringAttribute( AttributeSupportBuilder< ? > as, String[] key ) throws RecognitionException { AttributeDescrBuilder< ? > attribute = null; try { StringBuilder builder = new StringBuilder(); for ( String k : key ) { if ( "-".equals( k ) ) { match( input, DRL5Lexer.MINUS, k, null, DroolsEditorType.KEYWORD ); // part of the keyword if ( state.failed ) return null; } else { match( input, DRL5Lexer.ID, k, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } builder.append( k ); } if ( state.backtracking == 0 ) { attribute = helper.start( (DescrBuilder< ? , ? >) as, AttributeDescrBuilder.class, builder.toString() ); } Token value = match( input, DRL5Lexer.STRING, null, null, DroolsEditorType.STRING_CONST ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { attribute.value( StringUtils.unescapeJava( safeStripStringDelimiters( value.getText() ) ) ); attribute.type( AttributeDescr.Type.STRING ); } } finally { if ( attribute != null ) { helper.end( AttributeDescrBuilder.class, attribute ); } } return attribute != null ? attribute.getDescr() : null; } /** * stringListAttribute := attributeKey STRING (COMMA STRING)* * @param key * @throws RecognitionException */ private AttributeDescr stringListAttribute( AttributeSupportBuilder< ? > as, String[] key ) throws RecognitionException { AttributeDescrBuilder< ? > attribute = null; try { StringBuilder builder = new StringBuilder(); for ( String k : key ) { if ( "-".equals( k ) ) { match( input, DRL5Lexer.MINUS, k, null, DroolsEditorType.KEYWORD ); // part of the keyword if ( state.failed ) return null; } else { match( input, DRL5Lexer.ID, k, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } builder.append( k ); } if ( state.backtracking == 0 ) { attribute = helper.start( (DescrBuilder< ? , ? >) as, AttributeDescrBuilder.class, builder.toString() ); } builder = new StringBuilder(); builder.append( "[ " ); Token value = match( input, DRL5Lexer.STRING, null, null, DroolsEditorType.STRING_CONST ); if ( state.failed ) return null; builder.append( value.getText() ); while ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; builder.append( ", " ); value = match( input, DRL5Lexer.STRING, null, null, DroolsEditorType.STRING_CONST ); if ( state.failed ) return null; builder.append( value.getText() ); } builder.append( " ]" ); if ( state.backtracking == 0 ) { attribute.value( builder.toString() ); attribute.type( AttributeDescr.Type.LIST ); } } finally { if ( attribute != null ) { helper.end( AttributeDescrBuilder.class, attribute ); } } return attribute != null ? attribute.getDescr() : null; } /** * intOrChunkAttribute := attributeKey ( DECIMAL | chunk_(_) ) * @param key * @throws RecognitionException */ private AttributeDescr intOrChunkAttribute( AttributeSupportBuilder< ? > as, String[] key ) throws RecognitionException { AttributeDescrBuilder< ? > attribute = null; try { StringBuilder builder = new StringBuilder(); for ( String k : key ) { if ( "-".equals( k ) ) { match( input, DRL5Lexer.MINUS, k, null, DroolsEditorType.KEYWORD ); // part of the keyword if ( state.failed ) return null; } else { match( input, DRL5Lexer.ID, k, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; } builder.append( k ); } if ( state.backtracking == 0 ) { attribute = helper.start( (DescrBuilder< ? , ? >) as, AttributeDescrBuilder.class, builder.toString() ); } if ( input.LA( 1 ) == DRL5Lexer.LEFT_PAREN ) { String value = chunk( DRL5Lexer.LEFT_PAREN, DRL5Lexer.RIGHT_PAREN, -1 ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { attribute.value( safeStripDelimiters( value, "(", ")" ) ); attribute.type( AttributeDescr.Type.EXPRESSION ); } } else { String value = ""; if ( input.LA( 1 ) == DRL5Lexer.PLUS ) { Token sign = match( input, DRL5Lexer.PLUS, null, null, DroolsEditorType.NUMERIC_CONST ); if ( state.failed ) return null; value += sign.getText(); } else if ( input.LA( 1 ) == DRL5Lexer.MINUS ) { Token sign = match( input, DRL5Lexer.MINUS, null, null, DroolsEditorType.NUMERIC_CONST ); if ( state.failed ) return null; value += sign.getText(); } Token nbr = match( input, DRL5Lexer.DECIMAL, null, null, DroolsEditorType.NUMERIC_CONST ); if ( state.failed ) return null; value += nbr.getText(); if ( state.backtracking == 0 ) { attribute.value( value ); attribute.type( AttributeDescr.Type.NUMBER ); } } } finally { if ( attribute != null ) { helper.end( AttributeDescrBuilder.class, attribute ); } } return attribute != null ? attribute.getDescr() : null; } /** * lhs := WHEN COLON? lhsExpression * @param rule * @throws RecognitionException */ private void lhs( RuleDescrBuilder rule ) throws RecognitionException { match( input, DRL5Lexer.ID, DroolsSoftKeywords.WHEN, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( input.LA( 1 ) == DRL5Lexer.COLON ) { match( input, DRL5Lexer.COLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } lhsExpression( rule != null ? rule.lhs() : null ); } /** * lhsExpression := lhsOr* * * @param lhs * @throws RecognitionException */ private void lhsExpression( CEDescrBuilder< ? , AndDescr> lhs ) throws RecognitionException { helper.start( lhs, CEDescrBuilder.class, null ); if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } try { while ( input.LA( 1 ) != DRL5Lexer.EOF && !helper.validateIdentifierKey( DroolsSoftKeywords.THEN ) && !helper.validateIdentifierKey( DroolsSoftKeywords.END ) ) { if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } lhsOr( lhs, true ); if ( lhs.getDescr() != null && lhs.getDescr() instanceof ConditionalElementDescr ) { ConditionalElementDescr root = (ConditionalElementDescr) lhs.getDescr(); BaseDescr[] descrs = root.getDescrs().toArray( new BaseDescr[root.getDescrs().size()] ); root.getDescrs().clear(); for ( int i = 0; i < descrs.length; i++ ) { root.addOrMerge( descrs[i] ); } } if ( state.failed ) return; } } finally { helper.end( CEDescrBuilder.class, lhs ); } } /** * lhsOr := LEFT_PAREN OR lhsAnd+ RIGHT_PAREN * | lhsAnd (OR lhsAnd)* * * @param ce * @param allowOr * @throws RecognitionException */ private BaseDescr lhsOr( final CEDescrBuilder< ? , ? > ce, boolean allowOr ) throws RecognitionException { BaseDescr result = null; if ( allowOr && input.LA( 1 ) == DRL5Lexer.LEFT_PAREN && helper.validateLT( 2, DroolsSoftKeywords.OR ) ) { // prefixed OR CEDescrBuilder< ? , OrDescr> or = null; if ( state.backtracking == 0 ) { or = ce.or(); result = or.getDescr(); helper.start( or, CEDescrBuilder.class, null ); } try { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; match( input, DRL5Lexer.ID, DroolsSoftKeywords.OR, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( or ); if ( state.failed ) return null; } if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION_AND_OR ); } while ( input.LA( 1 ) != DRL5Lexer.RIGHT_PAREN ) { lhsAnd( or, allowOr ); if ( state.failed ) return null; } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } finally { if ( state.backtracking == 0 ) { helper.end( CEDescrBuilder.class, or ); } } } else { // infix OR // create an OR anyway, as if it is not an OR we remove it later CEDescrBuilder< ? , OrDescr> or = null; if ( state.backtracking == 0 ) { or = ce.or(); result = or.getDescr(); helper.start( or, CEDescrBuilder.class, null ); } try { lhsAnd( or, allowOr ); if ( state.failed ) return null; if ( allowOr && (helper.validateIdentifierKey( DroolsSoftKeywords.OR ) || input.LA( 1 ) == DRL5Lexer.DOUBLE_PIPE) ) { while ( helper.validateIdentifierKey( DroolsSoftKeywords.OR ) || input.LA( 1 ) == DRL5Lexer.DOUBLE_PIPE ) { if ( input.LA( 1 ) == DRL5Lexer.DOUBLE_PIPE ) { match( input, DRL5Lexer.DOUBLE_PIPE, null, null, DroolsEditorType.SYMBOL ); } else { match( input, DRL5Lexer.ID, DroolsSoftKeywords.OR, null, DroolsEditorType.KEYWORD ); } if ( state.failed ) return null; while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( or ); if ( state.failed ) return null; } if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION_AND_OR ); } lhsAnd( or, allowOr ); if ( state.failed ) return null; } } else if ( allowOr ) { if ( state.backtracking == 0 ) { // if no OR present, then remove it and add children to parent ((ConditionalElementDescr) ce.getDescr()).getDescrs().remove( or.getDescr() ); for ( BaseDescr base : or.getDescr().getDescrs() ) { ((ConditionalElementDescr) ce.getDescr()).addDescr( base ); } result = ce.getDescr(); } } } finally { if ( state.backtracking == 0 ) { helper.end( CEDescrBuilder.class, or ); } } } return result; } /** * lhsAnd := LEFT_PAREN AND lhsUnary+ RIGHT_PAREN * | lhsUnary (AND lhsUnary)* * * @param ce * @throws RecognitionException */ private BaseDescr lhsAnd( final CEDescrBuilder< ? , ? > ce, boolean allowOr ) throws RecognitionException { BaseDescr result = null; if ( input.LA( 1 ) == DRL5Lexer.LEFT_PAREN && helper.validateLT( 2, DroolsSoftKeywords.AND ) ) { // prefixed AND CEDescrBuilder< ? , AndDescr> and = null; if ( state.backtracking == 0 ) { and = ce.and(); result = ce.getDescr(); helper.start( and, CEDescrBuilder.class, null ); } try { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; match( input, DRL5Lexer.ID, DroolsSoftKeywords.AND, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( and ); if ( state.failed ) return null; } if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION_AND_OR ); } while ( input.LA( 1 ) != DRL5Lexer.RIGHT_PAREN ) { lhsUnary( and, allowOr ); if ( state.failed ) return null; } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } finally { if ( state.backtracking == 0 ) { helper.end( CEDescrBuilder.class, and ); } } } else { // infix AND // create an AND anyway, since if it is not an AND we remove it later CEDescrBuilder< ? , AndDescr> and = null; if ( state.backtracking == 0 ) { and = ce.and(); result = and.getDescr(); helper.start( and, CEDescrBuilder.class, null ); } try { lhsUnary( and, allowOr ); if ( state.failed ) return null; if ( helper.validateIdentifierKey( DroolsSoftKeywords.AND ) || input.LA( 1 ) == DRL5Lexer.DOUBLE_AMPER ) { while ( helper.validateIdentifierKey( DroolsSoftKeywords.AND ) || input.LA( 1 ) == DRL5Lexer.DOUBLE_AMPER ) { if ( input.LA( 1 ) == DRL5Lexer.DOUBLE_AMPER ) { match( input, DRL5Lexer.DOUBLE_AMPER, null, null, DroolsEditorType.SYMBOL ); } else { match( input, DRL5Lexer.ID, DroolsSoftKeywords.AND, null, DroolsEditorType.KEYWORD ); } if ( state.failed ) return null; while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( and ); if ( state.failed ) return null; } if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION_AND_OR ); } lhsUnary( and, allowOr ); if ( state.failed ) return null; } } else { if ( state.backtracking == 0 && and.getDescr().getDescrs().size() < 2 ) { // if no AND present, then remove it and add children to parent ((ConditionalElementDescr) ce.getDescr()).getDescrs().remove( and.getDescr() ); for ( BaseDescr base : and.getDescr().getDescrs() ) { ((ConditionalElementDescr) ce.getDescr()).addDescr( base ); } result = ce.getDescr(); } } } finally { if ( state.backtracking == 0 ) { helper.end( CEDescrBuilder.class, and ); } } } return result; } /** * lhsUnary := * ( lhsExists namedConsequence? * | lhsNot namedConsequence? * | lhsEval consequenceInvocation* * | lhsForall * | lhsAccumulate * | LEFT_PAREN lhsOr RIGHT_PAREN namedConsequence? * | lhsPatternBind consequenceInvocation* * ) * SEMICOLON? * * @param ce * @return */ private BaseDescr lhsUnary( final CEDescrBuilder< ? , ? > ce, boolean allowOr ) throws RecognitionException { BaseDescr result = null; if ( helper.validateIdentifierKey( DroolsSoftKeywords.EXISTS ) ) { result = lhsExists( ce, allowOr ); if ( helper.validateIdentifierKey( DroolsSoftKeywords.DO ) ) { namedConsequence( ce, null ); } } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.NOT ) ) { result = lhsNot( ce, allowOr ); if ( helper.validateIdentifierKey( DroolsSoftKeywords.DO ) ) { namedConsequence( ce, null ); } } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.EVAL ) ) { result = lhsEval( ce ); for (BaseDescr i = consequenceInvocation( ce ); i != null; i = consequenceInvocation( ce )); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.FORALL ) ) { result = lhsForall( ce ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.ACCUMULATE ) ) { result = lhsAccumulate( ce ); } else if ( input.LA( 1 ) == DRL5Lexer.LEFT_PAREN ) { // the order here is very important: this if branch must come before the lhsPatternBind below result = lhsParen( ce, allowOr ); if ( helper.validateIdentifierKey( DroolsSoftKeywords.DO ) ) { namedConsequence( ce, null ); } } else if ( input.LA( 1 ) == DRL5Lexer.ID || input.LA( 1 ) == DRL5Lexer.QUESTION ) { result = lhsPatternBind( ce, allowOr ); for (BaseDescr i = consequenceInvocation( ce ); i != null; i = consequenceInvocation( ce )); } else { failMismatchedTokenException(); } if ( input.LA( 1 ) == DRL5Lexer.SEMICOLON ) { match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } return result; } /** * consequenceInvocation := conditionalBranch | namedConsequence * * @param ce * @return */ private BaseDescr consequenceInvocation( CEDescrBuilder< ? , ? > ce ) throws RecognitionException { BaseDescr result = null; if ( helper.validateIdentifierKey( DroolsSoftKeywords.IF ) ) { result = conditionalBranch( ce, null ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.DO ) ) { result = namedConsequence( ce, null ); } return result; } /** * conditionalBranch := IF LEFT_PAREN conditionalExpression RIGHT_PAREN * ( namedConsequence | breakingNamedConsequence ) * ( ELSE ( namedConsequence | breakingNamedConsequence | conditionalBranch ) )? */ private BaseDescr conditionalBranch( CEDescrBuilder< ? , ? > ce, ConditionalBranchDescrBuilder<?> conditionalBranch ) throws RecognitionException { if ( conditionalBranch == null ) { conditionalBranch = helper.start( (DescrBuilder< ? , ? >) ce, ConditionalBranchDescrBuilder.class, null ); } try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.IF, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; EvalDescrBuilder<?> eval = conditionalBranch.condition(); if ( !parseEvalExpression(eval) ) return null; if ( helper.validateIdentifierKey( DroolsSoftKeywords.DO ) ) { if ( namedConsequence( null, conditionalBranch.consequence() ) == null ) return null; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.BREAK ) ) { if ( breakingNamedConsequence( null, conditionalBranch.consequence() ) == null ) return null; } else { return null; } if ( helper.validateIdentifierKey( DroolsSoftKeywords.ELSE ) ) { match( input, DRL5Lexer.ID, DroolsSoftKeywords.ELSE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; ConditionalBranchDescrBuilder<?> elseBranch = conditionalBranch.otherwise(); if ( helper.validateIdentifierKey( DroolsSoftKeywords.DO ) ) { if ( namedConsequence( null, elseBranch.consequence() ) == null ) return null; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.BREAK ) ) { if ( breakingNamedConsequence( null, elseBranch.consequence() ) == null ) return null; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.IF ) ) { if ( conditionalBranch( null, elseBranch ) == null ) return null; } else { return null; } } } finally { helper.end( ConditionalBranchDescrBuilder.class, conditionalBranch ); } return conditionalBranch.getDescr(); } /** * namedConsequence := DO LEFT_SQUARE ID RIGHT_SQUARE BREAK? */ private BaseDescr namedConsequence( CEDescrBuilder< ? , ? > ce, NamedConsequenceDescrBuilder<?> namedConsequence ) throws RecognitionException { if ( namedConsequence == null ) { namedConsequence = helper.start( (DescrBuilder< ? , ? >) ce, NamedConsequenceDescrBuilder.class, null ); } try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.DO, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; match( input, DRL5Lexer.LEFT_SQUARE, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; Token label = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; namedConsequence.name( label.getText() ); match( input, DRL5Lexer.RIGHT_SQUARE, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } finally { helper.end( NamedConsequenceDescrBuilder.class, namedConsequence ); } return namedConsequence.getDescr(); } /** * breakingNamedConsequence := BREAK LEFT_SQUARE ID RIGHT_SQUARE */ private BaseDescr breakingNamedConsequence( CEDescrBuilder< ? , ? > ce, NamedConsequenceDescrBuilder<?> namedConsequence ) throws RecognitionException { if ( namedConsequence == null ) { namedConsequence = helper.start( (DescrBuilder< ? , ? >) ce, NamedConsequenceDescrBuilder.class, null ); } try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.BREAK, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; match( input, DRL5Lexer.LEFT_SQUARE, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; Token label = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; namedConsequence.name( label.getText() ); namedConsequence.breaking(true); match( input, DRL5Lexer.RIGHT_SQUARE, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } finally { helper.end( NamedConsequenceDescrBuilder.class, namedConsequence ); } return namedConsequence.getDescr(); } /** * lhsExists := EXISTS * ( (LEFT_PAREN (or_key|and_key))=> lhsOr // prevents '((' for prefixed and/or * | LEFT_PAREN lhsOr RIGHT_PAREN * | lhsPatternBind * ) * * @param ce * @return * @throws RecognitionException */ private BaseDescr lhsExists( CEDescrBuilder< ? , ? > ce, boolean allowOr ) throws RecognitionException { CEDescrBuilder< ? , ExistsDescr> exists = null; if ( state.backtracking == 0 ) { exists = ce.exists(); helper.start( exists, CEDescrBuilder.class, null ); } try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.EXISTS, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION_EXISTS ); } if ( input.LA( 1 ) == DRL5Lexer.LEFT_PAREN ) { boolean prefixed = helper.validateLT( 2, DroolsSoftKeywords.AND ) || helper.validateLT( 2, DroolsSoftKeywords.OR ); if ( !prefixed ) { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } lhsOr( exists, allowOr ); if ( state.failed ) return null; if ( !prefixed ) { match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } } else { lhsPatternBind( exists, true ); if ( state.failed ) return null; } } finally { if ( state.backtracking == 0 ) { helper.end( CEDescrBuilder.class, exists ); } } return exists != null ? exists.getDescr() : null; } /** * lhsNot := NOT * ( (LEFT_PAREN (or_key|and_key))=> lhsOr // prevents '((' for prefixed and/or * | LEFT_PAREN lhsOr RIGHT_PAREN * | lhsPatternBind * ) * * @param ce * @return * @throws RecognitionException */ private BaseDescr lhsNot( CEDescrBuilder< ? , ? > ce, boolean allowOr ) throws RecognitionException { CEDescrBuilder< ? , NotDescr> not = null; if ( state.backtracking == 0 ) { not = ce.not(); helper.start( not, CEDescrBuilder.class, null ); } try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.NOT, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION_NOT ); } if ( input.LA( 1 ) == DRL5Lexer.LEFT_PAREN ) { boolean prefixed = helper.validateLT( 2, DroolsSoftKeywords.AND ) || helper.validateLT( 2, DroolsSoftKeywords.OR ); if ( !prefixed ) { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } lhsOr( not, allowOr ); if ( state.failed ) return null; if ( !prefixed ) { match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } } else if ( input.LA( 1 ) != DRL5Lexer.EOF ) { lhsPatternBind( not, true ); if ( state.failed ) return null; } } finally { if ( state.backtracking == 0 ) { helper.end( CEDescrBuilder.class, not ); } } return not != null ? not.getDescr() : null; } /** * lhsForall := FORALL LEFT_PAREN lhsPatternBind+ RIGHT_PAREN * * @param ce * @return * @throws RecognitionException */ private BaseDescr lhsForall( CEDescrBuilder< ? , ? > ce ) throws RecognitionException { ForallDescrBuilder< ? > forall = helper.start( ce, ForallDescrBuilder.class, null ); try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.FORALL, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; do { lhsPatternBind( forall, false ); if ( state.failed ) return null; if ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } } while ( input.LA( 1 ) != DRL5Lexer.EOF && input.LA( 1 ) != DRL5Lexer.RIGHT_PAREN ); match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } finally { helper.end( ForallDescrBuilder.class, forall ); } return forall != null ? forall.getDescr() : null; } /** * lhsEval := EVAL LEFT_PAREN conditionalExpression RIGHT_PAREN * * @param ce * @return * @throws RecognitionException */ private BaseDescr lhsEval( CEDescrBuilder< ? , ? > ce ) throws RecognitionException { EvalDescrBuilder< ? > eval = null; try { eval = helper.start( ce, EvalDescrBuilder.class, null ); match( input, DRL5Lexer.ID, DroolsSoftKeywords.EVAL, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( !parseEvalExpression(eval) ) return null; } catch (RecognitionException e) { throw e; } finally { helper.end( EvalDescrBuilder.class, eval ); } return eval != null ? eval.getDescr() : null; } private boolean parseEvalExpression(EvalDescrBuilder<?> eval) throws RecognitionException { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return false; if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_INSIDE_EVAL ); } int idx = input.index(); final String expr; try{ expr = conditionalExpression(); } catch (RecognitionException e){ final Token tempToken = helper.getLastTokenOnList(helper.getEditorInterface().getLast().getContent()); if (tempToken != null){ for (int i = tempToken.getTokenIndex() + 1; i < input.size(); i++) { final Token token = input.get(i); if (token.getType() == DRL5Lexer.EOF){ break; } helper.emit(token, DroolsEditorType.CODE_CHUNK); } } throw e; } if ( state.backtracking == 0 ) { eval.constraint( expr ); } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return false; helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); return true; } /** * lhsParen := LEFT_PAREN lhsOr RIGHT_PAREN * * @param ce * @return * @throws RecognitionException */ private BaseDescr lhsParen( CEDescrBuilder< ? , ? > ce, boolean allowOr ) throws RecognitionException { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } BaseDescr descr = lhsOr( ce, allowOr ); if ( state.failed ) return null; match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; return descr; } /** * lhsPatternBind := label? * ( LEFT_PAREN lhsPattern (OR lhsPattern)* RIGHT_PAREN * | lhsPattern ) * * @param ce * @return * @throws RecognitionException */ @SuppressWarnings("unchecked") private BaseDescr lhsPatternBind( PatternContainerDescrBuilder< ? , ? > ce, final boolean allowOr ) throws RecognitionException { PatternDescrBuilder< ? > pattern = null; CEDescrBuilder< ? , OrDescr> or = null; BaseDescr result = null; Token first = input.LT( 1 ); pattern = helper.start( (DescrBuilder< ? , ? >) ce, PatternDescrBuilder.class, null ); if ( pattern != null ) { result = pattern.getDescr(); } String label = null; boolean isUnification = false; if ( input.LA( 1 ) == DRL5Lexer.ID && input.LA( 2 ) == DRL5Lexer.COLON && !helper.validateCEKeyword( 1 ) ) { label = label( DroolsEditorType.IDENTIFIER_PATTERN ); if ( state.failed ) return null; } else if ( input.LA( 1 ) == DRL5Lexer.ID && input.LA( 2 ) == DRL5Lexer.UNIFY && !helper.validateCEKeyword( 1 ) ) { label = unif( DroolsEditorType.IDENTIFIER_PATTERN ); if ( state.failed ) return null; isUnification = true; } if ( input.LA( 1 ) == DRL5Lexer.LEFT_PAREN ) { try { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; if ( helper.validateCEKeyword( 1 ) ) { failMismatchedTokenException(); return null; // in case it is backtracking } lhsPattern( pattern, label, isUnification ); if ( state.failed ) return null; if ( allowOr && helper.validateIdentifierKey( DroolsSoftKeywords.OR ) && ce instanceof CEDescrBuilder ) { if ( state.backtracking == 0 ) { // this is necessary because of the crappy bind with multi-pattern OR syntax or = ((CEDescrBuilder<DescrBuilder< ? , ? >, OrDescr>) ce).or(); result = or.getDescr(); helper.end( PatternDescrBuilder.class, pattern ); helper.start( or, CEDescrBuilder.class, null ); // adjust real or starting token: helper.setStart( or, first ); // remove original pattern from the parent CE child list: ((ConditionalElementDescr) ce.getDescr()).getDescrs().remove( pattern.getDescr() ); // add pattern to the OR instead or.getDescr().addDescr( pattern.getDescr() ); } while ( helper.validateIdentifierKey( DroolsSoftKeywords.OR ) ) { match( input, DRL5Lexer.ID, DroolsSoftKeywords.OR, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; pattern = helper.start( or, PatternDescrBuilder.class, null ); // new pattern, same binding lhsPattern( pattern, label, isUnification ); if ( state.failed ) return null; helper.end( PatternDescrBuilder.class, pattern ); } } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } finally { if ( or != null ) { helper.end( CEDescrBuilder.class, or ); } else { helper.end( PatternDescrBuilder.class, pattern ); } } } else { try { lhsPattern( pattern, label, isUnification ); if ( state.failed ) return null; } finally { helper.end( PatternDescrBuilder.class, pattern ); } } return result; } /** * lhsAccumulate := ACCUMULATE LEFT_PAREN lhsAnd (COMMA|SEMICOLON) * accumulateFunctionBinding (COMMA accumulateFunctionBinding)* * (SEMICOLON constraints)? * RIGHT_PAREN SEMICOLON? * * @param ce * @return * @throws RecognitionException */ private BaseDescr lhsAccumulate( PatternContainerDescrBuilder< ? , ? > ce ) throws RecognitionException { PatternDescrBuilder< ? > pattern = null; BaseDescr result = null; pattern = helper.start( (DescrBuilder< ? , ? >) ce, PatternDescrBuilder.class, null ); if ( pattern != null ) { result = pattern.getDescr(); } try { if ( state.backtracking == 0 ) { pattern.type( "Object[]" ); pattern.isQuery( false ); // might have to add the implicit bindings as well } AccumulateDescrBuilder< ? > accumulate = helper.start( pattern, AccumulateDescrBuilder.class, null ); try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.ACCUMULATE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return null; if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_FROM_ACCUMULATE ); } match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; CEDescrBuilder< ? , AndDescr> source = accumulate.source(); try { helper.start( source, CEDescrBuilder.class, null ); lhsAnd( source, false ); if ( state.failed ) return null; if ( source.getDescr() != null && source.getDescr() instanceof ConditionalElementDescr ) { ConditionalElementDescr root = (ConditionalElementDescr) source.getDescr(); BaseDescr[] descrs = root.getDescrs().toArray( new BaseDescr[root.getDescrs().size()] ); root.getDescrs().clear(); for ( int i = 0; i < descrs.length; i++ ) { root.addOrMerge( descrs[i] ); } } } finally { helper.end( CEDescrBuilder.class, source ); } if( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } else if( input.LA( -1 ) != DRL5Lexer.SEMICOLON ) { // lhsUnary will consume an optional SEMICOLON, so we need to check if it was consumed already // or if we must fail consuming it now match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } // accumulate functions accumulateFunctionBinding( accumulate ); if ( state.failed ) return null; while ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; accumulateFunctionBinding( accumulate ); if ( state.failed ) return null; } if( input.LA( 1 ) == DRL5Lexer.SEMICOLON ) { match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; constraints( pattern ); } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } finally { helper.end( AccumulateDescrBuilder.class, accumulate ); if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } } finally { helper.end( PatternDescrBuilder.class, pattern ); } if ( input.LA( 1 ) == DRL5Lexer.SEMICOLON ) { match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; } return result; } private void failMismatchedTokenException() throws DroolsMismatchedTokenException { if ( state.backtracking > 0 ) { state.failed = true; } else { DroolsMismatchedTokenException mte = new DroolsMismatchedTokenException( input.LA( 1 ), input.LT( 1 ).getText(), input ); input.consume(); throw mte; } } private void failMissingTokenException() throws MissingTokenException { if ( state.backtracking > 0 ) { state.failed = true; } else { throw new MissingTokenException( DRL5Lexer.STRING, input, null ); } } /** * lhsPattern := QUESTION? qualifiedIdentifier * LEFT_PAREN positionalConstraints? constraints? RIGHT_PAREN * (OVER patternFilter)? (FROM patternSource)? * * @param pattern * @param label * @param isUnification * @throws RecognitionException */ private void lhsPattern( PatternDescrBuilder< ? > pattern, String label, boolean isUnification ) throws RecognitionException { boolean query = false; if ( input.LA( 1 ) == DRL5Lexer.QUESTION ) { match( input, DRL5Lexer.QUESTION, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; query = true; } String type = this.qualifiedIdentifier(); if ( state.failed ) return; if ( state.backtracking == 0 ) { pattern.type( type ); pattern.isQuery( query ); if ( label != null ) { pattern.id( label, isUnification ); } } match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; if ( input.LA( 1 ) != DRL5Lexer.RIGHT_PAREN && speculatePositionalConstraints() ) { positionalConstraints( pattern ); } if ( input.LA( 1 ) != DRL5Lexer.RIGHT_PAREN ) { constraints( pattern ); } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; while ( input.LA( 1 ) == DRL5Lexer.AT ) { // annotation* annotation( pattern ); if ( state.failed ) return; } if ( helper.validateIdentifierKey( DroolsSoftKeywords.OVER ) ) { // || input.LA( 1 ) == DRL5Lexer.PIPE ) { patternFilter( pattern ); } if ( helper.validateIdentifierKey( DroolsSoftKeywords.FROM ) ) { patternSource( pattern ); } if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } /** * label := ID COLON * @return * @throws RecognitionException */ private String label( DroolsEditorType edType ) throws RecognitionException { Token label = match( input, DRL5Lexer.ID, null, null, edType ); if ( state.failed ) return null; match( input, DRL5Lexer.COLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; return label.getText(); } /** * unif := ID UNIFY * @return * @throws RecognitionException */ private String unif( DroolsEditorType edType ) throws RecognitionException { Token label = match( input, DRL5Lexer.ID, null, null, edType ); if ( state.failed ) return null; match( input, DRL5Lexer.UNIFY, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; return label.getText(); } private boolean speculatePositionalConstraints() { state.backtracking++; int start = input.mark(); try { positionalConstraints( null ); // can never throw exception } catch ( RecognitionException re ) { System.err.println( "impossible: " + re ); re.printStackTrace(); } boolean success = !state.failed; input.rewind( start ); state.backtracking--; state.failed = false; return success; } /** * positionalConstraints := constraint (COMMA constraint)* SEMICOLON * @param pattern * @throws RecognitionException */ private void positionalConstraints( PatternDescrBuilder< ? > pattern ) throws RecognitionException { constraint( pattern, true, "" ); if ( state.failed ) return; while ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; constraint( pattern, true, "" ); if ( state.failed ) return; } match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } /** * constraints := constraint (COMMA constraint)* * @param pattern * @throws RecognitionException */ private void constraints( PatternDescrBuilder< ? > pattern ) throws RecognitionException { constraints( pattern, "" ); } private void constraints( PatternDescrBuilder< ? > pattern, String prefix ) throws RecognitionException { constraint( pattern, false, prefix ); if ( state.failed ) return; while ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; constraint( pattern, false, prefix ); if ( state.failed ) return; } } /** * constraint := nestedConstraint | conditionalOrExpression * @param pattern * @throws RecognitionException */ private void constraint( PatternDescrBuilder< ? > pattern, boolean positional, String prefix ) throws RecognitionException { if ( speculateNestedConstraint() ) { nestedConstraint( pattern, prefix ); return; } if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_START ); } int first = input.index(); exprParser.getHelper().setHasOperator( false ); // resetting try{ exprParser.conditionalOrExpression(); } finally { if ( state.backtracking == 0 ) { if ( input.LA( 1 ) == DRL5Lexer.ID && input.LA( 2 ) == DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } else if ( input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_END ); } else if( ! lastTokenWasWhiteSpace() ) { int location = getCurrentLocation(); if( location == Location.LOCATION_LHS_INSIDE_CONDITION_END ) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } else if ( input.get(input.index()).getType() != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_START ); } } else if ( getCurrentLocation() == Location.LOCATION_LHS_INSIDE_CONDITION_START && !exprParser.getHelper().getHasOperator() && lastTokenWasWhiteSpace() && input.LA( 1 ) == DRL5Lexer.EOF && input.LA( -1 ) == DRL5Lexer.ID ){ helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); } } } if ( state.failed ) return; if ( state.backtracking == 0 && input.index() > first ) { // expression consumed something int last = input.LT( -1 ).getTokenIndex(); String expr = toExpression(prefix, first, last); pattern.constraint( expr, positional ); BaseDescr constrDescr = pattern.getDescr().getDescrs().get( pattern.getDescr().getDescrs().size() - 1 ); constrDescr.setLocation( input.get( first ).getLine(), input.get( first ).getCharPositionInLine() ); constrDescr.setEndLocation( input.get( last ).getLine(), input.get( last ).getCharPositionInLine() ); constrDescr.setStartCharacter( ((CommonToken)input.get( first )).getStartIndex() ); constrDescr.setEndCharacter( ((CommonToken)input.get( last )).getStopIndex() ); } } private String toExpression(String prefix, int first, int last) { String expr = input.toString( first, last ); if (prefix.length() == 0) { return expr; } StringBuilder sb = new StringBuilder(); toOrExpression( sb, prefix, expr ); return sb.toString(); } private void toOrExpression( StringBuilder sb, String prefix, String expr ) { int start = 0; int end = expr.indexOf( "||" ); do { if ( start > 0 ) { sb.append( " || " ); } toAndExpression( sb, prefix, end > 0 ? expr.substring( start, end ) : expr.substring( start ) ); start = end + 2; end = expr.indexOf( "||", start ); } while ( start > 1 ); } private void toAndExpression( StringBuilder sb, String prefix, String expr ) { int start = 0; int end = expr.indexOf( "&&" ); do { if ( start > 0 ) { sb.append( " && " ); } sb.append( toExpression( prefix, end > 0 ? expr.substring( start, end ) : expr.substring( start ) ) ); start = end + 2; end = expr.indexOf( "&&", start ); } while ( start > 1 ); } private String toExpression( String prefix, String expr ) { expr = expr.trim(); int colonPos = expr.indexOf(":"); return colonPos < 0 ? prefix + expr : expr.substring(0, colonPos+1) + " " + prefix + expr.substring(colonPos+1).trim(); } private boolean speculateNestedConstraint() throws RecognitionException { return getNestedConstraintPrefixLenght() > 0; } /** * nestedConstraint := ( ID ( DOT | HASH ) )* ID DOT LEFT_PAREN constraints RIGHT_PAREN * @param pattern * @throws RecognitionException */ private void nestedConstraint( PatternDescrBuilder< ? > pattern, String prefix ) throws RecognitionException { int prefixLenght = getNestedConstraintPrefixLenght(); int prefixStart = input.index(); prefix += input.toString( prefixStart, prefixStart + prefixLenght - 2 ); for (int i = 0; i < prefixLenght; i++) { input.consume(); } constraints( pattern, prefix ); match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); } private int getNestedConstraintPrefixLenght() { int cursor = 0; int lastToken = input.LA( ++cursor ); while (true) { int nextToken = input.LA( ++cursor ); switch (lastToken) { case DRL5Lexer.ID: if ( nextToken != DRL5Lexer.DOT && nextToken != DRL5Lexer.HASH ) { return -1; } break; case DRL5Lexer.DOT: if ( nextToken == DRL5Lexer.LEFT_PAREN ) { return cursor; } case DRL5Lexer.HASH: if ( nextToken != DRL5Lexer.ID ) { return -1; } break; default: return -1; } lastToken = nextToken; } } private boolean lastTokenWasWhiteSpace() { int index = input.index(); while( index >= 0 ) { int type = input.get( index ).getType(); switch( type ) { case DRL5Lexer.EOF: index--; break; case DRL5Lexer.WS: return true; default: return false; } } return false; } private int getCurrentLocation() { LinkedList<DroolsSentence> ei = helper.getEditorInterface(); LinkedList<?> content = ei.getLast().getContent(); // the following call is efficient as it points to the tail of the list ListIterator<?> listIterator = content.listIterator( content.size() ); while( listIterator.hasPrevious() ) { Object previous = listIterator.previous(); if (previous instanceof Integer) { return ((Integer) previous).intValue(); } } return Location.LOCATION_UNKNOWN; } /** * patternFilter := OVER filterDef * DISALLOWED: | ( PIPE filterDef )+ * * @param pattern * @throws RecognitionException */ private void patternFilter( PatternDescrBuilder< ? > pattern ) throws RecognitionException { // if ( input.LA( 1 ) == DRL5Lexer.PIPE ) { // while ( input.LA( 1 ) == DRL5Lexer.PIPE ) { // match( input, // DRL5Lexer.PIPE, // null, // null, // DroolsEditorType.SYMBOL ); // if ( state.failed ) return; // // filterDef( pattern ); // if ( state.failed ) return; // } // } else { match( input, DRL5Lexer.ID, DroolsSoftKeywords.OVER, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; filterDef( pattern ); if ( state.failed ) return; // } } /** * filterDef := label ID LEFT_PAREN parameters RIGHT_PAREN * @param pattern * @throws RecognitionException */ private void filterDef( PatternDescrBuilder< ? > pattern ) throws RecognitionException { BehaviorDescrBuilder< ? > behavior = helper.start( pattern, BehaviorDescrBuilder.class, null ); try { String bName = label( DroolsEditorType.IDENTIFIER_PATTERN ); if ( state.failed ) return; Token subtype = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.IDENTIFIER_PATTERN ); if ( state.failed ) return; if ( state.backtracking == 0 ) { behavior.type( bName, subtype.getText() ); } List<String> parameters = parameters(); if ( state.failed ) return; if ( state.backtracking == 0 ) { behavior.parameters( parameters ); } } finally { helper.end( BehaviorDescrBuilder.class, behavior ); } } /** * patternSource := FROM * ( fromAccumulate * | fromCollect * | fromEntryPoint * | fromWindow * | fromExpression ) * @param pattern * @throws RecognitionException */ private void patternSource( PatternDescrBuilder< ? > pattern ) throws RecognitionException { match( input, DRL5Lexer.ID, DroolsSoftKeywords.FROM, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( state.backtracking == 0 ) { helper.emit( Location.LOCATION_LHS_FROM ); } if ( helper.validateIdentifierKey( DroolsSoftKeywords.ACCUMULATE ) ) { fromAccumulate( pattern ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.COLLECT ) ) { fromCollect( pattern ); } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.ENTRY ) && helper.validateLT( 2, "-" ) && helper.validateLT( 3, DroolsSoftKeywords.POINT ) ) { fromEntryPoint( pattern ); if ( state.failed ) return; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.WINDOW ) ) { fromWindow( pattern ); } else { fromExpression( pattern ); if ( !lastTokenWasWhiteSpace() && input.LA( 1 ) == DRL5Lexer.EOF) { helper.emit( Location.LOCATION_LHS_FROM ); throw new RecognitionException(); } if ( state.failed ) return; } if ( input.LA( 1 ) == DRL5Lexer.SEMICOLON ) { match( input, DRL5Lexer.SEMICOLON, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } } /** * fromExpression := conditionalOrExpression * * @param pattern * @throws RecognitionException */ private void fromExpression( PatternDescrBuilder< ? > pattern ) throws RecognitionException { String expr = conditionalOrExpression(); if ( state.failed ) return; if ( state.backtracking == 0 ) { pattern.from().expression( expr ); if ( input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } } /** * fromEntryPoint := ENTRY-POINT stringId * * @param pattern * @throws RecognitionException */ private void fromEntryPoint( PatternDescrBuilder< ? > pattern ) throws RecognitionException { String ep = ""; match( input, DRL5Lexer.ID, DroolsSoftKeywords.ENTRY, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; match( input, DRL5Lexer.MINUS, null, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; match( input, DRL5Lexer.ID, DroolsSoftKeywords.POINT, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; ep = stringId(); if ( state.backtracking == 0 ) { pattern.from().entryPoint( ep ); if ( input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } } /** * fromWindow := WINDOW ID * * @param pattern * @throws RecognitionException */ private void fromWindow( PatternDescrBuilder< ? > pattern ) throws RecognitionException { String window = ""; match( input, DRL5Lexer.ID, DroolsSoftKeywords.WINDOW, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; Token id = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return; window = id.getText(); if ( state.backtracking == 0 ) { pattern.from().window( window ); if ( input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } } /** * fromCollect := COLLECT LEFT_PAREN lhsPatternBind RIGHT_PAREN * * @param pattern * @throws RecognitionException */ private void fromCollect( PatternDescrBuilder< ? > pattern ) throws RecognitionException { CollectDescrBuilder< ? > collect = helper.start( pattern, CollectDescrBuilder.class, null ); try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.COLLECT, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_FROM_COLLECT ); } match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; lhsPatternBind( collect, false ); if ( state.failed ) return; match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } finally { helper.end( CollectDescrBuilder.class, collect ); if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } } /** * fromAccumulate := ACCUMULATE LEFT_PAREN lhsAnd COMMA * ( INIT chunk_(_) COMMA ACTION chunk_(_) COMMA * ( REVERSE chunk_(_) COMMA)? RESULT chunk_(_) * | accumulateFunction * ) RIGHT_PAREN * * @param pattern * @throws RecognitionException */ private void fromAccumulate( PatternDescrBuilder< ? > pattern ) throws RecognitionException { AccumulateDescrBuilder< ? > accumulate = helper.start( pattern, AccumulateDescrBuilder.class, null ); try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.ACCUMULATE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_FROM_ACCUMULATE ); } match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; CEDescrBuilder< ? , AndDescr> source = accumulate.source(); try { helper.start( source, CEDescrBuilder.class, null ); lhsAnd( source, false ); if ( state.failed ) return; if ( source.getDescr() != null && source.getDescr() instanceof ConditionalElementDescr ) { ConditionalElementDescr root = (ConditionalElementDescr) source.getDescr(); BaseDescr[] descrs = root.getDescrs().toArray( new BaseDescr[root.getDescrs().size()] ); root.getDescrs().clear(); for ( int i = 0; i < descrs.length; i++ ) { root.addOrMerge( descrs[i] ); } } } finally { helper.end( CEDescrBuilder.class, source ); } if ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } if ( helper.validateIdentifierKey( DroolsSoftKeywords.INIT ) ) { // custom code, inline accumulate // initBlock match( input, DRL5Lexer.ID, DroolsSoftKeywords.INIT, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_FROM_ACCUMULATE_INIT ); } String init = chunk( DRL5Lexer.LEFT_PAREN, DRL5Lexer.RIGHT_PAREN, Location.LOCATION_LHS_FROM_ACCUMULATE_INIT_INSIDE ); if ( state.failed ) return; if ( state.backtracking == 0 ) accumulate.init( init ); if ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } // actionBlock match( input, DRL5Lexer.ID, DroolsSoftKeywords.ACTION, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION ); } String action = chunk( DRL5Lexer.LEFT_PAREN, DRL5Lexer.RIGHT_PAREN, Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION_INSIDE ); if ( state.failed ) return; if ( state.backtracking == 0 ) accumulate.action( action ); if ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } // reverseBlock if ( helper.validateIdentifierKey( DroolsSoftKeywords.REVERSE ) ) { match( input, DRL5Lexer.ID, DroolsSoftKeywords.REVERSE, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_FROM_ACCUMULATE_REVERSE ); } String reverse = chunk( DRL5Lexer.LEFT_PAREN, DRL5Lexer.RIGHT_PAREN, Location.LOCATION_LHS_FROM_ACCUMULATE_REVERSE_INSIDE ); if ( state.failed ) return; if ( state.backtracking == 0 ) accumulate.reverse( reverse ); if ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } } // resultBlock match( input, DRL5Lexer.ID, DroolsSoftKeywords.RESULT, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT ); } String result = chunk( DRL5Lexer.LEFT_PAREN, DRL5Lexer.RIGHT_PAREN, Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT_INSIDE ); if ( state.failed ) return; if ( state.backtracking == 0 ) accumulate.result( result ); } else { // accumulate functions accumulateFunction( accumulate, false, null ); if ( state.failed ) return; } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; } finally { helper.end( AccumulateDescrBuilder.class, accumulate ); if ( state.backtracking == 0 && input.LA( 1 ) != DRL5Lexer.EOF ) { helper.emit( Location.LOCATION_LHS_BEGIN_OF_CONDITION ); } } } /** * accumulateFunctionBinding := label accumulateFunction * @param accumulate * @throws RecognitionException */ private void accumulateFunctionBinding( AccumulateDescrBuilder< ? > accumulate ) throws RecognitionException { String label = null; boolean unif = false; if (input.LA(2) == DRL6Lexer.COLON) { label = label(DroolsEditorType.IDENTIFIER_VARIABLE); } else if (input.LA(2) == DRL6Lexer.UNIFY) { label = unif(DroolsEditorType.IDENTIFIER_VARIABLE); unif = true; } accumulateFunction( accumulate, unif, label ); } /** * accumulateFunction := label? ID parameters * @param accumulate * @throws RecognitionException */ private void accumulateFunction( AccumulateDescrBuilder< ? > accumulate, boolean unif, String label ) throws RecognitionException { Token function = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; List<String> parameters = parameters(); if ( state.failed ) return; if ( state.backtracking == 0 ) { accumulate.function( function.getText(), label, unif, parameters.toArray( new String[parameters.size()] ) ); } } /** * parameters := LEFT_PAREN (conditionalExpression (COMMA conditionalExpression)* )? RIGHT_PAREN * * @return * @throws RecognitionException */ private List<String> parameters() throws RecognitionException { match( input, DRL5Lexer.LEFT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; List<String> parameters = new ArrayList<String>(); if ( input.LA( 1 ) != DRL5Lexer.EOF && input.LA( 1 ) != DRL5Lexer.RIGHT_PAREN ) { String param = conditionalExpression(); if ( state.failed ) return null; parameters.add( param ); while ( input.LA( 1 ) == DRL5Lexer.COMMA ) { match( input, DRL5Lexer.COMMA, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; param = conditionalExpression(); if ( state.failed ) return null; parameters.add( param ); } } match( input, DRL5Lexer.RIGHT_PAREN, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return null; return parameters; } /** * rhs := defaultConsequence namedConsequence* (~END)* * @param rule */ private void rhs( RuleDescrBuilder rule ) { defaultConsequence( rule ); while ( input.LA( 1 ) != DRL5Lexer.EOF && helper.validateIdentifierKey( DroolsSoftKeywords.THEN ) ) { namedConsequence( rule ); } } /** * defaultConsequence := THEN chunk * @param rule */ public void defaultConsequence( RuleDescrBuilder rule ) { try { int first = input.index(); Token t = match( input, DRL5Lexer.ID, DroolsSoftKeywords.THEN, null, DroolsEditorType.KEYWORD ); if ( state.failed ) return; if ( state.backtracking == 0 ) { rule.getDescr().setConsequenceLocation( t.getLine(), t.getCharPositionInLine() ); helper.emit( Location.LOCATION_RHS ); } String chunk = getConsequenceCode( first ); // remove the "then" keyword and any subsequent spaces and line breaks // keep indentation of 1st non-blank line chunk = chunk.replaceFirst( "^then\\s*\\r?\\n?", "" ); rule.rhs( chunk ); } catch ( RecognitionException re ) { reportError( re ); } } /** * namedConsequence := THEN LEFT_SQUARE ID RIGHT_SQUARE chunk * @param rule */ public void namedConsequence( RuleDescrBuilder rule ) { try { match( input, DRL5Lexer.ID, DroolsSoftKeywords.THEN, null, DroolsEditorType.KEYWORD ); match( input, DRL5Lexer.LEFT_SQUARE, null, null, DroolsEditorType.SYMBOL ); Token label = match( input, DRL5Lexer.ID, null, null, DroolsEditorType.SYMBOL ); int first = input.index(); match( input, DRL5Lexer.RIGHT_SQUARE, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; String name = label.getText(); String chunk = getConsequenceCode( first ); // remove the closing squqre bracket "]" and any subsequent spaces and line breaks // keep indentation of 1st non-blank line chunk = chunk.replaceFirst( "^\\]\\s*\\r?\\n?", "" ); rule.namedRhs( name, chunk ); } catch ( RecognitionException re ) { reportError( re ); } } private String getConsequenceCode( int first ) { while ( input.LA( 1 ) != DRL5Lexer.EOF && !helper.validateIdentifierKey( DroolsSoftKeywords.END ) && !helper.validateIdentifierKey( DroolsSoftKeywords.THEN ) ) { helper.emit( input.LT(1), DroolsEditorType.CODE_CHUNK ); input.consume(); } int last = input.LT(1).getTokenIndex(); if ( last <= first ) { return ""; } String chunk = input.toString( first, last ); if ( chunk.endsWith( DroolsSoftKeywords.END ) ) { chunk = chunk.substring( 0, chunk.length() - DroolsSoftKeywords.END.length() ); } else if ( chunk.endsWith( DroolsSoftKeywords.THEN ) ) { chunk = chunk.substring( 0, chunk.length() - DroolsSoftKeywords.THEN.length() ); } return chunk; } /* ------------------------------------------------------------------------------------------------ * ANNOTATION * ------------------------------------------------------------------------------------------------ */ /** * annotation := fullAnnotation | AT ID chunk_(_)? */ private void annotation( AnnotatedDescrBuilder< ? > adb ) { AnnotationDescrBuilder< ? > annotation = null; try { if ( speculateFullAnnotation() ) { boolean buildState = exprParser.isBuildDescr(); exprParser.setBuildDescr( true ); exprParser.fullAnnotation( adb ); exprParser.setBuildDescr( buildState ); } else { // '@' Token at = match( input, DRL5Lexer.AT, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return; // identifier String fqn = qualifiedIdentifier(); if ( state.failed ) return; if ( state.backtracking == 0 ) { annotation = adb.newAnnotation( fqn ); helper.setStart( annotation, at ); } try { if ( input.LA( 1 ) == DRL5Lexer.LEFT_PAREN ) { String value = chunk( DRL5Lexer.LEFT_PAREN, DRL5Lexer.RIGHT_PAREN, -1 ).trim(); if ( state.failed ) return; if ( state.backtracking == 0 ) { annotation.value( value ); } } } finally { if ( state.backtracking == 0 ) { helper.setEnd( annotation ); } } } } catch ( RecognitionException re ) { reportError( re ); } } /** * Invokes the expression parser, trying to parse the annotation * as a full java-style annotation * * @return true if the sequence of tokens will match the * elementValuePairs() syntax. false otherwise. */ private boolean speculateFullAnnotation() { state.backtracking++; int start = input.mark(); try { exprParser.fullAnnotation( null ); } catch ( RecognitionException re ) { System.err.println( "impossible: " + re ); re.printStackTrace(); } boolean success = ! state.failed; input.rewind( start ); state.backtracking--; state.failed = false; return success; } /* ------------------------------------------------------------------------------------------------ * UTILITY RULES * ------------------------------------------------------------------------------------------------ */ /** * Matches a type name * * type := ID typeArguments? ( DOT ID typeArguments? )* (LEFT_SQUARE RIGHT_SQUARE)* * * @return * @throws RecognitionException */ public String type() throws RecognitionException { String type = ""; try { int first = input.index(), last = first; match( input, DRL5Lexer.ID, null, new int[]{DRL5Lexer.DOT, DRL5Lexer.LESS}, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return type; if ( input.LA( 1 ) == DRL5Lexer.LESS ) { typeArguments(); if ( state.failed ) return type; } while ( input.LA( 1 ) == DRL5Lexer.DOT && input.LA( 2 ) == DRL5Lexer.ID ) { match( input, DRL5Lexer.DOT, null, new int[]{DRL5Lexer.ID}, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return type; match( input, DRL5Lexer.ID, null, new int[]{DRL5Lexer.DOT}, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return type; if ( input.LA( 1 ) == DRL5Lexer.LESS ) { typeArguments(); if ( state.failed ) return type; } } while ( input.LA( 1 ) == DRL5Lexer.LEFT_SQUARE && input.LA( 2 ) == DRL5Lexer.RIGHT_SQUARE ) { match( input, DRL5Lexer.LEFT_SQUARE, null, new int[]{DRL5Lexer.RIGHT_SQUARE}, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return type; match( input, DRL5Lexer.RIGHT_SQUARE, null, null, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return type; } last = input.LT( -1 ).getTokenIndex(); type = input.toString( first, last ); type = type.replace( " ", "" ); } catch ( RecognitionException re ) { reportError( re ); } return type; } /** * Matches type arguments * * typeArguments := LESS typeArgument (COMMA typeArgument)* GREATER * * @return * @throws RecognitionException */ public String typeArguments() throws RecognitionException { String typeArguments = ""; try { int first = input.index(); Token token = match( input, DRL5Lexer.LESS, null, new int[]{DRL5Lexer.QUESTION, DRL5Lexer.ID}, DroolsEditorType.SYMBOL ); if ( state.failed ) return typeArguments; typeArgument(); if ( state.failed ) return typeArguments; while ( input.LA( 1 ) == DRL5Lexer.COMMA ) { token = match( input, DRL5Lexer.COMMA, null, new int[]{DRL5Lexer.QUESTION, DRL5Lexer.ID}, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return typeArguments; typeArgument(); if ( state.failed ) return typeArguments; } token = match( input, DRL5Lexer.GREATER, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return typeArguments; typeArguments = input.toString( first, token.getTokenIndex() ); } catch ( RecognitionException re ) { reportError( re ); } return typeArguments; } /** * Matches a type argument * * typeArguments := QUESTION (( EXTENDS | SUPER ) type )? * | type * ; * * @return * @throws RecognitionException */ public String typeArgument() throws RecognitionException { String typeArgument = ""; try { int first = input.index(), last = first; int next = input.LA( 1 ); switch ( next ) { case DRL5Lexer.QUESTION : match( input, DRL5Lexer.QUESTION, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return typeArgument; if ( helper.validateIdentifierKey( DroolsSoftKeywords.EXTENDS ) ) { match( input, DRL5Lexer.ID, DroolsSoftKeywords.EXTENDS, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return typeArgument; type(); if ( state.failed ) return typeArgument; } else if ( helper.validateIdentifierKey( DroolsSoftKeywords.SUPER ) ) { match( input, DRL5Lexer.ID, DroolsSoftKeywords.SUPER, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return typeArgument; type(); if ( state.failed ) return typeArgument; } break; case DRL5Lexer.ID : type(); if ( state.failed ) return typeArgument; break; default : // TODO: raise error } last = input.LT( -1 ).getTokenIndex(); typeArgument = input.toString( first, last ); } catch ( RecognitionException re ) { reportError( re ); } return typeArgument; } /** * Matches a qualified identifier * * qualifiedIdentifier := ID ( DOT ID )* * * @return * @throws RecognitionException */ public String qualifiedIdentifier() throws RecognitionException { String qi = ""; try { Token first = match( input, DRL5Lexer.ID, null, new int[]{DRL5Lexer.DOT}, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return qi; Token last = first; while ( input.LA( 1 ) == DRL5Lexer.DOT && input.LA( 2 ) == DRL5Lexer.ID ) { last = match( input, DRL5Lexer.DOT, null, new int[]{DRL5Lexer.ID}, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return qi; last = match( input, DRL5Lexer.ID, null, new int[]{DRL5Lexer.DOT}, DroolsEditorType.IDENTIFIER ); if ( state.failed ) return qi; } qi = input.toString( first, last ); qi = qi.replace( " ", "" ); } catch ( RecognitionException re ) { reportError( re ); } return qi; } /** * Matches a conditional expression * * @return * @throws RecognitionException */ public String conditionalExpression() throws RecognitionException { int first = input.index(); exprParser.conditionalExpression(); if ( state.failed ) return null; if ( state.backtracking == 0 && input.index() > first ) { // expression consumed something String expr = input.toString( first, input.LT( -1 ).getTokenIndex() ); return expr; } return null; } /** * Matches a conditional || expression * * @return * @throws RecognitionException */ public String conditionalOrExpression() throws RecognitionException { int first = input.index(); exprParser.conditionalOrExpression(); if ( state.failed ) return null; if ( state.backtracking == 0 && input.index() > first ) { // expression consumed something String expr = input.toString( first, input.LT( -1 ).getTokenIndex() ); return expr; } return null; } /** * Matches a chunk started by the leftDelimiter and ended by the rightDelimiter. * * @param leftDelimiter * @param rightDelimiter * @param location * @return the matched chunk without the delimiters */ public String chunk( final int leftDelimiter, final int rightDelimiter, final int location ) { String chunk = ""; int first = -1, last = first; try { match( input, leftDelimiter, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return chunk; if ( state.backtracking == 0 && location >= 0 ) { helper.emit( location ); } int nests = 0; first = input.index(); while ( input.LA( 1 ) != DRL5Lexer.EOF && ( input.LA( 1 ) != rightDelimiter || nests > 0 ) ) { if ( input.LA( 1 ) == rightDelimiter ) { nests--; } else if ( input.LA( 1 ) == leftDelimiter ) { nests++; } input.consume(); } last = input.LT( -1 ).getTokenIndex(); for (int i = first; i < last + 1; i++) { helper.emit( input.get( i ), DroolsEditorType.CODE_CHUNK ); } match( input, rightDelimiter, null, null, DroolsEditorType.SYMBOL ); if ( state.failed ) return chunk; } catch ( RecognitionException re ) { reportError( re ); } finally { if ( last >= first ) { chunk = input.toString( first, last ); } } return chunk; } /* ------------------------------------------------------------------------------------------------ * GENERAL UTILITY METHODS * ------------------------------------------------------------------------------------------------ */ /** * Match current input symbol against ttype and optionally * check the text of the token against text. Attempt * single token insertion or deletion error recovery. If * that fails, throw MismatchedTokenException. */ private Token match( TokenStream input, int ttype, String text, int[] follow, DroolsEditorType etype ) throws RecognitionException { Token matchedSymbol = null; matchedSymbol = input.LT( 1 ); if ( input.LA( 1 ) == ttype && (text == null || text.equals( matchedSymbol.getText() )) ) { input.consume(); state.errorRecovery = false; state.failed = false; helper.emit( matchedSymbol, etype ); return matchedSymbol; } if ( state.backtracking > 0 ) { state.failed = true; return matchedSymbol; } matchedSymbol = recoverFromMismatchedToken( input, ttype, text, follow ); helper.emit( matchedSymbol, etype ); return matchedSymbol; } /** Attempt to recover from a single missing or extra token. * * EXTRA TOKEN * * LA(1) is not what we are looking for. If LA(2) has the right token, * however, then assume LA(1) is some extra spurious token. Delete it * and LA(2) as if we were doing a normal match(), which advances the * input. * * MISSING TOKEN * * If current token is consistent with what could come after * ttype then it is ok to "insert" the missing token, else throw * exception For example, Input "i=(3;" is clearly missing the * ')'. When the parser returns from the nested call to expr, it * will have call chain: * * stat -> expr -> atom * * and it will be trying to match the ')' at this point in the * derivation: * * => ID '=' '(' INT ')' ('+' atom)* ';' * ^ * match() will see that ';' doesn't match ')' and report a * mismatched token error. To recover, it sees that LA(1)==';' * is in the set of tokens that can follow the ')' token * reference in rule atom. It can assume that you forgot the ')'. */ protected Token recoverFromMismatchedToken( TokenStream input, int ttype, String text, int[] follow ) throws RecognitionException { RecognitionException e = null; // if next token is what we are looking for then "delete" this token if ( mismatchIsUnwantedToken( input, ttype, text ) ) { e = new UnwantedTokenException( ttype, input ); input.consume(); // simply delete extra token reportError( e ); // report after consuming so AW sees the token in the exception // we want to return the token we're actually matching Token matchedSymbol = input.LT( 1 ); input.consume(); // move past ttype token as if all were ok return matchedSymbol; } // can't recover with single token deletion, try insertion if ( mismatchIsMissingToken( input, follow ) ) { e = new MissingTokenException( ttype, input, null ); reportError( e ); // report after inserting so AW sees the token in the exception return null; } // even that didn't work; must throw the exception if ( text != null ) { e = new DroolsMismatchedTokenException( ttype, text, input ); } else { e = new MismatchedTokenException( ttype, input ); } throw e; } public boolean mismatchIsUnwantedToken( TokenStream input, int ttype, String text ) { return (input.LA( 2 ) == ttype && (text == null || text.equals( input.LT( 2 ).getText() ))); } public boolean mismatchIsMissingToken( TokenStream input, int[] follow ) { if ( follow == null ) { // we have no information about the follow; we can only consume // a single token and hope for the best return false; } // TODO: implement this error recovery strategy return false; } private String safeStripDelimiters( String value, String left, String right ) { if ( value != null ) { value = value.trim(); if ( value.length() >= left.length() + right.length() && value.startsWith( left ) && value.endsWith( right ) ) { value = value.substring( left.length(), value.length() - right.length() ); } } return value; } private String safeStripStringDelimiters( String value ) { if ( value != null ) { value = value.trim(); if ( value.length() >= 2 && value.startsWith( "\"" ) && value.endsWith( "\"" ) ) { value = value.substring( 1, value.length() - 1 ); } } return value; } }