Skip to content

Commit f793d71

Browse files
authored
Dev jspy 12 (#33)
* #12 created new chainingCallsNode and initial setup * fixed parser error * added parser logic to create chaining nodes * added chaining calls evaluations
1 parent 8a801bc commit f793d71

File tree

9 files changed

+446
-218
lines changed

9 files changed

+446
-218
lines changed

index.html

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,7 @@
2929
<div class="container">
3030
<h4>JSPython development console</h4>
3131
<div id="editor">
32-
m = ''
33-
try:
34-
raise 'My Message'
35-
x.push(1)
36-
except:
37-
m = error.message
38-
m
32+
"1,2,3".split(',')[0]
3933
</div>
4034

4135
<button onclick="tokenize()">Tokenize</button>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@
5959
"hooks": {
6060
"pre-commit": "npm run lint && npm run test"
6161
}
62-
}
62+
}
6363
}

src/common/ast-types.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ export type AstNodeType =
88
| 'logicalOp'
99
| 'getSingleVar'
1010
| 'setSingleVar'
11-
| 'dotObjectAccess'
12-
| 'bracketObjectAccess'
11+
| 'chainingCalls'
12+
| 'chainingObjectAccess'
1313
| 'funcCall'
1414
| 'funcDef'
1515
| 'arrowFuncDef'
@@ -211,9 +211,9 @@ export class GetSingleVarNode extends AstNode implements IsNullCoelsing {
211211
}
212212
}
213213

214-
export class DotObjectAccessNode extends AstNode {
215-
constructor(public nestedProps: AstNode[], public loc: Uint16Array) {
216-
super('dotObjectAccess');
214+
export class ChainingCallsNode extends AstNode {
215+
constructor(public innerNodes: AstNode[], public loc: Uint16Array) {
216+
super('chainingCalls');
217217
this.loc = loc;
218218
}
219219
}
@@ -232,18 +232,18 @@ export class CreateArrayNode extends AstNode {
232232
}
233233
}
234234

235-
export class BracketObjectAccessNode extends AstNode {
235+
export class ChainingObjectAccessNode extends AstNode {
236236
constructor(
237-
public propertyName: string,
238-
public bracketBody: AstNode,
239-
public nullCoalescing: boolean | undefined = undefined,
237+
public indexerBody: AstNode,
238+
public nullCoelsing: boolean | undefined = undefined,
240239
public loc: Uint16Array
241240
) {
242-
super('bracketObjectAccess');
241+
super('chainingObjectAccess');
243242
this.loc = loc;
244243
}
245244
}
246245

246+
247247
export interface LogicalNodeItem {
248248
node: AstNode;
249249
op: LogicalOperators | undefined;

src/common/token-types.ts

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,26 +65,38 @@ export function getEndColumn(token: Token): number {
6565
return token[1][4];
6666
}
6767

68-
export function splitTokens(tokens: Token[], separator: string): Token[][] {
68+
export function splitTokensByIndexes(tokens: Token[], sepIndexes: number[]): Token[][] {
6969
const result: Token[][] = [];
7070

7171
if (!tokens.length) {
7272
return [];
7373
}
7474

75-
const sepIndexes = findTokenValueIndexes(tokens, value => value === separator);
76-
7775
let start = 0;
7876
for (let i = 0; i < sepIndexes.length; i++) {
7977
const ind = sepIndexes[i];
78+
if (getTokenValue(tokens[start - 1]) === '[') {
79+
start = start - 1;
80+
}
8081
result.push(tokens.slice(start, ind));
8182
start = ind + 1;
8283
}
8384

85+
if (getTokenValue(tokens[start - 1]) === '[') {
86+
start = start - 1;
87+
}
8488
result.push(tokens.slice(start, tokens.length));
8589
return result;
8690
}
8791

92+
export function splitTokens(tokens: Token[], separator: string): Token[][] {
93+
if (!tokens.length) {
94+
return [];
95+
}
96+
const sepIndexes = findTokenValueIndexes(tokens, value => value === separator);
97+
return splitTokensByIndexes(tokens, sepIndexes);
98+
}
99+
88100
export function findTokenValueIndex(
89101
tokens: Token[],
90102
predicate: (value: TokenValue) => boolean,
@@ -109,6 +121,34 @@ export function findTokenValueIndex(
109121
return -1;
110122
}
111123

124+
export function findChainingCallTokensIndexes(tokens: Token[]): number[] {
125+
const opIndexes: number[] = [];
126+
127+
for (let i = 0; i < tokens.length; i++) {
128+
const tValue = getTokenValue(tokens[i]);
129+
const tType = getTokenType(tokens[i]);
130+
131+
if (tType === TokenTypes.LiteralString) {
132+
continue;
133+
}
134+
135+
if (tValue === '.') {
136+
opIndexes.push(i);
137+
} else if (tValue === '(') {
138+
i = skipInnerBrackets(tokens, i, '(', ')');
139+
} else if (tValue === '[' && i === 0) {
140+
i = skipInnerBrackets(tokens, i, '[', ']');
141+
} else if (tValue === '[' && i !== 0) {
142+
opIndexes.push(i);
143+
i = skipInnerBrackets(tokens, i, '[', ']');
144+
} else if (tValue === '{') {
145+
i = skipInnerBrackets(tokens, i, '{', '}');
146+
}
147+
}
148+
149+
return opIndexes;
150+
}
151+
112152
export function findTokenValueIndexes(
113153
tokens: Token[],
114154
predicate: (value: TokenValue) => boolean

src/evaluator/evaluator.ts

Lines changed: 82 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import {
44
AstBlock,
55
AstNode,
66
BinOpNode,
7-
BracketObjectAccessNode,
7+
ChainingCallsNode,
8+
ChainingObjectAccessNode,
89
ConstNode,
910
CreateArrayNode,
1011
CreateObjectNode,
11-
DotObjectAccessNode,
1212
ForNode,
1313
FuncDefNode,
1414
FunctionCallNode,
@@ -369,104 +369,48 @@ export class Evaluator {
369369

370370
if (assignNode.target.type === 'getSingleVar') {
371371
const node = assignNode.target as SetSingleVarNode;
372-
blockContext.blockScope.set(node.name, this.evalNode(assignNode.source, blockContext));
373-
} else if (assignNode.target.type === 'dotObjectAccess') {
374-
const targetNode = assignNode.target as DotObjectAccessNode;
372+
blockContext.blockScope.set(
373+
node.name,
374+
this.evalNode(assignNode.source, blockContext)
375+
);
376+
} else if (assignNode.target.type === 'chainingCalls') {
377+
const targetNode = assignNode.target as ChainingCallsNode;
375378

376379
// create a node for all but last property token
377380
// potentially it can go to parser
378-
const targetObjectNode = new DotObjectAccessNode(
379-
targetNode.nestedProps.slice(0, targetNode.nestedProps.length - 1),
381+
const targetObjectNode = new ChainingCallsNode(
382+
targetNode.innerNodes.slice(0, targetNode.innerNodes.length - 1),
380383
targetNode.loc
381384
);
382-
const targetObject = this.evalNode(targetObjectNode, blockContext) as Record<
385+
const targetObject = (this.evalNode(targetObjectNode, blockContext)) as Record<
383386
string,
384387
unknown
385388
>;
386389

387-
// not sure nested properties should be GetSingleVarNode
388-
// can be factored in the parser
389-
const lastPropertyName = (
390-
targetNode.nestedProps[targetNode.nestedProps.length - 1] as GetSingleVarNode
391-
).name;
390+
const lastInnerNode = targetNode.innerNodes[targetNode.innerNodes.length - 1];
391+
392+
let lastPropertyName = '';
393+
if (lastInnerNode.type === 'getSingleVar') {
394+
lastPropertyName = (lastInnerNode as GetSingleVarNode).name;
395+
} else if (lastInnerNode.type === 'chainingObjectAccess') {
396+
lastPropertyName = (this.evalNode(
397+
(lastInnerNode as ChainingObjectAccessNode).indexerBody,
398+
blockContext
399+
)) as string;
400+
} else {
401+
throw Error('Not implemented Assign operation with chaining calls');
402+
}
392403

393404
targetObject[lastPropertyName] = this.evalNode(assignNode.source, blockContext);
394-
} else if (assignNode.target.type === 'bracketObjectAccess') {
395-
const targetNode = assignNode.target as BracketObjectAccessNode;
396-
const keyValue = this.evalNode(targetNode.bracketBody, blockContext) as string | number;
397-
const targetObject = blockContext.blockScope.get(
398-
targetNode.propertyName as string
399-
) as Record<string, unknown>;
400-
401-
targetObject[keyValue] = this.evalNode(assignNode.source, blockContext);
402-
} else {
403-
throw Error('Not implemented Assign operation');
404-
// get chaining calls
405405
}
406406

407407
return null;
408408
}
409409

410-
if (node.type === 'bracketObjectAccess') {
411-
const sbNode = node as BracketObjectAccessNode;
412-
const key = this.evalNode(sbNode.bracketBody, blockContext) as string;
413-
const obj = blockContext.blockScope.get(sbNode.propertyName as string) as Record<
414-
string,
415-
unknown
416-
>;
417-
return obj[key] === undefined ? null : obj[key];
410+
if (node.type === 'chainingCalls') {
411+
return this.resolveChainingCallsNode(node as ChainingCallsNode, blockContext);
418412
}
419413

420-
if (node.type === 'dotObjectAccess') {
421-
const dotObject = node as DotObjectAccessNode;
422-
423-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
424-
let startObject = this.evalNode(dotObject.nestedProps[0], blockContext) as any;
425-
for (let i = 1; i < dotObject.nestedProps.length; i++) {
426-
const nestedProp = dotObject.nestedProps[i];
427-
428-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
429-
if ((dotObject.nestedProps[i - 1] as any).nullCoelsing && !startObject) {
430-
startObject = {};
431-
}
432-
433-
if (nestedProp.type === 'getSingleVar') {
434-
startObject = startObject[(nestedProp as SetSingleVarNode).name] as unknown;
435-
} else if (nestedProp.type === 'bracketObjectAccess') {
436-
const node = nestedProp as BracketObjectAccessNode;
437-
startObject = startObject[node.propertyName] as unknown;
438-
startObject = startObject[
439-
this.evalNode(node.bracketBody, blockContext) as string
440-
] as unknown;
441-
} else if (nestedProp.type === 'funcCall') {
442-
const funcCallNode = nestedProp as FunctionCallNode;
443-
const func = startObject[funcCallNode.name] as (...args: unknown[]) => unknown;
444-
445-
if (
446-
(func === undefined || func === null) &&
447-
(dotObject.nestedProps[i - 1] as unknown as IsNullCoelsing).nullCoelsing
448-
) {
449-
startObject = null;
450-
continue;
451-
}
452-
453-
if (typeof func !== 'function') {
454-
throw Error(`'${funcCallNode.name}' is not a function or not defined.`);
455-
}
456-
const pms = funcCallNode.paramNodes?.map(n => this.evalNode(n, blockContext)) || [];
457-
startObject = this.invokeFunction(func.bind(startObject), pms, {
458-
moduleName: blockContext.moduleName,
459-
line: funcCallNode.loc[0],
460-
column: funcCallNode.loc[1]
461-
});
462-
} else {
463-
throw Error("Can't resolve dotObjectAccess node");
464-
}
465-
}
466-
467-
// no undefined values, make it rather null
468-
return startObject === undefined ? null : startObject;
469-
}
470414

471415
if (node.type === 'createObject') {
472416
const createObjectNode = node as CreateObjectNode;
@@ -490,4 +434,60 @@ export class Evaluator {
490434
return res;
491435
}
492436
}
437+
438+
private resolveChainingCallsNode(
439+
chNode: ChainingCallsNode,
440+
blockContext: BlockContext
441+
): unknown {
442+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
443+
let startObject = (this.evalNode(chNode.innerNodes[0], blockContext)) as any;
444+
445+
for (let i = 1; i < chNode.innerNodes.length; i++) {
446+
const nestedProp = chNode.innerNodes[i];
447+
448+
if ((chNode.innerNodes[i - 1] as unknown as IsNullCoelsing).nullCoelsing && !startObject) {
449+
startObject = {};
450+
}
451+
452+
if (nestedProp.type === 'getSingleVar') {
453+
startObject = startObject[(nestedProp as SetSingleVarNode).name] as unknown;
454+
} else if (nestedProp.type === 'chainingObjectAccess') {
455+
const node = nestedProp as ChainingObjectAccessNode;
456+
// startObject = startObject[node.] as unknown;
457+
startObject = startObject[
458+
(this.evalNode(node.indexerBody, blockContext)) as string
459+
] as unknown;
460+
} else if (nestedProp.type === 'funcCall') {
461+
const funcCallNode = nestedProp as FunctionCallNode;
462+
const func = startObject[funcCallNode.name] as (...args: unknown[]) => unknown;
463+
464+
if (
465+
(func === undefined || func === null) &&
466+
(chNode.innerNodes[i - 1] as unknown as IsNullCoelsing).nullCoelsing
467+
) {
468+
startObject = null;
469+
continue;
470+
}
471+
472+
if (typeof func !== 'function') {
473+
throw Error(`'${funcCallNode.name}' is not a function or not defined.`);
474+
}
475+
const pms = [];
476+
for (const p of funcCallNode.paramNodes || []) {
477+
pms.push(this.evalNode(p, blockContext));
478+
}
479+
480+
startObject = this.invokeFunction(func.bind(startObject), pms, {
481+
moduleName: blockContext.moduleName,
482+
line: funcCallNode.loc[0],
483+
column: funcCallNode.loc[0]
484+
});
485+
} else {
486+
throw Error("Can't resolve chainingCalls node");
487+
}
488+
}
489+
490+
return startObject === undefined ? null : startObject;
491+
}
492+
493493
}

0 commit comments

Comments
 (0)