import { Types } from '../typeSystem/types'; import { IType } from '../typeSystem/itype'; import { Type } from '../typeSystem/type'; import { Operators, Operator } from '../ast/operators'; import { MultiType } from '../typeSystem/multiType'; import { Config } from '../util/config'; function buildInfixAddTable (): IType[][] { const table: IType[][] = [[], [], [], [], []] table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.INTEGER; table[Types.INTEGER.ord][Types.REAL.ord] = Types.REAL; table[Types.INTEGER.ord][Types.STRING.ord] = Types.STRING; table[Types.REAL.ord][Types.INTEGER.ord] = Types.REAL; table[Types.REAL.ord][Types.REAL.ord] = Types.REAL; table[Types.REAL.ord][Types.STRING.ord] = Types.STRING; table[Types.STRING.ord][Types.INTEGER.ord] = Types.STRING; table[Types.STRING.ord][Types.REAL.ord] = Types.STRING; table[Types.STRING.ord][Types.STRING.ord] = Types.STRING; table[Types.STRING.ord][Types.BOOLEAN.ord] = Types.STRING; table[Types.STRING.ord][Types.CHAR.ord] = Types.STRING; table[Types.CHAR.ord][Types.CHAR.ord] = Types.STRING; table[Types.CHAR.ord][Types.STRING.ord] = Types.STRING; return table; } function buildInfixMultiDivSubTable (): IType[][] { const table: IType[][] = [[], []]; table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.INTEGER; table[Types.INTEGER.ord][Types.REAL.ord] = Types.REAL; table[Types.REAL.ord][Types.INTEGER.ord] = Types.REAL; table[Types.REAL.ord][Types.REAL.ord] = Types.REAL; return table; } function buildInfixEqualityInequalityTable (): IType[][] { const table: IType[][] = [[], [], [], [], []]; table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.BOOLEAN; table[Types.REAL.ord][Types.REAL.ord] = Types.BOOLEAN; table[Types.BOOLEAN.ord][Types.BOOLEAN.ord] = Types.BOOLEAN; table[Types.STRING.ord][Types.STRING.ord] = Types.BOOLEAN; table[Types.CHAR.ord][Types.CHAR.ord] = Types.BOOLEAN; return table; } function buildInfixRelationalTable (): IType[][] { const table: IType[][] = [[], [], [], [], []]; table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.BOOLEAN; table[Types.REAL.ord][Types.REAL.ord] = Types.BOOLEAN; table[Types.STRING.ord][Types.STRING.ord] = Types.BOOLEAN; table[Types.CHAR.ord][Types.CHAR.ord] = Types.BOOLEAN; return table; } function buildInfixAndOrTable (): IType[][] { const table: IType[][] = [[], [], [], []]; table[Types.BOOLEAN.ord][Types.BOOLEAN.ord] = Types.BOOLEAN; return table; } function buildInfixModTable (): IType[][] { const table: IType[][] = [[]]; table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.INTEGER; return table; } function buildUnarySumSubList (): IType[] { const list: IType[] = []; list[Types.INTEGER.ord] = Types.INTEGER; list[Types.REAL.ord] = Types.REAL; return list; } function buildUnaryNegList (): IType[] { const list: IType[] = []; list[Types.BOOLEAN.ord] = Types.BOOLEAN; return list; } function buildInfixCompatibilityTable (): WeakMap { const compatibilityMap = new WeakMap(); compatibilityMap.set(Operators.ADD, buildInfixAddTable()); compatibilityMap.set(Operators.SUB, buildInfixMultiDivSubTable()); compatibilityMap.set(Operators.MULT, buildInfixMultiDivSubTable()); compatibilityMap.set(Operators.DIV, buildInfixMultiDivSubTable()); compatibilityMap.set(Operators.EQ, buildInfixEqualityInequalityTable()); compatibilityMap.set(Operators.NEQ, buildInfixEqualityInequalityTable()); compatibilityMap.set(Operators.GE, buildInfixRelationalTable()); compatibilityMap.set(Operators.GT, buildInfixRelationalTable()); compatibilityMap.set(Operators.LE, buildInfixRelationalTable()); compatibilityMap.set(Operators.LT, buildInfixRelationalTable()); compatibilityMap.set(Operators.OR, buildInfixAndOrTable()); compatibilityMap.set(Operators.AND, buildInfixAndOrTable()); compatibilityMap.set(Operators.MOD, buildInfixModTable()); return compatibilityMap; } function buildUnaryCompatibilityTable (): WeakMap { const compatibilityMap = new WeakMap(); compatibilityMap.set(Operators.ADD, buildUnarySumSubList()); compatibilityMap.set(Operators.SUB, buildUnarySumSubList()); compatibilityMap.set(Operators.NOT, buildUnaryNegList()); return compatibilityMap; } const infixMap = buildInfixCompatibilityTable(); const unaryMap = buildUnaryCompatibilityTable(); export function resultTypeAfterInfixOp ( operator: Operator, leftExpressionType: IType, rightExpressionType: IType ): IType { try { if (leftExpressionType instanceof MultiType && rightExpressionType instanceof MultiType) { let newMulti = []; for (let i = 0; i < leftExpressionType.types.length; ++i) { const typeA = leftExpressionType.types[i]; for (let j = 0; j < rightExpressionType.types.length; ++i) { const typeB = rightExpressionType.types[j]; newMulti.push(resultTypeAfterInfixOp(operator, typeA, typeB)); } } newMulti = newMulti.filter(x => !x.isCompatible(Types.UNDEFINED)); if (newMulti.length <= 0) { if (Config.enable_type_casting) { if (leftExpressionType.isCompatible(Types.INTEGER) || leftExpressionType.isCompatible(Types.REAL)) { if (rightExpressionType.isCompatible(Types.INTEGER) || rightExpressionType.isCompatible(Types.REAL)) { return new MultiType([Types.INTEGER, Types.REAL]); } } } return Types.UNDEFINED; } else { return new MultiType(newMulti as Type[]) } } else if (leftExpressionType instanceof MultiType) { if (leftExpressionType.isCompatible(rightExpressionType as Type)) { return resultTypeAfterInfixOp(operator, rightExpressionType, rightExpressionType); } else { if (Config.enable_type_casting) { if (leftExpressionType.isCompatible(Types.INTEGER) || leftExpressionType.isCompatible(Types.REAL)) { if (rightExpressionType.isCompatible(Types.INTEGER) || rightExpressionType.isCompatible(Types.REAL)) { return rightExpressionType; } } } return Types.UNDEFINED; } } else if (rightExpressionType instanceof MultiType) { if (rightExpressionType.isCompatible(leftExpressionType as Type)) { return resultTypeAfterInfixOp(operator, leftExpressionType, leftExpressionType); } else { if (Config.enable_type_casting) { if (leftExpressionType.isCompatible(Types.INTEGER) || leftExpressionType.isCompatible(Types.REAL)) { if (rightExpressionType.isCompatible(Types.INTEGER) || rightExpressionType.isCompatible(Types.REAL)) { return leftExpressionType; } } } return Types.UNDEFINED; } } const resultTable = infixMap.get(operator) || []; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const resultType = resultTable[leftExpressionType.ord!][rightExpressionType.ord!]; if (resultType === null || resultType === undefined) { if (Config.enable_type_casting) { if (leftExpressionType.isCompatible(Types.INTEGER) || leftExpressionType.isCompatible(Types.REAL)) { if (rightExpressionType.isCompatible(Types.INTEGER) || rightExpressionType.isCompatible(Types.REAL)) { if (operator === Operators.MOD) { return Types.INTEGER; } else if (operator.ord >= 5 && operator.ord <= 10) { return Types.BOOLEAN; } } } } return Types.UNDEFINED } return resultType; } catch (e) { if (e instanceof TypeError) { return Types.UNDEFINED; } else { throw e; } } } export function resultTypeAfterUnaryOp (operator: Operator, leftExpressionType: IType): IType { try { if (leftExpressionType instanceof MultiType) { let newMulti = []; for (let i = 0; i < leftExpressionType.types.length; ++i) { const type = leftExpressionType.types[i]; newMulti.push(resultTypeAfterUnaryOp(operator, type)); } newMulti = newMulti.filter(x => !x.isCompatible(Types.UNDEFINED)); if (newMulti.length <= 0) { return Types.UNDEFINED; } return new MultiType(newMulti as Type[]); } const resultTable = unaryMap.get(operator) || []; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const resultType = resultTable[leftExpressionType.ord!]; if (resultType == null) { return Types.UNDEFINED; } return resultType; } catch (e) { if (e instanceof TypeError) { return Types.UNDEFINED; } else { throw e; } } }