ソースを参照

Implement array element implicit type conversion

Remove type checking inside StoreObjectArray
Lucas de Souza 4 年 前
コミット
bbba0d3935

+ 87 - 27
js/processor/ivprogProcessor.js

@@ -18,6 +18,8 @@ import { Config } from '../util/config';
 import Decimal from 'decimal.js';
 import { ProcessorErrorFactory } from './error/processorErrorFactory';
 import { RuntimeError } from './error/runtimeError';
+import { Type } from '../typeSystem/type';
+import { Literal } from '../ast/expressions/literal';
 
 export class IVProgProcessor {
 
@@ -517,6 +519,7 @@ export class IVProgProcessor {
         column = columnSO.number;
       }
       const value = this.parseStoreObjectValue(results[2]);
+      let actualValue = value;
       if (line >= mustBeArray.lines) {
         if(mustBeArray.isVector) {
           return Promise.reject(ProcessorErrorFactory.vector_line_outbounds_full(cmd.id, line, mustBeArray.lines, cmd.sourceInfo));
@@ -539,33 +542,49 @@ export class IVProgProcessor {
 
       const newArray = Object.assign(new StoreObjectArray(null,null,null), mustBeArray);
       if (column !== null) {
-        if (value.type instanceof ArrayType || !newArray.type.canAccept(value.type)) {
-          const type = mustBeArray.type.innerType;
-          const stringInfo = type.stringInfo()
-          const info = stringInfo[0]
-          return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo));
+        if (!newArray.type.canAccept(value.type)) {
+          if(!Config.enable_type_casting || !Store.canImplicitTypeCast(mustBeArray.type.innerType, value.type)) {
+            const type = mustBeArray.type.innerType;
+            const stringInfo = type.stringInfo();
+            const info = stringInfo[0];
+            const exp = cmd.expression.toString();
+            return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo));
+          }
+          actualValue = Store.doImplicitCasting(mustBeArray.type.innerType, value);
         }
-        newArray.value[line].value[column] = value;
+        newArray.value[line].value[column] = actualValue;
         store.updateStore(cmd.id, newArray);
       } else {
-        if((mustBeArray.columns !== null && value.type instanceof ArrayType) || !newArray.type.canAccept(value.type)) {
-          const type = mustBeArray.type;
-          const stringInfo = type.stringInfo()
-          const info = stringInfo[0]
-          const exp = cmd.expression.toString()
-          return Promise.reject(ProcessorErrorFactory.incompatible_types_array_full(exp,info.type, info.dim-1, cmd.sourceInfo));
+        if(!newArray.type.canAccept(value.type)) {
+          if(!Config.enable_type_casting || !Store.canImplicitTypeCast(mustBeArray.type.innerType, value.type)) {
+            const type = mustBeArray.type;
+            const stringInfo = type.stringInfo();
+            const info = stringInfo[0];
+            const exp = cmd.expression.toString();
+            return Promise.reject(ProcessorErrorFactory.incompatible_types_array_full(exp,info.type, info.dim-1, cmd.sourceInfo));
+          }
+          actualValue = Store.doImplicitCasting(mustBeArray.type.innerType, value);
         }
-        newArray.value[line] = value;
+        newArray.value[line] = actualValue;
         store.updateStore(cmd.id, newArray);
       }
       return store;
     });
   }
 
+  /**
+   * 
+   * @param {Store} store 
+   * @param {Commands.Declaration} cmd 
+   */
   executeDeclaration (store, cmd) {
     try {
-      const $value = this.evaluateExpression(store, cmd.initial);
+      let $value = Promise.resolve(null);
       if(cmd instanceof Commands.ArrayDeclaration) {
+        if(cmd.initial !== null) {
+          // array can only be initialized by a literal....
+          $value = this.evaluateArrayLiteral(store, cmd.initial, cmd.type);
+        }
         const $lines = this.evaluateExpression(store, cmd.lines);
         const $columns = cmd.columns === null ? null: this.evaluateExpression(store, cmd.columns);
         return Promise.all([$lines, $columns, $value]).then(values => {
@@ -614,6 +633,9 @@ export class IVProgProcessor {
         });
         
       } else {
+        if(cmd.initial !== null) {
+          $value = this.evaluateExpression(store, cmd.initial);
+        }
         const temp = new StoreObject(cmd.type, null);
         store.insertStore(cmd.id, temp);
         return $value.then(vl => {
@@ -667,7 +689,7 @@ export class IVProgProcessor {
     } else if (exp instanceof Expressions.StringLiteral) {
       return this.evaluateLiteral(store, exp);
     } else if (exp instanceof Expressions.ArrayLiteral) {
-      return this.evaluateArrayLiteral(store, exp);
+      return Promise.reject(new Error("Internal Error: The system should not eval an array literal."))
     } else if (exp instanceof Expressions.FunctionCall) {
       return this.evaluateFunctionCall(store, exp);
     }
@@ -696,13 +718,22 @@ export class IVProgProcessor {
     });
   }
 
-  evaluateArrayLiteral (store, exp) {
+  /**
+   * 
+   * @param {Store} store 
+   * @param {Expressions.ArrayLiteral} exp 
+   * @param {ArrayType} type 
+   */
+  evaluateArrayLiteral (store, exp, type) {
     const errorHelperFunction = (validationResult, exp) => {
       const errorCode = validationResult[0];
+      let expectedColumns = null;
+      let actualColumns = null;
       switch(errorCode) {
         case StoreObjectArray.WRONG_COLUMN_NUMBER: {
-          const columnValue = validationResult[1];
-          return Promise.reject(ProcessorErrorFactory.invalid_array_literal_column_full(arr.columns, columnValue, exp.sourceInfo));
+          expectedColumns = validationResult[1];
+          actualColumns = validationResult[2];
+          return Promise.reject(ProcessorErrorFactory.invalid_array_literal_column_full(expectedColumns, actualColumns, exp.sourceInfo));
         }
         case StoreObjectArray.WRONG_LINE_NUMBER: {
           const lineValue = validationResult[1];
@@ -719,13 +750,13 @@ export class IVProgProcessor {
             line = validationResult[1];
             strExp = exp.value[line].toString()
           }
+          // TODO - fix error message
           return Promise.reject(ProcessorErrorFactory.invalid_array_literal_type_full(strExp, exp.sourceInfo));            }
       }
     };
     if(!exp.isVector) {
-      const $matrix = this.evaluateMatrix(store, exp.value);
+      const $matrix = this.evaluateMatrix(store, exp.value, type);
       return $matrix.then(list => {
-        const type = new ArrayType(list[0].type.innerType, 2);
         const arr = new StoreObjectArray(type, list.length, list[0].lines, list);
         const checkResult = arr.isValid;
         if(checkResult.length == 0)
@@ -735,7 +766,7 @@ export class IVProgProcessor {
         }
       });
     } else {
-      return this.evaluateVector(store, exp.value).then(list => {
+      return this.evaluateVector(store, exp.value, type).then(list => {
         const type = new ArrayType(list[0].type, 1);
         const stoArray = new StoreObjectArray(type, list.length, null, list);
         const checkResult = stoArray.isValid;
@@ -748,16 +779,45 @@ export class IVProgProcessor {
     }
   }
 
-  evaluateVector (store, exps) {
-    return Promise.all(exps.map( exp => this.evaluateExpression(store, exp)));
+  /**
+   * Evalautes a list of literals and expression composing the vector
+   * @param {Store} store 
+   * @param {Literal[]} exps 
+   * @param {ArrayType} type
+   * @returns {Promise<StoreObject[]>} store object list
+   */
+  evaluateVector (store, exps, type) {
+    const actual_values = Promise.all(exps.map( exp => this.evaluateExpression(store, exp)));
+    return actual_values.then( values => {
+      return values.map((v, index) => {
+        if(!type.canAccept(v.type)) {
+          if (!Config.enable_type_casting || !Store.canImplicitTypeCast(type.innerType, v.type)) {
+            const stringInfo = v.type.stringInfo();
+            const info = stringInfo[0];
+            const exp_str = exps[index].toString();
+            // TODO - fix error message
+            throw ProcessorErrorFactory.invalid_array_literal_type_full(exp_str, exps[index].sourceInfo);
+          }
+          const new_value = Store.doImplicitCasting(type.innerType, v);
+          return new_value;
+        }
+        return v;
+      });
+    });
   }
 
-  evaluateMatrix (store, exps) {
+  /**
+   * Evaluates a list of array literals composing the matrix
+   * @param {Store} store 
+   * @param {Expressions.ArrayLiteral[]} exps 
+   * @param {ArrayType} type 
+   */
+  evaluateMatrix (store, exps, type) {
     return Promise.all(exps.map( vector => {
-      const $vector = this.evaluateVector(store, vector.value)
+      const vec_type = new ArrayType(type.innerType, 1);
+      const $vector = this.evaluateVector(store, vector.value, vec_type);
       return $vector.then(list => {
-        const type = new ArrayType(list[0].type, 1);
-        return new StoreObjectArray(type, list.length, null, list)
+        return new StoreObjectArray(vec_type, list.length, null, list)
       });
     } ));
   }

+ 22 - 11
js/processor/semantic/semanticAnalyser.js

@@ -276,7 +276,7 @@ export class SemanticAnalyser {
       this.evaluateVectorLiteralType(literal, type);
     } else {
       // TODO matrix type check
-      for(let i = 0; i < literal.columns; ++i) {
+      for(let i = 0; i < literal.lines; ++i) {
         const line_literal = literal.value[i];
         this.evaluateVectorLiteralType(line_literal, new ArrayType(type.innerType, 1));
       }
@@ -370,7 +370,7 @@ export class SemanticAnalyser {
       if (typeInfo.columns === null && columnExp !== null) {
         throw ProcessorErrorFactory.invalid_matrix_access_full(cmd.id, cmd.sourceInfo);
       } else if (!typeInfo.type.isVector && columnExp == null) {
-        throw new Error("Cannot assign to matrix line");
+        throw new Error("Cannot assign to matrix line");      
       } else if (columnExp !== null) {
         const columnType = this.evaluateExpressionType(columnExp);
         if (!columnType.isCompatible(Types.INTEGER)) {
@@ -401,17 +401,28 @@ export class SemanticAnalyser {
       if(typeInfo === null) {
         throw ProcessorErrorFactory.symbol_not_found_full(cmd.id, cmd.sourceInfo);
       }
-      if(typeInfo.type instanceof ArrayType) {
-        // TODO better error message
-        throw new Error("Cannot assign to matrix/vector");
-      }
       const exp = cmd.expression;
       const exp_type = this.evaluateExpressionType(exp);
       if(exp_type instanceof ArrayType) {
-        // TODO better error message
-        throw new Error("Cannot assign a matrix/vector");
-      }
-      if(!exp_type.isCompatible(typeInfo.type)) {
+        if(!(typeInfo.type instanceof ArrayType)) {
+          // TODO better error message
+          throw new Error("Cannot assign an array to a non-array variable ");
+        } 
+        // Both are arrays...
+        // if both don't have same dimensions and type, cannot perform assignment
+        if(!exp_type.isCompatible(typeInfo.type)) {
+          if(exp_type.dimensions === typeInfo.type.dimensions && !exp_type.innerType.isCompatible(typeInfo.type.innerType)) {
+            if(!Config.enable_type_casting || !Store.canImplicitTypeCast(typeInfo.type.innerType, exp_type.innerType)) {
+              const stringInfo = typeInfo.type.stringInfo();
+              const info = stringInfo[0];
+              throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo);
+            }
+          } else {
+            // TODO better error message
+            throw new Error("Cannot assign a vector to matrix or matrix to vector");
+          }
+        }
+      } else if(!exp_type.isCompatible(typeInfo.type)) {
         if(!Config.enable_type_casting || !Store.canImplicitTypeCast(typeInfo.type, exp_type)) {
           const stringInfo = typeInfo.type.stringInfo();
           const info = stringInfo[0];
@@ -529,7 +540,7 @@ export class SemanticAnalyser {
   }
 
   evaluateVectorLiteralType (literal, type) {
-    for(let i = 0; i < literal.value; i+=1) {
+    for(let i = 0; i < literal.value.length; i+=1) {
       const exp = literal.value[i];
       const expType = this.evaluateExpressionType(exp);
       let compatible = false;

+ 26 - 29
js/processor/store/storeObjectArray.js

@@ -29,14 +29,11 @@ export class StoreObjectArray extends StoreObject {
   }
 
   isCompatible (another) {
-    if(another instanceof StoreObject) {
-      if(((this.lines === -1 && another.lines > 0) ||
-        (this.lines === another.lines))) {
-          if ((this.columns === -1 && another.columns > 0) ||
-            (this.columns === another.columns)) {
-              return super.isCompatible(another);
-          }
-        }
+    if(another instanceof StoreObjectArray) {
+      if((this.isVector && another.isVector) ||
+        (!this.isVector && !another.isVector)) {
+          return super.isCompatible(another);
+      }
     }
     return false;
   }
@@ -51,29 +48,29 @@ export class StoreObjectArray extends StoreObject {
         if(this.value.length !== this.lines) {
           return [StoreObjectArray.WRONG_LINE_NUMBER, this.value.length];;
         }
-        const mustBeNull = this.value.find(v => !this.type.canAccept(v.type) );
-        if(!!mustBeNull) {
-          return [StoreObjectArray.WRONG_TYPE, this.value.indexOf(mustBeNull)];;
-        }
+        // const mustBeNull = this.value.find(v => !this.type.canAccept(v.type) );
+        // if(!!mustBeNull) {
+        //   return [StoreObjectArray.WRONG_TYPE, this.value.indexOf(mustBeNull)];;
+        // }
+        return [];
+      } else if(this.lines !== this.value.length) {
+        return [StoreObjectArray.WRONG_LINE_NUMBER, this.value.length];
       }
-      return [];
-    } else {
-    if(this.lines !== this.value.length) {
-      return [StoreObjectArray.WRONG_LINE_NUMBER, this.value.length];
-    }
-    for (let i = 0; i < this.lines; i++) {
-      for (let j = 0; j < this.columns; j++) {
-        const arr = this.value[i];
-        if(arr.length !== this.columns) {
-          return [StoreObjectArray.WRONG_COLUMN_NUMBER, arr.length];
+      for (let i = 0; i < this.lines; i += 1) {
+        const thisRef = this;
+        const arrayObject = this.value[i];
+        if(arrayObject.lines !== this.columns) {
+          return [StoreObjectArray.WRONG_COLUMN_NUMBER, this.columns, arrayObject.lines];
         }
-        const mustBeNull = arr.find(v => !this.type.canAccept(v.type) );
-        if(!!mustBeNull) {
-          return [StoreObjectArray.WRONG_TYPE, i, arr.indexOf(mustBeNull)];
-        }            
+        // const mustBeNull = arrayObject.value.find(v => !arrayObject.type.canAccept(v.type) );
+        // if(!!mustBeNull) {
+        //   console.log(mustBeNull);
+        //   console.log(thisRef.type.canAccept(mustBeNull));
+        //   console.log(thisRef.type);
+        //   return [StoreObjectArray.WRONG_TYPE, i, arrayObject.value.indexOf(mustBeNull)];
+        // }
       }
-    }
-      return [];
-    }
+    } 
+    return [];
   }
 }

+ 1 - 1
js/typeSystem/array_type.js

@@ -32,7 +32,7 @@ export class ArrayType extends Type {
 
   canAccept (another) {
     if(another instanceof ArrayType) {
-      return this.dimensions > another.dimensions && this.innerType.isCompatible(another.innerType);
+      return false;// return this.dimensions > another.dimensions && this.innerType.isCompatible(another.innerType);
     } else {
       return this.dimensions == 1 && this.innerType.isCompatible(another);
     }