@sanbus/galley-core 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 +15 -0
- package/build/builder.mjs +276 -0
- package/build/fixture.mjs +85 -0
- package/build/shim.mjs +216 -0
- package/dist/artifact.d.ts +38 -0
- package/dist/artifact.js +45 -0
- package/dist/artifact.js.map +1 -0
- package/dist/constants.d.ts +34 -0
- package/dist/constants.js +41 -0
- package/dist/constants.js.map +1 -0
- package/dist/diagnostic.d.ts +34 -0
- package/dist/diagnostic.js +28 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +38 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/names.d.ts +20 -0
- package/dist/names.js +29 -0
- package/dist/names.js.map +1 -0
- package/dist/node.d.ts +45 -0
- package/dist/node.js +137 -0
- package/dist/node.js.map +1 -0
- package/dist/port.d.ts +193 -0
- package/dist/port.js +14 -0
- package/dist/port.js.map +1 -0
- package/dist/procedures.d.ts +62 -0
- package/dist/procedures.js +154 -0
- package/dist/procedures.js.map +1 -0
- package/dist/session.d.ts +123 -0
- package/dist/session.js +599 -0
- package/dist/session.js.map +1 -0
- package/dist/text.d.ts +7 -0
- package/dist/text.js +16 -0
- package/dist/text.js.map +1 -0
- package/package.json +31 -0
- package/src/artifact.ts +62 -0
- package/src/constants.ts +47 -0
- package/src/diagnostic.ts +48 -0
- package/src/errors.ts +49 -0
- package/src/index.ts +53 -0
- package/src/node.ts +154 -0
- package/src/port.ts +213 -0
- package/src/procedures.ts +169 -0
- package/src/session.ts +747 -0
- package/src/text.ts +19 -0
package/dist/port.d.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Neutral FFI port: the single seam between the runtime-neutral core
|
|
3
|
+
* (`session.ts`, `node.ts`, `procedures.ts`) and each runtime adapter
|
|
4
|
+
* (Node/koffi, Bun/`bun:ffi`, Deno/`Deno.dlopen`).
|
|
5
|
+
*
|
|
6
|
+
* The port mirrors `bindings/c/galley.h`, but with structured returns
|
|
7
|
+
* instead of C out-parameters: adapters own all memory copying (bytes are
|
|
8
|
+
* already-copied `Uint8Array`s here, valid after the next parse) and all
|
|
9
|
+
* integer normalization (addresses are `bigint`, counts and statuses are
|
|
10
|
+
* `number`). Opaque native pointers (sessions, walkers, procedure args)
|
|
11
|
+
* cross the seam as `Handle` and are never inspected by the core.
|
|
12
|
+
*/
|
|
13
|
+
export type Handle = unknown;
|
|
14
|
+
/** `GalleyCOptions` fields as plain data; null selects library defaults. */
|
|
15
|
+
export interface SessionCOptions {
|
|
16
|
+
maxErrors: number;
|
|
17
|
+
recoveryWindow: number;
|
|
18
|
+
stackOverflowRecovery: number;
|
|
19
|
+
syntaxErrorStackDepth: number;
|
|
20
|
+
verbosity: number;
|
|
21
|
+
astPreallocationRatio: number;
|
|
22
|
+
astPreallocationCap: bigint;
|
|
23
|
+
}
|
|
24
|
+
/** One pre-order walker step. */
|
|
25
|
+
export interface WalkedStep {
|
|
26
|
+
node: bigint;
|
|
27
|
+
depth: number;
|
|
28
|
+
isSemanticError: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Flat bulk read of the most recent successful parse, one slot per node
|
|
32
|
+
* address. `parent` holds `INVALID_NODE` for the root, `firstChild`/`next`
|
|
33
|
+
* hold `INVALID_NODE` where the link does not exist, `variable` holds -1
|
|
34
|
+
* for nodes without a variable, and `spanStart`/`spanLen` are byte
|
|
35
|
+
* offsets into the parsed input. Parent, firstChild, and next alone
|
|
36
|
+
* describe the whole tree with no further calls.
|
|
37
|
+
*/
|
|
38
|
+
export interface TreeSnapshot {
|
|
39
|
+
count: number;
|
|
40
|
+
parent: BigUint64Array;
|
|
41
|
+
firstChild: BigUint64Array;
|
|
42
|
+
next: BigUint64Array;
|
|
43
|
+
childCount: Uint32Array;
|
|
44
|
+
variable: BigInt64Array;
|
|
45
|
+
spanStart: BigUint64Array;
|
|
46
|
+
spanLen: BigUint64Array;
|
|
47
|
+
}
|
|
48
|
+
/** Native dispatch callback installed by the adapter; receives a decoded hook name. */
|
|
49
|
+
export type DispatchHandler = (name: string, args: Handle) => void;
|
|
50
|
+
export interface FfiPort {
|
|
51
|
+
version(): string;
|
|
52
|
+
parserType(): number;
|
|
53
|
+
errorRecoveryMode(): number;
|
|
54
|
+
hasAst(): boolean;
|
|
55
|
+
hasProcedures(): boolean;
|
|
56
|
+
allowsNoAstTreeProcedures(): boolean;
|
|
57
|
+
sourceRetentionEnabled(): boolean;
|
|
58
|
+
hasPositionTracking(): boolean;
|
|
59
|
+
hasInputStreaming(): boolean;
|
|
60
|
+
usesVerbatim(): boolean;
|
|
61
|
+
stackOverflowRecoveryAvailable(): boolean;
|
|
62
|
+
symbolCount(): number;
|
|
63
|
+
variableCount(): number;
|
|
64
|
+
statusString(status: number): string | null;
|
|
65
|
+
/** Null handle on initialization failure (most commonly allocation failure). */
|
|
66
|
+
createSession(options: SessionCOptions | null): Handle;
|
|
67
|
+
destroySession(handle: Handle): void;
|
|
68
|
+
/** Negative status on failure. */
|
|
69
|
+
setMessageOverride(handle: Handle, name: Uint8Array, message: Uint8Array): number;
|
|
70
|
+
/** Bytes parsed, or a negative status code. */
|
|
71
|
+
parse(handle: Handle, data: Uint8Array): number;
|
|
72
|
+
/** Bytes parsed, or a negative status code. */
|
|
73
|
+
parseFile(handle: Handle, path: string): number;
|
|
74
|
+
/** End position of the most recent successful parse; null on failure. */
|
|
75
|
+
lastPosition(handle: Handle): [number, number] | null;
|
|
76
|
+
nodeCount(handle: Handle): number;
|
|
77
|
+
/** Negative status on failure (e.g. capacity exceeded). */
|
|
78
|
+
reserveNodes(handle: Handle, capacity: bigint): number;
|
|
79
|
+
nodeCapacity(handle: Handle): number;
|
|
80
|
+
rootNode(handle: Handle): bigint;
|
|
81
|
+
nodeValid(handle: Handle, node: bigint): boolean;
|
|
82
|
+
childCount(handle: Handle, node: bigint): number;
|
|
83
|
+
firstChild(handle: Handle, node: bigint): bigint;
|
|
84
|
+
lastChild(handle: Handle, node: bigint): bigint;
|
|
85
|
+
nextSibling(handle: Handle, node: bigint): bigint;
|
|
86
|
+
priorSibling(handle: Handle, node: bigint): bigint;
|
|
87
|
+
parent(handle: Handle, node: bigint): bigint;
|
|
88
|
+
/** Flat bulk read of the most recent successful parse (see `TreeSnapshot`). */
|
|
89
|
+
treeSnapshot(handle: Handle): TreeSnapshot;
|
|
90
|
+
/** Null without AST construction or on invalid arguments. */
|
|
91
|
+
walkerCreate(handle: Handle, node: bigint, skipSemanticErrors: boolean): Handle | null;
|
|
92
|
+
/** Null when the walk is done. */
|
|
93
|
+
walkerNext(walker: Handle): WalkedStep | null;
|
|
94
|
+
walkerSkipChildren(walker: Handle): void;
|
|
95
|
+
walkerDestroy(walker: Handle): void;
|
|
96
|
+
nodeSymbolName(handle: Handle, node: bigint): Uint8Array | null;
|
|
97
|
+
nodeText(handle: Handle, node: bigint): Uint8Array | null;
|
|
98
|
+
nodeSpan(handle: Handle, node: bigint): [bigint, bigint] | null;
|
|
99
|
+
nodeLineColumn(handle: Handle, node: bigint): [number, number] | null;
|
|
100
|
+
/** Raw variable index; -1 when the node has no variable. */
|
|
101
|
+
nodeVariableIndex(handle: Handle, node: bigint): number;
|
|
102
|
+
symbolNameAt(handle: Handle, index: number): Uint8Array | null;
|
|
103
|
+
symbolIsTerminal(handle: Handle, index: number): boolean;
|
|
104
|
+
variableNameAt(handle: Handle, index: number): Uint8Array | null;
|
|
105
|
+
hasDiagnostic(handle: Handle): boolean;
|
|
106
|
+
diagnosticKind(handle: Handle): number;
|
|
107
|
+
diagnosticMessage(handle: Handle): string | null;
|
|
108
|
+
diagnosticMessageAnsi(handle: Handle): string | null;
|
|
109
|
+
diagnosticPosition(handle: Handle): [number, number] | null;
|
|
110
|
+
diagnosticUnexpectedToken(handle: Handle): Uint8Array | null;
|
|
111
|
+
diagnosticExpectedCount(handle: Handle): number;
|
|
112
|
+
diagnosticExpectedAt(handle: Handle, index: number): Uint8Array | null;
|
|
113
|
+
diagnosticContextCount(handle: Handle): number;
|
|
114
|
+
diagnosticContextAt(handle: Handle, index: number): Uint8Array | null;
|
|
115
|
+
syntaxErrorCount(handle: Handle): number;
|
|
116
|
+
semanticErrorCount(handle: Handle): number;
|
|
117
|
+
/** (variable, message); null when there is no semantic diagnostic. */
|
|
118
|
+
diagnosticSemantic(handle: Handle): [string, string] | null;
|
|
119
|
+
/** (spaces, width); null when not an indentation diagnostic. */
|
|
120
|
+
diagnosticIndentation(handle: Handle): [number, number] | null;
|
|
121
|
+
diagnosticRecoveryKind(handle: Handle): number;
|
|
122
|
+
diagnosticRecoveryTerminal(handle: Handle): Uint8Array | null;
|
|
123
|
+
diagnosticRecoveryResume(handle: Handle): number | null;
|
|
124
|
+
diagnosticRecoveryLhsVariable(handle: Handle): string | null;
|
|
125
|
+
diagnosticRecoveryProduction(handle: Handle): [string, number] | null;
|
|
126
|
+
diagnosticRecoveryOccurrence(handle: Handle): [string, number, number, string] | null;
|
|
127
|
+
recordedDiagnosticCount(handle: Handle): number;
|
|
128
|
+
recordedDiagnosticKind(handle: Handle, diagIndex: number): number;
|
|
129
|
+
recordedDiagnosticPosition(handle: Handle, diagIndex: number): [number, number] | null;
|
|
130
|
+
recordedUnexpectedToken(handle: Handle, diagIndex: number): Uint8Array | null;
|
|
131
|
+
recordedDiagnosticMessage(handle: Handle, diagIndex: number): string | null;
|
|
132
|
+
recordedIndentation(handle: Handle, diagIndex: number): [number, number] | null;
|
|
133
|
+
recordedSemantic(handle: Handle, diagIndex: number): [string, string] | null;
|
|
134
|
+
recordedExpectedCount(handle: Handle, diagIndex: number): number;
|
|
135
|
+
recordedExpectedToken(handle: Handle, diagIndex: number, tokenIndex: number): Uint8Array | null;
|
|
136
|
+
recordedContextCount(handle: Handle, diagIndex: number): number;
|
|
137
|
+
recordedContextName(handle: Handle, diagIndex: number, contextIndex: number): Uint8Array | null;
|
|
138
|
+
recordedRecoveryKind(handle: Handle, diagIndex: number): number;
|
|
139
|
+
recordedRecoveryTerminal(handle: Handle, diagIndex: number): Uint8Array | null;
|
|
140
|
+
recordedRecoveryResume(handle: Handle, diagIndex: number): number | null;
|
|
141
|
+
recordedRecoveryLhsVariable(handle: Handle, diagIndex: number): string | null;
|
|
142
|
+
recordedRecoveryProduction(handle: Handle, diagIndex: number): [string, number] | null;
|
|
143
|
+
recordedRecoveryOccurrence(handle: Handle, diagIndex: number): [string, number, number, string] | null;
|
|
144
|
+
treeAppendChildren(handle: Handle, parent: bigint, first: bigint): number;
|
|
145
|
+
treeInsertBefore(handle: Handle, target: bigint, first: bigint): number;
|
|
146
|
+
treeInsertAfter(handle: Handle, target: bigint, first: bigint): number;
|
|
147
|
+
treeRemoveSiblings(handle: Handle, node: bigint, count: number): {
|
|
148
|
+
status: number;
|
|
149
|
+
head: bigint;
|
|
150
|
+
};
|
|
151
|
+
treeRemoveSelf(handle: Handle, node: bigint): {
|
|
152
|
+
status: number;
|
|
153
|
+
head: bigint;
|
|
154
|
+
};
|
|
155
|
+
treePromoteChildrenOverWrapper(handle: Handle, wrapper: bigint): {
|
|
156
|
+
status: number;
|
|
157
|
+
head: bigint;
|
|
158
|
+
};
|
|
159
|
+
treeCleanChildren(handle: Handle, node: bigint): {
|
|
160
|
+
status: number;
|
|
161
|
+
head: bigint;
|
|
162
|
+
};
|
|
163
|
+
treeUnlinkWrapper(handle: Handle, wrapper: bigint): number;
|
|
164
|
+
treeInsertChildrenAt(handle: Handle, parent: bigint, index: number, first: bigint): number;
|
|
165
|
+
treeRemoveChildrenAt(handle: Handle, parent: bigint, index: number, count: number): {
|
|
166
|
+
status: number;
|
|
167
|
+
head: bigint;
|
|
168
|
+
};
|
|
169
|
+
procCurrentNode(args: Handle): bigint;
|
|
170
|
+
procSetCurrentNode(args: Handle, node: bigint): void;
|
|
171
|
+
procDropSelf(args: Handle): number;
|
|
172
|
+
procDropChildren(args: Handle): number;
|
|
173
|
+
procDropIfEmpty(args: Handle): number;
|
|
174
|
+
procReplaceWithChildren(args: Handle): number;
|
|
175
|
+
procContextLine(args: Handle): number;
|
|
176
|
+
procContextColumn(args: Handle): number;
|
|
177
|
+
/** Running semantic-error total, or a negative status code. */
|
|
178
|
+
procReportSemanticError(args: Handle, message: Uint8Array): number;
|
|
179
|
+
/**
|
|
180
|
+
* Enables exactly `names` in the native procedure gates before a parse
|
|
181
|
+
* (selective dispatch): the adapter clears all gates, then enables each
|
|
182
|
+
* name. Missing symbols (C-procedure or stale libraries) are no-ops.
|
|
183
|
+
*/
|
|
184
|
+
syncProcedures(names: string[]): void;
|
|
185
|
+
/**
|
|
186
|
+
* Hook names in integer-ID order for the ID dispatch path. Queried once
|
|
187
|
+
* from the library (`galley_js_procedure_count` /
|
|
188
|
+
* `galley_js_procedure_name_ptr` / `galley_js_procedure_name_len`) and
|
|
189
|
+
* cached; empty when the library predates the query exports (C-procedure
|
|
190
|
+
* or stale libraries use the name-carrying dispatch instead).
|
|
191
|
+
*/
|
|
192
|
+
procedureNames(): string[];
|
|
193
|
+
}
|
package/dist/port.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Neutral FFI port: the single seam between the runtime-neutral core
|
|
3
|
+
* (`session.ts`, `node.ts`, `procedures.ts`) and each runtime adapter
|
|
4
|
+
* (Node/koffi, Bun/`bun:ffi`, Deno/`Deno.dlopen`).
|
|
5
|
+
*
|
|
6
|
+
* The port mirrors `bindings/c/galley.h`, but with structured returns
|
|
7
|
+
* instead of C out-parameters: adapters own all memory copying (bytes are
|
|
8
|
+
* already-copied `Uint8Array`s here, valid after the next parse) and all
|
|
9
|
+
* integer normalization (addresses are `bigint`, counts and statuses are
|
|
10
|
+
* `number`). Opaque native pointers (sessions, walkers, procedure args)
|
|
11
|
+
* cross the seam as `Handle` and are never inspected by the core.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=port.js.map
|
package/dist/port.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.js","sourceRoot":"","sources":["../src/port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-language procedure registry for the JavaScript bindings.
|
|
3
|
+
*
|
|
4
|
+
* Runtime-neutral: the registry, `ProcedureArguments`, and the dispatcher
|
|
5
|
+
* live here. Each adapter installs the dispatcher into its shared library
|
|
6
|
+
* through its own native callback (`galley_install_*_dispatch`) and calls
|
|
7
|
+
* {@link dispatchProcedure} with the decoded hook name.
|
|
8
|
+
*
|
|
9
|
+
* Hooks receive a `ProcedureArguments` object. Tree queries use
|
|
10
|
+
* `currentNode()` plus the ordinary `Node` methods (which call the port's
|
|
11
|
+
* node accessors on the parsing session).
|
|
12
|
+
*/
|
|
13
|
+
import type { Handle, FfiPort } from "./port.ts";
|
|
14
|
+
import { Node } from "./node.ts";
|
|
15
|
+
import type { Session } from "./session.ts";
|
|
16
|
+
export declare class ProcedureArguments {
|
|
17
|
+
#private;
|
|
18
|
+
constructor(args: Handle, session: Session | null, port: FfiPort);
|
|
19
|
+
get session(): Session | null;
|
|
20
|
+
currentNode(): Node | null;
|
|
21
|
+
setCurrentNode(node: Node | bigint): void;
|
|
22
|
+
dropSelf(): void;
|
|
23
|
+
dropChildren(): void;
|
|
24
|
+
dropIfEmpty(): void;
|
|
25
|
+
replaceWithChildren(): void;
|
|
26
|
+
currentLine(): number;
|
|
27
|
+
currentColumn(): number;
|
|
28
|
+
/**
|
|
29
|
+
* Record a semantic error on the current node and return the running
|
|
30
|
+
* total. Parsing continues; a syntax-clean parse with any semantic
|
|
31
|
+
* error fails with status -12.
|
|
32
|
+
*/
|
|
33
|
+
reportSemanticError(message: string): number;
|
|
34
|
+
}
|
|
35
|
+
export type HookFn = (args: ProcedureArguments) => void;
|
|
36
|
+
/** Records the Session whose parse is in flight so hooks can wrap Nodes. */
|
|
37
|
+
export declare function setParsingSession(session: Session | null): Session | null;
|
|
38
|
+
/** Session of the parse currently in flight, if any. */
|
|
39
|
+
export declare function getParsingSession(): Session | null;
|
|
40
|
+
/**
|
|
41
|
+
* Runs the registered hook for `name` (silent no-op when unregistered).
|
|
42
|
+
* Called by each adapter's native dispatch callback. Hook exceptions are
|
|
43
|
+
* logged and swallowed so a throwing hook never aborts the parse (mirrors
|
|
44
|
+
* Python's PyErr_Print behavior).
|
|
45
|
+
*/
|
|
46
|
+
export declare function dispatchProcedure(name: string, args: Handle, port: FfiPort): void;
|
|
47
|
+
/**
|
|
48
|
+
* Installs a single procedure hook. `fn` may be
|
|
49
|
+
* `(args: ProcedureArguments)=>void` or `()=>void`.
|
|
50
|
+
* Overwrites any existing entry for `name`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function installProcedure(name: string, fn: HookFn | (() => void)): void;
|
|
53
|
+
/**
|
|
54
|
+
* Scans `module` for exported procedure hooks (`reduction`, `reduction_*`,
|
|
55
|
+
* `hook_*`) and registers each function. Returns the number installed.
|
|
56
|
+
* Mirrors `bindings/python/_galley.c:2339` `install_procedures`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function installProcedures(module: Record<string, unknown>): number;
|
|
59
|
+
/** Removes all registered hooks; subsequent parses will be no-ops. */
|
|
60
|
+
export declare function clearProcedures(): void;
|
|
61
|
+
/** Returns currently registered procedure names. */
|
|
62
|
+
export declare function listProcedures(): string[];
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-language procedure registry for the JavaScript bindings.
|
|
3
|
+
*
|
|
4
|
+
* Runtime-neutral: the registry, `ProcedureArguments`, and the dispatcher
|
|
5
|
+
* live here. Each adapter installs the dispatcher into its shared library
|
|
6
|
+
* through its own native callback (`galley_install_*_dispatch`) and calls
|
|
7
|
+
* {@link dispatchProcedure} with the decoded hook name.
|
|
8
|
+
*
|
|
9
|
+
* Hooks receive a `ProcedureArguments` object. Tree queries use
|
|
10
|
+
* `currentNode()` plus the ordinary `Node` methods (which call the port's
|
|
11
|
+
* node accessors on the parsing session).
|
|
12
|
+
*/
|
|
13
|
+
import { INVALID_NODE } from "./constants.js";
|
|
14
|
+
import { encodeUtf8 } from "./text.js";
|
|
15
|
+
import { Node } from "./node.js";
|
|
16
|
+
export class ProcedureArguments {
|
|
17
|
+
#args;
|
|
18
|
+
#session;
|
|
19
|
+
#port;
|
|
20
|
+
constructor(args, session, port) {
|
|
21
|
+
this.#args = args;
|
|
22
|
+
this.#session = session;
|
|
23
|
+
this.#port = port;
|
|
24
|
+
}
|
|
25
|
+
get session() {
|
|
26
|
+
return this.#session;
|
|
27
|
+
}
|
|
28
|
+
currentNode() {
|
|
29
|
+
if (this.#session === null || this.#session.isClosed)
|
|
30
|
+
return null;
|
|
31
|
+
const address = this.#port.procCurrentNode(this.#args);
|
|
32
|
+
if (address === INVALID_NODE)
|
|
33
|
+
return null;
|
|
34
|
+
return new Node(this.#session, address);
|
|
35
|
+
}
|
|
36
|
+
setCurrentNode(node) {
|
|
37
|
+
const address = typeof node === "bigint" ? node : node.address;
|
|
38
|
+
this.#port.procSetCurrentNode(this.#args, address);
|
|
39
|
+
}
|
|
40
|
+
dropSelf() {
|
|
41
|
+
if (this.#port.procDropSelf(this.#args) < 0) {
|
|
42
|
+
throw new Error("galley_procedure_drop_self failed");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
dropChildren() {
|
|
46
|
+
if (this.#port.procDropChildren(this.#args) < 0) {
|
|
47
|
+
throw new Error("galley_procedure_drop_children failed");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
dropIfEmpty() {
|
|
51
|
+
if (this.#port.procDropIfEmpty(this.#args) < 0) {
|
|
52
|
+
throw new Error("galley_procedure_drop_if_empty failed");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
replaceWithChildren() {
|
|
56
|
+
if (this.#port.procReplaceWithChildren(this.#args) < 0) {
|
|
57
|
+
throw new Error("galley_procedure_replace_with_children failed");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
currentLine() {
|
|
61
|
+
return this.#port.procContextLine(this.#args);
|
|
62
|
+
}
|
|
63
|
+
currentColumn() {
|
|
64
|
+
return this.#port.procContextColumn(this.#args);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Record a semantic error on the current node and return the running
|
|
68
|
+
* total. Parsing continues; a syntax-clean parse with any semantic
|
|
69
|
+
* error fails with status -12.
|
|
70
|
+
*/
|
|
71
|
+
reportSemanticError(message) {
|
|
72
|
+
const status = this.#port.procReportSemanticError(this.#args, encodeUtf8(message));
|
|
73
|
+
if (status < 0)
|
|
74
|
+
throw new Error("galley_procedure_report_semantic_error failed");
|
|
75
|
+
return status;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
let currentSession = null;
|
|
79
|
+
/** Records the Session whose parse is in flight so hooks can wrap Nodes. */
|
|
80
|
+
export function setParsingSession(session) {
|
|
81
|
+
const previous = currentSession;
|
|
82
|
+
currentSession = session;
|
|
83
|
+
return previous;
|
|
84
|
+
}
|
|
85
|
+
/** Session of the parse currently in flight, if any. */
|
|
86
|
+
export function getParsingSession() {
|
|
87
|
+
return currentSession;
|
|
88
|
+
}
|
|
89
|
+
const registry = new Map();
|
|
90
|
+
function isProcedureName(name) {
|
|
91
|
+
return name === "reduction" || name.startsWith("reduction_") || name.startsWith("hook_");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Runs the registered hook for `name` (silent no-op when unregistered).
|
|
95
|
+
* Called by each adapter's native dispatch callback. Hook exceptions are
|
|
96
|
+
* logged and swallowed so a throwing hook never aborts the parse (mirrors
|
|
97
|
+
* Python's PyErr_Print behavior).
|
|
98
|
+
*/
|
|
99
|
+
export function dispatchProcedure(name, args, port) {
|
|
100
|
+
const fn = registry.get(name);
|
|
101
|
+
if (!fn)
|
|
102
|
+
return;
|
|
103
|
+
try {
|
|
104
|
+
if (fn.length === 0) {
|
|
105
|
+
fn();
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
fn(new ProcedureArguments(args, currentSession, port));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
console.error(`galley procedure ${name} threw:`, err);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Installs a single procedure hook. `fn` may be
|
|
117
|
+
* `(args: ProcedureArguments)=>void` or `()=>void`.
|
|
118
|
+
* Overwrites any existing entry for `name`.
|
|
119
|
+
*/
|
|
120
|
+
export function installProcedure(name, fn) {
|
|
121
|
+
if (typeof name !== "string" || name.length === 0)
|
|
122
|
+
throw new TypeError("procedure name must be non-empty string");
|
|
123
|
+
if (typeof fn !== "function")
|
|
124
|
+
throw new TypeError("procedure must be a function");
|
|
125
|
+
registry.set(name, fn);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Scans `module` for exported procedure hooks (`reduction`, `reduction_*`,
|
|
129
|
+
* `hook_*`) and registers each function. Returns the number installed.
|
|
130
|
+
* Mirrors `bindings/python/_galley.c:2339` `install_procedures`.
|
|
131
|
+
*/
|
|
132
|
+
export function installProcedures(module) {
|
|
133
|
+
if (module === null || typeof module !== "object")
|
|
134
|
+
throw new TypeError("module must be an object");
|
|
135
|
+
let count = 0;
|
|
136
|
+
for (const [name, value] of Object.entries(module)) {
|
|
137
|
+
if (typeof value !== "function")
|
|
138
|
+
continue;
|
|
139
|
+
if (!isProcedureName(name))
|
|
140
|
+
continue;
|
|
141
|
+
registry.set(name, value);
|
|
142
|
+
count++;
|
|
143
|
+
}
|
|
144
|
+
return count;
|
|
145
|
+
}
|
|
146
|
+
/** Removes all registered hooks; subsequent parses will be no-ops. */
|
|
147
|
+
export function clearProcedures() {
|
|
148
|
+
registry.clear();
|
|
149
|
+
}
|
|
150
|
+
/** Returns currently registered procedure names. */
|
|
151
|
+
export function listProcedures() {
|
|
152
|
+
return [...registry.keys()];
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=procedures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing session bound to this library's parser.
|
|
3
|
+
* Mirrors Python/Rust/Go sessions over `bindings/c/galley.h`.
|
|
4
|
+
*
|
|
5
|
+
* Runtime-neutral: all native calls go through the injected {@link FfiPort}.
|
|
6
|
+
* Each adapter (`bindings/js/node`, `../bun`, `../deno`) subclasses `Session`
|
|
7
|
+
* to supply its port from `SessionOptions.libraryPath`.
|
|
8
|
+
*/
|
|
9
|
+
import type { Diagnostic } from "./diagnostic.ts";
|
|
10
|
+
import type { FfiPort, Handle, TreeSnapshot } from "./port.ts";
|
|
11
|
+
import { Node } from "./node.ts";
|
|
12
|
+
export interface SessionOptions {
|
|
13
|
+
maxErrors?: number;
|
|
14
|
+
recoveryWindow?: number;
|
|
15
|
+
stackOverflowRecovery?: boolean;
|
|
16
|
+
syntaxErrorStackDepth?: number;
|
|
17
|
+
verbosity?: number;
|
|
18
|
+
astPreallocationRatio?: number;
|
|
19
|
+
astPreallocationCap?: number | bigint;
|
|
20
|
+
messageOverrides?: Record<string, string>;
|
|
21
|
+
libraryPath?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class Session {
|
|
24
|
+
#private;
|
|
25
|
+
constructor(port: FfiPort, options?: SessionOptions);
|
|
26
|
+
get isClosed(): boolean;
|
|
27
|
+
version(): string;
|
|
28
|
+
parserType(): number;
|
|
29
|
+
errorRecoveryMode(): number;
|
|
30
|
+
hasAst(): boolean;
|
|
31
|
+
hasProcedures(): boolean;
|
|
32
|
+
allowsNoAstTreeProcedures(): boolean;
|
|
33
|
+
sourceRetentionEnabled(): boolean;
|
|
34
|
+
hasPositionTracking(): boolean;
|
|
35
|
+
hasInputStreaming(): boolean;
|
|
36
|
+
usesVerbatim(): boolean;
|
|
37
|
+
stackOverflowRecoveryAvailable(): boolean;
|
|
38
|
+
symbolCount(): number;
|
|
39
|
+
variableCount(): number;
|
|
40
|
+
statusString(status: number): string | null;
|
|
41
|
+
close(): void;
|
|
42
|
+
/** For `using session = new Session()` (Explicit Resource Management). */
|
|
43
|
+
[Symbol.dispose](): void;
|
|
44
|
+
parse(input: string | Uint8Array): number;
|
|
45
|
+
parseSentinel(input: string | Uint8Array): number;
|
|
46
|
+
parseFile(filePath: string): number;
|
|
47
|
+
nodeCount(): number;
|
|
48
|
+
reserveNodes(capacity: number | bigint): void;
|
|
49
|
+
nodeCapacity(): number;
|
|
50
|
+
rootNode(): Node | null;
|
|
51
|
+
nodeValid(node: Node | bigint | number): boolean;
|
|
52
|
+
childCount(node: Node | bigint | number): number;
|
|
53
|
+
children(node: Node | bigint | number): Node[];
|
|
54
|
+
firstChild(node: Node | bigint | number): Node | null;
|
|
55
|
+
lastChild(node: Node | bigint | number): Node | null;
|
|
56
|
+
nextSibling(node: Node | bigint | number): Node | null;
|
|
57
|
+
priorSibling(node: Node | bigint | number): Node | null;
|
|
58
|
+
parent(node: Node | bigint | number): Node | null;
|
|
59
|
+
/**
|
|
60
|
+
* Flat bulk read of the most recent successful parse in a single FFI
|
|
61
|
+
* crossing: one array slot per node address. Walk `parent`/`firstChild`/
|
|
62
|
+
* `next` directly instead of one call per node.
|
|
63
|
+
*/
|
|
64
|
+
snapshot(): TreeSnapshot;
|
|
65
|
+
/**
|
|
66
|
+
* Pre-order walker over the subtree rooted at `root`, with the root at
|
|
67
|
+
* depth 0. Pass true to prune subtrees rooted at semantic-error nodes.
|
|
68
|
+
* Returns null for invalid roots and builds without AST construction.
|
|
69
|
+
* Close the walker (or use `using`) before closing the session or
|
|
70
|
+
* parsing again.
|
|
71
|
+
*/
|
|
72
|
+
walk(root: Node | bigint | number, skipSemanticErrors?: boolean): Walker | null;
|
|
73
|
+
symbolNameBytes(node: Node | bigint | number): Uint8Array | null;
|
|
74
|
+
symbolName(node: Node | bigint | number): string | null;
|
|
75
|
+
text(node: Node | bigint | number): Uint8Array | null;
|
|
76
|
+
span(node: Node | bigint | number): [bigint, bigint] | null;
|
|
77
|
+
lineColumn(node: Node | bigint | number): [number, number] | null;
|
|
78
|
+
variableIndex(node: Node | bigint | number): number | null;
|
|
79
|
+
lastPosition(): [number, number] | null;
|
|
80
|
+
hasDiagnostic(): boolean;
|
|
81
|
+
setMessageOverride(name: string, message: string): void;
|
|
82
|
+
diagnostic(): Diagnostic | null;
|
|
83
|
+
diagnostics(): Diagnostic[];
|
|
84
|
+
appendChildren(parent: Node | bigint, chain: Node | bigint): void;
|
|
85
|
+
insertBefore(target: Node | bigint, chain: Node | bigint): void;
|
|
86
|
+
insertAfter(target: Node | bigint, chain: Node | bigint): void;
|
|
87
|
+
removeSiblings(node: Node | bigint, count: number): Node | null;
|
|
88
|
+
removeSelf(node: Node | bigint): Node | null;
|
|
89
|
+
promoteChildrenOverWrapper(wrapper: Node | bigint): Node | null;
|
|
90
|
+
cleanChildren(node: Node | bigint): Node | null;
|
|
91
|
+
unlinkWrapper(wrapper: Node | bigint): void;
|
|
92
|
+
insertChildrenAt(parent: Node | bigint, index: number, chain: Node | bigint): void;
|
|
93
|
+
removeChildrenAt(parent: Node | bigint, index: number, count: number): Node | null;
|
|
94
|
+
symbolNameAt(index: number): Uint8Array | null;
|
|
95
|
+
symbolIsTerminal(index: number): boolean;
|
|
96
|
+
variableNameAt(index: number): Uint8Array | null;
|
|
97
|
+
}
|
|
98
|
+
/** One pre-order step of a {@link Walker}. */
|
|
99
|
+
export interface WalkStep {
|
|
100
|
+
node: Node;
|
|
101
|
+
depth: number;
|
|
102
|
+
isSemanticError: boolean;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Pre-order tree walker over the last successful parse, yielding one
|
|
106
|
+
* {@link WalkStep} per node. Shares the session's node storage: close the
|
|
107
|
+
* walker (or use `using`) before closing the session or parsing again.
|
|
108
|
+
* Created by {@link Session.walk}.
|
|
109
|
+
*/
|
|
110
|
+
export declare class Walker implements IterableIterator<WalkStep> {
|
|
111
|
+
#private;
|
|
112
|
+
constructor(session: Session, port: FfiPort, handle: Handle);
|
|
113
|
+
next(): IteratorResult<WalkStep>;
|
|
114
|
+
[Symbol.iterator](): IterableIterator<WalkStep>;
|
|
115
|
+
/**
|
|
116
|
+
* Prunes the children of the last yielded step; iteration continues with
|
|
117
|
+
* its next sibling. No effect without a last step.
|
|
118
|
+
*/
|
|
119
|
+
skipChildren(): void;
|
|
120
|
+
close(): void;
|
|
121
|
+
/** For `using walker = session.walk(...)`. */
|
|
122
|
+
[Symbol.dispose](): void;
|
|
123
|
+
}
|