ivprogParser.js 18 KB

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