Er.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. 'use strict';
  16. const trim = (x) => {
  17. if (x[0] !== ' ' || x[x.length-1] !== ' ') {
  18. throw new Error("INTERNAL: Er input must begin " +
  19. "with and end with a space, unrecognized [" + x + "]");
  20. }
  21. return x.trim();
  22. };
  23. const defun = (ctx, spec) => {
  24. trim(spec);
  25. if (spec.lastIndexOf(')') !== spec.length - 2) {
  26. throw new Error("Er function spec must end " +
  27. "with a ), unrecognized [" + spec + "]");
  28. }
  29. let c = 1;
  30. let i = spec.length - 3;
  31. for (; c && i >= 0; i--) {
  32. c += spec[i] === ')';
  33. c -= spec[i] === '(';
  34. }
  35. const args = spec.slice(i + 2, spec.length - 2);
  36. const rettAndFunc = spec.slice(0, i + 1).trim();
  37. const func = rettAndFunc.replace(/^.*\s+([a-zA-Z_][a-zA-Z0-9_]*)$/, (all, a) => a);
  38. //console.log('defun: ' + rettAndFunc + ' - ' + args);
  39. if (func === rettAndFunc) {
  40. throw new Error("Could not parse function [" + spec + "]");
  41. }
  42. const rett = rettAndFunc.replace(/\s+[a-zA-Z_][a-zA-Z0-9_]*$/, '').trim();
  43. ctx.activeFunction = ctx.functions[func] = { rett: rett };
  44. if (rett === 'void') {
  45. return 'struct Er_Ret* ' + func + '(' + args + ')';
  46. } else {
  47. return 'struct Er_Ret* ' + func + '(' + rett + ' *Er_returnValP, ' + args + ')';
  48. }
  49. };
  50. const ret = (ctx, val) => {
  51. val = trim(val);
  52. if (ctx.activeFunction.rett === 'void') {
  53. return 'return (struct Er_Ret*)0';
  54. } else {
  55. return '*Er_returnValP = ' + val + '; return (struct Er_Ret*)0';
  56. }
  57. };
  58. const er = (ctx, assert, errOut, expr, file, line) => {
  59. expr = trim(expr);
  60. if (!/[a-zA-Z_][a-zA-Z0-9_]*\(.*\)$/.test(expr)) {
  61. throw new Error("Er() expr must be in the form Er(funcName(arg1, arg2, ...)) " +
  62. "in [" + expr + "]");
  63. }
  64. const funcName = expr.slice(0, expr.indexOf('('));
  65. const f = ctx.functions[funcName];
  66. if (!f) {
  67. throw new Error("Er() not a defined function [" + funcName + "] in [" + expr + "]");
  68. }
  69. let ifret = `if (Er_ret) { return Er_unwind("${file}", ${line}, Er_ret); }`;
  70. if (assert) {
  71. ifret = `
  72. if (Er_ret) {
  73. struct Er_Ret** Er_errOut = ${errOut ? errOut : '(struct Er_Ret**)0'};
  74. if (Er_errOut) {
  75. *Er_errOut = Er_unwind("${file}", ${line}, Er_ret);
  76. } else {
  77. Er__assertFail(Er_unwind("${file}", ${line}, Er_ret));
  78. }
  79. }
  80. `;
  81. }
  82. if (f.rett === 'void') {
  83. return `do {
  84. struct Er_Ret* Er_ret = ${expr};
  85. ${ifret}
  86. } while (0)`;
  87. } else {
  88. const args = expr.slice(expr.indexOf('(') + 1);
  89. return `(__extension__({
  90. ${f.rett} Er_returnVal;
  91. __builtin_memset(&Er_returnVal, 0, sizeof(Er_returnVal));
  92. struct Er_Ret* Er_ret = ${funcName}(&Er_returnVal, ${args};
  93. ${ifret}
  94. Er_returnVal;
  95. }))`;
  96. }
  97. };
  98. module.exports.create = () => {
  99. const ctx = {
  100. activeFunction: undefined,
  101. functions: {},
  102. };
  103. return {
  104. defun: (spec) => defun(ctx, spec).replace(/\n/g, ' '),
  105. ret: (val) => ret(ctx, val).replace(/\n/g, ' '),
  106. er: (expr, file, line) => er(ctx, false, null, expr, file, line).replace(/\n/g, ' '),
  107. assert: (expr, file, line) => er(ctx, true, null, expr, file, line).replace(/\n/g, ' '),
  108. check: (out, expr, file, line) => er(ctx, true, out, expr, file, line).replace(/\n/g, ' '),
  109. };
  110. };