ivprogProcessor.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. import { Store } from './store/store';
  2. import { StoreObject } from './store/storeObject';
  3. import { StoreObjectArray } from './store/storeObjectArray';
  4. import { Modes } from './mode';
  5. import { Context } from './context';
  6. import { Types } from './../ast/types';
  7. import * as Commands from './../ast/commands/';
  8. import * as Expressions from './../ast/expressions/';
  9. export class IVProgProcessor {
  10. constructor(ast) {
  11. this.ast = ast;
  12. this.globalStore = new Store();
  13. this.stores = [this.globalStore];
  14. this.context = [Context.BASE];
  15. this.input = null;
  16. this.output = null;
  17. }
  18. registerInput (input) {
  19. this.input = input;
  20. }
  21. registerOutput (output) {
  22. this.output = output;
  23. }
  24. checkContext(context) {
  25. return this.context[this.context.length] === context;
  26. }
  27. ignoreSwitchCases (store) {
  28. if (store.mode === Modes.RETURN) {
  29. return true;
  30. } else if (store.mode === Modes.BREAK) {
  31. return true;
  32. } else {
  33. return false;
  34. }
  35. }
  36. interpretAST () {
  37. this.initGlobal();
  38. const mainFunc = this.findMainFunction();
  39. if(mainFunc === null) {
  40. // TODO: Better error message
  41. throw new Error("Missing main funciton.");
  42. }
  43. return this.runFunction(mainFunc, [], this.globalStore);
  44. }
  45. initGlobal () {
  46. if(!this.checkContext(Context.BASE)) {
  47. throw new Error("!!!CRITICAL: Invalid call to initGlobal outside BASE context!!!");
  48. }
  49. this.ast.global.forEach(decl => {
  50. this.executeCommand(this.globalStore, decl).then(v => this.globalStore = v);
  51. });
  52. }
  53. findMainFunction () {
  54. return this.ast.functions.find(v => v.isMain);
  55. }
  56. findFunction (name) {
  57. const val = this.ast.functions.find( v => v.name === name);
  58. if (!!!val) {
  59. // TODO: better error message;
  60. throw new Error(`Function ${name} is not defined.`);
  61. }
  62. return val;
  63. }
  64. runFunction (func, actualParameters, store) {
  65. let funcStore = new Store();
  66. funcStore.extendStore(this.globalStore);
  67. const returnStoreObject = new StoreObject(func.returnType, null);
  68. const funcName = func.isMain ? 'main' : func.name;
  69. const funcNameStoreObject = new StoreObject(Types.STRING, funcName, true);
  70. funcStore.insertStore('$', returnStoreObject);
  71. funcStore.insertStore('$name', funcNameStoreObject);
  72. funcStore = this.associateParameters(func.formalParameters, actualParameters, store, funcStore);
  73. this.context.push(Context.FUNCTION);
  74. this.stores.push(funcStore);
  75. const result = this.executeCommands(funcStore, func.commands);
  76. this.stores.pop();
  77. this.context.pop();
  78. return result;
  79. }
  80. associateParameters (formalList, actualList, callerStore, calleeStore) {
  81. if (formalList.length != actualList.length) {
  82. // TODO: Better error message
  83. throw new Error("Numbers of parameters doesn't match");
  84. }
  85. formalList.forEach((v, i) => {
  86. const val = this.evaluateExpression(callerStore, actualList[i]);
  87. switch (v.dimensions) {
  88. case 1: {
  89. if (val.lines > 0 && val.columns === null) {
  90. calleeStore.insertStore(v.id, val);
  91. } else {
  92. // TODO: Better error message
  93. throw new Error(`Parameter ${v.id} is not compatible with the value given.`);
  94. }
  95. break;
  96. }
  97. case 2: {
  98. if (val.lines > 0 && val.columns > 0) {
  99. calleeStore.insertStore(v.id, val);
  100. } else {
  101. // TODO: Better error message
  102. throw new Error(`Parameter ${v.id} is not compatible with the value given.`);
  103. }
  104. break;
  105. }
  106. case 0: {
  107. if (val.type !== v.type) {
  108. // TODO: Better error message
  109. throw new Error(`Parameter ${v.id} is not compatible with ${val.type}.`);
  110. } else {
  111. calleeStore.insertStore(v.id, val);
  112. }
  113. }
  114. }
  115. });
  116. return calleeStore;
  117. }
  118. executeCommands (store, cmds) {
  119. return cmds.reduce((promise, cmd) => promise.then( sto => {
  120. while (sto.mode === Modes.PAUSE) {
  121. continue;
  122. }
  123. if(sto.mode === Modes.RETURN) {
  124. return Promise.resolve(sto);
  125. } else if (this.checkContext(Context.BREAKABLE &&
  126. sto.mode === Modes.BREAK)) {
  127. return Promise.resolve(sto);
  128. }
  129. return this.executeCommand(sto, cmd);
  130. }), Promise.resolve(store));
  131. }
  132. executeCommand (store, cmd) {
  133. while (store.mode === Modes.PAUSE) {
  134. continue;
  135. }
  136. if(store.mode === Modes.RETURN) {
  137. return Promise.resolve(store);
  138. } else if(this.checkContext(Context.BREAKABLE) && store.mode === Modes.BREAK) {
  139. return Promise.resolve(store);
  140. }
  141. if (cmd instanceof Commands.Declaration) {
  142. return this.executeDeclaration(store, cmd);
  143. } else if (cmd instanceof Commands.Assign) {
  144. return this.executeAssign(store, cmd);
  145. } else if (cmd instanceof Commands.Break) {
  146. return this.executeBreak(store, cmd);
  147. } else if (cmd instanceof Commands.Return) {
  148. return this.executeReturn(store, cmd);
  149. } else if (cmd instanceof Commands.IfThenElse) {
  150. return this.executeIfThenElse(store, cmd);
  151. } else if (cmd instanceof Commands.While) {
  152. return this.executeWhile(store, cmd);
  153. } else if (cmd instanceof Commands.DoWhile) {
  154. return this.executeDoWhile(store, cmd);
  155. } else if (cmd instanceof Commands.For) {
  156. return this.executeFor(store, cmd);
  157. } else if (cmd instanceof Commands.Switch) {
  158. return this.executeSwitch(store, cmd);
  159. } else if (cmd instanceof Commands.FunctionCall) {
  160. return this.executeFunctionCall(store, cmd);
  161. } else {
  162. throw new Error("!!!CRITICAL A unknown command was found!!!\n" + cmd);
  163. }
  164. }
  165. executeFunctionCall (store, cmd) {
  166. const func = this.findFunction(cmd.id);
  167. this.runFunction(func, cmd.actualParameters, store);
  168. return Promise.resolve(store);
  169. }
  170. executeSwitch (store, cmd) {
  171. const auxCaseFun = (promise, switchExp, aCase) => {
  172. return promise.then( result => {
  173. const sto = result.sto;
  174. if (this.ignoreSwitchCases(sto)) {
  175. return Promise.resolve(result);
  176. } else if (result.wasTrue || aCase.isDefault) {
  177. const newSto = this.executeCommand(result.sto,aCase.commands);
  178. return Promise.resolve({wasTrue: true, sto: newSto});
  179. } else {
  180. const value = this.evaluateExpression(sto,
  181. new Expressions.InfixApp('==', switchExp, aCase.expression));
  182. if (value.value) {
  183. const newSto = this.executeCommand(result.sto,aCase.commands);
  184. return Promise.resolve({wasTrue: true, sto: newSto});
  185. } else {
  186. return Promise.resolve({wasTrue: false, sto: newSto});
  187. }
  188. }
  189. });
  190. }
  191. try {
  192. this.context.push(Context.BREAKABLE);
  193. let breakLoop = false;
  194. const case0 = cmd.cases[0];
  195. let result = auxCaseFun(Promise.resolve({wasTrue: false, sto: store}),
  196. cmd.expression,
  197. case0);
  198. for (let index = 1; index < cmd.cases.length && !breakLoop; index++) {
  199. const aCase = cmd.cases[index];
  200. result = auxCaseFun(result, cmd.expression, aCase);
  201. result.then( r => breakLoop = this.ignoreSwitchCases(r.sto));
  202. }
  203. this.context.pop();
  204. return result.then(r => r.sto);
  205. } catch (error) {
  206. return Promise.reject(error);
  207. }
  208. }
  209. executeFor (store, cmd) {
  210. try {
  211. //BEGIN for -> while rewrite
  212. const initCmd = cmd.assignment;
  213. const condition = cmd.condition;
  214. const increment = cmd.increment;
  215. const whileBlock = new Commands.CommandBlock([],
  216. cmd.commands.concat(increment));
  217. const forAsWhile = new Commands.While(condition, whileBlock);
  218. //END for -> while rewrite
  219. const newCmdList = [initCmd,forAsWhile];
  220. return Promise.resolve(this.executeCommands(store, newCmdList));
  221. } catch (error) {
  222. return Promise.reject(error);
  223. }
  224. }
  225. executeDoWhile (store, cmd) {
  226. try {
  227. this.context.push(Context.BREAKABLE);
  228. const newStore = this.executeCommands(store, cmd.commands);
  229. const value = this.evaluateExpression(newStore, cmd.expression);
  230. if (value.type !== Types.BOOLEAN) {
  231. // TODO: Better error message -- Inform line and column from token!!!!
  232. // THIS IF SHOULD BE IN A SEMANTIC ANALYSER
  233. return Promise.reject(new Error(`DoWhile expression must be of type boolean`));
  234. }
  235. if (value.value) {
  236. return Promise.resolve(this.executeCommand(newStore, cmd));
  237. } else {
  238. return Promise.resolve(newStore);
  239. }
  240. } catch (error) {
  241. return Promise.reject(error)
  242. }
  243. }
  244. executeWhile (store, cmd) {
  245. try {
  246. this.context.push(Context.BREAKABLE);
  247. const value = this.evaluateExpression(store, cmd.expression);
  248. if(value.type === Types.BOOLEAN) {
  249. if(value.value) {
  250. const newStore = this.executeCommands(store, cmd.commands);
  251. this.context.pop();
  252. return Promise.resolve(this.executeCommand(newStore, cmd));
  253. } else {
  254. this.context.pop();
  255. return Promise.resolve(store);
  256. }
  257. } else {
  258. // TODO: Better error message -- Inform line and column from token!!!!
  259. // THIS IF SHOULD BE IN A SEMANTIC ANALYSER
  260. return Promise.reject(new Error(`Loop condition must be of type boolean`));
  261. }
  262. } catch (error) {
  263. return Promise.reject(error);
  264. }
  265. }
  266. executeIfThenElse (store, cmd) {
  267. try {
  268. const value = this.evaluateExpression(cmd.condition);
  269. if(value.type === Types.BOOLEAN) {
  270. if(value.value) {
  271. return Promise.resolve(this.executeCommand(store, cmd.ifTrue));
  272. } else {
  273. return Promise.resolve(this.executeCommand(store, cmd.ifFalse));
  274. }
  275. } else {
  276. // TODO: Better error message -- Inform line and column from token!!!!
  277. // THIS IF SHOULD BE IN A SEMANTIC ANALYSER
  278. return Promise.reject(new Error(`If expression must be of type boolean`));
  279. }
  280. } catch (error) {
  281. return Promise.reject(error);
  282. }
  283. }
  284. executeReturn (store, cmd) {
  285. try {
  286. const funcType = store.applyStore('$');
  287. const value = this.evaluateExpression(store, cmd.expression);
  288. const funcName = store.applyStore('$name');
  289. if (funcType.type !== value.type) {
  290. // TODO: Better error message -- Inform line and column from token!!!!
  291. // THIS IF SHOULD BE IN A SEMANTIC ANALYSER
  292. return Promise.reject(new Error(`Function ${funcName.value} must return ${funcType.type} instead of ${value.type}.`));
  293. } else {
  294. store.updateStore('$', value);
  295. store.mode = Modes.RETURN;
  296. return Promise.resolve(store);
  297. }
  298. } catch (error) {
  299. return Promise.reject(error);
  300. }
  301. }
  302. executeBreak (store, cmd) {
  303. if(this.checkContext(Context.BREAKABLE)) {
  304. store.mode = Modes.BREAK;
  305. return Promise.resolve(store);
  306. } else {
  307. return Promise.reject(new Error("!!!CRITIAL: Break command outside Loop/Switch scope!!!"));
  308. }
  309. }
  310. executeAssign (store, cmd) {
  311. try {
  312. const value = this.evaluateExpression(store, cmd.expression);
  313. store.updateStore(cmd.id, value);
  314. return Promise.resolve(store);
  315. } catch (error) {
  316. return Promise.reject(error);
  317. }
  318. }
  319. executeDeclaration (store, cmd) {
  320. try {
  321. const value = this.evaluateExpression(store, cmd.initial);
  322. if(cmd instanceof Commands.ArrayDeclaration) {
  323. const temp = new StoreObjectArray(decl.subtype, decl.lines, decl.columns, null, decl.isConst);
  324. store.insertStore(decl.id, temp);
  325. store.updateStore(decl.id, value);
  326. } else {
  327. const temp = new StoreObject(decl.type, null, decl.isConst);
  328. store.insertStore(decl.id, temp);
  329. store.updateStore(decl.id, value);
  330. }
  331. return Promise.resolve(store);
  332. } catch (e) {
  333. return Promise.reject(e);
  334. }
  335. }
  336. }