generate-c-tester.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. const XorShift = require('xorshift').constructor;
  2. const stream = require('stream');
  3. const reader = require('./reader.js');
  4. const header = `
  5. #include "mconf.h"
  6. #include "cephes.h"
  7. #include <stdio.h>
  8. int error_code = -1;
  9. int mtherr(char *name, int code) {
  10. error_code = code;
  11. return 0;
  12. }
  13. int main() {
  14. `;
  15. const footer = `}\n`;
  16. var rng = new XorShift([
  17. 6724524440630955, 4369800304473057,
  18. 1956920014856890, 8721370862793116
  19. ]);
  20. const type2printf = {
  21. 'double': '%.20f',
  22. 'int': '%d'
  23. };
  24. const type2zero = {
  25. 'double': '0.0',
  26. 'int': '0'
  27. };
  28. const argGenerators = {
  29. double: function () {
  30. return (rng.random() * 10).toFixed(8);
  31. },
  32. int: function () {
  33. return (rng.random() * 10 + 1).toFixed(0);
  34. },
  35. "double[]": function () {
  36. let code = '[';
  37. code += (rng.random() * 20 - 10).toFixed(2) + ', ';
  38. code += (rng.random() * 20 - 10).toFixed(2) + ', ';
  39. code += (rng.random() * 20 - 10).toFixed(2) + ', ';
  40. code += (rng.random() * 20 - 10).toFixed(2) + ']';
  41. return code;
  42. },
  43. // Bad range in igami, causes infinite loop
  44. "igami:y0": function () {
  45. return rng.random().toFixed(8);
  46. },
  47. "igami:a": function () {
  48. return (rng.random() * 10).toFixed(8);
  49. }
  50. };
  51. class CTesterGenerator extends stream.Transform {
  52. constructor() {
  53. super({ objectMode: true });
  54. this.push(header);
  55. }
  56. _generateSampleCode(data) {
  57. const {filename, returnType, functionName, functionArgs} = data;
  58. // Check if there is extra data returned
  59. const extraReturn = functionArgs.some((arg) => arg.isPointer);
  60. // Sample function arguments
  61. let fnargs = new Map();
  62. for (const { fullType, name, isPointer, isArrayLength } of functionArgs) {
  63. if (isPointer) continue;
  64. if (isArrayLength) {
  65. // The array is of length 4, .length - 1 is not always the correct
  66. // choice, but it is always the safe choice.
  67. fnargs.set(name, "3");
  68. } else if (argGenerators.hasOwnProperty(`${functionName}:${name}`)) {
  69. fnargs.set(name, argGenerators[`${functionName}:${name}`]());
  70. } else {
  71. fnargs.set(name, argGenerators[fullType]());
  72. }
  73. }
  74. //
  75. // Start code generation
  76. //
  77. let code = '';
  78. code += ` { /* ${functionName} from cephes/${filename}.c */\n`;
  79. code += ' error_code = -1;\n';
  80. //
  81. // Function call
  82. //
  83. // Create pointer inputs
  84. for (const { type, name, isPointer } of functionArgs) {
  85. if (!isPointer) continue;
  86. code += ` ${type} extra_${name} = ${type2zero[type]};\n`;
  87. }
  88. // Call function
  89. code += ` ${returnType} ret = cephes_${functionName}(`
  90. for (const { fullType, name, isPointer, isArray } of functionArgs) {
  91. if (isPointer) {
  92. code += `&extra_${name}, `;
  93. } else if (isArray) {
  94. const c_array = fnargs.get(name).replace('[', '{').replace(']', '}');
  95. code += `(${fullType}) ${c_array}, `;
  96. }
  97. else code += fnargs.get(name) + ', ';
  98. }
  99. code = code.slice(0, -2);
  100. code += ');\n';
  101. // Normalize return value
  102. code += ` ret = error_code == -1 ? ret : ${type2zero[returnType]};\n`;
  103. //
  104. // Print output
  105. //
  106. // The printf string
  107. code += ` printf("{"\n`;
  108. code += ` "\\"fn\\": \\"${functionName}\\", "\n`;
  109. code += ` "\\"ret\\": ${type2printf[returnType]}, "\n`;
  110. code += ` "\\"args\\": [`;
  111. for (const { name, isPointer } of functionArgs) {
  112. if (isPointer) continue;
  113. code += `${fnargs.get(name)}, `;
  114. }
  115. code = code.slice(0, -2);
  116. code += `], "\n`;
  117. code += ` "\\"extra\\": {`;
  118. if (extraReturn) {
  119. for (const { type, name, isPointer } of functionArgs) {
  120. if (!isPointer) continue;
  121. code += `\\"${name}\\": ${type2printf[type]}, `;
  122. }
  123. code = code.slice(0, -2);
  124. }
  125. code += `}, "\n`;
  126. code += ` "\\"error_code\\": %d"\n`;
  127. code += ` "}\\n",\n`;
  128. // The printf content
  129. code += ` ret, `;
  130. for (const { fullType, name, isPointer } of functionArgs) {
  131. if (!isPointer) continue;
  132. code += `extra_${name}, `;
  133. }
  134. code += `error_code`;
  135. // Close printf
  136. code += `);\n`;
  137. //
  138. code += ' }\n';
  139. code += '\n';
  140. return code;
  141. }
  142. _transform(data, encoding, done) {
  143. for (let i = 0; i < 5; i++) {
  144. this.push(this._generateSampleCode(data));
  145. }
  146. done(null);
  147. }
  148. _flush(done) {
  149. this.push(footer);
  150. done(null);
  151. }
  152. }
  153. process.stdin
  154. .pipe(reader())
  155. .pipe(new CTesterGenerator())
  156. .pipe(process.stdout)