Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/main/java/com/aerospike/dsl/parts/cdt/list/ListValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.aerospike.dsl.client.exp.ListExp;
import com.aerospike.dsl.parts.path.BasePath;

import static com.aerospike.dsl.util.ParsingUtils.objectToExp;
import static com.aerospike.dsl.util.ParsingUtils.parseValueIdentifier;

public class ListValue extends ListPart {
Expand All @@ -23,13 +24,7 @@ public static ListValue from(ConditionParser.ListValueContext ctx) {

@Override
public Exp constructExp(BasePath basePath, Exp.Type valueType, int cdtReturnType, CTX[] context) {
Exp valueExp = switch (valueType) {
case BOOL -> Exp.val((Boolean) value);
case STRING -> Exp.val((String) value);
case FLOAT -> Exp.val((Float) value);
default -> Exp.val((Integer) value); // for getByValue the default is INT
};
return ListExp.getByValue(cdtReturnType, valueExp, Exp.bin(basePath.getBinPart().getBinName(),
return ListExp.getByValue(cdtReturnType, objectToExp(value), Exp.bin(basePath.getBinPart().getBinName(),
basePath.getBinType()), context);
}

Expand Down
10 changes: 2 additions & 8 deletions src/main/java/com/aerospike/dsl/parts/cdt/map/MapValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.aerospike.dsl.client.exp.MapExp;
import com.aerospike.dsl.parts.path.BasePath;

import static com.aerospike.dsl.util.ParsingUtils.objectToExp;
import static com.aerospike.dsl.util.ParsingUtils.parseValueIdentifier;

public class MapValue extends MapPart {
Expand All @@ -23,14 +24,7 @@ public static MapValue from(ConditionParser.MapValueContext ctx) {

@Override
public Exp constructExp(BasePath basePath, Exp.Type valueType, int cdtReturnType, CTX[] context) {
Exp valueExp = switch (valueType) {
case BOOL -> Exp.val((Boolean) value);
case STRING -> Exp.val((String) value);
case FLOAT -> Exp.val((Float) value);
default -> Exp.val((Integer) value); // for getByValue the default is INT
};

return MapExp.getByValue(cdtReturnType, valueExp, Exp.bin(basePath.getBinPart().getBinName(),
return MapExp.getByValue(cdtReturnType, objectToExp(value), Exp.bin(basePath.getBinPart().getBinName(),
basePath.getBinType()), context);
}

Expand Down
9 changes: 6 additions & 3 deletions src/main/java/com/aerospike/dsl/parts/path/Path.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ public Exp processPath(BasePath basePath, PathFunction pathFunction) {
cdtReturnType = ((CdtPart) lastPathPart).getReturnType(pathFunction.getReturnParam());
}

if (lastPathPart != null) { // only if there are other parts except a bin
return switch (pathFunction.getPathFunctionType()) {
// CAST is the same as GET with a different type
if (lastPathPart != null) {
Exp exp = switch (pathFunction.getPathFunctionType()) {
case GET, COUNT, CAST -> processGet(basePath, lastPathPart, valueType, cdtReturnType);
case SIZE -> processSize(basePath, lastPathPart, valueType, cdtReturnType);
};
if (pathFunction.getPathFunctionType() == PathFunction.PathFunctionType.CAST && exp != null) {
exp = pathFunction.getBinType() == Exp.Type.FLOAT ? Exp.toFloat(exp) : Exp.toInt(exp);
}
return exp;
}
return null;
}
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/aerospike/dsl/parts/path/PathFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.aerospike.dsl.client.exp.Exp;
import com.aerospike.dsl.parts.AbstractPart;
import com.aerospike.dsl.parts.ExpressionContainer.ExprPartsOperation;
import lombok.Getter;

@Getter
Expand All @@ -25,6 +26,24 @@ public static Exp.Type castTypeToExpType(CastType castType) {
};
}

/**
* Returns the source {@link Exp.Type} for a cast — the type the value must
* already have before the cast is applied (e.g., casting to INT requires a FLOAT source).
*/
public static Exp.Type castSourceExpType(CastType castType) {
return switch (castType) {
case INT -> Exp.Type.FLOAT;
case FLOAT -> Exp.Type.INT;
};
}

public static ExprPartsOperation castTypeToOperation(CastType castType) {
return switch (castType) {
case INT -> ExprPartsOperation.TO_INT;
case FLOAT -> ExprPartsOperation.TO_FLOAT;
Comment on lines +35 to +43
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These methods both start with "cast" so I assume they have a similar function. Yet one maps INT -> INT, the other INT -> FLOAT. Is this deliberate or is one of the methods backwards? If so I would add a comment in to explain why.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually deliberate. The inverse mapping in castSourceExpType exists because when toInt() is used, the bin must be read with Exp.Type.FLOAT (its actual type) before the cast converts it to INT, it can be seen in PathOperandUtils.processValueType():56-59.
You are right that this can be confusing. The name castSourceExpType doesn't immediately convey "inverse of target". Adding a comment.

};
}

public enum ReturnParam {
VALUE,
INDEX,
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/aerospike/dsl/util/ParsingUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.aerospike.dsl.ConditionParser;
import com.aerospike.dsl.DslParseException;
import com.aerospike.dsl.client.exp.Exp;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
import org.antlr.v4.runtime.ParserRuleContext;
Expand Down Expand Up @@ -166,6 +167,21 @@ public static Integer requireIntValueIdentifier(ConditionParser.ValueIdentifierC
"Value range requires integer operands, got: %s".formatted(ctx.getText()));
}

/**
* Converts a parsed value object to an {@link Exp} value expression.
* Supports the types produced by {@link #parseValueIdentifier}: {@link String} and {@link Integer}.
*
* @param value The parsed value object
* @return The corresponding {@link Exp} value expression
* @throws DslParseException if the value type is not supported
*/
public static Exp objectToExp(Object value) {
if (value instanceof String s) return Exp.val(s);
if (value instanceof Integer i) return Exp.val(i);
throw new DslParseException(
"Unsupported value type for Exp conversion: " + value.getClass().getSimpleName());
}

/**
* Get the string inside the quotes.
*
Expand Down
25 changes: 24 additions & 1 deletion src/main/java/com/aerospike/dsl/util/PathOperandUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public class PathOperandUtils {
* @return The determined {@link Exp.Type} for the value
*/
public static Exp.Type processValueType(AbstractPart lastPathPart, PathFunction pathFunction) {
// there is always a path function with non-null function type and return param
if (pathFunction.getBinType() == null) {
if (isListTypeDesignator(lastPathPart)) {
return Exp.Type.LIST;
Expand All @@ -49,6 +48,16 @@ public static Exp.Type processValueType(AbstractPart lastPathPart, PathFunction
return findValueType(lastPathPart, pathFunction.getPathFunctionType());
}
}
if (pathFunction.getPathFunctionType() == PathFunction.PathFunctionType.CAST) {
// Value selectors (getByValue/getByValueList/getByValueRange) have no return-type
// parameter; their constructExp ignores valueType. The cast wrapping is applied
// afterward in Path.processPath(), so the source-type logic does not apply here.
if (isValueSelector(lastPathPart)) {
return TypeUtils.getDefaultType(lastPathPart);
}
PathFunction.CastType castType = PathFunction.CastType.valueOf(pathFunction.getBinType().toString());
return PathFunction.castSourceExpType(castType);
}
return Exp.Type.valueOf(pathFunction.getBinType().toString());
}

Expand Down Expand Up @@ -226,6 +235,20 @@ private static boolean isMapTypeDesignator(AbstractPart cdtPart) {
return cdtPart.getPartType() == MAP_PART && ((MapPart) cdtPart).getMapPartType().equals(MAP_TYPE_DESIGNATOR);
}

private static boolean isValueSelector(AbstractPart cdtPart) {
if (cdtPart.getPartType() == LIST_PART) {
ListPart.ListPartType type = ((ListPart) cdtPart).getListPartType();
return type == ListPart.ListPartType.VALUE || type == ListPart.ListPartType.VALUE_LIST
|| type == ListPart.ListPartType.VALUE_RANGE;
}
if (cdtPart.getPartType() == MAP_PART) {
MapPart.MapPartType type = ((MapPart) cdtPart).getMapPartType();
return type == MapPart.MapPartType.VALUE || type == MapPart.MapPartType.VALUE_LIST
|| type == MapPart.MapPartType.VALUE_RANGE;
}
return false;
}

/**
* Constructs an array of {@link CTX} (context) objects from a list of {@link AbstractPart} objects.
* This is used to build the context for nested CDT operations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1013,30 +1013,39 @@ public AbstractPart visitVariable(ConditionParser.VariableContext ctx) {
@Override
public AbstractPart visitPath(ConditionParser.PathContext ctx) {
BasePath basePath = (BasePath) visit(ctx.basePath());
List<AbstractPart> cdtParts = basePath.getCdtParts();
overrideWithPathFunction(basePath.getBinPart(), ctx);

// if there are other parts except bin, get a corresponding Exp
if (!cdtParts.isEmpty() || ctx.pathFunction() != null && ctx.pathFunction().pathFunctionCount() != null) {
return new Path(basePath, ctx.pathFunction() == null
? null
: (PathFunction) visit(ctx.pathFunction()));
PathFunction pathFunction = visitPathFunctionIfPresent(ctx);

if (!basePath.getCdtParts().isEmpty()
|| pathFunction != null && pathFunction.getPathFunctionType() == PathFunction.PathFunctionType.COUNT) {
return new Path(basePath, pathFunction);
}

if (pathFunction != null && pathFunction.getPathFunctionType() == PathFunction.PathFunctionType.CAST) {
return buildBinCast(basePath.getBinPart(), pathFunction);
}

overrideBinWithPathFunction(basePath.getBinPart(), pathFunction);
return basePath.getBinPart();
}

private void overrideWithPathFunction(BinPart binPart, ConditionParser.PathContext ctx) {
ConditionParser.PathFunctionContext pathFunctionContext = ctx.pathFunction();
private PathFunction visitPathFunctionIfPresent(ConditionParser.PathContext ctx) {
return ctx.pathFunction() != null ? (PathFunction) visit(ctx.pathFunction()) : null;
}

// Override with path function (explicit get or cast)
if (pathFunctionContext != null) {
PathFunction pathFunction = (PathFunction) visit(pathFunctionContext);
if (pathFunction != null) {
Exp.Type type = pathFunction.getBinType();
if (type != null) {
binPart.updateExp(type);
binPart.setTypeExplicitlySet(true);
}
private AbstractPart buildBinCast(BinPart binPart, PathFunction pathFunction) {
String castText = pathFunction.getBinType().toString();
PathFunction.CastType castType = PathFunction.CastType.valueOf(castText);
binPart.updateExp(PathFunction.castSourceExpType(castType));
binPart.setTypeExplicitlySet(true);
return new ExpressionContainer(binPart, PathFunction.castTypeToOperation(castType));
}

private void overrideBinWithPathFunction(BinPart binPart, PathFunction pathFunction) {
if (pathFunction != null) {
Exp.Type type = pathFunction.getBinType();
if (type != null) {
binPart.updateExp(type);
binPart.setTypeExplicitlySet(true);
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,14 @@ private static Exp getExpBinComparison(BinPart binPart, AbstractPart anotherPart
binExp = Exp.bin(binPart.getBinName(), binType);
yield anotherPart.getExp();
}
case EXPRESSION_CONTAINER, PATH_OPERAND, VARIABLE_OPERAND ->
// Can't validate with expression container
anotherPart.getExp();
case EXPRESSION_CONTAINER -> {
Exp.Type resolvedType = resolveExpType(anotherPart);
if (resolvedType != null) {
validateComparableTypes(binPart.getExpType(), resolvedType);
}
yield anotherPart.getExp();
}
case PATH_OPERAND, VARIABLE_OPERAND -> anotherPart.getExp();
case BIN_PART -> {
// Both are bin parts
validateComparableTypes(binPart.getExpType(), anotherPart.getExpType());
Expand Down
Loading
Loading