Skip to content

Commit 86a41fc

Browse files
committed
Fixed an issue with conflicting prototype methods. #37
1 parent 9ddcfb0 commit 86a41fc

File tree

6 files changed

+111
-63
lines changed

6 files changed

+111
-63
lines changed

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.1.13",
3+
"version": "2.1.14",
44
"description": "JSPython is a javascript implementation of Python language that runs within web browser or NodeJS environment",
55
"keywords": [
66
"python",

src/common/operators.ts

Lines changed: 74 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,38 @@ export type Operators =
1919
| LogicalOperators
2020
| MembershipOperators;
2121

22-
export const OperatorsMap: Record<Operators, OperationTypes> = {
23-
'+': OperationTypes.Arithmetic,
24-
'-': OperationTypes.Arithmetic,
25-
'*': OperationTypes.Arithmetic,
26-
'/': OperationTypes.Arithmetic,
27-
'%': OperationTypes.Arithmetic,
28-
'**': OperationTypes.Arithmetic,
29-
'//': OperationTypes.Arithmetic,
30-
31-
'>': OperationTypes.Comparison,
32-
'>=': OperationTypes.Comparison,
33-
'==': OperationTypes.Comparison,
34-
'!=': OperationTypes.Comparison,
35-
'<>': OperationTypes.Comparison,
36-
'<': OperationTypes.Comparison,
37-
'<=': OperationTypes.Comparison,
38-
39-
and: OperationTypes.Logical,
40-
or: OperationTypes.Logical,
41-
// "not": OperationTypes.Logical,
42-
// "not in": OperationTypes.Logical,
43-
44-
in: OperationTypes.Membership,
45-
46-
'=': OperationTypes.Assignment,
47-
'+=': OperationTypes.Assignment,
48-
'-=': OperationTypes.Assignment,
49-
'*=': OperationTypes.Assignment,
50-
'/=': OperationTypes.Assignment,
51-
'++': OperationTypes.Assignment,
52-
'--': OperationTypes.Assignment
53-
};
22+
export const OperatorsMap: Map<Operators, OperationTypes> = new Map<Operators, OperationTypes>([
23+
['+', OperationTypes.Arithmetic],
24+
['-', OperationTypes.Arithmetic],
25+
['*', OperationTypes.Arithmetic],
26+
['/', OperationTypes.Arithmetic],
27+
['%', OperationTypes.Arithmetic],
28+
['**', OperationTypes.Arithmetic],
29+
['//', OperationTypes.Arithmetic],
30+
31+
['>', OperationTypes.Comparison],
32+
['>=', OperationTypes.Comparison],
33+
['==', OperationTypes.Comparison],
34+
['!=', OperationTypes.Comparison],
35+
['<>', OperationTypes.Comparison],
36+
['<', OperationTypes.Comparison],
37+
['<=', OperationTypes.Comparison],
38+
39+
['and', OperationTypes.Logical],
40+
['or', OperationTypes.Logical],
41+
// "not", OperationTypes.Logical],
42+
// "not in", OperationTypes.Logical],
43+
44+
['in', OperationTypes.Membership],
45+
46+
['=', OperationTypes.Assignment],
47+
['+=', OperationTypes.Assignment],
48+
['-=', OperationTypes.Assignment],
49+
['*=', OperationTypes.Assignment],
50+
['/=', OperationTypes.Assignment],
51+
['++', OperationTypes.Assignment],
52+
['--', OperationTypes.Assignment]
53+
]);
5454

5555
export type Primitive = string | number | boolean | null;
5656

@@ -61,30 +61,48 @@ export type ExpressionOperators =
6161
| MembershipOperators;
6262
type ExpressionOperation = (l: Primitive, r: Primitive) => Primitive;
6363

64-
export const OperationFuncs: Record<ExpressionOperators, ExpressionOperation> = {
65-
'+': (l, r) => arithmeticOperation(l, r, '+'),
66-
'-': (l, r) => arithmeticOperation(l, r, '-'),
67-
'/': (l, r) => arithmeticOperation(l, r, '/'),
68-
'*': (l, r) => arithmeticOperation(l, r, '*'),
69-
'%': (l, r) => arithmeticOperation(l, r, '%'),
70-
'**': (l, r) => arithmeticOperation(l, r, '**'),
71-
'//': (l, r) => arithmeticOperation(l, r, '//'),
72-
73-
'>': (l, r) => comparissonOperation(l, r, '>'),
74-
'>=': (l, r) => comparissonOperation(l, r, '>='),
75-
'<': (l, r) => comparissonOperation(l, r, '<'),
76-
'<=': (l, r) => comparissonOperation(l, r, '<='),
77-
'==': (l, r) => comparissonOperation(l, r, '=='),
78-
'!=': (l, r) => comparissonOperation(l, r, '!='),
79-
'<>': (l, r) => comparissonOperation(l, r, '<>'),
80-
81-
and: (l, r) => logicalOperation(l, r, 'and'),
82-
or: (l, r) => logicalOperation(l, r, 'or'),
83-
// "not": (l, r) => logicalOperation(l, r, "not"),
84-
// "not in": (l, r) => logicalOperation(l, r, "not in"),
85-
86-
in: (l, r) => membershipOperation(l, r, 'in')
87-
};
64+
export const OperationFuncs: Map<ExpressionOperators, ExpressionOperation> = new Map<
65+
ExpressionOperators,
66+
ExpressionOperation
67+
>([
68+
['+' as ExpressionOperators, ((l, r) => arithmeticOperation(l, r, '+')) as ExpressionOperation],
69+
['-' as ExpressionOperators, ((l, r) => arithmeticOperation(l, r, '-')) as ExpressionOperation],
70+
['/' as ExpressionOperators, ((l, r) => arithmeticOperation(l, r, '/')) as ExpressionOperation],
71+
['*' as ExpressionOperators, ((l, r) => arithmeticOperation(l, r, '*')) as ExpressionOperation],
72+
['%' as ExpressionOperators, ((l, r) => arithmeticOperation(l, r, '%')) as ExpressionOperation],
73+
['**' as ExpressionOperators, ((l, r) => arithmeticOperation(l, r, '**')) as ExpressionOperation],
74+
['//' as ExpressionOperators, ((l, r) => arithmeticOperation(l, r, '//')) as ExpressionOperation],
75+
76+
['>' as ExpressionOperators, ((l, r) => comparissonOperation(l, r, '>')) as ExpressionOperation],
77+
[
78+
'>=' as ExpressionOperators,
79+
((l, r) => comparissonOperation(l, r, '>=')) as ExpressionOperation
80+
],
81+
['<' as ExpressionOperators, ((l, r) => comparissonOperation(l, r, '<')) as ExpressionOperation],
82+
[
83+
'<=' as ExpressionOperators,
84+
((l, r) => comparissonOperation(l, r, '<=')) as ExpressionOperation
85+
],
86+
[
87+
'==' as ExpressionOperators,
88+
((l, r) => comparissonOperation(l, r, '==')) as ExpressionOperation
89+
],
90+
[
91+
'!=' as ExpressionOperators,
92+
((l, r) => comparissonOperation(l, r, '!=')) as ExpressionOperation
93+
],
94+
[
95+
'<>' as ExpressionOperators,
96+
((l, r) => comparissonOperation(l, r, '<>')) as ExpressionOperation
97+
],
98+
99+
['and' as ExpressionOperators, ((l, r) => logicalOperation(l, r, 'and')) as ExpressionOperation],
100+
['or' as ExpressionOperators, ((l, r) => logicalOperation(l, r, 'or')) as ExpressionOperation],
101+
// "not" as ExpressionOperators, ((l, r) => logicalOperation(l, r, "not")) as ExpressionOperation],
102+
// "not in" as ExpressionOperators, ((l, r) => logicalOperation(l, r, "not in")) as ExpressionOperation],
103+
104+
['in' as ExpressionOperators, ((l, r) => membershipOperation(l, r, 'in')) as ExpressionOperation]
105+
]);
88106

89107
function membershipOperation(l: Primitive, r: Primitive, op: MembershipOperators): Primitive {
90108
if (typeof l === 'string') {

src/common/token-types.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,11 @@ export function findOperators(
182182
operationType: OperationTypes | null = null
183183
): number[] {
184184
return !operationType
185-
? findTokenValueIndexes(tokens, value => OperatorsMap[value as Operators] !== undefined)
186-
: findTokenValueIndexes(tokens, value => OperatorsMap[value as Operators] === operationType);
185+
? findTokenValueIndexes(tokens, value => OperatorsMap.has(value as Operators))
186+
: findTokenValueIndexes(
187+
tokens,
188+
value => OperatorsMap.get(value as Operators) === operationType
189+
);
187190
}
188191

189192
function skipInnerBrackets(

src/evaluator/evaluator.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export class Evaluator {
160160
}
161161
}
162162
}
163-
163+
164164
if (doElse && ifNode.elseBody) {
165165
this.evalBlock(
166166
{ name: blockContext.moduleName, type: 'if', body: ifNode.elseBody } as AstBlock,
@@ -332,7 +332,9 @@ export class Evaluator {
332332
const binOpNode = node as BinOpNode;
333333
const left = this.evalNode(binOpNode.left, blockContext);
334334
const right = this.evalNode(binOpNode.right, blockContext);
335-
return OperationFuncs[binOpNode.op](left as Primitive, right as Primitive);
335+
const func = OperationFuncs.get(binOpNode.op);
336+
if (typeof func === 'function') return func(left as Primitive, right as Primitive);
337+
else throw new Error('Unknown binary oprastion');
336338
}
337339

338340
if (node.type === 'logicalOp') {

src/evaluator/evaluatorAsync.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,10 @@ export class EvaluatorAsync {
436436
const binOpNode = node as BinOpNode;
437437
const left = await this.evalNodeAsync(binOpNode.left, blockContext);
438438
const right = await this.evalNodeAsync(binOpNode.right, blockContext);
439-
return OperationFuncs[binOpNode.op](left as Primitive, right as Primitive);
439+
440+
const func = OperationFuncs.get(binOpNode.op);
441+
if (typeof func === 'function') return func(left as Primitive, right as Primitive);
442+
else throw new Error('Unknown binary oprastion');
440443
}
441444

442445
if (node.type === 'logicalOp') {

src/parser/parser.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,28 @@ describe('Parser => ', () => {
157157
expect(!!(innerNodes[3] as ChainingObjectAccessNode).nullCoelsing).toBe(true);
158158
expect(!!(innerNodes[4] as ChainingObjectAccessNode).nullCoelsing).toBe(true);
159159
expect(!!(innerNodes[5] as ChainingObjectAccessNode).nullCoelsing).toBe(false);
160-
});
160+
});
161+
162+
it('chaining calls 1 with ? ', async () => {
163+
const script = `"1,2,3".split(',')?[0]`;
164+
const ast = new Parser().parse(new Tokenizer().tokenize(script));
165+
expect(ast.body.length).toBe(1);
166+
const innerNodes = (ast.body[0] as ChainingCallsNode).innerNodes;
167+
168+
expect(ast.body[0].type).toBe("chainingCalls");
169+
expect(innerNodes.length).toBe(3);
170+
expect(innerNodes[2].type).toBe("chainingObjectAccess");
171+
172+
expect(!!(innerNodes[0] as ChainingObjectAccessNode).nullCoelsing).toBe(false);
173+
expect((innerNodes[1] as ChainingObjectAccessNode).nullCoelsing).toBe(true);
174+
expect(!!(innerNodes[2] as ChainingObjectAccessNode).nullCoelsing).toBe(false);
175+
});
176+
177+
it('prototype methods call ', async () => {
178+
expect(new Parser().parse(new Tokenizer().tokenize(`t.toString`)).body.length).toBe(1);
179+
expect(new Parser().parse(new Tokenizer().tokenize(`t.toString()`)).body.length).toBe(1);
180+
expect(new Parser().parse(new Tokenizer().tokenize(`t.valueOf`)).body.length).toBe(1);
181+
expect(new Parser().parse(new Tokenizer().tokenize(`t.valueOf()`)).body.length).toBe(1);
182+
});
161183

162184
});

0 commit comments

Comments
 (0)