starlight-cli 1.0.24 → 1.0.26
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/dist/index.js +327 -254
- package/package.json +1 -1
- package/src/evaluator.js +292 -249
- package/src/lexer.js +7 -4
- package/src/parser.js +28 -1
- package/src/starlight.js +1 -1
package/package.json
CHANGED
package/src/evaluator.js
CHANGED
|
@@ -73,63 +73,100 @@ class Evaluator {
|
|
|
73
73
|
}
|
|
74
74
|
return n;
|
|
75
75
|
});
|
|
76
|
+
this.global.define('fetch', async (url, options = {}) => {
|
|
77
|
+
const res = await fetch(url, options);
|
|
78
|
+
return {
|
|
79
|
+
status: res.status,
|
|
80
|
+
ok: res.ok,
|
|
81
|
+
text: async () => await res.text(),
|
|
82
|
+
json: async () => await res.json()
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// GET request
|
|
87
|
+
this.global.define('get', async (url) => {
|
|
88
|
+
const res = await fetch(url);
|
|
89
|
+
return await res.json();
|
|
90
|
+
});
|
|
76
91
|
|
|
92
|
+
this.global.define('post', async (url, data) => {
|
|
93
|
+
const res = await fetch(url, {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
headers: { 'Content-Type': 'application/json' },
|
|
96
|
+
body: JSON.stringify(data)
|
|
97
|
+
});
|
|
98
|
+
return await res.json();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.global.define('sleep', async (ms) => {
|
|
102
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
103
|
+
});
|
|
77
104
|
this.global.define('str', arg => {
|
|
78
105
|
return String(arg);
|
|
79
106
|
});
|
|
80
107
|
|
|
81
108
|
}
|
|
82
109
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
async evaluate(node, env = this.global) {
|
|
111
|
+
switch (node.type) {
|
|
112
|
+
case 'Program': return await this.evalProgram(node, env);
|
|
113
|
+
case 'BlockStatement': return await this.evalBlock(node, new Environment(env));
|
|
114
|
+
case 'VarDeclaration': return await this.evalVarDeclaration(node, env);
|
|
115
|
+
case 'AssignmentExpression': return await this.evalAssignment(node, env);
|
|
116
|
+
case 'CompoundAssignment': return await this.evalCompoundAssignment(node, env);
|
|
117
|
+
case 'SldeployStatement': return await this.evalSldeploy(node, env);
|
|
118
|
+
case 'AskStatement': return await this.evalAsk(node, env);
|
|
119
|
+
case 'DefineStatement': return await this.evalDefine(node, env);
|
|
120
|
+
case 'ExpressionStatement': return await this.evaluate(node.expression, env);
|
|
121
|
+
case 'BinaryExpression': return await this.evalBinary(node, env);
|
|
122
|
+
case 'LogicalExpression': return await this.evalLogical(node, env);
|
|
123
|
+
case 'UnaryExpression': return await this.evalUnary(node, env);
|
|
124
|
+
case 'Literal': return node.value;
|
|
125
|
+
case 'Identifier': return env.get(node.name);
|
|
126
|
+
case 'IfStatement': return await this.evalIf(node, env);
|
|
127
|
+
case 'WhileStatement': return await this.evalWhile(node, env);
|
|
128
|
+
case 'ForStatement': return await this.evalFor(node, env);
|
|
129
|
+
case 'BreakStatement': throw new BreakSignal();
|
|
130
|
+
case 'ContinueStatement': throw new ContinueSignal();
|
|
131
|
+
case 'ImportStatement': return await this.evalImport(node, env);
|
|
132
|
+
case 'FunctionDeclaration': return this.evalFunctionDeclaration(node, env);
|
|
133
|
+
case 'CallExpression': return await this.evalCall(node, env);
|
|
134
|
+
case 'ArrowFunctionExpression': return this.evalArrowFunction(node, env);
|
|
135
|
+
case 'ReturnStatement': {
|
|
136
|
+
const val = node.argument ? await this.evaluate(node.argument, env) : null;
|
|
137
|
+
throw new ReturnValue(val);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
case 'ArrayExpression':
|
|
141
|
+
return await Promise.all(node.elements.map(el => this.evaluate(el, env)));
|
|
142
|
+
case 'IndexExpression': return await this.evalIndex(node, env);
|
|
143
|
+
case 'ObjectExpression': {
|
|
144
|
+
const out = {};
|
|
145
|
+
for (const p of node.props) {
|
|
146
|
+
out[p.key] = await this.evaluate(p.value, env);
|
|
114
147
|
}
|
|
115
|
-
|
|
116
|
-
case 'IndexExpression': return this.evalIndex(node, env);
|
|
117
|
-
case 'ObjectExpression': return this.evalObject(node, env);
|
|
118
|
-
case 'MemberExpression': return this.evalMember(node, env);
|
|
119
|
-
case 'UpdateExpression': return this.evalUpdate(node, env);
|
|
120
|
-
default:
|
|
121
|
-
throw new Error(`Unknown node type in evaluator: ${node.type}`);
|
|
148
|
+
return out;
|
|
122
149
|
}
|
|
150
|
+
case 'MemberExpression': return await this.evalMember(node, env);
|
|
151
|
+
case 'UpdateExpression': return await this.evalUpdate(node, env);
|
|
152
|
+
case 'AwaitExpression': {
|
|
153
|
+
const val = await this.evaluate(node.argument, env);
|
|
154
|
+
return val;
|
|
155
|
+
}
|
|
156
|
+
default:
|
|
157
|
+
throw new Error(`Unknown node type in evaluator: ${node.type}`);
|
|
123
158
|
}
|
|
159
|
+
}
|
|
124
160
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
return result;
|
|
161
|
+
async evalProgram(node, env) {
|
|
162
|
+
let result = null;
|
|
163
|
+
for (const stmt of node.body) {
|
|
164
|
+
result = await this.evaluate(stmt, env);
|
|
131
165
|
}
|
|
132
|
-
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async evalImport(node, env) {
|
|
133
170
|
const spec = node.path;
|
|
134
171
|
let lib;
|
|
135
172
|
|
|
@@ -152,7 +189,7 @@ evalImport(node, env) {
|
|
|
152
189
|
const ast = new Parser(tokens).parse();
|
|
153
190
|
|
|
154
191
|
const moduleEnv = new Environment(env);
|
|
155
|
-
this.evaluate(ast, moduleEnv);
|
|
192
|
+
await this.evaluate(ast, moduleEnv);
|
|
156
193
|
|
|
157
194
|
lib = {};
|
|
158
195
|
for (const key of Object.keys(moduleEnv.store)) {
|
|
@@ -180,259 +217,265 @@ evalImport(node, env) {
|
|
|
180
217
|
return null;
|
|
181
218
|
}
|
|
182
219
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
throw e;
|
|
192
|
-
}
|
|
220
|
+
async evalBlock(node, env) {
|
|
221
|
+
let result = null;
|
|
222
|
+
for (const stmt of node.body) {
|
|
223
|
+
try {
|
|
224
|
+
result = await this.evaluate(stmt, env);
|
|
225
|
+
} catch (e) {
|
|
226
|
+
if (e instanceof ReturnValue || e instanceof BreakSignal || e instanceof ContinueSignal) throw e;
|
|
227
|
+
throw e;
|
|
193
228
|
}
|
|
194
|
-
return result;
|
|
195
229
|
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async evalVarDeclaration(node, env) {
|
|
234
|
+
const val = await this.evaluate(node.expr, env);
|
|
235
|
+
return env.define(node.id, val);
|
|
236
|
+
}
|
|
196
237
|
|
|
197
|
-
evalVarDeclaration(node, env) {
|
|
198
|
-
const val = this.evaluate(node.expr, env);
|
|
199
|
-
return env.define(node.id, val);
|
|
200
|
-
}
|
|
201
238
|
evalArrowFunction(node, env) {
|
|
202
239
|
return {
|
|
203
240
|
params: node.params,
|
|
204
241
|
body: node.body,
|
|
205
242
|
env: env,
|
|
206
|
-
arrow: true
|
|
243
|
+
arrow: true,
|
|
244
|
+
async: node.async || false
|
|
207
245
|
};
|
|
208
246
|
}
|
|
209
247
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (left.type === 'Identifier') return env.set(left.name, rightVal);
|
|
215
|
-
if (left.type === 'MemberExpression') {
|
|
216
|
-
const obj = this.evaluate(left.object, env);
|
|
217
|
-
obj[left.property] = rightVal;
|
|
218
|
-
return rightVal;
|
|
219
|
-
}
|
|
220
|
-
if (left.type === 'IndexExpression') {
|
|
221
|
-
const obj = this.evaluate(left.object, env);
|
|
222
|
-
const idx = this.evaluate(left.indexer, env);
|
|
223
|
-
obj[idx] = rightVal;
|
|
224
|
-
return rightVal;
|
|
225
|
-
}
|
|
248
|
+
async evalAssignment(node, env) {
|
|
249
|
+
const rightVal = await this.evaluate(node.right, env);
|
|
250
|
+
const left = node.left;
|
|
226
251
|
|
|
227
|
-
|
|
252
|
+
if (left.type === 'Identifier') return env.set(left.name, rightVal);
|
|
253
|
+
if (left.type === 'MemberExpression') {
|
|
254
|
+
const obj = await this.evaluate(left.object, env);
|
|
255
|
+
obj[left.property] = rightVal;
|
|
256
|
+
return rightVal;
|
|
228
257
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (left.type === 'Identifier') current = env.get(left.name);
|
|
235
|
-
else if (left.type === 'MemberExpression') current = this.evalMember(left, env);
|
|
236
|
-
else if (left.type === 'IndexExpression') current = this.evalIndex(left, env);
|
|
237
|
-
else throw new Error('Invalid compound assignment target');
|
|
238
|
-
|
|
239
|
-
const rhs = this.evaluate(node.right, env);
|
|
240
|
-
let computed;
|
|
241
|
-
switch (node.operator) {
|
|
242
|
-
case 'PLUSEQ': computed = current + rhs; break;
|
|
243
|
-
case 'MINUSEQ': computed = current - rhs; break;
|
|
244
|
-
case 'STAREQ': computed = current * rhs; break;
|
|
245
|
-
case 'SLASHEQ': computed = current / rhs; break;
|
|
246
|
-
case 'MODEQ': computed = current % rhs; break;
|
|
247
|
-
default: throw new Error('Unknown compound operator');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (left.type === 'Identifier') env.set(left.name, computed);
|
|
251
|
-
else if (left.type === 'MemberExpression') this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
252
|
-
else this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
253
|
-
|
|
254
|
-
return computed;
|
|
258
|
+
if (left.type === 'IndexExpression') {
|
|
259
|
+
const obj = await this.evaluate(left.object, env);
|
|
260
|
+
const idx = await this.evaluate(left.indexer, env);
|
|
261
|
+
obj[idx] = rightVal;
|
|
262
|
+
return rightVal;
|
|
255
263
|
}
|
|
256
264
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
console.log(val);
|
|
260
|
-
return val;
|
|
261
|
-
}
|
|
265
|
+
throw new Error('Invalid assignment target');
|
|
266
|
+
}
|
|
262
267
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
268
|
+
async evalCompoundAssignment(node, env) {
|
|
269
|
+
const left = node.left;
|
|
270
|
+
let current;
|
|
271
|
+
|
|
272
|
+
if (left.type === 'Identifier') current = env.get(left.name);
|
|
273
|
+
else if (left.type === 'MemberExpression') current = await this.evalMember(left, env);
|
|
274
|
+
else if (left.type === 'IndexExpression') current = await this.evalIndex(left, env);
|
|
275
|
+
else throw new Error('Invalid compound assignment target');
|
|
276
|
+
|
|
277
|
+
const rhs = await this.evaluate(node.right, env);
|
|
278
|
+
let computed;
|
|
279
|
+
switch (node.operator) {
|
|
280
|
+
case 'PLUSEQ': computed = current + rhs; break;
|
|
281
|
+
case 'MINUSEQ': computed = current - rhs; break;
|
|
282
|
+
case 'STAREQ': computed = current * rhs; break;
|
|
283
|
+
case 'SLASHEQ': computed = current / rhs; break;
|
|
284
|
+
case 'MODEQ': computed = current % rhs; break;
|
|
285
|
+
default: throw new Error('Unknown compound operator');
|
|
267
286
|
}
|
|
268
287
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
288
|
+
if (left.type === 'Identifier') env.set(left.name, computed);
|
|
289
|
+
else if (left.type === 'MemberExpression') await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
290
|
+
else await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
273
291
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const r = this.evaluate(node.right, env);
|
|
292
|
+
return computed;
|
|
293
|
+
}
|
|
277
294
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
295
|
+
async evalSldeploy(node, env) {
|
|
296
|
+
const val = await this.evaluate(node.expr, env);
|
|
297
|
+
console.log(val);
|
|
298
|
+
return val;
|
|
299
|
+
}
|
|
281
300
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
case 'LTE': return l <= r;
|
|
292
|
-
case 'GT': return l > r;
|
|
293
|
-
case 'GTE': return l >= r;
|
|
294
|
-
default: throw new Error(`Unknown binary operator ${node.operator}`);
|
|
295
|
-
}
|
|
301
|
+
async evalAsk(node, env) {
|
|
302
|
+
const prompt = await this.evaluate(node.prompt, env);
|
|
303
|
+
const input = readlineSync.question(prompt + ' ');
|
|
304
|
+
return input;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async evalDefine(node, env) {
|
|
308
|
+
const val = node.expr ? await this.evaluate(node.expr, env) : null;
|
|
309
|
+
return this.global.define(node.id, val);
|
|
296
310
|
}
|
|
297
311
|
|
|
312
|
+
async evalBinary(node, env) {
|
|
313
|
+
const l = await this.evaluate(node.left, env);
|
|
314
|
+
const r = await this.evaluate(node.right, env);
|
|
298
315
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (node.operator === 'AND') return l && this.evaluate(node.right, env);
|
|
302
|
-
if (node.operator === 'OR') return l || this.evaluate(node.right, env);
|
|
303
|
-
throw new Error(`Unknown logical operator ${node.operator}`);
|
|
316
|
+
if (node.operator === 'SLASH' && r === 0) {
|
|
317
|
+
throw new Error('Division by zero');
|
|
304
318
|
}
|
|
305
319
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
320
|
+
switch (node.operator) {
|
|
321
|
+
case 'PLUS': return l + r;
|
|
322
|
+
case 'MINUS': return l - r;
|
|
323
|
+
case 'STAR': return l * r;
|
|
324
|
+
case 'SLASH': return l / r;
|
|
325
|
+
case 'MOD': return l % r;
|
|
326
|
+
case 'EQEQ': return l === r;
|
|
327
|
+
case 'NOTEQ': return l !== r;
|
|
328
|
+
case 'LT': return l < r;
|
|
329
|
+
case 'LTE': return l <= r;
|
|
330
|
+
case 'GT': return l > r;
|
|
331
|
+
case 'GTE': return l >= r;
|
|
332
|
+
default: throw new Error(`Unknown binary operator ${node.operator}`);
|
|
314
333
|
}
|
|
334
|
+
}
|
|
315
335
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
336
|
+
async evalLogical(node, env) {
|
|
337
|
+
const l = await this.evaluate(node.left, env);
|
|
338
|
+
if (node.operator === 'AND') return l && await this.evaluate(node.right, env);
|
|
339
|
+
if (node.operator === 'OR') return l || await this.evaluate(node.right, env);
|
|
340
|
+
throw new Error(`Unknown logical operator ${node.operator}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async evalUnary(node, env) {
|
|
344
|
+
const val = await this.evaluate(node.argument, env);
|
|
345
|
+
switch (node.operator) {
|
|
346
|
+
case 'NOT': return !val;
|
|
347
|
+
case 'MINUS': return -val;
|
|
348
|
+
case 'PLUS': return +val;
|
|
349
|
+
default: throw new Error(`Unknown unary operator ${node.operator}`);
|
|
321
350
|
}
|
|
351
|
+
}
|
|
322
352
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
353
|
+
async evalIf(node, env) {
|
|
354
|
+
const test = await this.evaluate(node.test, env);
|
|
355
|
+
if (test) return await this.evaluate(node.consequent, env);
|
|
356
|
+
if (node.alternate) return await this.evaluate(node.alternate, env);
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async evalWhile(node, env) {
|
|
361
|
+
while (await this.evaluate(node.test, env)) {
|
|
362
|
+
try { await this.evaluate(node.body, env); }
|
|
363
|
+
catch (e) {
|
|
364
|
+
if (e instanceof BreakSignal) break;
|
|
365
|
+
if (e instanceof ContinueSignal) continue;
|
|
366
|
+
throw e;
|
|
331
367
|
}
|
|
332
|
-
return null;
|
|
333
368
|
}
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
334
371
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
372
|
+
async evalFor(node, env) {
|
|
373
|
+
const local = new Environment(env);
|
|
374
|
+
if (node.init) await this.evaluate(node.init, local);
|
|
375
|
+
while (!node.test || await this.evaluate(node.test, local)) {
|
|
376
|
+
try { await this.evaluate(node.body, local); }
|
|
377
|
+
catch (e) {
|
|
378
|
+
if (e instanceof BreakSignal) break;
|
|
379
|
+
if (e instanceof ContinueSignal) {
|
|
380
|
+
if (node.update) await this.evaluate(node.update, local);
|
|
381
|
+
continue;
|
|
344
382
|
}
|
|
345
|
-
|
|
383
|
+
throw e;
|
|
346
384
|
}
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
evalFunctionDeclaration(node, env) {
|
|
351
|
-
const fn = { params: node.params, body: node.body, env };
|
|
352
|
-
env.define(node.name, fn);
|
|
353
|
-
return null;
|
|
385
|
+
if (node.update) await this.evaluate(node.update, local);
|
|
354
386
|
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
355
389
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return calleeEvaluated(...args);
|
|
361
|
-
}
|
|
362
|
-
if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) {
|
|
363
|
-
throw new Error('Call to non-function');
|
|
364
|
-
}
|
|
365
|
-
const fn = calleeEvaluated;
|
|
366
|
-
const callEnv = new Environment(fn.env);
|
|
367
|
-
fn.params.forEach((p, i) => {
|
|
368
|
-
const argVal = node.arguments[i] ? this.evaluate(node.arguments[i], env) : null;
|
|
369
|
-
callEnv.define(p, argVal);
|
|
370
|
-
});
|
|
371
|
-
try {
|
|
372
|
-
const result = this.evaluate(fn.body, callEnv);
|
|
373
|
-
return fn.arrow ? result : result;
|
|
374
|
-
} catch (e) {
|
|
375
|
-
if (e instanceof ReturnValue) return e.value;
|
|
376
|
-
throw e;
|
|
390
|
+
evalFunctionDeclaration(node, env) {
|
|
391
|
+
const fn = { params: node.params, body: node.body, env, async: node.async || false };
|
|
392
|
+
env.define(node.name, fn);
|
|
393
|
+
return null;
|
|
377
394
|
}
|
|
378
395
|
|
|
396
|
+
async evalCall(node, env) {
|
|
397
|
+
const calleeEvaluated = await this.evaluate(node.callee, env);
|
|
398
|
+
if (typeof calleeEvaluated === 'function') {
|
|
399
|
+
const args = [];
|
|
400
|
+
for (const a of node.arguments) args.push(await this.evaluate(a, env));
|
|
401
|
+
return calleeEvaluated(...args);
|
|
402
|
+
}
|
|
403
|
+
if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) {
|
|
404
|
+
throw new Error('Call to non-function');
|
|
379
405
|
}
|
|
380
406
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const idx = this.evaluate(node.indexer, env);
|
|
407
|
+
const fn = calleeEvaluated;
|
|
408
|
+
const callEnv = new Environment(fn.env);
|
|
384
409
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (typeof obj === 'object' && !(idx in obj)) {
|
|
390
|
-
throw new Error(`Property '${idx}' does not exist`);
|
|
391
|
-
}
|
|
410
|
+
for (let i = 0; i < fn.params.length; i++) {
|
|
411
|
+
const argVal = node.arguments[i] ? await this.evaluate(node.arguments[i], env) : null;
|
|
412
|
+
callEnv.define(fn.params[i], argVal);
|
|
413
|
+
}
|
|
392
414
|
|
|
393
|
-
|
|
415
|
+
try {
|
|
416
|
+
const result = await this.evaluate(fn.body, callEnv);
|
|
417
|
+
return fn.arrow ? result : result;
|
|
418
|
+
} catch (e) {
|
|
419
|
+
if (e instanceof ReturnValue) return e.value;
|
|
420
|
+
throw e;
|
|
421
|
+
}
|
|
394
422
|
}
|
|
395
423
|
|
|
424
|
+
async evalIndex(node, env) {
|
|
425
|
+
const obj = await this.evaluate(node.object, env);
|
|
426
|
+
const idx = await this.evaluate(node.indexer, env);
|
|
396
427
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
428
|
+
if (obj == null) throw new Error('Indexing null or undefined');
|
|
429
|
+
if (Array.isArray(obj) && (idx < 0 || idx >= obj.length)) {
|
|
430
|
+
throw new Error('Array index out of bounds');
|
|
431
|
+
}
|
|
432
|
+
if (typeof obj === 'object' && !(idx in obj)) {
|
|
433
|
+
throw new Error(`Property '${idx}' does not exist`);
|
|
401
434
|
}
|
|
402
435
|
|
|
403
|
-
|
|
404
|
-
const obj = this.evaluate(node.object, env);
|
|
405
|
-
if (obj == null) throw new Error('Member access of null or undefined');
|
|
406
|
-
if (!(node.property in obj)) throw new Error(`Property '${node.property}' does not exist`);
|
|
407
|
-
return obj[node.property];
|
|
436
|
+
return obj[idx];
|
|
408
437
|
}
|
|
409
438
|
|
|
439
|
+
async evalObject(node, env) {
|
|
440
|
+
const out = {};
|
|
441
|
+
for (const p of node.props) out[p.key] = await this.evaluate(p.value, env);
|
|
442
|
+
return out;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async evalMember(node, env) {
|
|
446
|
+
const obj = await this.evaluate(node.object, env);
|
|
447
|
+
if (obj == null) throw new Error('Member access of null or undefined');
|
|
448
|
+
if (!(node.property in obj)) throw new Error(`Property '${node.property}' does not exist`);
|
|
449
|
+
return obj[node.property];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async evalUpdate(node, env) {
|
|
453
|
+
const arg = node.argument;
|
|
454
|
+
const getCurrent = async () => {
|
|
455
|
+
if (arg.type === 'Identifier') return env.get(arg.name);
|
|
456
|
+
if (arg.type === 'MemberExpression') return await this.evalMember(arg, env);
|
|
457
|
+
if (arg.type === 'IndexExpression') return await this.evalIndex(arg, env);
|
|
458
|
+
throw new Error('Invalid update target');
|
|
459
|
+
};
|
|
460
|
+
const setValue = async (v) => {
|
|
461
|
+
if (arg.type === 'Identifier') env.set(arg.name, v);
|
|
462
|
+
else if (arg.type === 'MemberExpression') {
|
|
463
|
+
const obj = await this.evaluate(arg.object, env);
|
|
464
|
+
obj[arg.property] = v;
|
|
465
|
+
} else if (arg.type === 'IndexExpression') {
|
|
466
|
+
const obj = await this.evaluate(arg.object, env);
|
|
467
|
+
const idx = await this.evaluate(arg.indexer, env);
|
|
468
|
+
obj[idx] = v;
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
const current = await getCurrent();
|
|
473
|
+
const newVal = (node.operator === 'PLUSPLUS') ? current + 1 : current - 1;
|
|
474
|
+
|
|
475
|
+
if (node.prefix) { await setValue(newVal); return newVal; }
|
|
476
|
+
else { await setValue(newVal); return current; }
|
|
477
|
+
}
|
|
410
478
|
|
|
411
|
-
evalUpdate(node, env) {
|
|
412
|
-
const arg = node.argument;
|
|
413
|
-
const getCurrent = () => {
|
|
414
|
-
if (arg.type === 'Identifier') return env.get(arg.name);
|
|
415
|
-
if (arg.type === 'MemberExpression') return this.evalMember(arg, env);
|
|
416
|
-
if (arg.type === 'IndexExpression') return this.evalIndex(arg, env);
|
|
417
|
-
throw new Error('Invalid update target');
|
|
418
|
-
};
|
|
419
|
-
const setValue = (v) => {
|
|
420
|
-
if (arg.type === 'Identifier') env.set(arg.name, v);
|
|
421
|
-
else if (arg.type === 'MemberExpression') {
|
|
422
|
-
const obj = this.evaluate(arg.object, env);
|
|
423
|
-
obj[arg.property] = v;
|
|
424
|
-
}
|
|
425
|
-
else if (arg.type === 'IndexExpression') {
|
|
426
|
-
const obj = this.evaluate(arg.object, env);
|
|
427
|
-
const idx = this.evaluate(arg.indexer, env);
|
|
428
|
-
obj[idx] = v;
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
const current = getCurrent();
|
|
432
|
-
const newVal = (node.operator === 'PLUSPLUS') ? current + 1 : current - 1;
|
|
433
|
-
if (node.prefix) { setValue(newVal); return newVal; }
|
|
434
|
-
else { setValue(newVal); return current; }
|
|
435
|
-
}
|
|
436
479
|
}
|
|
437
480
|
|
|
438
|
-
module.exports = Evaluator;
|
|
481
|
+
module.exports = Evaluator;
|
package/src/lexer.js
CHANGED
|
@@ -79,10 +79,13 @@ class Lexer {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const keywords = [
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
'let', 'sldeploy', 'if', 'else', 'while', 'for',
|
|
83
|
+
'break', 'continue', 'func', 'return',
|
|
84
|
+
'true', 'false', 'null',
|
|
85
|
+
'ask', 'define', 'import', 'from', 'as',
|
|
86
|
+
'async', 'await'
|
|
87
|
+
];
|
|
88
|
+
|
|
86
89
|
|
|
87
90
|
if (keywords.includes(result)) {
|
|
88
91
|
return { type: result.toUpperCase(), value: result, line: startLine, column: startCol };
|