package ql.ast.visitor.typechecker; import ql.ast.Expression; import ql.ast.QLType; import ql.ast.Statement; import ql.ast.expression.Identifier; import ql.ast.expression.arithmetic.Add; import ql.ast.expression.arithmetic.Divide; import ql.ast.expression.arithmetic.Multiply; import ql.ast.expression.arithmetic.Negation; import ql.ast.expression.arithmetic.Positive; import ql.ast.expression.arithmetic.Subtract; import ql.ast.expression.booleanalgebra.And; import ql.ast.expression.booleanalgebra.Not; import ql.ast.expression.booleanalgebra.Or; import ql.ast.expression.literal.BooleanLiteral; import ql.ast.expression.literal.FloatLiteral; import ql.ast.expression.literal.IntegerLiteral; import ql.ast.expression.literal.MoneyLiteral; import ql.ast.expression.literal.StringLiteral; import ql.ast.expression.relational.Equal; import ql.ast.expression.relational.Greater; import ql.ast.expression.relational.GreaterOrEqual; import ql.ast.expression.relational.Lower; import ql.ast.expression.relational.LowerOrEqual; import ql.ast.expression.relational.NotEqual; import ql.ast.statement.Block; import ql.ast.statement.ComputedQuestion; import ql.ast.statement.Form; import ql.ast.statement.If; import ql.ast.statement.IfElse; import ql.ast.statement.Question; import ql.ast.type.QLBoolean; import ql.ast.type.QLError; import ql.ast.type.QLForm; import ql.ast.visitor.ExpressionVisitor; import ql.ast.visitor.StatementVisitor; import ql.ast.visitor.TypeVisitor; import ql.errorhandling.ErrorEnvironment; import ql.errorhandling.error.IllegalAssignmentError; import ql.errorhandling.error.RedefinedVariableError; import ql.errorhandling.error.TypeError; import ql.errorhandling.error.UndefinedVariableError; public class TypeChecker extends StatementVisitor<Void> implements ExpressionVisitor<QLType>, TypeVisitor<QLType> { private ErrorEnvironment errorEnvironment; private TypeEnvironment typeEnvironment, scopedEnvironment; private TypeChecker(TypeEnvironment typeEnvironment) { super.setExpressionVisitor(this); super.setTypeVisitor(this); // Type environment that is given. Will include ALL identifiers with their types. this.typeEnvironment = typeEnvironment; // Type environment for internal scope that abides scoping rules. scopedEnvironment = new TypeEnvironment(); errorEnvironment = new ErrorEnvironment(); } public ErrorEnvironment getErrorEnvironment() { return errorEnvironment; } public static ErrorEnvironment check(Statement tree, TypeEnvironment typeEnvironment) { TypeChecker typeChecker = new TypeChecker(typeEnvironment); tree.accept(typeChecker); return typeChecker.getErrorEnvironment(); } public static ErrorEnvironment check(Expression tree, TypeEnvironment typeEnvironment) { TypeChecker typeChecker = new TypeChecker(typeEnvironment); tree.accept(typeChecker); return typeChecker.getErrorEnvironment(); } public static ErrorEnvironment check(QLType tree, TypeEnvironment typeEnvironment) { TypeChecker typeChecker = new TypeChecker(typeEnvironment); tree.accept(typeChecker); return typeChecker.getErrorEnvironment(); } /** * OPERATORS */ @Override public QLType visit(Add add) { QLType lhs = add.getLeft().accept(this); QLType rhs = add.getRight().accept(this); if (!lhs.add(rhs)) { errorEnvironment.addError(new TypeError(add, lhs, rhs)); } return add.getType(); } @Override public QLType visit(Divide div) { QLType lhs = div.getLeft().accept(this); QLType rhs = div.getRight().accept(this); if (!lhs.divide(rhs)) { errorEnvironment.addError(new TypeError(div, lhs, rhs)); } return div.getType(); } @Override public QLType visit(Multiply mul) { QLType lhs = mul.getLeft().accept(this); QLType rhs = mul.getRight().accept(this); if (!lhs.multiply(rhs)) { errorEnvironment.addError(new TypeError(mul, lhs, rhs)); } return mul.getType(); } @Override public QLType visit(Subtract sub) { QLType lhs = sub.getLeft().accept(this); QLType rhs = sub.getRight().accept(this); if (!lhs.subtract(rhs)) { errorEnvironment.addError(new TypeError(sub, lhs, rhs)); } return sub.getType(); } @Override public QLType visit(Negation neg) { QLType lhs = neg.getExpression().accept(this); if (!lhs.negative()) { errorEnvironment.addError(new TypeError(neg, lhs)); } return neg.getType(); } @Override public QLType visit(Positive pos) { QLType lhs = pos.getExpression().accept(this); if (!lhs.positive()) { errorEnvironment.addError(new TypeError(pos, lhs)); } return pos.getType(); } /** * EQUALITY OPERATORS * * The left operand should be compatible with the right one. * So the compatibleWith value is one of the operands' type */ @Override public QLType visit(Equal eq) { QLType lhs = eq.getLeft().accept(this); QLType rhs = eq.getRight().accept(this); if (!lhs.equalTo(rhs)) { errorEnvironment.addError(new TypeError(eq, lhs, rhs)); } return eq.getType(); } @Override public QLType visit(NotEqual neq) { QLType lhs = neq.getLeft().accept(this); QLType rhs = neq.getRight().accept(this); if (!lhs.notEqualTo(rhs)) { errorEnvironment.addError(new TypeError(neq, lhs, rhs)); } return neq.getType(); } /** * Relational operators */ @Override public QLType visit(GreaterOrEqual geq) { QLType lhs = geq.getLeft().accept(this); QLType rhs = geq.getRight().accept(this); if (!lhs.greaterOrEqual(rhs)) { errorEnvironment.addError(new TypeError(geq, lhs, rhs)); } return geq.getType(); } @Override public QLType visit(Greater gt) { QLType lhs = gt.getLeft().accept(this); QLType rhs = gt.getRight().accept(this); if (!lhs.greaterThan(rhs)) { errorEnvironment.addError(new TypeError(gt, lhs, rhs)); } return gt.getType(); } @Override public QLType visit(LowerOrEqual leq) { QLType lhs = leq.getLeft().accept(this); QLType rhs = leq.getRight().accept(this); if (!lhs.lowerOrEqual(rhs)) { errorEnvironment.addError(new TypeError(leq, lhs, rhs)); } return leq.getType(); } @Override public QLType visit(Lower lt) { QLType lhs = lt.getLeft().accept(this); QLType rhs = lt.getRight().accept(this); if (!lhs.lowerThan(rhs)) { errorEnvironment.addError(new TypeError(lt, lhs, rhs)); } return lt.getType(); } @Override public QLType visit(And and) { QLType lhs = and.getLeft().accept(this); QLType rhs = and.getRight().accept(this); if (!lhs.and(rhs)) { errorEnvironment.addError(new TypeError(and, lhs, rhs)); } return and.getType(); } @Override public QLType visit(Or or) { QLType lhs = or.getLeft().accept(this); QLType rhs = or.getRight().accept(this); if (!lhs.or(rhs)) { errorEnvironment.addError(new TypeError(or, lhs, rhs)); } return or.getType(); } @Override public QLType visit(Not not) { QLType lhs = not.getExpression().accept(this); if (!lhs.not()) { errorEnvironment.addError(new TypeError(not, lhs)); } return not.getType(); } @Override public QLType visit(BooleanLiteral booleanNode) { return booleanNode.getType(); } @Override public QLType visit(FloatLiteral floatNode) { return floatNode.getType(); } @Override public QLType visit(MoneyLiteral moneyNode) { return moneyNode.getType(); } @Override public QLType visit(IntegerLiteral integerNode) { return integerNode.getType(); } @Override public QLType visit(StringLiteral stringNode) { return stringNode.getType(); } public QLType visit(Identifier identifier) { QLType identifierType = scopedEnvironment.resolve(identifier); if(identifierType == null) { errorEnvironment.addError(new UndefinedVariableError(identifier)); return new QLError(); } return identifierType; } /** * Statements */ @Override public Void visit(ComputedQuestion compQuestionNode) { QLType questionType = compQuestionNode.getType(); QLType expressionType = compQuestionNode.getExpression().accept(this); if(!questionType.assign(expressionType)) { errorEnvironment.addError(new IllegalAssignmentError(compQuestionNode, questionType, expressionType)); } Identifier questionIdentifier = compQuestionNode.getIdentifier(); if(scopedEnvironment.resolve(questionIdentifier) == null) { scopedEnvironment.store(questionIdentifier, questionType); typeEnvironment.store(questionIdentifier, questionType); } else { errorEnvironment.addError(new RedefinedVariableError(questionIdentifier)); } return null; } @Override public Void visit(Form formNode) { Identifier formIdentifier = formNode.getIdentifier(); if(scopedEnvironment.resolve(formIdentifier) == null) { scopedEnvironment.store(formNode.getIdentifier(), new QLForm()); typeEnvironment.store(formNode.getIdentifier(), new QLForm()); } else { errorEnvironment.addError(new RedefinedVariableError(formIdentifier)); } return super.visit(formNode); } @Override public Void visit(If ifNode) { // The expression must have a boolean type QLType ifType = ifNode.getExpression().accept(this); if (!ifType.equals(new QLBoolean())) { errorEnvironment.addError(new TypeError(ifNode, new QLBoolean(), ifType)); } return ifNode.getBlock().accept(this); } @Override public Void visit(IfElse ifElseNode) { // The expression must have a boolean type QLType ifType = ifElseNode.getExpression().accept(this); if (!ifType.equals(new QLBoolean())) { errorEnvironment.addError(new TypeError(ifElseNode, new QLBoolean(), ifType)); } ifElseNode.getIfBranch().accept(this); return ifElseNode.getElseBranch().accept(this); } @Override public Void visit(Question questionNode) { Identifier questionIdentifier = questionNode.getIdentifier(); if(scopedEnvironment.resolve(questionIdentifier) == null) { scopedEnvironment.store(questionIdentifier, questionNode.getType()); typeEnvironment.store(questionIdentifier, questionNode.getType()); } else { errorEnvironment.addError(new RedefinedVariableError(questionIdentifier)); } return null; } @Override public Void visit(Block blockNode) { increaseScope(); super.visit(blockNode); decreaseScope(); return null; } private void increaseScope() { scopedEnvironment = new TypeEnvironment(scopedEnvironment); } private void decreaseScope() { scopedEnvironment = scopedEnvironment.getParent(); } }