ivprogParser.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  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([]);
  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. dim1 = this.parseArrayDimension();
  186. this.checkCloseBrace();
  187. this.pos++;
  188. if(this.checkOpenBrace(true)) {
  189. this.pos++;
  190. dim2 = this.parseArrayDimension();
  191. this.checkCloseBrace();
  192. this.pos++;
  193. }
  194. }
  195. const equalsToken = this.getToken();
  196. if(equalsToken.type === this.lexerClass.EQUAL) {
  197. this.pos++;
  198. this.consumeNewLines();
  199. initial = this.parseExpressionOR();
  200. }
  201. this.consumeNewLines();
  202. const commaToken = this.getToken();
  203. if(commaToken.type === this.lexerClass.COMMA) {
  204. console.log("comma found");
  205. this.pos++;
  206. return [{
  207. isConst: isConst,
  208. tipo: typeString,
  209. id: idString,
  210. lines: dim1,
  211. columns: dim2,
  212. initial: initial
  213. }]
  214. .concat(this.parseDeclararion(typeString, isConst));
  215. } else {
  216. this.pos--;
  217. return [{
  218. isConst: isConst,
  219. tipo: typeString,
  220. id: idString,
  221. lines: dim1,
  222. columns: dim2,
  223. initial: initial
  224. }]
  225. }
  226. }
  227. consumeNewLines () {
  228. let token = this.getToken();
  229. while(token.type === this.lexerClass.EOS && token.text.match('[\r\n]+')) {
  230. this.pos++;
  231. token = this.getToken();
  232. }
  233. }
  234. isVariableType (token) {
  235. return this.variableTypes.find(v => v === token.type);
  236. }
  237. /*
  238. * Reads the next token of the stream to check if it is a Integer or an ID.
  239. * @returns Integer | ID
  240. **/
  241. parseArrayDimension () {
  242. const dimToken = this.getToken();
  243. if(dimToken.type === this.lexerClass.INTEGER) {
  244. //parse as int literal
  245. this.pos++;
  246. return this.getIntLiteral(dimToken);
  247. } else if(dimToken.type === this.lexerClass.ID) {
  248. //parse as variable
  249. this.pos++;
  250. return this.parseVariable(dimToken);
  251. } else {
  252. throw SyntaxError.createError('int or ID', dimToken);
  253. }
  254. }
  255. /*
  256. * Returns an object {type: 'int', value: value}.
  257. * It checks for binary and hexadecimal integers.
  258. * @returns object with fields type and value
  259. **/
  260. getIntLiteral (token) {
  261. const text = token.text;
  262. let val = null;
  263. if(text.match('^0b|^0B')) {
  264. val = parseInt(text.substring(2), 2);
  265. } else if (text.match('^0x|^0X')) {
  266. val = parseInt(text.substring(2), 16);
  267. } else {
  268. val = parseInt(text);
  269. }
  270. return {type: 'int', value: val};
  271. }
  272. getRealLiteral (token) {
  273. return {type: 'real', value: parseFloat(token.text)};
  274. }
  275. getStringLiteral (token) {
  276. const text = token.text;
  277. let valor = text.replace("\\b", "\b");
  278. valor = valor.replace("\\t", "\t");
  279. valor = valor.replace("\\n", "\n");
  280. valor = valor.replace("\\r", "\r");
  281. valor = valor.replace("\\\"", "\"");
  282. valor = valor.replace("\\\'", "\'");
  283. valor = valor.replace("\\\\", "\\");
  284. return {type: 'string', value: valor};
  285. }
  286. getBoolLiteral (token) {
  287. const val = token.type === this.lexerClass.RK_True ? true : false;
  288. return {type: 'bool', value: val};
  289. }
  290. /*
  291. * Returns an object {type: 'variable', value: value}.
  292. * @returns object with fields type and value
  293. **/
  294. parseVariable (token) {
  295. return {type: 'variable', value: token.text};
  296. }
  297. parseFunctions () {
  298. let list = [];
  299. while(true) {
  300. const func = this.parseFunction();
  301. if(func === null)
  302. break;
  303. else
  304. list.push(func);
  305. }
  306. return list;
  307. }
  308. /*
  309. * Returns an object representing a function. It has
  310. * four attributes: returnType, id, formalParams and block.
  311. * The block object has two attributes: declarations and commands
  312. **/
  313. parseFunction () {
  314. let formalParams = [];
  315. const token = this.getToken();
  316. if(token.type !== this.lexerClass.RK_FUNCTION) {
  317. //throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.PR_FUNCAO], token);
  318. return null;
  319. }
  320. this.pos++;
  321. this.consumeNewLines();
  322. const returnType = this.parseType(true);
  323. this.consumeNewLines();
  324. const functionID = this.parseID();
  325. this.consumeNewLines();
  326. this.checkOpenParenthesis();
  327. this.pos++;
  328. this.consumeNewLines();
  329. if (!this.checkCloseParenthesis(true)) {
  330. formalParams = this.parseFormalParameters(); // formal parameters
  331. this.consumeNewLines();
  332. this.checkCloseParenthesis();
  333. this.pos++;
  334. } else {
  335. this.pos++;
  336. }
  337. this.consumeNewLines();
  338. const commandsBlock = this.parseFunctionBody();
  339. return {returnType: returnType, id: functionID, formalParams: formalParams, block: commandsBlock};
  340. }
  341. /*
  342. * Parse the formal parameters of a function.
  343. * @returns a list of objects with the following attributes: type, id and dimensions.
  344. **/
  345. parseFormalParameters () {
  346. const list = [];
  347. while(true) {
  348. let dimensions = 0;
  349. this.consumeNewLines();
  350. const typeString = this.parseType();
  351. this.pos++;
  352. this.consumeNewLines();
  353. const idString = this.parseID();
  354. this.pos++;
  355. this.consumeNewLines();
  356. if (this.checkOpenBrace(true)) {
  357. this.pos++;
  358. dimensions++;
  359. this.checkCloseBrace();
  360. this.pos++;
  361. if(this.checkOpenBrace(true)) {
  362. this.pos++;
  363. dimensions++;
  364. this.checkCloseBrace();
  365. this.pos++;
  366. }
  367. }
  368. list.push({type: typeString, id: idString, dimensions: dimensions});
  369. this.consumeNewLines();
  370. const commaToken = this.getToken();
  371. if (commaToken.type !== this.lexerClass.COMMA)
  372. break;
  373. this.pos++;
  374. }
  375. return list;
  376. }
  377. parseID () {
  378. const token = this.getToken();
  379. if(token.type !== this.lexerClass.ID) {
  380. throw SyntaxError.createError('ID', token);
  381. }
  382. this.pos++;
  383. return token.text;
  384. }
  385. parseType (isFunction = false) {
  386. const token = this.getToken();
  387. if(token.type === this.lexerClass.ID && isFunction) {
  388. return 'void';
  389. } else if (token.type === this.lexerClass.RK_VOID && isFunction) {
  390. this.pos++;
  391. return 'void';
  392. } else if (this.isVariableType(token)) {
  393. this.pos++;
  394. switch(token.type) {
  395. case this.lexerClass.RK_INTEGER:
  396. return 'int';
  397. case this.lexerClass.RK_LOGIC:
  398. return 'bool';
  399. case this.lexerClass.RK_REAL:
  400. return 'real';
  401. case this.lexerClass.RK_STRING:
  402. return 'string';
  403. default:
  404. break;
  405. }
  406. }
  407. throw SyntaxError.createError(this.getTypesAsString(isFunction), token);
  408. }
  409. parseFunctionBody () {
  410. let variablesDecl = [];
  411. let commands = [];
  412. this.checkOpenCurly();
  413. this.pos++;
  414. while(true) {
  415. this.consumeNewLines();
  416. const token = this.getToken();
  417. let cmd = null;
  418. if (isVariableType(token)) {
  419. this.pos++;
  420. variablesDecl = variablesDecl.concat(this.parseDeclararion(token));
  421. this.checkEOS();
  422. this.pos++;
  423. cmd = -1;
  424. } else if (token.type === this.lexerClass.ID) {
  425. cmd = this.parseIDCommand();
  426. } else if (token.type === this.lexerClass.RK_RETURN) {
  427. cmd = this.parseReturn();
  428. } else if (token.type === this.lexerClass.RK_WHILE) {
  429. } else if (token.type === this.lexerClass.RK_FOR) {
  430. } else if (token.type === this.lexerClass.RK_BREAK) {
  431. cmd = this.parseBreak();
  432. } else if (token.type === this.lexerClass.RK_SWITCH) {
  433. } else if (token.type === this.lexerClass.RK_DO) {
  434. } else if (token.type === this.lexerClass.RK_IF) {
  435. }
  436. if (cmd === null)
  437. break;
  438. if(cmd !== -1)
  439. commands.push(cmd);
  440. }
  441. this.consumeNewLines();
  442. this.checkCloseCurly();
  443. return {variables: variablesDecl, commands: commands};
  444. }
  445. parseBreak () {
  446. this.pos++;
  447. this.checkEOS();
  448. this.pos++;
  449. return (new Break());
  450. }
  451. parseReturn () {
  452. this.pos++;
  453. const exp = this.parseExpressionOR();
  454. this.checkEOS();
  455. this.pos++;
  456. return (new Return(exp));
  457. }
  458. parseIDCommand () {
  459. const id = this.parseID();
  460. this.consumeNewLines();
  461. const equalOrParenthesis = this.getToken();
  462. if (equalOrParenthesis.type === this.lexerClass.EQUAL) {
  463. this.pos++
  464. const exp = this.parseExpressionOR();
  465. this.checkEOS();
  466. this.pos++;
  467. return (new Atribution(id, exp));
  468. } else if (equalOrParenthesis.type === this.lexerClass.OPEN_PARENTHESIS) {
  469. const actualParameters = this.parseActualParameters();
  470. this.checkEOS();
  471. this.pos++;
  472. return (new FunctionCall(id, actualParameters));
  473. } else {
  474. throw SyntaxError.createError("= or (", equalOrParenthesis);
  475. }
  476. }
  477. /*
  478. * Parses an Expression following the structure:
  479. *
  480. * EOR => EAnd ( 'or' EOR)? #expression and
  481. *
  482. * EOR => ENot ('and' EOR)? #expression or
  483. *
  484. * ENot => 'not'? ER #expression not
  485. *
  486. * ER => E ((>=, <=, ==, >, <) E)? #expression relational
  487. *
  488. * E => factor ((+, -) E)? #expression
  489. *
  490. * factor=> term ((*, /, %) factor)?
  491. *
  492. * term => literal || arrayAccess || FuncCall || ID || '('EAnd')'
  493. **/
  494. parseExpressionOR () {
  495. const exp1 = this.parseExpressionAND();
  496. let exp2 = null;
  497. let or = null;
  498. const maybeAnd = this.getToken();
  499. if (maybeAnd.type === this.lexerClass.OR_OPERATOR) {
  500. this.pos++;
  501. or = 'or';
  502. exp2 = this.parseExpressionOR();
  503. }
  504. return {left: exp1, op:or, right: exp2};
  505. }
  506. parseExpressionAND () {
  507. const exp1 = this.parseExpressionNot();
  508. let and = null;
  509. let exp2 = null;
  510. this.consumeNewLines();
  511. const andToken = this.getToken();
  512. if (andToken.type === this.lexerClass.AND_OPERATOR) {
  513. this.pos++;
  514. and = 'and';
  515. exp2 = this.parseExpressionAND();
  516. }
  517. return {left: exp1, op: and, right: exp2};
  518. }
  519. parseExpressionNot () {
  520. let not = null;
  521. this.consumeNewLines();
  522. const maybeNotToken = this.getToken();
  523. if (maybeNotToken.type === this.lexerClass.NOT_OPERATOR) {
  524. this.pos++;
  525. not = 'not';
  526. }
  527. const eRel = this.parseExpressionRel();
  528. return {left: null, op: not, right: eRel};
  529. }
  530. parseExpressionRel () {
  531. const exp1 = this.parseExpression();
  532. let rel = null;
  533. let exp2 = null;
  534. this.consumeNewLines();
  535. const relToken = this.getToken();
  536. if(relToken.type === this.lexerClass.RELATIONAL_OPERATOR) {
  537. this.pos++;
  538. rel = relToken.text; // TODO: source code line/column information
  539. exp2 = this.parseExpression();
  540. }
  541. return {left: exp1, op: rel, right: exp2};
  542. }
  543. parseExpression () {
  544. const factor = this.parseFactor();
  545. let op = null;
  546. let exp = null;
  547. this.consumeNewLines();
  548. const sumOpToken = this.getToken();
  549. if(sumOpToken.type === this.lexerClass.SUM_OP) {
  550. this.pos++;
  551. op = sumOpToken.text; // TODO: source code line/column information
  552. exp = this.parseExpression();
  553. }
  554. return {left: factor, op: op, right: exp};
  555. }
  556. parseFactor () {
  557. const term = this.parseTerm();
  558. let op = null;
  559. let factor = null;
  560. this.consumeNewLines();
  561. const multOpToken = this.getToken();
  562. if(multOpToken.type === this.lexerClass.MULTI_OP) {
  563. this.pos++;
  564. op = multOpToken.text; // TODO: source code line/column information
  565. factor = this.parseFactor();
  566. }
  567. return {left: term, op: op, right: factor};
  568. }
  569. parseTerm () {
  570. this.consumeNewLines();
  571. const token = this.getToken();
  572. switch(token.type) {
  573. case this.lexerClass.INTEGER:
  574. this.pos++;
  575. return this.getIntLiteral(token);
  576. case this.lexerClass.REAL:
  577. this.pos++;
  578. return this.getRealLiteral(token);
  579. case this.lexerClass.STRING:
  580. this.pos++;
  581. return this.getStringLiteral(token);
  582. case this.lexerClass.RK_TRUE:
  583. case this.lexerClass.RK_FALSE:
  584. this.pos++;
  585. return this.getBoolLiteral(token);
  586. case this.lexerClass.ID:
  587. return this.parseIDTerm();
  588. case this.lexerClass.OPEN_PARENTHESIS:
  589. return this.parseParenthesisExp();
  590. }
  591. }
  592. parseIDTerm () {
  593. const id = this.parseID();
  594. const last = this.pos;
  595. this.consumeNewLines(); // DANGEROUS: exp = ID eos => results in inapropriate syntax error
  596. if(this.checkOpenBrace(true)) {
  597. this.pos++;
  598. const firstIndex = this.parseExpression();
  599. let secondIndex = null;
  600. this.consumeNewLines();
  601. this.checkCloseBrace();
  602. this.pos++;
  603. this.consumeNewLines(); // DANGEROUS: exp = v1 + v2 + v[i] eos => results in inapropriate syntax error
  604. if(this.checkOpenBrace(true)){
  605. this.pos++;
  606. secondIndex = this.parseExpression();
  607. this.consumeNewLines();
  608. this.checkCloseBrace();
  609. this.pos++;
  610. } else {
  611. this.pos--;
  612. }
  613. return new ArrayAccess(id, firstIndex, secondIndex);
  614. } else if (this.checkOpenParenthesis(true)) {
  615. this.pos++;
  616. let actualParameters = [];
  617. if(!this.checkCloseParenthesis(true)) {
  618. actualParameters = this.parseActualParameters();
  619. this.consumeNewLines();
  620. this.checkCloseParenthesis();
  621. this.pos++;
  622. } else {
  623. this.pos++;
  624. }
  625. return new FunctionCall(id, actualParameters);
  626. } else {
  627. this.pos = last;
  628. return id;
  629. }
  630. }
  631. parseParenthesisExp () {
  632. this.checkOpenParenthesis();
  633. this.pos++;
  634. this.consumeNewLines();
  635. const exp = this.parseExpressionOR();
  636. this.consumeNewLines();
  637. this.checkCloseParenthesis();
  638. this.pos++;
  639. return exp;
  640. }
  641. parseActualParameters () {
  642. this.checkOpenParenthesis();
  643. this.pos++;
  644. list = [];
  645. while (true) {
  646. this.consumeNewLines();
  647. const exp = this.parseExpressionOR();
  648. this.consumeNewLines();
  649. list.push(exp);
  650. const commaToken = this.getToken();
  651. if (commaToken.type !== this.lexerClass.COMMA) {
  652. break;
  653. } else {
  654. this.pos++;
  655. }
  656. }
  657. this.consumeNewLines();
  658. this.checkCloseParenthesis();
  659. this.pos++;
  660. return list;
  661. }
  662. getTypesAsString (isFunction = false) {
  663. const types = isFunction ? this.functionTypes : this.variableTypes;
  664. return types.map( x => this.lexer.literalNames[x])
  665. .reduce((o, n) => {
  666. if (o.length <= 0)
  667. return n;
  668. else
  669. return o + ", " + n;
  670. }, '');
  671. }
  672. }