package org.uva.student.calinwouter.qlqls.ql.typechecker; import org.uva.student.calinwouter.qlqls.generated.analysis.ReversedDepthFirstAdapter; import org.uva.student.calinwouter.qlqls.generated.node.*; import org.uva.student.calinwouter.qlqls.ql.exceptions.FieldNotFoundException; import org.uva.student.calinwouter.qlqls.ql.interfaces.ITypeDescriptor; import org.uva.student.calinwouter.qlqls.ql.model.QLTypeCheckResults; import org.uva.student.calinwouter.qlqls.ql.model.StaticFields; import org.uva.student.calinwouter.qlqls.ql.types.BooleanValue; import org.uva.student.calinwouter.qlqls.ql.types.IntegerValue; import org.uva.student.calinwouter.qlqls.ql.types.StringValue; import java.util.List; import java.util.Map; import java.util.Stack; import static org.uva.student.calinwouter.qlqls.ql.helper.ASTHelper.*; /** * This type checker works by pushing elements on the stack, and assessing the type of the value that is popped * from the stack. When the type popped from the stack is not the same as (one of) the allowed types, the type checker * will add an error to the list of type check results. */ public class PExpressionTypeChecker extends ReversedDepthFirstAdapter { private final StaticFields staticFields; private final Stack<ITypeDescriptor> typeDescriptors; private final QLTypeCheckResults QLTypeCheckResults; private final Map<String, List<String>> variableDependencies; private String lastComputedValueIdentifier; @Override public void outAAddExpression(AAddExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushInteger(); } @Override public void caseASubtractExpression(ASubtractExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushInteger(); } @Override public void caseATrueExpression(ATrueExpression node) { pushBoolean(); } @Override public void caseAFalseExpression(AFalseExpression node) { pushBoolean(); } @Override public void caseAOrExpression(AOrExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popBoolean(); popBoolean(); pushBoolean(); } @Override public void caseAAndExpression(AAndExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popBoolean(); popBoolean(); pushBoolean(); } @Override public void caseAEqualsExpression(AEqualsExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popType(); popType(); pushBoolean(); } @Override public void caseANotEqualsExpression(ANotEqualsExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popType(); popType(); pushBoolean(); } @Override public void caseALesserThanExpression(ALesserThanExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushBoolean(); } @Override public void caseAGreaterThanExpression(AGreaterThanExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushBoolean(); } @Override public void caseALesserThanOrEqualsExpression(ALesserThanOrEqualsExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushBoolean(); } @Override public void caseAGreaterThanOrEqualsExpression(AGreaterThanOrEqualsExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushBoolean(); } @Override public void caseAStringElement(AStringElement node) { pushString(); } @Override public void caseAMultiplyExpression(AMultiplyExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushInteger(); } @Override public void caseADivideExpression(ADivideExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushInteger(); } @Override public void caseAModuloExpression(AModuloExpression node) { typeCheckExpression(node.getLeft()); typeCheckExpression(node.getRight()); popInteger(); popInteger(); pushInteger(); } @Override public void caseANotExpression(ANotExpression node) { typeCheckExpression(node.getExpression()); popBoolean(); pushBoolean(); } @Override public void caseANumberExpression(ANumberExpression node) { pushInteger(); } @Override public void caseAIdentifierExpression(AIdentifierExpression node) { final String variableName = getIdentifier(node); checkUndefinedReference(variableName); checkIfComputedValueDependency(variableName); pushIdentifierType(variableName); } public void typeCheckExpression(PExpression expression){ expression.apply(this); } private void checkUndefinedReference(String identifier) { if (!staticFields.containsField(identifier)) { addErrorUndefinedReference(identifier); } } private void checkIfComputedValueDependency(String identifier){ if (lastComputedValueIdentifier != null){ variableDependencies.get(lastComputedValueIdentifier).add(identifier); } } public void setLastComputedValueIdentifier(String identifier){ lastComputedValueIdentifier = identifier; } public ITypeDescriptor popType() { return typeDescriptors.pop(); } private void addErrorUndefinedReference(String variableName) { QLTypeCheckResults.addUndefinedReferenceError(variableName); } private void pushType(ITypeDescriptor typeDescriptor) { typeDescriptors.push(typeDescriptor); } private void checkPopGeneratesNoTypeError(ITypeDescriptor assertedType) { final ITypeDescriptor lastStackElement = popType(); if (!lastStackElement.equals(assertedType)) { QLTypeCheckResults.addErrorTypeIsNotOfType(assertedType); } } private void popBoolean() { checkPopGeneratesNoTypeError(BooleanValue.BOOL_VALUE_TYPE_DESCRIPTOR); } private void popInteger() { checkPopGeneratesNoTypeError(IntegerValue.INTEGER_VALUE_TYPE_DESCRIPTOR); } private void pushInteger() { pushType(IntegerValue.INTEGER_VALUE_TYPE_DESCRIPTOR); } private void pushBoolean() { pushType(BooleanValue.BOOL_VALUE_TYPE_DESCRIPTOR); } private void pushString() { pushType(StringValue.STRING_VALUE_TYPE_DESCRIPTOR); } private ITypeDescriptor getTypeOfField(String identifier) { ITypeDescriptor fieldType; try { fieldType = staticFields.getTypeOfField(identifier); } catch (FieldNotFoundException e) { fieldType = new UndefinedTypeDescriptor(); } return fieldType; } private void pushIdentifierType(String identifier) { final ITypeDescriptor fieldType = getTypeOfField(identifier); pushType(fieldType); } public void checkLastEntryIsOfType(final ITypeDescriptor typeDescriptor) { assert(typeDescriptors.size() == 1); if (!popType().equals(typeDescriptor)) { QLTypeCheckResults.addErrorTypeIsNotOfType(typeDescriptor); } } public PExpressionTypeChecker(StaticFields staticFields, QLTypeCheckResults QLTypeCheckResults, Map<String, List<String>> variableDependencies) { this.staticFields = staticFields; this.typeDescriptors = new Stack<ITypeDescriptor>(); this.QLTypeCheckResults = QLTypeCheckResults; this.variableDependencies = variableDependencies; } }