ivprogParser.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. import { CommonTokenStream, InputStream } from 'antlr4/index';
  2. import { ArrayAccess, FunctionCall} from './expressions/';
  3. import { Return, Break, Atribution } from './commands/';
  4. import { SyntaxError } from './SyntaxError';
  5. export class IVProgParser {
  6. constructor (input, lexerClass) {
  7. this.lexerClass = lexerClass;
  8. this.lexer = new lexerClass(new InputStream(input));
  9. this.tokenStream = new CommonTokenStream(this.lexer);
  10. this.tokenStream.fill();
  11. this.pos = 1;
  12. this.variableTypes = [this.lexerClass.RK_INTEGER,
  13. this.lexerClass.RK_REAL,
  14. this.lexerClass.RK_BOOLEAN,
  15. this.lexerClass.RK_STRING
  16. ];
  17. this.functionTypes = this.variableTypes.concat(this.lexerClass.RK_VOID);
  18. }
  19. parseTree () {
  20. return this.parseProgram();
  21. }
  22. getToken (index = this.pos) {
  23. // if(index === null)
  24. // index = this.pos;
  25. return this.tokenStream.LT(index);
  26. }
  27. isEOF () {
  28. this.getToken(this.pos);
  29. return this.tokenStream.fetchedEOF;
  30. }
  31. parseProgram () {
  32. const token = this.getToken();
  33. let globalVars = [];
  34. let functions = [];
  35. if(this.lexerClass.RK_PROGRAM === token.type) {
  36. this.pos++;
  37. this.consumeNewLines();
  38. this.checkOpenCurly();
  39. this.pos++;
  40. while(true) {
  41. this.consumeNewLines();
  42. const token = this.getToken();
  43. if (token.type === this.lexerClass.RK_CONST || token.type === this.lexerClass.ID) {
  44. globalVars = globalVars.concat(this.parseGlobalVariables());
  45. } else if (token.type === this.lexerClass.RK_FUNCTION) {
  46. functions = functions.concat(this.parseFunctions());
  47. } else {
  48. break;
  49. }
  50. }
  51. this.consumeNewLines();
  52. this.checkCloseCurly();
  53. this.pos++;
  54. this.consumeNewLines();
  55. if(!this.isEOF()) {
  56. throw new Error("No extra characters are allowed after 'program {...}'");
  57. }
  58. return {global: globalVars, functions: functions};
  59. } else {
  60. throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.RK_PROGRAM], token);
  61. }
  62. }
  63. checkOpenCurly () {
  64. const token = this.getToken();
  65. if(this.lexerClass.OPEN_CURLY !== token.type){
  66. throw SyntaxError.createError('{', token);
  67. }
  68. }
  69. checkCloseCurly () {
  70. const token = this.getToken();
  71. if(this.lexerClass.CLOSE_CURLY !== token.type){
  72. throw SyntaxError.createError('}', token);
  73. }
  74. }
  75. /* It checks if the current token at position pos is a ']'.
  76. * As a check function it doesn't increment pos.
  77. *
  78. * @params bool:attempt, indicates that the token is optional. Defaults: false
  79. *
  80. * @returns true if the attempt is true and current token is '[',
  81. * false is attempt is true and current token is not '['
  82. **/
  83. checkOpenBrace (attempt = false) {
  84. const token = this.getToken();
  85. if(this.lexerClass.OPEN_BRACE !== token.type){
  86. if (!attempt) {
  87. throw SyntaxError.createError('[', token);
  88. } else {
  89. return false;
  90. }
  91. }
  92. return true;
  93. }
  94. checkCloseBrace (attempt = false) {
  95. const token = this.getToken();
  96. if(this.lexerClass.CLOSE_BRACE !== token.type){
  97. if (!attempt) {
  98. throw SyntaxError.createError(']', token);
  99. } else {
  100. return false;
  101. }
  102. }
  103. return true;
  104. }
  105. checkOpenParenthesis (attempt = false) {
  106. const token = this.getToken();
  107. if(this.lexerClass.OPEN_PARENTHESIS !== token.type){
  108. if (!attempt) {
  109. throw SyntaxError.createError('(', token);
  110. } else {
  111. return false;
  112. }
  113. }
  114. return true;
  115. }
  116. checkCloseParenthesis (attempt = false) {
  117. const token = this.getToken();
  118. if(this.lexerClass.CLOSE_PARENTHESIS !== token.type){
  119. if (!attempt) {
  120. throw SyntaxError.createError(')', token);
  121. } else {
  122. return false;
  123. }
  124. }
  125. return true;
  126. }
  127. checkEOS (attempt = false) {
  128. const eosToken = this.getToken();
  129. if (eosToken.type !== this.lexerClass.EOS) {
  130. if (!attempt)
  131. throw SyntaxError.createError('new line or \';\'', eosToken);
  132. else
  133. return false;
  134. }
  135. return true;
  136. }
  137. parseGlobalVariables () {
  138. let vars = [];
  139. while(true) {
  140. const decl = this.parseMaybeConst();
  141. const eosToken = this.getToken();
  142. if (decl !== null) {
  143. this.checkEOS();
  144. }
  145. if (decl === null) {
  146. break;
  147. } else {
  148. vars = vars.concat(decl);
  149. this.pos++;
  150. }
  151. }
  152. return vars;
  153. }
  154. /*
  155. * Checks if the next token is PR_CONST. It's only available
  156. * at global variables declaration level
  157. * @returns Declararion(const, type, id, initVal?)
  158. **/
  159. parseMaybeConst () {
  160. const constToken = this.getToken();
  161. if(constToken.type === this.lexerClass.RK_CONST) {
  162. this.pos++;
  163. const typeString = this.parseType();
  164. return this.parseDeclararion(typeString, true);
  165. } else if(this.isVariableType(constToken)) {
  166. this.pos++;
  167. return this.parseDeclararion(constToken);
  168. } else {
  169. return null;
  170. }
  171. }
  172. /*
  173. * Parses a declarion of the form: type --- id --- (= --- EAnd)?
  174. * @returns a list of Declararion(const, type, id, initVal?)
  175. **/
  176. parseDeclararion (typeString, isConst = false) {
  177. let initial = null;
  178. let dim1 = null;
  179. let dim2 = null;
  180. const idString = this.parseID();
  181. // Check for array or vector
  182. // ID[int/IDi][int/IDj]
  183. if (this.checkOpenBrace(true)) {
  184. this.pos++;
  185. this.consumeNewLines();
  186. dim1 = this.parseArrayDimension();
  187. this.consumeNewLines();
  188. this.checkCloseBrace();
  189. this.pos++;
  190. if(this.checkOpenBrace(true)) {
  191. this.pos++;
  192. this.consumeNewLines();
  193. dim2 = this.parseArrayDimension();
  194. this.consumeNewLines();
  195. this.checkCloseBrace();
  196. this.pos++;
  197. }
  198. }
  199. const equalsToken = this.getToken();
  200. if(equalsToken.type === this.lexerClass.EQUAL) {
  201. this.pos++;
  202. initial = this.parseExpressionOR();
  203. }
  204. const commaToken = this.getToken();
  205. if(commaToken.type === this.lexerClass.COMMA) {
  206. console.log("comma found");
  207. this.pos++;
  208. this.consumeNewLines();
  209. return [{
  210. isConst: isConst,
  211. tipo: typeString,
  212. id: idString,
  213. lines: dim1,
  214. columns: dim2,
  215. initial: initial
  216. }]
  217. .concat(this.parseDeclararion(typeString, isConst));
  218. } else {
  219. return [{
  220. isConst: isConst,
  221. tipo: typeString,
  222. id: idString,
  223. lines: dim1,
  224. columns: dim2,
  225. initial: initial
  226. }]
  227. }
  228. }
  229. consumeNewLines () {
  230. let token = this.getToken();
  231. while(token.type === this.lexerClass.EOS && token.text.match('[\r\n]+')) {
  232. this.pos++;
  233. token = this.getToken();
  234. }
  235. }
  236. isVariableType (token) {
  237. return this.variableTypes.find(v => v === token.type);
  238. }
  239. /*
  240. * Reads the next token of the stream to check if it is a Integer or an ID.
  241. * @returns Integer | ID
  242. **/
  243. parseArrayDimension () {
  244. const dimToken = this.getToken();
  245. if(dimToken.type === this.lexerClass.INTEGER) {
  246. //parse as int literal
  247. this.pos++;
  248. return this.getIntLiteral(dimToken);
  249. } else if(dimToken.type === this.lexerClass.ID) {
  250. //parse as variable
  251. this.pos++;
  252. return this.parseVariable(dimToken);
  253. } else {
  254. throw SyntaxError.createError('int or ID', dimToken);
  255. }
  256. }
  257. /*
  258. * Returns an object {type: 'int', value: value}.
  259. * It checks for binary and hexadecimal integers.
  260. * @returns object with fields type and value
  261. **/
  262. getIntLiteral (token) {
  263. const text = token.text;
  264. let val = null;
  265. if(text.match('^0b|^0B')) {
  266. val = parseInt(text.substring(2), 2);
  267. } else if (text.match('^0x|^0X')) {
  268. val = parseInt(text.substring(2), 16);
  269. } else {
  270. val = parseInt(text);
  271. }
  272. return {type: 'int', value: val};
  273. }
  274. getRealLiteral (token) {
  275. return {type: 'real', value: parseFloat(token.text)};
  276. }
  277. getStringLiteral (token) {
  278. const text = token.text;
  279. let valor = text.replace("\\b", "\b");
  280. valor = valor.replace("\\t", "\t");
  281. valor = valor.replace("\\n", "\n");
  282. valor = valor.replace("\\r", "\r");
  283. valor = valor.replace("\\\"", "\"");
  284. valor = valor.replace("\\\'", "\'");
  285. valor = valor.replace("\\\\", "\\");
  286. return {type: 'string', value: valor};
  287. }
  288. getBoolLiteral (token) {
  289. const val = token.type === this.lexerClass.RK_True ? true : false;
  290. return {type: 'bool', value: val};
  291. }
  292. parseArrayLiteral () {
  293. }
  294. /*
  295. * Returns an object {type: 'variable', value: value}.
  296. * @returns object with fields type and value
  297. **/
  298. parseVariable (token) {
  299. return {type: 'variable', value: token.text};
  300. }
  301. parseFunctions () {
  302. let list = [];
  303. while(true) {
  304. const func = this.parseFunction();
  305. if(func === null)
  306. break;
  307. else
  308. list.push(func);
  309. }
  310. return list;
  311. }
  312. /*
  313. * Returns an object representing a function. It has
  314. * four attributes: returnType, id, formalParams and block.
  315. * The block object has two attributes: declarations and commands
  316. **/
  317. parseFunction () {
  318. let formalParams = [];
  319. const token = this.getToken();
  320. if(token.type !== this.lexerClass.RK_FUNCTION) {
  321. //throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.PR_FUNCAO], token);
  322. return null;
  323. }
  324. this.pos++;
  325. const returnType = this.parseType(true);
  326. const functionID = this.parseID();
  327. this.checkOpenParenthesis();
  328. this.pos++;
  329. this.consumeNewLines();
  330. if (!this.checkCloseParenthesis(true)) {
  331. formalParams = this.parseFormalParameters(); // formal parameters
  332. this.consumeNewLines();
  333. this.checkCloseParenthesis();
  334. this.pos++;
  335. } else {
  336. this.pos++;
  337. }
  338. this.consumeNewLines();
  339. const commandsBlock = this.parseFunctionBody();
  340. return {returnType: returnType, id: functionID, formalParams: formalParams, block: commandsBlock};
  341. }
  342. /*
  343. * Parse the formal parameters of a function.
  344. * @returns a list of objects with the following attributes: type, id and dimensions.
  345. **/
  346. parseFormalParameters () {
  347. const list = [];
  348. while(true) {
  349. let dimensions = 0;
  350. const typeString = this.parseType();
  351. const idString = this.parseID();
  352. if (this.checkOpenBrace(true)) {
  353. this.pos++;
  354. dimensions++;
  355. this.checkCloseBrace();
  356. this.pos++;
  357. if(this.checkOpenBrace(true)) {
  358. this.pos++;
  359. dimensions++;
  360. this.checkCloseBrace();
  361. this.pos++;
  362. }
  363. }
  364. list.push({type: typeString, id: idString, dimensions: dimensions});
  365. const commaToken = this.getToken();
  366. if (commaToken.type !== this.lexerClass.COMMA)
  367. break;
  368. this.pos++;
  369. this.consumeNewLines();
  370. }
  371. return list;
  372. }
  373. parseID () {
  374. const token = this.getToken();
  375. if(token.type !== this.lexerClass.ID) {
  376. throw SyntaxError.createError('ID', token);
  377. }
  378. this.pos++;
  379. return token.text;
  380. }
  381. parseType (isFunction = false) {
  382. const token = this.getToken();
  383. if(token.type === this.lexerClass.ID && isFunction) {
  384. return 'void';
  385. } else if (token.type === this.lexerClass.RK_VOID && isFunction) {
  386. this.pos++;
  387. return 'void';
  388. } else if (this.isVariableType(token)) {
  389. this.pos++;
  390. switch(token.type) {
  391. case this.lexerClass.RK_INTEGER:
  392. return 'int';
  393. case this.lexerClass.RK_LOGIC:
  394. return 'bool';
  395. case this.lexerClass.RK_REAL:
  396. return 'real';
  397. case this.lexerClass.RK_STRING:
  398. return 'string';
  399. default:
  400. break;
  401. }
  402. }
  403. throw SyntaxError.createError(this.getTypesAsString(isFunction), token);
  404. }
  405. parseFunctionBody () {
  406. let variablesDecl = [];
  407. let commands = [];
  408. this.checkOpenCurly();
  409. this.pos++;
  410. this.consumeNewLines();
  411. while(true) {
  412. const token = this.getToken();
  413. let cmd = null;
  414. if (this.isVariableType(token)) {
  415. this.pos++;
  416. variablesDecl = variablesDecl.concat(this.parseDeclararion(token));
  417. this.checkEOS();
  418. this.pos++;
  419. cmd = -1;
  420. } else if (token.type === this.lexerClass.ID) {
  421. cmd = this.parseIDCommand();
  422. } else if (token.type === this.lexerClass.RK_RETURN) {
  423. cmd = this.parseReturn();
  424. } else if (token.type === this.lexerClass.RK_WHILE) {
  425. } else if (token.type === this.lexerClass.RK_FOR) {
  426. } else if (token.type === this.lexerClass.RK_BREAK) {
  427. cmd = this.parseBreak();
  428. } else if (token.type === this.lexerClass.RK_SWITCH) {
  429. } else if (token.type === this.lexerClass.RK_DO) {
  430. } else if (token.type === this.lexerClass.RK_IF) {
  431. }
  432. if (cmd === null)
  433. break;
  434. if(cmd !== -1)
  435. commands.push(cmd);
  436. }
  437. this.consumeNewLines();
  438. this.checkCloseCurly();
  439. this.pos++;
  440. this.consumeNewLines();
  441. return {variables: variablesDecl, commands: commands};
  442. }
  443. parseBreak () {
  444. this.pos++;
  445. this.checkEOS();
  446. this.pos++;
  447. return (new Break());
  448. }
  449. parseReturn () {
  450. this.pos++;
  451. const exp = this.parseExpressionOR();
  452. this.checkEOS();
  453. this.pos++;
  454. return (new Return(exp));
  455. }
  456. parseIDCommand () {
  457. const id = this.parseID();
  458. const equalOrParenthesis = this.getToken();
  459. if (equalOrParenthesis.type === this.lexerClass.EQUAL) {
  460. this.pos++
  461. const exp = this.parseExpressionOR();
  462. this.checkEOS();
  463. this.pos++;
  464. return (new Atribution(id, exp));
  465. } else if (equalOrParenthesis.type === this.lexerClass.OPEN_PARENTHESIS) {
  466. const actualParameters = this.parseActualParameters();
  467. this.checkEOS();
  468. this.pos++;
  469. return (new FunctionCall(id, actualParameters));
  470. } else {
  471. throw SyntaxError.createError("= or (", equalOrParenthesis);
  472. }
  473. }
  474. /*
  475. * Parses an Expression following the structure:
  476. *
  477. * EOR => EAnd ( 'or' EOR)? #expression and
  478. *
  479. * EOR => ENot ('and' EOR)? #expression or
  480. *
  481. * ENot => 'not'? ER #expression not
  482. *
  483. * ER => E ((>=, <=, ==, >, <) E)? #expression relational
  484. *
  485. * E => factor ((+, -) E)? #expression
  486. *
  487. * factor=> term ((*, /, %) factor)?
  488. *
  489. * term => literal || arrayAccess || FuncCall || ID || '('EAnd')'
  490. **/
  491. parseExpressionOR () {
  492. const exp1 = this.parseExpressionAND();
  493. let exp2 = null;
  494. let or = null;
  495. const maybeAnd = this.getToken();
  496. if (maybeAnd.type === this.lexerClass.OR_OPERATOR) {
  497. this.pos++;
  498. or = 'or';
  499. this.consumeNewLines();
  500. exp2 = this.parseExpressionOR();
  501. }
  502. return {left: exp1, op:or, right: exp2};
  503. }
  504. parseExpressionAND () {
  505. const exp1 = this.parseExpressionNot();
  506. let and = null;
  507. let exp2 = null;
  508. const andToken = this.getToken();
  509. if (andToken.type === this.lexerClass.AND_OPERATOR) {
  510. this.pos++;
  511. and = 'and';
  512. this.consumeNewLines();
  513. exp2 = this.parseExpressionAND();
  514. }
  515. return {left: exp1, op: and, right: exp2};
  516. }
  517. parseExpressionNot () {
  518. let not = null;
  519. const maybeNotToken = this.getToken();
  520. if (maybeNotToken.type === this.lexerClass.NOT_OPERATOR) {
  521. this.pos++;
  522. not = 'not';
  523. }
  524. const eRel = this.parseExpressionRel();
  525. return {left: null, op: not, right: eRel};
  526. }
  527. parseExpressionRel () {
  528. const exp1 = this.parseExpression();
  529. let rel = null;
  530. let exp2 = null;
  531. const relToken = this.getToken();
  532. if(relToken.type === this.lexerClass.RELATIONAL_OPERATOR) {
  533. this.pos++;
  534. rel = relToken.text; // TODO: source code line/column information
  535. exp2 = this.parseExpression();
  536. }
  537. return {left: exp1, op: rel, right: exp2};
  538. }
  539. parseExpression () {
  540. const factor = this.parseFactor();
  541. let op = null;
  542. let exp = null;
  543. const sumOpToken = this.getToken();
  544. if(sumOpToken.type === this.lexerClass.SUM_OP) {
  545. this.pos++;
  546. op = sumOpToken.text; // TODO: source code line/column information
  547. exp = this.parseExpression();
  548. }
  549. return {left: factor, op: op, right: exp};
  550. }
  551. parseFactor () {
  552. const term = this.parseTerm();
  553. let op = null;
  554. let factor = null;
  555. const multOpToken = this.getToken();
  556. if(multOpToken.type === this.lexerClass.MULTI_OP) {
  557. this.pos++;
  558. op = multOpToken.text; // TODO: source code line/column information
  559. factor = this.parseFactor();
  560. }
  561. return {left: term, op: op, right: factor};
  562. }
  563. parseTerm () {
  564. const token = this.getToken();
  565. switch(token.type) {
  566. case this.lexerClass.INTEGER:
  567. this.pos++;
  568. return this.getIntLiteral(token);
  569. case this.lexerClass.REAL:
  570. this.pos++;
  571. return this.getRealLiteral(token);
  572. case this.lexerClass.STRING:
  573. this.pos++;
  574. return this.getStringLiteral(token);
  575. case this.lexerClass.RK_TRUE:
  576. case this.lexerClass.RK_FALSE:
  577. this.pos++;
  578. return this.getBoolLiteral(token);
  579. case this.lexerClass.ID:
  580. return this.parseIDTerm();
  581. case this.lexerClass.OPEN_PARENTHESIS:
  582. return this.parseParenthesisExp();
  583. }
  584. }
  585. parseIDTerm () {
  586. const id = this.parseID();
  587. const last = this.pos;
  588. if(this.checkOpenBrace(true)) {
  589. this.pos++;
  590. const firstIndex = this.parseExpression();
  591. let secondIndex = null;
  592. this.consumeNewLines();
  593. this.checkCloseBrace();
  594. this.pos++;
  595. if(this.checkOpenBrace(true)){
  596. this.pos++;
  597. secondIndex = this.parseExpression();
  598. this.consumeNewLines();
  599. this.checkCloseBrace();
  600. this.pos++;
  601. } else {
  602. this.pos--;
  603. }
  604. return new ArrayAccess(id, firstIndex, secondIndex);
  605. } else if (this.checkOpenParenthesis(true)) {
  606. this.pos++;
  607. this.consumeNewLines();
  608. let actualParameters = [];
  609. if(!this.checkCloseParenthesis(true)) {
  610. actualParameters = this.parseActualParameters();
  611. this.consumeNewLines();
  612. this.checkCloseParenthesis();
  613. this.pos++;
  614. } else {
  615. this.pos++;
  616. }
  617. return new FunctionCall(id, actualParameters);
  618. } else {
  619. this.pos = last;
  620. return id;
  621. }
  622. }
  623. parseParenthesisExp () {
  624. this.checkOpenParenthesis();
  625. this.pos++;
  626. this.consumeNewLines();
  627. const exp = this.parseExpressionOR();
  628. this.consumeNewLines();
  629. this.checkCloseParenthesis();
  630. this.pos++;
  631. return exp;
  632. }
  633. parseActualParameters () {
  634. this.checkOpenParenthesis();
  635. this.pos++;
  636. list = [];
  637. while (true) {
  638. this.consumeNewLines();
  639. const exp = this.parseExpressionOR();
  640. list.push(exp);
  641. const commaToken = this.getToken();
  642. if (commaToken.type !== this.lexerClass.COMMA) {
  643. break;
  644. } else {
  645. this.pos++;
  646. this.consumeNewLines();
  647. }
  648. }
  649. this.consumeNewLines();
  650. this.checkCloseParenthesis();
  651. this.pos++;
  652. return list;
  653. }
  654. getTypesAsString (isFunction = false) {
  655. const types = isFunction ? this.functionTypes : this.variableTypes;
  656. return types.map( x => this.lexer.literalNames[x])
  657. .reduce((o, n) => {
  658. if (o.length <= 0)
  659. return n;
  660. else
  661. return o + ", " + n;
  662. }, '');
  663. }
  664. }