// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. /** * When this file is changed, do "mvn generate-sources" to rebuild the parser. * * @author bratseth */ options { CACHE_TOKENS = true; DEBUG_PARSER = false; USER_TOKEN_MANAGER = false; ERROR_REPORTING = true; USER_CHAR_STREAM = false; } PARSER_BEGIN(RankingExpressionParser) package com.yahoo.searchlib.rankingexpression.parser; import com.yahoo.searchlib.rankingexpression.rule.*; import com.yahoo.searchlib.rankingexpression.evaluation.Value; import com.yahoo.searchlib.rankingexpression.evaluation.StringValue; import com.yahoo.searchlib.rankingexpression.evaluation.TensorValue; import com.yahoo.tensor.*; import com.yahoo.tensor.functions.*; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Arrays; import java.util.ArrayList; import java.util.List; import java.util.Optional; @SuppressWarnings({"rawtypes", "unchecked"}) public class RankingExpressionParser { } PARSER_END(RankingExpressionParser) SKIP : { <[" ","\n","\r","\t"]> } TOKEN : { (["l","L"])? | (["l","L"])? | (["l","L"])?> | <#DECIMAL: ["1"-"9"] (["0"-"9"])*> | <#HEX: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+> | <#OCTAL: "0" (["0"-"7"])*> | )? (["f","F","d","D"])?> | <#EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+> } TOKEN : { | | | | | | | | | | | | | | | | ="> | "> | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // MAX // MIN | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | } // Declare a special skip token for comments. SPECIAL_TOKEN : { } List featureList() : { List ret = new ArrayList(); ReferenceNode exp; } { ( ( exp = feature() { ret.add(exp); } )+ ) { return ret; } } ExpressionNode rankingExpression() : { ExpressionNode ret; } { ( ret = expression() ) { return ret; } } ExpressionNode expression() : { ExpressionNode left, right; List rightList; } { ( left = operationExpression() ( ( rightList = expressionList() { left = new SetMembershipNode(left, rightList); } ) ) ? ) { return left; } } ExpressionNode operationExpression() : { ExpressionNode left, right = null; Operator operator; } { ( left = value() ( operator = binaryOperator() right = value() { left = OperationNode.resolve(left, operator, right); } ) * ) { return left; } } Operator binaryOperator() : { } { ( { return Operator.or; } | { return Operator.and; } | { return Operator.largerOrEqual; } | { return Operator.larger; } | { return Operator.smallerOrEqual; } | { return Operator.smaller; } | { return Operator.approxEqual; } | { return Operator.notEqual; } | { return Operator.equal; } | { return Operator.plus; } | { return Operator.minus; } |
{ return Operator.divide; } | { return Operator.multiply; } | { return Operator.modulo; } | { return Operator.power; } ) { return null; } } ExpressionNode value() : { ExpressionNode value; boolean neg = false; boolean not = false; List valueAddress; } { ( [ { not = true; } ] [ LOOKAHEAD(2) { neg = true; } ] ( value = constantPrimitive(neg) | ( LOOKAHEAD(2) value = ifExpression() | LOOKAHEAD(4) value = function() | value = feature() | value = legacyQueryFeature() | ( value = expression() { value = new EmbracedNode(value); } ) ) { value = neg ? new NegativeNode(value) : value; } ) ) [ LOOKAHEAD(2) valueAddress = valueAddress() { value = new TensorFunctionNode(new Slice(TensorFunctionNode.wrap(value), valueAddress)); } ] { value = not ? new NotNode(value) : value; return value; } } IfNode ifExpression() : { ExpressionNode condition, ifTrue, ifFalse; Double trueProbability = null; } { ( ( condition = expression() ) ifTrue = expression() ifFalse = expression() ( trueProbability = doubleNumber() )? ) { return new IfNode(condition, ifTrue, ifFalse, trueProbability); } } ReferenceNode feature() : { List args = null; String name, out = null; } { ( name = identifier() [ args = args() ] [ out = outs() ] ) { if (args == null && out == null) // know the difference between "foo" and "foo()" return new ReferenceNode(name); else return new ReferenceNode(name, args, out); } } // Rank properties are referenced by $propertyname ReferenceNode legacyQueryFeature() : { String name; } { ( name = identifier() ) { return new ReferenceNode("query", Arrays.asList((ExpressionNode)new NameNode(name)), null); } } String outs() : { StringBuilder ret = new StringBuilder(); String str; } { ( str = out() { ret.append(str); } ( { ret.append(token.image); } str = out() { ret.append(str); } )* ) { return ret.toString(); } } String out() : { Function fnc; String name; } { ( { return token.image; } | { return token.image; } | name = identifier() { return name; } ) { return null; } } List args() : { List arguments = new ArrayList(); ExpressionNode argument; } { ( ( argument = expression() { arguments.add(argument); } ( argument = expression() { arguments.add(argument); } )* )? ) { return arguments; } } ExpressionNode function() : { ExpressionNode function; } { ( LOOKAHEAD(2) function = scalarOrTensorFunction() | function = tensorFunction() ) { return function; } } FunctionNode scalarOrTensorFunction() : { Function function; ExpressionNode arg1, arg2; } { ( ( function = unaryFunctionName() arg1 = expression() ) { return new FunctionNode(function, arg1); } ) | ( ( function = binaryFunctionName() arg1 = expression() arg2 = expression() ) { return new FunctionNode(function, arg1, arg2); } ) } TensorFunctionNode tensorFunction() : { TensorFunctionNode tensorExpression; } { ( tensorExpression = tensorMap() | tensorExpression = tensorReduce() | tensorExpression = tensorReduceComposites() | tensorExpression = tensorJoin() | tensorExpression = tensorMerge() | tensorExpression = tensorRename() | tensorExpression = tensorConcat() | tensorExpression = tensorGenerate() | tensorExpression = tensorRange() | tensorExpression = tensorDiag() | tensorExpression = tensorRandom() | tensorExpression = tensorL1Normalize() | tensorExpression = tensorL2Normalize() | tensorExpression = tensorEuclideanDistance() | tensorExpression = tensorCosineSimilarity() | tensorExpression = tensorMatmul() | tensorExpression = tensorSoftmax() | tensorExpression = tensorXwPlusB() | tensorExpression = tensorArgmax() | tensorExpression = tensorArgmin() | tensorExpression = tensorCellCast() | tensorExpression = tensorExpand() ) { return tensorExpression; } } TensorFunctionNode tensorMap() : { ExpressionNode tensor; LambdaFunctionNode doubleMapper; } { tensor = expression() doubleMapper = lambdaFunction() { return new TensorFunctionNode(new Map(TensorFunctionNode.wrap(tensor), doubleMapper.asDoubleUnaryOperator())); } } TensorFunctionNode tensorReduce() : { ExpressionNode tensor; Reduce.Aggregator aggregator; List dimensions = null; } { tensor = expression() aggregator = tensorReduceAggregator() dimensions = tagCommaLeadingList() { return new TensorFunctionNode(new Reduce(TensorFunctionNode.wrap(tensor), aggregator, dimensions)); } } TensorFunctionNode tensorReduceComposites() : { ExpressionNode tensor; Reduce.Aggregator aggregator; List dimensions = null; } { aggregator = tensorReduceAggregator() tensor = expression() dimensions = tagCommaLeadingList() { return new TensorFunctionNode(new Reduce(TensorFunctionNode.wrap(tensor), aggregator, dimensions)); } } TensorFunctionNode tensorJoin() : { ExpressionNode tensor1, tensor2; LambdaFunctionNode doubleJoiner; } { tensor1 = expression() tensor2 = expression() doubleJoiner = lambdaFunction() { return new TensorFunctionNode(new Join(TensorFunctionNode.wrap(tensor1), TensorFunctionNode.wrap(tensor2), doubleJoiner.asDoubleBinaryOperator())); } } TensorFunctionNode tensorMerge() : { ExpressionNode tensor1, tensor2; LambdaFunctionNode doubleMerger; } { tensor1 = expression() tensor2 = expression() doubleMerger = lambdaFunction() { return new TensorFunctionNode(new Merge(TensorFunctionNode.wrap(tensor1), TensorFunctionNode.wrap(tensor2), doubleMerger.asDoubleBinaryOperator())); } } TensorFunctionNode tensorRename() : { ExpressionNode tensor; List fromDimensions, toDimensions; } { tensor = expression() fromDimensions = bracedIdentifierList() toDimensions = bracedIdentifierList() { return new TensorFunctionNode(new Rename(TensorFunctionNode.wrap(tensor), fromDimensions, toDimensions)); } } TensorFunctionNode tensorConcat() : { ExpressionNode tensor1, tensor2; String dimension; } { tensor1 = expression() tensor2 = expression() dimension = tag() { return new TensorFunctionNode(new Concat(TensorFunctionNode.wrap(tensor1), TensorFunctionNode.wrap(tensor2), dimension)); } } TensorFunctionNode tensorGenerate() : { TensorType type; List dimensionOrder = new ArrayList(); TensorFunctionNode expression; } { type = tensorType(dimensionOrder) ( expression = tensorGenerateBody(type) | expression = tensorValueBody(type, dimensionOrder) ) { return expression; } } TensorFunctionNode tensorGenerateBody(TensorType type) : { ExpressionNode generator; } { generator = expression() { return new TensorFunctionNode(Generate.bound(type, TensorFunctionNode.wrapScalar(generator))); } } TensorFunctionNode tensorRange() : { TensorType type; } { type = tensorType(null) { return new TensorFunctionNode(new Range(type)); } } TensorFunctionNode tensorDiag() : { TensorType type; } { type = tensorType(null) { return new TensorFunctionNode(new Diag(type)); } } TensorFunctionNode tensorRandom() : { TensorType type; } { type = tensorType(null) { return new TensorFunctionNode(new Random(type)); } } TensorFunctionNode tensorL1Normalize() : { ExpressionNode tensor; String dimension; } { tensor = expression() dimension = identifier() { return new TensorFunctionNode(new L1Normalize(TensorFunctionNode.wrap(tensor), dimension)); } } TensorFunctionNode tensorL2Normalize() : { ExpressionNode tensor; String dimension; } { tensor = expression() dimension = identifier() { return new TensorFunctionNode(new L2Normalize(TensorFunctionNode.wrap(tensor), dimension)); } } TensorFunctionNode tensorEuclideanDistance() : { ExpressionNode tensor1, tensor2; String dimension; } { tensor1 = expression() tensor2 = expression() dimension = identifier() { return new TensorFunctionNode(new EuclideanDistance(TensorFunctionNode.wrap(tensor1), TensorFunctionNode.wrap(tensor2), dimension)); } } TensorFunctionNode tensorCosineSimilarity() : { ExpressionNode tensor1, tensor2; String dimension; } { tensor1 = expression() tensor2 = expression() dimension = identifier() { return new TensorFunctionNode(new CosineSimilarity(TensorFunctionNode.wrap(tensor1), TensorFunctionNode.wrap(tensor2), dimension)); } } TensorFunctionNode tensorMatmul() : { ExpressionNode tensor1, tensor2; String dimension; } { tensor1 = expression() tensor2 = expression() dimension = identifier() { return new TensorFunctionNode(new Matmul(TensorFunctionNode.wrap(tensor1), TensorFunctionNode.wrap(tensor2), dimension)); } } TensorFunctionNode tensorSoftmax() : { ExpressionNode tensor; String dimension; } { tensor = expression() dimension = identifier() { return new TensorFunctionNode(new Softmax(TensorFunctionNode.wrap(tensor), dimension)); } } TensorFunctionNode tensorXwPlusB() : { ExpressionNode tensor1, tensor2, tensor3; String dimension; } { tensor1 = expression() tensor2 = expression() tensor3 = expression() dimension = identifier() { return new TensorFunctionNode(new XwPlusB(TensorFunctionNode.wrap(tensor1), TensorFunctionNode.wrap(tensor2), TensorFunctionNode.wrap(tensor3), dimension)); } } TensorFunctionNode tensorExpand() : { ExpressionNode argument; String dimension; } { argument = expression() dimension = identifier() { return new TensorFunctionNode(new Expand(TensorFunctionNode.wrap(argument), dimension)); } } TensorFunctionNode tensorArgmax() : { ExpressionNode tensor; List dimensions = null; } { tensor = expression() dimensions = tagCommaLeadingList() { return new TensorFunctionNode(new Argmax(TensorFunctionNode.wrap(tensor), dimensions)); } } TensorFunctionNode tensorArgmin() : { ExpressionNode tensor; List dimensions = null; } { tensor = expression() dimensions = tagCommaLeadingList() { return new TensorFunctionNode(new Argmin(TensorFunctionNode.wrap(tensor), dimensions)); } } TensorFunctionNode tensorCellCast() : { ExpressionNode tensor; String valueType; } { tensor = expression() valueType = identifier() { return new TensorFunctionNode(new CellCast(TensorFunctionNode.wrap(tensor), TensorType.Value.fromId(valueType)));} } LambdaFunctionNode lambdaFunction() : { List variables; ExpressionNode functionExpression; } { ( variables = identifierList() functionExpression = expression() ) { return new LambdaFunctionNode(variables, functionExpression); } } Reduce.Aggregator tensorReduceAggregator() : { } { ( | | | | | | ) { return Reduce.Aggregator.valueOf(token.image); } } TensorType tensorType(List dimensionOrder) : { TensorType.Builder builder; TensorType.Value valueType; } { valueType = optionalTensorValueTypeParameter() { builder = new TensorType.Builder(valueType); } ( tensorTypeDimension(builder, dimensionOrder) ) ? ( tensorTypeDimension(builder, dimensionOrder) ) * { return builder.build(); } } TensorType.Value optionalTensorValueTypeParameter() : { String valueType = "double"; } { ( valueType = identifier() )? { return TensorType.Value.fromId(valueType); } } void tensorTypeDimension(TensorType.Builder builder, List dimensionOrder) : { String name; int size; } { name = identifier() { // Keep track of the order in which dimensions are written, if necessary if (dimensionOrder != null) dimensionOrder.add(name); } ( ( { builder.mapped(name); } ) | LOOKAHEAD(2) ( { builder.indexed(name); } ) | ( size = integerNumber() { builder.indexed(name, size); } ) ) } // This is needed not to parse tensor functions but for the "reserved names as literals" workaround cludge String tensorFunctionName() : { Reduce.Aggregator aggregator; } { ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( { return token.image; } ) | ( aggregator = tensorReduceAggregator() { return aggregator.toString(); } ) } Function unaryFunctionName() : { } { { return Function.abs; } | { return Function.acos; } | { return Function.asin; } | { return Function.atan; } | { return Function.ceil; } | { return Function.cos; } | { return Function.cosh; } | { return Function.elu; } | { return Function.exp; } | { return Function.fabs; } | { return Function.floor; } | { return Function.isNan; } | { return Function.log; } | { return Function.log10; } | { return Function.relu; } | { return Function.round; } | { return Function.sigmoid; } | { return Function.sign; } | { return Function.sin; } | { return Function.sinh; } | { return Function.square; } | { return Function.sqrt; } | { return Function.tan; } | { return Function.tanh; } | { return Function.erf; } } Function binaryFunctionName() : { } { { return Function.atan2; } | { return Function.fmod; } | { return Function.ldexp; } | { return Function.max; } | { return Function.min; } | { return Function.pow; } | { return Function.bit; } | { return Function.hamming; } } List expressionList() : { List list = new ArrayList(); ExpressionNode expression; } { expression = expression() { list.add(expression); } ( LOOKAHEAD(2) expression = expression() { list.add(expression); } ) * { return list; } } double doubleNumber() : { String sign = ""; } { ( { sign = "-";} )? ( | ) { return Double.parseDouble(sign + token.image); } } int integerNumber() : { String sign = ""; } { ( { sign = "-";} )? ( ) { return Integer.parseInt(sign + token.image); } } String identifier() : { String name; Function func; } { LOOKAHEAD(2) name = tensorFunctionName() { return name; } | func = unaryFunctionName() { return func.toString(); } | func = binaryFunctionName() { return func.toString(); } | { return token.image; } | { return token.image; } | { return token.image; } | { return token.image; } | { return token.image; } } List identifierList() : { List list = new ArrayList(); String element; } { ( element = identifier() { list.add(element); } )? ( element = identifier() { list.add(element); } ) * { return list; } } List bracedIdentifierList() : { List list = new ArrayList(); String element; } { ( element = identifier() { return Collections.singletonList(element); } ) | ( list = identifierList() { return list; } ) } // An identifier or integer String tag() : { String name; } { name = identifier() { return name; } | { return "-" + token.image; } | { return token.image; } } List tagCommaLeadingList() : { List list = new ArrayList(); String element; } { ( element = tag() { list.add(element); } ) * { return list; } } ExpressionNode constantPrimitive(boolean negate) : { String value; ExpressionNode node; } { ( { negate = !negate; } ) ? ( ( { value = token.image; } | { value = token.image; } ) { node = new ConstantNode(Value.parse(negate ? ("-" + value) : value)); } | ( | | ) { node = new ConstantNode(Value.parse(token.image)); if (negate) node = new NegativeNode(node); } ) { return node; } } TensorFunctionNode tensorValueBody(TensorType type, List dimensionOrder) : { DynamicTensor dynamicTensor; } { ( LOOKAHEAD(2) dynamicTensor = mixedTensorValueBody(type, dimensionOrder) | dynamicTensor = mappedTensorValueBody(type) | dynamicTensor = indexedTensorValueBody(type, dimensionOrder) ) { return new TensorFunctionNode(dynamicTensor); } } DynamicTensor mappedTensorValueBody(TensorType type) : { java.util.Map cells = new LinkedHashMap(); } { [ tensorCell(type, cells)] ( tensorCell(type, cells))* { return DynamicTensor.from(type, TensorFunctionNode.wrapScalars(cells)); } } DynamicTensor mixedTensorValueBody(TensorType type, List dimensionOrder) : { java.util.Map cells = new LinkedHashMap(); } { keyValueOrMixedBlock(type, dimensionOrder, cells) ( keyValueOrMixedBlock(type, dimensionOrder, cells))* { return DynamicTensor.from(type, cells); } } DynamicTensor indexedTensorValueBody(TensorType type, List dimensionOrder) : { List cells; } { cells = indexedTensorCells() { return DynamicTensor.from(type, TensorFunctionNode.wrapScalars(type, dimensionOrder, cells)); } } void keyValueOrMixedBlock(TensorType type, List dimensionOrder, java.util.Map cellMap) : {} { LOOKAHEAD(3) mixedBlock(type, dimensionOrder, cellMap) | keyValue(type, cellMap) } void keyValue(TensorType type, java.util.Map cellMap) : { String label; ExpressionNode value; } { label = label() value = expression() { cellMap.put(TensorAddress.ofLabels(label), TensorFunctionNode.wrapScalar(value)); } } void mixedBlock(TensorType type, List dimensionOrder, java.util.Map cellMap) : { String label; List cells; } { label = label() cells = indexedTensorCells() { TensorFunctionNode.wrapScalarBlock(type, dimensionOrder, label, cells, cellMap); } } List indexedTensorCells() : { List cells = new ArrayList(); } { indexedTensorCellSubspaceList(cells) { return cells; } } void indexedTensorCellSubspaceList(List cells) : { } { indexedTensorCellSubspace(cells) ( LOOKAHEAD(2) indexedTensorCellSubspace(cells) )* } void indexedTensorCellSubspace(List cells) : { ExpressionNode value; } { ( indexedTensorCellSubspaceList(cells) ) | ( value = expression() { cells.add(value); } ) } void tensorCell(TensorType type, java.util.Map cells) : { ExpressionNode value; TensorAddress.Builder addressBuilder = new TensorAddress.Builder(type); } { ( labelAndDimension(addressBuilder))* ( labelAndDimension(addressBuilder))* value = expression() { cells.put(addressBuilder.build(), value); } } void labelAndDimension(TensorAddress.Builder addressBuilder) : { String dimension, label; } { dimension = identifier() label = label() { addressBuilder.add(dimension, label); } } void labelAndDimensionValues(List addressValues) : { String dimension; Slice.DimensionValue dimensionValue; } { dimension = identifier() dimensionValue = dimensionValue(Optional.of(dimension)) { addressValues.add(dimensionValue); } } /** A tensor address (possibly on short form) represented as a list because the tensor type is not available */ List valueAddress() : { List dimensionValues = new ArrayList(); ExpressionNode valueExpression; Slice.DimensionValue dimensionValue; } { ( ( ( valueExpression = expression() { dimensionValues.add(new Slice.DimensionValue(TensorFunctionNode.wrapScalar(valueExpression))); } ) ) | LOOKAHEAD(3) ( ( labelAndDimensionValues(dimensionValues))+ ( labelAndDimensionValues(dimensionValues))* ) | ( dimensionValue = dimensionValue(Optional.empty()) { dimensionValues.add(dimensionValue); } ) ) { return dimensionValues;} } Slice.DimensionValue dimensionValue(Optional dimensionName) : { ExpressionNode value; } { value = expression() { if (value instanceof ReferenceNode && ((ReferenceNode)value).reference().isIdentifier()) // A label return new Slice.DimensionValue(dimensionName, ((ReferenceNode)value).reference().name()); else if (value instanceof ConstantNode && ((ConstantNode)value).getValue() instanceof StringValue) // A quoted label return new Slice.DimensionValue(dimensionName, ((StringValue)((ConstantNode)value).getValue()).asString()); else return new Slice.DimensionValue(dimensionName, TensorFunctionNode.wrapScalar(value)); } } String label() : { String label; } { ( label = tag() | label = string() ) { return label; } } String string() : {} { { return token.image.substring(1, token.image.length() - 1); } }