ivprogParser.js 31 KB

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