ivprogParser.js 33 KB

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