Skip to content

Commit

Permalink
Fix more cases of function resolution bugs (#1159)
Browse files Browse the repository at this point in the history
* Fix more cases of function resolution bugs

* Fix imports
  • Loading branch information
JPercival committed Apr 27, 2023
1 parent e0e99ec commit a53f315
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public void test_function_overload_with_no_signature() throws IOException, UcumE

var result = context.resolveExpressionRef("TestAnyFunctionWithNoArgs").getExpression().evaluate(context);
assertThat(result, is("any"));

result = context.resolveExpressionRef("TestAnyFunctionWith2Args").getExpression().evaluate(context);
assertThat(result, is(3));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ define function TestAny(c Decimal):
define function TestAny():
'any'

define function TestAny(a Integer, b Integer):
a + b


define TestAnyFunctionWithInteger:
TestAny(1)

Expand All @@ -22,4 +26,7 @@ define TestAnyFunctionWithDecimal:
TestAny(12.3)

define TestAnyFunctionWithNoArgs:
TestAny()
TestAny()

define TestAnyFunctionWith2Args:
TestAny(1, 2)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -560,66 +561,38 @@ private boolean isType(Class<?> argumentType, Class<?> operandType) {
return argumentType == null || operandType.isAssignableFrom(argumentType);
}

private FunctionDef resolveFunctionDesc(FunctionDesc functionDesc, List<Object> arguments) {
private boolean matchesTypes(FunctionDef functionDef, List<? extends Object> arguments) {
boolean isMatch = true;

var operands = functionDesc.operandTypes();
var operands = functionDef.getOperand();

// if argument length is mismatched, don't compare
if (arguments.size() != operands.size()) {
return null;
return false;
}

for (var i = 0; i < arguments.size(); i++) {
isMatch = isType(resolveType(arguments.get(i)), operands.get(i));
isMatch = isType(resolveType(arguments.get(i)), this.resolveOperandType(operands.get(i)));
if (!isMatch) {
break;
}
}

return isMatch ? functionDesc.functionDef() : null;
}

static class FunctionDesc {
public FunctionDesc(FunctionDef functionDef, List<Class<?>> operandTypes) {
this.functionDef = functionDef;
this.operandTypes = operandTypes;
}

private FunctionDef functionDef;
private List<Class<?>> operandTypes;

public FunctionDef functionDef() {
return this.functionDef;
}

public List<Class<?>> operandTypes() {
return this.operandTypes;
}
}

private FunctionDesc createFunctionDesc(FunctionDef functionDef) {
var operandTypes = new ArrayList<Class<?>>(functionDef.getOperand().size());
for (var op : functionDef.getOperand()) {
operandTypes.add(this.resolveOperandType(op));
}

return new FunctionDesc(functionDef, operandTypes);
return isMatch;
}

private String getMangledFunctionName(String libraryName, String name) {
return (libraryName == null ? getCurrentLibrary().getIdentifier().getId() : libraryName) + "." + name;
}
private Map<String, List<FunctionDesc>> functionCache = new HashMap<>();

public FunctionDef resolveFunctionRef(String libraryName, String name, List<Object> arguments, List<TypeSpecifier> signature) {
private Map<String, List<FunctionDef>> functionDefsByMangledName = new HashMap<>();

public FunctionDef resolveFunctionRef(final String libraryName, final String name, final List<Object> arguments, final List<TypeSpecifier> signature) {
FunctionDef ret;

List<Object> types = arguments;
if (!signature.isEmpty()) {
types = signature.stream().map(e -> (Object) e).collect(Collectors.toList());
}
ret = getResolvedFunctionDesc(libraryName, name, types, !arguments.isEmpty(), !signature.isEmpty());
final List<? extends Object> types = signature.isEmpty() ? arguments : signature;

ret = getResolvedFunctionDef(libraryName, name, types, !signature.isEmpty());

if (ret != null) {
return ret;
Expand All @@ -629,35 +602,41 @@ public FunctionDef resolveFunctionRef(String libraryName, String name, List<Obje
name, getUnresolvedMessage(types, name), getCurrentLibrary().getIdentifier().getId()));
}

private FunctionDef getResolvedFunctionDesc(String libraryName, String name, List<Object> types, boolean hasArguments, boolean hasSignature) {
private FunctionDef getResolvedFunctionDef(final String libraryName, final String name, final List<? extends Object> types, final boolean hasSignature) {
String mangledFunctionName = getMangledFunctionName(libraryName, name);
List<FunctionDesc> descriptions = this.functionCache.computeIfAbsent(mangledFunctionName, x -> this.buildDescriptions(name));
List<FunctionDef> namedDefs = this.functionDefsByMangledName
.computeIfAbsent(mangledFunctionName, x -> this.getFunctionDefs(name));

if (descriptions.size() > 1 && (hasArguments && !hasSignature)) {
throw new CqlException(String.format("Signature not provided for overloaded function '%s'", mangledFunctionName));
var candidateDefs = namedDefs
.stream()
.filter(x -> x.getOperand().size() == types.size())
.collect(Collectors.toList());

if (candidateDefs.size() == 1) {
return candidateDefs.get(0);
}

FunctionDef ret = null;
for (FunctionDesc functionDesc : descriptions) {
if ((ret = resolveFunctionDesc(functionDesc, types)) != null) {
break;
}
if (candidateDefs.size() > 1 && !hasSignature) {
throw new CqlException(String.format("Signature not provided for overloaded function '%s'", mangledFunctionName));
}
return ret;

return candidateDefs.stream().filter(x -> matchesTypes(x, types)).findFirst().orElse(null);
}

private List<FunctionDesc> buildDescriptions(String name) {
List<FunctionDesc> descriptions = new ArrayList<>();
for (ExpressionDef expressionDef : getCurrentLibrary().getStatements().getDef()) {
if (expressionDef.getName().equals(name) && expressionDef instanceof FunctionDef) {
descriptions.add(createFunctionDesc((FunctionDef) expressionDef));
}
private List<FunctionDef> getFunctionDefs(final String name) {
final var statements = getCurrentLibrary().getStatements();
if (statements == null) {
return Collections.emptyList();
}

return descriptions;
return statements.getDef().stream()
.filter(x -> x.getName().equals(name))
.filter(FunctionDef.class::isInstance)
.map(FunctionDef.class::cast)
.collect(Collectors.toList());
}

private String getUnresolvedMessage(List<Object> arguments, String name) {
private String getUnresolvedMessage(List<? extends Object> arguments, String name) {
StringBuilder argStr = new StringBuilder();
if (arguments != null) {
arguments.forEach(a -> argStr.append((argStr.length() > 0) ? ", " : "").append(resolveType(a).getName()));
Expand Down

0 comments on commit a53f315

Please sign in to comment.