package org.fugazi.ql.type_checker.visitor; import org.fugazi.ql.ast.expression.Expression; import org.fugazi.ql.ast.expression.comparison.*; import org.fugazi.ql.ast.expression.literal.BOOL; import org.fugazi.ql.ast.expression.literal.INT; import org.fugazi.ql.ast.expression.literal.STRING; import org.fugazi.ql.ast.expression.logical.And; import org.fugazi.ql.ast.expression.logical.Logical; import org.fugazi.ql.ast.expression.logical.Or; import org.fugazi.ql.ast.expression.numerical.*; import org.fugazi.ql.ast.expression.unary.Negative; import org.fugazi.ql.ast.expression.unary.Not; import org.fugazi.ql.ast.expression.unary.Positive; import org.fugazi.ql.ast.expression.unary.Unary; import org.fugazi.ql.ast.form.form_data.QLFormDataStorage; import org.fugazi.ql.ast.form.form_data.visitor.FullQLFormVisitor; import org.fugazi.ql.ast.type.Type; import org.fugazi.ql.type_checker.issue.error.TypeMismatchError; import java.util.List; public class TypeMismatchVisitor extends FullQLFormVisitor { public TypeMismatchVisitor(QLFormDataStorage _formData) { super(_formData); } /** * ======================= * Binary visitors * ======================= */ /* This checks if both sides of the binary logical expression are of required type bool. */ private Void visitBinaryLogical(Logical logical) { Expression left = logical.getLeft(); Expression right = logical.getRight(); boolean leftCorrect = left.isExpressionOfTypeBool(this.formData); boolean rightCorrect = right.isExpressionOfTypeBool(this.formData); if (!leftCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), logical, "Left side of the binary logical expression not of type bool." ); } if (!rightCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), logical, "Right side of the binary logical expression not of type bool." ); } left.accept(this); right.accept(this); return null; } /* This checks if unary expression is of required type bool. */ private Void visitUnaryLogical(Unary unary) { Expression expr = unary.getExpr(); boolean exprCorrect = unary.isExpressionOfTypeBool(this.formData); if (!exprCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), unary, "Unary logical expression not of type bool." ); } expr.accept(this); return null; } /* This checks if both sides of the logical comparison are of required type. */ private Void visitBinaryComparison(Comparison comparison, List<Type> expectedTypes) { Expression left = comparison.getLeft(); Expression right = comparison.getRight(); // both sides need to be of same supported type // in order for the expression to be correct boolean differentTypes = false; boolean unsupportedTypes = false; if (!expectedTypes.contains(left.getReturnedType(this.formData)) || !expectedTypes.contains(right.getReturnedType(this.formData))) { unsupportedTypes = true; } if (!left.getReturnedType(this.formData).equals(right.getReturnedType(this.formData))) { differentTypes = true; } if (unsupportedTypes) { this.astIssueHandler.registerNewError( new TypeMismatchError(), comparison, "Side(s) of the binary comparison not of supported type(s): " + expectedTypes.toString() + ". " + "Instead received types: " + left.getReturnedType(this.formData) + " and " + right.getReturnedType(this.formData) + "." ); } else if (differentTypes) { this.astIssueHandler.registerNewError( new TypeMismatchError(), comparison, "Two sides of the binary comparison expression of different types: " + left.getReturnedType(this.formData) + " and " + right.getReturnedType(this.formData) + "." ); } left.accept(this); right.accept(this); return null; } @Override public Void visitAnd(And and) { return this.visitBinaryLogical(and); } @Override public Void visitOr(Or or) { return this.visitBinaryLogical(or); } @Override public Void visitNot(Not not) { return this.visitUnaryLogical(not); } @Override public Void visitEQ(EQ eq) { return this.visitBinaryComparison(eq, eq.getSupportedTypes()); } @Override public Void visitGE(GE ge) { return this.visitBinaryComparison(ge, ge.getSupportedTypes()); } @Override public Void visitGreater(Greater greater) { return this.visitBinaryComparison(greater, greater.getSupportedTypes()); } @Override public Void visitLE(LE le) { return this.visitBinaryComparison(le, le.getSupportedTypes()); } @Override public Void visitLesser(Less less) { return this.visitBinaryComparison(less, less.getSupportedTypes()); } @Override public Void visitNotEq(NotEq notEq) { return this.visitBinaryComparison(notEq, notEq.getSupportedTypes()); } /** * ======================= * Numerical visitors * ======================= */ /* This checks if both sides of the binary numerical comparison are of required type int. */ private Void visitBinaryNumerical(Numerical numerical) { Expression left = numerical.getLeft(); Expression right = numerical.getRight(); boolean leftCorrect = left.isExpressionOfTypeInt(this.formData); boolean rightCorrect = right.isExpressionOfTypeInt(this.formData); if (!leftCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), numerical, "Left side of the binary expression not of type int." ); } if (!rightCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), numerical, "Right side of the binary expression not of type int." ); } left.accept(this); right.accept(this); return null; } /* This checks if the unary numerical expression is of required type int. */ private Void visitUnaryNumerical(Unary unary) { // Both sides of the expressions need to be of type boolean. Expression expr = unary.getExpr(); boolean exprCorrect = unary.isExpressionOfTypeInt(this.formData); if (!exprCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), unary, "Unary numerical expression not of type int." ); } expr.accept(this); return null; } @Override public Void visitNegative(Negative negative) { return this.visitUnaryNumerical(negative); } @Override public Void visitPositive(Positive positive) { return this.visitUnaryNumerical(positive); } @Override public Void visitAdd(Add add) { return this.visitBinaryNumerical(add); } @Override public Void visitSub(Sub sub) { return this.visitBinaryNumerical(sub); } @Override public Void visitMul(Mul mul) { return this.visitBinaryNumerical(mul); } @Override public Void visitDiv(Div div) { return this.visitBinaryNumerical(div); } /** * ======================= * Literal visitors * ======================= */ @Override public Void visitINT(INT intLiteral) { boolean exprCorrect = intLiteral.isExpressionOfTypeInt(this.formData); if (!exprCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), intLiteral, "Int Literal not of type int." ); } return null; } @Override public Void visitSTRING(STRING stringLiteral) { boolean exprCorrect = stringLiteral.isExpressionOfTypeString(this.formData); if (!exprCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), stringLiteral, "String Literal not of type string." ); } return null; } @Override public Void visitBOOL(BOOL boolLiteral) { boolean exprCorrect = boolLiteral.isExpressionOfTypeBool(this.formData); if (!exprCorrect) { this.astIssueHandler.registerNewError( new TypeMismatchError(), boolLiteral, "Bool Literal not of type bool." ); } return null; } }