cppbolt 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -62
- package/dist/compiler.d.ts +16 -0
- package/dist/compiler.js +92 -0
- package/dist/cppbolt.browser.js +509 -0
- package/dist/host.d.ts +45 -0
- package/dist/host.js +408 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +27 -0
- package/dist/runner.d.ts +29 -0
- package/dist/runner.js +40 -0
- package/package.json +15 -2
- package/wasm/cppbolt.wasm +0 -0
package/dist/host.js
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Formatting helpers for the C printf runtime contract.
|
|
3
|
+
// These reproduce the exact C printf rounding semantics for float/double output.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.createWasmHost = createWasmHost;
|
|
6
|
+
function roundToEvenExact(vNum, prec) {
|
|
7
|
+
if (isNaN(vNum))
|
|
8
|
+
return "nan";
|
|
9
|
+
if (vNum === Infinity)
|
|
10
|
+
return "inf";
|
|
11
|
+
if (vNum === -Infinity)
|
|
12
|
+
return "-inf";
|
|
13
|
+
if (Object.is(vNum, -0)) {
|
|
14
|
+
return "-0" + (prec > 0 ? "." + "0".repeat(prec) : "");
|
|
15
|
+
}
|
|
16
|
+
if (vNum === 0) {
|
|
17
|
+
return "0" + (prec > 0 ? "." + "0".repeat(prec) : "");
|
|
18
|
+
}
|
|
19
|
+
const isNeg = vNum < 0;
|
|
20
|
+
const v = Math.abs(vNum);
|
|
21
|
+
const buf = new ArrayBuffer(8);
|
|
22
|
+
const view = new DataView(buf);
|
|
23
|
+
view.setFloat64(0, v, false);
|
|
24
|
+
const bits = view.getBigUint64(0, false);
|
|
25
|
+
const rawExp = Number((bits >> 52n) & 0x7ffn);
|
|
26
|
+
const rawMantissa = bits & 0xfffffffffffffn;
|
|
27
|
+
let mantissa;
|
|
28
|
+
let exp;
|
|
29
|
+
if (rawExp === 0) {
|
|
30
|
+
mantissa = rawMantissa;
|
|
31
|
+
exp = -1022 - 52;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
mantissa = rawMantissa | (1n << 52n);
|
|
35
|
+
exp = rawExp - 1023 - 52;
|
|
36
|
+
}
|
|
37
|
+
const P = BigInt(prec);
|
|
38
|
+
const p5 = 5n ** P;
|
|
39
|
+
const expP = BigInt(exp) + P;
|
|
40
|
+
let num;
|
|
41
|
+
let den;
|
|
42
|
+
if (expP >= 0n) {
|
|
43
|
+
num = mantissa * 2n ** expP * p5;
|
|
44
|
+
den = 1n;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
num = mantissa * p5;
|
|
48
|
+
den = 2n ** -expP;
|
|
49
|
+
}
|
|
50
|
+
const Q = num / den;
|
|
51
|
+
const R = num % den;
|
|
52
|
+
const twoR = R * 2n;
|
|
53
|
+
let rounded;
|
|
54
|
+
if (twoR < den) {
|
|
55
|
+
rounded = Q;
|
|
56
|
+
}
|
|
57
|
+
else if (twoR > den) {
|
|
58
|
+
rounded = Q + 1n;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
rounded = Q % 2n === 0n ? Q : Q + 1n;
|
|
62
|
+
}
|
|
63
|
+
let s = rounded.toString();
|
|
64
|
+
if (prec > 0) {
|
|
65
|
+
while (s.length <= prec) {
|
|
66
|
+
s = "0" + s;
|
|
67
|
+
}
|
|
68
|
+
const intPart = s.slice(0, s.length - prec);
|
|
69
|
+
const fracPart = s.slice(s.length - prec);
|
|
70
|
+
s = intPart + "." + fracPart;
|
|
71
|
+
}
|
|
72
|
+
return (isNeg ? "-" : "") + s;
|
|
73
|
+
}
|
|
74
|
+
function formatExpExact(vNum, prec) {
|
|
75
|
+
if (isNaN(vNum))
|
|
76
|
+
return "nan";
|
|
77
|
+
if (vNum === Infinity)
|
|
78
|
+
return "inf";
|
|
79
|
+
if (vNum === -Infinity)
|
|
80
|
+
return "-inf";
|
|
81
|
+
if (Object.is(vNum, -0)) {
|
|
82
|
+
const zeros = prec > 0 ? "." + "0".repeat(prec) : "";
|
|
83
|
+
return "-0" + zeros + "e+00";
|
|
84
|
+
}
|
|
85
|
+
if (vNum === 0) {
|
|
86
|
+
const zeros = prec > 0 ? "." + "0".repeat(prec) : "";
|
|
87
|
+
return "0" + zeros + "e+00";
|
|
88
|
+
}
|
|
89
|
+
const isNeg = vNum < 0;
|
|
90
|
+
const v = Math.abs(vNum);
|
|
91
|
+
let exp = Math.floor(Math.log10(v));
|
|
92
|
+
const buf = new ArrayBuffer(8);
|
|
93
|
+
const view = new DataView(buf);
|
|
94
|
+
view.setFloat64(0, v, false);
|
|
95
|
+
const bits = view.getBigUint64(0, false);
|
|
96
|
+
const rawExp = Number((bits >> 52n) & 0x7ffn);
|
|
97
|
+
const rawMantissa = bits & 0xfffffffffffffn;
|
|
98
|
+
let mantissa;
|
|
99
|
+
let bexp;
|
|
100
|
+
if (rawExp === 0) {
|
|
101
|
+
mantissa = rawMantissa;
|
|
102
|
+
bexp = -1022 - 52;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
mantissa = rawMantissa | (1n << 52n);
|
|
106
|
+
bexp = rawExp - 1023 - 52;
|
|
107
|
+
}
|
|
108
|
+
let Q;
|
|
109
|
+
let R;
|
|
110
|
+
let den;
|
|
111
|
+
while (true) {
|
|
112
|
+
const shift = prec - exp;
|
|
113
|
+
const S = BigInt(shift);
|
|
114
|
+
let num;
|
|
115
|
+
if (S >= 0n) {
|
|
116
|
+
const p5 = 5n ** S;
|
|
117
|
+
const expP = BigInt(bexp) + S;
|
|
118
|
+
if (expP >= 0n) {
|
|
119
|
+
num = mantissa * 2n ** expP * p5;
|
|
120
|
+
den = 1n;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
num = mantissa * p5;
|
|
124
|
+
den = 2n ** -expP;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const negS = -S;
|
|
129
|
+
const p5 = 5n ** negS;
|
|
130
|
+
const expP = BigInt(bexp) - negS;
|
|
131
|
+
if (expP >= 0n) {
|
|
132
|
+
num = mantissa * 2n ** expP;
|
|
133
|
+
den = p5;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
num = mantissa;
|
|
137
|
+
den = p5 * 2n ** -expP;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
Q = num / den;
|
|
141
|
+
R = num % den;
|
|
142
|
+
if (Q === 0n && v > 0) {
|
|
143
|
+
exp--;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (Q >= 10n ** BigInt(prec + 1)) {
|
|
147
|
+
exp++;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
const twoR = R * 2n;
|
|
153
|
+
let rounded;
|
|
154
|
+
if (twoR < den) {
|
|
155
|
+
rounded = Q;
|
|
156
|
+
}
|
|
157
|
+
else if (twoR > den) {
|
|
158
|
+
rounded = Q + 1n;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
rounded = Q % 2n === 0n ? Q : Q + 1n;
|
|
162
|
+
}
|
|
163
|
+
if (rounded >= 10n ** BigInt(prec + 1)) {
|
|
164
|
+
rounded = rounded / 10n;
|
|
165
|
+
exp++;
|
|
166
|
+
}
|
|
167
|
+
let s = rounded.toString();
|
|
168
|
+
while (s.length < prec + 1) {
|
|
169
|
+
s = "0" + s;
|
|
170
|
+
}
|
|
171
|
+
const mStr = s[0] + (prec > 0 ? "." + s.slice(1) : "");
|
|
172
|
+
const expSign = exp >= 0 ? "+" : "-";
|
|
173
|
+
const expVal = Math.abs(exp).toString().padStart(2, "0");
|
|
174
|
+
return (isNeg ? "-" : "") + mStr + "e" + expSign + expVal;
|
|
175
|
+
}
|
|
176
|
+
function formatGExact(vNum, prec, alternate = false) {
|
|
177
|
+
if (isNaN(vNum))
|
|
178
|
+
return "nan";
|
|
179
|
+
if (vNum === Infinity)
|
|
180
|
+
return "inf";
|
|
181
|
+
if (vNum === -Infinity)
|
|
182
|
+
return "-inf";
|
|
183
|
+
if (!prec || prec <= 0)
|
|
184
|
+
prec = 1;
|
|
185
|
+
if (Object.is(vNum, -0) || vNum === 0) {
|
|
186
|
+
if (!alternate)
|
|
187
|
+
return Object.is(vNum, -0) ? "-0" : "0";
|
|
188
|
+
const res = roundToEvenExact(vNum, prec - 1);
|
|
189
|
+
return res.includes(".") ? res : res + ".";
|
|
190
|
+
}
|
|
191
|
+
const v = Math.abs(vNum);
|
|
192
|
+
const exp = Math.floor(Math.log10(v));
|
|
193
|
+
let res;
|
|
194
|
+
if (exp < -4 || exp >= prec) {
|
|
195
|
+
res = formatExpExact(vNum, prec - 1);
|
|
196
|
+
if (!alternate) {
|
|
197
|
+
let [m, e] = res.split("e");
|
|
198
|
+
if (m.includes(".")) {
|
|
199
|
+
m = m.replace(/0+$/, "").replace(/\.$/, "");
|
|
200
|
+
}
|
|
201
|
+
res = m + "e" + e;
|
|
202
|
+
}
|
|
203
|
+
else if (Number.isFinite(vNum) && !res.includes(".")) {
|
|
204
|
+
const e = res.indexOf("e");
|
|
205
|
+
res = e >= 0 ? res.slice(0, e) + "." + res.slice(e) : res + ".";
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const pAfterDot = Math.max(0, prec - 1 - exp);
|
|
210
|
+
res = roundToEvenExact(vNum, pAfterDot);
|
|
211
|
+
if (!alternate) {
|
|
212
|
+
if (res.includes(".")) {
|
|
213
|
+
res = res.replace(/0+$/, "").replace(/\.$/, "");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (Number.isFinite(vNum) && !res.includes(".")) {
|
|
217
|
+
res += ".";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return res;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Creates the runtime import environment (`env.*`) required by programs
|
|
224
|
+
* compiled with cppbolt.
|
|
225
|
+
*
|
|
226
|
+
* Every module emitted by the compiler imports exactly five functions:
|
|
227
|
+
* `print_char`, `print_int`, `print_double`, `read_char`, and `exit`.
|
|
228
|
+
* This function provides all five with correct printf-exact semantics that
|
|
229
|
+
* match the compiler's runtime contract.
|
|
230
|
+
*/
|
|
231
|
+
function createWasmHost(options = {}) {
|
|
232
|
+
const { stdin, onStdout, onExit } = options;
|
|
233
|
+
let stdoutBuffer = "";
|
|
234
|
+
let inPos = 0;
|
|
235
|
+
const writeStdout = (str) => {
|
|
236
|
+
stdoutBuffer += str;
|
|
237
|
+
if (onStdout) {
|
|
238
|
+
onStdout(str);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const imports = {
|
|
242
|
+
env: {
|
|
243
|
+
print_char: (ch) => {
|
|
244
|
+
writeStdout(String.fromCharCode(ch));
|
|
245
|
+
},
|
|
246
|
+
print_int: (val, radix, width, precision, flags) => {
|
|
247
|
+
const isZeroPad = (flags & 1) !== 0;
|
|
248
|
+
const isLeftAlign = (flags & 2) !== 0;
|
|
249
|
+
const isShowSign = (flags & 4) !== 0;
|
|
250
|
+
const isSpaceSign = (flags & 8) !== 0;
|
|
251
|
+
const isUppercase = (flags & 16) !== 0;
|
|
252
|
+
const isUnsigned = (flags & 32) !== 0;
|
|
253
|
+
const isAlternate = (flags & 64) !== 0;
|
|
254
|
+
let numVal = BigInt(val);
|
|
255
|
+
let isNeg = false;
|
|
256
|
+
if (isUnsigned) {
|
|
257
|
+
numVal = BigInt.asUintN(64, numVal);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
if (numVal < 0n) {
|
|
261
|
+
isNeg = true;
|
|
262
|
+
numVal = -numVal;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
let s = numVal.toString(radix);
|
|
266
|
+
if (isUppercase) {
|
|
267
|
+
s = s.toUpperCase();
|
|
268
|
+
}
|
|
269
|
+
if (precision >= 0) {
|
|
270
|
+
if (precision === 0 && numVal === 0n) {
|
|
271
|
+
s = "";
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
while (s.length < precision) {
|
|
275
|
+
s = "0" + s;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
let signStr = "";
|
|
280
|
+
if (isNeg) {
|
|
281
|
+
signStr = "-";
|
|
282
|
+
}
|
|
283
|
+
else if (!isUnsigned && isShowSign) {
|
|
284
|
+
signStr = "+";
|
|
285
|
+
}
|
|
286
|
+
else if (!isUnsigned && isSpaceSign) {
|
|
287
|
+
signStr = " ";
|
|
288
|
+
}
|
|
289
|
+
let prefix = "";
|
|
290
|
+
if (isAlternate) {
|
|
291
|
+
if (radix === 8) {
|
|
292
|
+
if (!s.startsWith("0")) {
|
|
293
|
+
prefix = "0";
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
else if (radix === 16 && numVal !== 0n) {
|
|
297
|
+
prefix = isUppercase ? "0X" : "0x";
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
let fullStr;
|
|
301
|
+
if (precision < 0 && isZeroPad && !isLeftAlign) {
|
|
302
|
+
let zeros = "";
|
|
303
|
+
while (signStr.length + prefix.length + zeros.length + s.length <
|
|
304
|
+
width) {
|
|
305
|
+
zeros += "0";
|
|
306
|
+
}
|
|
307
|
+
fullStr = signStr + prefix + zeros + s;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
fullStr = signStr + prefix + s;
|
|
311
|
+
}
|
|
312
|
+
if (fullStr.length < width) {
|
|
313
|
+
const padLen = width - fullStr.length;
|
|
314
|
+
const pad = " ".repeat(padLen);
|
|
315
|
+
fullStr = isLeftAlign ? fullStr + pad : pad + fullStr;
|
|
316
|
+
}
|
|
317
|
+
writeStdout(fullStr);
|
|
318
|
+
return fullStr.length;
|
|
319
|
+
},
|
|
320
|
+
print_double: (val, width, precision, mode, flags) => {
|
|
321
|
+
const isAlternate = (flags & 64) !== 0;
|
|
322
|
+
let s = "";
|
|
323
|
+
if (mode === 0) {
|
|
324
|
+
const prec = precision >= 0 ? precision : 6;
|
|
325
|
+
s = formatGExact(val, prec, isAlternate);
|
|
326
|
+
}
|
|
327
|
+
else if (mode === 1) {
|
|
328
|
+
const p = precision >= 0 ? precision : 6;
|
|
329
|
+
s = roundToEvenExact(val, p);
|
|
330
|
+
if (isAlternate && Number.isFinite(val) && !s.includes(".")) {
|
|
331
|
+
s += ".";
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else if (mode === 2) {
|
|
335
|
+
const p = precision >= 0 ? precision : 6;
|
|
336
|
+
s = formatExpExact(val, p);
|
|
337
|
+
if (isAlternate && Number.isFinite(val) && !s.includes(".")) {
|
|
338
|
+
const e = s.indexOf("e");
|
|
339
|
+
s = e >= 0 ? s.slice(0, e) + "." + s.slice(e) : s + ".";
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
throw new Error("unknown float format mode: " + mode);
|
|
344
|
+
}
|
|
345
|
+
if ((flags & 16) !== 0) {
|
|
346
|
+
s = s.toUpperCase();
|
|
347
|
+
}
|
|
348
|
+
const isZeroPad = (flags & 1) !== 0;
|
|
349
|
+
const isLeftAlign = (flags & 2) !== 0;
|
|
350
|
+
const isShowSign = (flags & 4) !== 0;
|
|
351
|
+
const isSpaceSign = (flags & 8) !== 0;
|
|
352
|
+
let sign = "";
|
|
353
|
+
if (s.startsWith("-")) {
|
|
354
|
+
sign = "-";
|
|
355
|
+
s = s.slice(1);
|
|
356
|
+
}
|
|
357
|
+
else if (isShowSign) {
|
|
358
|
+
sign = "+";
|
|
359
|
+
}
|
|
360
|
+
else if (isSpaceSign) {
|
|
361
|
+
sign = " ";
|
|
362
|
+
}
|
|
363
|
+
if (width > 0 && sign.length + s.length < width) {
|
|
364
|
+
const padLen = width - sign.length - s.length;
|
|
365
|
+
if (isZeroPad && !isLeftAlign && Number.isFinite(val)) {
|
|
366
|
+
s = sign + "0".repeat(padLen) + s;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
const pad = " ".repeat(padLen);
|
|
370
|
+
s = isLeftAlign ? sign + s + pad : pad + sign + s;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
s = sign + s;
|
|
375
|
+
}
|
|
376
|
+
writeStdout(s);
|
|
377
|
+
return s.length;
|
|
378
|
+
},
|
|
379
|
+
read_char: () => {
|
|
380
|
+
if (typeof stdin === "function") {
|
|
381
|
+
return stdin();
|
|
382
|
+
}
|
|
383
|
+
else if (typeof stdin === "string") {
|
|
384
|
+
if (inPos < stdin.length) {
|
|
385
|
+
return stdin.charCodeAt(inPos++);
|
|
386
|
+
}
|
|
387
|
+
return -1;
|
|
388
|
+
}
|
|
389
|
+
return -1;
|
|
390
|
+
},
|
|
391
|
+
exit: (code) => {
|
|
392
|
+
if (onExit) {
|
|
393
|
+
onExit(code);
|
|
394
|
+
}
|
|
395
|
+
else if (typeof process !== "undefined" &&
|
|
396
|
+
typeof process.exit === "function") {
|
|
397
|
+
process.exit(code);
|
|
398
|
+
}
|
|
399
|
+
// A browser with no onExit hook has nothing to stop the wasm program;
|
|
400
|
+
// browser hosts should supply onExit (the playground throws from it).
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
};
|
|
404
|
+
return {
|
|
405
|
+
imports,
|
|
406
|
+
getStdout: () => stdoutBuffer,
|
|
407
|
+
};
|
|
408
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CppBoltCompiler } from "./compiler.js";
|
|
2
|
+
import { type WasmHostOptions } from "./host.js";
|
|
3
|
+
import { type RunResult } from "./runner.js";
|
|
4
|
+
export { CppBoltCompiler, CompileError, createCompiler } from "./compiler.js";
|
|
5
|
+
export { createWasmHost } from "./host.js";
|
|
6
|
+
export type { WasmHost, WasmHostOptions } from "./host.js";
|
|
7
|
+
export { CppBoltRunner } from "./runner.js";
|
|
8
|
+
export type { RunResult } from "./runner.js";
|
|
9
|
+
/**
|
|
10
|
+
* Compiles `source` and runs the resulting module in one call.
|
|
11
|
+
*
|
|
12
|
+
* Builds a fresh host for the run, so streaming output and stdin are configured
|
|
13
|
+
* through `options`. Throws `CompileError` if `source` does not compile.
|
|
14
|
+
*/
|
|
15
|
+
export declare function compileAndRun(compiler: CppBoltCompiler, source: string, options?: WasmHostOptions): Promise<RunResult>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CppBoltRunner = exports.createWasmHost = exports.createCompiler = exports.CompileError = exports.CppBoltCompiler = void 0;
|
|
4
|
+
exports.compileAndRun = compileAndRun;
|
|
5
|
+
const host_js_1 = require("./host.js");
|
|
6
|
+
const runner_js_1 = require("./runner.js");
|
|
7
|
+
// Re-exports — the public surface of the package.
|
|
8
|
+
var compiler_js_1 = require("./compiler.js");
|
|
9
|
+
Object.defineProperty(exports, "CppBoltCompiler", { enumerable: true, get: function () { return compiler_js_1.CppBoltCompiler; } });
|
|
10
|
+
Object.defineProperty(exports, "CompileError", { enumerable: true, get: function () { return compiler_js_1.CompileError; } });
|
|
11
|
+
Object.defineProperty(exports, "createCompiler", { enumerable: true, get: function () { return compiler_js_1.createCompiler; } });
|
|
12
|
+
var host_js_2 = require("./host.js");
|
|
13
|
+
Object.defineProperty(exports, "createWasmHost", { enumerable: true, get: function () { return host_js_2.createWasmHost; } });
|
|
14
|
+
var runner_js_2 = require("./runner.js");
|
|
15
|
+
Object.defineProperty(exports, "CppBoltRunner", { enumerable: true, get: function () { return runner_js_2.CppBoltRunner; } });
|
|
16
|
+
/**
|
|
17
|
+
* Compiles `source` and runs the resulting module in one call.
|
|
18
|
+
*
|
|
19
|
+
* Builds a fresh host for the run, so streaming output and stdin are configured
|
|
20
|
+
* through `options`. Throws `CompileError` if `source` does not compile.
|
|
21
|
+
*/
|
|
22
|
+
async function compileAndRun(compiler, source, options) {
|
|
23
|
+
const host = (0, host_js_1.createWasmHost)(options);
|
|
24
|
+
const runner = new runner_js_1.CppBoltRunner(host);
|
|
25
|
+
const bytes = compiler.compile(source);
|
|
26
|
+
return runner.run(bytes);
|
|
27
|
+
}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { WasmHost } from "./host.js";
|
|
2
|
+
/**
|
|
3
|
+
* The outcome of running a compiled WebAssembly program.
|
|
4
|
+
*/
|
|
5
|
+
export interface RunResult {
|
|
6
|
+
/** Everything the program wrote to stdout. */
|
|
7
|
+
stdout: string;
|
|
8
|
+
/** The program's exit status, or 134 if it trapped. */
|
|
9
|
+
exitCode: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Instantiates and executes a compiled cppbolt module against a host.
|
|
13
|
+
*
|
|
14
|
+
* The runner knows nothing about how the bytes were produced, so it works
|
|
15
|
+
* equally with bytes from `CppBoltCompiler.compile()` and with a `.wasm` file
|
|
16
|
+
* loaded from disk or fetched from a URL.
|
|
17
|
+
*/
|
|
18
|
+
export declare class CppBoltRunner {
|
|
19
|
+
private readonly host;
|
|
20
|
+
constructor(host: WasmHost);
|
|
21
|
+
/**
|
|
22
|
+
* Instantiates `wasmBytes`, calls the program entry point (`main`, falling
|
|
23
|
+
* back to `_start`), and collects its stdout and exit status.
|
|
24
|
+
*
|
|
25
|
+
* A WebAssembly trap is reported as exit status 134 rather than thrown, so
|
|
26
|
+
* callers can inspect partial output the same way a shell would.
|
|
27
|
+
*/
|
|
28
|
+
run(wasmBytes: Uint8Array): Promise<RunResult>;
|
|
29
|
+
}
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CppBoltRunner = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Instantiates and executes a compiled cppbolt module against a host.
|
|
6
|
+
*
|
|
7
|
+
* The runner knows nothing about how the bytes were produced, so it works
|
|
8
|
+
* equally with bytes from `CppBoltCompiler.compile()` and with a `.wasm` file
|
|
9
|
+
* loaded from disk or fetched from a URL.
|
|
10
|
+
*/
|
|
11
|
+
class CppBoltRunner {
|
|
12
|
+
host;
|
|
13
|
+
constructor(host) {
|
|
14
|
+
this.host = host;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Instantiates `wasmBytes`, calls the program entry point (`main`, falling
|
|
18
|
+
* back to `_start`), and collects its stdout and exit status.
|
|
19
|
+
*
|
|
20
|
+
* A WebAssembly trap is reported as exit status 134 rather than thrown, so
|
|
21
|
+
* callers can inspect partial output the same way a shell would.
|
|
22
|
+
*/
|
|
23
|
+
async run(wasmBytes) {
|
|
24
|
+
const { instance } = await WebAssembly.instantiate(wasmBytes, this.host.imports);
|
|
25
|
+
let exitCode = 0;
|
|
26
|
+
try {
|
|
27
|
+
const exports = instance.exports;
|
|
28
|
+
const fn = exports.main ?? exports._start;
|
|
29
|
+
if (fn)
|
|
30
|
+
exitCode = fn() ?? 0;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// A WebAssembly trap — treat it like SIGABRT (exit 134), matching the
|
|
34
|
+
// standalone host runner.
|
|
35
|
+
return { stdout: this.host.getStdout(), exitCode: 134 };
|
|
36
|
+
}
|
|
37
|
+
return { stdout: this.host.getStdout(), exitCode };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.CppBoltRunner = CppBoltRunner;
|
package/package.json
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cppbolt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "A C++ compiler targeting WebAssembly",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/AJTimePyro/cppbolt.git"
|
|
9
9
|
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"jsdelivr": "./dist/cppbolt.browser.js",
|
|
13
|
+
"unpkg": "./dist/cppbolt.browser.js",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./wasm/cppbolt.wasm": "./wasm/cppbolt.wasm"
|
|
20
|
+
},
|
|
10
21
|
"files": [
|
|
22
|
+
"dist/",
|
|
11
23
|
"wasm/cppbolt.wasm",
|
|
12
24
|
"README.md"
|
|
13
|
-
]
|
|
25
|
+
],
|
|
26
|
+
"funding": "https://github.com/sponsors/AJTimePyro"
|
|
14
27
|
}
|
package/wasm/cppbolt.wasm
CHANGED
|
Binary file
|