summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@oath.com>2018-02-14 09:42:46 +0100
committerJon Bratseth <bratseth@oath.com>2018-02-14 09:42:46 +0100
commitfa0bc59e2313aa6b6249ad88f7c1892a3a29553d (patch)
treef426c5657280a25c4599deecc5cca3be9378cab7
parentc17b1582face7c7f31fea7e151a5855908fe04f5 (diff)
Handle argument bindings
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java20
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/MapEvaluationTypeContext.java90
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java21
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java3
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/FeatureNamesTestCase.java9
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeValidatorTestCase.java61
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java5
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java4
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleOnlyArrayContext.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapContext.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapTypeContext.java8
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/Arguments.java14
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNode.java91
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/evaluation/MapEvaluationContext.java4
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/evaluation/TypeContext.java27
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/evaluation/VariableTensor.java2
16 files changed, 277 insertions, 86 deletions
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java b/config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java
index a55ce0982dd..649d7bddcc2 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/FeatureNames.java
@@ -5,6 +5,8 @@
*/
package com.yahoo.searchdefinition;
+import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
+
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@@ -69,7 +71,7 @@ public class FeatureNames {
feature.substring(endParenthesis));
}
- /** Canomicalizes a single argument */
+ /** Canonicalizes a single argument */
private static String canonicalizeArgument(String argument) {
if (argument.startsWith("'")) {
if ( ! argument.endsWith("'"))
@@ -89,20 +91,20 @@ public class FeatureNames {
return "\"" + argument + "\"";
}
- public static String asConstantFeature(String constantName) {
- return canonicalize("constant(\"" + constantName + "\")");
+ public static ReferenceNode.Reference asConstantFeature(String constantName) {
+ return ReferenceNode.Reference.simple("constant", constantName);
}
- public static String asAttributeFeature(String attributeName) {
- return canonicalize("attribute(\"" + attributeName + "\")");
+ public static ReferenceNode.Reference asAttributeFeature(String attributeName) {
+ return ReferenceNode.Reference.simple("attribute", attributeName);
}
- public static String asQueryFeature(String propertyName) {
- return canonicalize("query(\"" + propertyName + "\")");
+ public static ReferenceNode.Reference asQueryFeature(String propertyName) {
+ return ReferenceNode.Reference.simple("query", propertyName);
}
- /** Returns true if this is a cpomstant, attribute, or query feature */
- public static boolean isFeature(String feature) {
+ /** Returns true if this is a constant, attribute, or query feature */
+ public static boolean isSimpleFeature(String feature) {
return FeatureNames.isConstantFeature(feature) ||
FeatureNames.isAttributeFeature(feature) ||
FeatureNames.isQueryFeature(feature);
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/MapEvaluationTypeContext.java b/config-model/src/main/java/com/yahoo/searchdefinition/MapEvaluationTypeContext.java
index 38a0a23f48a..bf6a95f10c1 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/MapEvaluationTypeContext.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/MapEvaluationTypeContext.java
@@ -3,12 +3,10 @@ package com.yahoo.searchdefinition;
import com.yahoo.searchlib.rankingexpression.ExpressionFunction;
import com.yahoo.searchlib.rankingexpression.rule.Arguments;
-import com.yahoo.searchlib.rankingexpression.rule.EvaluationTypeContext;
import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode;
import com.yahoo.searchlib.rankingexpression.rule.FunctionReferenceContext;
import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
import com.yahoo.tensor.TensorType;
-import com.yahoo.tensor.evaluation.MapEvaluationContext;
import com.yahoo.tensor.evaluation.TypeContext;
import java.util.Collection;
@@ -26,9 +24,9 @@ import java.util.Optional;
*
* @author bratseth
*/
-public class MapEvaluationTypeContext extends FunctionReferenceContext implements EvaluationTypeContext {
+public class MapEvaluationTypeContext extends FunctionReferenceContext implements TypeContext {
- private final Map<String, TensorType> featureTypes = new HashMap<>();
+ private final Map<ReferenceNode.Reference, TensorType> featureTypes = new HashMap<>();
public MapEvaluationTypeContext(Collection<ExpressionFunction> functions) {
super(functions);
@@ -36,33 +34,34 @@ public class MapEvaluationTypeContext extends FunctionReferenceContext implement
public MapEvaluationTypeContext(Map<String, ExpressionFunction> functions,
Map<String, String> bindings,
- Map<String, TensorType> featureTypes) {
+ Map<ReferenceNode.Reference, TensorType> featureTypes) {
super(functions, bindings);
this.featureTypes.putAll(featureTypes);
}
- public void setType(String name, TensorType type) {
- featureTypes.put(FeatureNames.canonicalize(name), type);
+ public void setType(Name name, TensorType type) {
+ // TODO: Use a type parameter if we do this both here and in getType ...
+ if ( ! (name instanceof ReferenceNode.Reference))
+ throw new IllegalArgumentException("Not expecting unstructured names here");
+ featureTypes.put((ReferenceNode.Reference)name, type);
}
- // TODO: Remove?
@Override
- public TensorType getType(String name) {
- if (FeatureNames.isFeature(name))
- return featureTypes.get(FeatureNames.canonicalize(name));
- else
- return TensorType.empty; // we do not have type information for these. Correct would be either empty or null
- }
-
- @Override
- public TensorType getType(String name, Arguments arguments, String output) {
- Optional<String> simpleFeature = simpleFeature(name, arguments); // (all simple feature outputs return the same type)
- if (simpleFeature.isPresent())
- return featureTypes.get(simpleFeature.get());
-
- Optional<ExpressionFunction> function = functionInvocation(name, output);
+ public TensorType getType(Name name) {
+ if ( ! (name instanceof ReferenceNode.Reference))
+ throw new IllegalArgumentException("Not expecting unstructured names here");
+ ReferenceNode.Reference reference = (ReferenceNode.Reference)name;
+
+ if (isSimpleFeature(reference)) {
+ // The argument may be a local identifier bound to the actual value
+ String argument = simpleArgument(reference.arguments()).get();
+ reference = ReferenceNode.Reference.simple(reference.name(), bindings.getOrDefault(argument, argument));
+ return featureTypes.get(reference);
+ }
+
+ Optional<ExpressionFunction> function = functionInvocation(reference);
if (function.isPresent())
- return function.get().getBody().type(this.withBindings(bind(function.get().arguments())));
+ return function.get().getBody().type(this.withBindings(bind(function.get().arguments(), reference.arguments())));
// We do not know what this is - since we do not have complete knowledge abut the match features
// in Java we must assume this is a match feature and return the double type - which is the type of all
@@ -71,18 +70,33 @@ public class MapEvaluationTypeContext extends FunctionReferenceContext implement
}
/**
- * If the arguments makes a simple feature ("attribute(name)", "constant(name)" or "query(name)",
+ * Return whether the reference (discarding the output) is a simple feature
+ * ("attribute(name)", "constant(name)" or "query(name)").
+ * We disregard the output because all outputs under a simple feature have the same type.
+ */
+ private boolean isSimpleFeature(ReferenceNode.Reference reference) {
+ Optional<String> argument = simpleArgument(reference.arguments());
+ if ( ! argument.isPresent()) return false;
+ return reference.name().equals("attribute") ||
+ reference.name().equals("constant") ||
+ reference.name().equals("query");
+ }
+
+ /**
+ * If the reference (discarding the output) is a simple feature
+ * ("attribute(name)", "constant(name)" or "query(name)"),
* it is returned. Otherwise empty is returned.
+ * We disregard the output because all outputs under a simple feature have the same type.
*/
- private Optional<String> simpleFeature(String name, Arguments arguments) {
- Optional<String> argument = simpleArgument(arguments);
+ private Optional<String> simpleFeature(ReferenceNode.Reference reference) {
+ Optional<String> argument = simpleArgument(reference.arguments());
if ( ! argument.isPresent()) return Optional.empty();
// The argument may be a "local value" bound to another value, or else it is the "global" argument of the feature
String actualArgument = bindings.getOrDefault(argument.get(), argument.get());
- String feature = asFeatureString(name, actualArgument);
- if (FeatureNames.isFeature(feature))
+ String feature = asFeatureString(reference.name(), actualArgument);
+ if (FeatureNames.isSimpleFeature(feature))
return Optional.of(feature);
else
return Optional.empty();
@@ -108,20 +122,24 @@ public class MapEvaluationTypeContext extends FunctionReferenceContext implement
return Optional.of(refArgument.getName());
}
- private Optional<ExpressionFunction> functionInvocation(String name, String output) {
- if (output != null) return Optional.empty();
- return Optional.ofNullable(functions().get(name));
+ private Optional<ExpressionFunction> functionInvocation(ReferenceNode.Reference reference) {
+ if (reference.output() != null) return Optional.empty();
+ return Optional.ofNullable(functions().get(reference.name()));
}
/** Binds the given list of formal arguments to their actual values */
- private Map<String, String> bind(List<String> arguments) {
- Map<String, String> bindings = new HashMap<>(arguments.size());
- for (String formalArgument : arguments)
- bindings.put(formalArgument, null); // TODO
+ private Map<String, String> bind(List<String> formalArguments,
+ Arguments invocationArguments) {
+ // TODO: What is our position on argument overloading/argument count differences?
+ Map<String, String> bindings = new HashMap<>(formalArguments.size());
+ for (int i = 0; i < formalArguments.size(); i++)
+ bindings.put(formalArguments.get(i), invocationArguments.expressions().get(i).toString()); // TODO: toString does not work generally
return bindings;
}
- public Map<String, TensorType> featureTypes() { return Collections.unmodifiableMap(featureTypes); }
+ public Map<ReferenceNode.Reference, TensorType> featureTypes() {
+ return Collections.unmodifiableMap(featureTypes);
+ }
@Override
public MapEvaluationTypeContext withBindings(Map<String, String> bindings) {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
index 51c7618acff..0b0c0c9e0ca 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
@@ -32,6 +32,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -357,14 +358,14 @@ public class RankProfile implements Serializable, Cloneable {
/** Returns a read-only view of the summary features to use in this profile. This is never null */
public Set<ReferenceNode> getSummaryFeatures() {
- if (summaryFeatures!=null) return Collections.unmodifiableSet(summaryFeatures);
- if (getInherited()!=null) return getInherited().getSummaryFeatures();
+ if (summaryFeatures != null) return Collections.unmodifiableSet(summaryFeatures);
+ if (getInherited() != null) return getInherited().getSummaryFeatures();
return Collections.emptySet();
}
public void addSummaryFeature(ReferenceNode feature) {
- if (summaryFeatures==null)
- summaryFeatures=new LinkedHashSet<>();
+ if (summaryFeatures == null)
+ summaryFeatures = new LinkedHashSet<>();
summaryFeatures.add(feature);
}
@@ -766,16 +767,18 @@ public class RankProfile implements Serializable, Cloneable {
for (QueryProfileType queryProfileType : queryProfiles.getTypeRegistry().allComponents()) {
for (FieldDescription field : queryProfileType.declaredFields().values()) {
TensorType type = field.getType().asTensorType();
- if ( ! FeatureNames.isQueryFeature(field.getName())) continue;
- String feature = FeatureNames.canonicalize(field.getName());
- TensorType existingType = context.getType(feature);
+ Optional<ReferenceNode.Reference> feature = ReferenceNode.Reference.simple(field.getName());
+ if ( ! feature.isPresent() || ! feature.get().name().equals("query")) continue;
+
+ TensorType existingType = context.getType(feature.get());
if (existingType != null)
type = existingType.dimensionwiseGeneralizationWith(type).orElseThrow( () ->
new IllegalArgumentException(queryProfileType + " contains query feature " + feature +
" with type " + field.getType().asTensorType() +
", but this is already defined " +
- "in another query profile with type " + context.getType(feature)));
- context.setType(feature, type);
+ "in another query profile with type " +
+ context.getType(feature.get())));
+ context.setType(feature.get(), type);
}
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java b/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
index 469f29098ad..e7cd21ac834 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
@@ -18,6 +18,7 @@ import com.yahoo.searchdefinition.parser.TokenMgrError;
import com.yahoo.searchdefinition.processing.Processing;
import com.yahoo.vespa.documentmodel.DocumentModel;
import com.yahoo.vespa.model.container.search.QueryProfiles;
+import com.yahoo.yolean.Exceptions;
import java.io.File;
import java.io.IOException;
@@ -153,7 +154,7 @@ public class SearchBuilder {
} catch (TokenMgrError e) {
throw new ParseException("Unknown symbol: " + e.getMessage());
} catch (ParseException pe) {
- throw new ParseException(stream.formatException(pe.getMessage()));
+ throw new ParseException(stream.formatException(Exceptions.toMessageString(pe)));
}
return importRawSearch(search);
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/FeatureNamesTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/FeatureNamesTestCase.java
index 1f60ad870ec..182aaba5be8 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/FeatureNamesTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/FeatureNamesTestCase.java
@@ -42,17 +42,20 @@ public class FeatureNamesTestCase {
@Test
public void testConstantFeature() {
- assertEquals("constant(\"foo/bar\")", FeatureNames.asConstantFeature("foo/bar"));
+ assertEquals("constant(\"foo/bar\")",
+ FeatureNames.asConstantFeature("foo/bar").toString());
}
@Test
public void testAttributeFeature() {
- assertEquals("attribute(foo)", FeatureNames.asAttributeFeature("foo"));
+ assertEquals("attribute(foo)",
+ FeatureNames.asAttributeFeature("foo").toString());
}
@Test
public void testQueryFeature() {
- assertEquals("query(\"foo.bar\")", FeatureNames.asQueryFeature("foo.bar"));
+ assertEquals("query(\"foo.bar\")",
+ FeatureNames.asQueryFeature("foo.bar").toString());
}
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeValidatorTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeValidatorTestCase.java
index db3b12db1bf..b8117178c74 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeValidatorTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeValidatorTestCase.java
@@ -1,22 +1,33 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.processing;
+import com.yahoo.searchdefinition.RankProfile;
import com.yahoo.searchdefinition.RankProfileRegistry;
+import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.SearchBuilder;
+import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
+import com.yahoo.tensor.TensorType;
import com.yahoo.yolean.Exceptions;
import org.junit.Test;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
import static com.yahoo.config.model.test.TestUtil.joinLines;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+/**
+ * @author bratseth
+ */
public class RankingExpressionTypeValidatorTestCase {
@Test
public void tensorFirstPhaseMustProduceDouble() throws Exception {
try {
RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
- SearchBuilder searchBuilder = new SearchBuilder(rankProfileRegistry);
- searchBuilder.importString(joinLines(
+ SearchBuilder builder = new SearchBuilder(rankProfileRegistry);
+ builder.importString(joinLines(
"search test {",
" document test { ",
" field a type tensor(x[],y[]) {",
@@ -30,7 +41,7 @@ public class RankingExpressionTypeValidatorTestCase {
" }",
"}"
));
- searchBuilder.build();
+ builder.build();
fail("Expected exception");
}
catch (IllegalArgumentException expected) {
@@ -43,8 +54,8 @@ public class RankingExpressionTypeValidatorTestCase {
public void tensorSecondPhaseMustProduceDouble() throws Exception {
try {
RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
- SearchBuilder searchBuilder = new SearchBuilder(rankProfileRegistry);
- searchBuilder.importString(joinLines(
+ SearchBuilder builder = new SearchBuilder(rankProfileRegistry);
+ builder.importString(joinLines(
"search test {",
" document test { ",
" field a type tensor(x[],y[]) {",
@@ -61,7 +72,7 @@ public class RankingExpressionTypeValidatorTestCase {
" }",
"}"
));
- searchBuilder.build();
+ builder.build();
fail("Expected exception");
}
catch (IllegalArgumentException expected) {
@@ -101,4 +112,42 @@ public class RankingExpressionTypeValidatorTestCase {
}
}
+ @Test
+ public void testMacroInvocationTypes() throws Exception {
+ RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
+ SearchBuilder builder = new SearchBuilder(rankProfileRegistry);
+ builder.importString(joinLines(
+ "search test {",
+ " document test { ",
+ " field a type tensor(x[],y[]) {",
+ " indexing: attribute",
+ " }",
+ " field b type tensor(z[10]) {",
+ " indexing: attribute",
+ " }",
+ " }",
+ " rank-profile my_rank_profile {",
+ " macro macro1(attribute_to_use) {",
+ " expression: attribute(attribute_to_use)",
+ " }",
+ " summary-features {",
+ " macro1(a)",
+ " macro1(b)",
+ " }",
+ " }",
+ "}"
+ ));
+ builder.build();
+ RankProfile profile =
+ builder.getRankProfileRegistry().getRankProfile(builder.getSearch(), "my_rank_profile");
+ assertEquals(TensorType.fromSpec("tensor(x[],y[])"),
+ summaryFeatures(profile).get("macro1(a)").type(profile.typeContext(builder.getQueryProfileRegistry())));
+ assertEquals(TensorType.fromSpec("tensor(z[10])"),
+ summaryFeatures(profile).get("macro1(b)").type(profile.typeContext(builder.getQueryProfileRegistry())));
+ }
+
+ private Map<String, ReferenceNode> summaryFeatures(RankProfile profile) {
+ return profile.getSummaryFeatures().stream().collect(Collectors.toMap(f -> f.toString(), f -> f));
+ }
+
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java
index 49466f1974d..f0532d9d433 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/FeatureList.java
@@ -91,8 +91,8 @@ public class FeatureList implements Iterable<ReferenceNode> {
/**
* Returns the feature at the given index.
*
- * @param i The index of the feature to return.
- * @return The featuer at the given index.
+ * @param i the index of the feature to return.
+ * @return the feature at the given index.
*/
public ReferenceNode get(int i) {
return features.get(i);
@@ -137,4 +137,5 @@ public class FeatureList implements Iterable<ReferenceNode> {
public Iterator<ReferenceNode> iterator() {
return features.iterator();
}
+
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java
index 5f8daa69ecf..486affe9371 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/ArrayContext.java
@@ -82,8 +82,8 @@ public class ArrayContext extends AbstractArrayContext implements Cloneable {
}
@Override
- public TensorType getType(String name) {
- Integer index = nameToIndex().get(name);
+ public TensorType getType(Name name) {
+ Integer index = nameToIndex().get(name.toString());
if (index == null) return null;
return values[index].type();
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleOnlyArrayContext.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleOnlyArrayContext.java
index 0625e8506cc..01b8bffe995 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleOnlyArrayContext.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleOnlyArrayContext.java
@@ -68,7 +68,7 @@ public class DoubleOnlyArrayContext extends AbstractArrayContext {
}
@Override
- public TensorType getType(String name) { return TensorType.empty; }
+ public TensorType getType(Name name) { return TensorType.empty; }
/** Perform a slow lookup by name */
@Override
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapContext.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapContext.java
index a81d0c89f8f..c7679ea9e55 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapContext.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapContext.java
@@ -42,7 +42,7 @@ public class MapContext extends Context {
/** Returns the type of the given value key, or null if it is not bound. */
@Override
- public TensorType getType(String key) {
+ public TensorType getType(Name key) {
Value value = bindings.get(key);
if (value == null) return null;
return value.type();
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapTypeContext.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapTypeContext.java
index d461ae52cbe..2ddc8213d94 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapTypeContext.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/MapTypeContext.java
@@ -15,18 +15,18 @@ import java.util.Map;
*/
public class MapTypeContext implements TypeContext {
- private final Map<String, TensorType> featureTypes = new HashMap<>();
+ private final Map<Name, TensorType> featureTypes = new HashMap<>();
public void setType(String name, TensorType type) {
- featureTypes.put(name, type);
+ featureTypes.put(new Name(name), type);
}
@Override
- public TensorType getType(String name) {
+ public TensorType getType(Name name) {
return featureTypes.get(name);
}
/** Returns an unmodifiable map of the bindings in this */
- public Map<String, TensorType> bindings() { return Collections.unmodifiableMap(featureTypes); }
+ public Map<Name, TensorType> bindings() { return Collections.unmodifiableMap(featureTypes); }
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/Arguments.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/Arguments.java
index fb9a7cb9ad7..d7163fe9166 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/Arguments.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/Arguments.java
@@ -13,7 +13,8 @@ import java.util.List;
/**
* A set of argument expressions to a function or feature.
- * This is immutable.
+ * This is a value object.
+ *.
*
* @author bratseth
*/
@@ -22,7 +23,11 @@ public final class Arguments implements Serializable {
private final ImmutableList<ExpressionNode> expressions;
public Arguments() {
- this(null);
+ this(ImmutableList.of());
+ }
+
+ public Arguments(ExpressionNode singleArgument) {
+ this(ImmutableList.of(singleArgument));
}
public Arguments(List<? extends ExpressionNode> expressions) {
@@ -62,8 +67,9 @@ public final class Arguments implements Serializable {
}
@Override
- public boolean equals(Object rhs) {
- return rhs instanceof Arguments && expressions.equals(((Arguments)rhs).expressions);
+ public boolean equals(Object other) {
+ if (other == this) return true;
+ return other instanceof Arguments && expressions.equals(((Arguments)other).expressions);
}
@Override
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNode.java
index ebfef21a815..e121fa12b5f 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ReferenceNode.java
@@ -9,8 +9,13 @@ import com.yahoo.tensor.TensorType;
import com.yahoo.tensor.evaluation.TypeContext;
import java.util.ArrayDeque;
+import java.util.Arrays;
import java.util.Deque;
import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* A node referring either to a value in the context or to a named ranking expression (function aka macro).
@@ -31,6 +36,7 @@ public final class ReferenceNode extends CompositeNode {
}
public ReferenceNode(String name, List<? extends ExpressionNode> arguments, String output) {
+ Objects.requireNonNull(name, "name cannot be null");
this.name = name;
this.arguments = arguments != null ? new Arguments(arguments) : new Arguments();
this.output = output;
@@ -119,11 +125,10 @@ public final class ReferenceNode extends CompositeNode {
@Override
public TensorType type(TypeContext context) {
- String feature = toString(new SerializationContext(), null, false);
- TensorType type = context.getType(feature);
- // TensorType type = context.getType(name, arguments, output); TODO
+ TensorType type = context.getType(new Reference(name, arguments, output,
+ toString(new SerializationContext(), null, true)));
if (type == null)
- throw new IllegalArgumentException("Unknown feature '" + feature + "'");
+ throw new IllegalArgumentException("Unknown feature '" + toString() + "'");
return type;
}
@@ -139,4 +144,82 @@ public final class ReferenceNode extends CompositeNode {
return new ReferenceNode(name, newChildren, output);
}
+ /** Wraps the content of this in a form which can be passed to a type context */
+ // TODO: Extract to top level?
+ public static class Reference extends TypeContext.Name {
+
+ private static final Pattern identifierRegexp = Pattern.compile("[A-Za-z0-9_][A-Za-z0-9_-]*");
+
+ private final String name;
+ private final Arguments arguments;
+
+ /** The output, or null if none */
+ private final String output;
+
+ public Reference(String name, Arguments arguments, String output, String stringForm) {
+ super(stringForm);
+ Objects.requireNonNull(name, "name cannot be null");
+ Objects.requireNonNull(arguments, "arguments cannot be null");
+ Objects.requireNonNull(stringForm, "stringForm cannot be null");
+ this.name = name;
+ this.arguments = arguments;
+ this.output = output;
+ }
+
+ public String name() { return name; }
+ public Arguments arguments() { return arguments; }
+ public String output() { return output; }
+
+ /** Creates a reference to a simple feature consisting of a name and a single argument */
+ public static Reference simple(String name, String argumentValue) {
+ return new Reference(name,
+ new Arguments(new ReferenceNode(argumentValue)),
+ null,
+ name + "(" + quoteIfNecessary(argumentValue) + ")");
+ }
+
+ /**
+ * Returns the given simple feature as a reference, or empty if it is not a valid simple
+ * feature string on the form name(argument).
+ */
+ public static Optional<Reference> simple(String feature) {
+ int startParenthesis = feature.indexOf('(');
+ if (startParenthesis < 0)
+ return Optional.empty();
+ int endParenthesis = feature.lastIndexOf(')');
+ String featureName = feature.substring(0, startParenthesis);
+ if (startParenthesis < 1 || endParenthesis < startParenthesis) return Optional.empty();
+ String argument = feature.substring(startParenthesis + 1, endParenthesis);
+ if (argument.startsWith("'") || argument.startsWith("\""))
+ argument = argument.substring(1);
+ if (argument.endsWith("'") || argument.endsWith("\""))
+ argument = argument.substring(0, argument.length() - 1);
+ return Optional.of(simple(featureName, argument));
+ }
+
+ private static String quoteIfNecessary(String s) {
+ if (identifierRegexp.matcher(s).matches())
+ return s;
+ else
+ return "\"" + s + "\"";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if ( ! (o instanceof Reference)) return false;
+ Reference other = (Reference)o;
+ if ( ! Objects.equals(other.name, this.name)) return false;
+ if ( ! Objects.equals(other.arguments, this.arguments)) return false;
+ if ( ! Objects.equals(other.output, this.output)) return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, arguments, output);
+ }
+
+ }
+
}
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/evaluation/MapEvaluationContext.java b/vespajlib/src/main/java/com/yahoo/tensor/evaluation/MapEvaluationContext.java
index 9fe6b7d053f..078f2e39815 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/evaluation/MapEvaluationContext.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/evaluation/MapEvaluationContext.java
@@ -20,8 +20,8 @@ public class MapEvaluationContext implements EvaluationContext {
public void put(String name, Tensor tensor) { bindings.put(name, tensor); }
@Override
- public TensorType getType(String name) {
- Tensor tensor = bindings.get(name);
+ public TensorType getType(Name name) {
+ Tensor tensor = bindings.get(name.toString());
if (tensor == null) return null;
return tensor.type();
}
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/evaluation/TypeContext.java b/vespajlib/src/main/java/com/yahoo/tensor/evaluation/TypeContext.java
index 760a225efdf..ecb3a801324 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/evaluation/TypeContext.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/evaluation/TypeContext.java
@@ -16,6 +16,31 @@ public interface TypeContext {
* @return returns the type of the tensor which will be returned by calling getTensor(name)
* or null if getTensor will return null.
*/
- TensorType getType(String name);
+ TensorType getType(Name name);
+
+ /** A name which is just a string. Names are value objects. */
+ class Name {
+
+ private final String name;
+
+ public Name(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() { return name; }
+
+ @Override
+ public int hashCode() { return name.hashCode(); }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) return true;
+ if ( ! (other instanceof Name)) return false;
+ return ((Name)other).name.equals(this.name);
+ }
+
+ }
+
}
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/evaluation/VariableTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/evaluation/VariableTensor.java
index 34beb465d4c..5f809a3d2b1 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/evaluation/VariableTensor.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/evaluation/VariableTensor.java
@@ -45,7 +45,7 @@ public class VariableTensor extends PrimitiveTensorFunction {
@Override
public TensorType type(TypeContext context) {
- TensorType givenType = context.getType(name);
+ TensorType givenType = context.getType(new TypeContext.Name(name));
if (givenType == null) return null;
verifyType(givenType);
return givenType;