ivprogParser.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. import { CommonTokenStream, InputStream } from 'antlr4/index';
  2. import * as Expressions from './expressions/';
  3. import * as Commands from './commands/';
  4. import { SourceInfo } from './sourceInfo';
  5. import { Types, toInt, toString } from './types';
  6. import { convertFromString } from './operators';
  7. import { SyntaxErrorFactory } from './error/syntaxErrorFactory';
  8. import { NAMES } from './../processor/definedFunctions';
  9. export class IVProgParser {
  10. // <BEGIN scope consts>
  11. static get BASE () {
  12. return 0;
  13. }
  14. static get FUNCTION () {
  15. return 1;
  16. }
  17. static get COMMAND () {
  18. return 2;
  19. }
  20. static get BREAKABLE () {
  21. return 4;
  22. }
  23. // </ END scope consts>
  24. constructor (input, lexerClass) {
  25. this.lexerClass = lexerClass;
  26. this.lexer = new lexerClass(new InputStream(input));
  27. this.tokenStream = new CommonTokenStream(this.lexer);
  28. this.tokenStream.fill();
  29. this.pos = 1;
  30. this.variableTypes = [this.lexerClass.RK_INTEGER,
  31. this.lexerClass.RK_REAL,
  32. this.lexerClass.RK_BOOLEAN,
  33. this.lexerClass.RK_STRING
  34. ];
  35. this.functionTypes = this.variableTypes.concat(this.lexerClass.RK_VOID);
  36. this.parsingArrayDimension = 0;
  37. this.scope = [];
  38. }
  39. parseTree () {
  40. return this.parseProgram();
  41. }
  42. getToken (index = this.pos) {
  43. // if(index === null)
  44. // index = this.pos;
  45. return this.tokenStream.LT(index);
  46. }
  47. insideScope (scope) {
  48. if(this.scope.length <= 0) {
  49. return IVProgParser.BASE === scope;
  50. } else {
  51. return this.scope[this.scope.length-1] === scope;
  52. }
  53. }
  54. pushScope (scope) {
  55. this.scope.push(scope);
  56. }
  57. popScope () {
  58. return this.scope.pop();
  59. }
  60. isEOF () {
  61. this.getToken(this.pos);
  62. return this.tokenStream.fetchedEOF;
  63. }
  64. parseProgram () {
  65. const token = this.getToken();
  66. let globalVars = [];
  67. let functions = [];
  68. if(this.lexerClass.RK_PROGRAM === token.type) {
  69. this.pos++;
  70. this.consumeNewLines();
  71. this.checkOpenCurly();
  72. this.pos++;
  73. while(true) {
  74. this.consumeNewLines();
  75. const token = this.getToken();
  76. if (token.type === this.lexerClass.RK_CONST || this.isVariableType(token)) {
  77. globalVars = globalVars.concat(this.parseGlobalVariables());
  78. } else if (token.type === this.lexerClass.RK_FUNCTION) {
  79. functions = functions.concat(this.parseFunction());
  80. } else {
  81. break;
  82. }
  83. }
  84. this.consumeNewLines();
  85. this.checkCloseCurly();
  86. this.pos++;
  87. this.consumeNewLines();
  88. if(!this.isEOF()) {
  89. throw SyntaxErrorFactory.extra_lines();
  90. }
  91. return {global: globalVars, functions: functions};
  92. } else {
  93. throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_PROGRAM], token);
  94. }
  95. }
  96. checkOpenCurly (attempt = false) {
  97. const token = this.getToken();
  98. if(this.lexerClass.OPEN_CURLY !== token.type){
  99. if(!attempt)
  100. throw SyntaxErrorFactory.token_missing_one('{', token);
  101. else
  102. return false;
  103. }
  104. return true;
  105. }
  106. checkCloseCurly (attempt = false) {
  107. const token = this.getToken();
  108. if(this.lexerClass.CLOSE_CURLY !== token.type){
  109. if(!attempt)
  110. throw SyntaxErrorFactory.token_missing_one('}', token);
  111. else
  112. return false;
  113. }
  114. return true;
  115. }
  116. /* It checks if the current token at position pos is a ']'.
  117. * As a check function it doesn't increment pos.
  118. *
  119. * @params bool:attempt, indicates that the token is optional. Defaults: false
  120. *
  121. * @returns true if the attempt is true and current token is '[',
  122. * false is attempt is true and current token is not '['
  123. **/
  124. checkOpenBrace (attempt = false) {
  125. const token = this.getToken();
  126. if(this.lexerClass.OPEN_BRACE !== token.type){
  127. if (!attempt) {
  128. throw SyntaxErrorFactory.token_missing_one('[', token);
  129. } else {
  130. return false;
  131. }
  132. }
  133. return true;
  134. }
  135. checkCloseBrace (attempt = false) {
  136. const token = this.getToken();
  137. if(this.lexerClass.CLOSE_BRACE !== token.type){
  138. if (!attempt) {
  139. throw SyntaxErrorFactory.token_missing_one(']', token);
  140. } else {
  141. return false;
  142. }
  143. }
  144. return true;
  145. }
  146. checkOpenParenthesis (attempt = false) {
  147. const token = this.getToken();
  148. if(this.lexerClass.OPEN_PARENTHESIS !== token.type){
  149. if (!attempt) {
  150. throw SyntaxErrorFactory.token_missing_one('(', token);
  151. } else {
  152. return false;
  153. }
  154. }
  155. return true;
  156. }
  157. checkCloseParenthesis (attempt = false) {
  158. const token = this.getToken();
  159. if(this.lexerClass.CLOSE_PARENTHESIS !== token.type){
  160. if (!attempt) {
  161. throw SyntaxErrorFactory.token_missing_one(')', token);
  162. } else {
  163. return false;
  164. }
  165. }
  166. return true;
  167. }
  168. checkEOS (attempt = false) {
  169. const eosToken = this.getToken();
  170. if (eosToken.type !== this.lexerClass.EOS) {
  171. if (!attempt)
  172. throw SyntaxErrorFactory.eos_missing(eosToken);
  173. else
  174. return false;
  175. }
  176. return true;
  177. }
  178. consumeForSemiColon () {
  179. const eosToken = this.getToken();
  180. if (eosToken.type === this.lexerClass.EOS && eosToken.text.match(';')) {
  181. this.pos++;
  182. return;
  183. }
  184. throw SyntaxErrorFactory.token_missing_one(';', eosToken);
  185. }
  186. parseGlobalVariables () {
  187. const decl = this.parseMaybeConst();
  188. this.checkEOS();
  189. this.pos++;
  190. return decl;
  191. }
  192. /*
  193. * Checks if the next token is PR_CONST. It's only available
  194. * at global variables declaration level
  195. * @returns Declararion(const, type, id, initVal?)
  196. **/
  197. parseMaybeConst () {
  198. const constToken = this.getToken();
  199. if(constToken.type === this.lexerClass.RK_CONST) {
  200. this.pos++;
  201. const typeString = this.parseType();
  202. return this.parseDeclaration(typeString, true);
  203. } else if(this.isVariableType(constToken)) {
  204. const typeString = this.parseType();
  205. return this.parseDeclaration(typeString);
  206. } else {
  207. throw SyntaxErrorFactory.token_missing_list(
  208. [this.lexer.literalNames[this.lexerClass.RK_CONST]].concat(this.getTypeArray()), constToken);
  209. }
  210. }
  211. /*
  212. * Parses a declarion of the form: type --- id --- (= --- EAnd)?
  213. * @returns a list of Declararion(const, type, id, initVal?)
  214. **/
  215. parseDeclaration (typeString, isConst = false) {
  216. let initial = null;
  217. let dim1 = null;
  218. let dim2 = null;
  219. const idToken = this.getToken();
  220. const idString = this.parseID();
  221. // Check for array or vector
  222. // ID[int/IDi][int/IDj]
  223. if (this.checkOpenBrace(true)) {
  224. this.pos++;
  225. this.consumeNewLines();
  226. dim1 = this.parseArrayDimension();
  227. this.consumeNewLines();
  228. this.checkCloseBrace();
  229. this.pos++;
  230. if(this.checkOpenBrace(true)) {
  231. this.pos++;
  232. this.consumeNewLines();
  233. dim2 = this.parseArrayDimension();
  234. this.consumeNewLines();
  235. this.checkCloseBrace();
  236. this.pos++;
  237. }
  238. }
  239. const equalsToken = this.getToken();
  240. if(isConst && equalsToken.type !== this.lexerClass.EQUAL ) {
  241. throw SyntaxErrorFactory.const_not_init(idToken);
  242. }
  243. if(equalsToken.type === this.lexerClass.EQUAL) {
  244. this.pos++;
  245. initial = this.parseExpressionOR();
  246. }
  247. let declaration = null;
  248. if (dim1 !== null) {
  249. declaration = new Commands.ArrayDeclaration(idString,
  250. typeString, dim1, dim2, initial, isConst);
  251. } else {
  252. declaration = new Commands.Declaration(idString, typeString, initial, isConst);
  253. }
  254. declaration.sourceInfo = SourceInfo.createSourceInfo(idToken);
  255. const commaToken = this.getToken();
  256. if(commaToken.type === this.lexerClass.COMMA) {
  257. this.pos++;
  258. this.consumeNewLines();
  259. return [declaration]
  260. .concat(this.parseDeclaration(typeString, isConst));
  261. } else {
  262. return [declaration]
  263. }
  264. }
  265. consumeNewLines () {
  266. let token = this.getToken();
  267. while(token.type === this.lexerClass.EOS && token.text.match('[\r\n]+')) {
  268. this.pos++;
  269. token = this.getToken();
  270. }
  271. }
  272. isVariableType (token) {
  273. return this.variableTypes.find(v => v === token.type);
  274. }
  275. /*
  276. * Reads the next token of the stream to check if it is a Integer or an ID.
  277. * @returns Integer | ID
  278. **/
  279. parseArrayDimension () {
  280. const dimToken = this.getToken();
  281. if(dimToken.type === this.lexerClass.INTEGER) {
  282. //parse as int literal
  283. this.pos++;
  284. return this.getIntLiteral(dimToken);
  285. } else if(dimToken.type === this.lexerClass.ID) {
  286. //parse as variable
  287. this.pos++;
  288. return this.parseVariable(dimToken);
  289. } else {
  290. throw SyntaxErrorFactory.invalid_array_dimension(this.lexer.literalNames[this.lexerClass.RK_INTEGER], dimToken);
  291. }
  292. }
  293. /*
  294. * Returns an object {type: 'int', value: value}.
  295. * It checks for binary and hexadecimal integers.
  296. * @returns object with fields type and value
  297. **/
  298. getIntLiteral (token) {
  299. const text = token.text;
  300. const sourceInfo = SourceInfo.createSourceInfo(token);
  301. const exp = new Expressions.IntLiteral(toInt(text));
  302. exp.sourceInfo = sourceInfo;
  303. return exp;
  304. }
  305. getRealLiteral (token) {
  306. const sourceInfo = SourceInfo.createSourceInfo(token);
  307. const exp = new Expressions.RealLiteral(parseFloat(token.text));
  308. exp.sourceInfo = sourceInfo;
  309. return exp;
  310. }
  311. getStringLiteral (token) {
  312. const text = token.text;
  313. const sourceInfo = SourceInfo.createSourceInfo(token);
  314. const exp = new Expressions.StringLiteral(toString(text));
  315. exp.sourceInfo = sourceInfo;
  316. return exp;
  317. }
  318. getBoolLiteral (token) {
  319. const val = token.type === this.lexerClass.RK_TRUE ? true : false;
  320. const sourceInfo = SourceInfo.createSourceInfo(token);
  321. const exp = new Expressions.BoolLiteral(val);
  322. exp.sourceInfo = sourceInfo;
  323. return exp;
  324. }
  325. parseArrayLiteral () {
  326. this.checkOpenCurly();
  327. const beginArray = this.getToken();
  328. if (this.parsingArrayDimension >= 2) {
  329. // TODO: better error message
  330. throw SyntaxErrorFactory.token_missing_list(`Array dimensions exceed maximum size of 2 at line ${beginArray.line}`);
  331. }
  332. this.pos++;
  333. this.parsingArrayDimension++;
  334. this.consumeNewLines();
  335. const data = this.parseExpressionList();
  336. this.consumeNewLines();
  337. this.checkCloseCurly()
  338. const endArray = this.getToken();
  339. this.pos++;
  340. this.parsingArrayDimension--;
  341. if (this.parsingArrayDimension === 0) {
  342. // if (!data.isValid) {
  343. // // TODO: better error message
  344. // console.log('invalid array');
  345. // throw new Error(`Invalid array at line ${beginArray.line}`);
  346. // }
  347. }
  348. const sourceInfo = SourceInfo.createSourceInfoFromList(beginArray, endArray);
  349. const exp = new Expressions.ArrayLiteral(data);
  350. exp.sourceInfo = sourceInfo;
  351. return exp;
  352. }
  353. /*
  354. * Returns an object {type: 'variable', value: value}.
  355. * @returns object with fields type and value
  356. **/
  357. parseVariable (token) {
  358. const sourceInfo = SourceInfo.createSourceInfo(token);
  359. const exp = new Expressions.VariableLiteral(token.text);
  360. exp.sourceInfo = sourceInfo;
  361. return exp;
  362. }
  363. /*
  364. * Returns an object representing a function. It has
  365. * four attributes: returnType, id, formalParams and block.
  366. * The block object has two attributes: declarations and commands
  367. **/
  368. parseFunction () {
  369. this.pushScope(IVProgParser.FUNCTION);
  370. let formalParams = [];
  371. const token = this.getToken();
  372. if(token.type !== this.lexerClass.RK_FUNCTION) {
  373. //throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.PR_FUNCAO], token);
  374. return null;
  375. }
  376. this.pos++;
  377. const returnType = this.parseType();
  378. const functionID = this.parseID();
  379. this.checkOpenParenthesis();
  380. this.pos++;
  381. this.consumeNewLines();
  382. if (!this.checkCloseParenthesis(true)) {
  383. formalParams = this.parseFormalParameters(); // formal parameters
  384. this.consumeNewLines();
  385. this.checkCloseParenthesis();
  386. this.pos++;
  387. } else {
  388. this.pos++;
  389. }
  390. this.consumeNewLines();
  391. const commandsBlock = this.parseCommandBlock();
  392. const func = new Commands.Function(functionID, returnType, formalParams, commandsBlock);
  393. if (functionID === null && !func.isMain) {
  394. // TODO: better error message
  395. throw SyntaxErrorFactory.invalid_main_return(this.lexerClass.MAIN_FUNCTION_NAME,
  396. this.lexer.literalNames[this.lexerClass.RK_VOID],
  397. token.line);
  398. }
  399. this.popScope();
  400. return func;
  401. }
  402. /*
  403. * Parse the formal parameters of a function.
  404. * @returns a list of objects with the following attributes: type, id and dimensions.
  405. **/
  406. parseFormalParameters () {
  407. const list = [];
  408. while(true) {
  409. let dimensions = 0;
  410. const typeString = this.parseType();
  411. const idString = this.parseID();
  412. if (this.checkOpenBrace(true)) {
  413. this.pos++;
  414. dimensions++;
  415. this.checkCloseBrace();
  416. this.pos++;
  417. if(this.checkOpenBrace(true)) {
  418. this.pos++;
  419. dimensions++;
  420. this.checkCloseBrace();
  421. this.pos++;
  422. }
  423. }
  424. list.push(new Commands.FormalParameter(typeString, idString, dimensions));
  425. const commaToken = this.getToken();
  426. if (commaToken.type !== this.lexerClass.COMMA)
  427. break;
  428. this.pos++;
  429. this.consumeNewLines();
  430. }
  431. return list;
  432. }
  433. parseID () {
  434. const token = this.getToken();
  435. if(token.type !== this.lexerClass.ID) {
  436. throw SyntaxErrorFactory.id_missing(token);
  437. }
  438. this.pos++;
  439. if (this.insideScope(IVProgParser.FUNCTION)) {
  440. if (token.text === this.lexerClass.MAIN_FUNCTION_NAME){
  441. return null;
  442. }
  443. }
  444. return token.text;
  445. }
  446. parseType () {
  447. const token = this.getToken();
  448. if(token.type === this.lexerClass.ID && this.insideScope(IVProgParser.FUNCTION)) {
  449. return Types.VOID;
  450. } else if (token.type === this.lexerClass.RK_VOID && this.insideScope(IVProgParser.FUNCTION)) {
  451. this.pos++;
  452. return Types.VOID;
  453. } else if (this.isVariableType(token)) {
  454. this.pos++;
  455. switch(token.type) {
  456. case this.lexerClass.RK_INTEGER:
  457. return Types.INTEGER;
  458. case this.lexerClass.RK_LOGIC:
  459. return Types.BOOLEAN;
  460. case this.lexerClass.RK_REAL:
  461. return Types.REAL;
  462. case this.lexerClass.RK_STRING:
  463. return Types.STRING;
  464. default:
  465. break;
  466. }
  467. }
  468. throw SyntaxErrorFactory.invalid_type(this.getTypeArray(), token);
  469. }
  470. parseCommandBlock (optionalCurly = false) {
  471. let variablesDecl = [];
  472. const commands = [];
  473. let hasOpen = false;
  474. if (this.checkOpenCurly(optionalCurly)) {
  475. this.pos++;
  476. hasOpen = true;
  477. }
  478. this.consumeNewLines();
  479. while(true) {
  480. const cmd = this.parseCommand();
  481. if (cmd === null)
  482. break;
  483. if(cmd !== -1) {
  484. if (cmd instanceof Array) {
  485. variablesDecl = variablesDecl.concat(cmd);
  486. } else {
  487. commands.push(cmd);
  488. }
  489. }
  490. }
  491. this.consumeNewLines();
  492. if (hasOpen) {
  493. this.checkCloseCurly()
  494. this.pos++;
  495. this.consumeNewLines();
  496. }
  497. return new Commands.CommandBlock(variablesDecl, commands);
  498. }
  499. parseCommand () {
  500. const token = this.getToken();
  501. if (this.isVariableType(token)) {
  502. if(!this.insideScope(IVProgParser.FUNCTION)) {
  503. // TODO better error message
  504. throw SyntaxErrorFactory.invalid_var_declaration(token.line);
  505. }
  506. this.pushScope(IVProgParser.BASE);
  507. const varType = this.parseType();
  508. this.popScope();
  509. const cmd = this.parseDeclaration(varType);
  510. this.checkEOS();
  511. this.pos++;
  512. return cmd;
  513. } else if (token.type === this.lexerClass.ID) {
  514. return this.parseIDCommand();
  515. } else if (token.type === this.lexerClass.RK_RETURN) {
  516. return this.parseReturn();
  517. } else if (token.type === this.lexerClass.RK_WHILE) {
  518. return this.parseWhile();
  519. } else if (token.type === this.lexerClass.RK_FOR) {
  520. return this.parseFor();
  521. } else if (token.type === this.lexerClass.RK_BREAK ) {
  522. if(!this.insideScope(IVProgParser.BREAKABLE)) {
  523. // TODO better error message
  524. throw SyntaxErrorFactory.invalid_break_command(
  525. this.lexer.literalNames[this.lexerClass.RK_BREAK],
  526. token
  527. );
  528. }
  529. return this.parseBreak();
  530. } else if (token.type === this.lexerClass.RK_SWITCH) {
  531. return this.parseSwitchCase();
  532. } else if (token.type === this.lexerClass.RK_DO) {
  533. return this.parseDoWhile();
  534. } else if (token.type === this.lexerClass.RK_IF) {
  535. return this.parseIfThenElse();
  536. } else if (this.checkEOS(true)){
  537. this.pos++;
  538. return -1;
  539. } else {
  540. return null;
  541. }
  542. }
  543. parseSwitchCase () {
  544. this.pushScope(IVProgParser.BREAKABLE);
  545. this.pos++;
  546. this.checkOpenParenthesis();
  547. this.pos++;
  548. this.consumeNewLines();
  549. const exp = this.parseExpressionOR();
  550. this.consumeNewLines();
  551. this.checkCloseParenthesis();
  552. this.pos++;
  553. this.consumeNewLines();
  554. this.checkOpenCurly();
  555. this.pos++;
  556. this.consumeNewLines();
  557. const casesList = this.parseCases();
  558. this.consumeNewLines();
  559. this.checkCloseCurly();
  560. this.pos++;
  561. this.consumeNewLines();
  562. this.popScope();
  563. return new Commands.Switch(exp, casesList);
  564. }
  565. parseDoWhile () {
  566. this.pos++;
  567. this.consumeNewLines();
  568. this.pushScope(IVProgParser.BREAKABLE);
  569. const commandsBlock = this.parseCommandBlock();
  570. this.consumeNewLines(); //Maybe not...
  571. const whileToken = this.getToken();
  572. if (whileToken.type !== this.lexerClass.RK_WHILE) {
  573. throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_WHILE], whileToken);
  574. }
  575. this.pos++;
  576. this.checkOpenParenthesis();
  577. this.pos++;
  578. this.consumeNewLines();
  579. const condition = this.parseExpressionOR();
  580. this.consumeNewLines();
  581. this.checkCloseParenthesis();
  582. this.pos++;
  583. this.checkEOS();
  584. this.popScope();
  585. return new Commands.DoWhile(condition, commandsBlock);
  586. }
  587. parseIfThenElse () {
  588. if(this.insideScope(IVProgParser.BREAKABLE)) {
  589. this.pushScope(IVProgParser.BREAKABLE);
  590. } else {
  591. this.pushScope(IVProgParser.COMMAND);
  592. }
  593. this.pos++;
  594. this.checkOpenParenthesis();
  595. this.pos++;
  596. this.consumeNewLines();
  597. const logicalExpression = this.parseExpressionOR();
  598. this.consumeNewLines();
  599. this.checkCloseParenthesis();
  600. this.pos++;
  601. this.consumeNewLines();
  602. const cmdBlocks = this.parseCommandBlock();
  603. const maybeElse = this.getToken();
  604. if(maybeElse.type === this.lexerClass.RK_ELSE) {
  605. this.pos++;
  606. this.consumeNewLines();
  607. const maybeIf = this.getToken();
  608. let elseBlock = null;
  609. if(this.checkOpenCurly(true)) {
  610. elseBlock = this.parseCommandBlock();
  611. } else if(maybeIf.type === this.lexerClass.RK_IF) {
  612. elseBlock = this.parseIfThenElse();
  613. } else {
  614. // TODO better error message
  615. throw SyntaxErrorFactory.token_missing_list([this.lexer.literalNames[this.lexerClass.RK_IF], '{'], maybeIf);
  616. }
  617. return new Commands.IfThenElse(logicalExpression, cmdBlocks, elseBlock);
  618. }
  619. this.popScope();
  620. return new Commands.IfThenElse(logicalExpression, cmdBlocks, null);
  621. }
  622. parseFor () {
  623. this.pushScope(IVProgParser.BREAKABLE);
  624. this.pos++;
  625. this.checkOpenParenthesis();
  626. this.pos++;
  627. this.consumeNewLines();
  628. const attribution = this.parseForAssign();
  629. this.consumeNewLines();
  630. const condition = this.parseExpressionOR();
  631. this.consumeForSemiColon();
  632. const increment = this.parseForAssign(true);
  633. this.checkCloseParenthesis()
  634. this.pos++;
  635. this.consumeNewLines();
  636. const commandsBlock = this.parseCommandBlock();
  637. this.popScope();
  638. return new Commands.For(attribution, condition, increment, commandsBlock);
  639. }
  640. parseWhile () {
  641. this.pushScope(IVProgParser.BREAKABLE);
  642. this.pos++;
  643. this.checkOpenParenthesis();
  644. this.pos++;
  645. this.consumeNewLines();
  646. const logicalExpression = this.parseExpressionOR();
  647. this.consumeNewLines();
  648. this.checkCloseParenthesis();
  649. this.pos++;
  650. this.consumeNewLines();
  651. const cmdBlocks = this.parseCommandBlock();
  652. this.popScope();
  653. return new Commands.While(logicalExpression, cmdBlocks);
  654. }
  655. parseBreak () {
  656. this.pos++;
  657. this.checkEOS();
  658. this.pos++;
  659. return new Commands.Break();
  660. }
  661. parseReturn () {
  662. this.pos++;
  663. let exp = null;
  664. if(!this.checkEOS(true)) {
  665. exp = this.parseExpressionOR();
  666. this.checkEOS();
  667. }
  668. this.pos++;
  669. return new Commands.Return(exp);
  670. }
  671. parseIDCommand () {
  672. const id = this.parseID();
  673. const equalOrParenthesis = this.getToken();
  674. if (equalOrParenthesis.type === this.lexerClass.EQUAL) {
  675. const sourceInfo = SourceInfo.createSourceInfo(this.getToken());
  676. this.pos++
  677. const exp = this.parseExpressionOR();
  678. this.checkEOS();
  679. this.pos++;
  680. const cmd = new Commands.Assign(id, exp);
  681. cmd.sourceInfo = sourceInfo;
  682. return cmd;
  683. } else if (equalOrParenthesis.type === this.lexerClass.OPEN_PARENTHESIS) {
  684. const funcCall = this.parseFunctionCallCommand(id);
  685. this.checkEOS();
  686. this.pos++;
  687. return funcCall;
  688. } else {
  689. throw SyntaxErrorFactory.token_missing_list(['=','('], equalOrParenthesis);
  690. }
  691. }
  692. parseForAssign (isLast = false) {
  693. if(!isLast)
  694. this.consumeNewLines();
  695. if(this.checkEOS(true)) {
  696. return null;
  697. }
  698. const id = this.parseID();
  699. const equal = this.getToken();
  700. if (equal.type !== this.lexerClass.EQUAL) {
  701. throw SyntaxErrorFactory.token_missing_one('=', equal);
  702. }
  703. this.pos++
  704. const exp = this.parseExpressionOR();
  705. if(!isLast) {
  706. this.consumeForSemiColon();
  707. }
  708. const sourceInfo = SourceInfo.createSourceInfo(equal);
  709. const cmd = new Commands.Assign(id, exp);
  710. cmd.sourceInfo = sourceInfo;
  711. return cmd;
  712. }
  713. parseCases () {
  714. const token = this.getToken();
  715. if(token.type !== this.lexerClass.RK_CASE) {
  716. throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_CASE], token);
  717. }
  718. this.pos++;
  719. const nextToken = this.getToken();
  720. if(nextToken.type === this.lexerClass.RK_DEFAULT) {
  721. this.pos++;
  722. const colonToken = this.getToken();
  723. if (colonToken.type !== this.lexerClass.COLON) {
  724. throw SyntaxErrorFactory.token_missing_one(':', colonToken);
  725. }
  726. this.pos++;
  727. this.consumeNewLines();
  728. const block = this.parseCommandBlock(true);
  729. const defaultCase = new Commands.Case(null);
  730. defaultCase.setCommands(block.commands);
  731. return [defaultCase];
  732. } else {
  733. const exp = this.parseExpressionOR();
  734. const colonToken = this.getToken();
  735. if (colonToken.type !== this.lexerClass.COLON) {
  736. throw SyntaxErrorFactory.token_missing_one(':', colonToken);
  737. }
  738. this.pos++;
  739. this.consumeNewLines();
  740. const block = this.parseCommandBlock(true);
  741. const aCase = new Commands.Case(exp);
  742. aCase.setCommands(block.commands);
  743. const caseToken = this.getToken();
  744. if(caseToken.type === this.lexerClass.RK_CASE) {
  745. return [aCase].concat(this.parseCases());
  746. } else {
  747. return [aCase];
  748. }
  749. }
  750. }
  751. /*
  752. * Parses an Expression following the structure:
  753. *
  754. * EOR => EAnd ( 'or' EOR)? #expression and
  755. *
  756. * EOR => ENot ('and' EOR)? #expression or
  757. *
  758. * ENot => 'not'? ER #expression not
  759. *
  760. * ER => E ((>=, <=, ==, >, <) E)? #expression relational
  761. *
  762. * E => factor ((+, -) E)? #expression
  763. *
  764. * factor=> term ((*, /, %) factor)?
  765. *
  766. * term => literal || arrayAccess || FuncCall || ID || '('EAnd')'
  767. **/
  768. parseExpressionOR () {
  769. let exp1 = this.parseExpressionAND();
  770. while (this.getToken().type === this.lexerClass.OR_OPERATOR) {
  771. const opToken = this.getToken();
  772. this.pos++;
  773. const or = convertFromString('or');
  774. this.consumeNewLines();
  775. const exp2 = this.parseExpressionAND();
  776. const finalExp = new Expressions.InfixApp(or, exp1, exp2);
  777. finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
  778. exp1 = finalExp
  779. }
  780. return exp1;
  781. }
  782. parseExpressionAND () {
  783. let exp1 = this.parseExpressionNot();
  784. while (this.getToken().type === this.lexerClass.AND_OPERATOR) {
  785. const opToken = this.getToken();
  786. this.pos++;
  787. const and = convertFromString('and');
  788. this.consumeNewLines();
  789. const exp2 = this.parseExpressionNot();
  790. const finalExp = new Expressions.InfixApp(and, exp1, exp2);
  791. finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
  792. exp1 = finalExp;
  793. }
  794. return exp1;
  795. }
  796. parseExpressionNot () {
  797. const maybeNotToken = this.getToken();
  798. if (maybeNotToken.type === this.lexerClass.NOT_OPERATOR) {
  799. const opToken = this.getToken();
  800. this.pos++;
  801. const not = convertFromString('not');
  802. const exp1 = this.parseExpressionRel();
  803. finalExp = new Expressions.UnaryApp(not, exp1);
  804. finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
  805. return finalExp;
  806. } else {
  807. return this.parseExpressionRel();
  808. }
  809. }
  810. parseExpressionRel () {
  811. let exp1 = this.parseExpression();
  812. while (this.getToken().type === this.lexerClass.RELATIONAL_OPERATOR) {
  813. const relToken = this.getToken();
  814. this.pos++;
  815. const rel = convertFromString(relToken.text);
  816. const exp2 = this.parseExpression();
  817. const finalExp = new Expressions.InfixApp(rel, exp1, exp2);
  818. finalExp.sourceInfo = SourceInfo.createSourceInfo(relToken);
  819. exp1 = finalExp;
  820. }
  821. return exp1;
  822. }
  823. parseExpression () {
  824. let factor = this.parseFactor();
  825. while (this.getToken().type === this.lexerClass.SUM_OP) {
  826. const sumOpToken = this.getToken();
  827. this.pos++;
  828. const op = convertFromString(sumOpToken.text);
  829. const factor2 = this.parseFactor();
  830. const finalExp = new Expressions.InfixApp(op, factor, factor2);
  831. finalExp.sourceInfo = SourceInfo.createSourceInfo(sumOpToken);
  832. factor = finalExp;
  833. }
  834. return factor;
  835. }
  836. parseFactor () {
  837. let term = this.parseTerm();
  838. while (this.getToken().type === this.lexerClass.MULTI_OP) {
  839. const multOpToken = this.getToken();
  840. this.pos++;
  841. const op = convertFromString(multOpToken.text);
  842. const term2 =this.parseTerm();
  843. const finalExp = new Expressions.InfixApp(op, term, term2);
  844. finalExp.sourceInfo = SourceInfo.createSourceInfo(multOpToken);
  845. term = finalExp;
  846. }
  847. return term;
  848. }
  849. parseTerm () {
  850. const token = this.getToken();
  851. let sourceInfo = null;
  852. switch(token.type) {
  853. case this.lexerClass.SUM_OP:
  854. this.pos++;
  855. sourceInfo = SourceInfo.createSourceInfo(token);
  856. const exp = new Expressions.UnaryApp(convertFromString(token.text), this.parseTerm());
  857. exp.sourceInfo = sourceInfo;
  858. return exp;
  859. case this.lexerClass.INTEGER:
  860. this.pos++;
  861. return this.getIntLiteral(token);
  862. case this.lexerClass.REAL:
  863. this.pos++;
  864. return this.getRealLiteral(token);
  865. case this.lexerClass.STRING:
  866. this.pos++;
  867. return this.getStringLiteral(token);
  868. case this.lexerClass.RK_TRUE:
  869. case this.lexerClass.RK_FALSE:
  870. this.pos++;
  871. return this.getBoolLiteral(token);
  872. case this.lexerClass.OPEN_CURLY:
  873. return this.parseArrayLiteral();
  874. case this.lexerClass.ID:
  875. return this.parseIDTerm();
  876. case this.lexerClass.OPEN_PARENTHESIS:
  877. return this.parseParenthesisExp();
  878. default:
  879. throw SyntaxErrorFactory.invalid_terminal(token);
  880. }
  881. }
  882. parseIDTerm () {
  883. const id = this.parseID();
  884. const tokenA = this.getToken(this.pos - 1);
  885. if(this.checkOpenBrace(true)) {
  886. let tokenB = null;
  887. this.pos++;
  888. const firstIndex = this.parseExpression();
  889. let secondIndex = null;
  890. this.consumeNewLines();
  891. this.checkCloseBrace();
  892. tokenB = this.getToken();
  893. this.pos++;
  894. if(this.checkOpenBrace(true)){
  895. this.pos++;
  896. secondIndex = this.parseExpression();
  897. this.consumeNewLines();
  898. this.checkCloseBrace();
  899. tokenB = this.getToken();
  900. this.pos++;
  901. }
  902. const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB);
  903. const exp = new Expressions.ArrayAccess(id, firstIndex, secondIndex);
  904. exp.sourceInfo = sourceInfo;
  905. return exp;
  906. } else if (this.checkOpenParenthesis(true)) {
  907. return this.parseFunctionCallExpression(id);
  908. } else {
  909. const sourceInfo = SourceInfo.createSourceInfo(tokenA);
  910. const exp = new Expressions.VariableLiteral(id);
  911. exp.sourceInfo = sourceInfo;
  912. return exp;
  913. }
  914. }
  915. getFunctionName (id) {
  916. if (id === this.lexerClass.READ_FUNCTION_NAME) {
  917. return NAMES.READ;
  918. } else if (id === this.lexerClass.WRITE_FUNCTION_NAME) {
  919. return NAMES.WRITE;
  920. } else {
  921. return id;
  922. }
  923. }
  924. parseFunctionCallExpression (id) {
  925. const tokenA = this.getToken(this.pos - 1);
  926. const actualParameters = this.parseActualParameters();
  927. const tokenB = this.getToken(this.pos - 1);
  928. const funcName = this.getFunctionName(id);
  929. const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB);
  930. const cmd = new Expressions.FunctionCall(funcName, actualParameters);
  931. cmd.sourceInfo = sourceInfo;
  932. return cmd;
  933. }
  934. parseFunctionCallCommand (id) {
  935. return this.parseFunctionCallExpression(id);
  936. }
  937. parseParenthesisExp () {
  938. this.checkOpenParenthesis();
  939. const tokenA = this.getToken();
  940. this.pos++;
  941. this.consumeNewLines();
  942. const exp = this.parseExpressionOR();
  943. this.consumeNewLines();
  944. this.checkCloseParenthesis();
  945. const tokenB = this.getToken();
  946. const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB);
  947. this.pos++;
  948. exp.sourceInfo = sourceInfo;
  949. return exp;
  950. }
  951. parseActualParameters () {
  952. this.checkOpenParenthesis();
  953. this.pos++;
  954. if(this.checkCloseParenthesis(true)) {
  955. this.pos++;
  956. return [];
  957. }
  958. this.consumeNewLines();
  959. const list = this.parseExpressionList();
  960. this.consumeNewLines();
  961. this.checkCloseParenthesis();
  962. this.pos++;
  963. return list;
  964. }
  965. parseExpressionList () {
  966. const list = [];
  967. while(true) {
  968. const exp = this.parseExpressionOR();
  969. list.push(exp);
  970. const maybeToken = this.getToken();
  971. if (maybeToken.type !== this.lexerClass.COMMA) {
  972. break;
  973. } else {
  974. this.pos++;
  975. this.consumeNewLines();
  976. }
  977. }
  978. return list;
  979. }
  980. getTypeArray () {
  981. const types = this.insideScope(IVProgParser.FUNCTION) ? this.functionTypes : this.variableTypes;
  982. return types.map( x => this.lexer.literalNames[x]);
  983. }
  984. }