ソースを参照

Merge branch 'implicitTypeCast' of LInE/ivprog into master

Lucas de Souza 5 年 前
コミット
171a92eb07

+ 33 - 0
js/processor/compatibilityTable.js

@@ -1,6 +1,7 @@
 import { Types } from './../typeSystem/types';
 import { Operators } from './../ast/operators';
 import { MultiType } from '../typeSystem/multiType';
+import { Config } from '../util/config';
 
 function buildInfixAddTable () {
   const table = [[], [], [], []];
@@ -133,6 +134,13 @@ export function resultTypeAfterInfixOp (operator, leftExpressionType, rightExpre
         }
       }
       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)
@@ -141,17 +149,42 @@ export function resultTypeAfterInfixOp (operator, leftExpressionType, rightExpre
       if(leftExpressionType.isCompatible(rightExpressionType)) {
         return 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)) {
         return 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 resultType = infixMap.get(operator)[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;

+ 115 - 31
js/processor/ivprogProcessor.js

@@ -162,27 +162,36 @@ export class IVProgProcessor {
     return Promise.all(promises$).then(values => {
       for (let i = 0; i < values.length; i++) {
         const stoObj = values[i];
-        const exp = actualList[i]
+        const exp = actualList[i];
+        let shouldTypeCast = false;
         const formalParameter = formalList[i];
-        if(formalParameter.type.isCompatible(stoObj.type)) {
-          if(formalParameter.byRef && !stoObj.inStore) {
-            throw ProcessorErrorFactory.invalid_ref(funcName, exp.toString());
+        if(!formalParameter.type.isCompatible(stoObj.type)) {
+          if (Config.enable_type_casting && !formalParameter.byRef
+            && Store.canImplicitTypeCast(formalParameter.type, stoObj.type)) {
+              shouldTypeCast =  true;
+          } else {
+            throw ProcessorErrorFactory.invalid_parameter_type(funcName, exp.toString());
           }
+        }
 
-          if(formalParameter.byRef) {
-            let ref = null;
-            if (stoObj instanceof StoreObjectArrayAddress) {
-              ref = new StoreObjectArrayAddressRef(stoObj);
-            } else {
-              ref = new StoreObjectRef(stoObj.id, callerStore);
-            }
-            calleeStore.insertStore(formalParameter.id, ref);
+        if(formalParameter.byRef && !stoObj.inStore) {
+          throw ProcessorErrorFactory.invalid_ref(funcName, exp.toString());
+        }
+
+        if(formalParameter.byRef) {
+          let ref = null;
+          if (stoObj instanceof StoreObjectArrayAddress) {
+            ref = new StoreObjectArrayAddressRef(stoObj);
           } else {
-            let realValue = this.parseStoreObjectValue(stoObj);
-            calleeStore.insertStore(formalParameter.id, realValue);
+            ref = new StoreObjectRef(stoObj.id, callerStore);
           }
+          calleeStore.insertStore(formalParameter.id, ref);
         } else {
-          throw ProcessorErrorFactory.invalid_parameter_type(funcName, exp.toString());
+          let realValue = this.parseStoreObjectValue(stoObj);
+          if (shouldTypeCast) {
+            realValue = Store.doImplicitCasting(formalParameter.type, realValue);
+          }
+          calleeStore.insertStore(formalParameter.id, realValue);
         }
       }
       return calleeStore;
@@ -323,6 +332,7 @@ export class IVProgProcessor {
       const whileBlock = new Commands.CommandBlock([],
         cmd.commands.concat(increment));
       const forAsWhile = new Commands.While(condition, whileBlock);
+      forAsWhile.sourceInfo = cmd.sourceInfo;
       //END for -> while rewrite
       const newCmdList = [initCmd,forAsWhile];
       return this.executeCommands(store, newCmdList);
@@ -476,9 +486,20 @@ export class IVProgProcessor {
 
   executeAssign (store, cmd) {
     try {
+      const inStore = store.applyStore(cmd.id);
       const $value = this.evaluateExpression(store, cmd.expression);
       return $value.then( vl => {
         let realValue = this.parseStoreObjectValue(vl);
+        if(!inStore.type.isCompatible(realValue.type)) {
+          if(Config.enable_type_casting && Store.canImplicitTypeCast(inStore.type, vl.type)) {
+            realValue = Store.doImplicitCasting(inStore.type, realValue);
+          } else {
+            const stringInfo = inStore.type.stringInfo()
+            const info = stringInfo[0]
+            return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo));
+          }
+        }
+        
         store.updateStore(cmd.id, realValue) 
         return store;
       });
@@ -612,15 +633,24 @@ export class IVProgProcessor {
         return $value.then(vl => {
           let realValue = vl;
           if (vl !== null) {
+            if(!vl.type.isCompatible(cmd.type)) {
+              if(Config.enable_type_casting && Store.canImplicitTypeCast(cmd.type, vl.type)) {
+                realValue = Store.doImplicitCasting(cmd.type, realValue);
+              } else {
+                const stringInfo = typeInfo.type.stringInfo();
+                const info = stringInfo[0];
+                return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo));
+              }
+            }
             if(vl instanceof StoreObjectArrayAddress) {
               if(vl.type instanceof CompoundType) {
-                realValue = Object.assign(new StoreObjectArray(null,null,null), vl.refValue);
+                return Promise.reject(new Error("!!!Critical Error: Compatibility check failed, a Type accepts a CompoundType"))
               } else {
                 realValue = Object.assign(new StoreObject(null,null), vl.refValue);
               }
             }
           } else {
-            realValue = new StoreObject(cmd.type,0);
+            realValue = new StoreObject(cmd.type, 0);
           }
           realValue.readOnly = cmd.isConst;
           store.updateStore(cmd.id, realValue);
@@ -836,16 +866,21 @@ export class IVProgProcessor {
     const $left = this.evaluateExpression(store, infixApp.left);
     const $right = this.evaluateExpression(store, infixApp.right);
     return Promise.all([$left, $right]).then(values => {
+      let shouldImplicitCast = false;
       const left = values[0];
       const right = values[1];
-      const resultType = resultTypeAfterInfixOp(infixApp.op, left.type, right.type);
+      let resultType = resultTypeAfterInfixOp(infixApp.op, left.type, right.type);
       if (Types.UNDEFINED.isCompatible(resultType)) {
-        const stringInfoLeft = left.type.stringInfo();
-        const infoLeft = stringInfoLeft[0];
-        const stringInfoRight = right.type.stringInfo();
-        const infoRight = stringInfoRight[0];
-        return Promise.reject(ProcessorErrorFactory.invalid_infix_op_full(infixApp.op, infoLeft.type, infoLeft.dim,
-          infoRight.type,infoRight.dim,infixApp.sourceInfo));
+        if (Config.enable_type_casting && Store.canImplicitTypeCast(left.type, right.type)) {
+          shouldImplicitCast = true;
+        } else {
+          const stringInfoLeft = left.type.stringInfo();
+          const infoLeft = stringInfoLeft[0];
+          const stringInfoRight = right.type.stringInfo();
+          const infoRight = stringInfoRight[0];
+          return Promise.reject(ProcessorErrorFactory.invalid_infix_op_full(infixApp.op, infoLeft.type, infoLeft.dim,
+            infoRight.type,infoRight.dim,infixApp.sourceInfo));
+        }
       }
       let result = null;
       switch (infixApp.op.ord) {
@@ -880,55 +915,104 @@ export class IVProgProcessor {
           return new StoreObject(resultType, result);
         }
         case Operators.MOD.ord: {
-          result = left.value.modulo(right.value);
+          let leftValue = left.value;
+          let rightValue = right.value;
+          if(shouldImplicitCast) {
+            resultType = Types.INTEGER;
+            leftValue = leftValue.trunc();
+            rightValue = rightValue.trunc();
+          }
+          result = leftValue.modulo(rightValue);
           if(result.dp() > Config.decimalPlaces) {
             result = new Decimal(result.toFixed(Config.decimalPlaces));
           }
           return new StoreObject(resultType, result);
         }          
         case Operators.GT.ord: {
+          let leftValue = left.value;
+          let rightValue = right.value;
           if (Types.STRING.isCompatible(left.type)) {
             result = left.value.length > right.value.length;
           } else {
-            result = left.value.gt(right.value);
+            if (shouldImplicitCast) {
+              resultType = Types.BOOLEAN;
+              leftValue = leftValue.trunc();
+              rightValue = rightValue.trunc();
+            }
+            result = leftValue.gt(rightValue);
           }
           return new StoreObject(resultType, result);
         }
         case Operators.GE.ord: {
+          let leftValue = left.value;
+          let rightValue = right.value;
           if (Types.STRING.isCompatible(left.type)) {
             result = left.value.length >= right.value.length;
           } else {
-            result = left.value.gte(right.value);
+            if (shouldImplicitCast) {
+              resultType = Types.BOOLEAN;
+              leftValue = leftValue.trunc();
+              rightValue = rightValue.trunc();
+            }
+            result = leftValue.gte(rightValue);
           }
           return new StoreObject(resultType, result);
         }
         case Operators.LT.ord: {
+          let leftValue = left.value;
+          let rightValue = right.value;
           if (Types.STRING.isCompatible(left.type)) {
             result = left.value.length < right.value.length;
           } else {
-            result = left.value.lt(right.value);
+            if (shouldImplicitCast) {
+              resultType = Types.BOOLEAN;
+              leftValue = leftValue.trunc();
+              rightValue = rightValue.trunc();
+            }
+            result = leftValue.lt(rightValue);
           }
           return new StoreObject(resultType, result);
         }
         case Operators.LE.ord: {
+          let leftValue = left.value;
+          let rightValue = right.value;
           if (Types.STRING.isCompatible(left.type)) {
             result = left.value.length <= right.value.length;
           } else {
-            result = left.value.lte(right.value);
+            if (shouldImplicitCast) {
+              resultType = Types.BOOLEAN;
+              leftValue = leftValue.trunc();
+              rightValue = rightValue.trunc();
+            }
+            result = leftValue.lte(rightValue);
           }
           return new StoreObject(resultType, result);
         }
         case Operators.EQ.ord: {
+          let leftValue = left.value;
+          let rightValue = right.value;
           if (Types.INTEGER.isCompatible(left.type) || Types.REAL.isCompatible(left.type)) {
-            result = left.value.eq(right.value);
+            if (shouldImplicitCast) {
+              resultType = Types.BOOLEAN;
+              leftValue = leftValue.trunc();
+              rightValue = rightValue.trunc();
+            }
+            result = leftValue.eq(rightValue);
           } else {
             result = left.value === right.value;
           }
           return new StoreObject(resultType, result);
         }
         case Operators.NEQ.ord: {
+          let leftValue = left.value;
+          let rightValue = right.value;
           if (Types.INTEGER.isCompatible(left.type) || Types.REAL.isCompatible(left.type)) {
-            result = !left.value.eq(right.value);
+            if (shouldImplicitCast) {
+              resultType = Types.BOOLEAN;
+              leftValue = leftValue.trunc();
+              rightValue = rightValue.trunc();
+            }
+            result = !leftValue.eq(rightValue);
           } else {
             result = left.value !== right.value;
           }

+ 2 - 12
js/processor/lib/io.js

@@ -1,22 +1,12 @@
 import { StoreObject } from './../store/storeObject';
 import * as Commands from './../../ast/commands';
-import {toInt, toString, toBool, toReal} from './../../typeSystem/parsers';
+import {toInt, toString, toBool, toReal, convertToString} from './../../typeSystem/parsers';
 import { Types } from './../../typeSystem/types';
 
 export function createOutputFun () {
   const writeFunction = function (store, _) {
     const val = store.applyStore('p1');
-    if(val.type.isCompatible(Types.INTEGER)) {
-      this.output.sendOutput(val.value.toString());
-    } else if (val.type.isCompatible(Types.REAL)) {
-      if (val.value.dp() <= 0) {
-        this.output.sendOutput(val.value.toFixed(1));  
-      } else {
-        this.output.sendOutput(val.value.toString());
-      }
-    } else {
-      this.output.sendOutput(val.value);
-    }
+    this.output.sendOutput(convertToString(val.value, val.type));
     return Promise.resolve(store);
   }
   const block = new Commands.CommandBlock([], [new Commands.SysCall(writeFunction)]);

+ 26 - 3
js/processor/semantic/semanticAnalyser.js

@@ -8,6 +8,8 @@ import { resultTypeAfterInfixOp, resultTypeAfterUnaryOp } from '../compatibility
 import { Types } from '../../typeSystem/types';
 import { CompoundType } from '../../typeSystem/compoundType';
 import { MultiType } from '../../typeSystem/multiType';
+import { Config } from '../../util/config';
+import { Store } from '../store/store';
 
 export class SemanticAnalyser {
 
@@ -125,12 +127,13 @@ export class SemanticAnalyser {
           throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, declaration.sourceInfo);
         }
         this.insertSymbol(declaration.id, {id: declaration.id, type: declaration.type})
-      } else if(!declaration.type.isCompatible(resultType)) {
+      } else if((!declaration.type.isCompatible(resultType) && !Config.enable_type_casting)
+        || (Config.enable_type_casting && !Store.canImplicitTypeCast(declaration.type, resultType))) {
         const stringInfo = declaration.type.stringInfo();
         const info = stringInfo[0];
         throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, declaration.sourceInfo);
       } else {
-        this.insertSymbol(declaration.id, {id: declaration.id, type: declaration.type})
+        this.insertSymbol(declaration.id, {id: declaration.id, type: declaration.type});
       }
     }
   }
@@ -422,7 +425,8 @@ export class SemanticAnalyser {
         this.evaluateArrayLiteral(cmd.id, typeInfo.lines, typeInfo.columns, typeInfo.type, exp);
       } else {
         const resultType = this.evaluateExpressionType(exp);
-        if(!resultType.isCompatible(typeInfo.type)) {
+        if((!resultType.isCompatible(typeInfo.type) && !Config.enable_type_casting)
+          || (Config.enable_type_casting && !Store.canImplicitTypeCast(typeInfo.type, resultType))) {
           const stringInfo = typeInfo.type.stringInfo();
           const info = stringInfo[0];
           throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo);
@@ -504,13 +508,32 @@ export class SemanticAnalyser {
           }
         }
         if(shared <= 0) {
+          if(Config.enable_type_casting && !formalParam.byRef) {
+            if(resultType.isCompatible(Types.INTEGER) || resultType.isCompatible(Types.REAL)) {
+              if(formalParam.type.isCompatible(Types.INTEGER) || formalParam.type.isCompatible(Types.REAL)) {
+                continue;
+              }
+            }
+          }
           throw ProcessorErrorFactory.invalid_parameter_type_full(id, param.toString(), param.sourceInfo);
         }
       } else if (resultType instanceof MultiType) {
         if(!resultType.isCompatible(formalParam.type)) {
+          if(Config.enable_type_casting && !formalParam.byRef) {
+            if(resultType.isCompatible(Types.INTEGER) || resultType.isCompatible(Types.REAL)) {
+              if(formalParam.type.isCompatible(Types.INTEGER) || formalParam.type.isCompatible(Types.REAL)) {
+                continue;
+              }
+            }
+          }
           throw ProcessorErrorFactory.invalid_parameter_type_full(id, param.toString(), param.sourceInfo);
         }
       } else if(!formalParam.type.isCompatible(resultType)) {
+        if(Config.enable_type_casting && !formalParam.byRef) {
+          if (Store.canImplicitTypeCast(formalParam.type, resultType)) {
+            continue;
+          }
+        }
         throw ProcessorErrorFactory.invalid_parameter_type_full(id, param.toString(), param.sourceInfo);
       }
 

+ 22 - 0
js/processor/store/store.js

@@ -1,7 +1,29 @@
 import { Modes } from './../modes';
+import { Types } from "./../../typeSystem/types";
+import { StoreObject } from './storeObject';
 
 export class Store {
 
+  static canImplicitTypeCast (castType, sourceType) {
+    if (castType.isCompatible(Types.INTEGER) || castType.isCompatible(Types.REAL)) {
+      if (sourceType.isCompatible(Types.INTEGER) || sourceType.isCompatible(Types.REAL)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  static doImplicitCasting (castType, stoObj) {
+    if(!Store.canImplicitTypeCast(castType, stoObj.type)) {
+      throw new Error("!!!Critical error: attempted to type cast invalid types");
+    }
+    if(Types.INTEGER.isCompatible(castType)) {
+      return new StoreObject(castType, stoObj.value.trunc());
+    } else {
+      return new StoreObject(castType, stoObj.value);
+    }
+  }
+
   constructor(name) {
     this.name = name;
     this.store = {};

+ 1 - 1
js/typeSystem/multiType.js

@@ -28,7 +28,7 @@ export class MultiType extends Type {
     if(another instanceof Type) {
       for (let i = 0; i < this.types.length; i++) {
         const t = this.types[i];
-        if (t.isCompatible(another)) {
+        if (another.isCompatible(t)) {
           return true;
         }
       }

+ 7 - 7
js/typeSystem/parsers.js

@@ -47,20 +47,20 @@ function convertBoolToString (bool) {
   }
 }
 
-export function convertToString(stoObj, type) {
+export function convertToString(value, type) {
   switch (type.ord) {
     case Types.INTEGER.ord:
-      return stoObj.toString();
+      return value.toString();
     case Types.REAL.ord: {
-      if (stoObj.dp() <= 0) {
-        return stoObj.toFixed(1);  
+      if (value.dp() <= 0) {
+        return value.toFixed(1);  
       } else {
-        return stoObj.toNumber();
+        return value.toNumber();
       }
     }
     case Types.BOOLEAN.ord:
-      return convertBoolToString(stoObj);
+      return convertBoolToString(value);
     default:
-      return stoObj;
+      return value;
   }
 }

+ 3 - 0
js/typeSystem/type.js

@@ -1,3 +1,6 @@
+import { Config } from "../util/config";
+import { BaseTypes } from "./baseTypes";
+
 export class Type {
 
   constructor(baseType) {

+ 1 - 0
js/util/config.js

@@ -5,6 +5,7 @@ class ConfigObject {
     this.decimalPlaces = 5;
     this.intConvertRoundMode = 2;
     this.default_lang = 'pt';
+    this.enable_type_casting = true;
   }
 
   setConfig (opts) {

+ 20 - 0
tests/testTypeCast.spec.js

@@ -0,0 +1,20 @@
+import { IVProgParser } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
+
+describe('When implicit type casting is enabled', function () {
+
+  let input = `programa {
+
+    funcao inicio() {
+      real v = 5
+    }
+  }`;
+
+  const lexer = LanguageService.getCurrentLexer();
+
+  it(`should coerce integer into real`, function () {
+    const parser = new IVProgParser(input, lexer);
+    const fun = parser.parseTree.bind(parser);
+    expect(fun).not.toThrow();
+  });
+});