Skip to content

Commit 9f5cb9c

Browse files
committed
Error reporting and other minor integration issues
1 parent 2bb0a29 commit 9f5cb9c

File tree

9 files changed

+59
-17
lines changed

9 files changed

+59
-17
lines changed

index.html

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

33-
def f():
34-
null
35-
36-
f()?.prop or 1
37-
33+
async def f2():
34+
"""
35+
long comment
36+
"""
37+
778
38+
39+
f2()
40+
3841
</div>
3942
<!--
4043
def f(n):

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jspython-interpreter",
3-
"version": "2.0.2",
3+
"version": "2.0.4",
44
"description": "JSPython is a javascript implementation of Python language that runs within web browser or NodeJS environment",
55
"keywords": [
66
"python",

src/evaluator/evaluator.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
ArrowFuncDefNode,
33
AssignNode, AstBlock, AstNode, BinOpNode, BracketObjectAccessNode, ConstNode, CreateArrayNode,
44
CreateObjectNode, DotObjectAccessNode, ForNode, FuncDefNode, FunctionCallNode, FunctionDefNode, GetSingleVarNode,
5-
IfNode, OperationFuncs, Primitive, ReturnNode, SetSingleVarNode, WhileNode
5+
IfNode, IsNullCoelsing, OperationFuncs, Primitive, ReturnNode, SetSingleVarNode, WhileNode
66
} from '../common';
77
import { JspyEvalError } from '../common/utils';
88
import { BlockContext, Scope } from './scope';
@@ -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(ast.name, loc[0], loc[1], err.message)
53+
throw new JspyEvalError(ast.name, loc[0], loc[1], err.message || err)
5454
}
5555
}
5656

@@ -277,8 +277,13 @@ export class Evaluator {
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 => this.evalNode(n, blockContext)) || []
281280

281+
if(func === undefined
282+
&& (dotObject.nestedProps[i - 1] as unknown as IsNullCoelsing).nullCoelsing){
283+
continue;
284+
}
285+
286+
const pms = funcCallNode.paramNodes?.map(n => this.evalNode(n, blockContext)) || []
282287
startObject = this.invokeFunction(func.bind(startObject), pms);
283288

284289
} else {

src/evaluator/evaluatorAsync.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
AssignNode, AstBlock, AstNode, BinOpNode, BracketObjectAccessNode, ConstNode, CreateArrayNode,
44
CreateObjectNode, DotObjectAccessNode, ForNode, FuncDefNode, FunctionCallNode, FunctionDefNode, GetSingleVarNode,
55
getTokenLoc,
6-
IfNode, OperationFuncs, Primitive, ReturnNode, SetSingleVarNode, WhileNode
6+
IfNode, IsNullCoelsing, OperationFuncs, Primitive, ReturnNode, SetSingleVarNode, WhileNode
77
} from '../common';
88
import { JspyEvalError } from '../common/utils';
99
import { Evaluator } from './evaluator';
@@ -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(blockContext.moduleName, loc[0], loc[1], err.message)
61+
throw new JspyEvalError(blockContext.moduleName, loc[0], loc[1], err.message || err)
6262
}
6363
}
6464
}
@@ -274,7 +274,7 @@ export class EvaluatorAsync {
274274
for (let i = 1; i < dotObject.nestedProps.length; i++) {
275275
const nestedProp = dotObject.nestedProps[i];
276276

277-
if ((dotObject.nestedProps[i - 1] as any).nullCoelsing && !startObject) {
277+
if ((dotObject.nestedProps[i - 1] as unknown as IsNullCoelsing).nullCoelsing && !startObject) {
278278
startObject = {};
279279
}
280280

@@ -288,6 +288,11 @@ export class EvaluatorAsync {
288288
const funcCallNode = nestedProp as FunctionCallNode;
289289
const func = startObject[funcCallNode.name] as (...args: unknown[]) => unknown;
290290

291+
if(func === undefined
292+
&& (dotObject.nestedProps[i - 1] as unknown as IsNullCoelsing).nullCoelsing){
293+
continue;
294+
}
295+
291296
const pms = []
292297
for (let p of funcCallNode.paramNodes || []) {
293298
pms.push(await this.evalNodeAsync(p, blockContext));

src/initialScope.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { parseDatetimeOrNull } from "./common/utils";
22

33
export const INITIAL_SCOPE = {
44
jsPython(): string {
5-
return [`JSPython v2.0.2`, "(c) FalconSoft Ltd"].join('\n')
5+
return [`JSPython v2.0.4`, "(c) FalconSoft Ltd"].join('\n')
66
},
77
dateTime: (str: number | string | any = null) => (str && str.length)
88
? parseDatetimeOrNull(str) || new Date() : new Date(),

src/interpreter.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,26 @@ describe('Interpreter', () => {
275275
expect(e.eval(script)).toBe(5);
276276
});
277277

278+
it('long comments issue', () => {
279+
const script = `
280+
async def f2():
281+
"""
282+
long comment
283+
"""
284+
5
285+
286+
f2()
287+
`
288+
;
289+
expect(e.eval(script)).toBe(5);
290+
});
291+
292+
it('chaining funcCall with null coelsing', () => {
293+
expect(e.eval("p?.f()?.sdsd")).toBe(null);
294+
//expect(e.eval("p?.f()?.sdsd or 5")).toBe(5);
295+
});
296+
297+
278298

279299

280300
});

src/interpreter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ export class Interpreter {
2121
private packageLoader?: PackageLoader;
2222
private fileLoader?: FileLoader;
2323

24-
2524
constructor() { }
2625
static create(): Interpreter {
2726
return new Interpreter();
@@ -32,6 +31,7 @@ export class Interpreter {
3231
return tokenizer.tokenize(script);
3332
}
3433

34+
3535
parse(script: string, moduleName: string='main.jspy'): AstBlock {
3636
const tokenizer = new Tokenizer();
3737
const parser = new Parser();

src/parser/parser.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ class InstructionLine {
2929

3030
export class Parser {
3131
private _currentToken: Token | null = null;
32+
private _moduleName = '';
3233

3334
/**
3435
* Parses tokens and return Ast - Abstract Syntax Tree for jsPython code
3536
* @param tokens tokens
3637
* @param options parsing options. By default it will exclude comments and include LOC (Line of code)
3738
*/
3839
parse(tokens: Token[], name = 'main.jspy', type: string = 'module'): AstBlock {
40+
this._moduleName = name;
3941
const ast = { name, type, funcs: [], body: [] } as AstBlock;
4042

4143
if (!tokens || !tokens.length) { return ast; }
@@ -50,7 +52,7 @@ export class Parser {
5052

5153
} catch (err) {
5254
const token = this._currentToken ?? {} as Token
53-
throw new JspyParserError(ast.name, getStartLine(token), getStartColumn(token), err.message)
55+
throw new JspyParserError(ast.name, getStartLine(token), getStartColumn(token), err.message || err)
5456
}
5557
return ast;
5658
}
@@ -59,7 +61,7 @@ export class Parser {
5961

6062
const getBody = (tokens: Token[], startTokenIndex: number): AstNode[] => {
6163
const instructionLines = this.tokensToInstructionLines(tokens, getStartLine(tokens[startTokenIndex]));
62-
const bodyAst = { body: [] as AstNode[], funcs: [] as AstNode[] } as AstBlock;
64+
const bodyAst = { name: ast.name, body: [] as AstNode[], funcs: [] as AstNode[] } as AstBlock;
6365
this.instructionsToNodes(instructionLines, bodyAst);
6466
return bodyAst.body;
6567
}
@@ -225,7 +227,7 @@ export class Parser {
225227
const sColumn = getStartColumn(token);
226228
const value = getTokenValue(token);
227229
this._currentToken = token;
228-
230+
229231
if (sLine >= startLine) {
230232

231233
if (currentLine !== sLine) {
@@ -293,6 +295,7 @@ export class Parser {
293295

294296
const instructionLines = this.tokensToInstructionLines(arrowFuncParts[1], 0);
295297
const funcAst = {
298+
name: this._moduleName,
296299
body: [] as AstNode[],
297300
funcs: [] as AstNode[]
298301
} as AstBlock;

src/tokenizer/tokenizer.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ export class Tokenizer {
125125

126126
// handle """ comment """"
127127
if (script[this._cursor + 1] === q && script[this._cursor + 2] === q) {
128+
const cLine = this._currentLine;
129+
const cColumn = this._currentColumn;
128130
this.incrementCursor(2);
129131
while (true) {
130132
this.tokenText += script[this.incrementCursor()];
@@ -133,6 +135,10 @@ export class Tokenizer {
133135
break;
134136
}
135137
}
138+
// a special case when multiline string
139+
this._startLine = cLine;
140+
this._startColumn = cColumn;
141+
136142
this.incrementCursor(3);
137143
} else {
138144
while (script[this.incrementCursor()] !== q) {

0 commit comments

Comments
 (0)