Skip to content

Commit 8eac198

Browse files
committed
revised project structure with main stub classes and dev configuration
1 parent e9c6630 commit 8eac198

18 files changed

+218
-43
lines changed

.eslintignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
dist
3+
**/*/*.d.ts
4+
./*js

.eslintrc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"plugins": [
5+
"@typescript-eslint"
6+
],
7+
"extends": [
8+
"eslint:recommended",
9+
"plugin:@typescript-eslint/eslint-recommended",
10+
"plugin:@typescript-eslint/recommended"
11+
],
12+
"rules": {
13+
"@typescript-eslint/no-explicit-any": "warn",
14+
"@typescript-eslint/explicit-module-boundary-types": "warn",
15+
"@typescript-eslint/explicit-function-return-type": "warn",
16+
"@typescript-eslint/no-unused-vars": "off"
17+
}
18+
}

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2019, FalconSoft Ltd
3+
Copyright (c) 2020, FalconSoft Ltd
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without

docs/jsPython-interpreter.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# jsPython interpreter
2+
v2 is a complete rewrite of jsPython interpreter. It uses a tree-walk interpreter approach. What consists of three major steps
3+
4+
> Tokenize -> Parse -> Evaluate

index.html

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
margin: 10px auto;
1414
}
1515

16-
#editor,#result {
16+
#editor,
17+
#result {
1718
height: 40%;
1819
display: block;
1920
min-height: 20%;
@@ -27,11 +28,10 @@
2728
<body>
2829
<div class="container">
2930
<h4>JSPython development console</h4>
30-
<div id="editor">
31-
[
32-
[12, 42]
33-
[22, 88]
34-
]</div>
31+
<div id="editor">a = 1
32+
b = a+2*3
33+
</div>
34+
<button onclick="parse()">Parse</button>
3535
<button onclick="runInterpreter()">Run</button>
3636
<textarea id="result"></textarea>
3737
</div>
@@ -43,20 +43,37 @@ <h4>JSPython development console</h4>
4343

4444
const jsPython = jspython.jsPython;
4545

46+
async function parse() {
47+
48+
const scripts = editor.getValue();
49+
try {
50+
const result = await jsPython()
51+
.parse(scripts);
52+
53+
document.getElementById('result').value = typeof result === 'object' ? JSON.stringify(result, null, '\t') : result
54+
console.log('Result => ', result);
55+
} catch (err) {
56+
document.getElementById('result').value = err;
57+
console.error(err);
58+
}
59+
}
60+
4661
async function runInterpreter() {
4762

4863
const scripts = editor.getValue();
4964
try {
5065
const result = await jsPython()
51-
.evaluate(scripts, {str: "shdsd sd,sd d s ds d"});
66+
.parse(scripts);
67+
5268
document.getElementById('result').value = typeof result === 'object' ? JSON.stringify(result, null, '\t') : result
5369
console.log('Result => ', result);
5470
} catch (err) {
5571
document.getElementById('result').value = err;
5672
console.error(err);
5773
}
5874
}
75+
5976
</script>
6077
</body>
6178

62-
</html>
79+
</html>

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
"test:dev:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch",
2121
"build": "npx rollup -c",
2222
"build:publish": "npx rollup -c && npm publish",
23-
"dev": "npx rollup --config rollup.config.dev.js --watch"
23+
"dev": "npx rollup --config rollup.config.dev.js --watch",
24+
"lint": "npx eslint . --ext .ts",
25+
"lint-fix": "eslint . --ext .ts --fix"
2426
},
2527
"repository": {
2628
"type": "git",
@@ -37,7 +39,10 @@
3739
"dependencies": {},
3840
"devDependencies": {
3941
"@types/jest": "^26.0.15",
42+
"@typescript-eslint/eslint-plugin": "^4.8.2",
43+
"@typescript-eslint/parser": "^4.8.2",
4044
"ace-builds": "^1.4.12",
45+
"eslint": "^7.14.0",
4146
"jest": "^26.6.3",
4247
"rollup": "^2.33.3",
4348
"rollup-plugin-copy": "^3.3.0",

src/common/ast-types.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
export abstract class AstNode {
3+
loc: Uint16Array | undefined = undefined;
4+
constructor(
5+
public type: 'assign' | 'binOp' | 'const' | 'singleVar' | 'chainingCalls' | 'funcCall' | 'funcDef' | 'if' | 'while' | 'try_catch'
6+
) { }
7+
}
8+
9+
export class Assign extends AstNode {
10+
constructor(
11+
public target: AstNode,
12+
public source: AstNode) {
13+
super('assign');
14+
}
15+
}
16+
17+
export class ConstNode extends AstNode {
18+
constructor(public value: number | string | boolean) {
19+
super('const');
20+
}
21+
}
22+
23+
export class SingleVarNode extends AstNode {
24+
constructor(public name: string) {
25+
super('singleVar');
26+
}
27+
}
28+
29+
export class ChainVarVarNode extends AstNode {
30+
constructor(public sub: AstNode[]) {
31+
super('chainingCalls');
32+
}
33+
}
34+
35+
36+
export class BinOp extends AstNode {
37+
constructor(
38+
public left: AstNode,
39+
public op: 'add' | 'sub' | 'mult' | 'div',
40+
public right: AstNode) {
41+
super('binOp');
42+
}
43+
}
44+
45+
export interface Ast {
46+
name: string;
47+
body: AstNode[]
48+
}

src/common/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './ast-types'
2+
export * from './token-types'
3+
export * from './parser-types'

src/common/parser-types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface ParserOptions {
2+
includeComments: boolean;
3+
includeLoc: boolean;
4+
}

src/common/token-types.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export enum TokenTypes
2+
{
3+
Identifier = 0,
4+
Keyword = 1,
5+
Separator = 2,
6+
Operator = 3,
7+
LiteralNumber = 4,
8+
LiteralBool = 5,
9+
LiteralString = 6,
10+
LiteralNull = 7,
11+
Comment = 8
12+
}
13+
/**
14+
* Token represent a single considered token in a script. Is represented as an array, where element at:
15+
* 0 : value
16+
* 1 : token details. For a memory and performance reasons we use Uint16Array with 5 elements in it:
17+
* [
18+
* 0 - tokenType number equivalent of @TokenTypes
19+
* 1 - beginLine
20+
* 2 - beginColumn
21+
* 3 - endLine
22+
* 4 - endColumn
23+
* ]
24+
* [(value). Uint16Array[5]([tokenType, beginLine, beginColumn, endLine, endColumn])]
25+
* tokenType
26+
*/
27+
export type Token = [string | number | boolean | null, Uint16Array]

src/evaluator/evaluator.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Ast } from '../common';
2+
3+
export class Evaluator {
4+
5+
registerFunction(funcName: string, fn: () => unknown): Evaluator {
6+
throw new Error('Not implemented yet!')
7+
}
8+
9+
registerInstance(instanceName: string, instance: Record<string, unknown>): Evaluator {
10+
throw new Error('Not implemented yet!')
11+
}
12+
13+
assignObject(obj: Record<string, unknown>): Evaluator {
14+
throw new Error('Not implemented yet!')
15+
}
16+
17+
eval(ast: Ast): Promise<unknown> {
18+
throw new Error("Not implemented yet");
19+
}
20+
21+
}

src/evaluator/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './evaluator'

src/interpreter.ts

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,33 @@
1+
import { Ast } from './common';
2+
import { Evaluator } from './evaluator';
3+
import { Parser } from './parser';
4+
import { Tokenizer } from './tokenizer';
15

2-
function range(start: number, stop: number = NaN, step: number = 1): number[] {
3-
const arr: number[] = [];
4-
const isStopNaN = isNaN(stop);
5-
stop = isStopNaN ? start : stop;
6-
start = isStopNaN ? 0 : start;
7-
let i = start;
8-
while (i < stop) {
9-
arr.push(i);
10-
i += step;
11-
}
12-
return arr;
13-
}
14-
15-
const INITIAL_SCOPE = {
16-
jsPython(): string {
17-
return [`JSPython v2.0.1`, "(c) FalconSoft Ltd"].join('\n')
18-
},
19-
dateTime: (str: number | string | any = null) => (str && str.length)
20-
? new Date(str) || new Date() : new Date(),
21-
range: range,
22-
print: (...args: any[]) => { console.log(...args); return args.length > 0 ? args[0] : null; },
23-
deleteProperty: (obj: any, propName: string): boolean => delete obj[propName],
24-
Math: Math,
25-
Object: Object,
26-
Array: Array,
27-
JSON: JSON,
28-
printExecutionContext: () => {}, // will be overriden at runtime
29-
getExecutionContext: () => {} // will be overriden at runtime
30-
};
316

327
export function jsPython(): Interpreter {
338
return Interpreter.create();
349
}
3510

3611
export class Interpreter {
12+
private readonly evaluator: Evaluator;
13+
constructor() {
14+
this.evaluator = new Evaluator();
15+
}
3716
static create(): Interpreter {
3817
return new Interpreter();
3918
}
4019

41-
async evaluate(script: string, context: object = {}, entryFunctionName: string = ''): Promise<any> {
42-
if (!script || !script.length) { return null; }
20+
parse(script: string): Ast {
21+
const tokenizer = new Tokenizer();
22+
const parser = new Parser();
23+
const jspyAst = parser.parse(tokenizer.tokenize(script));
24+
return jspyAst;
25+
}
4326

44-
return -1;
27+
async evaluate(codeOrAst: string | Ast, context: Record<string, unknown> = {}, entryFunctionName = ''): Promise<unknown> {
28+
const ast = (typeof codeOrAst === 'string') ? this.parse(codeOrAst as string) : codeOrAst as Ast;
29+
const result = await this.evaluator.eval(ast)
30+
return result;
4531
}
4632

4733

src/parser/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './parser'

src/parser/parser.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { BinOp, ConstNode, Ast, Token, ParserOptions } from '../common';
2+
3+
export class Parser {
4+
/**
5+
* Parses tokens and return Ast - Abstract Syntax Tree for jsPython code
6+
* @param tokens tokens
7+
* @param options parsing options. By default it will exclude comments and include LOC (Line of code)
8+
*/
9+
parse(tokens: Token[], options: ParserOptions = { includeComments: false, includeLoc: true }): Ast {
10+
const ast = {} as Ast;
11+
const binOp = new BinOp(new ConstNode(1), 'add', new ConstNode(2));
12+
binOp.loc = Uint16Array.of(11, 12, 13, 14);
13+
binOp.loc[3] = 24;
14+
binOp.loc[4] = 20;
15+
16+
ast.name = "main.jspy";
17+
ast.body = [binOp];
18+
19+
return ast;
20+
}
21+
}
22+

src/tokenizer/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './tokenizer'

src/tokenizer/tokenizer.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Token } from "../common";
2+
3+
export class Tokenizer
4+
{
5+
/**
6+
* Splits script code into a tokens
7+
* @param script A jsPython text
8+
*/
9+
tokenize(script: string): Token[] {
10+
return [];
11+
}
12+
13+
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
4040

4141
/* Module Resolution Options */
42-
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
42+
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
4343
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
4444
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
4545
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */

0 commit comments

Comments
 (0)