package org.nlamah.QL.TypeChecker;
import org.nlamah.QL.Error.ExpressionTypeMismatchError;
import org.nlamah.QBase.Constants.QBaseQuestionType;
import org.nlamah.QBase.Tools.ArrayTools;
import org.nlamah.QBase.TypeChecker.QBaseAbstractTypeChecker;
import org.nlamah.QL.Interfaces.QLNodeVisitor;
import org.nlamah.QL.Model.Expression.Abstract.BinaryExpression;
import org.nlamah.QL.Model.Expression.Abstract.BinaryLogicalExpression;
import org.nlamah.QL.Model.Expression.Abstract.Expression;
import org.nlamah.QL.Model.Expression.Abstract.UnaryExpression;
import org.nlamah.QL.Model.Expression.Binary.AddExpression;
import org.nlamah.QL.Model.Expression.Binary.AndExpression;
import org.nlamah.QL.Model.Expression.Binary.DivideExpression;
import org.nlamah.QL.Model.Expression.Binary.EqualExpression;
import org.nlamah.QL.Model.Expression.Binary.GreaterThanEqualExpression;
import org.nlamah.QL.Model.Expression.Binary.GreaterThanExpression;
import org.nlamah.QL.Model.Expression.Binary.MultiplyExpression;
import org.nlamah.QL.Model.Expression.Binary.OrExpression;
import org.nlamah.QL.Model.Expression.Binary.SmallerThanEqualExpression;
import org.nlamah.QL.Model.Expression.Binary.SmallerThanExpression;
import org.nlamah.QL.Model.Expression.Binary.SubtractExpression;
import org.nlamah.QL.Model.Expression.Binary.UnEqualExpression;
import org.nlamah.QL.Model.Expression.Literal.BooleanLiteral;
import org.nlamah.QL.Model.Expression.Literal.IdentifierLiteral;
import org.nlamah.QL.Model.Expression.Literal.NumberLiteral;
import org.nlamah.QL.Model.Expression.Literal.TextLiteral;
import org.nlamah.QL.Model.Expression.Unary.MinusExpression;
import org.nlamah.QL.Model.Expression.Unary.NotExpression;
import org.nlamah.QL.Model.Expression.Unary.PlusExpression;
import org.nlamah.QL.Model.Form.ComputedQuestion;
import org.nlamah.QL.Model.Form.ConditionalBlock;
import org.nlamah.QL.Model.Form.ElseIfThenBlock;
import org.nlamah.QL.Model.Form.ElseThenBlock;
import org.nlamah.QL.Model.Form.Form;
import org.nlamah.QL.Model.Form.IfThenBlock;
import org.nlamah.QL.Model.Form.InputQuestion;
import org.nlamah.QL.Model.Form.Abstract.FormElement;
import org.nlamah.QL.Model.Form.Abstract.QLNode;
public class ExpressionTypeChecker extends QBaseAbstractTypeChecker implements QLNodeVisitor
{
public ExpressionTypeChecker(Form form)
{
form.accept(this);
}
private void checkForErrorInEqualityExpression(BinaryLogicalExpression expression)
{
Expression leftHandExpression = (Expression) expression.leftHandExpression().accept(this);
Expression rightHandExpression = (Expression) expression.rightHandExpression().accept(this);
if ( ! (leftHandExpression.type().equals(rightHandExpression.type())))
{
errors.add(new ExpressionTypeMismatchError(expression));
}
}
private void checkForErrorInBinaryExpression(BinaryExpression expression, QBaseQuestionType type)
{
Expression leftHandExpression = (Expression) expression.leftHandExpression().accept(this);
Expression rightHandExpression = (Expression) expression.rightHandExpression().accept(this);
checkIfResultExpressionIsOfType(leftHandExpression, type);
checkIfResultExpressionIsOfType(rightHandExpression, type);
}
private void checkForErrorInUnaryExpression(UnaryExpression expression, QBaseQuestionType type)
{
Expression visitedExpression = (Expression) expression.expression().accept(this);
checkIfResultExpressionIsOfType(visitedExpression, type);
}
private void checkIfResultExpressionIsOfType(Expression expression, QBaseQuestionType type)
{
switch (expression.type())
{
case BOOLEAN:
{
if (type != QBaseQuestionType.BOOLEAN)
{
errors.add(new ExpressionTypeMismatchError(expression));
}
break;
}
case NUMBER:
{
if (type != QBaseQuestionType.NUMBER)
{
errors.add(new ExpressionTypeMismatchError(expression));
}
break;
}
case TEXT:
{
if (type != QBaseQuestionType.TEXT)
{
errors.add(new ExpressionTypeMismatchError(expression));
}
break;
}
default:
{
}
}
}
@Override
public QLNode visit(AddExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(AndExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.BOOLEAN);
return expression;
}
@Override
public QLNode visit(DivideExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(EqualExpression expression)
{
checkForErrorInEqualityExpression(expression);
return expression;
}
@Override
public QLNode visit(GreaterThanExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(GreaterThanEqualExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(MultiplyExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(OrExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.BOOLEAN);
return expression;
}
public QLNode visit(SmallerThanEqualExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(SmallerThanExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(SubtractExpression expression)
{
checkForErrorInBinaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(UnEqualExpression expression)
{
checkForErrorInEqualityExpression(expression);
return expression;
}
@Override
public QLNode visit(MinusExpression expression)
{
checkForErrorInUnaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(NotExpression expression)
{
checkForErrorInUnaryExpression(expression, QBaseQuestionType.BOOLEAN);
return expression;
}
@Override
public QLNode visit(PlusExpression expression)
{
checkForErrorInUnaryExpression(expression, QBaseQuestionType.NUMBER);
return expression;
}
@Override
public QLNode visit(BooleanLiteral booleanLiteral)
{
return booleanLiteral;
}
@Override
public QLNode visit(IdentifierLiteral identifierLiteral)
{
return identifierLiteral;
}
@Override
public QLNode visit(NumberLiteral numberLiteral)
{
return numberLiteral;
}
@Override
public QLNode visit(TextLiteral textLiteral)
{
return textLiteral;
}
@Override
public QLNode visit(ConditionalBlock conditionalBlock)
{
if (conditionalBlock.ifThenBlock() != null)
{
conditionalBlock.ifThenBlock().accept(this);
}
if (ArrayTools.arrayExistsAndHasElements(conditionalBlock.elseIfThenBlocks()))
{
for (ElseIfThenBlock elseIfThenBlock : conditionalBlock.elseIfThenBlocks())
{
elseIfThenBlock.accept(this);
}
}
if (conditionalBlock.elseThenBlock() != null)
{
conditionalBlock.elseThenBlock().accept(this);
}
return null;
}
@Override
public QLNode visit(ElseIfThenBlock elseIfThenBlock)
{
Expression expression = (Expression) elseIfThenBlock.expression().accept(this);
if (expression.type() != QBaseQuestionType.BOOLEAN)
{
errors.add(new ExpressionTypeMismatchError(expression));
}
if (ArrayTools.arrayExistsAndHasElements(elseIfThenBlock.childElements()))
{
for (FormElement childElement : elseIfThenBlock.childElements())
{
childElement.accept(this);
}
}
return null;
}
@Override
public QLNode visit(ElseThenBlock elseThenBlock)
{
for (FormElement childElement : elseThenBlock.childElements())
{
childElement.accept(this);
}
return null;
}
@Override
public QLNode visit(Form form)
{
for (FormElement childElement : form.childElements())
{
childElement.accept(this);
}
return null;
}
@Override
public QLNode visit(IfThenBlock ifThenBlock)
{
Expression expression = (Expression) ifThenBlock.expression().accept(this);
if (expression.type() != QBaseQuestionType.BOOLEAN)
{
errors.add(new ExpressionTypeMismatchError(expression));
}
if (ArrayTools.arrayExistsAndHasElements(ifThenBlock.childElements()))
{
for (FormElement childElement : ifThenBlock.childElements())
{
childElement.accept(this);
}
}
return null;
}
@Override
public QLNode visit(InputQuestion inputQuestion)
{
return null;
}
@Override
public QLNode visit(ComputedQuestion computedQuestion)
{
Expression expression = (Expression) computedQuestion.expression().accept(this);
if (expression.type() != computedQuestion.returnType())
{
errors.add(new ExpressionTypeMismatchError(expression));
}
return null;
}
}