package nl.uva.se.ql.typechecking;
import nl.uva.se.ql.ast.expression.Binary;
import nl.uva.se.ql.ast.expression.Expression;
import nl.uva.se.ql.ast.expression.ExpressionVisitor;
import nl.uva.se.ql.ast.expression.Unary;
import nl.uva.se.ql.ast.expression.arithmetical.Addition;
import nl.uva.se.ql.ast.expression.arithmetical.Divide;
import nl.uva.se.ql.ast.expression.arithmetical.Modulo;
import nl.uva.se.ql.ast.expression.arithmetical.Multiply;
import nl.uva.se.ql.ast.expression.arithmetical.Negative;
import nl.uva.se.ql.ast.expression.arithmetical.Positive;
import nl.uva.se.ql.ast.expression.arithmetical.Power;
import nl.uva.se.ql.ast.expression.arithmetical.Substraction;
import nl.uva.se.ql.ast.expression.literal.BooleanLiteral;
import nl.uva.se.ql.ast.expression.literal.DecimalLiteral;
import nl.uva.se.ql.ast.expression.literal.IntegerLiteral;
import nl.uva.se.ql.ast.expression.literal.StringLiteral;
import nl.uva.se.ql.ast.expression.logical.And;
import nl.uva.se.ql.ast.expression.logical.Equal;
import nl.uva.se.ql.ast.expression.logical.GreaterOrEqual;
import nl.uva.se.ql.ast.expression.logical.GreaterThen;
import nl.uva.se.ql.ast.expression.logical.LessOrEqual;
import nl.uva.se.ql.ast.expression.logical.LessThen;
import nl.uva.se.ql.ast.expression.logical.Not;
import nl.uva.se.ql.ast.expression.logical.NotEqual;
import nl.uva.se.ql.ast.expression.logical.Or;
import nl.uva.se.ql.ast.expression.variable.Reference;
import nl.uva.se.ql.ast.form.Form;
import nl.uva.se.ql.ast.form.FormVisitor;
import nl.uva.se.ql.ast.statement.CalculatedQuestion;
import nl.uva.se.ql.ast.statement.Condition;
import nl.uva.se.ql.ast.statement.Question;
import nl.uva.se.ql.ast.statement.StatementVisitor;
import nl.uva.se.ql.ast.type.BooleanType;
import nl.uva.se.ql.ast.type.DecimalType;
import nl.uva.se.ql.ast.type.IntegerType;
import nl.uva.se.ql.ast.type.StringType;
import nl.uva.se.ql.ast.type.Type;
import nl.uva.se.ql.ast.type.TypeVisitor;
import nl.uva.se.ql.ast.type.UndefinedType;
import nl.uva.se.ql.typechecking.error.ErrorList;
import nl.uva.se.ql.typechecking.error.InvalidConditionType;
import nl.uva.se.ql.typechecking.error.InvalidOperandType;
import nl.uva.se.ql.typechecking.error.TypeMismatch;
import nl.uva.se.ql.typechecking.error.TypeNotAllowed;
import nl.uva.se.ql.typechecking.error.UndefinedReference;
public class TypeChecker implements FormVisitor, StatementVisitor,
ExpressionVisitor<Type>, TypeVisitor<Type> {
private static final Type BOOLEAN = new BooleanType();
private static final Type INTEGER = new IntegerType();
private static final Type DECIMAL = new DecimalType();
private static final Type STRING = new StringType();
private static final Type[] NUMERIC_TYPES = {INTEGER,DECIMAL};
private static final Type[] ALPHA_NUMERIC_TYPES = {INTEGER,DECIMAL,STRING};
private static final Type[] ALL_TYPES = {INTEGER,DECIMAL,STRING,BOOLEAN};
private SymbolTable symbols;
private ErrorList errors;
private TypeChecker(SymbolTable symbols) {
this.symbols = symbols;
this.errors = new ErrorList();
}
public static ErrorList check(Form form) {
SymbolResult symbolResult = SymbolResolver.resolve(form);
ErrorList symbolErrorList = symbolResult.getErrorList();
if (symbolErrorList.hasErrors()) {
return symbolErrorList;
}
DependencyTable dependencies = DependencyResolver.resolve(form,
symbolResult.getSymbols());
ErrorList dependencyErrorList = CyclicDependencyChecker.check(dependencies);
if (dependencyErrorList.hasErrors()) {
return dependencyErrorList;
}
TypeChecker typeChecker = new TypeChecker(symbolResult.getSymbols());
typeChecker.visit(form);
return typeChecker.errors;
}
public void visit(Form form) {
form.visitChildren(this);
}
public void visit(Question question) {
}
public void visit(CalculatedQuestion calculatedQuestion) {
Type expressionType = calculatedQuestion.getExpression().accept(this);
Type questionType = calculatedQuestion.getType();
Type promotedExpressionType = expressionType.promote();
Type promotedQuesType = questionType.promote();
if (!promotedExpressionType.equals(promotedQuesType)) {
errors.addError(new TypeMismatch(
calculatedQuestion.getLineNumber(), calculatedQuestion
.getOffset(), questionType, expressionType));
}
}
public void visit(Condition condition) {
Type conditionType = condition.getExpression().accept(this);
if (!conditionType.equals(BOOLEAN)) {
errors.addError(new InvalidConditionType(condition.getLineNumber(),
condition.getOffset(), conditionType));
}
condition.visitChildren(this);
}
public Type visit(Addition plus) {
Type type = visitBinaryExpression(plus);
return defineType(plus, type, ALPHA_NUMERIC_TYPES);
}
public Type visit(Divide divide) {
Type type = visitBinaryExpression(divide);
return defineType(divide, type, NUMERIC_TYPES);
}
public Type visit(Power power) {
Type type = visitBinaryExpression(power);
return defineType(power, type, NUMERIC_TYPES);
}
public Type visit(Multiply multiply) {
Type type = visitBinaryExpression(multiply);
return defineType(multiply, type, NUMERIC_TYPES);
}
public Type visit(Modulo modulo) {
Type type = visitBinaryExpression(modulo);
return defineType(modulo, type, NUMERIC_TYPES);
}
public Type visit(Negative negative) {
Type type = visitUnaryExpression(negative);
return defineType(negative, type, NUMERIC_TYPES);
}
public Type visit(Positive positive) {
Type type = visitUnaryExpression(positive);
return defineType(positive, type, NUMERIC_TYPES);
}
public Type visit(Substraction minus) {
Type type = visitBinaryExpression(minus);
return defineType(minus, type, NUMERIC_TYPES);
}
public Type visit(Not not) {
Type type = visitUnaryExpression(not);
return defineType(not, type, BOOLEAN);
}
public Type visit(NotEqual notEqual) {
Type type = visitBinaryExpression(notEqual);
return defineType(notEqual, type, ALL_TYPES);
}
public Type visit(Or or) {
Type type = visitBinaryExpression(or);
return defineType(or, type, BOOLEAN);
}
public Type visit(LessThen lessThen) {
Type type = visitBinaryExpression(lessThen);
return defineType(lessThen, type, ALPHA_NUMERIC_TYPES);
}
public Type visit(LessOrEqual lessOrEqual) {
Type type = visitBinaryExpression(lessOrEqual);
return defineType(lessOrEqual, type, ALPHA_NUMERIC_TYPES);
}
public Type visit(GreaterThen greaterThen) {
Type type = visitBinaryExpression(greaterThen);
return defineType(greaterThen, type, ALPHA_NUMERIC_TYPES);
}
public Type visit(GreaterOrEqual greaterOrEqual) {
Type type = visitBinaryExpression(greaterOrEqual);
return defineType(greaterOrEqual, type, ALPHA_NUMERIC_TYPES);
}
public Type visit(Equal equal) {
Type type = visitBinaryExpression(equal);
return defineType(equal, type, ALL_TYPES);
}
public Type visit(And and) {
Type type = visitBinaryExpression(and);
return defineType(and, type, BOOLEAN);
}
public Type visit(BooleanLiteral booleanLiteral) {
return new BooleanType();
}
public Type visit(DecimalLiteral decimalLiteral) {
return new DecimalType();
}
public Type visit(IntegerLiteral integerLiteral) {
return new IntegerType();
}
public Type visit(StringLiteral stringLiteral) {
return new StringType();
}
public Type visit(Reference reference) {
if (symbols.containsSymbol(reference.getName())) {
return symbols.getTypeForSymbol(reference.getName());
}
errors.addError(new UndefinedReference(reference.getLineNumber(),
reference.getOffset(), reference.getName()));
return new UndefinedType();
}
@Override
public Type visit(BooleanType booleanType) {
return new BooleanType();
}
@Override
public Type visit(DecimalType decimalType) {
return new DecimalType();
}
@Override
public Type visit(IntegerType integerType) {
return new IntegerType();
}
@Override
public Type visit(StringType stringType) {
return new StringType();
}
@Override
public Type visit(UndefinedType undefinedType) {
return new UndefinedType();
}
private Type visitBinaryExpression(Binary binaryExpression) {
Expression left = binaryExpression.getLeft();
Expression right = binaryExpression.getRight();
Type leftType = left.accept(this);
Type rightType = right.accept(this);
if (leftType.isUndefined()) {
errors.addError(new UndefinedReference(left.getLineNumber(),
left.getOffset(), left.toString()));
return new UndefinedType();
}
if (rightType.isUndefined()) {
errors.addError(new UndefinedReference(right.getLineNumber(),
right.getOffset(), right.toString()));
return new UndefinedType();
}
if (leftType.equals(rightType)) {
return leftType.accept(this);
}
Type leftPromoted = leftType.promote();
Type rightPromoted = rightType.promote();
if (leftPromoted.equals(rightPromoted)) {
return leftPromoted.accept(this);
}
errors.addError(new InvalidOperandType(left.getLineNumber(),
left.getOffset(), leftType, leftType, rightType));
return new UndefinedType();
}
private Type visitUnaryExpression(Unary unaryExpression) {
Expression expression = unaryExpression.getSingleExpression();
Type type = expression.accept(this);
if (type.isUndefined()) {
errors.addError(new UndefinedReference(expression.getLineNumber(),
expression.getOffset(), expression.toString()));
return new UndefinedType();
}
return type;
}
private Type defineType(Expression expr, Type type, Type... types) {
if (type.isIn(types)) {
return type;
}
errors.addError(new TypeNotAllowed(expr.getLineNumber(),
expr.getOffset(), type, expr.getClass().getName()));
return new UndefinedType();
}
}