greybel-interpreter 5.6.2 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -1
- package/dist/bytecode-generator/context.js +2 -2
- package/dist/bytecode-generator/expression.js +4 -6
- package/dist/bytecode-generator/instruction.d.ts +8 -3
- package/dist/bytecode-generator/statement.js +10 -8
- package/dist/bytecode-generator.d.ts +4 -0
- package/dist/bytecode-generator.js +7 -2
- package/dist/interpreter.d.ts +2 -0
- package/dist/interpreter.js +10 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,3 +1,73 @@
|
|
|
1
1
|
# greybel-interpreter
|
|
2
2
|
|
|
3
|
-
[](https://circleci.com/gh/ayecue/greybel-interpreter)
|
|
3
|
+
[](https://circleci.com/gh/ayecue/greybel-interpreter)
|
|
4
|
+
|
|
5
|
+
A bytecode interpreter for [MiniScript](https://miniscript.org) written in TypeScript. It compiles MiniScript source code to bytecode and executes it in a stack-based VM built on top of [greybel-core](../greybel-core). For GreyScript-specific interpretation, see [greyscript-interpreter](../greyscript-interpreter).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
* Bytecode compiler that converts AST to efficient instructions
|
|
10
|
+
* Stack-based virtual machine with non-blocking scheduling
|
|
11
|
+
* Built-in type system: string, number, boolean, list, map, function, and nil
|
|
12
|
+
* Pluggable handler system for resource loading, output, and error handling
|
|
13
|
+
* Debugger support with breakpoints and step-through execution
|
|
14
|
+
* Code injection at runtime via `interpreter.inject()`
|
|
15
|
+
* Environment variable injection
|
|
16
|
+
* Extensible API surface - supply custom intrinsics via an `ObjectValue` map
|
|
17
|
+
* Runs in both Node.js and the browser (includes Rollup bundle config)
|
|
18
|
+
* Written in TypeScript with type definitions included
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
npm install greybel-interpreter
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { Interpreter, HandlerContainer } from 'greybel-interpreter';
|
|
30
|
+
|
|
31
|
+
const interpreter = new Interpreter({
|
|
32
|
+
target: '/path/to/main.src',
|
|
33
|
+
handler: new HandlerContainer(),
|
|
34
|
+
environmentVariables: new Map([['MY_VAR', '123']])
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await interpreter.run();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Custom intrinsics
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import {
|
|
44
|
+
Interpreter,
|
|
45
|
+
CustomFunction,
|
|
46
|
+
CustomString,
|
|
47
|
+
DefaultType,
|
|
48
|
+
ObjectValue
|
|
49
|
+
} from 'greybel-interpreter';
|
|
50
|
+
|
|
51
|
+
const api = new ObjectValue();
|
|
52
|
+
|
|
53
|
+
api.set(
|
|
54
|
+
new CustomString('exit'),
|
|
55
|
+
CustomFunction.createExternal('exit', (fnCtx, self, args) => {
|
|
56
|
+
interpreter.exit();
|
|
57
|
+
return Promise.resolve(DefaultType.Void);
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const interpreter = new Interpreter({
|
|
62
|
+
target: '/path/to/main.src',
|
|
63
|
+
api
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await interpreter.run();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Testing
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
npm test
|
|
73
|
+
```
|
|
@@ -40,8 +40,8 @@ class Context {
|
|
|
40
40
|
return {
|
|
41
41
|
name: name !== null && name !== void 0 ? name : node.type,
|
|
42
42
|
path: target,
|
|
43
|
-
start: node.
|
|
44
|
-
end: node.
|
|
43
|
+
start: { line: node.startLine, character: node.startChar },
|
|
44
|
+
end: { line: node.endLine, character: node.endChar }
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
getInternalLocation() {
|
|
@@ -91,7 +91,7 @@ class BytecodeExpressionGenerator {
|
|
|
91
91
|
case greybel_core_1.ASTType.FeatureLineExpression:
|
|
92
92
|
this.context.pushCode({
|
|
93
93
|
op: instruction_1.OpCode.PUSH,
|
|
94
|
-
value: new number_1.CustomNumber(node.
|
|
94
|
+
value: new number_1.CustomNumber(node.startLine)
|
|
95
95
|
}, node);
|
|
96
96
|
return;
|
|
97
97
|
case greybel_core_1.ASTType.FeatureFileExpression:
|
|
@@ -100,13 +100,11 @@ class BytecodeExpressionGenerator {
|
|
|
100
100
|
value: new string_1.CustomString((0, path_1.basename)(this.context.target.peek()))
|
|
101
101
|
}, node);
|
|
102
102
|
return;
|
|
103
|
-
case miniscript_core_1.ASTType.Comment:
|
|
104
|
-
return;
|
|
105
103
|
case miniscript_core_1.ASTType.ComparisonGroupExpression:
|
|
106
104
|
yield this.processComparisonGroupExpression(node);
|
|
107
105
|
return;
|
|
108
106
|
default: {
|
|
109
|
-
const range = new miniscript_core_1.ASTRange(node.
|
|
107
|
+
const range = new miniscript_core_1.ASTRange([node.startLine, node.startChar], [node.endLine, node.endChar]);
|
|
110
108
|
throw new error_1.PrepareError(`Unexpected AST type ${node.type}`, {
|
|
111
109
|
target: this.context.target.peek(),
|
|
112
110
|
range
|
|
@@ -486,7 +484,7 @@ class BytecodeExpressionGenerator {
|
|
|
486
484
|
const importTarget = yield this.context.handler.resourceHandler.getTargetRelativeTo(currentTarget, node.path);
|
|
487
485
|
const content = yield this.context.handler.resourceHandler.get(importTarget);
|
|
488
486
|
if (content == null) {
|
|
489
|
-
const range = new miniscript_core_1.ASTRange(node.
|
|
487
|
+
const range = new miniscript_core_1.ASTRange([node.startLine, node.startChar], [node.endLine, node.endChar]);
|
|
490
488
|
throw new error_1.PrepareError(`Cannot find injection "${currentTarget}"`, {
|
|
491
489
|
target: currentTarget,
|
|
492
490
|
range
|
|
@@ -531,7 +529,7 @@ BytecodeExpressionGenerator.binaryExpressionToOp = {
|
|
|
531
529
|
[miniscript_core_1.Operator.GreaterThanOrEqual]: instruction_1.OpCode.GREATER_THAN_OR_EQUAL,
|
|
532
530
|
[miniscript_core_1.Operator.Plus]: instruction_1.OpCode.ADD,
|
|
533
531
|
[miniscript_core_1.Operator.Minus]: instruction_1.OpCode.SUB,
|
|
534
|
-
[miniscript_core_1.Operator.
|
|
532
|
+
[miniscript_core_1.Operator.Asterisk]: instruction_1.OpCode.MUL,
|
|
535
533
|
[miniscript_core_1.Operator.Slash]: instruction_1.OpCode.DIV,
|
|
536
534
|
[miniscript_core_1.Operator.Modulo]: instruction_1.OpCode.MOD,
|
|
537
535
|
[miniscript_core_1.Operator.Power]: instruction_1.OpCode.POW,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ASTPosition } from 'miniscript-core';
|
|
2
1
|
import { CustomValue } from '../types/base';
|
|
3
2
|
import { CustomFunctionCallback } from '../types/function';
|
|
4
3
|
import { CustomString } from '../types/string';
|
|
@@ -70,8 +69,14 @@ export interface FunctionDefinitionInstructionArgument {
|
|
|
70
69
|
export type SourceLocation = {
|
|
71
70
|
name: string;
|
|
72
71
|
path: string;
|
|
73
|
-
start:
|
|
74
|
-
|
|
72
|
+
start: {
|
|
73
|
+
line: number;
|
|
74
|
+
character: number;
|
|
75
|
+
};
|
|
76
|
+
end: {
|
|
77
|
+
line: number;
|
|
78
|
+
character: number;
|
|
79
|
+
};
|
|
75
80
|
};
|
|
76
81
|
export interface BaseInstruction {
|
|
77
82
|
op: OpCode;
|
|
@@ -109,7 +109,6 @@ class BytecodeStatementGenerator {
|
|
|
109
109
|
yield this.processForGenericStatement(node);
|
|
110
110
|
return;
|
|
111
111
|
case miniscript_core_1.ASTType.EmptyExpression:
|
|
112
|
-
case miniscript_core_1.ASTType.Comment:
|
|
113
112
|
return;
|
|
114
113
|
case greybel_core_1.ASTType.FeatureInjectExpression:
|
|
115
114
|
return;
|
|
@@ -125,7 +124,7 @@ class BytecodeStatementGenerator {
|
|
|
125
124
|
yield this.processDebuggerExpression(node);
|
|
126
125
|
return;
|
|
127
126
|
default: {
|
|
128
|
-
const range = new miniscript_core_1.ASTRange(node.
|
|
127
|
+
const range = new miniscript_core_1.ASTRange([node.startLine, node.startChar], [node.endLine, node.endChar]);
|
|
129
128
|
throw new error_1.PrepareError(`Unexpected AST type ${node.type}`, {
|
|
130
129
|
target: this.context.target.peek(),
|
|
131
130
|
range
|
|
@@ -633,7 +632,7 @@ class BytecodeStatementGenerator {
|
|
|
633
632
|
}
|
|
634
633
|
throw new error_1.PrepareError(err.message, {
|
|
635
634
|
target: path,
|
|
636
|
-
range: new miniscript_core_1.ASTRange(node.
|
|
635
|
+
range: new miniscript_core_1.ASTRange([node.startLine, node.startChar], [node.endLine, node.endChar])
|
|
637
636
|
}, err);
|
|
638
637
|
}
|
|
639
638
|
});
|
|
@@ -643,13 +642,13 @@ class BytecodeStatementGenerator {
|
|
|
643
642
|
const currentTarget = this.context.target.peek();
|
|
644
643
|
const importTarget = yield this.context.handler.resourceHandler.getTargetRelativeTo(currentTarget, node.path);
|
|
645
644
|
if (this.context.target.includes(importTarget)) {
|
|
646
|
-
console.warn(`Found circular dependency between "${currentTarget}" and "${importTarget}" at line ${node.
|
|
645
|
+
console.warn(`Found circular dependency between "${currentTarget}" and "${importTarget}" at line ${node.startLine}. Using noop instead to prevent overflow.`);
|
|
647
646
|
return;
|
|
648
647
|
}
|
|
649
648
|
if (!this.context.imports.has(importTarget)) {
|
|
650
649
|
const code = yield this.context.handler.resourceHandler.get(importTarget);
|
|
651
650
|
if (code == null) {
|
|
652
|
-
const range = new miniscript_core_1.ASTRange(node.
|
|
651
|
+
const range = new miniscript_core_1.ASTRange([node.startLine, node.startChar], [node.endLine, node.endChar]);
|
|
653
652
|
throw new error_1.PrepareError(`Cannot find import "${currentTarget}"`, {
|
|
654
653
|
target: currentTarget,
|
|
655
654
|
range
|
|
@@ -679,15 +678,18 @@ class BytecodeStatementGenerator {
|
|
|
679
678
|
}
|
|
680
679
|
processIncludeExpression(node) {
|
|
681
680
|
return __awaiter(this, void 0, void 0, function* () {
|
|
681
|
+
if (node.typeOnly) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
682
684
|
const currentTarget = this.context.target.peek();
|
|
683
685
|
const importTarget = yield this.context.handler.resourceHandler.getTargetRelativeTo(currentTarget, node.path);
|
|
684
686
|
if (this.context.target.includes(importTarget)) {
|
|
685
|
-
console.warn(`Found circular dependency between "${currentTarget}" and "${importTarget}" at line ${node.
|
|
687
|
+
console.warn(`Found circular dependency between "${currentTarget}" and "${importTarget}" at line ${node.startLine}. Using noop instead to prevent overflow.`);
|
|
686
688
|
return;
|
|
687
689
|
}
|
|
688
690
|
const code = yield this.context.handler.resourceHandler.get(importTarget);
|
|
689
691
|
if (code == null) {
|
|
690
|
-
const range = new miniscript_core_1.ASTRange(node.
|
|
692
|
+
const range = new miniscript_core_1.ASTRange([node.startLine, node.startChar], [node.endLine, node.endChar]);
|
|
691
693
|
throw new error_1.PrepareError(`Cannot find import "${currentTarget}"`, {
|
|
692
694
|
target: currentTarget,
|
|
693
695
|
range
|
|
@@ -705,7 +707,7 @@ class BytecodeStatementGenerator {
|
|
|
705
707
|
}
|
|
706
708
|
throw new error_1.PrepareError(err.message, {
|
|
707
709
|
target: importTarget,
|
|
708
|
-
range: new miniscript_core_1.ASTRange(node.
|
|
710
|
+
range: new miniscript_core_1.ASTRange([node.startLine, node.startChar], [node.endLine, node.endChar])
|
|
709
711
|
}, err);
|
|
710
712
|
}
|
|
711
713
|
});
|
|
@@ -10,9 +10,13 @@ export interface BytecodeConverterOptions {
|
|
|
10
10
|
handler: HandlerContainer;
|
|
11
11
|
debugMode?: boolean;
|
|
12
12
|
context?: Context;
|
|
13
|
+
environmentVariables?: Map<string, string>;
|
|
14
|
+
strictMode?: boolean;
|
|
13
15
|
}
|
|
14
16
|
export declare class BytecodeGenerator {
|
|
15
17
|
protected context: Context;
|
|
18
|
+
protected environmentVariables: Map<string, string>;
|
|
19
|
+
protected strictMode: boolean;
|
|
16
20
|
constructor(options: BytecodeConverterOptions);
|
|
17
21
|
compile(code: string): Promise<BytecodeCompileResult>;
|
|
18
22
|
}
|
|
@@ -17,7 +17,10 @@ const greybel_core_1 = require("greybel-core");
|
|
|
17
17
|
const error_1 = require("./utils/error");
|
|
18
18
|
const parse = function (code) {
|
|
19
19
|
try {
|
|
20
|
-
const parser = new greybel_core_1.Parser(code
|
|
20
|
+
const parser = new greybel_core_1.Parser(code, {
|
|
21
|
+
environmentVariables: this.environmentVariables,
|
|
22
|
+
strictMode: this.strictMode
|
|
23
|
+
});
|
|
21
24
|
return parser.parseChunk();
|
|
22
25
|
}
|
|
23
26
|
catch (err) {
|
|
@@ -34,12 +37,14 @@ const parse = function (code) {
|
|
|
34
37
|
};
|
|
35
38
|
class BytecodeGenerator {
|
|
36
39
|
constructor(options) {
|
|
37
|
-
var _a;
|
|
40
|
+
var _a, _b, _c;
|
|
38
41
|
this.context = (_a = options.context) !== null && _a !== void 0 ? _a : new context_1.Context({
|
|
39
42
|
target: options.target,
|
|
40
43
|
handler: options.handler,
|
|
41
44
|
debugMode: options.debugMode
|
|
42
45
|
});
|
|
46
|
+
this.environmentVariables = (_b = options.environmentVariables) !== null && _b !== void 0 ? _b : new Map();
|
|
47
|
+
this.strictMode = (_c = options.strictMode) !== null && _c !== void 0 ? _c : false;
|
|
43
48
|
}
|
|
44
49
|
compile(code) {
|
|
45
50
|
return __awaiter(this, void 0, void 0, function* () {
|
package/dist/interpreter.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface InterpreterOptions {
|
|
|
17
17
|
debugger?: Debugger;
|
|
18
18
|
debugMode?: boolean;
|
|
19
19
|
environmentVariables?: Map<string, string>;
|
|
20
|
+
strictMode?: boolean;
|
|
20
21
|
}
|
|
21
22
|
export interface InterpreterRunOptions {
|
|
22
23
|
customCode?: string;
|
|
@@ -27,6 +28,7 @@ export declare class Interpreter extends EventEmitter {
|
|
|
27
28
|
api: ObjectValue;
|
|
28
29
|
params: Array<string>;
|
|
29
30
|
environmentVariables: Map<string, string>;
|
|
31
|
+
strictMode: boolean;
|
|
30
32
|
handler: HandlerContainer;
|
|
31
33
|
debugger: Debugger;
|
|
32
34
|
debugMode: boolean;
|
package/dist/interpreter.js
CHANGED
|
@@ -28,7 +28,7 @@ exports.PARAMS_PROPERTY = new string_1.CustomString('params');
|
|
|
28
28
|
exports.IS_GREYBEL_PROPERTY = new string_1.CustomString('IS_GREYBEL');
|
|
29
29
|
class Interpreter extends events_1.EventEmitter {
|
|
30
30
|
constructor(options = {}) {
|
|
31
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
31
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
32
32
|
super();
|
|
33
33
|
this.handler = (_a = options.handler) !== null && _a !== void 0 ? _a : new handler_container_1.HandlerContainer();
|
|
34
34
|
this.debugger = (_b = options.debugger) !== null && _b !== void 0 ? _b : new vm_1.Debugger();
|
|
@@ -36,10 +36,11 @@ class Interpreter extends events_1.EventEmitter {
|
|
|
36
36
|
this.api = (_c = options.api) !== null && _c !== void 0 ? _c : new object_value_1.ObjectValue();
|
|
37
37
|
this.params = (_d = options.params) !== null && _d !== void 0 ? _d : [];
|
|
38
38
|
this.environmentVariables = (_e = options.environmentVariables) !== null && _e !== void 0 ? _e : new Map();
|
|
39
|
-
this.
|
|
39
|
+
this.strictMode = (_f = options.strictMode) !== null && _f !== void 0 ? _f : false;
|
|
40
|
+
this.debugMode = (_g = options.debugMode) !== null && _g !== void 0 ? _g : false;
|
|
40
41
|
this.apiContext = null;
|
|
41
42
|
this.globalContext = null;
|
|
42
|
-
this.setTarget((
|
|
43
|
+
this.setTarget((_h = options.target) !== null && _h !== void 0 ? _h : 'unknown');
|
|
43
44
|
}
|
|
44
45
|
setTarget(target) {
|
|
45
46
|
if (this.vm !== null && this.vm.isPending()) {
|
|
@@ -73,7 +74,9 @@ class Interpreter extends events_1.EventEmitter {
|
|
|
73
74
|
return __awaiter(this, void 0, void 0, function* () {
|
|
74
75
|
const bytecodeGenerator = new bytecode_generator_1.BytecodeGenerator({
|
|
75
76
|
target: 'injected',
|
|
76
|
-
handler: this.handler
|
|
77
|
+
handler: this.handler,
|
|
78
|
+
environmentVariables: this.environmentVariables,
|
|
79
|
+
strictMode: this.strictMode
|
|
77
80
|
});
|
|
78
81
|
const result = yield bytecodeGenerator.compile(code);
|
|
79
82
|
const vm = new vm_1.VM({
|
|
@@ -171,7 +174,9 @@ class Interpreter extends events_1.EventEmitter {
|
|
|
171
174
|
const bytecodeConverter = new bytecode_generator_1.BytecodeGenerator({
|
|
172
175
|
target: this.target,
|
|
173
176
|
handler: this.handler,
|
|
174
|
-
debugMode: this.debugMode
|
|
177
|
+
debugMode: this.debugMode,
|
|
178
|
+
environmentVariables: this.environmentVariables,
|
|
179
|
+
strictMode: this.strictMode
|
|
175
180
|
});
|
|
176
181
|
const bytecode = yield bytecodeConverter.compile(code);
|
|
177
182
|
this.initVM(bytecode, vmOptions);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "greybel-interpreter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Interpreter",
|
|
5
5
|
"main": "dist/index",
|
|
6
6
|
"typings": "dist/index",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"typescript": "^5.0.4"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"greybel-core": "~
|
|
53
|
+
"greybel-core": "~3.0.1",
|
|
54
54
|
"hyperid": "^3.2.0",
|
|
55
55
|
"imurmurhash": "^0.1.4",
|
|
56
56
|
"non-blocking-schedule": "^0.2.0"
|