@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.
Files changed (48) hide show
  1. package/README.md +15 -0
  2. package/build/builder.mjs +276 -0
  3. package/build/fixture.mjs +85 -0
  4. package/build/shim.mjs +216 -0
  5. package/dist/artifact.d.ts +38 -0
  6. package/dist/artifact.js +45 -0
  7. package/dist/artifact.js.map +1 -0
  8. package/dist/constants.d.ts +34 -0
  9. package/dist/constants.js +41 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/diagnostic.d.ts +34 -0
  12. package/dist/diagnostic.js +28 -0
  13. package/dist/diagnostic.js.map +1 -0
  14. package/dist/errors.d.ts +22 -0
  15. package/dist/errors.js +38 -0
  16. package/dist/errors.js.map +1 -0
  17. package/dist/index.d.ts +21 -0
  18. package/dist/index.js +16 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/names.d.ts +20 -0
  21. package/dist/names.js +29 -0
  22. package/dist/names.js.map +1 -0
  23. package/dist/node.d.ts +45 -0
  24. package/dist/node.js +137 -0
  25. package/dist/node.js.map +1 -0
  26. package/dist/port.d.ts +193 -0
  27. package/dist/port.js +14 -0
  28. package/dist/port.js.map +1 -0
  29. package/dist/procedures.d.ts +62 -0
  30. package/dist/procedures.js +154 -0
  31. package/dist/procedures.js.map +1 -0
  32. package/dist/session.d.ts +123 -0
  33. package/dist/session.js +599 -0
  34. package/dist/session.js.map +1 -0
  35. package/dist/text.d.ts +7 -0
  36. package/dist/text.js +16 -0
  37. package/dist/text.js.map +1 -0
  38. package/package.json +31 -0
  39. package/src/artifact.ts +62 -0
  40. package/src/constants.ts +47 -0
  41. package/src/diagnostic.ts +48 -0
  42. package/src/errors.ts +49 -0
  43. package/src/index.ts +53 -0
  44. package/src/node.ts +154 -0
  45. package/src/port.ts +213 -0
  46. package/src/procedures.ts +169 -0
  47. package/src/session.ts +747 -0
  48. package/src/text.ts +19 -0
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@sanbus/galley-core",
3
+ "version": "0.0.1",
4
+ "description": "Runtime-neutral core for the Galley JavaScript bindings (Node, Bun, Deno). Pure TypeScript: no node:, bun:, or Deno imports. Each runtime ships a thin adapter implementing FfiPort.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./build/shim.mjs": "./build/shim.mjs",
15
+ "./build/builder.mjs": "./build/builder.mjs",
16
+ "./build/fixture.mjs": "./build/fixture.mjs"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src",
21
+ "build",
22
+ "README.md"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json",
26
+ "prepare": "npm run build"
27
+ },
28
+ "devDependencies": {
29
+ "typescript": "^7.0.2"
30
+ }
31
+ }
@@ -0,0 +1,62 @@
1
+ import { MissingArtifactError } from "./errors.ts";
2
+
3
+ /**
4
+ * Host capabilities artifact resolution needs from each adapter.
5
+ * Node, Bun, and wasm pass `process.env`, `path.resolve`, and an
6
+ * `fs.accessSync` probe; Deno passes `Deno.env.get`, the identity
7
+ * resolver (Deno reports the path it was given), and a `Deno.statSync`
8
+ * probe. Core stays runtime-neutral: no `node:`, `bun:`, or Deno imports.
9
+ */
10
+ export interface ArtifactHost {
11
+ getEnv(name: string): string | undefined;
12
+ resolvePath(candidate: string): string;
13
+ existsSync(candidate: string): boolean;
14
+ buildHint: string;
15
+ }
16
+
17
+ /**
18
+ * Shared library filename mapping for every JavaScript adapter.
19
+ * One library name per platform: `lib<base>.dylib` on macOS, `<base>.dll`
20
+ * on Windows (no `lib` prefix), `lib<base>.so` elsewhere. The platform
21
+ * string comes from the host (`process.platform` under Node/Bun, where
22
+ * Windows reports `win32`; `Deno.build.os`, where it reports `windows`);
23
+ * both spellings map to the Windows name here so the adapters cannot
24
+ * diverge. Each adapter keeps a thin `libFileName` wrapper passing its
25
+ * own base name; the mapping lives here.
26
+ */
27
+ export function artifactFileName(base: string, platform: string): string {
28
+ if (platform === "darwin") return `lib${base}.dylib`;
29
+ if (platform === "win32" || platform === "windows") return `${base}.dll`;
30
+ return `lib${base}.so`;
31
+ }
32
+
33
+ /**
34
+ * Shared wasm artifact filename. WebAssembly modules are platform-neutral:
35
+ * always `lib<base>.wasm`. The wasm adapter keeps a thin `wasmFileName`
36
+ * wrapper passing its base name.
37
+ */
38
+ export function wasmArtifactFileName(base: string): string {
39
+ return `lib${base}.wasm`;
40
+ }
41
+
42
+ /**
43
+ * Shared parser-artifact resolution for every JavaScript adapter.
44
+ * One place is named up front — an explicit path or GALLEY_LIBRARY_PATH.
45
+ * Anything else is a loud error, never a search. Each adapter keeps a
46
+ * thin `findLibrary` wrapper passing its host capabilities and its own
47
+ * build hint; the decision lives here.
48
+ */
49
+ export function resolveArtifact(explicit: string | undefined, host: ArtifactHost): string {
50
+ const chosen = explicit || host.getEnv("GALLEY_LIBRARY_PATH");
51
+ if (!chosen) {
52
+ throw new MissingArtifactError(
53
+ "no parser artifact given; pass libraryPath or set GALLEY_LIBRARY_PATH",
54
+ host.buildHint,
55
+ );
56
+ }
57
+ const resolved = host.resolvePath(chosen);
58
+ if (!host.existsSync(resolved)) {
59
+ throw new MissingArtifactError(`at ${resolved}`, host.buildHint);
60
+ }
61
+ return resolved;
62
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Constants mirroring `bindings/c/galley.h` status and kind enumerations.
3
+ * These are the single source for JavaScript consumers; they must stay
4
+ * in sync with the C header.
5
+ */
6
+
7
+ export const INVALID_NODE = 0xffffffffffffffffn; // 2^64-1
8
+
9
+ // Status codes (negative = failure)
10
+ export const STATUS_OK = 0;
11
+ export const STATUS_ERROR_NULL_ARGUMENT = -1;
12
+ export const STATUS_ERROR_SYNTAX = -2;
13
+ export const STATUS_ERROR_INDENTATION = -3;
14
+ export const STATUS_ERROR_STACK_OVERFLOW = -4;
15
+ export const STATUS_ERROR_AST_CAPACITY_EXCEEDED = -5;
16
+ export const STATUS_ERROR_UNTERMINATED_RAW_STRING = -6;
17
+ export const STATUS_ERROR_OUT_OF_MEMORY = -7;
18
+ export const STATUS_ERROR_INTERNAL = -8;
19
+ export const STATUS_ERROR_NO_DIAGNOSTIC = -9;
20
+ export const STATUS_ERROR_INVALID_NODE = -10;
21
+ export const STATUS_ERROR_IO = -11;
22
+ export const STATUS_ERROR_SEMANTIC = -12;
23
+
24
+ // Parser families
25
+ export const PARSER_TYPE_LL = 0;
26
+ export const PARSER_TYPE_LR = 1;
27
+
28
+ // Recovery modes
29
+ export const RECOVERY_MODE_DISABLED = 0;
30
+ export const RECOVERY_MODE_AUTOMATIC = 1;
31
+ export const RECOVERY_MODE_EXPLICIT = 2;
32
+
33
+ // Diagnostic kinds
34
+ export const KIND_NONE = 0;
35
+ export const KIND_SYNTAX = 1;
36
+ export const KIND_INDENTATION = 2;
37
+ export const KIND_SEMANTIC = 3;
38
+
39
+ // Recovery targets
40
+ export const RECOVERY_TARGET_NONE = 0;
41
+ export const RECOVERY_TARGET_LHS_VARIABLE = 1;
42
+ export const RECOVERY_TARGET_PRODUCTION = 2;
43
+ export const RECOVERY_TARGET_OCCURRENCE = 3;
44
+
45
+ // Resume sides
46
+ export const RESUME_BEFORE = 0;
47
+ export const RESUME_AFTER = 1;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Read-only snapshot of a parse diagnostic.
3
+ * All `Uint8Array` fields are copies that remain valid after the next parse.
4
+ */
5
+
6
+ /**
7
+ * Display name for the synthetic control-byte terminals (end of input and
8
+ * the indentation pair), which never occur as user-typable input. Exact
9
+ * full-token match only; anything else returns null and the caller keeps
10
+ * its existing rendering.
11
+ *
12
+ * Mirrors `tokenDisplayName` in `src/runtime/string.zig` until the general
13
+ * per-grammar mechanism arrives; keep the two tables in sync.
14
+ */
15
+ export function displayTokenName(token: Uint8Array): string | null {
16
+ if (token.length !== 1) return null;
17
+ switch (token[0]) {
18
+ case 0x00:
19
+ return "End of input";
20
+ case 0x01:
21
+ return "Indent";
22
+ case 0x02:
23
+ return "Dedent";
24
+ default:
25
+ return null;
26
+ }
27
+ }
28
+
29
+ export interface Diagnostic {
30
+ kind: number; // KIND_*
31
+ line: number; // 1-based
32
+ column: number;
33
+ message: string; // plain text
34
+ messageAnsi: string; // with ANSI
35
+ unexpectedToken: Uint8Array | null; // syntax only
36
+ expectedTokens: Uint8Array[]; // syntax only
37
+ context: string[]; // innermost-first variable names, syntax only
38
+ syntaxErrorCount: number;
39
+ semanticErrorCount: number;
40
+ semantic: [string, string] | null; // (variable, message) for semantic errors
41
+ indentation: [number, number] | null; // (spaces, width) for indentation errors
42
+ recoveryKind: number | null; // RECOVERY_TARGET_*
43
+ recoveryTerminal: Uint8Array | null;
44
+ recoveryResume: number | null; // RESUME_*
45
+ recoveryLhsVariable: string | null;
46
+ recoveryProduction: [string, number] | null; // (variable, rhs_index)
47
+ recoveryOccurrence: [string, number, number, string] | null; // (parent, rhs, symbol, variable)
48
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,49 @@
1
+ import type { Diagnostic } from "./diagnostic.ts";
2
+
3
+ /**
4
+ * Failure reported by a Galley operation.
5
+ * Mirrors Python's `galley.Error` (code + diagnostic snapshot).
6
+ */
7
+ export class GalleyError extends Error {
8
+ readonly code: number;
9
+ readonly diagnostic: Diagnostic | null;
10
+
11
+ constructor(
12
+ message: string,
13
+ code: number,
14
+ diagnostic: Diagnostic | null = null,
15
+ ) {
16
+ super(message);
17
+ this.name = "GalleyError";
18
+ this.code = code;
19
+ this.diagnostic = diagnostic;
20
+ }
21
+ }
22
+
23
+ const MISSING_ARTIFACT_CODE = "galley:missing-artifact";
24
+
25
+ /**
26
+ * The parser artifact a binding was told to load is not where it was told.
27
+ * Thrown by every adapter's artifact resolution instead of searching
28
+ * elsewhere. The universal loader catches exactly this class to try the
29
+ * next engine; anything else propagates loudly.
30
+ */
31
+ export class MissingArtifactError extends Error {
32
+ readonly code = MISSING_ARTIFACT_CODE;
33
+
34
+ constructor(detail: string, buildHint: string) {
35
+ super(`galley: parser artifact not found: ${detail}.\n${buildHint}`);
36
+ this.name = "MissingArtifactError";
37
+ }
38
+
39
+ /** True for missing-artifact failures even across duplicated installs. */
40
+ static is(error: unknown): error is MissingArtifactError {
41
+ if (error instanceof MissingArtifactError) return true;
42
+ return (
43
+ typeof error === "object" &&
44
+ error !== null &&
45
+ (error as { name?: unknown }).name === "MissingArtifactError" &&
46
+ (error as { code?: unknown }).code === MISSING_ARTIFACT_CODE
47
+ );
48
+ }
49
+ }
package/src/index.ts ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Galley JavaScript core — runtime-neutral public surface.
3
+ *
4
+ * Each adapter package (`@sanbus/galley-node`, `@sanbus/galley-bun`, `@sanbus/galley-deno`)
5
+ * binds these classes to its {@link FfiPort} and re-exports them.
6
+ */
7
+
8
+ import { Session, Walker } from "./session.ts";
9
+ import type { SessionOptions, WalkStep } from "./session.ts";
10
+ import { Node } from "./node.ts";
11
+ import { GalleyError, MissingArtifactError } from "./errors.ts";
12
+ import { resolveArtifact, artifactFileName, wasmArtifactFileName } from "./artifact.ts";
13
+ import type { ArtifactHost } from "./artifact.ts";
14
+ import type { Diagnostic } from "./diagnostic.ts";
15
+ import { displayTokenName } from "./diagnostic.ts";
16
+ import type { FfiPort, Handle, SessionCOptions, TreeSnapshot, WalkedStep, DispatchHandler } from "./port.ts";
17
+ import {
18
+ installProcedure,
19
+ installProcedures,
20
+ clearProcedures,
21
+ listProcedures,
22
+ ProcedureArguments,
23
+ dispatchProcedure,
24
+ setParsingSession,
25
+ getParsingSession,
26
+ } from "./procedures.ts";
27
+ import type { HookFn } from "./procedures.ts";
28
+ import { encodeUtf8, decodeUtf8, byteLengthUtf8 } from "./text.ts";
29
+
30
+ export {
31
+ Session,
32
+ Walker,
33
+ Node,
34
+ GalleyError,
35
+ MissingArtifactError,
36
+ resolveArtifact,
37
+ artifactFileName,
38
+ wasmArtifactFileName,
39
+ displayTokenName,
40
+ ProcedureArguments,
41
+ installProcedure,
42
+ installProcedures,
43
+ clearProcedures,
44
+ listProcedures,
45
+ dispatchProcedure,
46
+ setParsingSession,
47
+ getParsingSession,
48
+ encodeUtf8,
49
+ decodeUtf8,
50
+ byteLengthUtf8,
51
+ };
52
+ export type { Diagnostic, WalkStep, SessionOptions, FfiPort, Handle, SessionCOptions, TreeSnapshot, WalkedStep, DispatchHandler, HookFn, ArtifactHost };
53
+ export * from "./constants.ts";
package/src/node.ts ADDED
@@ -0,0 +1,154 @@
1
+ import type { Session } from "./session.ts";
2
+ import { decodeUtf8 } from "./text.ts";
3
+
4
+ /**
5
+ * Session-bound handle for a node in the non-relocating AST storage.
6
+ * Mirrors Python's `galley.Node`: keeps a strong reference to its Session
7
+ * and raises after the session is closed.
8
+ */
9
+ export class Node {
10
+ readonly #session: Session;
11
+ readonly #address: bigint;
12
+
13
+ constructor(session: Session, address: bigint | number) {
14
+ this.#session = session;
15
+ this.#address = typeof address === "bigint" ? address : BigInt(address);
16
+ }
17
+
18
+ /** Raw address (stable index in the session's node storage). */
19
+ get address(): bigint {
20
+ return this.#address;
21
+ }
22
+
23
+ /** Owning session. */
24
+ get session(): Session {
25
+ return this.#session;
26
+ }
27
+
28
+ private ensureAlive(): void {
29
+ if (this.#session.isClosed) {
30
+ throw new Error("node's session is closed");
31
+ }
32
+ }
33
+
34
+ /** Tuple of direct children, from first to last (empty when leaf). */
35
+ children(): Node[] {
36
+ this.ensureAlive();
37
+ return this.#session.children(this);
38
+ }
39
+
40
+ /** Text bytes of this node, or null for invalid node. */
41
+ text(): Uint8Array | null {
42
+ this.ensureAlive();
43
+ return this.#session.text(this);
44
+ }
45
+
46
+ /** Symbol name bytes as string, or null for invalid node. Terminal-only nodes → "". */
47
+ symbolName(): string | null {
48
+ this.ensureAlive();
49
+ const bytes = this.#session.symbolNameBytes(this);
50
+ if (bytes === null) return null;
51
+ return decodeUtf8(bytes);
52
+ }
53
+
54
+ /** Raw symbol name bytes (Uint8Array) or null. */
55
+ symbolNameBytes(): Uint8Array | null {
56
+ this.ensureAlive();
57
+ return this.#session.symbolNameBytes(this);
58
+ }
59
+
60
+ /** (start, length) byte span, or null. */
61
+ span(): [bigint, bigint] | null {
62
+ this.ensureAlive();
63
+ return this.#session.span(this);
64
+ }
65
+
66
+ /** 1-based (line, column) of first byte, or null. */
67
+ lineColumn(): [number, number] | null {
68
+ this.ensureAlive();
69
+ return this.#session.lineColumn(this);
70
+ }
71
+
72
+ /** Parent node, or null for root. */
73
+ parent(): Node | null {
74
+ this.ensureAlive();
75
+ return this.#session.parent(this);
76
+ }
77
+
78
+ nextSibling(): Node | null {
79
+ this.ensureAlive();
80
+ return this.#session.nextSibling(this);
81
+ }
82
+
83
+ priorSibling(): Node | null {
84
+ this.ensureAlive();
85
+ return this.#session.priorSibling(this);
86
+ }
87
+
88
+ firstChild(): Node | null {
89
+ this.ensureAlive();
90
+ return this.#session.firstChild(this);
91
+ }
92
+
93
+ lastChild(): Node | null {
94
+ this.ensureAlive();
95
+ return this.#session.lastChild(this);
96
+ }
97
+
98
+ cleanChildren(): Node | null {
99
+ this.ensureAlive();
100
+ return this.#session.cleanChildren(this);
101
+ }
102
+
103
+ appendChildren(chain: Node | bigint): void {
104
+ this.ensureAlive();
105
+ this.#session.appendChildren(this, chain);
106
+ }
107
+
108
+ /** Number of direct children. */
109
+ get length(): number {
110
+ this.ensureAlive();
111
+ return this.#session.childCount(this);
112
+ }
113
+
114
+ /** Child at index (negative indices supported). */
115
+ at(index: number): Node {
116
+ this.ensureAlive();
117
+ const count = this.length;
118
+ let i = index;
119
+ if (i < 0) i += count;
120
+ if (i < 0 || i >= count) throw new RangeError(`node index ${index} out of range (0..${count - 1})`);
121
+ const arr = this.children();
122
+ return arr[i];
123
+ }
124
+
125
+ *[Symbol.iterator](): Iterator<Node> {
126
+ this.ensureAlive();
127
+ for (const child of this.children()) yield child;
128
+ }
129
+
130
+ /** Raw address for `Number(node)` / `BigInt(node)`. */
131
+ valueOf(): bigint {
132
+ return this.#address;
133
+ }
134
+
135
+ toString(): string {
136
+ return `Node(${this.#address.toString()})`;
137
+ }
138
+
139
+ equals(other: unknown): boolean {
140
+ if (other instanceof Node) {
141
+ return this.#address === other.#address && this.#session === other.#session;
142
+ }
143
+ if (typeof other === "bigint") return this.#address === other;
144
+ if (typeof other === "number") return this.#address === BigInt(other);
145
+ return false;
146
+ }
147
+
148
+ // allow `Number(node)` and `+node`
149
+ [Symbol.toPrimitive](hint: string): bigint | string | number {
150
+ if (hint === "number") return Number(this.#address);
151
+ if (hint === "string") return this.toString();
152
+ return this.#address;
153
+ }
154
+ }
package/src/port.ts ADDED
@@ -0,0 +1,213 @@
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
+
14
+ export type Handle = unknown;
15
+
16
+ /** `GalleyCOptions` fields as plain data; null selects library defaults. */
17
+ export interface SessionCOptions {
18
+ maxErrors: number;
19
+ recoveryWindow: number;
20
+ stackOverflowRecovery: number;
21
+ syntaxErrorStackDepth: number;
22
+ verbosity: number;
23
+ astPreallocationRatio: number;
24
+ astPreallocationCap: bigint;
25
+ }
26
+
27
+ /** One pre-order walker step. */
28
+ export interface WalkedStep {
29
+ node: bigint;
30
+ depth: number;
31
+ isSemanticError: boolean;
32
+ }
33
+
34
+ /**
35
+ * Flat bulk read of the most recent successful parse, one slot per node
36
+ * address. `parent` holds `INVALID_NODE` for the root, `firstChild`/`next`
37
+ * hold `INVALID_NODE` where the link does not exist, `variable` holds -1
38
+ * for nodes without a variable, and `spanStart`/`spanLen` are byte
39
+ * offsets into the parsed input. Parent, firstChild, and next alone
40
+ * describe the whole tree with no further calls.
41
+ */
42
+ export interface TreeSnapshot {
43
+ count: number;
44
+ parent: BigUint64Array;
45
+ firstChild: BigUint64Array;
46
+ next: BigUint64Array;
47
+ childCount: Uint32Array;
48
+ variable: BigInt64Array;
49
+ spanStart: BigUint64Array;
50
+ spanLen: BigUint64Array;
51
+ }
52
+
53
+ /** Native dispatch callback installed by the adapter; receives a decoded hook name. */
54
+ export type DispatchHandler = (name: string, args: Handle) => void;
55
+
56
+ export interface FfiPort {
57
+ // -- module-level queries (mirror galley.h) --------------------------
58
+ version(): string;
59
+ parserType(): number;
60
+ errorRecoveryMode(): number;
61
+ hasAst(): boolean;
62
+ hasProcedures(): boolean;
63
+ allowsNoAstTreeProcedures(): boolean;
64
+ sourceRetentionEnabled(): boolean;
65
+ hasPositionTracking(): boolean;
66
+ hasInputStreaming(): boolean;
67
+ usesVerbatim(): boolean;
68
+ stackOverflowRecoveryAvailable(): boolean;
69
+ symbolCount(): number;
70
+ variableCount(): number;
71
+ statusString(status: number): string | null;
72
+
73
+ // -- sessions ---------------------------------------------------------
74
+ /** Null handle on initialization failure (most commonly allocation failure). */
75
+ createSession(options: SessionCOptions | null): Handle;
76
+ destroySession(handle: Handle): void;
77
+ /** Negative status on failure. */
78
+ setMessageOverride(handle: Handle, name: Uint8Array, message: Uint8Array): number;
79
+
80
+ // -- parsing ----------------------------------------------------------
81
+ /** Bytes parsed, or a negative status code. */
82
+ parse(handle: Handle, data: Uint8Array): number;
83
+ /** Bytes parsed, or a negative status code. */
84
+ parseFile(handle: Handle, path: string): number;
85
+ /** End position of the most recent successful parse; null on failure. */
86
+ lastPosition(handle: Handle): [number, number] | null;
87
+
88
+ // -- arena and navigation ----------------------------------------------
89
+ nodeCount(handle: Handle): number;
90
+ /** Negative status on failure (e.g. capacity exceeded). */
91
+ reserveNodes(handle: Handle, capacity: bigint): number;
92
+ nodeCapacity(handle: Handle): number;
93
+ rootNode(handle: Handle): bigint;
94
+ nodeValid(handle: Handle, node: bigint): boolean;
95
+ childCount(handle: Handle, node: bigint): number;
96
+ firstChild(handle: Handle, node: bigint): bigint;
97
+ lastChild(handle: Handle, node: bigint): bigint;
98
+ nextSibling(handle: Handle, node: bigint): bigint;
99
+ priorSibling(handle: Handle, node: bigint): bigint;
100
+ parent(handle: Handle, node: bigint): bigint;
101
+ /** Flat bulk read of the most recent successful parse (see `TreeSnapshot`). */
102
+ treeSnapshot(handle: Handle): TreeSnapshot;
103
+
104
+ // -- walker ------------------------------------------------------------
105
+ /** Null without AST construction or on invalid arguments. */
106
+ walkerCreate(handle: Handle, node: bigint, skipSemanticErrors: boolean): Handle | null;
107
+ /** Null when the walk is done. */
108
+ walkerNext(walker: Handle): WalkedStep | null;
109
+ walkerSkipChildren(walker: Handle): void;
110
+ walkerDestroy(walker: Handle): void;
111
+
112
+ // -- node accessors (null on invalid node) ------------------------------
113
+ nodeSymbolName(handle: Handle, node: bigint): Uint8Array | null;
114
+ nodeText(handle: Handle, node: bigint): Uint8Array | null;
115
+ nodeSpan(handle: Handle, node: bigint): [bigint, bigint] | null;
116
+ nodeLineColumn(handle: Handle, node: bigint): [number, number] | null;
117
+ /** Raw variable index; -1 when the node has no variable. */
118
+ nodeVariableIndex(handle: Handle, node: bigint): number;
119
+ symbolNameAt(handle: Handle, index: number): Uint8Array | null;
120
+ symbolIsTerminal(handle: Handle, index: number): boolean;
121
+ variableNameAt(handle: Handle, index: number): Uint8Array | null;
122
+
123
+ // -- diagnostics ---------------------------------------------------------
124
+ hasDiagnostic(handle: Handle): boolean;
125
+ diagnosticKind(handle: Handle): number;
126
+ diagnosticMessage(handle: Handle): string | null;
127
+ diagnosticMessageAnsi(handle: Handle): string | null;
128
+ diagnosticPosition(handle: Handle): [number, number] | null;
129
+ diagnosticUnexpectedToken(handle: Handle): Uint8Array | null;
130
+ diagnosticExpectedCount(handle: Handle): number;
131
+ diagnosticExpectedAt(handle: Handle, index: number): Uint8Array | null;
132
+ diagnosticContextCount(handle: Handle): number;
133
+ diagnosticContextAt(handle: Handle, index: number): Uint8Array | null;
134
+ syntaxErrorCount(handle: Handle): number;
135
+ semanticErrorCount(handle: Handle): number;
136
+ /** (variable, message); null when there is no semantic diagnostic. */
137
+ diagnosticSemantic(handle: Handle): [string, string] | null;
138
+ /** (spaces, width); null when not an indentation diagnostic. */
139
+ diagnosticIndentation(handle: Handle): [number, number] | null;
140
+
141
+ // -- recovery, current diagnostic ------------------------------------------
142
+ diagnosticRecoveryKind(handle: Handle): number;
143
+ diagnosticRecoveryTerminal(handle: Handle): Uint8Array | null;
144
+ diagnosticRecoveryResume(handle: Handle): number | null;
145
+ diagnosticRecoveryLhsVariable(handle: Handle): string | null;
146
+ diagnosticRecoveryProduction(handle: Handle): [string, number] | null;
147
+ diagnosticRecoveryOccurrence(handle: Handle): [string, number, number, string] | null;
148
+
149
+ // -- recovery, recorded diagnostics ------------------------------------------
150
+ recordedDiagnosticCount(handle: Handle): number;
151
+ recordedDiagnosticKind(handle: Handle, diagIndex: number): number;
152
+ recordedDiagnosticPosition(handle: Handle, diagIndex: number): [number, number] | null;
153
+ recordedUnexpectedToken(handle: Handle, diagIndex: number): Uint8Array | null;
154
+ recordedDiagnosticMessage(handle: Handle, diagIndex: number): string | null;
155
+ recordedIndentation(handle: Handle, diagIndex: number): [number, number] | null;
156
+ recordedSemantic(handle: Handle, diagIndex: number): [string, string] | null;
157
+ recordedExpectedCount(handle: Handle, diagIndex: number): number;
158
+ recordedExpectedToken(handle: Handle, diagIndex: number, tokenIndex: number): Uint8Array | null;
159
+ recordedContextCount(handle: Handle, diagIndex: number): number;
160
+ recordedContextName(handle: Handle, diagIndex: number, contextIndex: number): Uint8Array | null;
161
+ recordedRecoveryKind(handle: Handle, diagIndex: number): number;
162
+ recordedRecoveryTerminal(handle: Handle, diagIndex: number): Uint8Array | null;
163
+ recordedRecoveryResume(handle: Handle, diagIndex: number): number | null;
164
+ recordedRecoveryLhsVariable(handle: Handle, diagIndex: number): string | null;
165
+ recordedRecoveryProduction(handle: Handle, diagIndex: number): [string, number] | null;
166
+ recordedRecoveryOccurrence(
167
+ handle: Handle,
168
+ diagIndex: number,
169
+ ): [string, number, number, string] | null;
170
+
171
+ // -- tree editing ----------------------------------------------------------
172
+ treeAppendChildren(handle: Handle, parent: bigint, first: bigint): number;
173
+ treeInsertBefore(handle: Handle, target: bigint, first: bigint): number;
174
+ treeInsertAfter(handle: Handle, target: bigint, first: bigint): number;
175
+ treeRemoveSiblings(handle: Handle, node: bigint, count: number): { status: number; head: bigint };
176
+ treeRemoveSelf(handle: Handle, node: bigint): { status: number; head: bigint };
177
+ treePromoteChildrenOverWrapper(handle: Handle, wrapper: bigint): { status: number; head: bigint };
178
+ treeCleanChildren(handle: Handle, node: bigint): { status: number; head: bigint };
179
+ treeUnlinkWrapper(handle: Handle, wrapper: bigint): number;
180
+ treeInsertChildrenAt(handle: Handle, parent: bigint, index: number, first: bigint): number;
181
+ treeRemoveChildrenAt(
182
+ handle: Handle,
183
+ parent: bigint,
184
+ index: number,
185
+ count: number,
186
+ ): { status: number; head: bigint };
187
+
188
+ // -- procedure hooks (parse-time state) ---------------------------------------
189
+ procCurrentNode(args: Handle): bigint;
190
+ procSetCurrentNode(args: Handle, node: bigint): void;
191
+ procDropSelf(args: Handle): number;
192
+ procDropChildren(args: Handle): number;
193
+ procDropIfEmpty(args: Handle): number;
194
+ procReplaceWithChildren(args: Handle): number;
195
+ procContextLine(args: Handle): number;
196
+ procContextColumn(args: Handle): number;
197
+ /** Running semantic-error total, or a negative status code. */
198
+ procReportSemanticError(args: Handle, message: Uint8Array): number;
199
+ /**
200
+ * Enables exactly `names` in the native procedure gates before a parse
201
+ * (selective dispatch): the adapter clears all gates, then enables each
202
+ * name. Missing symbols (C-procedure or stale libraries) are no-ops.
203
+ */
204
+ syncProcedures(names: string[]): void;
205
+ /**
206
+ * Hook names in integer-ID order for the ID dispatch path. Queried once
207
+ * from the library (`galley_js_procedure_count` /
208
+ * `galley_js_procedure_name_ptr` / `galley_js_procedure_name_len`) and
209
+ * cached; empty when the library predates the query exports (C-procedure
210
+ * or stale libraries use the name-carrying dispatch instead).
211
+ */
212
+ procedureNames(): string[];
213
+ }