From 84512926a9b2ec5134e8ed69ae205aef09a1d754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Locker?= <jiri.locker@gmail.com> Date: Mon, 15 Jan 2024 21:25:22 +0100 Subject: [PATCH] Move necessary files without any additional modifications --- .../org/drools/parser/DRL6Expressions.g4 | 836 ++++++++++++++++++ .../org/drools/parser/DRLExpressions.java | 54 ++ .../java/org/drools/parser/DrlExprParser.java | 88 ++ .../parser/DroolsParserExceptionFactory.java | 380 ++++++++ .../java/org/drools/parser/ParserHelper.java | 672 ++++++++++++++ .../org/drools/parser/DRLExprParserTest.java | 270 ++++++ 6 files changed, 2300 insertions(+) create mode 100644 drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRL6Expressions.g4 create mode 100644 drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLExpressions.java create mode 100644 drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DrlExprParser.java create mode 100644 drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DroolsParserExceptionFactory.java create mode 100644 drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/ParserHelper.java create mode 100644 drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/DRLExprParserTest.java diff --git a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRL6Expressions.g4 b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRL6Expressions.g4 new file mode 100644 index 00000000000..b531c38feff --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRL6Expressions.g4 @@ -0,0 +1,836 @@ +parser grammar DRL6Expressions; + +options { + language = Java; + tokenVocab = DRL6Lexer; + superClass=DRLExpressions; +} + +@header { + package org.drools.drl.parser.lang; + + import java.util.LinkedList; + import org.drools.drl.parser.DroolsParserException; + import org.drools.drl.parser.lang.ParserHelper; + import org.drools.drl.parser.lang.DroolsParserExceptionFactory; + import org.drools.drl.parser.lang.Location; + + import org.drools.drl.ast.dsl.AnnotatedDescrBuilder; + import org.drools.drl.ast.dsl.AnnotationDescrBuilder; + + import org.drools.drl.ast.descr.AtomicExprDescr; + import org.drools.drl.ast.descr.AnnotatedBaseDescr; + import org.drools.drl.ast.descr.AnnotationDescr; + import org.drools.drl.ast.descr.BaseDescr; + import org.drools.drl.ast.descr.ConstraintConnectiveDescr; + import org.drools.drl.ast.descr.RelationalExprDescr; + import org.drools.drl.ast.descr.BindingDescr; +} + +@members { + private ParserHelper helper; + + public DRL6Expressions(TokenStream input, + RecognizerSharedState state, + ParserHelper helper ) { + this( input, + state ); + this.helper = helper; + } + + public ParserHelper getHelper() { return helper; } + public boolean hasErrors() { return helper.hasErrors(); } + public List<DroolsParserException> getErrors() { return helper.getErrors(); } + public List<String> getErrorMessages() { return helper.getErrorMessages(); } + public void enableEditorInterface() { helper.enableEditorInterface(); } + public void disableEditorInterface() { helper.disableEditorInterface(); } + public LinkedList<DroolsSentence> getEditorInterface() { return helper.getEditorInterface(); } + public void reportError(RecognitionException ex) { helper.reportError( ex ); } + public void emitErrorMessage(String msg) {} + + private boolean buildDescr; + private int inMap = 0; + private int ternOp = 0; + private boolean hasBindings; + public void setBuildDescr( boolean build ) { this.buildDescr = build; } + public boolean isBuildDescr() { return this.buildDescr; } + + public void setLeftMostExpr( String value ) { helper.setLeftMostExpr( value ); } + public String getLeftMostExpr() { return helper.getLeftMostExpr(); } + + public void setHasBindings( boolean value ) { this.hasBindings = value; } + public boolean hasBindings() { return this.hasBindings; } + + private boolean isNotEOF() { + if (state.backtracking != 0){ + return false; + } + if (input.get( input.index() - 1 ).getType() == DRL6Lexer.WS){ + return true; + } + if (input.LA(-1) == DRL6Lexer.LEFT_PAREN){ + return true; + } + return input.get( input.index() ).getType() != DRL6Lexer.EOF; + } + + private boolean notStartWithNewline() { + int currentTokenIndex = input.index(); // current position in input stream + Token previousHiddenToken = input.get(currentTokenIndex - 1); + String previousHiddenTokenText = previousHiddenToken.getText(); + return !previousHiddenTokenText.contains("\n"); + } +} + +// Alter code generation so catch-clauses get replace with +// this action. +@rulecatch { +catch (RecognitionException re) { + throw re; +} +} + +// -------------------------------------------------------- +// GENERAL RULES +// -------------------------------------------------------- +literal + : STRING { helper.emit($STRING, DroolsEditorType.STRING_CONST); } + | DECIMAL { helper.emit($DECIMAL, DroolsEditorType.NUMERIC_CONST); } + | HEX { helper.emit($HEX, DroolsEditorType.NUMERIC_CONST); } + | FLOAT { helper.emit($FLOAT, DroolsEditorType.NUMERIC_CONST); } + | BOOL { helper.emit($BOOL, DroolsEditorType.BOOLEAN_CONST); } + | NULL { helper.emit($NULL, DroolsEditorType.NULL_CONST); } + | TIME_INTERVAL { helper.emit($TIME_INTERVAL, DroolsEditorType.NULL_CONST); } + | STAR { helper.emit($STAR, DroolsEditorType.NUMERIC_CONST); } // this means "infinity" in Drools + ; + +operator returns [boolean negated, String opr] +@init{ if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); helper.setHasOperator( true ); } +@after{ if( state.backtracking == 0 && input.LA( 1 ) != DRL6Lexer.EOF) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } } + : x=TILDE? + ( op=EQUALS { $negated = false; $opr=($x != null ? $x.text : "")+$op.text; helper.emit($op, DroolsEditorType.SYMBOL); } + | op=NOT_EQUALS { $negated = false; $opr=($x != null ? $x.text : "")+$op.text; helper.emit($op, DroolsEditorType.SYMBOL); } + | rop=relationalOp { $negated = $rop.negated; $opr=($x != null ? $x.text : "")+$rop.opr; } + ) + ; + + + +relationalOp returns [boolean negated, String opr, java.util.List<String> params] +@init{ if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); helper.setHasOperator( true ); } +@after{ if( state.backtracking == 0 && input.LA( 1 ) != DRL6Lexer.EOF) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } } + : ( op=LESS_EQUALS { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | op=GREATER_EQUALS { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | op=LESS { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | op=GREATER { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | xop=complexOp { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | not_key nop=neg_operator_key { $negated = true; $opr=$nop.text;} + | cop=operator_key { $negated = false; $opr=$cop.text;} + ) + ; + +complexOp returns [String opr] + : t=TILDE e=EQUALS_ASSIGN { $opr=$t.text+$e.text; } + ; + +typeList + : type (COMMA type)* + ; + +type + : tm=typeMatch + ; + +typeMatch + : (primitiveType) => ( primitiveType ((LEFT_SQUARE RIGHT_SQUARE)=> LEFT_SQUARE RIGHT_SQUARE)* ) + | ( ID ((typeArguments)=>typeArguments)? (DOT ID ((typeArguments)=>typeArguments)? )* ((LEFT_SQUARE RIGHT_SQUARE)=> LEFT_SQUARE RIGHT_SQUARE)* ) + ; + +typeArguments + : LESS typeArgument (COMMA typeArgument)* GREATER + ; + +typeArgument + : type + | QUESTION ((extends_key | super_key) type)? + ; + +// -------------------------------------------------------- +// EXPRESSIONS +// -------------------------------------------------------- +// the following dymmy rule is to force the AT symbol to be +// included in the follow set of the expression on the DFAs +dummy + : expression ( AT | SEMICOLON | EOF | ID | RIGHT_PAREN ) ; + +dummy2 + : relationalExpression EOF; + +// top level entry point for arbitrary expression parsing +expression returns [BaseDescr result] + : left=conditionalExpression { if( buildDescr ) { $result = $left.result; } } + ((assignmentOperator) => op=assignmentOperator right=expression)? + ; + +conditionalExpression returns [BaseDescr result] + : left=conditionalOrExpression { if( buildDescr ) { $result = $left.result; } } + ternaryExpression? + ; + +ternaryExpression +@init{ ternOp++; } + : QUESTION ts=expression COLON fs=expression + ; +finally { ternOp--; } + + +fullAnnotation [AnnotatedDescrBuilder inDescrBuilder] returns [AnnotationDescr result] +@init{ String n = ""; AnnotationDescrBuilder annoBuilder = null; } + : AT name=ID { n = $name.text; } ( DOT x=ID { n += "." + $x.text; } )* + { if( buildDescr ) { + if ( inDescrBuilder == null ) { + $result = new AnnotationDescr( n ); + } else { + annoBuilder = inDescrBuilder instanceof AnnotationDescrBuilder ? + ((AnnotationDescrBuilder) inDescrBuilder).newAnnotation( n ) : inDescrBuilder.newAnnotation( n ); + $result = (AnnotationDescr) annoBuilder.getDescr(); + } + } + } + annotationArgs[result, annoBuilder] + ; + +annotationArgs [AnnotationDescr descr, AnnotatedDescrBuilder inDescrBuilder] + : LEFT_PAREN + ( + (ID EQUALS_ASSIGN) => annotationElementValuePairs[descr, inDescrBuilder] + | value=annotationValue[inDescrBuilder] { if ( buildDescr ) { $descr.setValue( $value.result ); } } + )? + RIGHT_PAREN + ; + +annotationElementValuePairs [AnnotationDescr descr, AnnotatedDescrBuilder inDescrBuilder] + : annotationElementValuePair[descr, inDescrBuilder] ( COMMA annotationElementValuePair[descr, inDescrBuilder] )* + ; + +annotationElementValuePair [AnnotationDescr descr, AnnotatedDescrBuilder inDescrBuilder] + : key=ID EQUALS_ASSIGN val=annotationValue[inDescrBuilder] { if ( buildDescr ) { $descr.setKeyValue( $key.text, $val.result ); } } + ; + +annotationValue[AnnotatedDescrBuilder inDescrBuilder] returns [Object result] + : exp=expression { if ( buildDescr ) $result = $exp.text; } + | annos=annotationArray[inDescrBuilder] { if ( buildDescr ) $result = $annos.result.toArray(); } + | anno=fullAnnotation[inDescrBuilder] { if ( buildDescr ) $result = $anno.result; } + ; + +annotationArray[AnnotatedDescrBuilder inDescrBuilder] returns [java.util.List result] +@init { $result = new java.util.ArrayList();} + : LEFT_CURLY ( anno=annotationValue[inDescrBuilder] { $result.add( $anno.result ); } + ( COMMA anno=annotationValue[inDescrBuilder] { $result.add( $anno.result ); } )* )? + RIGHT_CURLY + ; + + + +conditionalOrExpression returns [BaseDescr result] + : left=conditionalAndExpression { if( buildDescr ) { $result = $left.result; } } + ( DOUBLE_PIPE + { if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); } + args=fullAnnotation[null]? right=conditionalAndExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newOr(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + if ( args != null ) { descr.addAnnotation( $args.result ); } + $result = descr; + } + } + )* + ; + +conditionalAndExpression returns [BaseDescr result] + : left=inclusiveOrExpression { if( buildDescr ) { $result = $left.result; } } + ( DOUBLE_AMPER + { if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); } + args=fullAnnotation[null]? right=inclusiveOrExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newAnd(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + if ( args != null ) { descr.addAnnotation( $args.result ); } + $result = descr; + } + } + )* + ; + +inclusiveOrExpression returns [BaseDescr result] + : left=exclusiveOrExpression { if( buildDescr ) { $result = $left.result; } } + ( PIPE right=exclusiveOrExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newIncOr(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + $result = descr; + } + } + )* + ; + +exclusiveOrExpression returns [BaseDescr result] + : left=andExpression { if( buildDescr ) { $result = $left.result; } } + ( XOR right=andExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newXor(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + $result = descr; + } + } + )* + ; + +andExpression returns [BaseDescr result] + : left=equalityExpression { if( buildDescr ) { $result = $left.result; } } + ( AMPER right=equalityExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newIncAnd(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + $result = descr; + } + } + )* + ; + +equalityExpression returns [BaseDescr result] + : left=instanceOfExpression { if( buildDescr ) { $result = $left.result; } } + ( ( op=EQUALS | op=NOT_EQUALS ) + { helper.setHasOperator( true ); + if( input.LA( 1 ) != DRL6Lexer.EOF ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + right=instanceOfExpression + { if( buildDescr ) { + $result = new RelationalExprDescr( $op.text, false, null, $left.result, $right.result ); + } + } + )* + ; + +instanceOfExpression returns [BaseDescr result] + : left=inExpression { if( buildDescr ) { $result = $left.result; } } + ( op=instanceof_key + { helper.setHasOperator( true ); + if( input.LA( 1 ) != DRL6Lexer.EOF ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + right=type + { if( buildDescr ) { + $result = new RelationalExprDescr( $op.text, false, null, $left.result, new AtomicExprDescr($right.text) ); + } + } + )? + ; + +inExpression returns [BaseDescr result] +@init { ConstraintConnectiveDescr descr = null; BaseDescr leftDescr = null; BindingDescr binding = null; } +@after { if( binding != null && descr != null ) descr.addOrMerge( binding ); } + : left=relationalExpression + { if( buildDescr ) { $result = $left.result; } + if( $left.result instanceof BindingDescr ) { + binding = (BindingDescr)$left.result; + leftDescr = new AtomicExprDescr( binding.getExpression() ); + } else { + leftDescr = $left.result; + } + } + ((not_key in_key)=> not_key in=in_key LEFT_PAREN + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + e1=expression + { descr = ConstraintConnectiveDescr.newAnd(); + RelationalExprDescr rel = new RelationalExprDescr( "!=", false, null, leftDescr, $e1.result ); + descr.addOrMerge( rel ); + $result = descr; + } + (COMMA e2=expression + { RelationalExprDescr rel = new RelationalExprDescr( "!=", false, null, leftDescr, $e2.result ); + descr.addOrMerge( rel ); + } + )* RIGHT_PAREN + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_END ); } + | in=in_key LEFT_PAREN + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + e1=expression + { descr = ConstraintConnectiveDescr.newOr(); + RelationalExprDescr rel = new RelationalExprDescr( "==", false, null, leftDescr, $e1.result ); + descr.addOrMerge( rel ); + $result = descr; + } + (COMMA e2=expression + { RelationalExprDescr rel = new RelationalExprDescr( "==", false, null, leftDescr, $e2.result ); + descr.addOrMerge( rel ); + } + )* RIGHT_PAREN + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_END ); } + )? + ; + +relationalExpression returns [BaseDescr result] +scope { BaseDescr lsd; } +@init { $relationalExpression::lsd = null; } + : left=shiftExpression + { if( buildDescr ) { + if ( $left.result == null ) { + $result = new AtomicExprDescr( $left.text ); + } else if ( $left.result instanceof AtomicExprDescr ) { + if ( $left.text.equals(((AtomicExprDescr)$left.result).getExpression()) ) { + $result = $left.result; + } else { + $result = new AtomicExprDescr( $left.text ) ; + } + } else if ( $left.result instanceof BindingDescr ) { + if ( $left.text.equals(((BindingDescr)$left.result).getExpression()) ) { + $result = $left.result; + } else { + BindingDescr bind = (BindingDescr) $left.result; + int offset = bind.isUnification() ? 2 : 1; + String fullExpression = $left.text.substring( $left.text.indexOf( ":" ) + offset ).trim(); + $result = new BindingDescr( bind.getVariable(), bind.getExpression(), fullExpression, bind.isUnification() ); + } + } else { + $result = $left.result; + } + $relationalExpression::lsd = $result; + } + } + ( ( operator | LEFT_PAREN )=> right=orRestriction + { if( buildDescr ) { + $result = $right.result; + $relationalExpression::lsd = $result; + } + } + )* + ; + +orRestriction returns [BaseDescr result] + : left=andRestriction { if( buildDescr ) { $result = $left.result; } } + ( (DOUBLE_PIPE fullAnnotation[null]? andRestriction)=>lop=DOUBLE_PIPE args=fullAnnotation[null]? right=andRestriction + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newOr(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + if ( args != null ) { descr.addAnnotation( $args.result ); } + $result = descr; + } + } + )* EOF? + ; + +andRestriction returns [BaseDescr result] + : left=singleRestriction { if( buildDescr ) { $result = $left.result; } } + ( (DOUBLE_AMPER fullAnnotation[null]? operator)=>lop=DOUBLE_AMPER + { if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); } + args=fullAnnotation[null]?right=singleRestriction + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newAnd(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + if ( args != null ) { descr.addAnnotation( $args.result ); } + $result = descr; + } + } + )* + ; + +singleRestriction returns [BaseDescr result] + : op=operator + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + ( (squareArguments shiftExpression)=> sa=squareArguments value=shiftExpression + | value=shiftExpression + ) + { if( buildDescr ) { + BaseDescr descr = ( $value.result != null && + ( (!($value.result instanceof AtomicExprDescr)) || + ($value.text.equals(((AtomicExprDescr)$value.result).getExpression())) )) ? + $value.result : + new AtomicExprDescr( $value.text ) ; + $result = new RelationalExprDescr( $op.opr, $op.negated, $sa.args, $relationalExpression::lsd, descr ); + if( $relationalExpression::lsd instanceof BindingDescr ) { + $relationalExpression::lsd = new AtomicExprDescr( ((BindingDescr)$relationalExpression::lsd).getExpression() ); + } + } + helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_END ); + } + | LEFT_PAREN or=orRestriction RIGHT_PAREN { $result = $or.result; } + ; + + + +shiftExpression returns [BaseDescr result] + : left=additiveExpression { if( buildDescr ) { $result = $left.result; } } + ( (shiftOp)=>shiftOp additiveExpression )* + ; + +shiftOp + : ( LESS LESS + | GREATER GREATER GREATER + | GREATER GREATER ) + ; + +additiveExpression returns [BaseDescr result] + : left=multiplicativeExpression { if( buildDescr ) { $result = $left.result; } } + ( (PLUS|MINUS)=> (PLUS | MINUS) multiplicativeExpression )* + ; + +multiplicativeExpression returns [BaseDescr result] + : left=unaryExpression { if( buildDescr ) { $result = $left.result; } } + ( ( STAR | DIV | MOD ) unaryExpression )* + ; + +unaryExpression returns [BaseDescr result] + : PLUS ue=unaryExpression + { if( buildDescr ) { + $result = $ue.result; + if( $result instanceof AtomicExprDescr ) { + ((AtomicExprDescr)$result).setExpression( "+" + ((AtomicExprDescr)$result).getExpression() ); + } + } } + | MINUS ue=unaryExpression + { if( buildDescr ) { + $result = $ue.result; + if( $result instanceof AtomicExprDescr ) { + ((AtomicExprDescr)$result).setExpression( "-" + ((AtomicExprDescr)$result).getExpression() ); + } + } } + | INCR primary + | DECR primary + | left=unaryExpressionNotPlusMinus { if( buildDescr ) { $result = $left.result; } } + ; + +unaryExpressionNotPlusMinus returns [BaseDescr result] +@init { boolean isLeft = false; BindingDescr bind = null;} + : TILDE unaryExpression + | NEGATION ue=unaryExpression + { + if( buildDescr && ue != null ) { + $result = ue.negate(); + } + } + | (castExpression)=>castExpression + | (backReferenceExpression)=>backReferenceExpression + | { isLeft = helper.getLeftMostExpr() == null;} + ( ({inMap == 0 && ternOp == 0 && input.LA(2) == DRL6Lexer.COLON}? (var=ID COLON + { hasBindings = true; helper.emit($var, DroolsEditorType.IDENTIFIER_VARIABLE); helper.emit($COLON, DroolsEditorType.SYMBOL); if( buildDescr ) { bind = new BindingDescr($var.text, null, false); helper.setStart( bind, $var ); } } )) + | ({inMap == 0 && ternOp == 0 && input.LA(2) == DRL6Lexer.UNIFY}? (var=ID UNIFY + { hasBindings = true; helper.emit($var, DroolsEditorType.IDENTIFIER_VARIABLE); helper.emit($UNIFY, DroolsEditorType.SYMBOL); if( buildDescr ) { bind = new BindingDescr($var.text, null, true); helper.setStart( bind, $var ); } } )) + )? + + ( (xpathSeparator ID)=>left2=xpathPrimary { if( buildDescr ) { $result = $left2.result; } } + | left1=primary { if( buildDescr ) { $result = $left1.result; } } + ) + + ((selector)=>selector)* + { + if( buildDescr ) { + String expr = $unaryExpressionNotPlusMinus.text; + if( isLeft ) { + helper.setLeftMostExpr( expr ); + } + if( bind != null ) { + if( bind.isUnification() ) { + expr = expr.substring( expr.indexOf( ":=" ) + 2 ).trim(); + } else { + expr = expr.substring( expr.indexOf( ":" ) + 1 ).trim(); + } + bind.setExpressionAndBindingField( expr ); + helper.setEnd( bind ); + $result = bind; + } + } + } + ((INCR|DECR)=> (INCR|DECR))? + ; + +castExpression + : (LEFT_PAREN primitiveType) => LEFT_PAREN primitiveType RIGHT_PAREN expr=unaryExpression + | (LEFT_PAREN type) => LEFT_PAREN type RIGHT_PAREN unaryExpressionNotPlusMinus + ; + +backReferenceExpression + : (DOT DOT DIV) => (DOT DOT DIV)+ unaryExpressionNotPlusMinus + ; + +primitiveType + : boolean_key + | char_key + | byte_key + | short_key + | int_key + | long_key + | float_key + | double_key + ; + +xpathSeparator + : DIV + | QUESTION_DIV + ; + +xpathPrimary returns [BaseDescr result] + : xpathChunk ({notStartWithNewline()}? xpathChunk)* + ; + +xpathChunk returns [BaseDescr result] + : (xpathSeparator ID)=> xpathSeparator ID (DOT ID)* (HASH ID)? (LEFT_SQUARE xpathExpressionList RIGHT_SQUARE)? + ; + +xpathExpressionList returns [java.util.List<String> exprs] +@init { $exprs = new java.util.ArrayList<String>();} + : f=expression { $exprs.add( $f.text ); } + (COMMA s=expression { $exprs.add( $s.text ); })* + ; + +primary returns [BaseDescr result] + : (LEFT_PAREN)=> expr=parExpression { if( buildDescr ) { $result = $expr.result; } } + | (nonWildcardTypeArguments)=> nonWildcardTypeArguments (explicitGenericInvocationSuffix | this_key arguments) + | (literal)=> literal { if( buildDescr ) { $result = new AtomicExprDescr( $literal.text, true ); } } + //| this_key ({!helper.validateSpecialID(2)}?=> DOT ID)* ({helper.validateIdentifierSufix()}?=> identifierSuffix)? + | (super_key)=> super_key superSuffix + | (new_key)=> new_key creator + | (primitiveType)=> primitiveType (LEFT_SQUARE RIGHT_SQUARE)* DOT class_key + //| void_key DOT class_key + | (inlineMapExpression)=> inlineMapExpression + | (inlineListExpression)=> inlineListExpression + | (ID)=>i1=ID { helper.emit($i1, DroolsEditorType.IDENTIFIER); } + ( + ( (DOT ID)=>d=DOT i2=ID { helper.emit($d, DroolsEditorType.SYMBOL); helper.emit($i2, DroolsEditorType.IDENTIFIER); } ) + | + ( ((DOT|NULL_SAFE_DOT) LEFT_PAREN)=>d=(DOT|NULL_SAFE_DOT) LEFT_PAREN { helper.emit($d, DroolsEditorType.SYMBOL); helper.emit($LEFT_PAREN, DroolsEditorType.SYMBOL); } + expression (COMMA { helper.emit($COMMA, DroolsEditorType.SYMBOL); } expression)* + RIGHT_PAREN { helper.emit($RIGHT_PAREN, DroolsEditorType.SYMBOL); } + ) + | + ( (HASH ID)=>h=HASH i2=ID { helper.emit($h, DroolsEditorType.SYMBOL); helper.emit($i2, DroolsEditorType.IDENTIFIER); } ) + | + ( (NULL_SAFE_DOT ID)=>n=NULL_SAFE_DOT i2=ID { helper.emit($n, DroolsEditorType.SYMBOL); helper.emit($i2, DroolsEditorType.IDENTIFIER); } ) + )* ((identifierSuffix)=>identifierSuffix)? + ; + +inlineListExpression + : LEFT_SQUARE expressionList? RIGHT_SQUARE + ; + +inlineMapExpression +@init{ inMap++; } + : LEFT_SQUARE mapExpressionList RIGHT_SQUARE + ; +finally { inMap--; } + +mapExpressionList + : mapEntry (COMMA mapEntry)* + ; + +mapEntry + : expression COLON expression + ; + +parExpression returns [BaseDescr result] + : LEFT_PAREN expr=expression RIGHT_PAREN + { if( buildDescr ) { + $result = $expr.result; + if( $result instanceof AtomicExprDescr ) { + ((AtomicExprDescr)$result).setExpression("(" +((AtomicExprDescr)$result).getExpression() + ")" ); + } + } + } + ; + +identifierSuffix + : (LEFT_SQUARE RIGHT_SQUARE)=>(LEFT_SQUARE { helper.emit($LEFT_SQUARE, DroolsEditorType.SYMBOL); } + RIGHT_SQUARE { helper.emit($RIGHT_SQUARE, DroolsEditorType.SYMBOL); } )+ + DOT { helper.emit($DOT, DroolsEditorType.SYMBOL); } class_key + | ((LEFT_SQUARE) => LEFT_SQUARE { helper.emit($LEFT_SQUARE, DroolsEditorType.SYMBOL); } + expression + RIGHT_SQUARE { helper.emit($RIGHT_SQUARE, DroolsEditorType.SYMBOL); } )+ // can also be matched by selector, but do here + | arguments +// | DOT class_key +// | DOT explicitGenericInvocation +// | DOT this_key +// | DOT super_key arguments +// | DOT new_key (nonWildcardTypeArguments)? innerCreator + ; + +creator + : nonWildcardTypeArguments? createdName + (arrayCreatorRest | classCreatorRest) + ; + +createdName + : ID typeArguments? + ( DOT ID typeArguments?)* + | primitiveType + ; + +innerCreator + : {!(helper.validateIdentifierKey(DroolsSoftKeywords.INSTANCEOF))}?=> ID classCreatorRest + ; + +arrayCreatorRest + : LEFT_SQUARE + ( RIGHT_SQUARE (LEFT_SQUARE RIGHT_SQUARE)* arrayInitializer + | expression RIGHT_SQUARE ({!helper.validateLT(2,"]")}?=>LEFT_SQUARE expression RIGHT_SQUARE)* ((LEFT_SQUARE RIGHT_SQUARE)=> LEFT_SQUARE RIGHT_SQUARE)* + ) + ; + +variableInitializer + : arrayInitializer + | expression + ; + +arrayInitializer + : LEFT_CURLY (variableInitializer (COMMA variableInitializer)* (COMMA)? )? RIGHT_CURLY + ; + +classCreatorRest + : arguments //classBody? //sotty: restored classBody to allow for inline, anonymous classes + ; + +explicitGenericInvocation + : nonWildcardTypeArguments arguments + ; + +nonWildcardTypeArguments + : LESS typeList GREATER + ; + +explicitGenericInvocationSuffix + : super_key superSuffix + | ID arguments + ; + +selector + : (DOT super_key)=>DOT { helper.emit($DOT, DroolsEditorType.SYMBOL); } super_key superSuffix + | (DOT new_key)=>DOT { helper.emit($DOT, DroolsEditorType.SYMBOL); } new_key (nonWildcardTypeArguments)? innerCreator + | (DOT ID)=>DOT { helper.emit($DOT, DroolsEditorType.SYMBOL); } + ID { helper.emit($ID, DroolsEditorType.IDENTIFIER); } + ((LEFT_PAREN) => arguments)? + | (NULL_SAFE_DOT ID)=>NULL_SAFE_DOT { helper.emit($NULL_SAFE_DOT, DroolsEditorType.SYMBOL); } + ID { helper.emit($ID, DroolsEditorType.IDENTIFIER); } + ((LEFT_PAREN) => arguments)? + //| DOT this_key + | (LEFT_SQUARE)=>LEFT_SQUARE { helper.emit($LEFT_SQUARE, DroolsEditorType.SYMBOL); } + expression + RIGHT_SQUARE { helper.emit($RIGHT_SQUARE, DroolsEditorType.SYMBOL); } + ; + +superSuffix + : arguments + | DOT ID ((LEFT_PAREN) => arguments)? + ; + +squareArguments returns [java.util.List<String> args] + : LEFT_SQUARE (el=expressionList { $args = $el.exprs; })? RIGHT_SQUARE + ; + +arguments + : LEFT_PAREN { helper.emit($LEFT_PAREN, DroolsEditorType.SYMBOL); } + expressionList? + RIGHT_PAREN { helper.emit($RIGHT_PAREN, DroolsEditorType.SYMBOL); } + ; + +expressionList returns [java.util.List<String> exprs] +@init { $exprs = new java.util.ArrayList<String>();} + : f=expression { $exprs.add( $f.text ); } + (COMMA s=expression { $exprs.add( $s.text ); })* + ; + +assignmentOperator + : EQUALS_ASSIGN + | PLUS_ASSIGN + | MINUS_ASSIGN + | MULT_ASSIGN + | DIV_ASSIGN + | AND_ASSIGN + | OR_ASSIGN + | XOR_ASSIGN + | MOD_ASSIGN + | LESS LESS EQUALS_ASSIGN + | (GREATER GREATER GREATER)=> GREATER GREATER GREATER EQUALS_ASSIGN + | (GREATER GREATER)=> GREATER GREATER EQUALS_ASSIGN + ; + +// -------------------------------------------------------- +// KEYWORDS +// -------------------------------------------------------- +extends_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.EXTENDS))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +super_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.SUPER))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +instanceof_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.INSTANCEOF))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +boolean_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.BOOLEAN))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +char_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.CHAR))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +byte_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.BYTE))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +short_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.SHORT))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +int_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.INT))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +float_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.FLOAT))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +long_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.LONG))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +double_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.DOUBLE))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +void_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.VOID))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +this_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.THIS))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +class_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.CLASS))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +new_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.NEW))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +not_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.NOT))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +in_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.IN))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +operator_key + : {(helper.isPluggableEvaluator(false))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; + +neg_operator_key + : {(helper.isPluggableEvaluator(true))}?=> id=ID { helper.emit($ID, DroolsEditorType.KEYWORD); } + ; diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLExpressions.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLExpressions.java new file mode 100644 index 00000000000..ba811347b29 --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLExpressions.java @@ -0,0 +1,54 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.parser; + +import java.util.LinkedList; +import java.util.List; + +import org.antlr.runtime.Parser; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.RecognizerSharedState; +import org.antlr.runtime.TokenStream; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.parser.DroolsParserException; + +public abstract class DRLExpressions extends Parser { + public DRLExpressions(TokenStream input, RecognizerSharedState state) { + super(input, state); + } + + public abstract void setBuildDescr( boolean build ); + public abstract boolean isBuildDescr(); + + public abstract void setLeftMostExpr( String value ); + public abstract String getLeftMostExpr(); + + public abstract void setHasBindings( boolean value ); + public abstract boolean hasBindings(); + + public abstract BaseDescr conditionalOrExpression() throws RecognitionException; + + public abstract ParserHelper getHelper(); + public abstract boolean hasErrors(); + public abstract List<DroolsParserException> getErrors(); + public abstract List<String> getErrorMessages(); + public abstract void enableEditorInterface(); + public abstract void disableEditorInterface(); + public abstract LinkedList<DroolsSentence> getEditorInterface(); +} diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DrlExprParser.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DrlExprParser.java new file mode 100644 index 00000000000..7d624224cb5 --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DrlExprParser.java @@ -0,0 +1,88 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.parser; + +import java.util.Collections; +import java.util.List; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.RecognizerSharedState; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.ast.descr.ConstraintConnectiveDescr; +import org.drools.drl.parser.lang.ParserHelper; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * This is a helper class that provides helper methods to parse expressions + * using both the DRLExpressions parser and the DRLExprTree parser. + */ +public class DrlExprParser { + + private ParserHelper helper = null; + + private final LanguageLevelOption languageLevel; + + public DrlExprParser(LanguageLevelOption languageLevel) { + this.languageLevel = languageLevel; + } + + /** Parse an expression from text */ + public ConstraintConnectiveDescr parse( final String text ) { + ConstraintConnectiveDescr constraint = null; + try { + DRLLexer lexer = DRLFactory.getDRLLexer(new ANTLRStringStream(text), languageLevel); + CommonTokenStream input = new CommonTokenStream( lexer ); + RecognizerSharedState state = new RecognizerSharedState(); + helper = new ParserHelper( input, state, languageLevel ); + DRLExpressions parser = DRLFactory.getDRLExpressions(input, state, helper, languageLevel); + parser.setBuildDescr( true ); + parser.setLeftMostExpr( null ); // setting initial value just in case + BaseDescr expr = parser.conditionalOrExpression(); + if ( expr != null && !parser.hasErrors() ) { + constraint = ConstraintConnectiveDescr.newAnd(); + constraint.addOrMerge( expr ); + } + } catch ( RecognitionException e ) { + helper.reportError( e ); + } + return constraint; + } + + public String getLeftMostExpr() { + return helper != null ? helper.getLeftMostExpr() : null; + } + + /** + * @return true if there were parser errors. + */ + public boolean hasErrors() { + return helper != null && helper.hasErrors(); + } + + /** + * @return a list of errors found while parsing. + */ + @SuppressWarnings("unchecked") + public List<DroolsParserException> getErrors() { + return helper != null ? helper.getErrors() : Collections.EMPTY_LIST; + } + +} diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DroolsParserExceptionFactory.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DroolsParserExceptionFactory.java new file mode 100644 index 00000000000..f94eab73ccc --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DroolsParserExceptionFactory.java @@ -0,0 +1,380 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.parser; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.antlr.runtime.BitSet; +import org.antlr.runtime.EarlyExitException; +import org.antlr.runtime.FailedPredicateException; +import org.antlr.runtime.MismatchedNotSetException; +import org.antlr.runtime.MismatchedSetException; +import org.antlr.runtime.MismatchedTokenException; +import org.antlr.runtime.MismatchedTreeNodeException; +import org.antlr.runtime.NoViableAltException; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.drools.drl.parser.DRLFactory; +import org.drools.drl.parser.DroolsParserException; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * Helper class that generates DroolsParserException with user friendly error + * messages. + * + * @see DroolsParserException + */ +public class DroolsParserExceptionFactory { + public final static String MISMATCHED_TOKEN_MESSAGE_COMPLETE = "Line %1$d:%2$d mismatched input '%3$s' expecting '%4$s'%5$s"; + public final static String MISMATCHED_TOKEN_MESSAGE_PART = "Line %1$d:%2$d mismatched input '%3$s'%4$s"; + public final static String MISMATCHED_TREE_NODE_MESSAGE_COMPLETE = "Line %1$d:%2$d mismatched tree node '%3$s' expecting '%4$s'%5$s"; + public final static String MISMATCHED_TREE_NODE_MESSAGE_PART = "Line %1$d:%2$d mismatched tree node '%3$s'%4$s"; + public final static String NO_VIABLE_ALT_MESSAGE = "Line %1$d:%2$d no viable alternative at input '%3$s'%4$s"; + public final static String EARLY_EXIT_MESSAGE = "Line %1$d:%2$d required (...)+ loop did not match anything at input '%3$s'%4$s"; + public final static String MISMATCHED_SET_MESSAGE = "Line %1$d:%2$d mismatched input '%3$s' expecting one of the following tokens: '%4$s'%5$s."; + public final static String MISMATCHED_NOT_SET_MESSAGE = "Line %1$d:%2$d mismatched input '%3$s' not expecting any of the following tokens: '%4$s'%5$s"; + public final static String FAILED_PREDICATE_MESSAGE = "Line %1$d:%2$d rule '%3$s' failed predicate: {%4$s}?%5$s"; + public final static String TRAILING_SEMI_COLON_NOT_ALLOWED_MESSAGE = "Line %1$d:%2$d trailing semi-colon not allowed%3$s"; + public final static String PARSER_LOCATION_MESSAGE_COMPLETE = " in %1$s %2$s"; + public final static String PARSER_LOCATION_MESSAGE_PART = " in %1$s"; + public final static String UNEXPECTED_EXCEPTION = "Line %1$d:%2$d unexpected exception at input '%3$s'. Exception: %4$s. Stack trace:\n %5$s"; + + private final Collection<Map<DroolsParaphraseTypes, String>> paraphrases; + + // TODO: need to deal with this array + private String[] tokenNames = null; + + private final LanguageLevelOption languageLevel; + + /** + * DroolsParserErrorMessages constructor. + * + * @param tokenNames + * tokenNames generated by ANTLR + * @param paraphrases + * paraphrases parser structure + */ + public DroolsParserExceptionFactory(Collection<Map<DroolsParaphraseTypes, String>> paraphrases, + LanguageLevelOption languageLevel) { + this.paraphrases = paraphrases; + this.languageLevel = languageLevel; + } + + /** + * This method creates a DroolsParserException for trailing semicolon + * exception, full of information. + * + * @param line + * line number + * @param column + * column position + * @param offset + * char offset + * @return DroolsParserException filled. + */ + public DroolsParserException createTrailingSemicolonException( int line, + int column, + int offset ) { + String message = String + .format( + TRAILING_SEMI_COLON_NOT_ALLOWED_MESSAGE, + line, + column, + formatParserLocation() ); + + return new DroolsParserException( "ERR 104", + message, + line, + column, + offset, + null ); + } + + /** + * This method creates a DroolsParserException full of information. + * + * @param e + * original exception + * @return DroolsParserException filled. + */ + public DroolsParserException createDroolsException( RecognitionException e ) { + List<String> codeAndMessage = createErrorMessage( e ); + return new DroolsParserException( codeAndMessage.get( 1 ), + codeAndMessage + .get( 0 ), + e.line, + e.charPositionInLine, + e.index, + e ); + } + + /** + * This will take a RecognitionException, and create a sensible error + * message out of it + */ + private List<String> createErrorMessage( RecognitionException e ) { + List<String> codeAndMessage = new ArrayList<>( 2 ); + String message; + if ( e instanceof MismatchedTokenException ) { + MismatchedTokenException mte = (MismatchedTokenException) e; + String expecting = mte instanceof DroolsMismatchedTokenException ? ((DroolsMismatchedTokenException)mte).getTokenText() : getBetterToken( mte.expecting ); + if ( tokenNames != null && mte.expecting >= 0 && mte.expecting < tokenNames.length ) { + message = String + .format( + MISMATCHED_TOKEN_MESSAGE_COMPLETE, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + expecting, + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 102" ); + } else { + message = String + .format( + MISMATCHED_TOKEN_MESSAGE_PART, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 102" ); + } + } else if ( e instanceof MismatchedTreeNodeException ) { + MismatchedTreeNodeException mtne = (MismatchedTreeNodeException) e; + if ( mtne.expecting >= 0 && mtne.expecting < tokenNames.length ) { + message = String + .format( + MISMATCHED_TREE_NODE_MESSAGE_COMPLETE, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + getBetterToken( mtne.expecting ), + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 106" ); + } else { + message = String + .format( + MISMATCHED_TREE_NODE_MESSAGE_PART, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 106" ); + } + } else if ( e instanceof NoViableAltException ) { + // NoViableAltException nvae = (NoViableAltException) e; + message = String.format( + NO_VIABLE_ALT_MESSAGE, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 101" ); + } else if ( e instanceof EarlyExitException ) { + // EarlyExitException eee = (EarlyExitException) e; + message = String.format( + EARLY_EXIT_MESSAGE, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 105" ); + } else if ( e instanceof MismatchedSetException ) { + MismatchedSetException mse = (MismatchedSetException) e; + String expected = expectedTokensAsString( mse.expecting ); + message = String.format( + MISMATCHED_SET_MESSAGE, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + expected, + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 107" ); + } else if ( e instanceof DroolsMismatchedSetException ) { + DroolsMismatchedSetException mse = (DroolsMismatchedSetException) e; + String expected = Arrays.asList( mse.getTokenText() ).toString(); + message = String.format( + MISMATCHED_SET_MESSAGE, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + expected, + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 107" ); + } else if ( e instanceof MismatchedNotSetException ) { + MismatchedNotSetException mse = (MismatchedNotSetException) e; + String expected = expectedTokensAsString( mse.expecting ); + message = String.format( + MISMATCHED_NOT_SET_MESSAGE, + e.line, + e.charPositionInLine, + getBetterToken( e.token ), + expected, + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 108" ); + } else if ( e instanceof FailedPredicateException ) { + FailedPredicateException fpe = (FailedPredicateException) e; + message = String.format( + FAILED_PREDICATE_MESSAGE, + e.line, + e.charPositionInLine, + fpe.ruleName, + fpe.predicateText, + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 103" ); + } + if ( codeAndMessage.get( 0 ).length() == 0 ) { + codeAndMessage.add( "?????" ); + } + return codeAndMessage; + } + + public DroolsParserException createDroolsException( Exception e, + Token token ) { + StringWriter sw = new StringWriter(); + e.printStackTrace( new PrintWriter(sw) ); + return new DroolsParserException( String.format( + DroolsParserExceptionFactory.UNEXPECTED_EXCEPTION, + token.getLine(), + token.getCharPositionInLine(), + getBetterToken( token ), + e.toString(), + sw.toString() ), + e ); + + } + + private String expectedTokensAsString( BitSet set ) { + StringBuilder buf = new StringBuilder(); + buf.append( "{ " ); + int i = 0; + for ( int token : set.toArray() ) { + if ( i > 0 ) buf.append( ", " ); + buf.append( getBetterToken( token ) ); + i++; + } + buf.append( " }" ); + return buf.toString(); + } + + /** + * This will take Paraphrases stack, and create a sensible location + */ + private String formatParserLocation() { + StringBuilder sb = new StringBuilder(); + if ( paraphrases != null ) { + for ( Map<DroolsParaphraseTypes, String> map : paraphrases ) { + for ( Entry<DroolsParaphraseTypes, String> activeEntry : map.entrySet() ) { + if ( activeEntry.getValue().length() == 0 ) { + String kStr = getLocationName( activeEntry.getKey() ); + if( kStr.length() > 0 ){ + sb.append( String.format( PARSER_LOCATION_MESSAGE_PART, kStr ) ); + } + } else { + sb.append( String.format( PARSER_LOCATION_MESSAGE_COMPLETE, + getLocationName( activeEntry.getKey() ), + activeEntry.getValue() ) ); + } + } + } + } + return sb.toString(); + } + + /** + * Returns a string based on Paraphrase Type + * + * @param type + * Paraphrase Type + * @return a string representing the + */ + private String getLocationName( DroolsParaphraseTypes type ) { + switch ( type ) { + case PACKAGE : + return "package"; + case IMPORT : + return "import"; + case FUNCTION_IMPORT : + return "function import"; + case ACCUMULATE_IMPORT : + return "accumulate import"; + case GLOBAL : + return "global"; + case FUNCTION : + return "function"; + case QUERY : + return "query"; + case TEMPLATE : + return "template"; + case RULE : + return "rule"; + case RULE_ATTRIBUTE : + return "rule attribute"; + case PATTERN : + return "pattern"; + case EVAL : + return "eval"; + default : + return ""; + } + } + + /** + * Helper method that creates a user friendly token definition + * + * @param token + * token + * @return user friendly token definition + */ + private String getBetterToken( Token token ) { + if ( token == null ) { + return ""; + } + return DRLFactory.getBetterToken(token.getType(), token.getText(), languageLevel); + } + + /** + * Helper method that creates a user friendly token definition + * + * @param tokenType + * token type + * @return user friendly token definition + */ + private String getBetterToken( int tokenType ) { + return DRLFactory.getBetterToken( tokenType, null, languageLevel ); + } + + +} diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/ParserHelper.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/ParserHelper.java new file mode 100644 index 00000000000..94e5e2f76ce --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/ParserHelper.java @@ -0,0 +1,672 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/* + * 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.parser; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.RecognizerSharedState; +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.drools.drl.ast.descr.AttributeDescr; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.ast.dsl.AbstractClassTypeDeclarationBuilder; +import org.drools.drl.ast.dsl.AccumulateDescrBuilder; +import org.drools.drl.ast.dsl.AccumulateImportDescrBuilder; +import org.drools.drl.ast.dsl.AttributeDescrBuilder; +import org.drools.drl.ast.dsl.AttributeSupportBuilder; +import org.drools.drl.ast.dsl.BehaviorDescrBuilder; +import org.drools.drl.ast.dsl.CEDescrBuilder; +import org.drools.drl.ast.dsl.CollectDescrBuilder; +import org.drools.drl.ast.dsl.ConditionalBranchDescrBuilder; +import org.drools.drl.ast.dsl.DeclareDescrBuilder; +import org.drools.drl.ast.dsl.DescrBuilder; +import org.drools.drl.ast.dsl.DescrFactory; +import org.drools.drl.ast.dsl.EntryPointDeclarationDescrBuilder; +import org.drools.drl.ast.dsl.EnumDeclarationDescrBuilder; +import org.drools.drl.ast.dsl.EnumLiteralDescrBuilder; +import org.drools.drl.ast.dsl.EvalDescrBuilder; +import org.drools.drl.ast.dsl.FieldDescrBuilder; +import org.drools.drl.ast.dsl.ForallDescrBuilder; +import org.drools.drl.ast.dsl.FunctionDescrBuilder; +import org.drools.drl.ast.dsl.GlobalDescrBuilder; +import org.drools.drl.ast.dsl.GroupByDescrBuilder; +import org.drools.drl.ast.dsl.ImportDescrBuilder; +import org.drools.drl.ast.dsl.NamedConsequenceDescrBuilder; +import org.drools.drl.ast.dsl.PackageDescrBuilder; +import org.drools.drl.ast.dsl.PatternContainerDescrBuilder; +import org.drools.drl.ast.dsl.PatternDescrBuilder; +import org.drools.drl.ast.dsl.QueryDescrBuilder; +import org.drools.drl.ast.dsl.RuleDescrBuilder; +import org.drools.drl.ast.dsl.TypeDeclarationDescrBuilder; +import org.drools.drl.ast.dsl.UnitDescrBuilder; +import org.drools.drl.ast.dsl.WindowDeclarationDescrBuilder; +import org.drools.drl.parser.DroolsParserException; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * This is a class to hold all the helper functions/methods used + * by the DRL parser + */ +public class ParserHelper { + public final String[] statementKeywords = new String[]{ + DroolsSoftKeywords.PACKAGE, + DroolsSoftKeywords.UNIT, + DroolsSoftKeywords.IMPORT, + DroolsSoftKeywords.GLOBAL, + DroolsSoftKeywords.DECLARE, + DroolsSoftKeywords.FUNCTION, + DroolsSoftKeywords.RULE, + DroolsSoftKeywords.QUERY + }; + + public List<DroolsParserException> errors = new ArrayList<>(); + public LinkedList<DroolsSentence> editorInterface = null; + public boolean isEditorInterfaceEnabled = false; + private Deque<Map<DroolsParaphraseTypes, String>> paraphrases = new ArrayDeque<>(); + + // parameters from parser + private DroolsParserExceptionFactory errorMessageFactory = null; + private TokenStream input = null; + private RecognizerSharedState state = null; + + private String leftMostExpr = null; + + // helper attribute + private boolean hasOperator = false; + + private final LanguageLevelOption languageLevel; + + public ParserHelper(TokenStream input, + RecognizerSharedState state, + LanguageLevelOption languageLevel) { + this.errorMessageFactory = new DroolsParserExceptionFactory( paraphrases, languageLevel ); + this.input = input; + this.state = state; + this.languageLevel = languageLevel; + } + + public LinkedList<DroolsSentence> getEditorInterface() { + return editorInterface; + } + + public void setLeftMostExpr( String value ) { + this.leftMostExpr = value; + } + + public String getLeftMostExpr() { + return this.leftMostExpr; + } + + public void enableEditorInterface() { + isEditorInterfaceEnabled = true; + } + + public void disableEditorInterface() { + isEditorInterfaceEnabled = false; + } + + public void setHasOperator( boolean hasOperator ) { + this.hasOperator = hasOperator; + } + + public boolean getHasOperator() { + return hasOperator; + } + + public void beginSentence( DroolsSentenceType sentenceType ) { + if ( isEditorInterfaceEnabled ) { + if ( null == editorInterface ) { + editorInterface = new LinkedList<>(); + } + if (editorInterface.isEmpty()){ + DroolsSentence sentence = new DroolsSentence(); + sentence.setType( sentenceType ); + editorInterface.add( sentence ); + } + } + } + + public DroolsSentence getActiveSentence() { + return editorInterface.getLast(); + } + + public void emit( List< ? > tokens, + DroolsEditorType editorType ) { + if ( isEditorInterfaceEnabled && tokens != null ) { + for ( Object activeObject : tokens ) { + emit( (Token) activeObject, + editorType ); + } + } + } + + public void emit( Token token, + DroolsEditorType editorType ) { + if ( isEditorInterfaceEnabled && token != null && editorType != null ) { + ((DroolsToken) token).setEditorType( editorType ); + getActiveSentence().addContent( (DroolsToken) token ); + } + } + + public void emit( int activeContext ) { + if ( isEditorInterfaceEnabled ) { + getActiveSentence().addContent( activeContext ); + } + } + + public DroolsToken getLastTokenOnList( LinkedList< ? > list ) { + DroolsToken lastToken = null; + for ( Object object : list ) { + if ( object instanceof DroolsToken ) { + lastToken = (DroolsToken) object; + } + } + return lastToken; + } + + public String retrieveLT( int LTNumber ) { + if ( null == input ) return null; + if ( null == input.LT( LTNumber ) ) return null; + if ( null == input.LT( LTNumber ).getText() ) return null; + + return input.LT( LTNumber ).getText(); + } + + public boolean validateLT( int LTNumber, + String text ) { + String text2Validate = retrieveLT( LTNumber ); + return validateText( text, text2Validate ); + } + + private boolean validateText( String text, String text2Validate ) { + return text2Validate != null && text2Validate.equals( text ); + } + + public boolean isPluggableEvaluator( int offset, + boolean negated ) { + String text2Validate = retrieveLT( offset ); + return text2Validate != null && DroolsSoftKeywords.isOperator(text2Validate, negated); + } + + public boolean isPluggableEvaluator( boolean negated ) { + return isPluggableEvaluator( 1, + negated ); + } + + public boolean validateIdentifierKey( String text ) { + return validateLT( 1, + text ); + } + + public boolean validateCEKeyword( int index ) { + String text2Validate = retrieveLT( index ); + return validateText( text2Validate, + DroolsSoftKeywords.NOT ) || + validateText( text2Validate, + DroolsSoftKeywords.EXISTS ) || + validateText( text2Validate, + DroolsSoftKeywords.FORALL ) || + validateText( text2Validate, + DroolsSoftKeywords.AND ) || + validateText( text2Validate, + DroolsSoftKeywords.OR ) || + validateText( text2Validate, + DroolsSoftKeywords.COLLECT ) || + validateText( text2Validate, + DroolsSoftKeywords.FROM ) || + validateText( text2Validate, + DroolsSoftKeywords.END ) || + validateText( text2Validate, + DroolsSoftKeywords.EVAL ) || + validateText( text2Validate, + DroolsSoftKeywords.OVER ) || + validateText( text2Validate, + DroolsSoftKeywords.THEN ); + } + + public boolean validateStatement( int index ) { + boolean ret = false; + String text2Validate = retrieveLT( index ); + for ( String st : statementKeywords ) { + if ( validateText( text2Validate, + st ) ) { + ret = true; + break; + } + } + return ret || validateAttribute( index ); + } + + public boolean validateAttribute( int index ) { + String text2Validate = retrieveLT( index ); + return validateText( text2Validate, + DroolsSoftKeywords.SALIENCE ) || + validateText( text2Validate, + DroolsSoftKeywords.ENABLED ) || + (validateText( text2Validate, + DroolsSoftKeywords.NO ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.LOOP )) || + (validateText( text2Validate, + DroolsSoftKeywords.AUTO ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.FOCUS )) || + (validateText( text2Validate, + DroolsSoftKeywords.LOCK ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.ON ) && + validateLT( index + 3, + "-" ) && + validateLT( index + 4, + DroolsSoftKeywords.ACTIVE )) || + (validateText( text2Validate, + DroolsSoftKeywords.AGENDA ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.GROUP )) || + (validateText( text2Validate, + DroolsSoftKeywords.ACTIVATION ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.GROUP )) || + (validateText( text2Validate, + DroolsSoftKeywords.RULEFLOW ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.GROUP )) || + (validateText( text2Validate, + DroolsSoftKeywords.DATE ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.EFFECTIVE )) || + (validateText( text2Validate, + DroolsSoftKeywords.DATE ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.EXPIRES )) || + validateText( text2Validate, + DroolsSoftKeywords.DIALECT ) || + validateText( text2Validate, + DroolsSoftKeywords.CALENDARS ) || + validateText( text2Validate, + DroolsSoftKeywords.TIMER ) || + validateText( text2Validate, + DroolsSoftKeywords.DURATION ) || + validateText( text2Validate, + DroolsSoftKeywords.REFRACT ) || + validateText( text2Validate, + DroolsSoftKeywords.DIRECT ); + } + + public void reportError( RecognitionException ex ) { + // if we've already reported an error and have not matched a token + // yet successfully, don't report any errors. + if ( state.errorRecovery ) { + return; + } + state.errorRecovery = true; + + errors.add( errorMessageFactory.createDroolsException( ex ) ); + } + + public void reportError( Exception e ) { + try { + errors.add( errorMessageFactory.createDroolsException( e, + input.LT( 1 ) ) ); + } catch (Exception ignored) { + errors.add(new DroolsParserException( "Unexpected error: " + e.getMessage(), e )); + } + } + + /** return the raw DroolsParserException errors */ + public List<DroolsParserException> getErrors() { + return errors; + } + + /** Return a list of pretty strings summarising the errors */ + public List<String> getErrorMessages() { + List<String> messages = new ArrayList<>( errors.size() ); + + for ( DroolsParserException activeException : errors ) { + messages.add( activeException.getMessage() ); + } + + return messages; + } + + /** return true if any parser errors were accumulated */ + public boolean hasErrors() { + return !errors.isEmpty(); + } + + /** + * Method that adds a paraphrase type into paraphrases stack. + * + * @param type + * paraphrase type + */ + public void pushParaphrases( DroolsParaphraseTypes type ) { + Map<DroolsParaphraseTypes, String> activeMap = new HashMap<>(); + activeMap.put( type, + "" ); + paraphrases.push( activeMap ); + } + + public Map<DroolsParaphraseTypes, String> popParaphrases() { + return paraphrases.pop(); + } + + /** + * Method that sets paraphrase value for a type into paraphrases stack. + * + * @param type + * paraphrase type + * @param value + * paraphrase value + */ + public void setParaphrasesValue( DroolsParaphraseTypes type, + String value ) { + paraphrases.peek().put( type, + value ); + } + + void setStart( DescrBuilder< ? , ? > db ) { + setStart( db, + input.LT( 1 ) ); + } + + void setStart( DescrBuilder< ? , ? > db, + Token first ) { + if ( db != null && first != null ) { + db.startCharacter( ((CommonToken) first).getStartIndex() ).startLocation( first.getLine(), + first.getCharPositionInLine() ); + } + } + + void setStart( BaseDescr descr, + Token first ) { + if ( descr != null && first != null ) { + descr.setLocation( first.getLine(), + first.getCharPositionInLine() ); + descr.setStartCharacter( ((CommonToken) first).getStartIndex() ); + } + } + + void setEnd( BaseDescr descr ) { + Token last = input.LT( -1 ); + if ( descr != null && last != null ) { + int endLocation = last.getText() != null ? last.getCharPositionInLine() + last.getText().length() - 1 : last.getCharPositionInLine(); + descr.setEndCharacter( ((CommonToken) last).getStopIndex() + 1 ); + descr.setEndLocation( last.getLine(), + endLocation ); + } + } + + void setEnd( DescrBuilder< ? , ? > db ) { + Token last = input.LT( -1 ); + if ( db != null && last != null ) { + int endLocation = last.getText() != null ? last.getCharPositionInLine() + last.getText().length() - 1 : last.getCharPositionInLine(); + db.endCharacter( ((CommonToken) last).getStopIndex() + 1 ).endLocation( last.getLine(), + endLocation ); + } + } + + @SuppressWarnings("unchecked") + public <T extends DescrBuilder< ? , ? >> T start( DescrBuilder< ? , ? > ctxBuilder, + Class<T> clazz, + String param ) { + if ( state.backtracking == 0 ) { + if ( PackageDescrBuilder.class.isAssignableFrom( clazz ) ) { + pushParaphrases( DroolsParaphraseTypes.PACKAGE ); + beginSentence( DroolsSentenceType.PACKAGE ); + setStart( ctxBuilder ); + } else if ( ImportDescrBuilder.class.isAssignableFrom( clazz ) ) { + ImportDescrBuilder imp; + if ( validateLT( 2, + DroolsSoftKeywords.FUNCTION ) || + validateLT( 2, + DroolsSoftKeywords.STATIC ) ) { + imp = ctxBuilder == null ? + DescrFactory.newPackage().newFunctionImport() : + ((PackageDescrBuilder) ctxBuilder).newFunctionImport(); + } else { + imp = ctxBuilder == null ? + DescrFactory.newPackage().newImport() : + ((PackageDescrBuilder) ctxBuilder).newImport(); + } + pushParaphrases( DroolsParaphraseTypes.IMPORT ); + beginSentence( DroolsSentenceType.IMPORT_STATEMENT ); + setStart( imp ); + return (T) imp; + } else if ( UnitDescrBuilder.class.isAssignableFrom( clazz ) ) { + UnitDescrBuilder imp = ctxBuilder == null ? + DescrFactory.newPackage().newUnit() : + ((PackageDescrBuilder) ctxBuilder).newUnit(); + pushParaphrases( DroolsParaphraseTypes.UNIT ); + beginSentence( DroolsSentenceType.UNIT ); + setStart( imp ); + return (T) imp; + } else if ( AccumulateImportDescrBuilder.class.isAssignableFrom( clazz ) ) { + AccumulateImportDescrBuilder imp = ctxBuilder == null ? + DescrFactory.newPackage().newAccumulateImport() : + ((PackageDescrBuilder) ctxBuilder).newAccumulateImport(); + pushParaphrases( DroolsParaphraseTypes.ACCUMULATE_IMPORT ); + beginSentence( DroolsSentenceType.ACCUMULATE_IMPORT_STATEMENT ); + setStart( imp ); + return (T) imp; + } else if ( GlobalDescrBuilder.class.isAssignableFrom( clazz ) ) { + GlobalDescrBuilder global = ctxBuilder == null ? + DescrFactory.newPackage().newGlobal() : + ((PackageDescrBuilder) ctxBuilder).newGlobal(); + pushParaphrases( DroolsParaphraseTypes.GLOBAL ); + beginSentence( DroolsSentenceType.GLOBAL ); + setStart( global ); + return (T) global; + } else if ( DeclareDescrBuilder.class.isAssignableFrom( clazz ) ) { + DeclareDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare() : + ((PackageDescrBuilder) ctxBuilder).newDeclare(); + return (T) declare; + } else if ( TypeDeclarationDescrBuilder.class.isAssignableFrom( clazz ) ) { + TypeDeclarationDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare().type() : + ((DeclareDescrBuilder) ctxBuilder).type(); + pushParaphrases( DroolsParaphraseTypes.TYPE_DECLARE ); + beginSentence( DroolsSentenceType.TYPE_DECLARATION ); + setStart( declare ); + return (T) declare; + } else if ( EnumDeclarationDescrBuilder.class.isAssignableFrom( clazz ) ) { + EnumDeclarationDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare().enumerative() : + ((DeclareDescrBuilder) ctxBuilder).enumerative(); + pushParaphrases( DroolsParaphraseTypes.ENUM_DECLARE ); + beginSentence( DroolsSentenceType.ENUM_DECLARATION ); + setStart( declare ); + return (T) declare; + }else if ( EntryPointDeclarationDescrBuilder.class.isAssignableFrom( clazz ) ) { + EntryPointDeclarationDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare().entryPoint() : + ((DeclareDescrBuilder) ctxBuilder).entryPoint(); + pushParaphrases( DroolsParaphraseTypes.ENTRYPOINT_DECLARE ); + beginSentence( DroolsSentenceType.ENTRYPOINT_DECLARATION ); + setStart( declare ); + return (T) declare; + } else if ( WindowDeclarationDescrBuilder.class.isAssignableFrom( clazz ) ) { + WindowDeclarationDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare().window() : + ((DeclareDescrBuilder) ctxBuilder).window(); + pushParaphrases( DroolsParaphraseTypes.WINDOW_DECLARE ); + beginSentence( DroolsSentenceType.WINDOW_DECLARATION ); + setStart( declare ); + return (T) declare; + } else if ( FieldDescrBuilder.class.isAssignableFrom( clazz ) ) { + FieldDescrBuilder field = ((AbstractClassTypeDeclarationBuilder) ctxBuilder).newField( param ); + setStart( field ); + return (T) field; + } else if ( EnumLiteralDescrBuilder.class.isAssignableFrom( clazz ) ) { + EnumLiteralDescrBuilder literal = ((EnumDeclarationDescrBuilder) ctxBuilder).newEnumLiteral( param ); + setStart( literal ); + return (T) literal; + } else if ( FunctionDescrBuilder.class.isAssignableFrom( clazz ) ) { + FunctionDescrBuilder function; + if ( ctxBuilder == null ) { + function = DescrFactory.newPackage().newFunction(); + } else { + PackageDescrBuilder pkg = (PackageDescrBuilder) ctxBuilder; + function = pkg.newFunction().namespace( pkg.getDescr().getName() ); + AttributeDescr attribute = pkg.getDescr().getAttribute( "dialect" ); + if ( attribute != null ) { + function.dialect( attribute.getValue() ); + } + } + pushParaphrases( DroolsParaphraseTypes.FUNCTION ); + beginSentence( DroolsSentenceType.FUNCTION ); + setStart( function ); + return (T) function; + } else if ( RuleDescrBuilder.class.isAssignableFrom( clazz ) ) { + RuleDescrBuilder rule = ctxBuilder == null ? + DescrFactory.newPackage().newRule() : + ((PackageDescrBuilder) ctxBuilder).newRule(); + pushParaphrases( DroolsParaphraseTypes.RULE ); + beginSentence( DroolsSentenceType.RULE ); + setStart( rule ); + return (T) rule; + } else if ( QueryDescrBuilder.class.isAssignableFrom( clazz ) ) { + QueryDescrBuilder query = ctxBuilder == null ? + DescrFactory.newPackage().newQuery() : + ((PackageDescrBuilder) ctxBuilder).newQuery(); + pushParaphrases( DroolsParaphraseTypes.QUERY ); + beginSentence( DroolsSentenceType.QUERY ); + setStart( query ); + return (T) query; + } else if ( AttributeDescrBuilder.class.isAssignableFrom( clazz ) ) { + AttributeDescrBuilder< ? > attribute = ((AttributeSupportBuilder< ? >) ctxBuilder).attribute(param); + setStart( attribute ); + return (T) attribute; + } else if ( EvalDescrBuilder.class.isAssignableFrom( clazz ) ) { + EvalDescrBuilder< ? > eval = ((CEDescrBuilder< ? , ? >) ctxBuilder).eval(); + pushParaphrases( DroolsParaphraseTypes.EVAL ); + beginSentence( DroolsSentenceType.EVAL ); + setStart( eval ); + return (T) eval; + } else if ( ForallDescrBuilder.class.isAssignableFrom( clazz ) ) { + ForallDescrBuilder< ? > forall = ((CEDescrBuilder< ? , ? >) ctxBuilder).forall(); + setStart( forall ); + return (T) forall; + } else if ( CEDescrBuilder.class.isAssignableFrom( clazz ) ) { + setStart( ctxBuilder ); + return (T) ctxBuilder; + } else if ( PatternDescrBuilder.class.isAssignableFrom( clazz ) ) { + PatternDescrBuilder< ? > pattern = ((PatternContainerDescrBuilder< ? , ? >) ctxBuilder).pattern(); + pushParaphrases( DroolsParaphraseTypes.PATTERN ); + setStart( pattern ); + return (T) pattern; + } else if ( CollectDescrBuilder.class.isAssignableFrom( clazz ) ) { + CollectDescrBuilder< ? > collect = ((PatternDescrBuilder< ? >) ctxBuilder).from().collect(); + setStart( collect ); + return (T) collect; + } else if ( GroupByDescrBuilder.class.isAssignableFrom(clazz) ) { + // GroupBy extends Accumulate and thus need to be before it + GroupByDescrBuilder< ? > groupBy = ((PatternDescrBuilder< ? >) ctxBuilder).from().groupBy(); + setStart( groupBy ); + return (T) groupBy; + } else if ( AccumulateDescrBuilder.class.isAssignableFrom( clazz ) ) { + AccumulateDescrBuilder< ? > accumulate = ((PatternDescrBuilder< ? >) ctxBuilder).from().accumulate(); + setStart( accumulate ); + return (T) accumulate; + } else if ( BehaviorDescrBuilder.class.isAssignableFrom( clazz ) ) { + BehaviorDescrBuilder< ? > behavior = ((PatternDescrBuilder< ? >) ctxBuilder).behavior(); + setStart( behavior ); + return (T) behavior; + } else if ( NamedConsequenceDescrBuilder.class.isAssignableFrom( clazz ) ) { + NamedConsequenceDescrBuilder< ? > namedConsequence = ((CEDescrBuilder< ? , ? >) ctxBuilder).namedConsequence(); + setStart( namedConsequence ); + return (T) namedConsequence; + } else if ( ConditionalBranchDescrBuilder.class.isAssignableFrom( clazz ) ) { + ConditionalBranchDescrBuilder< ? > conditionalBranch = ((CEDescrBuilder< ? , ? >) ctxBuilder).conditionalBranch(); + setStart( conditionalBranch ); + return (T) conditionalBranch; + } + } + return null; + } + + @SuppressWarnings("unchecked") + public <T extends DescrBuilder< ? , ? >> T end( Class<T> clazz, + DescrBuilder< ? , ? > builder ) { + if ( state.backtracking == 0 ) { + if ( !(FieldDescrBuilder.class.isAssignableFrom( clazz ) || + AttributeDescrBuilder.class.isAssignableFrom( clazz ) || + CEDescrBuilder.class.isAssignableFrom( clazz ) || + CollectDescrBuilder.class.isAssignableFrom( clazz ) || + AccumulateDescrBuilder.class.isAssignableFrom( clazz ) || + ForallDescrBuilder.class.isAssignableFrom( clazz ) || + BehaviorDescrBuilder.class.isAssignableFrom( clazz ) || + ConditionalBranchDescrBuilder.class.isAssignableFrom( clazz ) || + NamedConsequenceDescrBuilder.class.isAssignableFrom( clazz )) ) { + popParaphrases(); + } + + if (RuleDescrBuilder.class.isAssignableFrom(clazz)) { + RuleDescrBuilder ruleDescrBuilder = (RuleDescrBuilder)builder; + ruleDescrBuilder.end().getDescr().afterRuleAdded(ruleDescrBuilder.getDescr()); + } + + setEnd( builder ); + return (T) builder; + } + return null; + } + + public String[] getStatementKeywords() { + return statementKeywords; + } +} diff --git a/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/DRLExprParserTest.java b/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/DRLExprParserTest.java new file mode 100644 index 00000000000..a7672090cc8 --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/DRLExprParserTest.java @@ -0,0 +1,270 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.parser; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.drools.compiler.builder.impl.EvaluatorRegistry; +import org.drools.drl.ast.descr.AtomicExprDescr; +import org.drools.drl.ast.descr.BindingDescr; +import org.drools.drl.ast.descr.ConnectiveType; +import org.drools.drl.ast.descr.ConstraintConnectiveDescr; +import org.drools.drl.ast.descr.RelationalExprDescr; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * DRLExprTreeTest + */ +public class DRLExprParserTest { + + DrlExprParser parser; + + @Before + public void setUp() throws Exception { + new EvaluatorRegistry(); + this.parser = new DrlExprParser(LanguageLevelOption.DRL6); + } + + @After + public void tearDown() throws Exception { + this.parser = null; + } + + @Test + public void testSimpleExpression() throws Exception { + String source = "a > b"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(1); + + RelationalExprDescr expr = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + + AtomicExprDescr left = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr right = (AtomicExprDescr) expr.getRight(); + + assertThat(left.getExpression()).isEqualTo("a"); + assertThat(right.getExpression()).isEqualTo("b"); + } + + @Test + public void testAndConnective() throws Exception { + String source = "a > b && 10 != 20"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + RelationalExprDescr expr = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + AtomicExprDescr left = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("a"); + assertThat(right.getExpression()).isEqualTo("b"); + + expr = (RelationalExprDescr) result.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("!="); + left = (AtomicExprDescr) expr.getLeft(); + right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("10"); + assertThat(right.getExpression()).isEqualTo("20"); + } + + @Test + public void testConnective2() throws Exception { + String source = "(a > b || 10 != 20) && someMethod(10) == 20"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + ConstraintConnectiveDescr or = (ConstraintConnectiveDescr) result.getDescrs().get( 0 ); + assertThat(or.getConnective()).isEqualTo(ConnectiveType.OR); + assertThat(or.getDescrs().size()).isEqualTo(2); + + RelationalExprDescr expr = (RelationalExprDescr) or.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + AtomicExprDescr left = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("a"); + assertThat(right.getExpression()).isEqualTo("b"); + + expr = (RelationalExprDescr) or.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("!="); + left = (AtomicExprDescr) expr.getLeft(); + right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("10"); + assertThat(right.getExpression()).isEqualTo("20"); + + expr = (RelationalExprDescr) result.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("=="); + left = (AtomicExprDescr) expr.getLeft(); + right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("someMethod(10)"); + assertThat(right.getExpression()).isEqualTo("20"); + + } + + @Test + public void testBinding() throws Exception { + String source = "$x : property"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(1); + + BindingDescr bind = (BindingDescr) result.getDescrs().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("property"); + } + + @Test + public void testBindingConstraint() throws Exception { + String source = "$x : property > value"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(1); + + RelationalExprDescr rel = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(rel.getOperator()).isEqualTo(">"); + + BindingDescr bind = (BindingDescr) rel.getLeft(); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("property"); + + AtomicExprDescr right = (AtomicExprDescr) rel.getRight(); + assertThat(right.getExpression()).isEqualTo("value"); + } + + @Test + public void testBindingWithRestrictions() throws Exception { + String source = "$x : property > value && < 20"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + RelationalExprDescr rel = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(rel.getOperator()).isEqualTo(">"); + + BindingDescr bind = (BindingDescr) rel.getLeft(); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("property"); + + AtomicExprDescr right = (AtomicExprDescr) rel.getRight(); + assertThat(right.getExpression()).isEqualTo("value"); + + rel = (RelationalExprDescr) result.getDescrs().get( 1 ); + assertThat(rel.getOperator()).isEqualTo("<"); + + AtomicExprDescr left = (AtomicExprDescr) rel.getLeft(); + assertThat(left.getExpression()).isEqualTo("property"); + + right = (AtomicExprDescr) rel.getRight(); + assertThat(right.getExpression()).isEqualTo("20"); + } + + @Test + public void testDoubleBinding() throws Exception { + String source = "$x : x.m( 1, a ) && $y : y[z].foo"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + BindingDescr bind = (BindingDescr) result.getDescrs().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("x.m( 1, a )"); + + bind = (BindingDescr) result.getDescrs().get( 1 ); + assertThat(bind.getVariable()).isEqualTo("$y"); + assertThat(bind.getExpression()).isEqualTo("y[z].foo"); + } + + @Test + public void testDeepBinding() throws Exception { + String source = "($a : a > $b : b[10].prop || 10 != 20) && $x : someMethod(10) == 20"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + ConstraintConnectiveDescr or = (ConstraintConnectiveDescr) result.getDescrs().get( 0 ); + assertThat(or.getConnective()).isEqualTo(ConnectiveType.OR); + assertThat(or.getDescrs().size()).isEqualTo(2); + + RelationalExprDescr expr = (RelationalExprDescr) or.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + BindingDescr leftBind = (BindingDescr) expr.getLeft(); + BindingDescr rightBind = (BindingDescr) expr.getRight(); + assertThat(leftBind.getVariable()).isEqualTo("$a"); + assertThat(leftBind.getExpression()).isEqualTo("a"); + assertThat(rightBind.getVariable()).isEqualTo("$b"); + assertThat(rightBind.getExpression()).isEqualTo("b[10].prop"); + + expr = (RelationalExprDescr) or.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("!="); + AtomicExprDescr leftExpr = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr rightExpr = (AtomicExprDescr) expr.getRight(); + assertThat(leftExpr.getExpression()).isEqualTo("10"); + assertThat(rightExpr.getExpression()).isEqualTo("20"); + + expr = (RelationalExprDescr) result.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("=="); + leftBind = (BindingDescr) expr.getLeft(); + rightExpr = (AtomicExprDescr) expr.getRight(); + assertThat(leftBind.getVariable()).isEqualTo("$x"); + assertThat(leftBind.getExpression()).isEqualTo("someMethod(10)"); + assertThat(rightExpr.getExpression()).isEqualTo("20"); + + } + + @Test(timeout = 10000L) + public void testNestedExpression() throws Exception { + // DROOLS-982 + String source = "(((((((((((((((((((((((((((((((((((((((((((((((((( a > b ))))))))))))))))))))))))))))))))))))))))))))))))))"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(1); + + RelationalExprDescr expr = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + + AtomicExprDescr left = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr right = (AtomicExprDescr) expr.getRight(); + + assertThat(left.getExpression()).isEqualTo("a"); + assertThat(right.getExpression()).isEqualTo("b"); + } +}