converter.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. import { BaseConverter } from "./../baseConverter";
  2. import {
  3. Declaration,
  4. ArrayDeclaration,
  5. FunctionCall
  6. } from "../../ast/commands";
  7. import { Types } from "../../typeSystem/types";
  8. import { ArrayType } from "../../typeSystem/array_type";
  9. import {
  10. InfixApp,
  11. StringLiteral,
  12. VariableLiteral,
  13. UnaryApp,
  14. BoolLiteral
  15. } from "../../ast/expressions";
  16. import { TextNode } from "./ast/textNode";
  17. import { Pointer } from "./types/pointer";
  18. import { Operators } from "../../ast/operators";
  19. import { LocalizedStrings } from "./../../services/localizedStringsService";
  20. import { LanguageDefinedFunction } from "../../processor/definedFunctions";
  21. const FORBIDDEN_FUNCS = [
  22. "$isReal",
  23. "$isInt",
  24. "$isBool",
  25. "$castReal",
  26. "$castInt",
  27. "$castBool",
  28. "$castString",
  29. "$max",
  30. "$min",
  31. "$invert"
  32. ];
  33. export class CConverter extends BaseConverter {
  34. constructor (ivprogAST) {
  35. super(ivprogAST);
  36. this.globalVars = [];
  37. this.cAST = {
  38. includes: new Set(),
  39. globalVars: [],
  40. functionDecl: [],
  41. functions: []
  42. };
  43. this.currentVars = [];
  44. this.currentCommands = [];
  45. this.auxCounter = 1;
  46. }
  47. createAuxID () {
  48. let id = null;
  49. let symbol = true;
  50. while (symbol) {
  51. id = `aux_${this.auxCounter}`;
  52. this.auxCounter += 1;
  53. symbol = this.findSymbol(id);
  54. }
  55. return id;
  56. }
  57. resetContext () {
  58. this.currentVars = [];
  59. this.currentCommands = [];
  60. }
  61. toText () {
  62. this.doConvertion();
  63. }
  64. doConversion () {
  65. this.ivprogAST.global.forEach(decl => {
  66. if (decl instanceof ArrayDeclaration) {
  67. if (Types.STRING.isCompatible(decl.type.innerType)) {
  68. throw new Error(
  69. "Cannot convert a program that contains an array of text"
  70. );
  71. }
  72. }
  73. this.insertGlobalSymbol(decl.id, decl);
  74. if (decl instanceof ArrayDeclaration) {
  75. let initial = decl.initial;
  76. if (initial) {
  77. initial = this.convertArrayInit(initial);
  78. }
  79. const lines = this.convertExpression(decl.lines);
  80. if (decl.isVector) {
  81. this.cAST.globalVars.push(
  82. new ArrayDeclaration(
  83. decl.id,
  84. decl.type,
  85. lines,
  86. null,
  87. initial,
  88. decl.isConst
  89. )
  90. );
  91. } else {
  92. const columns = this.convertExpression(decl.lines);
  93. this.cAST.globalVars.push(
  94. new ArrayDeclaration(
  95. decl.id,
  96. decl.type,
  97. lines,
  98. columns,
  99. initial,
  100. decl.isConst
  101. )
  102. );
  103. }
  104. } else {
  105. if (decl.type.isCompatible(Types.STRING)) {
  106. if (
  107. decl.initial == null ||
  108. !(decl.initial instanceof StringLiteral)
  109. ) {
  110. throw new Error(
  111. "String must have a literal as its initial value to be converted to C"
  112. );
  113. }
  114. }
  115. let initial = decl.initial;
  116. if (initial) {
  117. initial = this.convertExpression(initial);
  118. }
  119. if (decl.type.isCompatible(Types.STRING)) {
  120. // Must be a pointer since concatenation under the same symbol is expected (str = str + "abc")
  121. this.cAST.globalVars.push(
  122. new Declaration(
  123. decl.id,
  124. new Pointer(decl.type),
  125. initial,
  126. decl.isConst
  127. )
  128. );
  129. } else {
  130. this.cAST.globalVars.push(
  131. new Declaration(decl.id, decl.type, initial, decl.isConst)
  132. );
  133. }
  134. }
  135. });
  136. // convert every function...
  137. for (let i = 0; i < this.ivprogAST.functions.length; i += 1) {
  138. const func = this.ivprogAST.functions[i];
  139. if (func.isMain) {
  140. // deal with main function
  141. } else {
  142. // TODO
  143. }
  144. }
  145. }
  146. convertExpression (expression) {
  147. const type = this.evaluateExpressionType(expression);
  148. if (type instanceof ArrayType) {
  149. // TODO
  150. } else if (Types.STRING.isCompatible(type)) {
  151. return this.convertStringExpression(expression);
  152. } else if (Types.BOOLEAN.isCompatible(type)) {
  153. return this.convertBooleanExpression(expression);
  154. } else {
  155. // It's a numeric expression
  156. return this.convertNumericExpression(expression);
  157. }
  158. }
  159. convertArrayInit (decl) {
  160. return null;
  161. }
  162. convertNumericExpression (expression) {
  163. if (expression instanceof UnaryApp) {
  164. const leftExp = this.convertNumericExpression(expression.left);
  165. return new TextNode(`${expression.op.value}${leftExp.toString()}`);
  166. } else if (expression instanceof InfixApp) {
  167. const leftExp = this.convertNumericExpression(expression.left);
  168. const rightExp = this.convertNumericExpression(expression.right);
  169. return new TextNode(
  170. `${leftExp.toString()} ${expression.op.value} ${rightExp.toString()}`
  171. );
  172. } else if (expression instanceof FunctionCall) {
  173. let funcName = expression.id;
  174. const langFun = LanguageDefinedFunction.getFunction(funcName);
  175. if (langFun) {
  176. this.isValidFunction(funcName);
  177. // function is valid, do we need to import any c lib?
  178. // tan, cos, sen, sqrt, pow ...
  179. const funcInfo = this.convertFunctionCall(funcName);
  180. this.cAST.includes.add(funcInfo.lib);
  181. funcName = funcInfo.name;
  182. }
  183. // need to verify every parameter...
  184. const paramList = [];
  185. for (let i = 0; i < expression.parametersSize; i += 1) {
  186. const param = expression.actualParameters[i];
  187. const paramExp = this.convertExpression(param);
  188. paramList.push(paramExp.toString());
  189. }
  190. const paramString = paramList.join(", ");
  191. return TextNode(`${funcName}(${paramString})`);
  192. } else {
  193. return expression;
  194. }
  195. }
  196. convertBooleanExpression (expression) {
  197. if (expression instanceof UnaryApp) {
  198. const left = this.convertExpression(expression.left);
  199. return new TextNode(`!${left.toString()}`);
  200. } else if (expression instanceof InfixApp) {
  201. const leftExp = this.convertExpression(expression.left);
  202. const rightExp = this.convertExpression(expression.right);
  203. const op = expression.op;
  204. switch (op.ord) {
  205. case Operators.AND.ord:
  206. return new TextNode(
  207. `${leftExp.toString()} && ${rightExp.toString()}`
  208. );
  209. case Operators.OR.ord:
  210. return new TextNode(
  211. `${leftExp.toString()} || ${rightExp.toString()}`
  212. );
  213. default:
  214. return new TextNode(
  215. `${leftExp.toString()} ${op.value} ${rightExp.toString()}`
  216. );
  217. }
  218. } else if (expression instanceof BoolLiteral) {
  219. this.cAST.includes.add("stdbool.h");
  220. const value = expression.value ? "true" : "false";
  221. return new TextNode(value);
  222. } else if (expression instanceof FunctionCall) {
  223. // Function call... if it's one of the convert/check functions DO NOT convert the code
  224. let funcName = expression.id;
  225. const langFun = LanguageDefinedFunction.getFunction(funcName);
  226. if (langFun) {
  227. this.isValidFunction(funcName);
  228. const funcInfo = this.convertFunctionCall(funcName);
  229. this.cAST.includes.add(funcInfo.lib);
  230. funcName = funcInfo.name;
  231. }
  232. const paramList = [];
  233. for (let i = 0; i < expression.parametersSize; i += 1) {
  234. const param = expression.actualParameters[i];
  235. const paramExp = this.convertExpression(param);
  236. paramList.push(paramExp.toString());
  237. }
  238. const paramString = paramList.join(", ");
  239. return TextNode(`${funcName}(${paramString})`);
  240. } else {
  241. return expression;
  242. }
  243. }
  244. convertStringExpression (expression) {
  245. if (expression instanceof InfixApp) {
  246. const leftType = this.evaluateExpressionType(expression.left);
  247. const rightType = this.evaluateExpressionType(expression.right);
  248. if (
  249. leftType.isCompatible(Types.STRING) &&
  250. rightType.isCompatible(Types.STRING)
  251. ) {
  252. // BOTH strings....
  253. // create a temp var with size equals to left + right + 1
  254. const leftExp = this.convertStringExpression(expression.left);
  255. const rightExp = this.convertStringExpression(expression.right);
  256. this.currentCommands.push({
  257. value:
  258. "As 3 linhas seguintes convertem a expressão " +
  259. expression.toString()
  260. });
  261. const auxID = this.createAuxID();
  262. this.createCharPointer(auxID, true, leftExp, rightExp);
  263. this.createCharCopy(auxID, leftExp);
  264. this.createCharConcat(auxID, rightExp);
  265. return new VariableLiteral(auxID);
  266. } else if (leftType.isCompatible(Types.STRING)) {
  267. // left is a string, right needs conversion
  268. const leftExp = this.convertStringExpression(expression.left);
  269. const rightExp = this.convertExpression(expression.right);
  270. const formatString = this.getFormatStringFromType(rightType);
  271. const auxConvert = this.createAuxID();
  272. this.convertToString(auxConvert, formatString, rightExp);
  273. const auxID = this.createAuxID();
  274. this.createCharPointer(auxID, true, leftExp, auxConvert);
  275. this.createCharCopy(auxID, leftExp);
  276. this.createCharConcat(auxID, auxConvert);
  277. return new VariableLiteral(auxID);
  278. } else {
  279. // right is a string, left needs conversion
  280. const leftExp = this.convertExpression(expression.left);
  281. const rightExp = this.convertStringExpression(expression.right);
  282. const formatString = this.getFormatStringFromType(leftType);
  283. const auxConvert = this.createAuxID();
  284. this.convertToString(auxConvert, formatString, leftExp);
  285. const auxID = this.createAuxID();
  286. this.createCharPointer(auxID, true, auxConvert, rightExp);
  287. this.createCharCopy(auxID, auxConvert);
  288. this.createCharConcat(auxID, rightExp);
  289. return new VariableLiteral(auxID);
  290. }
  291. } else if (expression instanceof StringLiteral) {
  292. return expression;
  293. } else if (expression instanceof FunctionCall) {
  294. let funcName = expression.id;
  295. const langFun = LanguageDefinedFunction.getFunction(funcName);
  296. if (langFun) {
  297. this.isValidFunction(funcName);
  298. const funcInfo = this.convertFunctionCall(funcName);
  299. this.cAST.includes.add(funcInfo.lib);
  300. funcName = funcInfo.name;
  301. }
  302. const paramList = [];
  303. for (let i = 0; i < expression.parametersSize; i += 1) {
  304. const param = expression.actualParameters[i];
  305. const paramExp = this.convertExpression(param);
  306. paramList.push(paramExp.toString());
  307. }
  308. const paramString = paramList.join(", ");
  309. return TextNode(`${funcName}(${paramString})`);
  310. } else {
  311. return expression;
  312. }
  313. }
  314. getFormatStringFromType (type) {
  315. switch (type.ord) {
  316. case Types.INTEGER.ord:
  317. case Types.BOOLEAN.ord:
  318. return "%d";
  319. case Types.STRING.ord:
  320. return "%s";
  321. default:
  322. return "%g";
  323. }
  324. }
  325. convertToString (id, format, expression) {
  326. this.currentCommands.push({
  327. value:
  328. "As 3 linhas seguintes convertem a expressão " + expression.toString()
  329. });
  330. const lenID = this.createAuxID();
  331. this.currentCommands.push(
  332. new TextNode(
  333. `int ${lenID} = snprintf(NULL, 0, ${format}, ${expression.toString()})`
  334. )
  335. );
  336. this.createCharPointer(id, true, lenID);
  337. this.currentCommands.push(
  338. new TextNode(
  339. `snprintf(${id}, ${lenID} + 1, ${format}, ${expression.toString()})`
  340. )
  341. );
  342. }
  343. createCharPointer (id, plusOne, ...args) {
  344. this.cAST.includes.add("stdlib.h");
  345. const funArgs = [];
  346. for (let i = 0; i < args.length; i += 1) {
  347. funArgs.push(`sizeof(${args[i].toString()})`);
  348. }
  349. if (plusOne) {
  350. funArgs.push("1");
  351. }
  352. const params = funArgs.join(" + ");
  353. const initial = new TextNode(`malloc(${params})`);
  354. const decl = new Declaration(id, new Pointer(Types.STRING), initial);
  355. this.currentCommands.push(decl);
  356. }
  357. createCharCopy (id, arg) {
  358. this.cAST.includes.add("string.h");
  359. const cmd = new TextNode(`strcpy(${id}, ${arg.toString()})`);
  360. this.currentCommands.push(cmd);
  361. }
  362. createCharConcat (id, arg) {
  363. this.cAST.includes.add("string.h");
  364. const cmd = new TextNode(`strcat(${id}, ${arg.toString()})`);
  365. this.currentCommands.push(cmd);
  366. }
  367. convertFunctionCall (name) {
  368. let id = name;
  369. if (name.indexOf(".") !== -1) {
  370. const names = name.split(".");
  371. id = names[1];
  372. }
  373. switch (id) {
  374. case "$log":
  375. return { lib: "math.h", name: "log10" };
  376. case "$sin":
  377. case "$cos":
  378. case "$tan":
  379. case "$sqrt":
  380. case "$pow":
  381. return { lib: "math.h", name: id.substring(1) };
  382. case "$rand":
  383. return { lib: "stdlib.h", name: id.substring(1) };
  384. case "$abs":
  385. return { lib: "stdlib.h", name: id.substring(1) };
  386. }
  387. }
  388. isValidFunction (id) {
  389. for (const name in FORBIDDEN_FUNCS) {
  390. if (id.indexOf(name) !== -1) {
  391. throw new Error(
  392. `Não é possível converter um código que faz uso da função ${LocalizedStrings.translateInternalFunction(
  393. name
  394. )}.`
  395. );
  396. }
  397. }
  398. }
  399. }