mirror of
https://github.com/cjdelisle/cjdns
synced 2025-10-06 00:32:50 +02:00
115 lines
4.1 KiB
JavaScript
115 lines
4.1 KiB
JavaScript
/* vim: set expandtab ts=4 sw=4: */
|
|
/*
|
|
* You may redistribute this program and/or modify it under the terms of
|
|
* the GNU General Public License as published by the Free Software Foundation,
|
|
* either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
'use strict';
|
|
const trim = (x) => {
|
|
if (x[0] !== ' ' || x[x.length-1] !== ' ') {
|
|
throw new Error("INTERNAL: Er input must begin " +
|
|
"with and end with a space, unrecognized [" + x + "]");
|
|
}
|
|
return x.trim();
|
|
};
|
|
|
|
const defun = (ctx, spec) => {
|
|
trim(spec);
|
|
if (spec.lastIndexOf(')') !== spec.length - 2) {
|
|
throw new Error("Er function spec must end " +
|
|
"with a ), unrecognized [" + spec + "]");
|
|
}
|
|
let c = 1;
|
|
let i = spec.length - 3;
|
|
for (; c && i >= 0; i--) {
|
|
c += spec[i] === ')';
|
|
c -= spec[i] === '(';
|
|
}
|
|
const args = spec.slice(i + 2, spec.length - 2);
|
|
const rettAndFunc = spec.slice(0, i + 1).trim();
|
|
const func = rettAndFunc.replace(/^.*\s+([a-zA-Z_][a-zA-Z0-9_]*)$/, (all, a) => a);
|
|
//console.log('defun: ' + rettAndFunc + ' - ' + args);
|
|
if (func === rettAndFunc) {
|
|
throw new Error("Could not parse function [" + spec + "]");
|
|
}
|
|
const rett = rettAndFunc.replace(/\s+[a-zA-Z_][a-zA-Z0-9_]*$/, '').trim();
|
|
ctx.activeFunction = ctx.functions[func] = { rett: rett };
|
|
if (rett === 'void') {
|
|
return 'struct Er_Ret* ' + func + '(' + args + ')';
|
|
} else {
|
|
return 'struct Er_Ret* ' + func + '(' + rett + ' *Er_returnValP, ' + args + ')';
|
|
}
|
|
};
|
|
|
|
const ret = (ctx, val) => {
|
|
val = trim(val);
|
|
if (ctx.activeFunction.rett === 'void') {
|
|
return 'return (struct Er_Ret*)0';
|
|
} else {
|
|
return '*Er_returnValP = ' + val + '; return (struct Er_Ret*)0';
|
|
}
|
|
};
|
|
|
|
const er = (ctx, assert, errOut, expr, file, line) => {
|
|
expr = trim(expr);
|
|
if (!/[a-zA-Z_][a-zA-Z0-9_]*\(.*\)$/.test(expr)) {
|
|
throw new Error("Er() expr must be in the form Er(funcName(arg1, arg2, ...)) " +
|
|
"in [" + expr + "]");
|
|
}
|
|
const funcName = expr.slice(0, expr.indexOf('('));
|
|
const f = ctx.functions[funcName];
|
|
if (!f) {
|
|
throw new Error("Er() not a defined function [" + funcName + "] in [" + expr + "]");
|
|
}
|
|
let ifret = `if (Er_ret) { return Er_unwind("${file}", ${line}, Er_ret); }`;
|
|
if (assert) {
|
|
ifret = `
|
|
if (Er_ret) {
|
|
struct Er_Ret** Er_errOut = ${errOut ? errOut : '(struct Er_Ret**)0'};
|
|
if (Er_errOut) {
|
|
*Er_errOut = Er_unwind("${file}", ${line}, Er_ret);
|
|
} else {
|
|
Er__assertFail(Er_unwind("${file}", ${line}, Er_ret));
|
|
}
|
|
}
|
|
`;
|
|
}
|
|
if (f.rett === 'void') {
|
|
return `do {
|
|
struct Er_Ret* Er_ret = ${expr};
|
|
${ifret}
|
|
} while (0)`;
|
|
} else {
|
|
const args = expr.slice(expr.indexOf('(') + 1);
|
|
return `(__extension__({
|
|
${f.rett} Er_returnVal;
|
|
__builtin_memset(&Er_returnVal, 0, sizeof(Er_returnVal));
|
|
struct Er_Ret* Er_ret = ${funcName}(&Er_returnVal, ${args};
|
|
${ifret}
|
|
Er_returnVal;
|
|
}))`;
|
|
}
|
|
};
|
|
|
|
module.exports.create = () => {
|
|
const ctx = {
|
|
activeFunction: undefined,
|
|
functions: {},
|
|
};
|
|
return {
|
|
defun: (spec) => defun(ctx, spec).replace(/\n/g, ' '),
|
|
ret: (val) => ret(ctx, val).replace(/\n/g, ' '),
|
|
er: (expr, file, line) => er(ctx, false, null, expr, file, line).replace(/\n/g, ' '),
|
|
assert: (expr, file, line) => er(ctx, true, null, expr, file, line).replace(/\n/g, ' '),
|
|
check: (out, expr, file, line) => er(ctx, true, out, expr, file, line).replace(/\n/g, ' '),
|
|
};
|
|
};
|