@robinpath/robinpath 0.30.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 +856 -0
- package/bin/robinpath.js +374 -0
- package/dist/classes/ASTSerializer.d.ts +45 -0
- package/dist/classes/ExecutionStateTracker.d.ts +30 -0
- package/dist/classes/Executor.d.ts +193 -0
- package/dist/classes/ExpressionEvaluator.d.ts +20 -0
- package/dist/classes/Lexer.d.ts +86 -0
- package/dist/classes/Parser.d.ts +71 -0
- package/dist/classes/RobinPathThread.d.ts +146 -0
- package/dist/classes/TokenStream.d.ts +217 -0
- package/dist/classes/code-converter/ASTToCodeConverter.d.ts +178 -0
- package/dist/classes/code-converter/LineIndex.d.ts +54 -0
- package/dist/classes/code-converter/Printer.d.ts +117 -0
- package/dist/classes/code-converter/Writer.d.ts +42 -0
- package/dist/classes/code-converter/index.d.ts +8 -0
- package/dist/classes/code-converter/types.d.ts +29 -0
- package/dist/classes/exceptions.d.ts +26 -0
- package/dist/classes/index.d.ts +16 -0
- package/dist/index.d.ts +485 -0
- package/dist/index.js +13808 -0
- package/dist/modules/Array.d.ts +10 -0
- package/dist/modules/Core.d.ts +10 -0
- package/dist/modules/Dom.d.ts +10 -0
- package/dist/modules/Fetch.d.ts +6 -0
- package/dist/modules/Json.d.ts +10 -0
- package/dist/modules/Math.d.ts +10 -0
- package/dist/modules/Object.d.ts +10 -0
- package/dist/modules/Random.d.ts +6 -0
- package/dist/modules/String.d.ts +10 -0
- package/dist/modules/Test.d.ts +10 -0
- package/dist/modules/Time.d.ts +10 -0
- package/dist/parsers/ArrayLiteralParser.d.ts +17 -0
- package/dist/parsers/AssignmentParser.d.ts +37 -0
- package/dist/parsers/BracketParser.d.ts +31 -0
- package/dist/parsers/BreakParser.d.ts +15 -0
- package/dist/parsers/CellBlockParser.d.ts +11 -0
- package/dist/parsers/ChunkMarkerParser.d.ts +12 -0
- package/dist/parsers/CommandParser.d.ts +56 -0
- package/dist/parsers/CommentParser.d.ts +37 -0
- package/dist/parsers/ContinueParser.d.ts +15 -0
- package/dist/parsers/DecoratorParser.d.ts +29 -0
- package/dist/parsers/DefineParser.d.ts +18 -0
- package/dist/parsers/EventParser.d.ts +17 -0
- package/dist/parsers/ExpressionParser.d.ts +3 -0
- package/dist/parsers/FenceClassifier.d.ts +29 -0
- package/dist/parsers/ForLoopParser.d.ts +17 -0
- package/dist/parsers/IfBlockParser.d.ts +17 -0
- package/dist/parsers/ObjectLiteralParser.d.ts +17 -0
- package/dist/parsers/ParserUtils.d.ts +15 -0
- package/dist/parsers/PromptBlockParser.d.ts +10 -0
- package/dist/parsers/ReturnParser.d.ts +16 -0
- package/dist/parsers/ScopeParser.d.ts +24 -0
- package/dist/parsers/StringTemplateParser.d.ts +31 -0
- package/dist/parsers/SubexpressionParser.d.ts +33 -0
- package/dist/parsers/TogetherBlockParser.d.ts +18 -0
- package/dist/parsers/WithScopeParser.d.ts +24 -0
- package/dist/types/Ast.type.d.ts +455 -0
- package/dist/types/Environment.type.d.ts +53 -0
- package/dist/utils/args.d.ts +24 -0
- package/dist/utils/errorFormatter.d.ts +22 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/stringParsing.d.ts +41 -0
- package/dist/utils/types.d.ts +15 -0
- package/dist/utils/valueConversion.d.ts +25 -0
- package/package.json +50 -0
package/bin/robinpath.js
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createInterface } from 'readline';
|
|
4
|
+
import { RobinPath } from '../dist/index.js';
|
|
5
|
+
|
|
6
|
+
// Create interpreter instance with thread control enabled and start a thread for REPL
|
|
7
|
+
const rp = new RobinPath({ threadControl: true });
|
|
8
|
+
rp.createThread('default'); // Creates and sets as currentThread
|
|
9
|
+
|
|
10
|
+
// Helper function to get the prompt with thread ID and current module
|
|
11
|
+
function getPrompt() {
|
|
12
|
+
if (!rp.currentThread) return '> ';
|
|
13
|
+
const threadId = rp.currentThread.id;
|
|
14
|
+
const currentModule = rp.currentThread.getCurrentModule();
|
|
15
|
+
if (currentModule) {
|
|
16
|
+
return `${threadId}@${currentModule}> `;
|
|
17
|
+
}
|
|
18
|
+
return `${threadId}> `;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Create readline interface (prompt will be updated after thread is ready)
|
|
22
|
+
const rl = createInterface({
|
|
23
|
+
input: process.stdin,
|
|
24
|
+
output: process.stdout,
|
|
25
|
+
prompt: '> '
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Set the initial prompt with current module
|
|
29
|
+
rl.setPrompt(getPrompt());
|
|
30
|
+
|
|
31
|
+
// State for multi-line blocks
|
|
32
|
+
let accumulatedLines = [];
|
|
33
|
+
|
|
34
|
+
// Helper function to check if a line ends with backslash (ignoring trailing whitespace)
|
|
35
|
+
function endsWithBackslash(line) {
|
|
36
|
+
const trimmed = line.trimEnd();
|
|
37
|
+
return trimmed.endsWith('\\');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Process a line of input
|
|
41
|
+
async function processLine(line) {
|
|
42
|
+
const trimmed = line.trim();
|
|
43
|
+
|
|
44
|
+
// Skip empty lines
|
|
45
|
+
if (!trimmed) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Handle exit commands
|
|
50
|
+
if (trimmed === 'exit' || trimmed === 'quit' || trimmed === '.exit' || trimmed === '.quit') {
|
|
51
|
+
console.log('Goodbye!');
|
|
52
|
+
rl.close();
|
|
53
|
+
process.exit(0);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle help
|
|
58
|
+
if (trimmed === 'help' || trimmed === '.help') {
|
|
59
|
+
console.log(`
|
|
60
|
+
RobinPath REPL Commands:
|
|
61
|
+
exit, quit, .exit, .quit - Exit the REPL
|
|
62
|
+
help, .help - Show this help message
|
|
63
|
+
clear, .clear - Clear the screen
|
|
64
|
+
.. - Show all available commands as JSON
|
|
65
|
+
|
|
66
|
+
Multi-line blocks:
|
|
67
|
+
def <name> ... enddef - Define a function
|
|
68
|
+
if <expr> ... endif - Conditional block
|
|
69
|
+
for $var in <expr> ... endfor - Loop block
|
|
70
|
+
scope ... endscope - Scope block
|
|
71
|
+
fn(...) - Parenthesized function call (multi-line)
|
|
72
|
+
{ ... } - Object literal (multi-line)
|
|
73
|
+
[ ... ] - Array literal (multi-line)
|
|
74
|
+
<line> \\ - Backslash line continuation
|
|
75
|
+
`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Handle clear
|
|
80
|
+
if (trimmed === 'clear' || trimmed === '.clear') {
|
|
81
|
+
console.clear();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Handle ".." command - show available commands
|
|
86
|
+
if (trimmed === '..') {
|
|
87
|
+
let commands;
|
|
88
|
+
if (rp.currentThread) {
|
|
89
|
+
commands = rp.currentThread.getAvailableCommands();
|
|
90
|
+
} else {
|
|
91
|
+
commands = rp.getAvailableCommands();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Just show JSON
|
|
95
|
+
console.log(JSON.stringify(commands, null, 2));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// If we have accumulated lines, add this line and check if block is complete
|
|
100
|
+
if (accumulatedLines.length > 0) {
|
|
101
|
+
accumulatedLines.push(line);
|
|
102
|
+
const script = accumulatedLines.join('\n');
|
|
103
|
+
|
|
104
|
+
// Check if line ends with backslash - if so, continue accumulating
|
|
105
|
+
if (endsWithBackslash(line)) {
|
|
106
|
+
// Still in continuation mode, update prompt
|
|
107
|
+
if (!rp.currentThread) {
|
|
108
|
+
rl.setPrompt(`... `);
|
|
109
|
+
} else {
|
|
110
|
+
const threadId = rp.currentThread.id;
|
|
111
|
+
const currentModule = rp.currentThread.getCurrentModule();
|
|
112
|
+
if (currentModule) {
|
|
113
|
+
rl.setPrompt(`[${threadId}]@[${currentModule}]... `);
|
|
114
|
+
} else {
|
|
115
|
+
rl.setPrompt(`[${threadId}]... `);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check if the block is now complete using the built-in method
|
|
122
|
+
let needsMore;
|
|
123
|
+
if (rp.currentThread) {
|
|
124
|
+
needsMore = rp.currentThread.needsMoreInput(script);
|
|
125
|
+
} else {
|
|
126
|
+
needsMore = rp.needsMoreInput(script);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!needsMore.needsMore) {
|
|
130
|
+
// Block is complete, execute it
|
|
131
|
+
const finalScript = accumulatedLines.join('\n');
|
|
132
|
+
accumulatedLines = [];
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
let result;
|
|
136
|
+
if (rp.currentThread) {
|
|
137
|
+
// Execute in current thread
|
|
138
|
+
result = await rp.currentThread.executeScript(finalScript);
|
|
139
|
+
} else {
|
|
140
|
+
// Execute in global thread (root RobinPath instance)
|
|
141
|
+
result = await rp.executeScript(finalScript);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check if result is from explain command (structured object)
|
|
145
|
+
if (result && typeof result === 'object' && !Array.isArray(result) && result.type) {
|
|
146
|
+
if (result.type === 'function') {
|
|
147
|
+
// Format function documentation
|
|
148
|
+
console.log(`\nFunction: ${result.name}`);
|
|
149
|
+
console.log(`\nDescription: ${result.description}\n`);
|
|
150
|
+
|
|
151
|
+
if (result.parameters && result.parameters.length > 0) {
|
|
152
|
+
console.log('Parameters:');
|
|
153
|
+
for (const param of result.parameters) {
|
|
154
|
+
let paramLine = ` - ${param.name} (${param.dataType})`;
|
|
155
|
+
if (param.required) {
|
|
156
|
+
paramLine += ' [required]';
|
|
157
|
+
}
|
|
158
|
+
console.log(paramLine);
|
|
159
|
+
console.log(` ${param.description}`);
|
|
160
|
+
if (param.formInputType) {
|
|
161
|
+
console.log(` Input type: ${param.formInputType}`);
|
|
162
|
+
}
|
|
163
|
+
if (param.defaultValue !== undefined) {
|
|
164
|
+
console.log(` Default: ${JSON.stringify(param.defaultValue)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
console.log('Parameters: None');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log(`\nReturns: ${result.returnType}`);
|
|
172
|
+
if (result.returnDescription) {
|
|
173
|
+
console.log(` ${result.returnDescription}`);
|
|
174
|
+
}
|
|
175
|
+
console.log('');
|
|
176
|
+
} else if (result.type === 'module') {
|
|
177
|
+
// Format module documentation
|
|
178
|
+
console.log(`\nModule: ${result.name}`);
|
|
179
|
+
console.log(`\nDescription: ${result.description}\n`);
|
|
180
|
+
|
|
181
|
+
if (result.methods && result.methods.length > 0) {
|
|
182
|
+
console.log('Available Methods:');
|
|
183
|
+
for (const method of result.methods) {
|
|
184
|
+
console.log(` - ${method}`);
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
console.log('Available Methods: None');
|
|
188
|
+
}
|
|
189
|
+
console.log('');
|
|
190
|
+
} else if (result.error) {
|
|
191
|
+
console.log(result.error);
|
|
192
|
+
}
|
|
193
|
+
} else if (result && typeof result === 'object' && result.error) {
|
|
194
|
+
// Handle error objects
|
|
195
|
+
console.log(result.error);
|
|
196
|
+
} else if (result !== null && result !== undefined) {
|
|
197
|
+
// Only show result if it's meaningful
|
|
198
|
+
// (log commands already print, so we don't need to show null)
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error(`Error: ${error.message}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Reset prompt after block execution
|
|
205
|
+
rl.setPrompt(getPrompt());
|
|
206
|
+
} else {
|
|
207
|
+
// Still in block mode, update prompt to show continuation
|
|
208
|
+
if (!rp.currentThread) {
|
|
209
|
+
rl.setPrompt(`... `);
|
|
210
|
+
} else {
|
|
211
|
+
const threadId = rp.currentThread.id;
|
|
212
|
+
const currentModule = rp.currentThread.getCurrentModule();
|
|
213
|
+
if (currentModule) {
|
|
214
|
+
rl.setPrompt(`[${threadId}]@[${currentModule}]... `);
|
|
215
|
+
} else {
|
|
216
|
+
rl.setPrompt(`[${threadId}]... `);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Check if this line ends with backslash - if so, enter continuation mode
|
|
225
|
+
if (endsWithBackslash(line)) {
|
|
226
|
+
accumulatedLines = [line];
|
|
227
|
+
if (!rp.currentThread) {
|
|
228
|
+
rl.setPrompt(`... `);
|
|
229
|
+
} else {
|
|
230
|
+
const threadId = rp.currentThread.id;
|
|
231
|
+
const currentModule = rp.currentThread.getCurrentModule();
|
|
232
|
+
if (currentModule) {
|
|
233
|
+
rl.setPrompt(`[${threadId}]@[${currentModule}]... `);
|
|
234
|
+
} else {
|
|
235
|
+
rl.setPrompt(`[${threadId}]... `);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check if this line starts an incomplete block using the built-in method
|
|
242
|
+
let needsMore;
|
|
243
|
+
if (rp.currentThread) {
|
|
244
|
+
needsMore = rp.currentThread.needsMoreInput(line);
|
|
245
|
+
} else {
|
|
246
|
+
needsMore = rp.needsMoreInput(line);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (needsMore.needsMore) {
|
|
250
|
+
// This line starts an incomplete block, enter block mode
|
|
251
|
+
accumulatedLines = [line];
|
|
252
|
+
if (!rp.currentThread) {
|
|
253
|
+
rl.setPrompt(`... `);
|
|
254
|
+
} else {
|
|
255
|
+
const threadId = rp.currentThread.id;
|
|
256
|
+
const currentModule = rp.currentThread.getCurrentModule();
|
|
257
|
+
if (currentModule) {
|
|
258
|
+
rl.setPrompt(`[${threadId}]@[${currentModule}]... `);
|
|
259
|
+
} else {
|
|
260
|
+
rl.setPrompt(`[${threadId}]... `);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Regular single-line command - use executeLine for persistent state
|
|
267
|
+
try {
|
|
268
|
+
let result;
|
|
269
|
+
if (rp.currentThread) {
|
|
270
|
+
// Execute in current thread
|
|
271
|
+
result = await rp.currentThread.executeLine(line);
|
|
272
|
+
} else {
|
|
273
|
+
// Execute in global thread (root RobinPath instance)
|
|
274
|
+
result = await rp.executeLine(line);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Check if result is from explain command (structured object)
|
|
278
|
+
if (result && typeof result === 'object' && !Array.isArray(result) && result.type) {
|
|
279
|
+
if (result.type === 'function') {
|
|
280
|
+
// Format function documentation
|
|
281
|
+
console.log(`\nFunction: ${result.name}`);
|
|
282
|
+
console.log(`\nDescription: ${result.description}\n`);
|
|
283
|
+
|
|
284
|
+
if (result.parameters && result.parameters.length > 0) {
|
|
285
|
+
console.log('Parameters:');
|
|
286
|
+
for (const param of result.parameters) {
|
|
287
|
+
let paramLine = ` - ${param.name} (${param.dataType})`;
|
|
288
|
+
if (param.required) {
|
|
289
|
+
paramLine += ' [required]';
|
|
290
|
+
}
|
|
291
|
+
console.log(paramLine);
|
|
292
|
+
console.log(` ${param.description}`);
|
|
293
|
+
if (param.formInputType) {
|
|
294
|
+
console.log(` Input type: ${param.formInputType}`);
|
|
295
|
+
}
|
|
296
|
+
if (param.defaultValue !== undefined) {
|
|
297
|
+
console.log(` Default: ${JSON.stringify(param.defaultValue)}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
console.log('Parameters: None');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
console.log(`\nReturns: ${result.returnType}`);
|
|
305
|
+
if (result.returnDescription) {
|
|
306
|
+
console.log(` ${result.returnDescription}`);
|
|
307
|
+
}
|
|
308
|
+
console.log('');
|
|
309
|
+
} else if (result.type === 'module') {
|
|
310
|
+
// Format module documentation
|
|
311
|
+
console.log(`\nModule: ${result.name}`);
|
|
312
|
+
console.log(`\nDescription: ${result.description}\n`);
|
|
313
|
+
|
|
314
|
+
if (result.methods && result.methods.length > 0) {
|
|
315
|
+
console.log('Available Methods:');
|
|
316
|
+
for (const method of result.methods) {
|
|
317
|
+
console.log(` - ${method}`);
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
console.log('Available Methods: None');
|
|
321
|
+
}
|
|
322
|
+
console.log('');
|
|
323
|
+
} else if (result.error) {
|
|
324
|
+
console.log(result.error);
|
|
325
|
+
}
|
|
326
|
+
} else if (result && typeof result === 'object' && result.error) {
|
|
327
|
+
// Handle error objects
|
|
328
|
+
console.log(result.error);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Update prompt in case module context changed (e.g., "use" command)
|
|
332
|
+
rl.setPrompt(getPrompt());
|
|
333
|
+
// Don't print null/undefined results (log commands handle their own output)
|
|
334
|
+
if (result !== null && result !== undefined && result !== '' &&
|
|
335
|
+
(!result || typeof result !== 'object' || (!result.type && !result.error))) {
|
|
336
|
+
// Only print if it's a meaningful value and not a structured object
|
|
337
|
+
// Most commands use log for output, so we skip printing here
|
|
338
|
+
}
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error(`Error: ${error.message}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Start the REPL
|
|
345
|
+
console.log('RobinPath REPL');
|
|
346
|
+
console.log('Type "help" for commands, "exit" to quit');
|
|
347
|
+
console.log('');
|
|
348
|
+
|
|
349
|
+
rl.prompt();
|
|
350
|
+
|
|
351
|
+
rl.on('line', async (line) => {
|
|
352
|
+
await processLine(line);
|
|
353
|
+
rl.prompt();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
rl.on('close', () => {
|
|
357
|
+
console.log('\nGoodbye!');
|
|
358
|
+
process.exit(0);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Handle Ctrl+C gracefully
|
|
362
|
+
process.on('SIGINT', () => {
|
|
363
|
+
if (accumulatedLines.length > 0) {
|
|
364
|
+
console.log('\nBlock cancelled. Returning to normal mode.');
|
|
365
|
+
accumulatedLines = [];
|
|
366
|
+
rl.setPrompt(getPrompt());
|
|
367
|
+
rl.prompt();
|
|
368
|
+
} else {
|
|
369
|
+
console.log('\nGoodbye!');
|
|
370
|
+
rl.close();
|
|
371
|
+
process.exit(0);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Environment } from '../types/Environment.type';
|
|
2
|
+
import { Statement, Arg } from '../types/Ast.type';
|
|
3
|
+
import { Value } from '../utils';
|
|
4
|
+
export declare class ASTSerializer {
|
|
5
|
+
private environment;
|
|
6
|
+
constructor(environment: Environment);
|
|
7
|
+
/**
|
|
8
|
+
* Find the module name for a given function name
|
|
9
|
+
* Returns the module name if found, null otherwise
|
|
10
|
+
*/
|
|
11
|
+
findModuleName(functionName: string, currentModuleContext?: string | null): string | null;
|
|
12
|
+
/**
|
|
13
|
+
|
|
14
|
+
* Get a simplified tree structure of the AST for navigation
|
|
15
|
+
|
|
16
|
+
*/
|
|
17
|
+
getStructure(statements: Statement[], nodeKeyPrefix?: string): any[];
|
|
18
|
+
/**
|
|
19
|
+
|
|
20
|
+
* Generate a human-readable label for a statement
|
|
21
|
+
|
|
22
|
+
*/
|
|
23
|
+
private getNodeLabel;
|
|
24
|
+
/**
|
|
25
|
+
|
|
26
|
+
* Serialize a statement to a JSON-serializable object
|
|
27
|
+
|
|
28
|
+
* @param stmt The statement to serialize
|
|
29
|
+
|
|
30
|
+
* @param currentModuleContext Optional module context from "use" command
|
|
31
|
+
|
|
32
|
+
* @param lastValue Optional last value (execution state) - can be a Value or a state object with lastValue and beforeValue
|
|
33
|
+
|
|
34
|
+
* @param nodeKey Optional key for the node
|
|
35
|
+
|
|
36
|
+
*/
|
|
37
|
+
serializeStatement(stmt: Statement, currentModuleContext?: string | null, lastValue?: Value | {
|
|
38
|
+
lastValue: Value;
|
|
39
|
+
beforeValue: Value;
|
|
40
|
+
} | null, nodeKey?: string): any;
|
|
41
|
+
/**
|
|
42
|
+
* Serialize an argument to a JSON-serializable object
|
|
43
|
+
*/
|
|
44
|
+
serializeArg(arg: Arg, currentModuleContext?: string | null, nodeKey?: string): any;
|
|
45
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Value } from '../utils';
|
|
2
|
+
export interface ExecutionStepInfo {
|
|
3
|
+
nodeKey: string;
|
|
4
|
+
variables: Record<string, Value>;
|
|
5
|
+
result: Value;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ExecutionLog {
|
|
9
|
+
message: string;
|
|
10
|
+
nodeKey?: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
source?: 'log' | 'say' | string;
|
|
13
|
+
}
|
|
14
|
+
export type LogCallback = (log: ExecutionLog) => void;
|
|
15
|
+
export declare class ExecutionStateTracker {
|
|
16
|
+
private steps;
|
|
17
|
+
private logs;
|
|
18
|
+
private nodeStates;
|
|
19
|
+
private indexedStates;
|
|
20
|
+
private onLog;
|
|
21
|
+
setLogCallback(callback: LogCallback | null): void;
|
|
22
|
+
addStep(step: ExecutionStepInfo): void;
|
|
23
|
+
addLog(log: ExecutionLog): void;
|
|
24
|
+
getSteps(): ExecutionStepInfo[];
|
|
25
|
+
getLogs(): ExecutionLog[];
|
|
26
|
+
getNodeState(nodeKey: string): ExecutionStepInfo | undefined;
|
|
27
|
+
getState(index: number): any;
|
|
28
|
+
setState(index: number, state: any): void;
|
|
29
|
+
clear(): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Value } from '../utils';
|
|
2
|
+
import { Environment, Frame } from '../index';
|
|
3
|
+
import { Statement, Arg, DefineFunction, OnBlock, DecoratorCall, Expression } from '../types/Ast.type';
|
|
4
|
+
import { RobinPathThread } from './RobinPathThread';
|
|
5
|
+
export declare class Executor {
|
|
6
|
+
private environment;
|
|
7
|
+
private callStack;
|
|
8
|
+
private parentThread;
|
|
9
|
+
private sourceCode;
|
|
10
|
+
private recursionDepth;
|
|
11
|
+
private currentStatement;
|
|
12
|
+
private static readonly MAX_RECURSION_DEPTH;
|
|
13
|
+
/**
|
|
14
|
+
* Debug mode flag - set to true to enable logging
|
|
15
|
+
* Can be controlled via VITE_DEBUG environment variable or set programmatically
|
|
16
|
+
*/
|
|
17
|
+
static debug: boolean;
|
|
18
|
+
constructor(environment: Environment, parentThread?: RobinPathThread | null, sourceCode?: string | null);
|
|
19
|
+
getCurrentFrame(frameOverride?: Frame): Frame;
|
|
20
|
+
getCurrentStatement(): Statement | null;
|
|
21
|
+
getEnvironment(): Environment;
|
|
22
|
+
getCallStack(): Frame[];
|
|
23
|
+
/**
|
|
24
|
+
* Compute Levenshtein distance between two strings
|
|
25
|
+
*/
|
|
26
|
+
private levenshteinDistance;
|
|
27
|
+
/**
|
|
28
|
+
* Find similar function names for "did you mean?" suggestions
|
|
29
|
+
*/
|
|
30
|
+
private findSimilarFunctions;
|
|
31
|
+
/**
|
|
32
|
+
* Format "Unknown function" error with suggestions
|
|
33
|
+
*/
|
|
34
|
+
private unknownFunctionError;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new Executor instance that shares the same environment and call stack,
|
|
37
|
+
* but has its own call stack array to allow parallel execution without stack corruption.
|
|
38
|
+
* The frames themselves (locals Maps) are shared.
|
|
39
|
+
*/
|
|
40
|
+
spawnChild(): Executor;
|
|
41
|
+
/**
|
|
42
|
+
* Execute an event handler with the provided arguments
|
|
43
|
+
* Arguments are available as $1, $2, $3, etc. in the handler body
|
|
44
|
+
*/
|
|
45
|
+
executeEventHandler(handler: OnBlock, args: Value[]): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Execute a single statement (public method for state tracking)
|
|
48
|
+
*/
|
|
49
|
+
executeStatementPublic(stmt: Statement): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Execute a function call and return the result (public method for expression evaluation)
|
|
52
|
+
*/
|
|
53
|
+
executeFunctionCall(funcName: string, args: Arg[]): Promise<Value>;
|
|
54
|
+
execute(statements: Statement[]): Promise<Value>;
|
|
55
|
+
private executeStatement;
|
|
56
|
+
/**
|
|
57
|
+
* Get all variables visible in the current frame
|
|
58
|
+
*/
|
|
59
|
+
private getVariableStateInternal;
|
|
60
|
+
/**
|
|
61
|
+
* Reconstructs the original input string from an Arg object.
|
|
62
|
+
* This is useful for commands that need to preserve the original input
|
|
63
|
+
* (e.g., variable/function names) rather than evaluating them.
|
|
64
|
+
*
|
|
65
|
+
* Examples:
|
|
66
|
+
* - { type: 'var', name: 'a' } -> '$a'
|
|
67
|
+
* - { type: 'var', name: 'a', path: [{ type: 'property', name: 'b' }] } -> '$a.b'
|
|
68
|
+
* - { type: 'var', name: 'a', path: [{ type: 'index', index: 0 }] } -> '$a[0]'
|
|
69
|
+
* - { type: 'string', value: 'hello' } -> 'hello'
|
|
70
|
+
*
|
|
71
|
+
* @param arg The Arg object to reconstruct
|
|
72
|
+
* @returns The original input string, or null if the arg type cannot be reconstructed
|
|
73
|
+
*/
|
|
74
|
+
private reconstructOriginalInput;
|
|
75
|
+
private executeCommand;
|
|
76
|
+
/**
|
|
77
|
+
* Execute decorators for a target (function or variable)
|
|
78
|
+
* @param decorators Array of decorator calls
|
|
79
|
+
* @param targetName Name of the target (function or variable)
|
|
80
|
+
* @param func Function object (null for variables)
|
|
81
|
+
* @param originalArgs Original arguments (for functions, empty array for variables)
|
|
82
|
+
* @returns Modified arguments (for functions) or original args unchanged
|
|
83
|
+
*/
|
|
84
|
+
executeDecorators(decorators: DecoratorCall[], targetName: string, func: DefineFunction | null, originalArgs: Value[], frameOverride?: Frame): Promise<Value[]>;
|
|
85
|
+
private callFunction;
|
|
86
|
+
private executeAssignment;
|
|
87
|
+
private executeShorthandAssignment;
|
|
88
|
+
private executeInlineIf;
|
|
89
|
+
private executeIfBlock;
|
|
90
|
+
private executeIfTrue;
|
|
91
|
+
private executeIfFalse;
|
|
92
|
+
private executeReturn;
|
|
93
|
+
private executeBreak;
|
|
94
|
+
private executeContinue;
|
|
95
|
+
private registerFunction;
|
|
96
|
+
private registerEventHandler;
|
|
97
|
+
private executeScope;
|
|
98
|
+
private executeTogether;
|
|
99
|
+
/**
|
|
100
|
+
* Set a variable at a path in the parent scope (for together blocks)
|
|
101
|
+
*/
|
|
102
|
+
private setVariableAtPathInParentScope;
|
|
103
|
+
private executeForLoop;
|
|
104
|
+
private evaluateArg;
|
|
105
|
+
/**
|
|
106
|
+
* Interpolate variables and subexpressions in object literal code
|
|
107
|
+
* Replaces $var, $(expr), $ (last value), and [$key] with their actual values
|
|
108
|
+
*
|
|
109
|
+
* TODO: AST Refactor - This method will be removed once ObjectLiteralExpression
|
|
110
|
+
* is used. Object literals will be evaluated by walking the properties AST nodes.
|
|
111
|
+
*/
|
|
112
|
+
private interpolateObjectLiteral;
|
|
113
|
+
/**
|
|
114
|
+
* Execute a subexpression from code string with frame override support
|
|
115
|
+
*
|
|
116
|
+
* @param code - The subexpression code to execute
|
|
117
|
+
* @param frameOverride - Optional frame override for variable resolution
|
|
118
|
+
*/
|
|
119
|
+
private executeSubexpressionWithFrame;
|
|
120
|
+
/**
|
|
121
|
+
* Execute a subexpression from code string
|
|
122
|
+
*
|
|
123
|
+
* TODO: AST Refactor - This will be replaced with executeSubexpressionStatements()
|
|
124
|
+
* which takes Statement[] directly, eliminating runtime parsing.
|
|
125
|
+
*/
|
|
126
|
+
executeSubexpression(code: string): Promise<Value>;
|
|
127
|
+
/**
|
|
128
|
+
* Execute subexpression statements directly (for Expression-based AST)
|
|
129
|
+
*
|
|
130
|
+
* @param statements - Pre-parsed statements from SubexpressionExpression.body
|
|
131
|
+
* @param frameOverride - Optional frame override for parallel execution
|
|
132
|
+
* @returns The lastValue after executing the statements
|
|
133
|
+
*/
|
|
134
|
+
executeSubexpressionStatements(statements: Statement[], frameOverride?: Frame): Promise<Value>;
|
|
135
|
+
/**
|
|
136
|
+
* Evaluate an Expression node
|
|
137
|
+
*
|
|
138
|
+
* @param expr - The Expression node to evaluate
|
|
139
|
+
* @param frameOverride - Optional frame override
|
|
140
|
+
* @returns The evaluated value
|
|
141
|
+
*/
|
|
142
|
+
evaluateExpression(expr: Expression, frameOverride?: Frame): Promise<Value>;
|
|
143
|
+
/**
|
|
144
|
+
* Evaluate an ObjectLiteralExpression
|
|
145
|
+
*
|
|
146
|
+
* @param expr - The ObjectLiteralExpression node
|
|
147
|
+
* @param frameOverride - Optional frame override
|
|
148
|
+
* @returns The evaluated object
|
|
149
|
+
*/
|
|
150
|
+
evaluateObjectLiteral(expr: import('../types/Ast.type').ObjectLiteralExpression, frameOverride?: Frame): Promise<Record<string, Value>>;
|
|
151
|
+
/**
|
|
152
|
+
* Evaluate an ArrayLiteralExpression
|
|
153
|
+
*
|
|
154
|
+
* @param expr - The ArrayLiteralExpression node
|
|
155
|
+
* @param frameOverride - Optional frame override
|
|
156
|
+
* @returns The evaluated array
|
|
157
|
+
*/
|
|
158
|
+
evaluateArrayLiteral(expr: import('../types/Ast.type').ArrayLiteralExpression, frameOverride?: Frame): Promise<Value[]>;
|
|
159
|
+
/**
|
|
160
|
+
* Evaluate a BinaryExpression
|
|
161
|
+
*
|
|
162
|
+
* @param expr - The BinaryExpression node
|
|
163
|
+
* @param frameOverride - Optional frame override
|
|
164
|
+
* @returns The evaluated value
|
|
165
|
+
*/
|
|
166
|
+
evaluateBinaryExpression(expr: import('../types/Ast.type').BinaryExpression, frameOverride?: Frame): Promise<Value>;
|
|
167
|
+
/**
|
|
168
|
+
* Evaluate a UnaryExpression
|
|
169
|
+
*
|
|
170
|
+
* @param expr - The UnaryExpression node
|
|
171
|
+
* @param frameOverride - Optional frame override
|
|
172
|
+
* @returns The evaluated value
|
|
173
|
+
*/
|
|
174
|
+
evaluateUnaryExpression(expr: import('../types/Ast.type').UnaryExpression, frameOverride?: Frame): Promise<Value>;
|
|
175
|
+
/**
|
|
176
|
+
* Evaluate a CallExpression
|
|
177
|
+
*
|
|
178
|
+
* @param expr - The CallExpression node
|
|
179
|
+
* @param frameOverride - Optional frame override
|
|
180
|
+
* @returns The evaluated value
|
|
181
|
+
*/
|
|
182
|
+
evaluateCallExpression(expr: import('../types/Ast.type').CallExpression, frameOverride?: Frame): Promise<Value>;
|
|
183
|
+
private resolveVariable;
|
|
184
|
+
private setVariable;
|
|
185
|
+
/**
|
|
186
|
+
* Set a value at an attribute path (e.g., $animal.cat = 5 or $.property = value)
|
|
187
|
+
*/
|
|
188
|
+
private setVariableAtPath;
|
|
189
|
+
/**
|
|
190
|
+
* Resolve a dynamic key segment to its actual key value
|
|
191
|
+
*/
|
|
192
|
+
private resolveDynamicKey;
|
|
193
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Frame, Environment } from '../index';
|
|
2
|
+
import { Executor } from './Executor';
|
|
3
|
+
export declare class ExpressionEvaluator {
|
|
4
|
+
private frame;
|
|
5
|
+
private globals;
|
|
6
|
+
private executor;
|
|
7
|
+
constructor(frame: Frame, globals: Environment, executor?: Executor | null);
|
|
8
|
+
evaluate(expr: string): Promise<boolean>;
|
|
9
|
+
/**
|
|
10
|
+
* Replace all subexpressions $(...) in an expression string with their evaluated values
|
|
11
|
+
*/
|
|
12
|
+
private replaceSubexpressions;
|
|
13
|
+
/**
|
|
14
|
+
* Extract a subexpression $(...) from a string, starting at the given position
|
|
15
|
+
* Returns null if no valid subexpression is found
|
|
16
|
+
*/
|
|
17
|
+
private extractSubexpressionFromString;
|
|
18
|
+
private executeFunctionCall;
|
|
19
|
+
private resolveVariable;
|
|
20
|
+
}
|