|
@@ -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);
|