/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*/
/**
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3, 29 June 2007;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl-3.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingml.compilers.checker;
import org.sintef.thingml.*;
import org.sintef.thingml.constraints.Types;
import org.sintef.thingml.helpers.TyperHelper;
import org.sintef.thingml.util.ThingmlSwitch;
public class TypeChecker extends ThingmlSwitch<Type> {
public Type computeTypeOf(Expression exp) {
Type result = null;
if (exp == null) {
return Types.ANY_TYPE;
}
result = doSwitch(exp);
if (result == null) {
System.out.println("TODO: Type checking for " + exp.getClass().getName());
return Types.ANY_TYPE;
}
return result;
}
@Override
public Type caseExternExpression(ExternExpression object) {
return Types.ANY_TYPE;
}
@Override
public Type caseIntegerLiteral(IntegerLiteral object) {
return Types.INTEGER_TYPE;
}
@Override
public Type caseBooleanLiteral(BooleanLiteral object) {
return Types.BOOLEAN_TYPE;
}
@Override
public Type caseStringLiteral(StringLiteral object) {
return Types.STRING_TYPE;
}
@Override
public Type caseDoubleLiteral(DoubleLiteral object) {
return Types.REAL_TYPE;
}
@Override
public Type caseUnaryMinus(UnaryMinus object) {
Type t = computeTypeOf(object.getTerm());
if (t.equals(Types.ANY_TYPE))
return Types.ANY_TYPE;
if (!t.equals(Types.INTEGER_TYPE) && !t.equals(Types.REAL_TYPE)) {
return Types.ERROR_TYPE;
}
return t;
}
private Type caseBinaryNumericalOperator(Type t1, Type t2) {
if (t1.equals(Types.ANY_TYPE) || t2.equals(Types.ANY_TYPE))
return Types.ANY_TYPE;
if ((!t1.equals(Types.INTEGER_TYPE) && !t1.equals(Types.REAL_TYPE)) || (!t2.equals(Types.INTEGER_TYPE) && !t2.equals(Types.REAL_TYPE))) {
return Types.ERROR_TYPE;
}
if (!TyperHelper.getBroadType(t1).getName().equals(TyperHelper.getBroadType(t2).getName())) //One Integer and one Real
return Types.REAL_TYPE;
return t1;
}
@Override
public Type casePlusExpression(PlusExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseBinaryNumericalOperator(t1, t2);
}
@Override
public Type caseMinusExpression(MinusExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseBinaryNumericalOperator(t1, t2);
}
@Override
public Type caseTimesExpression(TimesExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseBinaryNumericalOperator(t1, t2);
}
@Override
public Type caseDivExpression(DivExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseBinaryNumericalOperator(t1, t2);
}
@Override
public Type caseModExpression(ModExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
if (t1.equals(Types.ANY_TYPE) || t2.equals(Types.ANY_TYPE))
return Types.ANY_TYPE;
if (!t1.equals(Types.INTEGER_TYPE) || !t2.equals(Types.INTEGER_TYPE)) {
return Types.ERROR_TYPE;
}
return Types.INTEGER_TYPE;
}
private Type caseComparison(Type t1, Type t2) {
if ((t1.equals(Types.INTEGER_TYPE) || t1.equals(Types.REAL_TYPE) || t1.equals(Types.ANY_TYPE)) && (t2.equals(Types.INTEGER_TYPE) || t2.equals(Types.REAL_TYPE) || t2.equals(Types.ANY_TYPE)))
return Types.BOOLEAN_TYPE;
if ((t1.equals(Types.BOOLEAN_TYPE) || t1.equals(Types.ANY_TYPE)) && (t2.equals(Types.BOOLEAN_TYPE) || t2.equals(Types.ANY_TYPE)))
return Types.BOOLEAN_TYPE;
if (TyperHelper.isA(t1, Types.ANY_TYPE) && TyperHelper.isA(t2, Types.ANY_TYPE))
return Types.BOOLEAN_TYPE;
return Types.ERROR_TYPE;
}
@Override
public Type caseEqualsExpression(EqualsExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseComparison(t1, t2);
}
@Override
public Type caseGreaterExpression(GreaterExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseComparison(t1, t2);
}
@Override
public Type caseGreaterOrEqualExpression(GreaterOrEqualExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseComparison(t1, t2);
}
@Override
public Type caseLowerExpression(LowerExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseComparison(t1, t2);
}
@Override
public Type caseLowerOrEqualExpression(LowerOrEqualExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseComparison(t1, t2);
}
//Boolean
private Type caseBooleanOperator(Type t1, Type t2) {
if (t1.equals(Types.ANY_TYPE) || t2.equals(Types.ANY_TYPE))
return Types.ANY_TYPE;
if (!t1.equals(Types.BOOLEAN_TYPE) || !t2.equals(Types.BOOLEAN_TYPE)) {
return Types.ERROR_TYPE;
}
return Types.BOOLEAN_TYPE;
}
@Override
public Type caseAndExpression(AndExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseBooleanOperator(t1, t2);
}
@Override
public Type caseOrExpression(OrExpression object) {
Type t1 = computeTypeOf(object.getLhs());
Type t2 = computeTypeOf(object.getRhs());
return caseBooleanOperator(t1, t2);
}
@Override
public Type caseNotExpression(NotExpression object) {
Type t = computeTypeOf(object.getTerm());
if (t.equals(Types.ANY_TYPE))
return Types.ANY_TYPE;
if (!t.equals(Types.BOOLEAN_TYPE)) {
return Types.ERROR_TYPE;
}
return Types.BOOLEAN_TYPE;
}
//End Boolean
@Override
public Type casePropertyReference(PropertyReference object) {
return TyperHelper.getBroadType(object.getProperty().getType());
}
@Override
public Type caseExpressionGroup(ExpressionGroup object) {
return computeTypeOf(object.getExp());
}
@Override
public Type caseReference(Reference object) {
if (object.getReference() instanceof ReceiveMessage) {
ReceiveMessage rm = (ReceiveMessage) object.getReference();
if (object.getParameter() instanceof SimpleParamRef) {
SimpleParamRef ref = (SimpleParamRef) object.getParameter();
if (ref.getParameterRef().getType() == null)
return Types.ERROR_TYPE;
return TyperHelper.getBroadType(ref.getParameterRef().getType());
}
} else if (object instanceof PropertyReference) {
PropertyReference pr = (PropertyReference) object;
if (pr.getProperty().getType() == null)
return Types.ERROR_TYPE;
return TyperHelper.getBroadType(pr.getProperty().getType());
}
return Types.ANY_TYPE;
}
@Override
public Type caseFunctionCallExpression(FunctionCallExpression object) {
if (object.getFunction().getType() == null)
return Types.VOID_TYPE;
return TyperHelper.getBroadType(object.getFunction().getType());
}
@Override
public Type caseArrayIndex(ArrayIndex object) {
Type t = computeTypeOf(object.getIndex());
if (t.equals(Types.INTEGER_TYPE) || t.equals(Types.ANY_TYPE))
return computeTypeOf(object.getArray());
return Types.ERROR_TYPE;
}
}