|
@@ -34,6 +34,7 @@ export class IVProgParser {
|
|
];
|
|
];
|
|
this.functionTypes = this.variableTypes.concat(this.lexerClass.RK_VOID);
|
|
this.functionTypes = this.variableTypes.concat(this.lexerClass.RK_VOID);
|
|
this.parsingArrayDimension = 0;
|
|
this.parsingArrayDimension = 0;
|
|
|
|
+ this.scope = [];
|
|
}
|
|
}
|
|
|
|
|
|
parseTree () {
|
|
parseTree () {
|
|
@@ -46,6 +47,22 @@ export class IVProgParser {
|
|
return this.tokenStream.LT(index);
|
|
return this.tokenStream.LT(index);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ insideScope (scope) {
|
|
|
|
+ if(this.scope.length <= 0) {
|
|
|
|
+ return IVProgParser.BASE === scope;
|
|
|
|
+ } else {
|
|
|
|
+ return this.scope[this.scope.length-1] === scope;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pushScope (scope) {
|
|
|
|
+ this.scope.push(scope);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ popScope () {
|
|
|
|
+ return this.scope.pop();
|
|
|
|
+ }
|
|
|
|
+
|
|
isEOF () {
|
|
isEOF () {
|
|
this.getToken(this.pos);
|
|
this.getToken(this.pos);
|
|
return this.tokenStream.fetchedEOF;
|
|
return this.tokenStream.fetchedEOF;
|
|
@@ -363,6 +380,7 @@ export class IVProgParser {
|
|
* The block object has two attributes: declarations and commands
|
|
* The block object has two attributes: declarations and commands
|
|
**/
|
|
**/
|
|
parseFunction () {
|
|
parseFunction () {
|
|
|
|
+ this.pushScope(IVProgParser.FUNCTION);
|
|
let formalParams = [];
|
|
let formalParams = [];
|
|
const token = this.getToken();
|
|
const token = this.getToken();
|
|
if(token.type !== this.lexerClass.RK_FUNCTION) {
|
|
if(token.type !== this.lexerClass.RK_FUNCTION) {
|
|
@@ -370,8 +388,8 @@ export class IVProgParser {
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
this.pos++;
|
|
this.pos++;
|
|
- const returnType = this.parseType(IVProgParser.FUNCTION);
|
|
|
|
- const functionID = this.parseID(IVProgParser.FUNCTION);
|
|
|
|
|
|
+ const returnType = this.parseType();
|
|
|
|
+ const functionID = this.parseID();
|
|
this.checkOpenParenthesis();
|
|
this.checkOpenParenthesis();
|
|
this.pos++;
|
|
this.pos++;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
@@ -390,6 +408,7 @@ export class IVProgParser {
|
|
// TODO: better error message
|
|
// TODO: better error message
|
|
throw new Error(`Function ${this.lexerClass.MAIN_FUNCTION_NAME} must return void (line ${token.line})`);
|
|
throw new Error(`Function ${this.lexerClass.MAIN_FUNCTION_NAME} must return void (line ${token.line})`);
|
|
}
|
|
}
|
|
|
|
+ this.popScope();
|
|
return func;
|
|
return func;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -425,13 +444,13 @@ export class IVProgParser {
|
|
return list;
|
|
return list;
|
|
}
|
|
}
|
|
|
|
|
|
- parseID (scope = IVProgParser.BASE) {
|
|
|
|
|
|
+ parseID () {
|
|
const token = this.getToken();
|
|
const token = this.getToken();
|
|
if(token.type !== this.lexerClass.ID) {
|
|
if(token.type !== this.lexerClass.ID) {
|
|
throw SyntaxError.createError('ID', token);
|
|
throw SyntaxError.createError('ID', token);
|
|
}
|
|
}
|
|
this.pos++;
|
|
this.pos++;
|
|
- if (scope === IVProgParser.FUNCTION) {
|
|
|
|
|
|
+ if (this.insideScope(IVProgParser.FUNCTION)) {
|
|
if (token.text === this.lexerClass.MAIN_FUNCTION_NAME){
|
|
if (token.text === this.lexerClass.MAIN_FUNCTION_NAME){
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
@@ -439,11 +458,11 @@ export class IVProgParser {
|
|
return token.text;
|
|
return token.text;
|
|
}
|
|
}
|
|
|
|
|
|
- parseType (scope = IVProgParser.BASE) {
|
|
|
|
|
|
+ parseType () {
|
|
const token = this.getToken();
|
|
const token = this.getToken();
|
|
- if(token.type === this.lexerClass.ID && scope === IVProgParser.FUNCTION) {
|
|
|
|
|
|
+ if(token.type === this.lexerClass.ID && this.insideScope(IVProgParser.FUNCTION)) {
|
|
return Types.VOID;
|
|
return Types.VOID;
|
|
- } else if (token.type === this.lexerClass.RK_VOID && scope === IVProgParser.FUNCTION) {
|
|
|
|
|
|
+ } else if (token.type === this.lexerClass.RK_VOID && this.insideScope(IVProgParser.FUNCTION)) {
|
|
this.pos++;
|
|
this.pos++;
|
|
return Types.VOID;
|
|
return Types.VOID;
|
|
} else if (this.isVariableType(token)) {
|
|
} else if (this.isVariableType(token)) {
|
|
@@ -462,10 +481,10 @@ export class IVProgParser {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- throw SyntaxError.createError(this.getTypesAsString(scope), token);
|
|
|
|
|
|
+ throw SyntaxError.createError(this.getTypesAsString(), token);
|
|
}
|
|
}
|
|
|
|
|
|
- parseCommandBlock (scope = IVProgParser.FUNCTION, optionalCurly = false) {
|
|
|
|
|
|
+ parseCommandBlock (optionalCurly = false) {
|
|
let variablesDecl = [];
|
|
let variablesDecl = [];
|
|
const commands = [];
|
|
const commands = [];
|
|
let hasOpen = false;
|
|
let hasOpen = false;
|
|
@@ -476,7 +495,7 @@ export class IVProgParser {
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
while(true) {
|
|
while(true) {
|
|
|
|
|
|
- const cmd = this.parseCommand(scope);
|
|
|
|
|
|
+ const cmd = this.parseCommand();
|
|
if (cmd === null)
|
|
if (cmd === null)
|
|
break;
|
|
break;
|
|
if(cmd !== -1) {
|
|
if(cmd !== -1) {
|
|
@@ -496,11 +515,11 @@ export class IVProgParser {
|
|
return new Commands.CommandBlock(variablesDecl, commands);
|
|
return new Commands.CommandBlock(variablesDecl, commands);
|
|
}
|
|
}
|
|
|
|
|
|
- parseCommand (scope) {
|
|
|
|
|
|
+ parseCommand () {
|
|
const token = this.getToken();
|
|
const token = this.getToken();
|
|
let cmd = null;
|
|
let cmd = null;
|
|
if (this.isVariableType(token)) {
|
|
if (this.isVariableType(token)) {
|
|
- if(scope !== IVProgParser.FUNCTION) {
|
|
|
|
|
|
+ if(!this.insideScope(IVProgParser.FUNCTION)) {
|
|
// TODO better error message
|
|
// TODO better error message
|
|
throw new Error(`Cannot declare variable here (line ${token.line})`);
|
|
throw new Error(`Cannot declare variable here (line ${token.line})`);
|
|
}
|
|
}
|
|
@@ -517,7 +536,7 @@ export class IVProgParser {
|
|
} else if (token.type === this.lexerClass.RK_FOR) {
|
|
} else if (token.type === this.lexerClass.RK_FOR) {
|
|
cmd = this.parseFor();
|
|
cmd = this.parseFor();
|
|
} else if (token.type === this.lexerClass.RK_BREAK ) {
|
|
} else if (token.type === this.lexerClass.RK_BREAK ) {
|
|
- if(scope !== IVProgParser.BREAKABLE) {
|
|
|
|
|
|
+ if(!this.insideScope(IVProgParser.BREAKABLE)) {
|
|
// TODO better error message
|
|
// TODO better error message
|
|
throw new Error("Break cannot be used outside of a loop.");
|
|
throw new Error("Break cannot be used outside of a loop.");
|
|
}
|
|
}
|
|
@@ -536,6 +555,7 @@ export class IVProgParser {
|
|
}
|
|
}
|
|
|
|
|
|
parseSwitchCase () {
|
|
parseSwitchCase () {
|
|
|
|
+ this.pushScope(IVProgParser.BREAKABLE);
|
|
this.pos++;
|
|
this.pos++;
|
|
this.checkOpenParenthesis();
|
|
this.checkOpenParenthesis();
|
|
this.pos++;
|
|
this.pos++;
|
|
@@ -553,15 +573,19 @@ export class IVProgParser {
|
|
this.checkCloseCurly();
|
|
this.checkCloseCurly();
|
|
this.pos++;
|
|
this.pos++;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
|
|
+
|
|
|
|
+ this.popScope();
|
|
|
|
+ return null;
|
|
}
|
|
}
|
|
|
|
|
|
parseDoWhile () {
|
|
parseDoWhile () {
|
|
this.pos++;
|
|
this.pos++;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
- const commandsBlock = this.parseCommandBlock(IVProgParser.BREAKABLE);
|
|
|
|
|
|
+ this.pushScope(IVProgParser.BREAKABLE);
|
|
|
|
+ const commandsBlock = this.parseCommandBlock();
|
|
this.consumeNewLines(); //Maybe not...
|
|
this.consumeNewLines(); //Maybe not...
|
|
const whileToken = this.getToken();
|
|
const whileToken = this.getToken();
|
|
- if (whileToken !== this.lexerClass.RK_WHILE) {
|
|
|
|
|
|
+ if (whileToken.type !== this.lexerClass.RK_WHILE) {
|
|
throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.RK_WHILE], whileToken);
|
|
throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.RK_WHILE], whileToken);
|
|
}
|
|
}
|
|
this.pos++;
|
|
this.pos++;
|
|
@@ -573,11 +597,16 @@ export class IVProgParser {
|
|
this.checkCloseParenthesis();
|
|
this.checkCloseParenthesis();
|
|
this.pos++;
|
|
this.pos++;
|
|
this.checkEOS();
|
|
this.checkEOS();
|
|
-
|
|
|
|
|
|
+ this.popScope();
|
|
return new Commands.DoWhile(condition, commandsBlock);
|
|
return new Commands.DoWhile(condition, commandsBlock);
|
|
}
|
|
}
|
|
|
|
|
|
parseIfThenElse () {
|
|
parseIfThenElse () {
|
|
|
|
+ if(this.insideScope(IVProgParser.BREAKABLE)) {
|
|
|
|
+ this.pushScope(IVProgParser.BREAKABLE);
|
|
|
|
+ } else {
|
|
|
|
+ this.pushScope(IVProgParser.COMMAND);
|
|
|
|
+ }
|
|
this.pos++;
|
|
this.pos++;
|
|
this.checkOpenParenthesis();
|
|
this.checkOpenParenthesis();
|
|
this.pos++;
|
|
this.pos++;
|
|
@@ -587,7 +616,7 @@ export class IVProgParser {
|
|
this.checkCloseParenthesis();
|
|
this.checkCloseParenthesis();
|
|
this.pos++;
|
|
this.pos++;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
- const cmdBlocks = this.parseCommandBlock(IVProgParser.COMMAND);
|
|
|
|
|
|
+ const cmdBlocks = this.parseCommandBlock();
|
|
|
|
|
|
const maybeElse = this.getToken();
|
|
const maybeElse = this.getToken();
|
|
if(maybeElse.type === this.lexerClass.RK_ELSE) {
|
|
if(maybeElse.type === this.lexerClass.RK_ELSE) {
|
|
@@ -595,16 +624,18 @@ export class IVProgParser {
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
let elseBlock = null;
|
|
let elseBlock = null;
|
|
if(this.checkOpenCurly(true)) {
|
|
if(this.checkOpenCurly(true)) {
|
|
- elseBlock = this.parseCommandBlock(IVProgParser.COMMAND);
|
|
|
|
|
|
+ elseBlock = this.parseCommandBlock();
|
|
} else {
|
|
} else {
|
|
elseBlock = this.parseIfThenElse();
|
|
elseBlock = this.parseIfThenElse();
|
|
}
|
|
}
|
|
return new Commands.IfThenElse(logicalExpression, cmdBlocks, elseBlock);
|
|
return new Commands.IfThenElse(logicalExpression, cmdBlocks, elseBlock);
|
|
}
|
|
}
|
|
|
|
+ this.popScope();
|
|
return new Commands.IfThenElse(logicalExpression, cmdBlocks, null);
|
|
return new Commands.IfThenElse(logicalExpression, cmdBlocks, null);
|
|
}
|
|
}
|
|
|
|
|
|
parseFor () {
|
|
parseFor () {
|
|
|
|
+ this.pushScope(IVProgParser.BREAKABLE);
|
|
this.pos++;
|
|
this.pos++;
|
|
this.checkOpenParenthesis();
|
|
this.checkOpenParenthesis();
|
|
this.pos++;
|
|
this.pos++;
|
|
@@ -618,11 +649,13 @@ export class IVProgParser {
|
|
this.checkCloseParenthesis()
|
|
this.checkCloseParenthesis()
|
|
this.pos++;
|
|
this.pos++;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
- const commandsBlock = this.parseCommandBlock(IVProgParser.BREAKABLE);
|
|
|
|
|
|
+ const commandsBlock = this.parseCommandBlock();
|
|
|
|
+ this.popScope();
|
|
return new Commands.For(attribution, condition, increment, commandsBlock);
|
|
return new Commands.For(attribution, condition, increment, commandsBlock);
|
|
}
|
|
}
|
|
|
|
|
|
parseWhile () {
|
|
parseWhile () {
|
|
|
|
+ this.pushScope(IVProgParser.BREAKABLE);
|
|
this.pos++;
|
|
this.pos++;
|
|
this.checkOpenParenthesis();
|
|
this.checkOpenParenthesis();
|
|
this.pos++;
|
|
this.pos++;
|
|
@@ -632,7 +665,8 @@ export class IVProgParser {
|
|
this.checkCloseParenthesis();
|
|
this.checkCloseParenthesis();
|
|
this.pos++;
|
|
this.pos++;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
- const cmdBlocks = this.parseCommandBlock(IVProgParser.BREAKABLE);
|
|
|
|
|
|
+ const cmdBlocks = this.parseCommandBlock();
|
|
|
|
+ this.popScope();
|
|
return new Commands.While(logicalExpression, cmdBlocks);
|
|
return new Commands.While(logicalExpression, cmdBlocks);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -896,8 +930,8 @@ export class IVProgParser {
|
|
return list;
|
|
return list;
|
|
}
|
|
}
|
|
|
|
|
|
- getTypesAsString (scope = IVProgParser.BASE) {
|
|
|
|
- const types = scope === IVProgParser.FUNCTION ? this.functionTypes : this.variableTypes;
|
|
|
|
|
|
+ getTypesAsString () {
|
|
|
|
+ const types = this.insideScope(IVProgParser.FUNCTION) ? this.functionTypes : this.variableTypes;
|
|
return types.map( x => this.lexer.literalNames[x])
|
|
return types.map( x => this.lexer.literalNames[x])
|
|
.reduce((o, n) => {
|
|
.reduce((o, n) => {
|
|
if (o.length <= 0)
|
|
if (o.length <= 0)
|