Browse Source

Merge branch 'loopKill' of LInE/ivprog into master

Lucas de Souza 6 years ago
parent
commit
2824764911

+ 7 - 1
js/processor/definedFunctions.js

@@ -96,7 +96,13 @@ export const LanguageDefinedFunction = Object.freeze({
       }
       return lib + "." + internalName;
     }
-    return valueToKey(localName, LanguageService.getCurrentLangFuncs());
+    const funcName = valueToKey(localName, LanguageService.getCurrentLangFuncs());
+    if(funcName !== null) {
+      if(funcsObject[funcName]) {
+        return funcName;
+      }
+    }
+    return null;
   },
   getFunction: (internalName) => {
     if (internalName.indexOf(".") !== -1) {

+ 126 - 96
js/processor/ivprogProcessor.js

@@ -15,14 +15,26 @@ import { StoreObjectArrayAddressRef } from './store/storeObjectArrayAddressRef';
 import { CompoundType } from './../typeSystem/compoundType';
 import { convertToString } from '../typeSystem/parsers';
 
+let loopTimeoutMs = 10000
+
 export class IVProgProcessor {
 
+  static get LOOP_TIMEOUT () {
+    return loopTimeoutMs;
+  }
+
+  static set LOOP_TIMEOUT (ms) {
+    loopTimeoutMs = ms;
+  }
+
   constructor (ast) {
     this.ast = ast;
     this.globalStore = new Store();
     this.stores = [this.globalStore];
     this.context = [Context.BASE];
     this.input = null;
+    this.forceKill = false;
+    this.loopTimers = [];
     this.output = null;
   }
 
@@ -155,20 +167,21 @@ export class IVProgProcessor {
 
   executeCommands (store, cmds) {
     // helper to partially apply a function, in this case executeCommand
+    const outerRef = this;
     const partial = (fun, cmd) => (sto) => fun(sto, cmd);
     return cmds.reduce((lastCommand, next) => {
-      const nextCommand = partial(this.executeCommand.bind(this), next);
+      const nextCommand = partial(outerRef.executeCommand.bind(outerRef), next);
       return lastCommand.then(nextCommand);
     }, Promise.resolve(store));
   }
 
   executeCommand (store, cmd) {
 
-    while (store.mode === Modes.PAUSE) {
-      continue;
-    }
-
-    if(store.mode === Modes.RETURN) {
+    if(this.forceKill) {
+      return Promise.reject("Interrupção forçada do programa!");
+    } else if (store.mode === Modes.PAUSE) {
+      return Promise.resolve(this.executeCommand(store, cmd));
+    } else if(store.mode === Modes.RETURN) {
       return Promise.resolve(store);
     } else if(this.checkContext(Context.BREAKABLE) && store.mode === Modes.BREAK) {
       return Promise.resolve(store);
@@ -209,19 +222,16 @@ export class IVProgProcessor {
   }
 
   executeFunctionCall (store, cmd) {
-    return new Promise((resolve, reject) => {
-      const func = this.findFunction(cmd.id);
-      this.runFunction(func, cmd.actualParameters, store)
-        .then(sto => {
-          if(!Types.VOID.isCompatible(func.returnType) && sto.mode !== Modes.RETURN) {
-            // TODO: better error message
-            reject(new Error(`Function ${func.name} must have a return command`));
-          } else {
-            resolve(store);
-          }
-        })
-        .catch(err => reject(err));
-    }); 
+    const func = this.findFunction(cmd.id);
+    return this.runFunction(func, cmd.actualParameters, store)
+      .then(sto => {
+        if(!Types.VOID.isCompatible(func.returnType) && sto.mode !== Modes.RETURN) {
+          // TODO: better error message
+          return Promise.reject(new Error(`Function ${func.name} must have a return command`));
+        } else {
+          return store;
+        }
+      })
   }
 
   executeSwitch (store, cmd) {
@@ -291,16 +301,19 @@ export class IVProgProcessor {
   }
 
   executeDoWhile (store, cmd) {
+    const outerRef = this;
     try {
-      this.context.push(Context.BREAKABLE);
-      const $newStore = this.executeCommands(store, cmd.commands);
+      outerRef.loopTimers.push(Date.now());
+      outerRef.context.push(Context.BREAKABLE);
+      const $newStore = outerRef.executeCommands(store, cmd.commands);
       return $newStore.then(sto => {
         if(sto.mode === Modes.BREAK) {
-          this.context.pop();
+          outerRef.context.pop();
           sto.mode = Modes.RUN;
-          return Promise.resolve(sto);
+          outerRef.loopTimers.pop();
+          return sto;
         }
-        const $value = this.evaluateExpression(sto, cmd.expression);
+        const $value = outerRef.evaluateExpression(sto, cmd.expression);
         return $value.then(vl => {
           if (!vl.type.isCompatible(Types.BOOLEAN)) {
             // TODO: Better error message -- Inform line and column from token!!!!
@@ -308,45 +321,69 @@ export class IVProgProcessor {
             return Promise.reject(new Error(`DoWhile expression must be of type boolean`));
           }
           if (vl.value) {
-            this.context.pop();
-            return this.executeCommand(sto, cmd);
+            outerRef.context.pop();
+            for (let i = 0; i < outerRef.loopTimers.length; i++) {
+              const time = outerRef.loopTimers[i];
+              if(Date.now() - time >= IVProgProcessor.LOOP_TIMEOUT) {
+                console.log("Kill by Timeout...");
+                outerRef.forceKill = true;
+                return Promise.reject(new Error("Potential endless loop detected."));
+              }
+            }
+            return outerRef.executeCommand(sto, cmd);
           } else {
-            this.context.pop();
-            return Promise.resolve(sto);
+            outerRef.context.pop();
+            outerRef.loopTimers.pop();
+            console.log("Clear Timeout...");
+            return sto;
           }
-        });
-      });
+        })
+      })
     } catch (error) {
-      return Promise.reject(error)
+      return Promise.reject(error);
     }
   }
 
   executeWhile (store, cmd) {
+    const outerRef = this;
     try {
-      this.context.push(Context.BREAKABLE);
-      const $value = this.evaluateExpression(store, cmd.expression);
+      outerRef.loopTimers.push(Date.now());
+      outerRef.context.push(Context.BREAKABLE);
+      const $value = outerRef.evaluateExpression(store, cmd.expression);
       return $value.then(vl => {
         if(vl.type.isCompatible(Types.BOOLEAN)) {
           if(vl.value) {
-            const $newStore = this.executeCommands(store, cmd.commands);
+            const $newStore = outerRef.executeCommands(store, cmd.commands);
             return $newStore.then(sto => {
-              this.context.pop();
+              outerRef.context.pop();
               if (sto.mode === Modes.BREAK) {
+                outerRef.loopTimers.pop();
                 sto.mode = Modes.RUN;
-                return Promise.resolve(sto);
+                return sto;
+              }
+              for (let i = 0; i < outerRef.loopTimers.length; i++) {
+                const time = outerRef.loopTimers[i];
+                if(Date.now() - time >= IVProgProcessor.LOOP_TIMEOUT) {
+                  console.log("Kill by Timeout...");
+                  outerRef.forceKill = true;
+                  return Promise.reject(new Error("Potential endless loop detected."));
+                }
               }
-              return this.executeCommand(sto, cmd);
+              return outerRef.executeCommand(sto, cmd);
             });
           } else {
-            this.context.pop();
-            return Promise.resolve(store);
+            outerRef.context.pop();
+            outerRef.loopTimers.pop();
+            console.log("Clear Timeout...");
+            return store;
           }
         } else {
           // TODO: Better error message -- Inform line and column from token!!!!
           // THIS IF SHOULD BE IN A SEMANTIC ANALYSER
           return Promise.reject(new Error(`Loop condition must be of type boolean`));
         }
-      });
+      })
+      
     } catch (error) {
       return Promise.reject(error);
     }
@@ -429,67 +466,60 @@ export class IVProgProcessor {
   }
 
   executeArrayIndexAssign (store, cmd) {
-    return new Promise((resolve, reject) => {
-      const mustBeArray = store.applyStore(cmd.id);
-      if(!(mustBeArray.type instanceof CompoundType)) {
-        reject(new Error(cmd.id + " is not a vector/matrix"));
-        return;
+    const mustBeArray = store.applyStore(cmd.id);
+    if(!(mustBeArray.type instanceof CompoundType)) {
+      return Promise.reject(new Error(cmd.id + " is not a vector/matrix"));
+    }
+    const line$ = this.evaluateExpression(store, cmd.line);
+    const column$ = this.evaluateExpression(store, cmd.column);
+    const value$ =  this.evaluateExpression(store, cmd.expression);
+    return Promise.all([line$, column$, value$]).then(results => {
+      const lineSO = results[0];
+      if(!Types.INTEGER.isCompatible(lineSO.type)) {
+        // TODO: better error message
+        //SHOULD NOT BE HERE. IT MUST HAVE A SEMANTIC ANALYSIS
+        return Promise.reject(new Error("Array dimension must be of type int"));
       }
-      const line$ = this.evaluateExpression(store, cmd.line);
-      const column$ = this.evaluateExpression(store, cmd.column);
-      const value$ =  this.evaluateExpression(store, cmd.expression);
-      Promise.all([line$, column$, value$]).then(results => {
-        const lineSO = results[0];
-        if(!Types.INTEGER.isCompatible(lineSO.type)) {
+      const line = lineSO.number;
+      const columnSO = results[1];
+      let column = null
+      if (columnSO !== null) {
+        if(!Types.INTEGER.isCompatible(columnSO.type)) {
           // TODO: better error message
           //SHOULD NOT BE HERE. IT MUST HAVE A SEMANTIC ANALYSIS
-          reject(new Error("Array dimension must be of type int"));
-          return;
-        }
-        const line = lineSO.number;
-        const columnSO = results[1];
-        let column = null
-        if (columnSO !== null) {
-          if(!Types.INTEGER.isCompatible(columnSO.type)) {
-            // TODO: better error message
-            //SHOULD NOT BE HERE. IT MUST HAVE A SEMANTIC ANALYSIS
-            reject(new Error("Array dimension must be of type int"));
-            return;
-          }
-          column = columnSO.number;
-        }
-        const value = this.parseStoreObjectValue(results[2]);
-        if (line >= mustBeArray.lines) {
-          // TODO: better error message
-          return Promise.reject(new Error(`${exp.id}: index out of bounds: ${lines}`));
-        }
-        if (column !== null && mustBeArray.columns === null ){
-          // TODO: better error message
-          return Promise.reject(new Error(`${exp.id}: index out of bounds: ${column}`));
-        }
-        if(column !== null && column >= mustBeArray.columns) {
-          // TODO: better error message
-          return Promise.reject(new Error(`${exp.id}: index out of bounds: ${column}`));
+          return Promise.reject(new Error("Array dimension must be of type int"));
         }
+        column = columnSO.number;
+      }
+      const value = this.parseStoreObjectValue(results[2]);
+      if (line >= mustBeArray.lines) {
+        // TODO: better error message
+        return Promise.reject(new Error(`${exp.id}: index out of bounds: ${lines}`));
+      }
+      if (column !== null && mustBeArray.columns === null ){
+        // TODO: better error message
+        return Promise.reject(new Error(`${exp.id}: index out of bounds: ${column}`));
+      }
+      if(column !== null && column >= mustBeArray.columns) {
+        // TODO: better error message
+        return Promise.reject(new Error(`${exp.id}: index out of bounds: ${column}`));
+      }
 
-        const newArray = Object.assign(new StoreObjectArray(null,null,null), mustBeArray);
-        if (column !== null) {
-         if (value.type instanceof CompoundType) {
-           reject(new Error("Invalid operation. This must be a value: line "+cmd.sourceInfo.line));
-           return;
-         }
-         newArray.value[line].value[column] = value;
-         store.updateStore(cmd.id, newArray);
-        } else {
-         if(mustBeArray.columns !== null && value.type instanceof CompoundType) {
-          reject(new Error("Invalid operation. This must be a vector: line "+cmd.sourceInfo.line));
-          return;
-         }
-         newArray.value[line] = value;
-         store.updateStore(cmd.id, newArray);
+      const newArray = Object.assign(new StoreObjectArray(null,null,null), mustBeArray);
+      if (column !== null) {
+        if (value.type instanceof CompoundType) {
+          return Promise.reject(new Error("Invalid operation. This must be a value: line "+cmd.sourceInfo.line));
         }
-        resolve(store);
-      }).catch(err => reject(err));
+        newArray.value[line].value[column] = value;
+        store.updateStore(cmd.id, newArray);
+      } else {
+        if(mustBeArray.columns !== null && value.type instanceof CompoundType) {
+          return Promise.reject(new Error("Invalid operation. This must be a vector: line "+cmd.sourceInfo.line));
+        }
+        newArray.value[line] = value;
+        store.updateStore(cmd.id, newArray);
+      }
+      return store;
     });
   }
 
@@ -764,7 +794,7 @@ export class IVProgProcessor {
         case Operators.DIV.ord: {
           result = left.value / right.value;
           if (Types.INTEGER.isCompatible(resultType))
-            result = left.value.idiv(right.value);
+            result = left.value.divToInt(right.value);
           else
             result = left.value.div(right.value);
           return new StoreObject(resultType, result);

+ 1 - 0
js/processor/lib/io.js

@@ -44,6 +44,7 @@ export function createInputFun () {
       } else if (typeToConvert.isCompatible(Types.STRING)) {
         stoObj = new StoreObject(Types.STRING, toString(text));
       }
+      this.loopTimers.splice(0,this.loopTimers.length)
       store.updateStore('p1', stoObj);
       return Promise.resolve(store);
     });

+ 16 - 12
js/processor/lib/math.js

@@ -2,7 +2,7 @@ import { StoreObject } from '../store/storeObject';
 import * as Commands from './../../ast/commands';
 import { Types } from './../../typeSystem/types';
 import { toReal } from "./../../typeSystem/parsers";
-import { BigNumber } from 'bignumber.js';
+import { Decimal } from 'decimal.js';
 import { MultiType } from '../../typeSystem/multiType';
 import { CompoundType } from '../../typeSystem/compoundType';
 import { Modes } from '../modes';
@@ -21,10 +21,14 @@ import { Modes } from '../modes';
  * min
  */
 
+function convertToRadians (degrees) {
+  return degrees.times(Decimal.acos(-1)).div(180);
+}
+
 export function createSinFun () {
    const sinFun = (sto, _) => {
      const x = sto.applyStore('x');
-     const result = toReal(Math.sin(x.number));
+     const result = Decimal.sin(convertToRadians(x.value));
      const temp = new StoreObject(Types.REAL, result);
      sto.mode = Modes.RETURN;
      return Promise.resolve(sto.updateStore('$', temp));
@@ -32,7 +36,7 @@ export function createSinFun () {
 
   const block = new Commands.CommandBlock([],  [new Commands.SysCall(sinFun)]);
   const func = new Commands.Function('$sin', Types.REAL,
-    [new Commands.FormalParameter(new MultiType([[Types.INTEGER, Types.REAL]]), 'x', false)],
+    [new Commands.FormalParameter(new MultiType([Types.INTEGER, Types.REAL]), 'x', false)],
     block);
   return func;
 }
@@ -40,7 +44,7 @@ export function createSinFun () {
 export function createCosFun () {
   const cosFun = (sto, _) => {
     const x = sto.applyStore('x');
-    const result = toReal(Math.cos(x.number));
+    const result = Decimal.cos(convertToRadians(x.value));
     const temp = new StoreObject(Types.REAL, result);
     sto.mode = Modes.RETURN;
     return Promise.resolve(sto.updateStore('$', temp));
@@ -56,7 +60,7 @@ export function createCosFun () {
 export function createTanFun () {
   const tanFun = (sto, _) => {
     const x = sto.applyStore('x');
-    const result = toReal(Math.tan(x.number));
+    const result = Decimal.tan(convertToRadians(x.value));
     const temp = new StoreObject(Types.REAL, result);
     sto.mode = Modes.RETURN;
     return Promise.resolve(sto.updateStore('$', temp));
@@ -87,9 +91,9 @@ export function createSqrtFun () {
 
 export function createPowFun () {
   const powFun = (sto, _) => {
-    const x = sto.applyStore('x');f
+    const x = sto.applyStore('x');
     const y = sto.applyStore('y');
-    const result = toReal(Math.pow(x.number, y.number));
+    const result = x.value.pow(y.value);
     const temp = new StoreObject(Types.REAL, result);
     sto.mode = Modes.RETURN;
     return Promise.resolve(sto.updateStore('$', temp));
@@ -109,7 +113,7 @@ export function createLogFun () {
     if (x.value.isNegative()) {
       return Promise.reject("the value passed to log function cannot be negative");
     }
-    const result = toReal(Math.log10(x.number));
+    const result = Decimal.log10(x.value);
     const temp = new StoreObject(Types.REAL, result);
     sto.mode = Modes.RETURN;
     return Promise.resolve(sto.updateStore('$', temp));
@@ -173,8 +177,8 @@ export function createInvertFun () {
 export function createMaxFun () {
   const maxFun = (sto, _) => {
     const x = sto.applyStore('x');
-    const numbers = x.value.map(stoObj => stoObj.number);
-    const result = BigNumber.max(numbers);
+    const numbers = x.value.map(stoObj => stoObj.value);
+    const result = Decimal.max(...numbers);
     const temp = new StoreObject(x.type.innerType, result);
     sto.mode = Modes.RETURN;
     return Promise.resolve(sto.updateStore('$', temp));
@@ -190,8 +194,8 @@ export function createMaxFun () {
 export function createMinFun () {
   const minFun = (sto, _) => {
     const x = sto.applyStore('x');
-    const numbers = x.value.map(stoObj => stoObj.value.toNumber());
-    const result = BigNumber.min(numbers);
+    const numbers = x.value.map(stoObj => stoObj.value);
+    const result = Decimal.min(...numbers);
     const temp = new StoreObject(x.type.innerType, result);
     sto.mode = Modes.RETURN;
     return Promise.resolve(sto.updateStore('$', temp));

+ 2 - 2
js/processor/store/storeObject.js

@@ -1,4 +1,4 @@
-import { BigNumber } from 'bignumber.js'
+import Decimal from 'decimal.js';
 
 export class StoreObject {
 
@@ -30,7 +30,7 @@ export class StoreObject {
   }
   
   get number () {
-    if (this._value instanceof BigNumber) {
+    if (this._value instanceof Decimal) {
       return this._value.toNumber();
     } else {
       return null;

+ 2 - 2
js/processor/store/storeObjectArrayAddressRef.js

@@ -1,5 +1,5 @@
 import { StoreObject } from './storeObject';
-import { BigNumber } from "bignumber.js";
+import Decimal from 'decimal.js';
 
 export class StoreObjectArrayAddressRef extends StoreObject {
 
@@ -21,7 +21,7 @@ export class StoreObjectArrayAddressRef extends StoreObject {
   }
 
   get number () {
-    if (this.value instanceof BigNumber) {
+    if (this.value instanceof Decimal) {
       return this.value.toNumber();
     } else {
       return null;

+ 2 - 2
js/processor/store/storeObjectRef.js

@@ -1,5 +1,5 @@
 import { StoreObject } from './storeObject';
-import { BigNumber } from "bignumber.js";
+import Decimal from 'decimal.js';
 
 export class StoreObjectRef extends StoreObject {
 
@@ -22,7 +22,7 @@ export class StoreObjectRef extends StoreObject {
   }
 
   get number () {
-    if (this.value instanceof BigNumber) {
+    if (this.value instanceof Decimal) {
       return this.value.toNumber();
     } else {
       return null;

+ 3 - 3
js/typeSystem/parsers.js

@@ -1,9 +1,9 @@
 import { LanguageService } from "../services/languageService";
 import { Types } from "./types";
-import { BigNumber } from 'bignumber.js'
+import Decimal from "decimal.js";
 
 export function toInt (str) {
-  return new BigNumber(str);
+  return new Decimal(str);
 }
 
 export function toString (str) {
@@ -20,7 +20,7 @@ export function toString (str) {
 }
 
 export function toReal (value) {
-  return new BigNumber(value);
+  return new Decimal(value);
 }
 
 export function toBool (str) {

+ 10 - 10
package-lock.json

@@ -1401,11 +1401,6 @@
       "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
       "dev": true
     },
-    "bignumber.js": {
-      "version": "7.2.1",
-      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
-      "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ=="
-    },
     "binary-extensions": {
       "version": "1.11.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
@@ -1419,9 +1414,9 @@
       "dev": true
     },
     "bluebird": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
-      "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==",
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz",
+      "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==",
       "dev": true
     },
     "bn.js": {
@@ -2147,6 +2142,11 @@
       "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
       "dev": true
     },
+    "decimal.js": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.0.1.tgz",
+      "integrity": "sha512-vklWB5C4Cj423xnaOtsUmAv0/7GqlXIgDv2ZKDyR64OV3OSzGHNx2mk4p/1EKnB5s70k73cIOOEcG9YzF0q4Lw=="
+    },
     "decode-uri-component": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
@@ -4504,8 +4504,8 @@
       }
     },
     "line-i18n": {
-      "version": "git+https://git.lcalion.com/lucascalion/line-i18n.git#d62cb2d82d27761758fe59376d22f4f5b191b51a",
-      "from": "git+https://git.lcalion.com/lucascalion/line-i18n.git"
+      "version": "git+https://git.lcalion.com/LInE/line-i18n.git#3d9c23059ba04f63fde6f2263798f66cdc29c889",
+      "from": "git+https://git.lcalion.com/LInE/line-i18n.git"
     },
     "load-json-file": {
       "version": "1.1.0",

+ 2 - 2
package.json

@@ -46,8 +46,8 @@
   },
   "dependencies": {
     "antlr4": "^4.7.1",
-    "bignumber.js": "^7.2.1",
+    "decimal.js": "^10.0.1",
     "jquery": "^3.3.1",
-    "line-i18n": "git+https://git.lcalion.com/lucascalion/line-i18n.git"
+    "line-i18n": "git+https://git.lcalion.com/LInE/line-i18n.git"
   }
 }

+ 32 - 0
tests/test67.spec.js

@@ -0,0 +1,32 @@
+import { IVProgParser } from './../js/ast/ivprogParser';
+import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { SemanticAnalyser } from "./../js/processor/semantic/semanticAnalyser";
+import { LanguageService } from '../js/services/languageService';
+
+describe('Command Do...While after the set timeout', function () {
+
+  IVProgProcessor.LOOP_TIMEOUT = 500;
+  let input = `programa {
+
+    funcao inicio() {
+      inteiro a = 0
+      faca {
+        a = a + 1
+      } enquanto(1 < 4)
+    }
+  }`;
+
+  const lexer = LanguageService.getCurrentLexer();
+
+  it(`should be forcedly killed `, function (done) {
+    const parser = new IVProgParser(input, lexer);
+    const semantic = new SemanticAnalyser(parser.parseTree());
+    const exec = new IVProgProcessor(semantic.analyseTree());
+    exec.interpretAST().then(_ => {
+      done("No error thrown");
+    }).catch( _ => {
+      expect(1).toEqual(1);
+      done();
+    });
+  });
+});