Skip to content

Commit b5e9678

Browse files
committed
Add definition scopes to builder
1 parent 304821d commit b5e9678

File tree

4 files changed

+89
-5
lines changed

4 files changed

+89
-5
lines changed

src/builder/builder.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,25 @@ describe("ScopeInfoBuilder", () => {
125125
assertStrictEquals(info.ranges[0], info.ranges[0].children[0].parent);
126126
});
127127

128+
describe("startRange", () => {
129+
it("sets the definition scope when it's provided as a number", () => {
130+
const info = builder.startScope(0, 0).endScope(10, 0).startRange(0, 0, {
131+
scope: 0,
132+
}).endRange(0, 10).build();
133+
134+
assertStrictEquals(info.scopes[0], info.ranges[0].originalScope);
135+
});
136+
137+
it("sets the definition scope when it's provided directly", () => {
138+
const scope = builder.startScope(0, 0).endScope(10, 0).lastScope();
139+
const info = builder.startRange(0, 0, { scope: scope! }).endRange(0, 10)
140+
.build();
141+
142+
assertStrictEquals(info.scopes[0], info.ranges[0].originalScope);
143+
assertStrictEquals(info.ranges[0].originalScope, scope);
144+
});
145+
});
146+
128147
describe("endRange", () => {
129148
it("does nothing when no range is open", () => {
130149
builder.endRange(0, 20);

src/builder/builder.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export class ScopeInfoBuilder {
2323

2424
#scopeCounter = 0;
2525
#scopeToCount = new Map<OriginalScope, number>();
26+
#countToScope = new Map<number, OriginalScope>();
2627
#lastScope: OriginalScope | null = null;
2728

2829
addNullScope(): this {
@@ -50,7 +51,8 @@ export class ScopeInfoBuilder {
5051
scope.parent = this.#scopeStack.at(-1);
5152
}
5253
this.#scopeStack.push(scope);
53-
this.#scopeToCount.set(scope, this.#scopeCounter++);
54+
this.#scopeToCount.set(scope, this.#scopeCounter);
55+
this.#countToScope.set(this.#scopeCounter++, scope);
5456

5557
return this;
5658
}
@@ -103,7 +105,16 @@ export class ScopeInfoBuilder {
103105
return this.#lastScope;
104106
}
105107

106-
startRange(line: number, column: number): this {
108+
/**
109+
* @param option The definition 'scope' of this range can either be the "OriginalScope" directly
110+
* (produced by this builder) or the scope's number.
111+
* If a scope was started with the n-th call to `startScope` then n is the scope's number.
112+
*/
113+
startRange(
114+
line: number,
115+
column: number,
116+
options?: { scope?: number | OriginalScope },
117+
): this {
107118
const range: GeneratedRange = {
108119
start: { line, column },
109120
end: { line, column },
@@ -117,6 +128,12 @@ export class ScopeInfoBuilder {
117128
range.parent = this.#rangeStack.at(-1);
118129
}
119130

131+
if (typeof options?.scope === "number") {
132+
range.originalScope = this.#countToScope.get(options.scope);
133+
} else if (options?.scope !== undefined) {
134+
range.originalScope = options.scope;
135+
}
136+
120137
this.#rangeStack.push(range);
121138

122139
return this;
@@ -142,7 +159,9 @@ export class ScopeInfoBuilder {
142159

143160
this.#scopes = [];
144161
this.#ranges = [];
162+
this.#scopeCounter = 0;
145163
this.#scopeToCount.clear();
164+
this.#countToScope.clear();
146165

147166
return info;
148167
}
@@ -162,4 +181,12 @@ export class ScopeInfoBuilder {
162181
protected get ranges(): ReadonlyArray<GeneratedRange> {
163182
return this.#ranges;
164183
}
184+
185+
protected isValidScopeNumber(n: number): boolean {
186+
return this.#countToScope.has(n);
187+
}
188+
189+
protected isKnownScope(scope: OriginalScope): boolean {
190+
return this.#scopeToCount.has(scope);
191+
}
165192
}

src/builder/safe_builder.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import { beforeEach, describe, it } from "jsr:@std/testing/bdd";
66
import { SafeScopeInfoBuilder } from "./safe_builder.ts";
7-
import { assertThrows } from "jsr:@std/assert";
7+
import { assert, assertThrows } from "jsr:@std/assert";
88

99
describe("SafeScopeInfoBuilder", () => {
1010
let builder: SafeScopeInfoBuilder;
@@ -141,6 +141,24 @@ describe("SafeScopeInfoBuilder", () => {
141141

142142
builder.startRange(10, 5);
143143
});
144+
145+
it("throws when the definition scope doesnt point to a valid scope", () => {
146+
assertThrows(() => builder.startRange(0, 0, { scope: 0 }));
147+
});
148+
149+
it("throws when the definition scope is not known to the builder", () => {
150+
assertThrows(() =>
151+
builder.startRange(0, 0, {
152+
scope: {
153+
start: { line: 0, column: 0 },
154+
end: { line: 10, column: 10 },
155+
isStackFrame: false,
156+
variables: [],
157+
children: [],
158+
},
159+
})
160+
);
161+
});
144162
});
145163

146164
describe("endRange", () => {

src/builder/safe_builder.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ export class SafeScopeInfoBuilder extends ScopeInfoBuilder {
9292
return this;
9393
}
9494

95-
override startRange(line: number, column: number): this {
95+
override startRange(
96+
line: number,
97+
column: number,
98+
options?: { scope?: number | OriginalScope },
99+
): this {
96100
this.#verifyEmptyScopeStack("starRange");
97101

98102
const parent = this.rangeStack.at(-1);
@@ -114,7 +118,23 @@ export class SafeScopeInfoBuilder extends ScopeInfoBuilder {
114118
);
115119
}
116120

117-
super.startRange(line, column);
121+
if (
122+
typeof options?.scope === "number" &&
123+
!this.isValidScopeNumber(options.scope)
124+
) {
125+
throw new Error(
126+
`${options.scope} does not reference a valid OriginalScope`,
127+
);
128+
}
129+
if (
130+
typeof options?.scope === "object" && !this.isKnownScope(options.scope)
131+
) {
132+
throw new Error(
133+
"The provided definition scope was not produced by this builder!",
134+
);
135+
}
136+
137+
super.startRange(line, column, options);
118138
return this;
119139
}
120140

0 commit comments

Comments
 (0)