ivprogParser.js 24 KB

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