1
0

ivprogParser.js 25 KB

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