@teasel/parser 0.0.0 → 0.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 +138 -2
- package/api.js +185 -0
- package/binding.cjs +705 -0
- package/binding.d.ts +10 -0
- package/decode.js +282 -0
- package/identifier.js +218 -0
- package/index.d.ts +250 -0
- package/index.js +25 -0
- package/package.json +67 -10
- package/teasel.wasm +0 -0
- package/wasm.d.ts +1 -0
- package/wasm.js +62 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import type { Expression, Identifier, Node, Pattern, Program, SourceLocation, Statement } from 'estree';
|
|
2
|
+
|
|
3
|
+
export interface Options {
|
|
4
|
+
/**
|
|
5
|
+
* The grammar of a host language the whole source is a document of: a template language
|
|
6
|
+
* with JavaScript inside it. The program entry then answers with the document's root, the
|
|
7
|
+
* host's own nodes around the JavaScript ones, in one tree; the other entries read
|
|
8
|
+
* JavaScript at an offset as before. TypeScript turns on by what the grammar says of a
|
|
9
|
+
* script tag.
|
|
10
|
+
*/
|
|
11
|
+
host?: string;
|
|
12
|
+
/** `script` by default, as in acorn. */
|
|
13
|
+
sourceType?: 'script' | 'module';
|
|
14
|
+
/**
|
|
15
|
+
* Parse TypeScript. `'erase'` parses it and emits JavaScript: annotations, type-only
|
|
16
|
+
* declarations and imports go, assertions give way to their expression, and what erasure
|
|
17
|
+
* cannot express (enums, namespaces with values, parameter properties, `export =`, `import =`)
|
|
18
|
+
* stays in the tree and is listed as `typescript` on the answer, as are the proposals
|
|
19
|
+
* JavaScript itself has: decorators and accessor fields (`AccessorProperty`).
|
|
20
|
+
*/
|
|
21
|
+
typescript?: boolean | 'erase';
|
|
22
|
+
/**
|
|
23
|
+
* Which decorators are read. 'legacy' refuses decorators on private elements, class
|
|
24
|
+
* expressions and their members; 'proposal' refuses parameter decorators and decorators
|
|
25
|
+
* on abstract or declared fields. Unset reads both syntaxes.
|
|
26
|
+
*/
|
|
27
|
+
decorators?: 'legacy' | 'proposal';
|
|
28
|
+
/** Attach `leadingComments`, `trailingComments` and `innerComments` to nodes, and list every comment read as `comments` on the answer. */
|
|
29
|
+
comments?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Scope analysis: the answer lists `scopes`, `bindings` and `references`, and `scopeOf`,
|
|
32
|
+
* `bindingOf` and `referenceOf` answer for a node. The tree itself carries nothing, and a copy
|
|
33
|
+
* of a node carries no facts. TypeScript type positions bind nothing.
|
|
34
|
+
*/
|
|
35
|
+
scopes?: boolean;
|
|
36
|
+
/** Add `loc` with line and column to every node, as in acorn; off by default. */
|
|
37
|
+
locations?: boolean;
|
|
38
|
+
/** Mark a node the source wraps in parens with `parenthesized: true`, absent otherwise. */
|
|
39
|
+
parenthesized?: boolean;
|
|
40
|
+
allowReturnOutsideFunction?: boolean;
|
|
41
|
+
allowAwaitOutsideFunction?: boolean;
|
|
42
|
+
allowSuperOutsideMethod?: boolean;
|
|
43
|
+
allowUndeclaredExports?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* List syntax errors on the answer as `errors` instead of throwing the first: a missing
|
|
46
|
+
* operand, name or pattern is an `Identifier` named `''` of no width where it was expected,
|
|
47
|
+
* and a statement or entry that cannot be read is skipped to the next stop token or
|
|
48
|
+
* unmatched closing bracket, an empty identifier standing for it. Placeholders are neither
|
|
49
|
+
* bindings nor references.
|
|
50
|
+
*/
|
|
51
|
+
errorRecovery?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Thrown for a syntax error. `code` names what went wrong, for a host to branch on, and
|
|
56
|
+
* `message` says it in words, without a position. An error at the token being read spans it
|
|
57
|
+
* with `pos` and `end`; one reported elsewhere, at a declaration seen earlier say, has `end`
|
|
58
|
+
* equal to `pos`. `unexpected_eof` is the end of what was parsed: the `end` the parse was given,
|
|
59
|
+
* else the end of the source. A bad offset from the host is an `invalid_request` without a `loc`.
|
|
60
|
+
*/
|
|
61
|
+
export interface ParseError extends SyntaxError {
|
|
62
|
+
code: string;
|
|
63
|
+
pos: number;
|
|
64
|
+
end: number;
|
|
65
|
+
loc?: { line: number; column: number };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** A scope, as one of `scopes` on the answer. */
|
|
69
|
+
export interface Scope {
|
|
70
|
+
kind:
|
|
71
|
+
| 'module'
|
|
72
|
+
| 'script'
|
|
73
|
+
| 'function'
|
|
74
|
+
| 'function-name'
|
|
75
|
+
| 'class'
|
|
76
|
+
| 'block'
|
|
77
|
+
| 'catch'
|
|
78
|
+
| 'for'
|
|
79
|
+
| 'switch'
|
|
80
|
+
| 'static-block'
|
|
81
|
+
| 'with'
|
|
82
|
+
| 'namespace'
|
|
83
|
+
| 'enum'
|
|
84
|
+
| 'fragment';
|
|
85
|
+
/** The node that opens it; null for a function-name scope and for the scope around a parameter list parsed on its own. */
|
|
86
|
+
node: Node | null;
|
|
87
|
+
parent: Scope | null;
|
|
88
|
+
/** An `await` or `for await` runs directly in it, no function around; only a program or fragment scope can say so. */
|
|
89
|
+
topLevelAwait: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** A binding, as one of `bindings` on the answer. */
|
|
93
|
+
export interface Binding {
|
|
94
|
+
name: string;
|
|
95
|
+
kind:
|
|
96
|
+
| 'var'
|
|
97
|
+
| 'let'
|
|
98
|
+
| 'const'
|
|
99
|
+
| 'using'
|
|
100
|
+
| 'await using'
|
|
101
|
+
| 'function'
|
|
102
|
+
| 'class'
|
|
103
|
+
| 'param'
|
|
104
|
+
| 'catch'
|
|
105
|
+
| 'import'
|
|
106
|
+
| 'function-name'
|
|
107
|
+
| 'class-name'
|
|
108
|
+
| 'arguments'
|
|
109
|
+
| 'enum'
|
|
110
|
+
| 'enum-member'
|
|
111
|
+
| 'namespace'
|
|
112
|
+
| 'pattern';
|
|
113
|
+
scope: Scope;
|
|
114
|
+
/** The identifier that declares it; null for `arguments`. */
|
|
115
|
+
node: Identifier | null;
|
|
116
|
+
/** What declares it: the declarator, function, class, import specifier, catch clause or enum, as eslint-scope's definition node; null for `arguments` and for a pattern or parameter list parsed on its own. */
|
|
117
|
+
declaration: Node | null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** A piece of JavaScript a host read on its own, as one of `roots` on a document's answer, with what the tables hold for it. */
|
|
121
|
+
export interface Root {
|
|
122
|
+
node: Node;
|
|
123
|
+
/** The scope the piece sits in. */
|
|
124
|
+
scope: Scope;
|
|
125
|
+
/** The scopes opened inside it, the bindings declared and the references made there. */
|
|
126
|
+
scopes: Scope[];
|
|
127
|
+
bindings: Binding[];
|
|
128
|
+
references: Reference[];
|
|
129
|
+
}
|
|
130
|
+
/** A reference, as one of `references` on the answer. */
|
|
131
|
+
export interface Reference {
|
|
132
|
+
node: Identifier;
|
|
133
|
+
/** The scope the reference is made from. */
|
|
134
|
+
scope: Scope;
|
|
135
|
+
/** Null for a global. */
|
|
136
|
+
binding: Binding | null;
|
|
137
|
+
/** The identifier is assigned to, updated or bound by a destructuring assignment. */
|
|
138
|
+
write: boolean;
|
|
139
|
+
/** A member of the identifier's value is assigned to, updated or deleted. */
|
|
140
|
+
mutate: boolean;
|
|
141
|
+
/** The identifier's value is read: every reference but a plain assignment's target or a destructuring one's; a compound assignment or an update reads and writes. */
|
|
142
|
+
read: boolean;
|
|
143
|
+
/** What a write assigns: the right side of the assignment or the iterated expression of a `for-in` or `for-of`, as eslint-scope's `writeExpr`; null for an update. */
|
|
144
|
+
writeExpr: Expression | null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** The node `node` is a child of; undefined for the root of an answer. A literal's `regex` and a template element's `value` are not nodes and have none. */
|
|
148
|
+
export function parentOf(node: Node): Node | undefined;
|
|
149
|
+
/** With `scopes`: the scope `node` opens, when it opens one. */
|
|
150
|
+
export function scopeOf(node: Node): Scope | undefined;
|
|
151
|
+
/** With `scopes`: what an identifier declares or refers to; null for a global, undefined when it names no value, a property key say. */
|
|
152
|
+
export function bindingOf(node: Node): Binding | null | undefined;
|
|
153
|
+
/** With `scopes`: the reference an identifier makes, with its `write` and `mutate`; a global's too, which no binding lists. */
|
|
154
|
+
export function referenceOf(node: Node): Reference | undefined;
|
|
155
|
+
|
|
156
|
+
/** A range of the source, with `loc` when `locations` is on. */
|
|
157
|
+
export interface Span {
|
|
158
|
+
start: number;
|
|
159
|
+
end: number;
|
|
160
|
+
loc?: { start: { line: number; column: number }; end: { line: number; column: number } };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface Comment extends Span {
|
|
164
|
+
type: 'Line' | 'Block';
|
|
165
|
+
value: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** A node erasure left in place, by type. */
|
|
169
|
+
export interface Kept extends Span {
|
|
170
|
+
type: string;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** A recovered error: what the thrown `SyntaxError` carries, as a plain object. */
|
|
174
|
+
export type Recovered = Pick<ParseError, 'code' | 'message' | 'pos' | 'end'> & { loc: { line: number; column: number } };
|
|
175
|
+
|
|
176
|
+
/** What a parse returns: the node, or the patterns of a parameter list, and what the options add; a key is there exactly when its option is on. */
|
|
177
|
+
export interface Parsed<T> {
|
|
178
|
+
node: T;
|
|
179
|
+
/** The offset after everything the parse consumed: the node, its closing parens and the comments after it; a program's is the end it was given. */
|
|
180
|
+
end: number;
|
|
181
|
+
/** Every comment read, in source order; with `comments`. */
|
|
182
|
+
comments?: Comment[];
|
|
183
|
+
/** What erasure left in place; with `typescript: 'erase'`. */
|
|
184
|
+
typescript?: Kept[];
|
|
185
|
+
/** The errors recovered from, in source order; with `errorRecovery`. */
|
|
186
|
+
errors?: Recovered[];
|
|
187
|
+
/** With `scopes`. */
|
|
188
|
+
scopes?: Scope[];
|
|
189
|
+
bindings?: Binding[];
|
|
190
|
+
references?: Reference[];
|
|
191
|
+
/** With `scopes`, for a document read by a host grammar: its pieces of JavaScript in source order. */
|
|
192
|
+
roots?: Root[];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* What a parse reads: a program, or what a host embedding JavaScript in a larger syntax reads at
|
|
197
|
+
* a point of it. A type parameter list `<...>` is TypeScript only, `not_typescript` otherwise.
|
|
198
|
+
*/
|
|
199
|
+
export type Entry = 'program' | 'expression' | 'pattern' | 'params' | 'statement' | 'typeParameters';
|
|
200
|
+
|
|
201
|
+
export interface At {
|
|
202
|
+
/** Where the source is cut, a UTF-16 offset; the end of the source by default. A program reads to it. */
|
|
203
|
+
end?: number;
|
|
204
|
+
/**
|
|
205
|
+
* The host's own tokens, words or punctuators, that follow what is parsed. One read outside
|
|
206
|
+
* every bracket the parse opened, where the expression could end, ends it: `,` ends an
|
|
207
|
+
* expression before a sequence would, and `/>` is never a division. A `then` after `.` is a
|
|
208
|
+
* property name. A TypeScript `as` is the host's unless another `as` follows the assertion,
|
|
209
|
+
* so `xs as T[] as item` ends after the type.
|
|
210
|
+
*/
|
|
211
|
+
stopAt?: string[];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A node of a host language, as its grammar names the type and the fields; the JavaScript under
|
|
216
|
+
* it is ESTree.
|
|
217
|
+
*/
|
|
218
|
+
export interface HostNode {
|
|
219
|
+
type: string;
|
|
220
|
+
start: number;
|
|
221
|
+
end: number;
|
|
222
|
+
loc?: SourceLocation;
|
|
223
|
+
[field: string]: unknown;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* A source kept with its options: the parses out of it share the source copy and the position
|
|
228
|
+
* tables. Offsets are UTF-16, as in acorn; positions stay those of the whole source. `Root` is
|
|
229
|
+
* what the program entry answers with: the program, or the document's root with a `host`.
|
|
230
|
+
*/
|
|
231
|
+
export class Source<Root = Program> {
|
|
232
|
+
constructor(source: string, options?: Options);
|
|
233
|
+
/** The program starting at `offset`, the whole source by default; the document with a `host`. */
|
|
234
|
+
parse(entry?: 'program', offset?: number, at?: At): Parsed<Root>;
|
|
235
|
+
parse(entry: 'expression', offset: number, at?: At): Parsed<Expression>;
|
|
236
|
+
/** An assignment target: an identifier or a destructuring pattern. */
|
|
237
|
+
parse(entry: 'pattern', offset: number, at?: At): Parsed<Pattern>;
|
|
238
|
+
/** A parenthesized parameter list, as an arrow function's is read. */
|
|
239
|
+
parse(entry: 'params', offset: number, at?: At): Parsed<Pattern[]>;
|
|
240
|
+
parse(entry: 'statement', offset: number, at?: At): Parsed<Statement>;
|
|
241
|
+
/** A `TSTypeParameterDeclaration`. */
|
|
242
|
+
parse(entry: 'typeParameters', offset: number, at?: At): Parsed<Node>;
|
|
243
|
+
/** Releases what the engine holds for the source; the collector does it otherwise. */
|
|
244
|
+
free(): void;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Whether a code point can start an identifier, as acorn decides it. */
|
|
248
|
+
export function isIdentifierStart(code: number): boolean;
|
|
249
|
+
/** Whether a code point can continue an identifier. */
|
|
250
|
+
export function isIdentifierChar(code: number): boolean;
|
package/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// the source goes over as bytes: V8's encoder is 14x faster than napi reading a string
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { bind } from './api.js';
|
|
4
|
+
|
|
5
|
+
export { isIdentifierStart, isIdentifierChar } from './identifier.js';
|
|
6
|
+
export { scopeOf, bindingOf, referenceOf, parentOf } from './decode.js';
|
|
7
|
+
|
|
8
|
+
const native = createRequire(import.meta.url)('./binding.cjs');
|
|
9
|
+
const encoder = new TextEncoder();
|
|
10
|
+
let scratch = new Uint8Array(1 << 16);
|
|
11
|
+
|
|
12
|
+
function bytes(text) {
|
|
13
|
+
const size = text.length * 3;
|
|
14
|
+
if (scratch.length < size && size <= 1 << 20) scratch = new Uint8Array(size);
|
|
15
|
+
const room = size <= scratch.length ? scratch : new Uint8Array(size);
|
|
16
|
+
const { written } = encoder.encodeInto(text, room);
|
|
17
|
+
return room.subarray(0, written);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Source = bind({
|
|
21
|
+
create: (source, names, host) => new native.Source(bytes(source), names, host),
|
|
22
|
+
parse: (held, entry, offset, end, stop) => held.parse(entry, offset, end, stop),
|
|
23
|
+
constants: native.constants,
|
|
24
|
+
shapes: native.shapes,
|
|
25
|
+
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,68 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"name": "@teasel/parser",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A JavaScript and TypeScript parser in Rust. It answers in ESTree.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": "github:Nic-Polumeyv/teasel",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "index.js",
|
|
9
|
+
"types": "index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"node": {
|
|
13
|
+
"types": "./index.d.ts",
|
|
14
|
+
"default": "./index.js"
|
|
15
|
+
},
|
|
16
|
+
"default": {
|
|
17
|
+
"types": "./wasm.d.ts",
|
|
18
|
+
"default": "./wasm.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"./wasm": {
|
|
22
|
+
"types": "./wasm.d.ts",
|
|
23
|
+
"default": "./wasm.js"
|
|
24
|
+
},
|
|
25
|
+
"./teasel.wasm": "./teasel.wasm"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"index.js",
|
|
29
|
+
"index.d.ts",
|
|
30
|
+
"wasm.js",
|
|
31
|
+
"wasm.d.ts",
|
|
32
|
+
"api.js",
|
|
33
|
+
"decode.js",
|
|
34
|
+
"identifier.js",
|
|
35
|
+
"binding.cjs",
|
|
36
|
+
"binding.d.ts",
|
|
37
|
+
"teasel.wasm"
|
|
38
|
+
],
|
|
39
|
+
"napi": {
|
|
40
|
+
"binaryName": "teasel",
|
|
41
|
+
"targets": [
|
|
42
|
+
"x86_64-unknown-linux-gnu",
|
|
43
|
+
"aarch64-unknown-linux-gnu",
|
|
44
|
+
"x86_64-apple-darwin",
|
|
45
|
+
"aarch64-apple-darwin",
|
|
46
|
+
"x86_64-pc-windows-msvc"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "napi build --platform --release --manifest-path ../bindings/node/Cargo.toml --package-json-path package.json -o . --js binding.cjs --dts binding.d.ts",
|
|
51
|
+
"build:wasm": "cargo build --release -p teasel-wasm --target wasm32-unknown-unknown && cp ../target/wasm32-unknown-unknown/release/teasel_wasm.wasm teasel.wasm",
|
|
52
|
+
"test": "bun test.js && bun test.js interpret",
|
|
53
|
+
"check": "node check.js"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@types/estree": "^1.0.6"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@napi-rs/cli": "^3.9.0"
|
|
60
|
+
},
|
|
61
|
+
"optionalDependencies": {
|
|
62
|
+
"@teasel/parser-linux-x64-gnu": "0.0.1",
|
|
63
|
+
"@teasel/parser-linux-arm64-gnu": "0.0.1",
|
|
64
|
+
"@teasel/parser-darwin-x64": "0.0.1",
|
|
65
|
+
"@teasel/parser-darwin-arm64": "0.0.1",
|
|
66
|
+
"@teasel/parser-win32-x64-msvc": "0.0.1"
|
|
67
|
+
}
|
|
68
|
+
}
|
package/teasel.wasm
ADDED
|
Binary file
|
package/wasm.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './index.js';
|
package/wasm.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { bind } from './api.js';
|
|
2
|
+
|
|
3
|
+
export { isIdentifierStart, isIdentifierChar } from './identifier.js';
|
|
4
|
+
export { scopeOf, bindingOf, referenceOf, parentOf } from './decode.js';
|
|
5
|
+
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const utf8 = new TextDecoder();
|
|
8
|
+
|
|
9
|
+
// `teasel.wasm` next to this file, read where there is a file system and fetched elsewhere
|
|
10
|
+
const url = new URL('./teasel.wasm', import.meta.url);
|
|
11
|
+
const { instance } =
|
|
12
|
+
url.protocol === 'file:'
|
|
13
|
+
? await WebAssembly.instantiate(await (await import('node:fs/promises')).readFile(url), {})
|
|
14
|
+
: await WebAssembly.instantiateStreaming(fetch(url), {});
|
|
15
|
+
/** @type {WebAssembly.Exports & Record<string, Function> & { memory: WebAssembly.Memory }} */
|
|
16
|
+
const wasm = /** @type {any} */ (instance.exports);
|
|
17
|
+
/** @type {string[]} */
|
|
18
|
+
let constants = [];
|
|
19
|
+
/** @type {number[]} */
|
|
20
|
+
let shapes = [];
|
|
21
|
+
let shapes_known = 0;
|
|
22
|
+
|
|
23
|
+
// the module takes the bytes over
|
|
24
|
+
function bytes(text) {
|
|
25
|
+
const capacity = text.length * 3;
|
|
26
|
+
const ptr = wasm.alloc(capacity);
|
|
27
|
+
const { written } = encoder.encodeInto(text, new Uint8Array(wasm.memory.buffer, ptr, capacity));
|
|
28
|
+
return [ptr, written, capacity];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function create(source, names, host) {
|
|
32
|
+
const handle = wasm.source_new(...bytes(source), ...bytes(names), ...bytes(host));
|
|
33
|
+
if (handle === 0) throw new Error(JSON.parse(text()).error.message);
|
|
34
|
+
return handle;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const text = () => utf8.decode(new Uint8Array(wasm.memory.buffer, wasm.text_ptr(), wasm.text_len()));
|
|
38
|
+
const words = () => new Uint32Array(wasm.memory.buffer, wasm.words_ptr(), wasm.words_len());
|
|
39
|
+
|
|
40
|
+
// the constants and shapes come first: writing them can grow the memory and detach a view taken before
|
|
41
|
+
function answer(status) {
|
|
42
|
+
if (status !== 0) return text();
|
|
43
|
+
if (words()[4] > constants.length) {
|
|
44
|
+
wasm.constants();
|
|
45
|
+
constants = JSON.parse(text());
|
|
46
|
+
}
|
|
47
|
+
if (words()[5] > shapes_known) {
|
|
48
|
+
shapes_known = words()[5];
|
|
49
|
+
wasm.shapes();
|
|
50
|
+
shapes = JSON.parse(text());
|
|
51
|
+
}
|
|
52
|
+
return words();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const Source = bind({
|
|
56
|
+
create,
|
|
57
|
+
// the words outlive the source: they sit in the answer buffer until the next parse
|
|
58
|
+
parse: (held, entry, offset, end, stop) => answer(wasm.source_parse(held, entry, offset, end ?? 0, end === undefined ? 0 : 1, ...bytes(stop))),
|
|
59
|
+
free: (held) => wasm.source_free(held),
|
|
60
|
+
constants: () => constants,
|
|
61
|
+
shapes: () => shapes,
|
|
62
|
+
});
|