Skip to content

Commit 2bb0a29

Browse files
committed
error reporting improvements
1 parent 4efaabe commit 2bb0a29

File tree

7 files changed

+175
-171
lines changed

7 files changed

+175
-171
lines changed

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ <h4>JSPython development console</h4>
119119
}
120120
};
121121
const result = await jsPython()
122-
.evaluate(scripts, scope);
122+
.evaluate(scripts, scope, undefined, 'index1.jspy');
123123

124124
// const result = jsPython()
125125
// .eval(scripts, scope);

src/common/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Token } from "./token-types";
2+
13
export function parseDatetimeOrNull(value: string | Date): Date | null {
24
if (!value) { return null; }
35
if (value instanceof Date && !isNaN(value.valueOf())) { return value; }
@@ -92,6 +94,6 @@ export class JspyEvalError extends Error {
9294
constructor(public module: string, public line: number, public column: number, public message: string) {
9395
super();
9496
this.message = jspyErrorMessage("JspyEvalError", module, line, column, message);
95-
Object.setPrototypeOf(this, JspyParserError.prototype);
97+
Object.setPrototypeOf(this, JspyEvalError.prototype);
9698
}
9799
}

src/evaluator/evaluator.ts

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { BlockContext, Scope } from './scope';
99

1010
export class Evaluator {
1111

12-
static evalBlock(ast: AstBlock, blockContext: BlockContext): unknown {
12+
evalBlock(ast: AstBlock, blockContext: BlockContext): unknown {
1313
let lastResult = null;
1414

1515
for (let node of ast?.funcs || []) {
@@ -19,14 +19,14 @@ export class Evaluator {
1919
const newScope = blockContext.blockScope;
2020

2121
newScope.set(funcDef.funcAst.name,
22-
(...args: unknown[]): unknown => Evaluator.jspyFuncInvoker(funcDef, blockContext, ...args)
22+
(...args: unknown[]): unknown => this.jspyFuncInvoker(funcDef, blockContext, ...args)
2323
);
2424
}
2525

2626
for (const node of ast.body) {
2727
if (node.type === 'comment') { continue; }
2828
try {
29-
lastResult = Evaluator.evalNode(node, blockContext);
29+
lastResult = this.evalNode(node, blockContext);
3030

3131
if (blockContext.returnCalled) {
3232
const res = blockContext.returnObject;
@@ -50,7 +50,7 @@ export class Evaluator {
5050
throw err;
5151
} else {
5252
const loc = node.loc ? node.loc : [0, 0]
53-
throw new JspyEvalError('mainModule', loc[0], loc[1], err.message)
53+
throw new JspyEvalError(ast.name, loc[0], loc[1], err.message)
5454
}
5555
}
5656

@@ -59,13 +59,13 @@ export class Evaluator {
5959
return lastResult;
6060
}
6161

62-
static jspyFuncInvoker(funcDef: FuncDefNode, context: BlockContext, ...args: unknown[]): unknown {
62+
jspyFuncInvoker(funcDef: FuncDefNode, context: BlockContext, ...args: unknown[]): unknown {
6363

6464
const ast = Object.assign({}, funcDef.funcAst);
6565
ast.type = 'func';
6666

6767
const blockContext = {
68-
namelessFuncsCount: 0,
68+
moduleName: context.moduleName,
6969
blockScope: context.blockScope.clone()
7070
} as BlockContext;
7171

@@ -78,10 +78,10 @@ export class Evaluator {
7878
blockContext.blockScope.set(funcDef.params[i], args[i]);
7979
}
8080

81-
return Evaluator.evalBlock(ast, blockContext);
81+
return this.evalBlock(ast, blockContext);
8282
}
8383

84-
private static invokeFunction(func: (...args: unknown[]) => unknown, fps: unknown[]): unknown {
84+
private invokeFunction(func: (...args: unknown[]) => unknown, fps: unknown[]): unknown {
8585
if (fps.length === 0) { return func(); }
8686
if (fps.length === 1) { return func(fps[0]); }
8787
if (fps.length === 2) { return func(fps[0], fps[1]); }
@@ -118,7 +118,7 @@ export class Evaluator {
118118
}
119119
}
120120

121-
private static evalNode(node: AstNode, blockContext: BlockContext): unknown {
121+
private evalNode(node: AstNode, blockContext: BlockContext): unknown {
122122
if (node.type === 'import') {
123123
// skip this for now. As modules are implemented externally
124124
return null;
@@ -130,10 +130,10 @@ export class Evaluator {
130130

131131
if (node.type === 'if') {
132132
const ifNode = node as IfNode;
133-
if (Evaluator.evalNode(ifNode.conditionNode, blockContext)) {
134-
Evaluator.evalBlock({ type: 'if', body: ifNode.ifBody } as AstBlock, blockContext);
133+
if (this.evalNode(ifNode.conditionNode, blockContext)) {
134+
this.evalBlock({ type: 'if', body: ifNode.ifBody } as AstBlock, blockContext);
135135
} else if (ifNode.elseBody) {
136-
Evaluator.evalBlock({ type: 'if', body: ifNode.elseBody } as AstBlock, blockContext);
136+
this.evalBlock({ type: 'if', body: ifNode.elseBody } as AstBlock, blockContext);
137137
}
138138

139139
return;
@@ -143,7 +143,7 @@ export class Evaluator {
143143
const returnNode = node as ReturnNode;
144144
blockContext.returnCalled = true;
145145
blockContext.returnObject = returnNode.returnValue ?
146-
Evaluator.evalNode(returnNode.returnValue, blockContext)
146+
this.evalNode(returnNode.returnValue, blockContext)
147147
: null;
148148

149149
return blockContext.returnObject;
@@ -162,11 +162,11 @@ export class Evaluator {
162162
if (node.type === 'for') {
163163
const forNode = node as ForNode;
164164

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

167167
for (let item of array) {
168168
blockContext.blockScope.set(forNode.itemVarName, item);
169-
Evaluator.evalBlock({ type: 'for', body: forNode.body } as AstBlock, blockContext);
169+
this.evalBlock({ type: 'for', body: forNode.body } as AstBlock, blockContext);
170170
if (blockContext.continueCalled) { blockContext.continueCalled = false; }
171171
if (blockContext.breakCalled) { break; }
172172
}
@@ -177,8 +177,8 @@ export class Evaluator {
177177
if (node.type === 'while') {
178178
const whileNode = node as WhileNode;
179179

180-
while (Evaluator.evalNode(whileNode.condition, blockContext)) {
181-
Evaluator.evalBlock({ type: 'while', body: whileNode.body } as AstBlock, blockContext);
180+
while (this.evalNode(whileNode.condition, blockContext)) {
181+
this.evalBlock({ type: 'while', body: whileNode.body } as AstBlock, blockContext);
182182

183183
if (blockContext.continueCalled) { blockContext.continueCalled = false; }
184184
if (blockContext.breakCalled) { break; }
@@ -198,50 +198,50 @@ export class Evaluator {
198198

199199
if (node.type === "binOp") {
200200
const binOpNode = (node as BinOpNode);
201-
var left = Evaluator.evalNode(binOpNode.left, blockContext);
202-
var right = Evaluator.evalNode(binOpNode.right, blockContext);
201+
var left = this.evalNode(binOpNode.left, blockContext);
202+
var right = this.evalNode(binOpNode.right, blockContext);
203203
return OperationFuncs[binOpNode.op](left as Primitive, right as Primitive);
204204
}
205205

206206
if (node.type === "arrowFuncDef") {
207207
const arrowFuncDef = node as ArrowFuncDefNode;
208208

209-
return (...args: unknown[]): unknown => Evaluator.jspyFuncInvoker(arrowFuncDef, blockContext, ...args);
209+
return (...args: unknown[]): unknown => this.jspyFuncInvoker(arrowFuncDef, blockContext, ...args);
210210
}
211211

212212
if (node.type === "funcCall") {
213213
const funcCallNode = node as FunctionCallNode;
214214
const func = blockContext.blockScope.get(funcCallNode.name) as (...args: unknown[]) => unknown;
215-
const pms = funcCallNode.paramNodes?.map(n => Evaluator.evalNode(n, blockContext)) || []
215+
const pms = funcCallNode.paramNodes?.map(n => this.evalNode(n, blockContext)) || []
216216

217-
return Evaluator.invokeFunction(func, pms);
217+
return this.invokeFunction(func, pms);
218218
}
219219

220220
if (node.type === "assign") {
221221
const assignNode = node as AssignNode;
222222

223223
if (assignNode.target.type === 'getSingleVar') {
224224
const node = assignNode.target as SetSingleVarNode;
225-
blockContext.blockScope.set(node.name, Evaluator.evalNode(assignNode.source, blockContext));
225+
blockContext.blockScope.set(node.name, this.evalNode(assignNode.source, blockContext));
226226
} else if (assignNode.target.type === 'dotObjectAccess') {
227227
const targetNode = assignNode.target as DotObjectAccessNode;
228228

229229
// create a node for all but last property token
230230
// potentially it can go to parser
231231
const targetObjectNode = new DotObjectAccessNode(targetNode.nestedProps.slice(0, targetNode.nestedProps.length - 1), targetNode.loc);
232-
const targetObject = Evaluator.evalNode(targetObjectNode, blockContext) as Record<string, unknown>;
232+
const targetObject = this.evalNode(targetObjectNode, blockContext) as Record<string, unknown>;
233233

234234
// not sure nested properties should be GetSingleVarNode
235235
// can be factored in the parser
236236
const lastPropertyName = (targetNode.nestedProps[targetNode.nestedProps.length - 1] as GetSingleVarNode).name
237237

238-
targetObject[lastPropertyName] = Evaluator.evalNode(assignNode.source, blockContext);
238+
targetObject[lastPropertyName] = this.evalNode(assignNode.source, blockContext);
239239
} else if (assignNode.target.type === 'bracketObjectAccess') {
240240
const targetNode = assignNode.target as BracketObjectAccessNode;
241-
const keyValue = Evaluator.evalNode(targetNode.bracketBody, blockContext) as string | number;
241+
const keyValue = this.evalNode(targetNode.bracketBody, blockContext) as string | number;
242242
const targetObject = blockContext.blockScope.get(targetNode.propertyName as string) as Record<string, unknown>;
243243

244-
targetObject[keyValue] = Evaluator.evalNode(assignNode.source, blockContext);
244+
targetObject[keyValue] = this.evalNode(assignNode.source, blockContext);
245245
} else {
246246
throw Error('Not implemented Assign operation');
247247
// get chaining calls
@@ -252,15 +252,15 @@ export class Evaluator {
252252

253253
if (node.type === 'bracketObjectAccess') {
254254
const sbNode = node as BracketObjectAccessNode;
255-
const key = Evaluator.evalNode(sbNode.bracketBody, blockContext) as string;
255+
const key = this.evalNode(sbNode.bracketBody, blockContext) as string;
256256
const obj = blockContext.blockScope.get(sbNode.propertyName as string) as Record<string, unknown>;
257257
return (obj[key] === undefined) ? null : obj[key];
258258
}
259259

260260
if (node.type === "dotObjectAccess") {
261261
const dotObject = node as DotObjectAccessNode;
262262

263-
let startObject = Evaluator.evalNode(dotObject.nestedProps[0], blockContext) as any;
263+
let startObject = this.evalNode(dotObject.nestedProps[0], blockContext) as any;
264264
for (let i = 1; i < dotObject.nestedProps.length; i++) {
265265
const nestedProp = dotObject.nestedProps[i];
266266

@@ -273,13 +273,13 @@ export class Evaluator {
273273
} else if (nestedProp.type === 'bracketObjectAccess') {
274274
const node = nestedProp as BracketObjectAccessNode;
275275
startObject = startObject[node.propertyName] as unknown;
276-
startObject = startObject[Evaluator.evalNode(node.bracketBody, blockContext) as string] as unknown;
276+
startObject = startObject[this.evalNode(node.bracketBody, blockContext) as string] as unknown;
277277
} else if (nestedProp.type === 'funcCall') {
278278
const funcCallNode = nestedProp as FunctionCallNode;
279279
const func = startObject[funcCallNode.name] as (...args: unknown[]) => unknown;
280-
const pms = funcCallNode.paramNodes?.map(n => Evaluator.evalNode(n, blockContext)) || []
280+
const pms = funcCallNode.paramNodes?.map(n => this.evalNode(n, blockContext)) || []
281281

282-
startObject = Evaluator.invokeFunction(func.bind(startObject), pms);
282+
startObject = this.invokeFunction(func.bind(startObject), pms);
283283

284284
} else {
285285
throw Error("Can't resolve dotObjectAccess node")
@@ -295,7 +295,7 @@ export class Evaluator {
295295
const obj = {} as Record<string, unknown>;
296296

297297
for (const p of createObjectNode.props) {
298-
obj[Evaluator.evalNode(p.name, blockContext) as string] = Evaluator.evalNode(p.value, blockContext);
298+
obj[this.evalNode(p.name, blockContext) as string] = this.evalNode(p.value, blockContext);
299299
}
300300

301301
return obj;
@@ -306,7 +306,7 @@ export class Evaluator {
306306
const res = [] as unknown[];
307307

308308
for (const item of arrayNode.items) {
309-
res.push(Evaluator.evalNode(item, blockContext));
309+
res.push(this.evalNode(item, blockContext));
310310
}
311311

312312
return res;

src/evaluator/evaluatorAsync.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class EvaluatorAsync {
2727

2828
const invoker = (funcDef.isAsync) ?
2929
async (...args: unknown[]): Promise<unknown> => await this.jspyFuncInvokerAsync(funcDef, blockContext, ...args)
30-
: (...args: unknown[]): unknown => Evaluator.jspyFuncInvoker(funcDef, blockContext, ...args);
30+
: (...args: unknown[]): unknown => new Evaluator().jspyFuncInvoker(funcDef, blockContext, ...args);
3131

3232
newScope.set(funcDef.funcAst.name, invoker);
3333
}
@@ -58,7 +58,7 @@ export class EvaluatorAsync {
5858
throw err;
5959
} else {
6060
const loc = node.loc? node.loc : [0, 0]
61-
throw new JspyEvalError('mainModule', loc[0], loc[1], err.message)
61+
throw new JspyEvalError(blockContext.moduleName, loc[0], loc[1], err.message)
6262
}
6363
}
6464
}
@@ -72,7 +72,7 @@ export class EvaluatorAsync {
7272
ast.type = 'func';
7373

7474
const blockContext = {
75-
namelessFuncsCount: 0,
75+
moduleName: context.moduleName,
7676
blockScope: context.blockScope.clone()
7777
} as BlockContext;
7878

@@ -213,7 +213,7 @@ export class EvaluatorAsync {
213213
if (node.type === "arrowFuncDef") {
214214
const arrowFuncDef = node as ArrowFuncDefNode;
215215

216-
return (...args: unknown[]): unknown => Evaluator.jspyFuncInvoker(arrowFuncDef, blockContext, ...args);
216+
return (...args: unknown[]): unknown => new Evaluator().jspyFuncInvoker(arrowFuncDef, blockContext, ...args);
217217
}
218218

219219
if (node.type === "funcCall") {

src/evaluator/scope.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11

22
export interface BlockContext {
3+
moduleName: string;
34
returnCalled: boolean;
45
breakCalled: boolean;
56
continueCalled: boolean;
67
returnObject: any;
7-
currentLevel: string;
8-
namelessFuncsCount: number;
98
blockScope: Scope
109
}
1110

src/interpreter.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,26 @@ export class Interpreter {
3232
return tokenizer.tokenize(script);
3333
}
3434

35-
parse(script: string): AstBlock {
35+
parse(script: string, moduleName: string='main.jspy'): AstBlock {
3636
const tokenizer = new Tokenizer();
3737
const parser = new Parser();
38-
const jspyAst = parser.parse(tokenizer.tokenize(script));
38+
const jspyAst = parser.parse(tokenizer.tokenize(script), moduleName);
3939
return jspyAst;
4040
}
4141

4242
eval(codeOrAst: string | AstBlock, scope: Record<string, unknown> = {}
43-
, entryFunctionName: string = ''): unknown {
44-
const ast = (typeof codeOrAst === 'string') ? this.parse(codeOrAst as string) : codeOrAst as AstBlock;
43+
, entryFunctionName: string = '', moduleName: string = 'main.jspy'): unknown {
44+
const ast = (typeof codeOrAst === 'string') ? this.parse(codeOrAst as string, moduleName) : codeOrAst as AstBlock;
4545

4646
const blockContext = {
47+
moduleName: moduleName,
4748
blockScope: new Scope(scope)
4849
} as BlockContext;
4950

5051
blockContext.blockScope.set('printExecutionContext', () => console.log(blockContext.blockScope));
5152
blockContext.blockScope.set('getExecutionContext', () => blockContext.blockScope);
5253

53-
const result = Evaluator.evalBlock(ast, blockContext);
54+
const result = new Evaluator().evalBlock(ast, blockContext);
5455
if (!entryFunctionName || !entryFunctionName.length) {
5556
return result;
5657
} else {
@@ -63,10 +64,11 @@ export class Interpreter {
6364
}
6465

6566
async evalAsync(codeOrAst: string | AstBlock, scope: Record<string, unknown> = {}
66-
, entryFunctionName: string = ''): Promise<unknown> {
67-
const ast = (typeof codeOrAst === 'string') ? this.parse(codeOrAst as string) : codeOrAst as AstBlock;
67+
, entryFunctionName: string = '', moduleName: string = 'main.jspy'): Promise<unknown> {
68+
const ast = (typeof codeOrAst === 'string') ? this.parse(codeOrAst as string, moduleName) : codeOrAst as AstBlock;
6869
const evaluator = new EvaluatorAsync();
6970
const blockContext = {
71+
moduleName: moduleName,
7072
blockScope: new Scope(scope)
7173
} as BlockContext;
7274
blockContext.blockScope.set('printExecutionContext', () => console.log(blockContext.blockScope));
@@ -89,9 +91,10 @@ export class Interpreter {
8991
/**
9092
* Compatibility method! Will be deprecated soon
9193
*/
92-
async evaluate(script: string, context: object = {}, entryFunctionName: string = ''): Promise<any> {
94+
async evaluate(script: string, context: object = {}, entryFunctionName: string = ''
95+
, moduleName: string = 'main.jspy'): Promise<any> {
9396
if (!script || !script.length) { return null; }
94-
const ast = this.parse(script);
97+
const ast = this.parse(script, moduleName);
9598

9699
context = (context && typeof context === 'object') ? context : {};
97100
context = await this.assignLegacyImportContext(ast, context);
@@ -102,8 +105,7 @@ export class Interpreter {
102105
} as Record<string, unknown>;
103106

104107
try {
105-
106-
return this.evalAsync(ast, this.globalScope, entryFunctionName);
108+
return this.evalAsync(ast, this.globalScope, entryFunctionName, moduleName);
107109
} catch (error) {
108110
throw error;
109111
}

0 commit comments

Comments
 (0)