Bladeren bron

Implement a better error messages for incompatible type and vector and matrix literals with wrong sizes

Lucas de Souza 4 jaren geleden
bovenliggende
commit
aff48e0cab

+ 8 - 4
i18n/pt/error.json

@@ -69,13 +69,13 @@
   "array_dimension_not_int": "As dimensões de um vetor/matriz devem ser do tipo inteiro.",
   "array_dimension_not_positive_full": "As dimensões de um vetor/matriz na linha: $0 devem ser valores positivos.",
   "array_dimension_not_positive": "As dimensões de um vetor/matriz devem ser valores positivos.",
-  "incompatible_types_full": "O tipo $0 não é compatível com o tipo resultante da expressão na linha $1",
-  "incompatible_types": "O tipo $0 não é compatível com o tipo resultante da expressão fornecida.",
+  "incompatible_types_full": "Era esperado um $0 mas a expressão $1 na linha $2 resulta em $3.",
+  "incompatible_types": "Era esperado um $0 mas a expressão $1 resulta em $2.",
   "incompatible_types_array_full": "A expressão $0 é incompatível com o tipo $1 na linha: $2, coluna: $3.",
   "incompatible_types_array": "A expressão $0 é incompatível com o tipo $1.",
   "invalid_case_type_full": "O caso $0 na linha $1 é incompatível com o tipo $2.",
   "invalid_case_type": "O caso $0 é incompatível com o tipo $1.",
-  "function_no_return": "A função $0 não possui um retorno acessível. Toda função deve ter ao menos um retorno no seu corpo principal.",
+  "function_no_return": "A função $0 não possui um comando de devolução acessível. Toda função deve ter ao menos um comando 'devolva' no seu corpo principal.",
   "invalid_array_literal_type_full": "Erro na linha $0: a expressão $1 não resulta em um tipo compatível.",
   "invalid_array_literal_type": "A expressão $0 não resulta em um tipo compatível.",
   "invalid_array_literal_line_full": "Erro na linha $0: esperava-se $1 linhas mas encontrou $2.",
@@ -117,5 +117,9 @@
   "invalid_matrix_assignment_full": "Erro na linha $0: Só se pode atribuir uma matriz a outra desde que ambas possuam dimensões de mesmo tamanho. $1 tem $2 linhas e $3 colunas, enquanto $4 possui $5 linhas e $6 colunas!",
   "invalid_matrix_assignment": "Só se pode atribuir uma matriz a outra desde que ambas possuam dimensões de mesmo tamanho. $0 tem $1 linhas e $2 colunas, enquanto $3 possui $4 linhas e $5 colunas!",
   "matrix_to_vector_attr":  "Erro na linha $0: $1 representa uma matriz e não pode ser atribuída ao vetor $2.",
-  "vector_to_matrix_attr":  "Erro na linha $0: $1 representa um vetor e não pode ser atribuído a matriz $2."
+  "vector_to_matrix_attr":  "Erro na linha $0: $1 representa um vetor e não pode ser atribuído a matriz $2.",
+  "invalid_matrix_index_assign_full": "Erro na linha $0: A posição $1 da matriz $2 aceita apenas vetores de tamanho $3, mas $4 tem tamanho $5",
+  "invalid_matrix_index_assign": "A linha $0 da matriz $1 aceita apenas vetores de tamanho $2, mas $3 tem tamanho $4",
+  "invalid_number_elements_vector": "Esperava-se por $0 elementos na linha $1 mas a expressão $2 possui $3 elementos.",
+  "invalid_number_lines_matrix": "Esperava-se por uma matriz com $0 linhas na linha $1 mas a expressão $2 possui $3 linhas"
 }

+ 30 - 6
js/processor/error/processorErrorFactory.js

@@ -64,16 +64,18 @@ export const ProcessorErrorFactory  = Object.freeze({
   unknown_command: ()=> {
     return createRuntimeError("unknown_command");
   },
-  incompatible_types_full: (type, dim, sourceInfo) => {
-    if(sourceInfo) {
-      const context = [LocalizedStrings.translateType(type, dim), sourceInfo.line, sourceInfo.column];
+  incompatible_types_full: (left_type, left_dim, right_type, right_dim, exp, source_info) => {
+    if(source_info) {
+      const context = [LocalizedStrings.translateType(left_type, left_dim), exp, source_info.line,
+        LocalizedStrings.translateType(right_type, right_dim)];
       return createSemanticError("incompatible_types_full", context);
     } else {
-      return ProcessorErrorFactory.incompatible_types(type, dim);
+      return ProcessorErrorFactory.incompatible_types(left_type, left_dim, right_type, right_dim, exp);
     }
   },
-  incompatible_types: (type, dim) => {
-    const context = [LocalizedStrings.translateType(type, dim)];
+  incompatible_types: (left_type, left_dim, right_type, right_dim, exp) => {
+    const context = [LocalizedStrings.translateType(left_type, left_dim), exp,
+      LocalizedStrings.translateType(right_type, right_dim)];
     return createSemanticError("incompatible_types", context);
   },
   incompatible_types_array_full: (exp, type, dim, sourceInfo) => {
@@ -453,5 +455,27 @@ export const ProcessorErrorFactory  = Object.freeze({
     // SourceInfo have to be valid...
     const context = [source_info.line, right_id, left_id];
     return createSemanticError("vector_to_matrix_attr", context);
+  },
+  invalid_matrix_index_assign_full: (mat_id, mat_line, mat_size, exp, exp_size, source_info) => {
+    if(source_info){
+      const context = [source_info.line, mat_line, mat_id, mat_size, exp, exp_size];
+      return createRuntimeError("invalid_matrix_index_assign_full", context);
+    } else {
+      return ProcessorErrorFactory.invalid_matrix_index_assign(mat_id, mat_line, mat_size, exp, exp_size)
+    }
+  },
+  invalid_matrix_index_assign: (mat_id, mat_line, mat_size, exp, exp_size) =>{
+    const context = [mat_line, mat_id, mat_size, exp, exp_size];
+    return createRuntimeError("invalid_matrix_index_assign", context);
+  },
+  invalid_number_elements_vector: (expected_num, exp, actual_num, source_info) => {
+    // SourceInfo have to be valid...
+    const context = [expected_num, source_info.line, exp, actual_num];
+    return createRuntimeError("invalid_number_elements_vector", context);
+  },
+  invalid_number_lines_matrix: (expected_num, exp, actual_num, source_info) => {
+    // SourceInfo have to be valid...
+    const context = [expected_num, source_info.line, exp, actual_num];
+    return createRuntimeError("invalid_number_lines_matrix", context);
   }
 });

+ 35 - 29
js/processor/ivprogProcessor.js

@@ -90,7 +90,7 @@ export class IVProgProcessor {
     return this.initGlobal().then( _ => {
       const mainFunc = this.findMainFunction();
       if(mainFunc === null) {
-        throw ProcessorErrorFactory.main_missing();
+        return Promise.reject(ProcessorErrorFactory.main_missing())
       }
       return this.runFunction(mainFunc, [], this.globalStore);
     });
@@ -98,7 +98,7 @@ export class IVProgProcessor {
 
   initGlobal () {
     if(!this.checkContext(Context.BASE)) {
-      throw ProcessorErrorFactory.invalid_global_var();
+      return Promise.reject(ProcessorErrorFactory.invalid_global_var())
     }
     return this.executeCommands(this.globalStore, this.ast.global);
   }
@@ -145,7 +145,7 @@ export class IVProgProcessor {
       LanguageDefinedFunction.getMainFunctionName() : callee_store.name;
 
     if (formal_params.length != effective_params.length) {
-      throw ProcessorErrorFactory.invalid_parameters_size(funcName, formal_params.length, effective_params.length);
+      return Promise.reject(ProcessorErrorFactory.invalid_parameters_size(funcName, formal_params.length, effective_params.length))
     }
     const promises$ = effective_params.map(actual_param => this.evaluateExpression(caller_store, actual_param));
     return Promise.all(promises$).then(values => {
@@ -161,12 +161,12 @@ export class IVProgProcessor {
             && Store.canImplicitTypeCast(formalParameter.type, sto_value.type)) {
               shouldTypeCast =  true;
           } else {
-            throw ProcessorErrorFactory.invalid_parameter_type(funcName, exp.toString());
+            return Promise.reject(ProcessorErrorFactory.invalid_parameter_type(funcName, exp.toString()))
           }
         }
 
         if(formalParameter.byRef && !sto_value.inStore()) {
-          throw ProcessorErrorFactory.invalid_ref(funcName, exp.toString());
+          return Promise.reject(ProcessorErrorFactory.invalid_ref(funcName, exp.toString()))
         }
 
         if(formalParameter.byRef) {
@@ -247,7 +247,7 @@ export class IVProgProcessor {
     } else if (cmd instanceof Commands.SysCall) {
       return this.executeSysCall(store, cmd);
     } else {
-      throw ProcessorErrorFactory.unknown_command(cmd.sourceInfo);
+      return Promise.reject(ProcessorErrorFactory.unknown_command(cmd.sourceInfo))
     }
   }
 
@@ -479,7 +479,7 @@ export class IVProgProcessor {
     try {
       const inStore = store.applyStore(cmd.id);
       if(inStore.isConst) {
-        throw ProcessorErrorFactory.invalid_const_assignment_full(cmd.id, cmd.sourceInfo);
+        return Promise.reject(ProcessorErrorFactory.invalid_const_assignment_full(cmd.id, cmd.sourceInfo))
       }
       const $value = this.evaluateExpression(store, cmd.expression);
       return $value.then( vl => {
@@ -490,7 +490,10 @@ export class IVProgProcessor {
           } else {
             const stringInfo = inStore.type.stringInfo()
             const info = stringInfo[0]
-            return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo));
+            const exp_type_string_info = vl.type.stringInfo();
+            const exp_type_info = exp_type_string_info[0];
+            const exp = cmd.expression.toString();
+            return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, exp_type_info.type, exp_type_info.dim,  exp, cmd.sourceInfo));
           }
         }
 
@@ -518,7 +521,7 @@ export class IVProgProcessor {
     const mustBeArray = store.applyStore(cmd.id);
     let used_dims = 0;
     if(mustBeArray.isConst) {
-      throw ProcessorErrorFactory.invalid_const_assignment_full(cmd.id, cmd.sourceInfo);
+      return Promise.reject(ProcessorErrorFactory.invalid_const_assignment_full(cmd.id, cmd.sourceInfo))
     }
     if(!(mustBeArray.type instanceof ArrayType)) {
       return Promise.reject(ProcessorErrorFactory.invalid_array_access_full(cmd.id, cmd.sourceInfo));
@@ -548,7 +551,7 @@ export class IVProgProcessor {
           return Promise.reject(ProcessorErrorFactory.matrix_line_outbounds_full(cmd.id, line, mustBeArray.lines, cmd.sourceInfo));
         }
       } else if (line < 0) {
-        throw ProcessorErrorFactory.array_dimension_not_positive_full(cmd.sourceInfo);
+        return Promise.reject(ProcessorErrorFactory.array_dimension_not_positive_full(cmd.sourceInfo))
       }
       if (column != null && mustBeArray.columns === 0 ){
         return Promise.reject(ProcessorErrorFactory.vector_not_matrix_full(cmd.id, cmd.sourceInfo));
@@ -557,7 +560,7 @@ export class IVProgProcessor {
         if (column >= mustBeArray.columns) {
           return Promise.reject(ProcessorErrorFactory.matrix_column_outbounds_full(cmd.id, column,mustBeArray.columns, cmd.sourceInfo));
         } else if (column < 0) {
-          throw ProcessorErrorFactory.array_dimension_not_positive_full(cmd.sourceInfo);
+          return Promise.reject(ProcessorErrorFactory.array_dimension_not_positive_full(cmd.sourceInfo))
         }
       }
 
@@ -566,8 +569,10 @@ export class IVProgProcessor {
           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));
+          const exp_type_string_info = value.type.stringInfo();
+          const exp_type_info = exp_type_string_info[0];
+          const exp = cmd.expression.toString();
+          return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, exp_type_info.type, exp_type_info.dim,  exp, cmd.sourceInfo));
         }
         actualValue = Store.doImplicitCasting(mustBeArray.type.innerType, value);
       }
@@ -575,14 +580,14 @@ export class IVProgProcessor {
       const current_value = mustBeArray.getAt(line, column);
       if(current_value instanceof ArrayStoreValue) {
         if(current_value.lines !== actualValue.lines || current_value.columns !== actualValue.columns){
-          // TODO better error message
-          throw new Error("exp exceeds the number of elements of the vector");
+          const exp = cmd.expression.toString();
+          return Promise.reject(ProcessorErrorFactory.invalid_matrix_index_assign_full(cmd.id, line, current_value.lines, exp, actualValue.lines, cmd.sourceInfo))
         }
       }
 
       // mustBeArray.setAt(actualValue, line, column);
       // store.updateStore(cmd.id, mustBeArray);
-      return store.updateStoreArray(cmd.id,actualValue, line, column);
+      return store.updateStoreArray(cmd.id, actualValue, line, column);
     });
   }
 
@@ -610,7 +615,10 @@ export class IVProgProcessor {
               } else {
                 const stringInfo = vl.type.stringInfo();
                 const info = stringInfo[0];
-                return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo));
+                const exp_type_string_info = vl.type.stringInfo();
+                const exp_type_info = exp_type_string_info[0];
+                const exp = cmd.expression.toString();
+                return Promise.reject(ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, exp_type_info.type, exp_type_info.dim,  exp, cmd.sourceInfo));
               }
             }
             temp = new StoreValue(cmd.type, realValue.get(), null, cmd.isConst);
@@ -640,7 +648,7 @@ export class IVProgProcessor {
       }
       const line = line_sv.get().toNumber();
       if(line < 0) {
-        throw ProcessorErrorFactory.array_dimension_not_positive_full(cmd.sourceInfo);
+        return Promise.reject(ProcessorErrorFactory.array_dimension_not_positive_full(cmd.sourceInfo));
       }
       let column = null
       if (column_sv !== null) {
@@ -649,7 +657,7 @@ export class IVProgProcessor {
         }
         column = column_sv.get().toNumber();
         if(column < 0) {
-          throw ProcessorErrorFactory.array_dimension_not_positive_full(cmd.sourceInfo);
+          return Promise.reject(ProcessorErrorFactory.array_dimension_not_positive_full(cmd.sourceInfo));
         }
       }
       let $value = Promise.resolve(null);
@@ -706,7 +714,7 @@ export class IVProgProcessor {
     const $newStore = this.runFunction(func, exp.actualParameters, store);
     return $newStore.then( sto => {
       if(sto.mode !== Modes.RETURN) {
-        return Promise.reject(new Error("The function that was called did not have a return command: "+exp.id));
+        return Promise.reject(new Error("!!!Internal error: the function that was called did not have a return command or did not set the store mode properly -> "+exp.id));
       }
       const val = sto.applyStore('$');
       sto.destroy();
@@ -723,7 +731,7 @@ export class IVProgProcessor {
   evaluateArrayLiteral (store, exp, type, lines, columns) {
     if(!exp.isVector) {
       if(columns == null) {
-        throw new Error("Vector cannot be initialized by a matrix");
+        return Promise.reject(new Error("This should never happen: Vector cannot be initialized by a matrix"));
       }
       const $matrix = this.evaluateMatrix(store, exp, type, lines, columns);
       return Promise.all($matrix).then(vectorList => {
@@ -732,7 +740,7 @@ export class IVProgProcessor {
       });
     } else {
       if(columns != null) {
-        throw new Error("Matrix cannot be initialized by a vector");
+        return Promise.reject(new Error("This should never happen: Matrix cannot be initialized by a vector"));
       }
       return this.evaluateVector(store, exp, type, lines).then(list => {
         return Promise.resolve(list);
@@ -751,8 +759,7 @@ export class IVProgProcessor {
   evaluateVector (store, exps, type, n_elements) {
     const values =  exps.value;
     if(n_elements !== values.length) {
-      // TODO better error message
-      throw new Error("invalid number of elements to array literal...");
+      return Promise.reject(ProcessorErrorFactory.invalid_number_elements_vector(n_elements, exps.toString(), values.length, exps.sourceInfo));
     }
     const actual_values = Promise.all(values.map( exp => this.evaluateExpression(store, exp)));
     return actual_values.then( values => {
@@ -763,7 +770,7 @@ export class IVProgProcessor {
             // const info = stringInfo[0];
             const exp_str = values[index].toString();
             // TODO - fix error message
-            throw ProcessorErrorFactory.invalid_array_literal_type_full(exp_str, values[index].sourceInfo);
+            return Promise.reject(ProcessorErrorFactory.invalid_array_literal_type_full(exp_str, values[index].sourceInfo));
           }
           const new_value = Store.doImplicitCasting(type.innerType, v);
           return new_value;
@@ -783,8 +790,7 @@ export class IVProgProcessor {
   evaluateMatrix (store, exps, type, lines, columns) {
     const values = exps.value;
     if(values.length !== lines) {
-      // TODO better error message
-      throw new Error("Invalid number of lines to matrix literal...");
+      return Promise.reject(ProcessorErrorFactory.invalid_number_lines_matrix(lines,exps.toString(),values.length, exps.sourceInfo));
     }
     return values.map( vector => {
       const vec_type = new ArrayType(type.innerType, 1);
@@ -832,7 +838,7 @@ export class IVProgProcessor {
           return Promise.reject(ProcessorErrorFactory.matrix_line_outbounds_full(exp.id, line, mustBeArray.lines, exp.sourceInfo));
         }
       } else if (line < 0) {
-        throw ProcessorErrorFactory.array_dimension_not_positive_full(exp.sourceInfo);
+        return Promise.reject(ProcessorErrorFactory.array_dimension_not_positive_full(exp.sourceInfo));
       }
       if (column !== null && mustBeArray.columns === 0 ){
         return Promise.reject(ProcessorErrorFactory.vector_not_matrix_full(exp.id, exp.sourceInfo));
@@ -841,7 +847,7 @@ export class IVProgProcessor {
         if (column >= mustBeArray.columns) {
           return Promise.reject(ProcessorErrorFactory.matrix_column_outbounds_full(exp.id, column,mustBeArray.columns, exp.sourceInfo));
         } else if (column < 0) {
-          throw ProcessorErrorFactory.array_dimension_not_positive_full(exp.sourceInfo);
+          return Promise.reject(ProcessorErrorFactory.array_dimension_not_positive_full(exp.sourceInfo));
         }
       }
       const result = mustBeArray.getAt(line, column);

+ 30 - 14
js/processor/semantic/semanticAnalyser.js

@@ -48,14 +48,14 @@ export class SemanticAnalyser {
     this.symbolMap.map[id] = typeInfo;
   }
 
-  findSymbol (id, symMap) {
-    if(!symMap.map[id]) {
-      if(symMap.next) {
-        return this.findSymbol(id, symMap.next);
+  findSymbol (id, symbol_map) {
+    if(!symbol_map.map[id]) {
+      if(symbol_map.next) {
+        return this.findSymbol(id, symbol_map.next);
       }
       return null;
     } else {
-      return symMap.map[id];
+      return symbol_map.map[id];
     }
   }
 
@@ -117,7 +117,10 @@ export class SemanticAnalyser {
         if(!resultType.isCompatible(declaration.type)) {
           const stringInfo = declaration.type.stringInfo();
           const info = stringInfo[0];
-          throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, declaration.sourceInfo);
+          const result_string_info = resultType.stringInfo();
+          const result_info = result_string_info[0];
+          const exp = declaration.initial;
+          throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, result_info.type, result_info.dim,  exp.toString(), declaration.sourceInfo);
         }
         this.insertSymbol(declaration.id, {id: declaration.id, type: declaration.type, isConst: declaration.isConst})
       } else if((!declaration.type.isCompatible(resultType) && !Config.enable_type_casting)
@@ -125,7 +128,10 @@ export class SemanticAnalyser {
         && !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);
+        const result_string_info = resultType.stringInfo();
+        const result_info = result_string_info[0];
+        const exp = declaration.initial;
+        throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, result_info.type, result_info.dim,  exp.toString(), declaration.sourceInfo);
       } else {
         this.insertSymbol(declaration.id, {id: declaration.id, type: declaration.type, isConst: declaration.isConst});
       }
@@ -246,7 +252,7 @@ export class SemanticAnalyser {
       }
       return typeInfo.type;
     } else {
-      console.warn("Evaluating type only for an array literal...");
+      // console.warn("Evaluating type only for an array literal...");
       let last = null;
       if(literal.value.length === 1) {
         last = this.evaluateExpressionType(literal.value[0]);
@@ -401,7 +407,11 @@ export class SemanticAnalyser {
       }
       if(!compatible) {
         if(!Config.enable_type_casting || !Store.canImplicitTypeCast(access_type, exp_type)) {
-          throw new Error("invalid vector element type");
+          const access_type_string_info = access_type.stringInfo();
+          const access_type_info = access_type_string_info[0];
+          const exp_type_string_info = exp_type.stringInfo();
+          const exp_type_info = exp_type_string_info[0];
+          throw ProcessorErrorFactory.incompatible_types_full(access_type_info.type, access_type_info.dim - used_dims, exp_type_info.type, exp_type_info.dim, exp.toString(), cmd.sourceInfo);
         }
       }
       return optional;
@@ -428,7 +438,9 @@ export class SemanticAnalyser {
             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);
+              const exp_type_string_info = exp_type.stringInfo();
+              const exp_type_info = exp_type_string_info[0];
+              throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, exp_type_info.type, exp_type_info.dim,  exp.toString(), cmd.sourceInfo);
             }
           } else {
             switch(exp_type.dimensions) {
@@ -445,7 +457,9 @@ export class SemanticAnalyser {
         if(!Config.enable_type_casting || !Store.canImplicitTypeCast(typeInfo.type, exp_type)) {
           const stringInfo = typeInfo.type.stringInfo();
           const info = stringInfo[0];
-          throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, cmd.sourceInfo);
+          const exp_type_string_info = exp_type.stringInfo();
+          const exp_type_info = exp_type_string_info[0];
+          throw ProcessorErrorFactory.incompatible_types_full(info.type, info.dim, exp_type_info.type, exp_type_info.dim,  exp.toString(), cmd.sourceInfo);
         }
       }
       return optional;
@@ -575,10 +589,12 @@ export class SemanticAnalyser {
         compatible = type.canAccept(expType, 1);
       }
       if(!compatible) {
-        // vector wrong type
-        // TODO better error message
         if(!Config.enable_type_casting || !Store.canImplicitTypeCast(type.innerType, expType)) {
-          throw new Error("invalid vector element type");
+          const stringInfo = type.stringInfo();
+          const info = stringInfo[0];
+          const result_string_info = expType.stringInfo();
+          const result_info = result_string_info[0];
+          throw ProcessorErrorFactory.incompatible_types_full(info.type, 0, result_info.type, result_info.dim,  exp.toString(), literal.sourceInfo);
         }
       }
     }