import { CommonTokenStream, InputStream } from 'antlr4/index';
import * as Expressions from './expressions/';
import * as Commands from './commands/';
import { toInt, toString, toBool, toReal } from './../typeSystem/parsers';
import { Types } from "./../typeSystem/types";
import { CompoundType } from "./../typeSystem/compoundType";
import { SourceInfo } from './sourceInfo';
import { convertFromString } from './operators';
import { SyntaxErrorFactory } from './error/syntaxErrorFactory';
import { LanguageDefinedFunction } from './../processor/definedFunctions';
import { LanguageService } from '../services/languageService';
export class IVProgParser {
static createParser (input) {
const lexerClass = LanguageService.getCurrentLexer();
return new IVProgParser(input, lexerClass);
}
// <BEGIN scope consts>
static get BASE () {
return 0;
}
static get FUNCTION () {
return 1;
}
static get COMMAND () {
return 2;
}
static get BREAKABLE () {
return 4;
}
// </ END scope consts>
constructor (input, lexerClass) {
this.lexerClass = lexerClass;
this.lexer = new lexerClass(new InputStream(input));
this.tokenStream = new CommonTokenStream(this.lexer);
this.tokenStream.fill();
this.pos = 1;
this.variableTypes = [this.lexerClass.RK_INTEGER,
this.lexerClass.RK_REAL,
this.lexerClass.RK_BOOLEAN,
this.lexerClass.RK_STRING
];
this.functionTypes = this.variableTypes.concat(this.lexerClass.RK_VOID);
this.parsingArrayDimension = 0;
this.scope = [];
this.langFuncs = LanguageService.getCurrentLangFuncs();
this.definedFuncsNameList = [];
this.definedVariablesStack = [];
}
parseTree () {
return this.parseProgram();
}
getToken (index = this.pos) {
// if(index === null)
// index = this.pos;
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);
}
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;
}
parseProgram () {
const token = this.getToken();
let globalVars = [];
let functions = [];
if(this.lexerClass.RK_PROGRAM === token.type) {
this.pos++;
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;
}
}
this.consumeNewLines();
this.checkCloseCurly();
this.pos++;
this.consumeNewLines();
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);
}
}
checkOpenCurly (attempt = false) {
const token = this.getToken();
if(this.lexerClass.OPEN_CURLY !== token.type){
if(!attempt)
throw SyntaxErrorFactory.token_missing_one('{', token);
else
return false;
}
return true;
}
checkCloseCurly (attempt = false) {
const token = this.getToken();
if(this.lexerClass.CLOSE_CURLY !== token.type){
if(!attempt)
throw SyntaxErrorFactory.token_missing_one('}', token);
else
return false;
}
return true;
}
/* It checks if the current token at position pos is a ']'.
* As a check function it doesn't increment pos.
*
* @params bool:attempt, indicates that the token is optional. Defaults: false
*
* @returns true if the attempt is true and current token is '[',
* false is attempt is true and current token is not '['
**/
checkOpenBrace (attempt = false) {
const token = this.getToken();
if(this.lexerClass.OPEN_BRACE !== token.type){
if (!attempt) {
throw SyntaxErrorFactory.token_missing_one('[', token);
} else {
return false;
}
}
return true;
}
checkCloseBrace (attempt = false) {
const token = this.getToken();
if(this.lexerClass.CLOSE_BRACE !== token.type){
if (!attempt) {
throw SyntaxErrorFactory.token_missing_one(']', token);
} else {
return false;
}
}
return true;
}
checkOpenParenthesis (attempt = false) {
const token = this.getToken();
if(this.lexerClass.OPEN_PARENTHESIS !== token.type){
if (!attempt) {
throw SyntaxErrorFactory.token_missing_one('(', token);
} else {
return false;
}
}
return true;
}
checkCloseParenthesis (attempt = false) {
const token = this.getToken();
if(this.lexerClass.CLOSE_PARENTHESIS !== token.type){
if (!attempt) {
throw SyntaxErrorFactory.token_missing_one(')', token);
} else {
return false;
}
}
return true;
}
checkEOS (attempt = false) {
const eosToken = this.getToken();
if (eosToken.type !== this.lexerClass.EOS) {
if (!attempt)
throw SyntaxErrorFactory.eos_missing(eosToken);
else
return false;
}
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(';')) {
this.pos++;
return;
}
throw SyntaxErrorFactory.token_missing_one(';', eosToken);
}
parseGlobalVariables () {
const decl = this.parseMaybeConst();
this.checkEOS();
this.pos++;
return decl;
}
/*
* Checks if the next token is PR_CONST. It's only available
* at global variables declaration level
* @returns Declararion(const, type, id, initVal?)
**/
parseMaybeConst () {
const constToken = this.getToken();
if(constToken.type === this.lexerClass.RK_CONST) {
this.pos++;
const typeString = this.parseType();
return this.parseDeclaration(typeString, true);
} else if(this.isVariableType(constToken)) {
const typeString = this.parseType();
return this.parseDeclaration(typeString);
} else {
throw SyntaxErrorFactory.token_missing_list(
[this.lexer.literalNames[this.lexerClass.RK_CONST]].concat(this.getTypeArray()), constToken);
}
}
/*
* Parses a declarion of the form: type --- id --- (= --- EAnd)?
* @returns a list of Declararion(const, type, id, initVal?)
**/
parseDeclaration (typeString, isConst = false) {
let initial = null;
let dim1 = null;
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)) {
this.pos++;
this.consumeNewLines();
dim1 = this.parseArrayDimension();
this.consumeNewLines();
this.checkCloseBrace();
this.pos++;
if(this.checkOpenBrace(true)) {
this.pos++;
this.consumeNewLines();
dim2 = this.parseArrayDimension();
this.consumeNewLines();
this.checkCloseBrace();
this.pos++;
}
}
const equalsToken = this.getToken();
if(isConst && equalsToken.type !== this.lexerClass.EQUAL ) {
throw SyntaxErrorFactory.const_not_init(idToken);
}
if(equalsToken.type === this.lexerClass.EQUAL) {
this.pos++;
initial = this.parseExpressionOR();
}
let declaration = null;
let dimensions = 0;
if (dim1 !== null) {
dimensions++;
if(dim2 !== null) {
dimensions++;
}
declaration = new Commands.ArrayDeclaration(idString,
new CompoundType(typeString, dimensions), dim1, dim2, initial, isConst);
} else {
declaration = new Commands.Declaration(idString, typeString, initial, isConst);
}
declaration.sourceInfo = SourceInfo.createSourceInfo(idToken);
const commaToken = this.getToken();
if(commaToken.type === this.lexerClass.COMMA) {
this.pos++;
this.consumeNewLines();
return [declaration]
.concat(this.parseDeclaration(typeString, isConst));
} else {
return [declaration]
}
}
consumeNewLines () {
let token = this.getToken();
while(token.type === this.lexerClass.EOS && token.text.match('[\r\n]+')) {
this.pos++;
token = this.getToken();
}
}
isVariableType (token) {
return this.variableTypes.find(v => v === token.type);
}
/*
* Reads the next token of the stream to check if it is a Integer or an ID.
* @returns Integer | ID
**/
parseArrayDimension () {
const dimToken = this.getToken();
if(dimToken.type === this.lexerClass.INTEGER) {
//parse as int literal
this.pos++;
return this.getIntLiteral(dimToken);
} else if(dimToken.type === this.lexerClass.ID) {
//parse as variable
this.pos++;
return this.parseVariable(dimToken);
} else {
throw SyntaxErrorFactory.invalid_array_dimension(this.lexer.literalNames[this.lexerClass.RK_INTEGER], dimToken);
}
}
/*
* Returns an object {type: 'int', value: value}.
* It checks for binary and hexadecimal integers.
* @returns object with fields type and value
**/
getIntLiteral (token) {
const text = token.text;
const sourceInfo = SourceInfo.createSourceInfo(token);
const exp = new Expressions.IntLiteral(toInt(text));
exp.sourceInfo = sourceInfo;
return exp;
}
getRealLiteral (token) {
const sourceInfo = SourceInfo.createSourceInfo(token);
const exp = new Expressions.RealLiteral(toReal(token.text));
exp.sourceInfo = sourceInfo;
return exp;
}
getStringLiteral (token) {
const text = token.text;
const sourceInfo = SourceInfo.createSourceInfo(token);
const exp = new Expressions.StringLiteral(toString(text));
exp.sourceInfo = sourceInfo;
return exp;
}
getBoolLiteral (token) {
const val = toBool(token.text);
const exp = new Expressions.BoolLiteral(val);
exp.sourceInfo = SourceInfo.createSourceInfo(token);;
return exp;
}
parseArrayLiteral () {
this.checkOpenCurly();
const beginArray = this.getToken();
if (this.parsingArrayDimension >= 2) {
throw SyntaxErrorFactory.token_missing_list(`Array dimensions exceed maximum size of 2 at line ${beginArray.line}`);
}
this.pos++;
this.parsingArrayDimension++;
this.consumeNewLines();
const data = this.parseExpressionList();
this.consumeNewLines();
this.checkCloseCurly()
const endArray = this.getToken();
this.pos++;
this.parsingArrayDimension--;
if (this.parsingArrayDimension === 0) {
// if (!data.isValid) {
// // TODO: better error message
// console.log('invalid array');
// throw new Error(`Invalid array at line ${beginArray.line}`);
// }
}
const sourceInfo = SourceInfo.createSourceInfoFromList(beginArray, endArray);
let dataDim = 1;
if(data[0] instanceof Expressions.ArrayLiteral) {
dataDim++;
}
const type = new CompoundType(Types.UNDEFINED, dataDim);
const exp = new Expressions.ArrayLiteral(type, data);
exp.sourceInfo = sourceInfo;
return exp;
}
/*
* Returns an object {type: 'variable', value: value}.
* @returns object with fields type and value
**/
parseVariable (token) {
const sourceInfo = SourceInfo.createSourceInfo(token);
const exp = new Expressions.VariableLiteral(token.text);
exp.sourceInfo = sourceInfo;
return exp;
}
/*
* Returns an object representing a function. It has
* four attributes: returnType, id, formalParams and block.
* The block object has two attributes: declarations and commands
**/
parseFunction () {
this.pushScope(IVProgParser.FUNCTION);
let formalParams = [];
const token = this.getToken();
if(token.type !== this.lexerClass.RK_FUNCTION) {
//throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.PR_FUNCAO], token);
return null;
}
this.pos++;
const funType = this.parseType();
let dimensions = 0;
if(this.checkOpenBrace(true)) {
this.pos++;
this.checkCloseBrace();
this.pos++;
dimensions++;
if(this.checkOpenBrace(true)) {
this.pos++;
this.checkCloseBrace();
this.pos++;
dimensions++;
}
}
const funcIDToken = this.getToken();
const functionID = this.parseID();
this.checkFunctionDuplicate(functionID, funcIDToken);
this.checkOpenParenthesis();
this.pos++;
this.consumeNewLines();
if (!this.checkCloseParenthesis(true)) {
formalParams = this.parseFormalParameters(); // formal parameters
this.consumeNewLines();
this.checkCloseParenthesis();
this.pos++;
} else {
this.pos++;
}
this.consumeNewLines();
const commandsBlock = this.parseCommandBlock();
let returnType = funType;
if(dimensions > 0) {
returnType = new CompoundType(funType, dimensions);
}
const func = new Commands.Function(functionID, returnType, formalParams, commandsBlock);
if (functionID === null && !func.isMain) {
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;
}
/*
* Parse the formal parameters of a function.
* @returns a list of objects with the following attributes: type, id and dimensions.
**/
parseFormalParameters () {
const list = [];
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++;
this.checkCloseBrace();
this.pos++;
if(this.checkOpenBrace(true)) {
this.pos++;
dimensions++;
this.checkCloseBrace();
this.pos++;
}
}
let type = null;
if(dimensions > 0) {
type = new CompoundType(typeString, dimensions);
} else {
type = typeString;
}
list.push(new Commands.FormalParameter(type, idString));
const commaToken = this.getToken();
if (commaToken.type !== this.lexerClass.COMMA)
break;
this.pos++;
this.consumeNewLines();
}
return list;
}
parseID () {
const token = this.getToken();
if(token.type !== this.lexerClass.ID) {
throw SyntaxErrorFactory.id_missing(token);
}
this.pos++;
if (this.insideScope(IVProgParser.FUNCTION)) {
if (token.text === LanguageDefinedFunction.getMainFunctionName()){
return null;
}
}
return token.text;
}
parseMaybeLibID () {
const token = this.getToken();
if(token.type !== this.lexerClass.ID && token.type !== this.lexerClass.LIB_ID) {
throw SyntaxErrorFactory.id_missing(token);
}
this.pos++;
return token.text;
}
parseType () {
const token = this.getToken();
if(token.type === this.lexerClass.ID && this.insideScope(IVProgParser.FUNCTION)) {
return Types.VOID;
} else if (token.type === this.lexerClass.RK_VOID && this.insideScope(IVProgParser.FUNCTION)) {
this.pos++;
return Types.VOID;
} else if (this.isVariableType(token)) {
this.pos++;
switch(token.type) {
case this.lexerClass.RK_INTEGER:
return Types.INTEGER;
case this.lexerClass.RK_BOOLEAN:
return Types.BOOLEAN;
case this.lexerClass.RK_REAL:
return Types.REAL;
case this.lexerClass.RK_STRING:
return Types.STRING;
default:
break;
}
}
throw SyntaxErrorFactory.invalid_type(this.getTypeArray(), token);
}
parseCommandBlock (optionalCurly = false) {
let variablesDecl = [];
const commands = [];
let hasOpen = false;
if (this.checkOpenCurly(optionalCurly)) {
this.pos++;
hasOpen = true;
}
this.consumeNewLines();
while(true) {
const cmd = this.parseCommand();
if (cmd === null)
break;
if(cmd !== -1) {
if (cmd instanceof Array) {
variablesDecl = variablesDecl.concat(cmd);
} else {
commands.push(cmd);
}
}
}
this.consumeNewLines();
if (hasOpen) {
this.checkCloseCurly()
this.pos++;
this.consumeNewLines();
}
return new Commands.CommandBlock(variablesDecl, commands);
}
parseCommand () {
const token = this.getToken();
if (this.isVariableType(token)) {
if(!this.insideScope(IVProgParser.FUNCTION)) {
throw SyntaxErrorFactory.invalid_var_declaration(token.line);
}
this.pushScope(IVProgParser.BASE);
const varType = this.parseType();
this.popScope();
const cmd = this.parseDeclaration(varType);
this.checkEOS();
this.pos++;
return cmd;
} else if (token.type === this.lexerClass.ID) {
return this.parseIDCommand();
} else if (token.type === this.lexerClass.LIB_ID) {
return this.parseIDCommand();
} else if (token.type === this.lexerClass.RK_RETURN) {
return this.parseReturn();
} else if (token.type === this.lexerClass.RK_WHILE) {
return this.parseWhile();
} else if (token.type === this.lexerClass.RK_FOR) {
return this.parseFor();
} else if (token.type === this.lexerClass.RK_BREAK ) {
if(!this.insideScope(IVProgParser.BREAKABLE)) {
throw SyntaxErrorFactory.invalid_break_command(
this.lexer.literalNames[this.lexerClass.RK_BREAK],
token
);
}
return this.parseBreak();
} else if (token.type === this.lexerClass.RK_SWITCH) {
return this.parseSwitchCase();
} else if (token.type === this.lexerClass.RK_DO) {
return this.parseDoWhile();
} else if (token.type === this.lexerClass.RK_IF) {
return this.parseIfThenElse();
} else if (this.checkEOS(true)){
this.pos++;
return -1;
} else {
return null;
}
}
parseSwitchCase () {
this.pushScope(IVProgParser.BREAKABLE);
this.pos++;
this.checkOpenParenthesis();
this.pos++;
this.consumeNewLines();
const exp = this.parseExpressionOR();
this.consumeNewLines();
this.checkCloseParenthesis();
this.pos++;
this.consumeNewLines();
this.checkOpenCurly();
this.pos++;
this.consumeNewLines();
const casesList = this.parseCases();
this.consumeNewLines();
this.checkCloseCurly();
this.pos++;
this.consumeNewLines();
this.popScope();
return new Commands.Switch(exp, casesList);
}
parseDoWhile () {
this.pos++;
this.consumeNewLines();
this.pushScope(IVProgParser.BREAKABLE);
const commandsBlock = this.parseCommandBlock();
this.consumeNewLines(); //Maybe not...
const whileToken = this.getToken();
if (whileToken.type !== this.lexerClass.RK_WHILE) {
throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_WHILE], whileToken);
}
this.pos++;
this.checkOpenParenthesis();
this.pos++;
this.consumeNewLines();
const condition = this.parseExpressionOR();
this.consumeNewLines();
this.checkCloseParenthesis();
this.pos++;
this.checkEOS();
this.popScope();
return new Commands.DoWhile(condition, commandsBlock);
}
parseIfThenElse () {
if(this.insideScope(IVProgParser.BREAKABLE)) {
this.pushScope(IVProgParser.BREAKABLE);
} else {
this.pushScope(IVProgParser.COMMAND);
}
this.pos++;
this.checkOpenParenthesis();
this.pos++;
this.consumeNewLines();
const logicalExpression = this.parseExpressionOR();
this.consumeNewLines();
this.checkCloseParenthesis();
this.pos++;
this.consumeNewLines();
const cmdBlocks = this.parseCommandBlock();
const maybeElse = this.getToken();
if(maybeElse.type === this.lexerClass.RK_ELSE) {
this.pos++;
this.consumeNewLines();
const maybeIf = this.getToken();
let elseBlock = null;
if(this.checkOpenCurly(true)) {
elseBlock = this.parseCommandBlock();
} else if(maybeIf.type === this.lexerClass.RK_IF) {
elseBlock = this.parseIfThenElse();
} else {
throw SyntaxErrorFactory.token_missing_list([this.lexer.literalNames[this.lexerClass.RK_IF], '{'], maybeIf);
}
return new Commands.IfThenElse(logicalExpression, cmdBlocks, elseBlock);
}
this.popScope();
return new Commands.IfThenElse(logicalExpression, cmdBlocks, null);
}
parseFor () {
this.pushScope(IVProgParser.BREAKABLE);
this.pos++;
this.checkOpenParenthesis();
this.pos++;
this.consumeNewLines();
const attribution = this.parseForAssign();
this.consumeNewLines();
const condition = this.parseExpressionOR();
this.consumeForSemiColon();
const increment = this.parseForAssign(true);
this.checkCloseParenthesis()
this.pos++;
this.consumeNewLines();
const commandsBlock = this.parseCommandBlock();
this.popScope();
return new Commands.For(attribution, condition, increment, commandsBlock);
}
parseWhile () {
this.pushScope(IVProgParser.BREAKABLE);
this.pos++;
this.checkOpenParenthesis();
this.pos++;
this.consumeNewLines();
const logicalExpression = this.parseExpressionOR();
this.consumeNewLines();
this.checkCloseParenthesis();
this.pos++;
this.consumeNewLines();
const cmdBlocks = this.parseCommandBlock();
this.popScope();
return new Commands.While(logicalExpression, cmdBlocks);
}
parseBreak () {
this.pos++;
this.checkEOS();
this.pos++;
return new Commands.Break();
}
parseReturn () {
this.pos++;
let exp = null;
if(!this.checkEOS(true)) {
exp = this.parseExpressionOR();
this.checkEOS();
}
this.pos++;
return new Commands.Return(exp);
}
parseIDCommand () {
const refToken = this.getToken();
const isID = refToken.type === this.lexerClass.ID;
const id = this.parseMaybeLibID();
if(this.checkOpenBrace(true)) {
this.pos++;
let lineExpression = null;
let columnExpression = null;
this.consumeNewLines();
lineExpression = this.parseExpression()
this.consumeNewLines();
this.checkCloseBrace();
this.pos++;
if (this.checkOpenBrace(true)) {
this.pos++
this.consumeNewLines();
columnExpression = this.parseExpression();
this.consumeNewLines();
this.checkCloseBrace();
this.pos++;
}
const equalToken = this.getToken();
if (equalToken.type !== this.lexerClass.EQUAL) {
throw SyntaxErrorFactory.token_missing_one('=', equalToken);
}
this.pos++;
const exp = this.parseExpressionOR();
this.checkEOS();
this.pos++;
const cmd = new Commands.ArrayIndexAssign(id, lineExpression, columnExpression, exp);
cmd.sourceInfo = SourceInfo.createSourceInfo(equalToken);
return cmd;
}
const equalOrParenthesis = this.getToken();
if (isID && equalOrParenthesis.type === this.lexerClass.EQUAL) {
this.pos++
const exp = this.parseExpressionOR();
this.checkEOS();
this.pos++;
const cmd = new Commands.Assign(id, exp);
cmd.sourceInfo = SourceInfo.createSourceInfo(equalOrParenthesis);
return cmd;
} else if (equalOrParenthesis.type === this.lexerClass.OPEN_PARENTHESIS) {
const funcCall = this.parseFunctionCallCommand(id);
this.checkEOS();
this.pos++;
return funcCall;
} else if (isID) {
throw SyntaxErrorFactory.token_missing_list(['=','('], equalOrParenthesis);
} else {
throw SyntaxErrorFactory.invalid_id_format(refToken);
}
}
parseForAssign (isLast = false) {
if(!isLast)
this.consumeNewLines();
if(this.checkEOS(true)) {
return null;
}
const id = this.parseID();
const equal = this.getToken();
if (equal.type !== this.lexerClass.EQUAL) {
throw SyntaxErrorFactory.token_missing_one('=', equal);
}
this.pos++
const exp = this.parseExpressionOR();
if(!isLast) {
this.consumeForSemiColon();
}
const sourceInfo = SourceInfo.createSourceInfo(equal);
const cmd = new Commands.Assign(id, exp);
cmd.sourceInfo = sourceInfo;
return cmd;
}
parseCases () {
const token = this.getToken();
if(token.type !== this.lexerClass.RK_CASE) {
throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_CASE], token);
}
this.pos++;
const nextToken = this.getToken();
if(nextToken.type === this.lexerClass.RK_DEFAULT) {
this.pos++;
const colonToken = this.getToken();
if (colonToken.type !== this.lexerClass.COLON) {
throw SyntaxErrorFactory.token_missing_one(':', colonToken);
}
this.pos++;
this.consumeNewLines();
const block = this.parseCommandBlock(true);
const defaultCase = new Commands.Case(null);
defaultCase.setCommands(block.commands);
return [defaultCase];
} else {
const exp = this.parseExpressionOR();
const colonToken = this.getToken();
if (colonToken.type !== this.lexerClass.COLON) {
throw SyntaxErrorFactory.token_missing_one(':', colonToken);
}
this.pos++;
this.consumeNewLines();
const block = this.parseCommandBlock(true);
const aCase = new Commands.Case(exp);
aCase.setCommands(block.commands);
const caseToken = this.getToken();
if(caseToken.type === this.lexerClass.RK_CASE) {
return [aCase].concat(this.parseCases());
} else {
return [aCase];
}
}
}
/*
* Parses an Expression following the structure:
*
* EOR => EAnd ( 'or' EOR)? #expression and
*
* EOR => ENot ('and' EOR)? #expression or
*
* ENot => 'not'? ER #expression not
*
* ER => E ((>=, <=, ==, >, <) E)? #expression relational
*
* E => factor ((+, -) E)? #expression
*
* factor=> term ((*, /, %) factor)?
*
* term => literal || arrayAccess || FuncCall || ID || '('EAnd')'
**/
parseExpressionOR () {
let exp1 = this.parseExpressionAND();
while (this.getToken().type === this.lexerClass.OR_OPERATOR) {
const opToken = this.getToken();
this.pos++;
const or = convertFromString('or');
this.consumeNewLines();
const exp2 = this.parseExpressionAND();
const finalExp = new Expressions.InfixApp(or, exp1, exp2);
finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
exp1 = finalExp
}
return exp1;
}
parseExpressionAND () {
let exp1 = this.parseExpressionNot();
while (this.getToken().type === this.lexerClass.AND_OPERATOR) {
const opToken = this.getToken();
this.pos++;
const and = convertFromString('and');
this.consumeNewLines();
const exp2 = this.parseExpressionNot();
const finalExp = new Expressions.InfixApp(and, exp1, exp2);
finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
exp1 = finalExp;
}
return exp1;
}
parseExpressionNot () {
const maybeNotToken = this.getToken();
if (maybeNotToken.type === this.lexerClass.NOT_OPERATOR) {
const opToken = this.getToken();
this.pos++;
const not = convertFromString('not');
const exp1 = this.parseExpressionRel();
finalExp = new Expressions.UnaryApp(not, exp1);
finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
return finalExp;
} else {
return this.parseExpressionRel();
}
}
parseExpressionRel () {
let exp1 = this.parseExpression();
while (this.getToken().type === this.lexerClass.RELATIONAL_OPERATOR) {
const relToken = this.getToken();
this.pos++;
const rel = convertFromString(relToken.text);
const exp2 = this.parseExpression();
const finalExp = new Expressions.InfixApp(rel, exp1, exp2);
finalExp.sourceInfo = SourceInfo.createSourceInfo(relToken);
exp1 = finalExp;
}
return exp1;
}
parseExpression () {
let factor = this.parseFactor();
while (this.getToken().type === this.lexerClass.SUM_OP) {
const sumOpToken = this.getToken();
this.pos++;
const op = convertFromString(sumOpToken.text);
const factor2 = this.parseFactor();
const finalExp = new Expressions.InfixApp(op, factor, factor2);
finalExp.sourceInfo = SourceInfo.createSourceInfo(sumOpToken);
factor = finalExp;
}
return factor;
}
parseFactor () {
let term = this.parseTerm();
while (this.getToken().type === this.lexerClass.MULTI_OP) {
const multOpToken = this.getToken();
this.pos++;
const op = convertFromString(multOpToken.text);
const term2 =this.parseTerm();
const finalExp = new Expressions.InfixApp(op, term, term2);
finalExp.sourceInfo = SourceInfo.createSourceInfo(multOpToken);
term = finalExp;
}
return term;
}
parseTerm () {
const token = this.getToken();
let sourceInfo = null;
switch(token.type) {
case this.lexerClass.SUM_OP:
this.pos++;
sourceInfo = SourceInfo.createSourceInfo(token);
const exp = new Expressions.UnaryApp(convertFromString(token.text), this.parseTerm());
exp.sourceInfo = sourceInfo;
return exp;
case this.lexerClass.INTEGER:
this.pos++;
return this.getIntLiteral(token);
case this.lexerClass.REAL:
this.pos++;
return this.getRealLiteral(token);
case this.lexerClass.STRING:
this.pos++;
return this.getStringLiteral(token);
case this.lexerClass.RK_TRUE:
case this.lexerClass.RK_FALSE:
this.pos++;
return this.getBoolLiteral(token);
case this.lexerClass.OPEN_CURLY:
return this.parseArrayLiteral();
case this.lexerClass.ID:
case this.lexerClass.LIB_ID:
return this.parseIDTerm();
case this.lexerClass.OPEN_PARENTHESIS:
return this.parseParenthesisExp();
default:
throw SyntaxErrorFactory.invalid_terminal(token);
}
}
parseIDTerm () {
const tokenA = this.getToken();
const id = this.parseMaybeLibID();
const isID = tokenA.type === this.lexerClass.ID;
if(isID && this.checkOpenBrace(true)) {
let tokenB = null;
this.pos++;
const firstIndex = this.parseExpression();
let secondIndex = null;
this.consumeNewLines();
this.checkCloseBrace();
tokenB = this.getToken();
this.pos++;
if(this.checkOpenBrace(true)){
this.pos++;
secondIndex = this.parseExpression();
this.consumeNewLines();
this.checkCloseBrace();
tokenB = this.getToken();
this.pos++;
}
const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB);
const exp = new Expressions.ArrayAccess(id, firstIndex, secondIndex);
exp.sourceInfo = sourceInfo;
return exp;
} else if (this.checkOpenParenthesis(true)) {
return this.parseFunctionCallExpression(id);
} else if (isID) {
const sourceInfo = SourceInfo.createSourceInfo(tokenA);
const exp = new Expressions.VariableLiteral(id);
exp.sourceInfo = sourceInfo;
return exp;
} else {
throw SyntaxErrorFactory.invalid_id_format(tokenA);
}
}
getFunctionName (id) {
const name = LanguageDefinedFunction.getInternalName(id);
if (name === null) {
if (id === LanguageDefinedFunction.getMainFunctionName()) {
return null;
}
return id;
} else {
return name;
}
}
parseFunctionCallExpression (id) {
const tokenA = this.getToken(this.pos - 1);
const actualParameters = this.parseActualParameters();
const tokenB = this.getToken(this.pos - 1);
const funcName = this.getFunctionName(id);
const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB);
const cmd = new Expressions.FunctionCall(funcName, actualParameters);
cmd.sourceInfo = sourceInfo;
return cmd;
}
parseFunctionCallCommand (id) {
return this.parseFunctionCallExpression(id);
}
parseParenthesisExp () {
this.checkOpenParenthesis();
const tokenA = this.getToken();
this.pos++;
this.consumeNewLines();
const exp = this.parseExpressionOR();
this.consumeNewLines();
this.checkCloseParenthesis();
const tokenB = this.getToken();
const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB);
this.pos++;
exp.sourceInfo = sourceInfo;
return exp;
}
parseActualParameters () {
this.checkOpenParenthesis();
this.pos++;
if(this.checkCloseParenthesis(true)) {
this.pos++;
return [];
}
this.consumeNewLines();
const list = this.parseExpressionList();
this.consumeNewLines();
this.checkCloseParenthesis();
this.pos++;
return list;
}
parseExpressionList () {
const list = [];
while(true) {
const exp = this.parseExpressionOR();
list.push(exp);
const maybeToken = this.getToken();
if (maybeToken.type !== this.lexerClass.COMMA) {
break;
} else {
this.pos++;
this.consumeNewLines();
}
}
return list;
}
getTypeArray () {
const types = this.insideScope(IVProgParser.FUNCTION) ? this.functionTypes : this.variableTypes;
return types.map( x => this.lexer.literalNames[x]);
}
}