prompto.expression.ContainsExpression Maven / Gradle / Ivy
The newest version!
package prompto.expression;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import prompto.compiler.ClassConstant;
import prompto.compiler.CompilerUtils;
import prompto.compiler.Descriptor;
import prompto.compiler.Flags;
import prompto.compiler.IInstructionListener;
import prompto.compiler.IVerifierEntry.VerifierType;
import prompto.compiler.InterfaceConstant;
import prompto.compiler.MethodConstant;
import prompto.compiler.MethodInfo;
import prompto.compiler.NamedType;
import prompto.compiler.OffsetListenerConstant;
import prompto.compiler.Opcode;
import prompto.compiler.ResultInfo;
import prompto.compiler.StackLocal;
import prompto.compiler.StackState;
import prompto.compiler.StringConstant;
import prompto.declaration.AttributeDeclaration;
import prompto.declaration.TestMethodDeclaration;
import prompto.error.PromptoError;
import prompto.error.SyntaxError;
import prompto.grammar.ContOp;
import prompto.grammar.Identifier;
import prompto.intrinsic.PromptoString;
import prompto.parser.CodeSection;
import prompto.parser.Dialect;
import prompto.runtime.Context;
import prompto.runtime.Variable;
import prompto.store.AttributeInfo;
import prompto.store.Family;
import prompto.store.FamilyInfo;
import prompto.store.IQueryBuilder;
import prompto.store.IQueryBuilder.MatchOp;
import prompto.store.IStore;
import prompto.transpiler.Transpiler;
import prompto.type.BooleanType;
import prompto.type.ContainerType;
import prompto.type.IType;
import prompto.type.IterableType;
import prompto.type.VoidType;
import prompto.utils.CodeWriter;
import prompto.utils.StoreUtils;
import prompto.value.BooleanValue;
import prompto.value.IContainer;
import prompto.value.IInstance;
import prompto.value.IIterable;
import prompto.value.IValue;
import prompto.value.NullValue;
public class ContainsExpression extends CodeSection implements IPredicate, IAssertion {
IExpression left;
ContOp operator;
IExpression right;
public ContainsExpression(IExpression left, ContOp operator, IExpression right) {
this.left = left;
this.operator = operator;
this.right = right;
}
@Override
public String toString() {
return left.toString() + " " + operator.toString() + " " + right.toString();
}
@Override
public void toDialect(CodeWriter writer) {
left.toDialect(writer);
writer.append(" ");
writer.append(operator.toString());
writer.append(" ");
if(right instanceof PredicateExpression)
((PredicateExpression)right).containsToDialect(writer);
else
right.toDialect(writer);
}
@Override
public IType check(Context context) {
if(right instanceof PredicateExpression)
return checkPredicate(context);
else
return checkValue(context);
}
private IType checkPredicate(Context context) {
IType lt = left.check(context);
if(lt instanceof IterableType) {
IType itemType = ((IterableType)lt).getItemType();
ArrowExpression arrow = ((PredicateExpression)right).toArrowExpression();
return arrow.checkFilter(context, itemType);
} else {
context.getProblemListener().reportExpectingCollection(this, lt);
return VoidType.instance();
}
}
IType checkValue(Context context) {
IType lt = left.check(context);
IType rt = right.check(context);
checkOperator(context, lt, rt);
return BooleanType.instance();
}
private void checkOperator(Context context, IType lt, IType rt) {
switch(operator) {
case IN:
case NOT_IN:
rt.checkContains(context,lt, this);
break;
case HAS:
case NOT_HAS:
lt.checkContains(context, rt, this);
break;
default:
lt.checkContainsAllOrAny(context, rt);
}
}
@Override
public IValue interpret(Context context) throws PromptoError {
if(right instanceof PredicateExpression)
return interpretPredicate(context);
else
return interpretValue(context);
}
@SuppressWarnings("unchecked")
IValue interpretPredicate(Context context) throws PromptoError {
IValue lval = left.interpret(context);
if(lval instanceof IContainer) {
IType itemType = ((ContainerType)lval.getType()).getItemType();
ArrowExpression arrow = ((PredicateExpression)right).toArrowExpression();
Predicate predicate = arrow.getFilter(context, itemType);
return interpretPredicate(context, (IContainer)lval, predicate);
} else {
context.getProblemListener().reportExpectingCollection(this, lval.getType());
return NullValue.instance();
}
}
private IValue interpretPredicate(Context context, IContainer lval, Predicate predicate) {
java.lang.Boolean result = null;
switch (operator)
{
case HAS_ALL:
case NOT_HAS_ALL:
result = allMatch(context, lval, predicate);
break;
case HAS_ANY:
case NOT_HAS_ANY:
result = anyMatch(context, lval, predicate);
break;
default:
// will throw
}
if (result != null)
{
if (operator.name().startsWith("NOT_"))
result = !result;
return BooleanValue.valueOf(result);
}
String lowerName = operator.name().toLowerCase().replace('_', ' ');
throw new SyntaxError("Illegal filter: " + lval.getClass().getSimpleName() + " " + lowerName);
}
public boolean allMatch(Context context, IContainer> container, Predicate predicate) throws PromptoError
{
return StreamSupport.stream(container.getIterable(context).spliterator(), false)
.allMatch(predicate);
}
public boolean anyMatch(Context context, IContainer> container, Predicate predicate) throws PromptoError
{
return StreamSupport.stream(container.getIterable(context).spliterator(), false)
.anyMatch(predicate);
}
IValue interpretValue(Context context) throws PromptoError {
IValue lval = left.interpret(context);
IValue rval = right.interpret(context);
return interpretValue(context, lval, rval);
}
private IValue interpretValue(Context context, IValue lval, IValue rval) throws PromptoError {
java.lang.Boolean result = null;
switch (operator)
{
case IN:
case NOT_IN:
if(rval instanceof IContainer)
result = ((IContainer>)rval).hasItem(context, lval);
else if(rval instanceof IIterable)
result = containsOne(context, (IIterable>)rval, lval);
break;
case HAS:
case NOT_HAS:
if(lval instanceof IContainer)
result = ((IContainer>)lval).hasItem(context, rval);
else if(lval instanceof IIterable)
result = containsOne(context, (IIterable>)lval, rval);
break;
case HAS_ALL:
case NOT_HAS_ALL:
if (lval instanceof IContainer && rval instanceof IContainer)
result = containsAll(context, (IContainer>)lval, (IContainer>)rval);
break;
case HAS_ANY:
case NOT_HAS_ANY:
if (lval instanceof IContainer && rval instanceof IContainer)
result = containsAny(context, (IContainer>)lval, (IContainer>)rval);
break;
}
if (result != null)
{
if (operator.name().startsWith("NOT_"))
result = !result;
return BooleanValue.valueOf(result);
}
if (operator.name().endsWith("IN"))
{
IValue tmp = lval;
lval = rval;
rval = tmp;
}
String lowerName = operator.name().toLowerCase().replace('_', ' ');
throw new SyntaxError("Illegal comparison: " + lval.getClass().getSimpleName() +
" " + lowerName + " " + rval.getClass().getSimpleName());
}
private java.lang.Boolean containsOne(Context context, IIterable> container, IValue item) {
return StreamSupport.stream(container.getIterable(context).spliterator(), false)
.anyMatch(o -> o.equals(item));
}
public boolean containsAll(Context context, IContainer> container, IContainer> items) throws PromptoError
{
return StreamSupport.stream(items.getIterable(context).spliterator(), false)
.allMatch(item -> container.hasItem(context, item));
}
public boolean containsAny(Context context, IContainer> container, IContainer> items) throws PromptoError
{
return StreamSupport.stream(items.getIterable(context).spliterator(), false)
.anyMatch(item -> container.hasItem(context, item));
}
@Override
public ResultInfo compile(Context context, MethodInfo method, Flags flags) {
ResultInfo result = compileOperator(context, method, flags.withPrimitive(true));
if (operator.name().startsWith("NOT_"))
return compileNot(context, method, flags, result);
else if((!flags.toPrimitive() && boolean.class==result.getType()))
return CompilerUtils.booleanToBoolean(method);
else
return result;
}
private ResultInfo compileNot(Context context, MethodInfo method, Flags flags, ResultInfo info) {
if(BooleanValue.class==info.getType())
CompilerUtils.BooleanToboolean(method);
CompilerUtils.reverseBoolean(method);
if(flags.toPrimitive())
return new ResultInfo(boolean.class);
else
return CompilerUtils.booleanToBoolean(method);
}
private ResultInfo compileOperator(Context context, MethodInfo method, Flags flags) {
switch (operator) {
case IN:
case NOT_IN:
return compileContains(context, method, flags, right, left);
case HAS:
case NOT_HAS:
return compileContains(context, method, flags, left, right);
case HAS_ALL:
case NOT_HAS_ALL:
return compileContainsAll(context, method, flags, left, right);
case HAS_ANY:
case NOT_HAS_ANY:
return compileContainsAny(context, method, flags, left, right);
default:
throw new UnsupportedOperationException("Uknown operator: " + operator.name());
}
}
private ResultInfo compileContains(Context context, MethodInfo method, Flags flags, IExpression left, IExpression right) {
ResultInfo linfo = left.compile(context, method, flags);
right.compile(context, method, flags.withPrimitive(false));
if(String.class==linfo.getType()) {
MethodConstant m = new MethodConstant(PromptoString.class, "contains", String.class, Object.class, boolean.class);
method.addInstruction(Opcode.INVOKESTATIC, m);
if(flags.toPrimitive())
return new ResultInfo(boolean.class);
else
return CompilerUtils.booleanToBoolean(method);
} else {
MethodConstant m = new MethodConstant(linfo.getType(), "contains", Object.class, boolean.class);
method.addInstruction(Opcode.INVOKEVIRTUAL, m);
if(flags.toPrimitive())
return new ResultInfo(boolean.class);
else
return CompilerUtils.booleanToBoolean(method);
}
}
private ResultInfo compileContainsAll(Context context, MethodInfo method, Flags flags, IExpression left, IExpression right) {
ResultInfo linfo = left.compile(context, method, flags);
if(String.class==linfo.getType())
return compileStringContainsX(context, method, flags, right, "containsAll");
else if(right instanceof PredicateExpression)
return compilePredicateMatchX(context, method, flags, linfo, right, "allMatch");
else
return compileCollectionContainsX(context, method, flags, linfo, right, "containsAll");
}
private ResultInfo compileContainsAny(Context context, MethodInfo method, Flags flags, IExpression left, IExpression right) {
ResultInfo linfo = left.compile(context, method, flags);
if(String.class==linfo.getType())
return compileStringContainsX(context, method, flags, right, "containsAny");
else if(right instanceof PredicateExpression)
return compilePredicateMatchX(context, method, flags, linfo, right, "anyMatch");
else
return compileCollectionContainsX(context, method, flags, linfo, right, "containsAny");
}
private ResultInfo compileStringContainsX(Context context, MethodInfo method, Flags flags, IExpression right, String methodName) {
right.compile(context, method, flags.withPrimitive(false));
MethodConstant m = new MethodConstant(PromptoString.class, methodName, String.class, Object.class, boolean.class);
method.addInstruction(Opcode.INVOKESTATIC, m);
if(flags.toPrimitive())
return new ResultInfo(boolean.class);
else
return CompilerUtils.booleanToBoolean(method);
}
private ResultInfo compilePredicateMatchX(Context context, MethodInfo method, Flags flags, ResultInfo left, IExpression right, String methodName) {
// get stream
MethodConstant m = new MethodConstant(left.getType(), "stream", Stream.class);
method.addInstruction(Opcode.INVOKEVIRTUAL, m);
// create inner class for filter
String innerClassName = CompilerUtils.compileInnerFilterClass(context, method.getClassFile(), this.left, (PredicateExpression)right);
// instantiate filter
ClassConstant innerClass = new ClassConstant(new NamedType(innerClassName));
method.addInstruction(Opcode.NEW, innerClass);
method.addInstruction(Opcode.DUP);
// call filter constructor
Descriptor.Method proto = new Descriptor.Method(void.class);
m = new MethodConstant(innerClass, "", proto);
method.addInstruction(Opcode.INVOKESPECIAL, m);
// invoke filter on stream
Descriptor.Method desc = new Descriptor.Method(Predicate.class, boolean.class);
InterfaceConstant intf = new InterfaceConstant(new ClassConstant(Stream.class), methodName, desc);
method.addInstruction(Opcode.INVOKEINTERFACE, intf);
return new ResultInfo(boolean.class);
}
private ResultInfo compileCollectionContainsX(Context context, MethodInfo method, Flags flags, ResultInfo left, IExpression right, String methodName) {
right.compile(context, method, flags.withPrimitive(false));
MethodConstant m = new MethodConstant(left.getType(), methodName, Collection.class, boolean.class);
method.addInstruction(Opcode.INVOKEVIRTUAL, m);
if(flags.toPrimitive())
return new ResultInfo(boolean.class);
else
return CompilerUtils.booleanToBoolean(method);
}
@Override
public boolean interpretAssert(Context context, TestMethodDeclaration test) throws PromptoError {
IValue lval = left.interpret(context);
IValue rval = right.interpret(context);
IValue result = interpretValue(context, lval, rval);
if(result==BooleanValue.TRUE)
return true;
String expected = buildExpectedMessage(context, test);
String actual = lval.toString() + " " + operator.toString() + " " + rval.toString();
test.printFailedAssertion(context, expected, actual);
return false;
}
private String buildExpectedMessage(Context context, TestMethodDeclaration test) {
CodeWriter writer = new CodeWriter(test.getDialect(), context);
this.toDialect(writer);
return writer.toString();
}
@Override
public void compileAssert(Context context, MethodInfo method, Flags flags, TestMethodDeclaration test) {
context = context.newChildContext();
StackState finalState = method.captureStackState();
// compile left and store in local
IType leftType = this.left.check(context);
ResultInfo leftInfo = this.left.compile(context, method, flags.withPrimitive(false));
String leftName = method.nextTransientName("left");
StackLocal left = method.registerLocal(leftName, VerifierType.ITEM_Object, new ClassConstant(leftInfo.getType()));
CompilerUtils.compileASTORE(method, left);
// compile right and store in local
IType rightType = this.right.check(context);
ResultInfo rightInfo = this.right.compile(context, method, flags.withPrimitive(true));
String rightName = method.nextTransientName("right");
StackLocal right = method.registerLocal(rightName, VerifierType.ITEM_Object, new ClassConstant(rightInfo.getType()));
CompilerUtils.compileASTORE(method, right);
// call regular compile
IExpression newLeft = new InstanceExpression(new Identifier(leftName));
context.registerInstance(new Variable(new Identifier(leftName), leftType));
IExpression newRight = new InstanceExpression(new Identifier(rightName));
context.registerInstance(new Variable(new Identifier(rightName), rightType));
ContainsExpression newExp = new ContainsExpression(newLeft, this.operator, newRight);
ResultInfo info = newExp.compile(context, method, flags.withPrimitive(true));
if(BooleanValue.class==info.getType())
CompilerUtils.BooleanToboolean(method);
// 1 = success
IInstructionListener finalListener = method.addOffsetListener(new OffsetListenerConstant());
method.activateOffsetListener(finalListener);
method.addInstruction(Opcode.IFNE, finalListener);
// increment failure counter
method.addInstruction(Opcode.ICONST_1);
method.addInstruction(Opcode.IADD);
// build failure message
String message = buildExpectedMessage(context, test);
message = test.buildFailedAssertionMessagePrefix(message);
method.addInstruction(Opcode.LDC, new StringConstant(message));
CompilerUtils.compileALOAD(method, left);
MethodConstant toString = new MethodConstant(leftInfo.getType(), "toString", String.class);
method.addInstruction(Opcode.INVOKEVIRTUAL, toString);
MethodConstant concat = new MethodConstant(String.class, "concat", String.class, String.class);
method.addInstruction(Opcode.INVOKEVIRTUAL, concat);
method.addInstruction(Opcode.LDC, new StringConstant(" " + operator.toString() + " "));
method.addInstruction(Opcode.INVOKEVIRTUAL, concat);
CompilerUtils.compileALOAD(method, right);
toString = new MethodConstant(rightInfo.getType(), "toString", String.class);
method.addInstruction(Opcode.INVOKEVIRTUAL, toString);
method.addInstruction(Opcode.INVOKEVIRTUAL, concat);
test.compileFailure(context, method, flags);
// success/final
method.unregisterLocal(right);
method.unregisterLocal(left);
method.restoreFullStackState(finalState);
method.placeLabel(finalState);
method.inhibitOffsetListener(finalListener);
}
@Override
public void checkQuery(Context context) throws PromptoError {
AttributeDeclaration decl = left.checkAttribute(context, this);
if(decl==null)
return;
if(!decl.isStorable(context)) {
context.getProblemListener().reportNotStorable(this, decl.getName());
return;
}
IType rt = right.check(context);
checkOperator(context, decl.getType(), rt);
}
@Override
public void interpretQuery(Context context, IQueryBuilder query, IStore store) throws PromptoError {
AttributeInfo info = left.checkAttributeInfo(context, this, store);
if(info==null)
throw new SyntaxError("Unable to interpret predicate: " + this.toString());
IValue value = right.interpret(context);
AttributeInfo fieldInfo = StoreUtils.getAttributeInfo(context, info.getName(), store);
FamilyInfo valueInfo = value.getType().getFamilyInfo(context);
MatchOp matchOp = getMatchOp(context, fieldInfo, valueInfo, this.operator, false);
if(value instanceof IInstance)
value = ((IInstance)value).getMember(context, new Identifier(IStore.dbIdName), false);
Object data = value==null ? null : value.getStorableData();
query.
© 2015 - 2024 Weber Informatics LLC | Privacy Policy