Browse Source

Implement Char basic operations:

* Relational
* Equality and Inequality
* Concatenation (+)
Lucas de Souza 4 years ago
parent
commit
b57eaae608
5 changed files with 337 additions and 1 deletions
  1. 38 0
      js/ast/operators.ts
  2. 3 1
      js/main.js
  3. 246 0
      js/processor/compatibilityTable.ts
  4. 12 0
      js/processor/ivprogProcessor.js
  5. 38 0
      js/util/config.ts

+ 38 - 0
js/ast/operators.ts

@@ -0,0 +1,38 @@
+export type Operator = {ord: number; value: string}
+
+export const Operators = Object.freeze({
+  ADD: {ord: 0, value: "+"},
+  SUB: {ord: 1, value: "-"},
+  MULT: {ord: 2, value: '*'},
+  DIV: {ord: 3, value: '/'},
+  MOD: {ord: 4, value: '%'},
+  GT: {ord: 5, value: '>'},
+  GE: {ord: 6, value: '>='},
+  LT: {ord: 7, value: '<'},
+  LE: {ord: 8, value: '<='},
+  EQ: {ord: 9, value: '=='},
+  NEQ: {ord: 10, value: '!='},
+  AND: {ord: 11, value: 'and'},
+  OR: {ord: 12, value: 'or'},
+  NOT: {ord: 13, value: 'not'}
+});
+
+export function convertFromString (op: string): Operator | null  {
+  switch (op) {
+    case '+' : return Operators.ADD;
+    case '-' : return Operators.SUB;
+    case '*' : return Operators.MULT;
+    case '/' : return Operators.DIV;
+    case '%' : return Operators.MOD;
+    case '>' : return Operators.GT;
+    case '>=' : return Operators.GE;
+    case '<' : return Operators.LT;
+    case '<=' : return Operators.LE;
+    case '==' : return Operators.EQ;
+    case '!=' : return Operators.NEQ;
+    case 'and' : return Operators.AND;
+    case 'or' : return Operators.OR;
+    case 'not' : return Operators.NOT;
+  }
+  return null;
+}

+ 3 - 1
js/main.js

@@ -7,6 +7,7 @@ import { i18nHelper } from "./services/i18nHelper";
 import { ActionTypes, getLogs, getLogsAsString, registerClick, registerUserEvent, parseLogs } from "./services/userLog";
 import { prepareActivityToStudentHelper, autoEval } from "./util/iassignHelpers";
 import { openAssessmentDetail } from "./util/utils";
+import { Config } from './util/config';
 import * as CodeEditorAll from "./visualUI/text_editor";
 import {autoGenerateTestCaseOutput} from './util/auto_gen_output';
 
@@ -41,5 +42,6 @@ export {
   ActionTypes,
   CodeEditor,
   openAssessmentDetail,
-  autoGenerateTestCaseOutput
+  autoGenerateTestCaseOutput,
+  Config
 }

+ 246 - 0
js/processor/compatibilityTable.ts

@@ -0,0 +1,246 @@
+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<Operator, IType[][]> {
+  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<Operator, IType[]> {
+  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;
+    } 
+  }
+}

+ 12 - 0
js/processor/ivprogProcessor.js

@@ -1312,6 +1312,10 @@ export class IVProgProcessor {
         } else if (Types.STRING.isCompatible(right.type)) {
           const leftStr = convertToString(left.get(), left.type);
           return new StoreValue(resultType, leftStr + right.get());
+        } else if (Types.CHAR.isCompatible(left.type)) {
+          const strLeft = convertToString(left.get(), left.type);
+          const strRight = convertToString(right.get(), right.type);
+          return new StoreValue(resultType, strLeft + strRight);
         } else {
           return new StoreValue(resultType, left.get().plus(right.get()));
         }
@@ -1350,6 +1354,8 @@ export class IVProgProcessor {
         let rightValue = right.get();
         if (Types.STRING.isCompatible(left.type)) {
           result = leftValue.length > rightValue.length;
+        } else if (Types.CHAR.isCompatible(left.type)) {
+          result = leftValue.charCodeAt(0) > rightValue.charCodeAt(0);
         } else {
           if (shouldImplicitCast) {
             resultType = Types.BOOLEAN;
@@ -1365,6 +1371,8 @@ export class IVProgProcessor {
         let rightValue = right.get();
         if (Types.STRING.isCompatible(left.type)) {
           result = leftValue.length >= rightValue.length;
+        } else if (Types.CHAR.isCompatible(left.type)) {
+          result = leftValue.charCodeAt(0) >= rightValue.charCodeAt(0);
         } else {
           if (shouldImplicitCast) {
             resultType = Types.BOOLEAN;
@@ -1380,6 +1388,8 @@ export class IVProgProcessor {
         let rightValue = right.get();
         if (Types.STRING.isCompatible(left.type)) {
           result = leftValue.length < rightValue.length;
+        } else if (Types.CHAR.isCompatible(left.type)) {
+          result = leftValue.charCodeAt(0) < rightValue.charCodeAt(0);
         } else {
           if (shouldImplicitCast) {
             resultType = Types.BOOLEAN;
@@ -1395,6 +1405,8 @@ export class IVProgProcessor {
         let rightValue = right.get();
         if (Types.STRING.isCompatible(left.type)) {
           result = leftValue.length <= rightValue.length;
+        } else if (Types.CHAR.isCompatible(left.type)) {
+          result = leftValue.charCodeAt(0) <= rightValue.charCodeAt(0);
         } else {
           if (shouldImplicitCast) {
             resultType = Types.BOOLEAN;

+ 38 - 0
js/util/config.ts

@@ -0,0 +1,38 @@
+interface ConfigInterface {
+  [id: string]: unknown;
+}
+
+class ConfigObject implements ConfigInterface{
+
+  public decimalPlaces: number;
+  public intConvertRoundMode: number;
+  public default_lang: string;
+  public enable_type_casting: boolean;
+  public idle_input_interval: number;
+  public suspend_threshold: number;
+  public max_instruction_count: number;
+  [id: string]: unknown;
+
+  constructor () {
+    this.decimalPlaces = 8;
+    this.intConvertRoundMode = 2;
+    this.default_lang = 'pt';
+    this.enable_type_casting = true;
+    this.idle_input_interval = 5000;
+    this.suspend_threshold = 100;
+    // this.max_instruction_count = 350250; - automated evaluation limit
+    this.max_instruction_count = Number.MAX_SAFE_INTEGER;
+  }
+
+  setConfig (opts: object): void {
+    const otherConfig = opts as ConfigInterface;
+    for (const key in otherConfig) {
+      if(Object.prototype.hasOwnProperty.call(this, key)){
+        this[key] = otherConfig[key];
+      }
+    }
+  }
+}
+
+const config = new ConfigObject();
+export const Config = config;