Jelajahi Sumber

Merge branch 'fixDuplicateSymbols' of LInE/ivprog into master

Lucas de Souza 5 tahun lalu
induk
melakukan
2e5a1b6a93
3 mengubah file dengan 58 tambahan dan 8 penghapusan
  1. 4 1
      i18n/pt/error.json
  2. 12 1
      js/ast/error/syntaxErrorFactory.js
  3. 42 6
      js/ast/ivprogParser.js

+ 4 - 1
i18n/pt/error.json

@@ -33,5 +33,8 @@
   "line_outbounds": "Número de linhas $0 é invaálido para a matriz $1 que possui $2 linhas",
   "invalid_infix_op": "Não é possível aplicar a operação $0 entre os tipos $1 e $2",
   "invalid_unary_op": "Não é possível aplicar a operação $0 ao tipo $1",
-  "unknown_op": "Erro interno crítico: Operação $0 desconhecida"
+  "unknown_op": "Erro interno crítico: Operação $0 desconhecida",
+  "duplicate_function": "A função $0 na linha: $1, coluna: $2 já foi definida anteriormente.",
+  "duplicate_variable": "A variável $0 na linha: $1, coluna: $2 já foi declarada anteriormente.",
+  "main_parameters": "A função inicio não pode ter parâmetros."
 }

+ 12 - 1
js/ast/error/syntaxErrorFactory.js

@@ -39,7 +39,7 @@ export const SyntaxErrorFactory = Object.freeze({
     const context = [token.line, cmdName];
     return new SyntaxError(LocalizedStrings.getError("invalid_break_command", context));
   },
-  invalid_terminal: () => {
+  invalid_terminal: (token) => {
     const context = [token.text, token.line, token.column];
     return new SyntaxError(LocalizedStrings.getError('invalid_terminal', context));
   },
@@ -55,5 +55,16 @@ export const SyntaxErrorFactory = Object.freeze({
   invalid_id_format: (token) => {
     const context = [token.text, token.line, token.column];
     return new SyntaxError(LocalizedStrings.getError("invalid_id_format", context));
+  },
+  duplicate_function: (token) => {
+    const context = [token.text, token.line, token.column];
+    return new SyntaxError(LocalizedStrings.getError("duplicate_function", context));
+  },
+  main_parameters: () => {
+    return new SyntaxError(LocalizedStrings.getError("main_parameters"));
+  },
+  duplicate_variable: (token) => {
+    const context = [token.text, token.line, token.column];
+    return new SyntaxError(LocalizedStrings.getError("duplicate_variable", context));
   }
 });

+ 42 - 6
js/ast/ivprogParser.js

@@ -47,6 +47,8 @@ export class IVProgParser {
     this.parsingArrayDimension = 0;
     this.scope = [];
     this.langFuncs = LanguageService.getCurrentLangFuncs();
+    this.definedFuncsNameList = [];
+    this.definedVariablesStack = [];
   }
 
   parseTree () {
@@ -71,10 +73,22 @@ export class IVProgParser {
     this.scope.push(scope);
   }
 
+  pushVariableStack () {
+    this.definedVariablesStack.push([]);
+  }
+
   popScope () {
     return this.scope.pop();
   }
 
+  popVariableStack () {
+    return this.definedVariablesStack.pop();
+  }
+
+  getCurrentVariableStack () {
+    return this.definedVariablesStack[this.definedVariablesStack.length - 1];
+  }
+
   isEOF () {
     this.getToken(this.pos);
     return this.tokenStream.fetchedEOF;
@@ -90,13 +104,16 @@ export class IVProgParser {
       this.consumeNewLines();
       this.checkOpenCurly();
       this.pos++;
+      this.pushVariableStack();
       while(true) {
         this.consumeNewLines();
         const token = this.getToken();
         if (token.type === this.lexerClass.RK_CONST || this.isVariableType(token)) {
           globalVars = globalVars.concat(this.parseGlobalVariables());
         } else if (token.type === this.lexerClass.RK_FUNCTION) {
+          this.pushVariableStack();
           functions = functions.concat(this.parseFunction());
+          this.popVariableStack();
         } else {
           break;
         }
@@ -108,6 +125,7 @@ export class IVProgParser {
       if(!this.isEOF()) {
         throw SyntaxErrorFactory.extra_lines();
       }
+      this.popVariableStack();
       return {global: globalVars, functions: functions};
     } else {
       throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_PROGRAM], token);
@@ -203,6 +221,23 @@ export class IVProgParser {
     return true;
   }
 
+  checkFunctionDuplicate (functionID, funcIDToken) {
+    const id = functionID === null ? "$main" : functionID;
+    const index = this.definedFuncsNameList.indexOf(id);
+    if(index !== -1) {
+      throw SyntaxErrorFactory.duplicate_function(funcIDToken);
+    }
+    this.definedFuncsNameList.push(id);
+  }
+
+  checkVariableDuplicate (variableID, variableIDToken) {
+    const index = this.getCurrentVariableStack().indexOf(variableID);
+    if(index !== -1) {
+      throw SyntaxErrorFactory.duplicate_variable(variableIDToken);
+    }
+    this.getCurrentVariableStack().push(variableID);
+  }
+
   consumeForSemiColon () {
     const eosToken = this.getToken();
     if (eosToken.type === this.lexerClass.EOS && eosToken.text.match(';')) {
@@ -250,7 +285,7 @@ export class IVProgParser {
     let dim2 = null;
     const idToken = this.getToken();
     const idString = this.parseID();
-    
+    this.checkVariableDuplicate(idString,idToken);
     // Check for array or vector
     // ID[int/IDi][int/IDj]
     if (this.checkOpenBrace(true)) {
@@ -372,7 +407,6 @@ export class IVProgParser {
     this.checkOpenCurly();
     const beginArray = this.getToken();
     if (this.parsingArrayDimension >= 2) {
-      // TODO: better error message
       throw SyntaxErrorFactory.token_missing_list(`Array dimensions exceed maximum size of 2 at line ${beginArray.line}`);
     }
     this.pos++;
@@ -441,7 +475,9 @@ export class IVProgParser {
         dimensions++;
       }
     }
+    const funcIDToken = this.getToken();
     const functionID = this.parseID();
+    this.checkFunctionDuplicate(functionID, funcIDToken);
     this.checkOpenParenthesis();
     this.pos++;
     this.consumeNewLines();
@@ -461,10 +497,11 @@ export class IVProgParser {
     }
     const func = new Commands.Function(functionID, returnType, formalParams, commandsBlock);
     if (functionID === null && !func.isMain) {
-      // TODO: better error message
       throw SyntaxErrorFactory.invalid_main_return(LanguageDefinedFunction.getMainFunctionName(),
         this.lexer.literalNames[this.lexerClass.RK_VOID],
         token.line);
+    } else if (func.isMain && formalParams.length !== 0) {
+      throw SyntaxErrorFactory.main_parameters();
     }
     this.popScope();
     return func;
@@ -479,7 +516,9 @@ export class IVProgParser {
     while(true) {
       let dimensions = 0;
       const typeString = this.parseType();
+      const idToken = this.getToken();
       const idString = this.parseID();
+      this.checkVariableDuplicate(idString, idToken);
       if (this.checkOpenBrace(true)) {
         this.pos++;
         dimensions++;
@@ -592,7 +631,6 @@ export class IVProgParser {
     const token = this.getToken();
     if (this.isVariableType(token)) {
       if(!this.insideScope(IVProgParser.FUNCTION)) {
-        // TODO better error message
         throw SyntaxErrorFactory.invalid_var_declaration(token.line);
       }
       this.pushScope(IVProgParser.BASE);
@@ -614,7 +652,6 @@ export class IVProgParser {
       return this.parseFor();
     } else if (token.type === this.lexerClass.RK_BREAK ) {
       if(!this.insideScope(IVProgParser.BREAKABLE)) {
-        // TODO better error message
         throw SyntaxErrorFactory.invalid_break_command(
           this.lexer.literalNames[this.lexerClass.RK_BREAK],
           token
@@ -710,7 +747,6 @@ export class IVProgParser {
       } else if(maybeIf.type === this.lexerClass.RK_IF) {
         elseBlock = this.parseIfThenElse();
       } else {
-        // TODO better error message
         throw SyntaxErrorFactory.token_missing_list([this.lexer.literalNames[this.lexerClass.RK_IF], '{'], maybeIf);
       }
       return new Commands.IfThenElse(logicalExpression, cmdBlocks, elseBlock);