Skip to content

Commit d0a876d

Browse files
committed
Fixed all tests from v1.
1 parent 07389b4 commit d0a876d

File tree

4 files changed

+57
-46
lines changed

4 files changed

+57
-46
lines changed

index.html

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
<h4>JSPython development console</h4>
3131
<div id="editor">
3232

33-
sqrPromise(5) * 2
34-
33+
def f(n):
34+
n * n
35+
x = [2, 3, 4]
36+
x.map(f).join("|")
3537

3638
</div>
3739
<!--
@@ -119,11 +121,11 @@ <h4>JSPython development console</h4>
119121
// const result = await jsPython()
120122
// .evaluate(scripts, scope);
121123

122-
// const result = jsPython()
123-
// .eval(scripts, scope);
124+
const result = jsPython()
125+
.eval(scripts, scope);
124126

125-
const result = await jsPython()
126-
.evalAsync(scripts, scope);
127+
// const result = await jsPython()
128+
// .evalAsync(scripts, scope);
127129

128130
const data = typeof result === 'object' ? JSON.stringify(result, null, '\t') : String(result);
129131
resultEditor.getSession().setValue(data)

src/evaluator/evaluator.ts

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import {
22
ArrowFuncDefNode,
33
AssignNode, AstBlock, AstNode, BinOpNode, BracketObjectAccessNode, ConstNode, CreateArrayNode,
4-
CreateObjectNode, DotObjectAccessNode, ForNode, FuncDefNode, FunctionCallNode, FunctionDefNode, GetSingleVarNode, IfNode, OperationFuncs, Primitive, ReturnNode, SetSingleVarNode, WhileNode
4+
CreateObjectNode, DotObjectAccessNode, ForNode, FuncDefNode, FunctionCallNode, FunctionDefNode, GetSingleVarNode,
5+
IfNode, OperationFuncs, Primitive, ReturnNode, SetSingleVarNode, WhileNode
56
} from '../common';
67
import { BlockContext, Scope } from './scope';
78

89
export class Evaluator {
910

10-
evalBlock(ast: AstBlock, blockContext: BlockContext): unknown {
11+
static evalBlock(ast: AstBlock, blockContext: BlockContext): unknown {
1112
let lastResult = null;
1213

1314
for (let node of ast?.funcs || []) {
@@ -17,13 +18,13 @@ export class Evaluator {
1718
const newScope = blockContext.blockScope;
1819

1920
newScope.set(funcDef.funcAst.name,
20-
(...args: unknown[]): unknown => this.jspyFuncInvoker(funcDef, blockContext, ...args)
21+
(...args: unknown[]): unknown => Evaluator.jspyFuncInvoker(funcDef, blockContext, ...args)
2122
);
2223
}
2324

2425
for (const node of ast.body) {
2526
if (node.type === 'comment') { continue; }
26-
lastResult = this.evalNode(node, blockContext);
27+
lastResult = Evaluator.evalNode(node, blockContext);
2728

2829
if (blockContext.returnCalled) {
2930
const res = blockContext.returnObject;
@@ -47,7 +48,7 @@ export class Evaluator {
4748
return lastResult;
4849
}
4950

50-
private jspyFuncInvoker(funcDef: FuncDefNode, context: BlockContext, ...args: unknown[]): unknown {
51+
static jspyFuncInvoker(funcDef: FuncDefNode, context: BlockContext, ...args: unknown[]): unknown {
5152

5253
const ast = Object.assign({}, funcDef.funcAst);
5354
ast.type = 'func';
@@ -66,10 +67,10 @@ export class Evaluator {
6667
blockContext.blockScope.set(funcDef.params[i], args[i]);
6768
}
6869

69-
return this.evalBlock(ast, blockContext);
70+
return Evaluator.evalBlock(ast, blockContext);
7071
}
7172

72-
private invokeFunction(func: (...args: unknown[]) => unknown, fps: unknown[]): unknown {
73+
private static invokeFunction(func: (...args: unknown[]) => unknown, fps: unknown[]): unknown {
7374
if (fps.length === 0) { return func(); }
7475
if (fps.length === 1) { return func(fps[0]); }
7576
if (fps.length === 2) { return func(fps[0], fps[1]); }
@@ -106,7 +107,7 @@ export class Evaluator {
106107
}
107108
}
108109

109-
private evalNode(node: AstNode, blockContext: BlockContext): unknown {
110+
private static evalNode(node: AstNode, blockContext: BlockContext): unknown {
110111
if (node.type === 'import') {
111112
// skip this for now. As modules are implemented externally
112113
return null;
@@ -118,10 +119,10 @@ export class Evaluator {
118119

119120
if (node.type === 'if') {
120121
const ifNode = node as IfNode;
121-
if (this.evalNode(ifNode.conditionNode, blockContext)) {
122-
this.evalBlock({ type: 'if', body: ifNode.ifBody } as AstBlock, blockContext);
122+
if (Evaluator.evalNode(ifNode.conditionNode, blockContext)) {
123+
Evaluator.evalBlock({ type: 'if', body: ifNode.ifBody } as AstBlock, blockContext);
123124
} else if (ifNode.elseBody) {
124-
this.evalBlock({ type: 'if', body: ifNode.elseBody } as AstBlock, blockContext);
125+
Evaluator.evalBlock({ type: 'if', body: ifNode.elseBody } as AstBlock, blockContext);
125126
}
126127

127128
return;
@@ -131,7 +132,7 @@ export class Evaluator {
131132
const returnNode = node as ReturnNode;
132133
blockContext.returnCalled = true;
133134
blockContext.returnObject = returnNode.returnValue ?
134-
this.evalNode(returnNode.returnValue, blockContext)
135+
Evaluator.evalNode(returnNode.returnValue, blockContext)
135136
: null;
136137

137138
return blockContext.returnObject;
@@ -150,11 +151,11 @@ export class Evaluator {
150151
if (node.type === 'for') {
151152
const forNode = node as ForNode;
152153

153-
const array = this.evalNode(forNode.sourceArray, blockContext) as unknown[] | string;
154+
const array = Evaluator.evalNode(forNode.sourceArray, blockContext) as unknown[] | string;
154155

155156
for (let item of array) {
156157
blockContext.blockScope.set(forNode.itemVarName, item);
157-
this.evalBlock({ type:'for', body: forNode.body } as AstBlock, blockContext);
158+
Evaluator.evalBlock({ type:'for', body: forNode.body } as AstBlock, blockContext);
158159
if (blockContext.continueCalled) { blockContext.continueCalled = false; }
159160
if (blockContext.breakCalled) { break; }
160161
}
@@ -165,8 +166,8 @@ export class Evaluator {
165166
if (node.type === 'while') {
166167
const whileNode = node as WhileNode;
167168

168-
while (this.evalNode(whileNode.condition, blockContext)) {
169-
this.evalBlock({ type:'while', body: whileNode.body } as AstBlock, blockContext);
169+
while (Evaluator.evalNode(whileNode.condition, blockContext)) {
170+
Evaluator.evalBlock({ type:'while', body: whileNode.body } as AstBlock, blockContext);
170171

171172
if (blockContext.continueCalled) { blockContext.continueCalled = false; }
172173
if (blockContext.breakCalled) { break; }
@@ -186,50 +187,50 @@ export class Evaluator {
186187

187188
if (node.type === "binOp") {
188189
const binOpNode = (node as BinOpNode);
189-
var left = this.evalNode(binOpNode.left, blockContext);
190-
var right = this.evalNode(binOpNode.right, blockContext);
190+
var left = Evaluator.evalNode(binOpNode.left, blockContext);
191+
var right = Evaluator.evalNode(binOpNode.right, blockContext);
191192
return OperationFuncs[binOpNode.op](left as Primitive, right as Primitive);
192193
}
193194

194195
if (node.type === "arrowFuncDef") {
195196
const arrowFuncDef = node as ArrowFuncDefNode;
196197

197-
return (...args: unknown[]): unknown => this.jspyFuncInvoker(arrowFuncDef, blockContext, ...args);
198+
return (...args: unknown[]): unknown => Evaluator.jspyFuncInvoker(arrowFuncDef, blockContext, ...args);
198199
}
199200

200201
if (node.type === "funcCall") {
201202
const funcCallNode = node as FunctionCallNode;
202203
const func = blockContext.blockScope.get(funcCallNode.name) as (...args: unknown[]) => unknown;
203-
const pms = funcCallNode.paramNodes?.map(n => this.evalNode(n, blockContext)) || []
204+
const pms = funcCallNode.paramNodes?.map(n => Evaluator.evalNode(n, blockContext)) || []
204205

205-
return this.invokeFunction(func, pms);
206+
return Evaluator.invokeFunction(func, pms);
206207
}
207208

208209
if (node.type === "assign") {
209210
const assignNode = node as AssignNode;
210211

211212
if (assignNode.target.type === 'getSingleVar') {
212213
const node = assignNode.target as SetSingleVarNode;
213-
blockContext.blockScope.set(node.name, this.evalNode(assignNode.source, blockContext));
214+
blockContext.blockScope.set(node.name, Evaluator.evalNode(assignNode.source, blockContext));
214215
} else if (assignNode.target.type === 'dotObjectAccess') {
215216
const targetNode = assignNode.target as DotObjectAccessNode;
216217

217218
// create a node for all but last property token
218219
// potentially it can go to parser
219220
const targetObjectNode = new DotObjectAccessNode(targetNode.nestedProps.slice(0, targetNode.nestedProps.length - 1));
220-
const targetObject = this.evalNode(targetObjectNode, blockContext) as Record<string, unknown>;
221+
const targetObject = Evaluator.evalNode(targetObjectNode, blockContext) as Record<string, unknown>;
221222

222223
// not sure nested properties should be GetSingleVarNode
223224
// can be factored in the parser
224225
const lastPropertyName = (targetNode.nestedProps[targetNode.nestedProps.length - 1] as GetSingleVarNode).name
225226

226-
targetObject[lastPropertyName] = this.evalNode(assignNode.source, blockContext);
227+
targetObject[lastPropertyName] = Evaluator.evalNode(assignNode.source, blockContext);
227228
} else if (assignNode.target.type === 'bracketObjectAccess') {
228229
const targetNode = assignNode.target as BracketObjectAccessNode;
229-
const keyValue = this.evalNode(targetNode.bracketBody, blockContext) as string | number;
230+
const keyValue = Evaluator.evalNode(targetNode.bracketBody, blockContext) as string | number;
230231
const targetObject = blockContext.blockScope.get(targetNode.propertyName as string) as Record<string, unknown>;
231232

232-
targetObject[keyValue] = this.evalNode(assignNode.source, blockContext);
233+
targetObject[keyValue] = Evaluator.evalNode(assignNode.source, blockContext);
233234
} else {
234235
throw Error('Not implemented Assign operation');
235236
// get chaining calls
@@ -240,15 +241,15 @@ export class Evaluator {
240241

241242
if (node.type === 'bracketObjectAccess') {
242243
const sbNode = node as BracketObjectAccessNode;
243-
const key = this.evalNode(sbNode.bracketBody, blockContext) as string;
244+
const key = Evaluator.evalNode(sbNode.bracketBody, blockContext) as string;
244245
const obj = blockContext.blockScope.get(sbNode.propertyName as string) as Record<string, unknown>;
245246
return (obj[key] === undefined) ? null : obj[key];
246247
}
247248

248249
if (node.type === "dotObjectAccess") {
249250
const dotObject = node as DotObjectAccessNode;
250251

251-
let startObject = this.evalNode(dotObject.nestedProps[0], blockContext) as any;
252+
let startObject = Evaluator.evalNode(dotObject.nestedProps[0], blockContext) as any;
252253
for (let i = 1; i < dotObject.nestedProps.length; i++) {
253254
const nestedProp = dotObject.nestedProps[i];
254255

@@ -261,13 +262,13 @@ export class Evaluator {
261262
} else if (nestedProp.type === 'bracketObjectAccess') {
262263
const node = nestedProp as BracketObjectAccessNode;
263264
startObject = startObject[node.propertyName] as unknown;
264-
startObject = startObject[this.evalNode(node.bracketBody, blockContext) as string] as unknown;
265+
startObject = startObject[Evaluator.evalNode(node.bracketBody, blockContext) as string] as unknown;
265266
} else if (nestedProp.type === 'funcCall') {
266267
const funcCallNode = nestedProp as FunctionCallNode;
267268
const func = startObject[funcCallNode.name] as (...args: unknown[]) => unknown;
268-
const pms = funcCallNode.paramNodes?.map(n => this.evalNode(n, blockContext)) || []
269+
const pms = funcCallNode.paramNodes?.map(n => Evaluator.evalNode(n, blockContext)) || []
269270

270-
startObject = this.invokeFunction(func.bind(startObject), pms);
271+
startObject = Evaluator.invokeFunction(func.bind(startObject), pms);
271272

272273
} else {
273274
throw Error("Can't resolve dotObjectAccess node")
@@ -283,7 +284,7 @@ export class Evaluator {
283284
const obj = {} as Record<string, unknown>;
284285

285286
for (const p of createObjectNode.props) {
286-
obj[this.evalNode(p.name, blockContext) as string] = this.evalNode(p.value, blockContext);
287+
obj[Evaluator.evalNode(p.name, blockContext) as string] = Evaluator.evalNode(p.value, blockContext);
287288
}
288289

289290
return obj;
@@ -294,7 +295,7 @@ export class Evaluator {
294295
const res = [] as unknown[];
295296

296297
for (const item of arrayNode.items) {
297-
res.push(this.evalNode(item, blockContext));
298+
res.push(Evaluator.evalNode(item, blockContext));
298299
}
299300

300301
return res;

src/evaluator/evaluatorAsync.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import {
22
ArrowFuncDefNode,
33
AssignNode, AstBlock, AstNode, BinOpNode, BracketObjectAccessNode, ConstNode, CreateArrayNode,
4-
CreateObjectNode, DotObjectAccessNode, ForNode, FuncDefNode, FunctionCallNode, FunctionDefNode, GetSingleVarNode, IfNode, OperationFuncs, Primitive, ReturnNode, SetSingleVarNode, WhileNode
4+
CreateObjectNode, DotObjectAccessNode, ForNode, FuncDefNode, FunctionCallNode, FunctionDefNode, GetSingleVarNode,
5+
IfNode, OperationFuncs, Primitive, ReturnNode, SetSingleVarNode, WhileNode
56
} from '../common';
7+
import { Evaluator } from './evaluator';
68
import { BlockContext, Scope } from './scope';
79

10+
/**
11+
* This is copy/paste from Evaluator.
12+
* Sadly, we have to copy code around to support both async and non async methods.
13+
* So, any changes to this method, should be replicated in the evaluator.ts
14+
*/
815
export class EvaluatorAsync {
916

1017
async evalBlockAsync(ast: AstBlock, blockContext: BlockContext): Promise<unknown> {
@@ -16,9 +23,11 @@ export class EvaluatorAsync {
1623
// a child scope needs to be created here
1724
const newScope = blockContext.blockScope;
1825

19-
newScope.set(funcDef.funcAst.name,
26+
const invoker = (funcDef.isAsync) ?
2027
async (...args: unknown[]): Promise<unknown> => await this.jspyFuncInvokerAsync(funcDef, blockContext, ...args)
21-
);
28+
: (...args: unknown[]): unknown => Evaluator.jspyFuncInvoker(funcDef, blockContext, ...args);
29+
30+
newScope.set(funcDef.funcAst.name, invoker);
2231
}
2332

2433
for (const node of ast.body) {
@@ -193,7 +202,7 @@ export class EvaluatorAsync {
193202
if (node.type === "arrowFuncDef") {
194203
const arrowFuncDef = node as ArrowFuncDefNode;
195204

196-
return (...args: unknown[]): unknown => this.jspyFuncInvokerAsync(arrowFuncDef, blockContext, ...args);
205+
return (...args: unknown[]): unknown => Evaluator.jspyFuncInvoker(arrowFuncDef, blockContext, ...args);
197206
}
198207

199208
if (node.type === "funcCall") {
@@ -272,7 +281,7 @@ export class EvaluatorAsync {
272281
for (let p of funcCallNode.paramNodes || []) {
273282
pms.push(await this.evalNodeAsync(p, blockContext));
274283
}
275-
284+
276285
startObject = await this.invokeFunctionAsync(func.bind(startObject), pms);
277286

278287
} else {

src/interpreter.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,12 @@ export class Interpreter {
3939
eval(codeOrAst: string | AstBlock, scope: Record<string, unknown> = {}
4040
, entryFunctionName: string = ''): unknown {
4141
const ast = (typeof codeOrAst === 'string') ? this.parse(codeOrAst as string) : codeOrAst as AstBlock;
42-
const evaluator = new Evaluator();
4342

4443
const blockContext = {
4544
blockScope: new Scope(scope)
4645
} as BlockContext;
4746

48-
const result = evaluator.evalBlock(ast, blockContext);
47+
const result = Evaluator.evalBlock(ast, blockContext);
4948
if (!entryFunctionName || !entryFunctionName.length) {
5049
return result;
5150
} else {

0 commit comments

Comments
 (0)