wasm-idle 0.3.2 → 0.4.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 +4 -2
- package/dist/clang/app.d.ts +2 -1
- package/dist/clang/app.js +149 -132
- package/dist/clang/index.js +2 -2
- package/dist/debug/expression.js +135 -19
- package/dist/debug/index.d.ts +1 -1
- package/dist/debug/index.js +1 -1
- package/dist/debug/language/index.d.ts +1 -0
- package/dist/debug/language/index.js +1 -0
- package/dist/debug/language/python.d.ts +2 -0
- package/dist/debug/language/python.js +14 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/playground/clang.d.ts +5 -2
- package/dist/playground/clang.js +44 -18
- package/dist/playground/index.d.ts +2 -1
- package/dist/playground/index.js +7 -1
- package/dist/playground/java.d.ts +28 -0
- package/dist/playground/java.js +135 -0
- package/dist/playground/javaStdin.d.ts +8 -0
- package/dist/playground/javaStdin.js +90 -0
- package/dist/playground/options.d.ts +10 -0
- package/dist/playground/python.d.ts +15 -5
- package/dist/playground/python.js +98 -21
- package/dist/playground/sandbox.d.ts +3 -1
- package/dist/playground/stdinBuffer.d.ts +7 -0
- package/dist/playground/stdinBuffer.js +100 -0
- package/dist/playground/teavmConfig.d.ts +3 -0
- package/dist/playground/teavmConfig.js +14 -0
- package/dist/playground/worker/clang.js +2 -20
- package/dist/playground/worker/java.d.ts +1 -0
- package/dist/playground/worker/java.js +248 -0
- package/dist/playground/worker/python.js +253 -27
- package/dist/terminal/Terminal.svelte +41 -11
- package/dist/terminal/Terminal.svelte.d.ts +2 -1
- package/dist/terminal/types.d.ts +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Executes C++, Python
|
|
5
|
+
Executes C++, Python, and Java code with working stdio.
|
|
6
6
|
|
|
7
7
|
Refer to src/lib/clang.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Java uses TeaVM's browser compiler/runtime. TeaVM compiler/runtime/classlib assets are bundled under `static/teavm/` by default, and the asset base URL can be overridden with `PUBLIC_TEAVM_BASE_URL`.
|
|
10
|
+
|
|
11
|
+
Powered by [wasm-clang](https://github.com/binji/wasm-clang), Pyodide, and TeaVM.
|
package/dist/clang/app.d.ts
CHANGED
|
@@ -52,7 +52,8 @@ export default class App {
|
|
|
52
52
|
constructor(module: WebAssembly.Module, memfs: MemFS, name: string, ...args: string[]);
|
|
53
53
|
run(): Promise<boolean | undefined>;
|
|
54
54
|
proc_exit(code: number): void;
|
|
55
|
-
|
|
55
|
+
private pauseDebugSession;
|
|
56
|
+
__wasm_idle_debug_enter(functionId: number, line: number): number;
|
|
56
57
|
__wasm_idle_debug_leave(functionId: number): number;
|
|
57
58
|
__wasm_idle_debug_value_num(functionId: number, slot: number, value: number): number;
|
|
58
59
|
__wasm_idle_debug_value_bool(functionId: number, slot: number, value: number): number;
|
package/dist/clang/app.js
CHANGED
|
@@ -68,135 +68,9 @@ export default class App {
|
|
|
68
68
|
proc_exit(code) {
|
|
69
69
|
throw new ProcExit(code);
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
if (!
|
|
74
|
-
return ESUCCESS;
|
|
75
|
-
session.callDepth += 1;
|
|
76
|
-
session.currentFunctionId = functionId;
|
|
77
|
-
session.frames.push({
|
|
78
|
-
functionId,
|
|
79
|
-
functionName: session.functionMetadata[functionId] || `fn_${functionId}`,
|
|
80
|
-
line: 0,
|
|
81
|
-
values: new Map()
|
|
82
|
-
});
|
|
83
|
-
this.trace(`enter(function=${functionId}, depth=${session.callDepth})`);
|
|
84
|
-
return ESUCCESS;
|
|
85
|
-
}
|
|
86
|
-
__wasm_idle_debug_leave(functionId) {
|
|
87
|
-
const session = this.debugSession;
|
|
88
|
-
if (!session?.buffer)
|
|
89
|
-
return ESUCCESS;
|
|
90
|
-
this.trace(`leave(function=${functionId}, depth=${session.callDepth})`);
|
|
91
|
-
session.callDepth = Math.max(0, session.callDepth - 1);
|
|
92
|
-
if (session.currentFunctionId === functionId)
|
|
93
|
-
session.currentFunctionId = 0;
|
|
94
|
-
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
95
|
-
if (session.frames[index]?.functionId === functionId) {
|
|
96
|
-
session.frames.splice(index, 1);
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return ESUCCESS;
|
|
101
|
-
}
|
|
102
|
-
__wasm_idle_debug_value_num(functionId, slot, value) {
|
|
103
|
-
const session = this.debugSession;
|
|
104
|
-
if (!session?.buffer)
|
|
105
|
-
return ESUCCESS;
|
|
106
|
-
if (functionId === 0) {
|
|
107
|
-
session.globalValues.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
|
|
108
|
-
return ESUCCESS;
|
|
109
|
-
}
|
|
110
|
-
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
111
|
-
const frame = session.frames[index];
|
|
112
|
-
if (frame?.functionId !== functionId)
|
|
113
|
-
continue;
|
|
114
|
-
frame.values.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
return ESUCCESS;
|
|
118
|
-
}
|
|
119
|
-
__wasm_idle_debug_value_bool(functionId, slot, value) {
|
|
120
|
-
const session = this.debugSession;
|
|
121
|
-
if (!session?.buffer)
|
|
122
|
-
return ESUCCESS;
|
|
123
|
-
if (functionId === 0) {
|
|
124
|
-
session.globalValues.set(slot, value ? 'true' : 'false');
|
|
125
|
-
return ESUCCESS;
|
|
126
|
-
}
|
|
127
|
-
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
128
|
-
const frame = session.frames[index];
|
|
129
|
-
if (frame?.functionId !== functionId)
|
|
130
|
-
continue;
|
|
131
|
-
frame.values.set(slot, value ? 'true' : 'false');
|
|
132
|
-
break;
|
|
133
|
-
}
|
|
134
|
-
return ESUCCESS;
|
|
135
|
-
}
|
|
136
|
-
__wasm_idle_debug_value_addr(functionId, slot, value) {
|
|
137
|
-
const session = this.debugSession;
|
|
138
|
-
if (!session?.buffer)
|
|
139
|
-
return ESUCCESS;
|
|
140
|
-
if (functionId === 0) {
|
|
141
|
-
session.globalValues.set(slot, String(value >>> 0));
|
|
142
|
-
return ESUCCESS;
|
|
143
|
-
}
|
|
144
|
-
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
145
|
-
const frame = session.frames[index];
|
|
146
|
-
if (frame?.functionId !== functionId)
|
|
147
|
-
continue;
|
|
148
|
-
frame.values.set(slot, String(value >>> 0));
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
return ESUCCESS;
|
|
152
|
-
}
|
|
153
|
-
__wasm_idle_debug_value_text(functionId, slot, ptr, len) {
|
|
154
|
-
const session = this.debugSession;
|
|
155
|
-
if (!session?.buffer)
|
|
156
|
-
return ESUCCESS;
|
|
157
|
-
this.mem?.check?.();
|
|
158
|
-
const text = this.mem?.readStr ? this.mem.readStr(ptr, len) : '?';
|
|
159
|
-
if (functionId === 0) {
|
|
160
|
-
session.globalValues.set(slot, text);
|
|
161
|
-
return ESUCCESS;
|
|
162
|
-
}
|
|
163
|
-
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
164
|
-
const frame = session.frames[index];
|
|
165
|
-
if (frame?.functionId !== functionId)
|
|
166
|
-
continue;
|
|
167
|
-
frame.values.set(slot, text);
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
return ESUCCESS;
|
|
171
|
-
}
|
|
172
|
-
__wasm_idle_debug_line(functionId, line) {
|
|
173
|
-
const session = this.debugSession;
|
|
174
|
-
if (!session?.buffer)
|
|
175
|
-
return ESUCCESS;
|
|
176
|
-
if (session.resumeSkipActive) {
|
|
177
|
-
if (functionId === session.resumeSkipFunctionId && line === session.resumeSkipLine) {
|
|
178
|
-
return ESUCCESS;
|
|
179
|
-
}
|
|
180
|
-
session.resumeSkipActive = false;
|
|
181
|
-
session.resumeSkipFunctionId = 0;
|
|
182
|
-
session.resumeSkipLine = 0;
|
|
183
|
-
}
|
|
184
|
-
let reason = '';
|
|
185
|
-
if (session.pauseOnEntry)
|
|
186
|
-
reason = 'entry';
|
|
187
|
-
else if (session.breakpoints.has(line))
|
|
188
|
-
reason = 'breakpoint';
|
|
189
|
-
else if (session.stepArmed)
|
|
190
|
-
reason = 'step';
|
|
191
|
-
else if (session.nextLineArmed &&
|
|
192
|
-
functionId === session.nextLineFunctionId &&
|
|
193
|
-
line !== session.nextLineLine) {
|
|
194
|
-
reason = 'nextLine';
|
|
195
|
-
}
|
|
196
|
-
else if (session.stepOutArmed && session.callDepth <= session.stepOutDepth) {
|
|
197
|
-
reason = 'stepOut';
|
|
198
|
-
}
|
|
199
|
-
if (!reason)
|
|
71
|
+
pauseDebugSession(session, functionId, line, reason) {
|
|
72
|
+
const buffer = session.buffer;
|
|
73
|
+
if (!buffer)
|
|
200
74
|
return ESUCCESS;
|
|
201
75
|
session.currentFunctionId = functionId;
|
|
202
76
|
session.currentLine = line;
|
|
@@ -314,14 +188,14 @@ export default class App {
|
|
|
314
188
|
.reverse()
|
|
315
189
|
.map((stackFrame) => ({ functionName: stackFrame.functionName, line: stackFrame.line }))
|
|
316
190
|
});
|
|
317
|
-
const sequence = Atomics.load(
|
|
191
|
+
const sequence = Atomics.load(buffer, 0);
|
|
318
192
|
while (true) {
|
|
319
193
|
if (session.interruptBuffer?.[0] === 2)
|
|
320
194
|
throw new AbortError();
|
|
321
|
-
Atomics.wait(
|
|
195
|
+
Atomics.wait(buffer, 0, sequence, 100);
|
|
322
196
|
if (session.interruptBuffer?.[0] === 2)
|
|
323
197
|
throw new AbortError();
|
|
324
|
-
const command = Atomics.exchange(
|
|
198
|
+
const command = Atomics.exchange(buffer, 1, 0);
|
|
325
199
|
if (command === 1) {
|
|
326
200
|
session.resumeSkipActive = true;
|
|
327
201
|
session.resumeSkipFunctionId = session.currentFunctionId;
|
|
@@ -354,6 +228,149 @@ export default class App {
|
|
|
354
228
|
}
|
|
355
229
|
}
|
|
356
230
|
}
|
|
231
|
+
__wasm_idle_debug_enter(functionId, line) {
|
|
232
|
+
const session = this.debugSession;
|
|
233
|
+
if (!session?.buffer)
|
|
234
|
+
return ESUCCESS;
|
|
235
|
+
session.callDepth += 1;
|
|
236
|
+
session.currentFunctionId = functionId;
|
|
237
|
+
session.currentLine = line;
|
|
238
|
+
session.frames.push({
|
|
239
|
+
functionId,
|
|
240
|
+
functionName: session.functionMetadata[functionId] || `fn_${functionId}`,
|
|
241
|
+
line,
|
|
242
|
+
values: new Map()
|
|
243
|
+
});
|
|
244
|
+
this.trace(`enter(function=${functionId}, line=${line}, depth=${session.callDepth})`);
|
|
245
|
+
if (session.pauseOnEntry) {
|
|
246
|
+
return this.pauseDebugSession(session, functionId, line, 'entry');
|
|
247
|
+
}
|
|
248
|
+
if (session.stepArmed) {
|
|
249
|
+
return this.pauseDebugSession(session, functionId, line, 'step');
|
|
250
|
+
}
|
|
251
|
+
return ESUCCESS;
|
|
252
|
+
}
|
|
253
|
+
__wasm_idle_debug_leave(functionId) {
|
|
254
|
+
const session = this.debugSession;
|
|
255
|
+
if (!session?.buffer)
|
|
256
|
+
return ESUCCESS;
|
|
257
|
+
this.trace(`leave(function=${functionId}, depth=${session.callDepth})`);
|
|
258
|
+
if (session.nextLineArmed && functionId === session.nextLineFunctionId) {
|
|
259
|
+
session.nextLineArmed = false;
|
|
260
|
+
session.stepArmed = true;
|
|
261
|
+
}
|
|
262
|
+
session.callDepth = Math.max(0, session.callDepth - 1);
|
|
263
|
+
if (session.currentFunctionId === functionId)
|
|
264
|
+
session.currentFunctionId = 0;
|
|
265
|
+
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
266
|
+
if (session.frames[index]?.functionId === functionId) {
|
|
267
|
+
session.frames.splice(index, 1);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return ESUCCESS;
|
|
272
|
+
}
|
|
273
|
+
__wasm_idle_debug_value_num(functionId, slot, value) {
|
|
274
|
+
const session = this.debugSession;
|
|
275
|
+
if (!session?.buffer)
|
|
276
|
+
return ESUCCESS;
|
|
277
|
+
if (functionId === 0) {
|
|
278
|
+
session.globalValues.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
|
|
279
|
+
return ESUCCESS;
|
|
280
|
+
}
|
|
281
|
+
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
282
|
+
const frame = session.frames[index];
|
|
283
|
+
if (frame?.functionId !== functionId)
|
|
284
|
+
continue;
|
|
285
|
+
frame.values.set(slot, Number.isInteger(value) ? String(value) : `${value}`);
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
return ESUCCESS;
|
|
289
|
+
}
|
|
290
|
+
__wasm_idle_debug_value_bool(functionId, slot, value) {
|
|
291
|
+
const session = this.debugSession;
|
|
292
|
+
if (!session?.buffer)
|
|
293
|
+
return ESUCCESS;
|
|
294
|
+
if (functionId === 0) {
|
|
295
|
+
session.globalValues.set(slot, value ? 'true' : 'false');
|
|
296
|
+
return ESUCCESS;
|
|
297
|
+
}
|
|
298
|
+
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
299
|
+
const frame = session.frames[index];
|
|
300
|
+
if (frame?.functionId !== functionId)
|
|
301
|
+
continue;
|
|
302
|
+
frame.values.set(slot, value ? 'true' : 'false');
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
return ESUCCESS;
|
|
306
|
+
}
|
|
307
|
+
__wasm_idle_debug_value_addr(functionId, slot, value) {
|
|
308
|
+
const session = this.debugSession;
|
|
309
|
+
if (!session?.buffer)
|
|
310
|
+
return ESUCCESS;
|
|
311
|
+
if (functionId === 0) {
|
|
312
|
+
session.globalValues.set(slot, String(value >>> 0));
|
|
313
|
+
return ESUCCESS;
|
|
314
|
+
}
|
|
315
|
+
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
316
|
+
const frame = session.frames[index];
|
|
317
|
+
if (frame?.functionId !== functionId)
|
|
318
|
+
continue;
|
|
319
|
+
frame.values.set(slot, String(value >>> 0));
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
return ESUCCESS;
|
|
323
|
+
}
|
|
324
|
+
__wasm_idle_debug_value_text(functionId, slot, ptr, len) {
|
|
325
|
+
const session = this.debugSession;
|
|
326
|
+
if (!session?.buffer)
|
|
327
|
+
return ESUCCESS;
|
|
328
|
+
this.mem?.check?.();
|
|
329
|
+
const text = this.mem?.readStr ? this.mem.readStr(ptr, len) : '?';
|
|
330
|
+
if (functionId === 0) {
|
|
331
|
+
session.globalValues.set(slot, text);
|
|
332
|
+
return ESUCCESS;
|
|
333
|
+
}
|
|
334
|
+
for (let index = session.frames.length - 1; index >= 0; index -= 1) {
|
|
335
|
+
const frame = session.frames[index];
|
|
336
|
+
if (frame?.functionId !== functionId)
|
|
337
|
+
continue;
|
|
338
|
+
frame.values.set(slot, text);
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
return ESUCCESS;
|
|
342
|
+
}
|
|
343
|
+
__wasm_idle_debug_line(functionId, line) {
|
|
344
|
+
const session = this.debugSession;
|
|
345
|
+
if (!session?.buffer)
|
|
346
|
+
return ESUCCESS;
|
|
347
|
+
if (session.resumeSkipActive) {
|
|
348
|
+
if (functionId === session.resumeSkipFunctionId && line === session.resumeSkipLine) {
|
|
349
|
+
return ESUCCESS;
|
|
350
|
+
}
|
|
351
|
+
session.resumeSkipActive = false;
|
|
352
|
+
session.resumeSkipFunctionId = 0;
|
|
353
|
+
session.resumeSkipLine = 0;
|
|
354
|
+
}
|
|
355
|
+
let reason = '';
|
|
356
|
+
if (session.pauseOnEntry)
|
|
357
|
+
reason = 'entry';
|
|
358
|
+
else if (session.breakpoints.has(line))
|
|
359
|
+
reason = 'breakpoint';
|
|
360
|
+
else if (session.stepArmed)
|
|
361
|
+
reason = 'step';
|
|
362
|
+
else if (session.nextLineArmed &&
|
|
363
|
+
functionId === session.nextLineFunctionId &&
|
|
364
|
+
line !== session.nextLineLine) {
|
|
365
|
+
reason = 'nextLine';
|
|
366
|
+
}
|
|
367
|
+
else if (session.stepOutArmed && session.callDepth <= session.stepOutDepth) {
|
|
368
|
+
reason = 'stepOut';
|
|
369
|
+
}
|
|
370
|
+
if (!reason)
|
|
371
|
+
return ESUCCESS;
|
|
372
|
+
return this.pauseDebugSession(session, functionId, line, reason);
|
|
373
|
+
}
|
|
357
374
|
environ_sizes_get(environ_count_out, environ_buf_size_out) {
|
|
358
375
|
this.mem.check();
|
|
359
376
|
let size = 0;
|
package/dist/clang/index.js
CHANGED
|
@@ -151,7 +151,7 @@ export default class Clang {
|
|
|
151
151
|
'#include <string>',
|
|
152
152
|
'#include <type_traits>',
|
|
153
153
|
'#include <vector>',
|
|
154
|
-
'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_enter"))) void __wasm_idle_debug_enter(int functionId);',
|
|
154
|
+
'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_enter"))) void __wasm_idle_debug_enter(int functionId, int line);',
|
|
155
155
|
'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_leave"))) void __wasm_idle_debug_leave(int functionId);',
|
|
156
156
|
'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_value_num"))) void __wasm_idle_debug_value_num(int functionId, int slot, double value);',
|
|
157
157
|
'extern "C" __attribute__((import_module("env"), import_name("__wasm_idle_debug_value_bool"))) void __wasm_idle_debug_value_bool(int functionId, int slot, int value);',
|
|
@@ -584,7 +584,7 @@ export default class Clang {
|
|
|
584
584
|
nextVariableSlot = 1;
|
|
585
585
|
currentFunctionVariables = new Map();
|
|
586
586
|
currentFunctionContainers = new Map();
|
|
587
|
-
instrumented.push(`${indent} __wasm_idle_debug_enter(${currentFunctionId});`);
|
|
587
|
+
instrumented.push(`${indent} __wasm_idle_debug_enter(${currentFunctionId}, ${index + 1});`);
|
|
588
588
|
const parameterSource = startsInlineFunctionBody
|
|
589
589
|
? normalized.slice(normalized.indexOf('(') + 1, normalized.lastIndexOf(')'))
|
|
590
590
|
: pendingFunctionHeader?.parameters || '';
|
package/dist/debug/expression.js
CHANGED
|
@@ -8,6 +8,38 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
8
8
|
values.set(variable.name, variable.value);
|
|
9
9
|
const parsedValues = new Map();
|
|
10
10
|
const tokens = [];
|
|
11
|
+
const parseQuotedValue = (text, start) => {
|
|
12
|
+
const quote = text[start];
|
|
13
|
+
if (quote !== "'" && quote !== '"')
|
|
14
|
+
throw new Error('expected quoted string');
|
|
15
|
+
let index = start + 1;
|
|
16
|
+
let value = '';
|
|
17
|
+
while (index < text.length) {
|
|
18
|
+
const character = text[index];
|
|
19
|
+
if (!character)
|
|
20
|
+
break;
|
|
21
|
+
if (character === '\\') {
|
|
22
|
+
const escaped = text[index + 1];
|
|
23
|
+
if (!escaped)
|
|
24
|
+
throw new Error('unterminated string literal');
|
|
25
|
+
if (escaped === 'n')
|
|
26
|
+
value += '\n';
|
|
27
|
+
else if (escaped === 'r')
|
|
28
|
+
value += '\r';
|
|
29
|
+
else if (escaped === 't')
|
|
30
|
+
value += '\t';
|
|
31
|
+
else
|
|
32
|
+
value += escaped;
|
|
33
|
+
index += 2;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (character === quote)
|
|
37
|
+
return { value, next: index + 1 };
|
|
38
|
+
value += character;
|
|
39
|
+
index += 1;
|
|
40
|
+
}
|
|
41
|
+
throw new Error('unterminated string literal');
|
|
42
|
+
};
|
|
11
43
|
for (let index = 0; index < source.length;) {
|
|
12
44
|
const character = source[index];
|
|
13
45
|
if (!character)
|
|
@@ -32,6 +64,12 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
32
64
|
index += operator.length;
|
|
33
65
|
continue;
|
|
34
66
|
}
|
|
67
|
+
if (character === "'" || character === '"') {
|
|
68
|
+
const parsed = parseQuotedValue(source, index);
|
|
69
|
+
tokens.push({ type: 'string', value: parsed.value });
|
|
70
|
+
index = parsed.next;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
35
73
|
const number = source.slice(index).match(/^\d+(?:\.\d+)?/);
|
|
36
74
|
if (number?.[0]) {
|
|
37
75
|
tokens.push({ type: 'number', value: number[0] });
|
|
@@ -40,8 +78,26 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
40
78
|
}
|
|
41
79
|
const identifier = source.slice(index).match(/^[A-Za-z_]\w*/);
|
|
42
80
|
if (identifier?.[0]) {
|
|
43
|
-
if (identifier[0] === 'true' ||
|
|
44
|
-
|
|
81
|
+
if (identifier[0] === 'true' ||
|
|
82
|
+
identifier[0] === 'false' ||
|
|
83
|
+
identifier[0] === 'True' ||
|
|
84
|
+
identifier[0] === 'False') {
|
|
85
|
+
tokens.push({
|
|
86
|
+
type: 'boolean',
|
|
87
|
+
value: identifier[0] === 'true' || identifier[0] === 'True'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else if (identifier[0] === 'null' || identifier[0] === 'None') {
|
|
91
|
+
tokens.push({ type: 'null' });
|
|
92
|
+
}
|
|
93
|
+
else if (identifier[0] === 'and') {
|
|
94
|
+
tokens.push({ type: 'operator', value: '&&' });
|
|
95
|
+
}
|
|
96
|
+
else if (identifier[0] === 'or') {
|
|
97
|
+
tokens.push({ type: 'operator', value: '||' });
|
|
98
|
+
}
|
|
99
|
+
else if (identifier[0] === 'not') {
|
|
100
|
+
tokens.push({ type: 'operator', value: '!' });
|
|
45
101
|
}
|
|
46
102
|
else {
|
|
47
103
|
tokens.push({ type: 'identifier', value: identifier[0] });
|
|
@@ -81,16 +137,44 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
81
137
|
throw new Error('unsupported array preview');
|
|
82
138
|
}
|
|
83
139
|
}
|
|
84
|
-
if (character ===
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
140
|
+
if (character === '(') {
|
|
141
|
+
index += 1;
|
|
142
|
+
const items = [];
|
|
143
|
+
while (true) {
|
|
144
|
+
while (/\s/.test(text[index] || ''))
|
|
145
|
+
index += 1;
|
|
146
|
+
if (text[index] === ')')
|
|
147
|
+
return { value: items, next: index + 1 };
|
|
148
|
+
if (text.startsWith('...', index))
|
|
149
|
+
throw new Error('unavailable');
|
|
150
|
+
const item = parsePreviewValue(text, index);
|
|
151
|
+
items.push(item.value);
|
|
152
|
+
index = item.next;
|
|
153
|
+
while (/\s/.test(text[index] || ''))
|
|
154
|
+
index += 1;
|
|
155
|
+
if (text[index] === ',') {
|
|
156
|
+
index += 1;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (text[index] === ')')
|
|
160
|
+
return { value: items, next: index + 1 };
|
|
161
|
+
throw new Error('unsupported tuple preview');
|
|
162
|
+
}
|
|
89
163
|
}
|
|
164
|
+
if (character === "'" || character === '"')
|
|
165
|
+
return parseQuotedValue(text, index);
|
|
90
166
|
if (text.startsWith('true', index))
|
|
91
167
|
return { value: true, next: index + 4 };
|
|
92
168
|
if (text.startsWith('false', index))
|
|
93
169
|
return { value: false, next: index + 5 };
|
|
170
|
+
if (text.startsWith('True', index))
|
|
171
|
+
return { value: true, next: index + 4 };
|
|
172
|
+
if (text.startsWith('False', index))
|
|
173
|
+
return { value: false, next: index + 5 };
|
|
174
|
+
if (text.startsWith('null', index))
|
|
175
|
+
return { value: null, next: index + 4 };
|
|
176
|
+
if (text.startsWith('None', index))
|
|
177
|
+
return { value: null, next: index + 4 };
|
|
94
178
|
const number = text.slice(index).match(/^-?\d+(?:\.\d+)?/);
|
|
95
179
|
if (number?.[0])
|
|
96
180
|
return { value: Number(number[0]), next: index + number[0].length };
|
|
@@ -102,17 +186,24 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
102
186
|
const value = values.get(name);
|
|
103
187
|
if (value == null || value === '?')
|
|
104
188
|
throw new Error('unavailable');
|
|
105
|
-
if (value === 'true' || value === 'false') {
|
|
106
|
-
const parsed = value === 'true';
|
|
189
|
+
if (value === 'true' || value === 'false' || value === 'True' || value === 'False') {
|
|
190
|
+
const parsed = value === 'true' || value === 'True';
|
|
107
191
|
parsedValues.set(name, parsed);
|
|
108
192
|
return parsed;
|
|
109
193
|
}
|
|
194
|
+
if (value === 'null' || value === 'None') {
|
|
195
|
+
parsedValues.set(name, null);
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
110
198
|
const numeric = Number(value);
|
|
111
199
|
if (!Number.isNaN(numeric)) {
|
|
112
200
|
parsedValues.set(name, numeric);
|
|
113
201
|
return numeric;
|
|
114
202
|
}
|
|
115
|
-
if (value.startsWith('[')
|
|
203
|
+
if (value.startsWith('[') ||
|
|
204
|
+
value.startsWith('(') ||
|
|
205
|
+
value.startsWith("'") ||
|
|
206
|
+
value.startsWith('"')) {
|
|
116
207
|
const parsed = parsePreviewValue(value).value;
|
|
117
208
|
parsedValues.set(name, parsed);
|
|
118
209
|
return parsed;
|
|
@@ -131,10 +222,21 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
131
222
|
cursor += 1;
|
|
132
223
|
return token.value;
|
|
133
224
|
}
|
|
225
|
+
if (token.type === 'null') {
|
|
226
|
+
cursor += 1;
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
if (token.type === 'string') {
|
|
230
|
+
cursor += 1;
|
|
231
|
+
return token.value;
|
|
232
|
+
}
|
|
134
233
|
if (token.type === 'identifier') {
|
|
135
234
|
cursor += 1;
|
|
136
235
|
let resolved = resolveIdentifierValue(token.value);
|
|
137
|
-
while (
|
|
236
|
+
while (true) {
|
|
237
|
+
const bracket = tokens[cursor];
|
|
238
|
+
if (!bracket || bracket.type !== 'bracket' || bracket.value !== '[')
|
|
239
|
+
break;
|
|
138
240
|
cursor += 1;
|
|
139
241
|
const index = Number(parseOr());
|
|
140
242
|
const closing = tokens[cursor];
|
|
@@ -201,8 +303,14 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
201
303
|
return left;
|
|
202
304
|
cursor += 1;
|
|
203
305
|
const right = parseMul();
|
|
204
|
-
if (operator.value === '+')
|
|
205
|
-
|
|
306
|
+
if (operator.value === '+') {
|
|
307
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
308
|
+
left = `${left ?? 'null'}${right ?? 'null'}`;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
left = Number(left) + Number(right);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
206
314
|
if (operator.value === '-')
|
|
207
315
|
left = Number(left) - Number(right);
|
|
208
316
|
}
|
|
@@ -215,14 +323,16 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
215
323
|
return left;
|
|
216
324
|
cursor += 1;
|
|
217
325
|
const right = parseAdd();
|
|
326
|
+
const leftValue = typeof left === 'string' && typeof right === 'string' ? left : Number(left);
|
|
327
|
+
const rightValue = typeof left === 'string' && typeof right === 'string' ? right : Number(right);
|
|
218
328
|
if (operator.value === '<')
|
|
219
|
-
left =
|
|
329
|
+
left = leftValue < rightValue;
|
|
220
330
|
if (operator.value === '<=')
|
|
221
|
-
left =
|
|
331
|
+
left = leftValue <= rightValue;
|
|
222
332
|
if (operator.value === '>')
|
|
223
|
-
left =
|
|
333
|
+
left = leftValue > rightValue;
|
|
224
334
|
if (operator.value === '>=')
|
|
225
|
-
left =
|
|
335
|
+
left = leftValue >= rightValue;
|
|
226
336
|
}
|
|
227
337
|
};
|
|
228
338
|
const parseEquality = () => {
|
|
@@ -241,7 +351,10 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
241
351
|
};
|
|
242
352
|
const parseAnd = () => {
|
|
243
353
|
let left = parseEquality();
|
|
244
|
-
while (
|
|
354
|
+
while (true) {
|
|
355
|
+
const operator = tokens[cursor];
|
|
356
|
+
if (!operator || operator.type !== 'operator' || operator.value !== '&&')
|
|
357
|
+
break;
|
|
245
358
|
cursor += 1;
|
|
246
359
|
left = Boolean(left) && Boolean(parseEquality());
|
|
247
360
|
}
|
|
@@ -249,7 +362,10 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
249
362
|
};
|
|
250
363
|
const parseOr = () => {
|
|
251
364
|
let left = parseAnd();
|
|
252
|
-
while (
|
|
365
|
+
while (true) {
|
|
366
|
+
const operator = tokens[cursor];
|
|
367
|
+
if (!operator || operator.type !== 'operator' || operator.value !== '||')
|
|
368
|
+
break;
|
|
253
369
|
cursor += 1;
|
|
254
370
|
left = Boolean(left) || Boolean(parseAnd());
|
|
255
371
|
}
|
package/dist/debug/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { evaluateDebugExpression } from './expression';
|
|
2
2
|
export { selectInlineDebugLocals } from './locals';
|
|
3
|
-
export { cppDebugLanguageAdapter } from './language';
|
|
3
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter } from './language';
|
|
4
4
|
export { MonacoDebugView } from './editor';
|
|
5
5
|
export type { DebugLanguageAdapter } from './language';
|
package/dist/debug/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { evaluateDebugExpression } from './expression';
|
|
2
2
|
export { selectInlineDebugLocals } from './locals';
|
|
3
|
-
export { cppDebugLanguageAdapter } from './language';
|
|
3
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter } from './language';
|
|
4
4
|
export { MonacoDebugView } from './editor';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { evaluateDebugExpression } from '../expression';
|
|
2
|
+
const selectInlinePythonLocals = (lineText, locals) => {
|
|
3
|
+
const sanitized = lineText
|
|
4
|
+
.replace(/#.*$/g, ' ')
|
|
5
|
+
.replace(/"(?:\\.|[^"\\])*"/g, ' ')
|
|
6
|
+
.replace(/'(?:\\.|[^'\\])*'/g, ' ');
|
|
7
|
+
const identifiers = new Set(sanitized.match(/[A-Za-z_]\w*/g) || []);
|
|
8
|
+
return locals.filter((variable) => identifiers.has(variable.name));
|
|
9
|
+
};
|
|
10
|
+
export const pythonDebugLanguageAdapter = {
|
|
11
|
+
id: 'python',
|
|
12
|
+
evaluateExpression: evaluateDebugExpression,
|
|
13
|
+
selectInlineLocals: selectInlinePythonLocals
|
|
14
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Terminal from './terminal';
|
|
2
|
-
export { cppDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
2
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
3
3
|
import playground from './playground';
|
|
4
4
|
export default Terminal;
|
|
5
5
|
export { playground };
|
|
6
6
|
export type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, SandboxExecutionOptions, TerminalControl } from './terminal';
|
|
7
|
+
export type { CompilerDiagnostic } from './playground/options';
|
|
7
8
|
export type { DebugLanguageAdapter } from './debug';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Terminal from './terminal';
|
|
2
|
-
export { cppDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
2
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
3
3
|
import playground from './playground';
|
|
4
4
|
export default Terminal;
|
|
5
5
|
export { playground };
|