@hatchingpoint/point 0.0.6 → 0.0.7

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.
@@ -1,347 +1,347 @@
1
- import type { PointCoreProgram, PointSourceSpan } from "../core/ast.ts";
2
- import type { PointCoreDiagnostic } from "../core/check.ts";
3
- import { mapDiagnosticsToSemanticRefs } from "../core/context.ts";
4
- import type {
5
- PointSemanticBinding,
6
- PointSemanticDeclaration,
7
- PointSemanticExpression,
8
- PointSemanticOutputBinding,
9
- PointSemanticProgram,
10
- PointSemanticTypeExpression,
11
- } from "./ast.ts";
12
-
13
- function formatSemanticType(type: PointSemanticTypeExpression): string {
14
- if (type.args.length === 0) return type.name;
15
- return `${type.name}<${type.args.map(formatSemanticType).join(", ")}>`;
16
- }
17
-
18
- export type PointSemanticSymbolKind =
19
- | "module"
20
- | "use"
21
- | "record"
22
- | "field"
23
- | "calculation"
24
- | "rule"
25
- | "label"
26
- | "external"
27
- | "action"
28
- | "policy"
29
- | "view"
30
- | "route"
31
- | "workflow"
32
- | "command"
33
- | "param";
34
-
35
- export interface PointSemanticSymbol {
36
- ref: string;
37
- path: string;
38
- kind: PointSemanticSymbolKind;
39
- name: string;
40
- module: string;
41
- type?: string;
42
- from?: string;
43
- effects?: string[];
44
- span: PointSourceSpan | null;
45
- }
46
-
47
- export interface PointSemanticIndex {
48
- schemaVersion: "point.semantic.index.v1";
49
- module: string;
50
- refs: PointSemanticSymbol[];
51
- }
52
-
53
- export interface PointSemanticExplanation {
54
- schemaVersion: "point.semantic.explain.v1";
55
- ref: string;
56
- found: boolean;
57
- symbol?: PointSemanticSymbol;
58
- relatedRefs: string[];
59
- summary: string;
60
- }
61
-
62
- export function createSemanticIndex(program: PointSemanticProgram): PointSemanticIndex {
63
- const moduleName = program.module ?? "anonymous";
64
- const refs: PointSemanticSymbol[] = [
65
- {
66
- ref: semanticRefFor(moduleName, "module"),
67
- path: "module",
68
- kind: "module",
69
- name: moduleName,
70
- module: moduleName,
71
- span: program.span ?? null,
72
- },
73
- ...program.uses.map((use) => ({
74
- ref: semanticRefFor(moduleName, `use.${use.moduleName}`),
75
- path: `use.${use.moduleName}`,
76
- kind: "use" as const,
77
- name: use.moduleName,
78
- module: moduleName,
79
- from: use.from,
80
- span: use.span ?? null,
81
- })),
82
- ];
83
- for (const declaration of program.declarations) {
84
- refs.push(...symbolsForDeclaration(moduleName, declaration));
85
- }
86
- return { schemaVersion: "point.semantic.index.v1", module: moduleName, refs };
87
- }
88
-
89
- export function explainSemanticRef(program: PointSemanticProgram, ref: string): PointSemanticExplanation {
90
- const index = createSemanticIndex(program);
91
- const symbol = index.refs.find((candidate) => candidate.ref === ref);
92
- if (!symbol) {
93
- return {
94
- schemaVersion: "point.semantic.explain.v1",
95
- ref,
96
- found: false,
97
- relatedRefs: [],
98
- summary: `No Point symbol found for ${ref}.`,
99
- };
100
- }
101
- const relatedRefs = relatedRefsFor(symbol, index);
102
- return {
103
- schemaVersion: "point.semantic.explain.v1",
104
- ref,
105
- found: true,
106
- symbol,
107
- relatedRefs,
108
- summary: summaryFor(symbol),
109
- };
110
- }
111
-
112
- export function mapPublicDiagnostics(program: PointCoreProgram, diagnostics: PointCoreDiagnostic[]): PointCoreDiagnostic[] {
113
- const mapped = mapDiagnosticsToSemanticRefs(program, diagnostics);
114
- if (!program.semanticSource) return mapped;
115
- return mapped.map((diagnostic) => ({
116
- ...diagnostic,
117
- span: diagnostic.span ?? semanticSpanForDiagnosticPath(program.semanticSource!, program, diagnostic.path),
118
- }));
119
- }
120
-
121
- function symbolsForDeclaration(moduleName: string, declaration: PointSemanticDeclaration): PointSemanticSymbol[] {
122
- if (declaration.kind === "record") {
123
- const recordPath = `record.${declaration.name}`;
124
- return [
125
- {
126
- ref: semanticRefFor(moduleName, recordPath),
127
- path: recordPath,
128
- kind: "record",
129
- name: declaration.name,
130
- module: moduleName,
131
- span: declaration.span ?? null,
132
- },
133
- ...declaration.fields.map((field) => ({
134
- ref: semanticRefFor(moduleName, `${recordPath}.field.${field.label}`),
135
- path: `${recordPath}.field.${field.label}`,
136
- kind: "field" as const,
137
- name: field.label,
138
- module: moduleName,
139
- type: formatSemanticType(field.type),
140
- span: field.span ?? null,
141
- })),
142
- ];
143
- }
144
- if (declaration.kind === "external") {
145
- return declaration.functions.flatMap((fn) => {
146
- const path = `external.${fn.label}`;
147
- return [
148
- {
149
- ref: semanticRefFor(moduleName, path),
150
- path,
151
- kind: "external" as const,
152
- name: fn.label,
153
- module: moduleName,
154
- type: formatSemanticType(fn.returnType),
155
- from: fn.from,
156
- span: fn.span ?? null,
157
- },
158
- ...bindingSymbols(moduleName, path, fn.params),
159
- ];
160
- });
161
- }
162
- const callable = callableDeclaration(declaration);
163
- if (!callable) return [];
164
- const path = `${callable.kind}.${callable.name}`;
165
- return [
166
- {
167
- ref: semanticRefFor(moduleName, path),
168
- path,
169
- kind: callable.kind,
170
- name: callable.name,
171
- module: moduleName,
172
- type: formatSemanticType(callable.output.type),
173
- effects: callable.effects,
174
- span: callable.declaration.span ?? null,
175
- },
176
- ...bindingSymbols(moduleName, path, callable.inputs),
177
- {
178
- ref: semanticRefFor(moduleName, `${path}.output.${callable.output.name}`),
179
- path: `${path}.output.${callable.output.name}`,
180
- kind: "param",
181
- name: callable.output.name,
182
- module: moduleName,
183
- type: formatSemanticType(callable.output.type),
184
- span: callable.output.span ?? null,
185
- },
186
- ];
187
- }
188
-
189
- function callableDeclaration(declaration: PointSemanticDeclaration):
190
- | {
191
- kind: Exclude<PointSemanticSymbolKind, "module" | "use" | "record" | "field" | "external" | "param">;
192
- name: string;
193
- inputs: PointSemanticBinding[];
194
- output: PointSemanticOutputBinding;
195
- effects?: string[];
196
- declaration: PointSemanticDeclaration;
197
- }
198
- | null {
199
- if (declaration.kind === "calculation" || declaration.kind === "rule" || declaration.kind === "label") {
200
- return { kind: declaration.kind, name: declaration.name, inputs: declaration.inputs, output: declaration.output, declaration };
201
- }
202
- if (declaration.kind === "action") {
203
- return {
204
- kind: "action",
205
- name: declaration.name,
206
- inputs: declaration.inputs,
207
- output: declaration.output,
208
- effects: declaration.touches,
209
- declaration,
210
- };
211
- }
212
- if (declaration.kind === "policy") {
213
- return { kind: "policy", name: declaration.name, inputs: declaration.inputs, output: { name: "allowed", type: boolType() }, declaration };
214
- }
215
- if (declaration.kind === "view" || declaration.kind === "route" || declaration.kind === "workflow" || declaration.kind === "command") {
216
- return { kind: declaration.kind, name: declaration.name, inputs: declaration.inputs, output: declaration.output, declaration };
217
- }
218
- return null;
219
- }
220
-
221
- function bindingSymbols(moduleName: string, ownerPath: string, bindings: PointSemanticBinding[]): PointSemanticSymbol[] {
222
- return bindings.map((binding) => ({
223
- ref: semanticRefFor(moduleName, `${ownerPath}.input.${binding.label}`),
224
- path: `${ownerPath}.input.${binding.label}`,
225
- kind: "param" as const,
226
- name: binding.label,
227
- module: moduleName,
228
- type: formatSemanticType(binding.type),
229
- span: binding.span ?? null,
230
- }));
231
- }
232
-
233
- function semanticSpanForDiagnosticPath(
234
- semantic: PointSemanticProgram,
235
- program: PointCoreProgram,
236
- path: string,
237
- ): PointSourceSpan | null {
238
- const fnMatch = path.match(/^fn\.([^.]+)(?:\.(.+))?$/);
239
- if (fnMatch) {
240
- const fnName = fnMatch[1] ?? "";
241
- const suffix = fnMatch[2] ?? "";
242
- const coreFn = program.declarations.find((declaration) => declaration.kind === "function" && declaration.name === fnName);
243
- if (!coreFn || coreFn.kind !== "function" || !coreFn.semantic) return coreFn?.span ?? null;
244
- const semanticDecl = semantic.declarations.find(
245
- (declaration) => declaration.kind === coreFn.semantic!.kind && "name" in declaration && declaration.name === coreFn.semantic!.name,
246
- );
247
- if (!semanticDecl) return coreFn.span ?? null;
248
- if (suffix.startsWith("if.condition") || suffix.endsWith(".condition")) {
249
- return findConditionSpan(semanticDecl) ?? coreFn.span ?? null;
250
- }
251
- if (suffix === "return" || suffix.endsWith(".return")) {
252
- return findReturnSpan(semanticDecl) ?? coreFn.span ?? null;
253
- }
254
- return ("span" in semanticDecl ? semanticDecl.span : null) ?? coreFn.span ?? null;
255
- }
256
- const typeMatch = path.match(/^type\.([^.]+)(?:\.(.+))?$/);
257
- if (typeMatch) {
258
- const typeName = typeMatch[1] ?? "";
259
- const fieldName = typeMatch[2];
260
- const coreType = program.declarations.find((declaration) => declaration.kind === "type" && declaration.name === typeName);
261
- if (fieldName && coreType?.kind === "type") {
262
- const field = coreType.fields.find((candidate) => candidate.name === fieldName);
263
- return field?.span ?? coreType.span ?? null;
264
- }
265
- return coreType?.kind === "type" ? coreType.span ?? null : null;
266
- }
267
- return null;
268
- }
269
-
270
- function findConditionSpan(declaration: PointSemanticDeclaration): PointSourceSpan | null {
271
- if (declaration.kind === "label") {
272
- for (const statement of declaration.body) {
273
- if (statement.kind === "whenReturn") return expressionSpan(statement.condition) ?? statement.span ?? null;
274
- }
275
- }
276
- if (declaration.kind === "view") {
277
- for (const statement of declaration.body) {
278
- if (statement.kind === "whenRender") return expressionSpan(statement.condition) ?? statement.span ?? null;
279
- }
280
- }
281
- if (declaration.kind === "rule") {
282
- for (const statement of declaration.body) {
283
- if (statement.kind === "addWhen") return expressionSpan(statement.condition) ?? statement.span ?? null;
284
- }
285
- }
286
- if (declaration.kind === "policy") {
287
- for (const statement of declaration.body) {
288
- if (statement.kind === "allow" || statement.kind === "deny" || statement.kind === "require") {
289
- return expressionSpan(statement.condition) ?? statement.span ?? null;
290
- }
291
- }
292
- }
293
- return null;
294
- }
295
-
296
- function findReturnSpan(declaration: PointSemanticDeclaration): PointSourceSpan | null {
297
- if ("body" in declaration && Array.isArray(declaration.body)) {
298
- for (const statement of declaration.body) {
299
- if (statement.kind === "return") return expressionSpan(statement.value) ?? statement.span ?? null;
300
- if (statement.kind === "otherwiseReturn") return expressionSpan(statement.value) ?? statement.span ?? null;
301
- if (statement.kind === "render") return expressionSpan(statement.value) ?? statement.span ?? null;
302
- }
303
- }
304
- return null;
305
- }
306
-
307
- function expressionSpan(expression: PointSemanticExpression): PointSourceSpan | null {
308
- return expression.span ?? null;
309
- }
310
-
311
- function relatedRefsFor(symbol: PointSemanticSymbol, index: PointSemanticIndex): string[] {
312
- if (symbol.kind === "field") {
313
- const ownerPath = symbol.path.split(".").slice(0, 2).join(".");
314
- return index.refs.filter((candidate) => candidate.path.startsWith(`${ownerPath}.`) && candidate.ref !== symbol.ref).map((candidate) => candidate.ref);
315
- }
316
- if (symbol.kind === "record" || symbol.kind === "calculation" || symbol.kind === "rule" || symbol.kind === "label" || symbol.kind === "action" || symbol.kind === "policy" || symbol.kind === "view" || symbol.kind === "route" || symbol.kind === "workflow" || symbol.kind === "command" || symbol.kind === "external") {
317
- return index.refs.filter((candidate) => candidate.path.startsWith(`${symbol.path}.`)).map((candidate) => candidate.ref);
318
- }
319
- return [];
320
- }
321
-
322
- function summaryFor(symbol: PointSemanticSymbol): string {
323
- if (symbol.kind === "module") return `Module ${symbol.name}.`;
324
- if (symbol.kind === "use") return `Use ${symbol.name}${symbol.from ? ` from ${symbol.from}` : ""}.`;
325
- if (symbol.kind === "record") return `Semantic record ${symbol.name}.`;
326
- if (symbol.kind === "field") return `Field ${symbol.name}: ${symbol.type}.`;
327
- if (symbol.kind === "param") return `Parameter ${symbol.name}: ${symbol.type}.`;
328
- if (symbol.kind === "external") return `External function ${symbol.name} from ${symbol.from} returns ${symbol.type}.`;
329
- if (symbol.kind === "calculation") return `Semantic calculation ${symbol.name} returns ${symbol.type}.`;
330
- if (symbol.kind === "rule") return `Semantic rule ${symbol.name} returns ${symbol.type}.`;
331
- if (symbol.kind === "label") return `Semantic label ${symbol.name} returns ${symbol.type}.`;
332
- if (symbol.kind === "action") return `Semantic action ${symbol.name} returns ${symbol.type}; effects: ${(symbol.effects ?? []).join(", ") || "none"}.`;
333
- if (symbol.kind === "policy") return `Semantic policy ${symbol.name} returns ${symbol.type}.`;
334
- if (symbol.kind === "view") return `Semantic view ${symbol.name} returns ${symbol.type}.`;
335
- if (symbol.kind === "route") return `Semantic route ${symbol.name} returns ${symbol.type}.`;
336
- if (symbol.kind === "workflow") return `Semantic workflow ${symbol.name} returns ${symbol.type}.`;
337
- if (symbol.kind === "command") return `Semantic command ${symbol.name} returns ${symbol.type}.`;
338
- return `Point symbol ${symbol.name}.`;
339
- }
340
-
341
- function semanticRefFor(moduleName: string, path: string): string {
342
- return `point://semantic/${moduleName}/${path}`;
343
- }
344
-
345
- function boolType(): PointSemanticTypeExpression {
346
- return { kind: "typeRef", name: "Bool", args: [] };
347
- }
1
+ import type { PointCoreProgram, PointSourceSpan } from "../core/ast.ts";
2
+ import type { PointCoreDiagnostic } from "../core/check.ts";
3
+ import { mapDiagnosticsToSemanticRefs } from "../core/context.ts";
4
+ import type {
5
+ PointSemanticBinding,
6
+ PointSemanticDeclaration,
7
+ PointSemanticExpression,
8
+ PointSemanticOutputBinding,
9
+ PointSemanticProgram,
10
+ PointSemanticTypeExpression,
11
+ } from "./ast.ts";
12
+
13
+ function formatSemanticType(type: PointSemanticTypeExpression): string {
14
+ if (type.args.length === 0) return type.name;
15
+ return `${type.name}<${type.args.map(formatSemanticType).join(", ")}>`;
16
+ }
17
+
18
+ export type PointSemanticSymbolKind =
19
+ | "module"
20
+ | "use"
21
+ | "record"
22
+ | "field"
23
+ | "calculation"
24
+ | "rule"
25
+ | "label"
26
+ | "external"
27
+ | "action"
28
+ | "policy"
29
+ | "view"
30
+ | "route"
31
+ | "workflow"
32
+ | "command"
33
+ | "param";
34
+
35
+ export interface PointSemanticSymbol {
36
+ ref: string;
37
+ path: string;
38
+ kind: PointSemanticSymbolKind;
39
+ name: string;
40
+ module: string;
41
+ type?: string;
42
+ from?: string;
43
+ effects?: string[];
44
+ span: PointSourceSpan | null;
45
+ }
46
+
47
+ export interface PointSemanticIndex {
48
+ schemaVersion: "point.semantic.index.v1";
49
+ module: string;
50
+ refs: PointSemanticSymbol[];
51
+ }
52
+
53
+ export interface PointSemanticExplanation {
54
+ schemaVersion: "point.semantic.explain.v1";
55
+ ref: string;
56
+ found: boolean;
57
+ symbol?: PointSemanticSymbol;
58
+ relatedRefs: string[];
59
+ summary: string;
60
+ }
61
+
62
+ export function createSemanticIndex(program: PointSemanticProgram): PointSemanticIndex {
63
+ const moduleName = program.module ?? "anonymous";
64
+ const refs: PointSemanticSymbol[] = [
65
+ {
66
+ ref: semanticRefFor(moduleName, "module"),
67
+ path: "module",
68
+ kind: "module",
69
+ name: moduleName,
70
+ module: moduleName,
71
+ span: program.span ?? null,
72
+ },
73
+ ...program.uses.map((use) => ({
74
+ ref: semanticRefFor(moduleName, `use.${use.moduleName}`),
75
+ path: `use.${use.moduleName}`,
76
+ kind: "use" as const,
77
+ name: use.moduleName,
78
+ module: moduleName,
79
+ from: use.from,
80
+ span: use.span ?? null,
81
+ })),
82
+ ];
83
+ for (const declaration of program.declarations) {
84
+ refs.push(...symbolsForDeclaration(moduleName, declaration));
85
+ }
86
+ return { schemaVersion: "point.semantic.index.v1", module: moduleName, refs };
87
+ }
88
+
89
+ export function explainSemanticRef(program: PointSemanticProgram, ref: string): PointSemanticExplanation {
90
+ const index = createSemanticIndex(program);
91
+ const symbol = index.refs.find((candidate) => candidate.ref === ref);
92
+ if (!symbol) {
93
+ return {
94
+ schemaVersion: "point.semantic.explain.v1",
95
+ ref,
96
+ found: false,
97
+ relatedRefs: [],
98
+ summary: `No Point symbol found for ${ref}.`,
99
+ };
100
+ }
101
+ const relatedRefs = relatedRefsFor(symbol, index);
102
+ return {
103
+ schemaVersion: "point.semantic.explain.v1",
104
+ ref,
105
+ found: true,
106
+ symbol,
107
+ relatedRefs,
108
+ summary: summaryFor(symbol),
109
+ };
110
+ }
111
+
112
+ export function mapPublicDiagnostics(program: PointCoreProgram, diagnostics: PointCoreDiagnostic[]): PointCoreDiagnostic[] {
113
+ const mapped = mapDiagnosticsToSemanticRefs(program, diagnostics);
114
+ if (!program.semanticSource) return mapped;
115
+ return mapped.map((diagnostic) => ({
116
+ ...diagnostic,
117
+ span: diagnostic.span ?? semanticSpanForDiagnosticPath(program.semanticSource!, program, diagnostic.path),
118
+ }));
119
+ }
120
+
121
+ function symbolsForDeclaration(moduleName: string, declaration: PointSemanticDeclaration): PointSemanticSymbol[] {
122
+ if (declaration.kind === "record") {
123
+ const recordPath = `record.${declaration.name}`;
124
+ return [
125
+ {
126
+ ref: semanticRefFor(moduleName, recordPath),
127
+ path: recordPath,
128
+ kind: "record",
129
+ name: declaration.name,
130
+ module: moduleName,
131
+ span: declaration.span ?? null,
132
+ },
133
+ ...declaration.fields.map((field) => ({
134
+ ref: semanticRefFor(moduleName, `${recordPath}.field.${field.label}`),
135
+ path: `${recordPath}.field.${field.label}`,
136
+ kind: "field" as const,
137
+ name: field.label,
138
+ module: moduleName,
139
+ type: formatSemanticType(field.type),
140
+ span: field.span ?? null,
141
+ })),
142
+ ];
143
+ }
144
+ if (declaration.kind === "external") {
145
+ return declaration.functions.flatMap((fn) => {
146
+ const path = `external.${fn.label}`;
147
+ return [
148
+ {
149
+ ref: semanticRefFor(moduleName, path),
150
+ path,
151
+ kind: "external" as const,
152
+ name: fn.label,
153
+ module: moduleName,
154
+ type: formatSemanticType(fn.returnType),
155
+ from: fn.from,
156
+ span: fn.span ?? null,
157
+ },
158
+ ...bindingSymbols(moduleName, path, fn.params),
159
+ ];
160
+ });
161
+ }
162
+ const callable = callableDeclaration(declaration);
163
+ if (!callable) return [];
164
+ const path = `${callable.kind}.${callable.name}`;
165
+ return [
166
+ {
167
+ ref: semanticRefFor(moduleName, path),
168
+ path,
169
+ kind: callable.kind,
170
+ name: callable.name,
171
+ module: moduleName,
172
+ type: formatSemanticType(callable.output.type),
173
+ effects: callable.effects,
174
+ span: callable.declaration.span ?? null,
175
+ },
176
+ ...bindingSymbols(moduleName, path, callable.inputs),
177
+ {
178
+ ref: semanticRefFor(moduleName, `${path}.output.${callable.output.name}`),
179
+ path: `${path}.output.${callable.output.name}`,
180
+ kind: "param",
181
+ name: callable.output.name,
182
+ module: moduleName,
183
+ type: formatSemanticType(callable.output.type),
184
+ span: callable.output.span ?? null,
185
+ },
186
+ ];
187
+ }
188
+
189
+ function callableDeclaration(declaration: PointSemanticDeclaration):
190
+ | {
191
+ kind: Exclude<PointSemanticSymbolKind, "module" | "use" | "record" | "field" | "external" | "param">;
192
+ name: string;
193
+ inputs: PointSemanticBinding[];
194
+ output: PointSemanticOutputBinding;
195
+ effects?: string[];
196
+ declaration: PointSemanticDeclaration;
197
+ }
198
+ | null {
199
+ if (declaration.kind === "calculation" || declaration.kind === "rule" || declaration.kind === "label") {
200
+ return { kind: declaration.kind, name: declaration.name, inputs: declaration.inputs, output: declaration.output, declaration };
201
+ }
202
+ if (declaration.kind === "action") {
203
+ return {
204
+ kind: "action",
205
+ name: declaration.name,
206
+ inputs: declaration.inputs,
207
+ output: declaration.output,
208
+ effects: declaration.touches,
209
+ declaration,
210
+ };
211
+ }
212
+ if (declaration.kind === "policy") {
213
+ return { kind: "policy", name: declaration.name, inputs: declaration.inputs, output: { name: "allowed", type: boolType() }, declaration };
214
+ }
215
+ if (declaration.kind === "view" || declaration.kind === "route" || declaration.kind === "workflow" || declaration.kind === "command") {
216
+ return { kind: declaration.kind, name: declaration.name, inputs: declaration.inputs, output: declaration.output, declaration };
217
+ }
218
+ return null;
219
+ }
220
+
221
+ function bindingSymbols(moduleName: string, ownerPath: string, bindings: PointSemanticBinding[]): PointSemanticSymbol[] {
222
+ return bindings.map((binding) => ({
223
+ ref: semanticRefFor(moduleName, `${ownerPath}.input.${binding.label}`),
224
+ path: `${ownerPath}.input.${binding.label}`,
225
+ kind: "param" as const,
226
+ name: binding.label,
227
+ module: moduleName,
228
+ type: formatSemanticType(binding.type),
229
+ span: binding.span ?? null,
230
+ }));
231
+ }
232
+
233
+ function semanticSpanForDiagnosticPath(
234
+ semantic: PointSemanticProgram,
235
+ program: PointCoreProgram,
236
+ path: string,
237
+ ): PointSourceSpan | null {
238
+ const fnMatch = path.match(/^fn\.([^.]+)(?:\.(.+))?$/);
239
+ if (fnMatch) {
240
+ const fnName = fnMatch[1] ?? "";
241
+ const suffix = fnMatch[2] ?? "";
242
+ const coreFn = program.declarations.find((declaration) => declaration.kind === "function" && declaration.name === fnName);
243
+ if (!coreFn || coreFn.kind !== "function" || !coreFn.semantic) return coreFn?.span ?? null;
244
+ const semanticDecl = semantic.declarations.find(
245
+ (declaration) => declaration.kind === coreFn.semantic!.kind && "name" in declaration && declaration.name === coreFn.semantic!.name,
246
+ );
247
+ if (!semanticDecl) return coreFn.span ?? null;
248
+ if (suffix.startsWith("if.condition") || suffix.endsWith(".condition")) {
249
+ return findConditionSpan(semanticDecl) ?? coreFn.span ?? null;
250
+ }
251
+ if (suffix === "return" || suffix.endsWith(".return")) {
252
+ return findReturnSpan(semanticDecl) ?? coreFn.span ?? null;
253
+ }
254
+ return ("span" in semanticDecl ? semanticDecl.span : null) ?? coreFn.span ?? null;
255
+ }
256
+ const typeMatch = path.match(/^type\.([^.]+)(?:\.(.+))?$/);
257
+ if (typeMatch) {
258
+ const typeName = typeMatch[1] ?? "";
259
+ const fieldName = typeMatch[2];
260
+ const coreType = program.declarations.find((declaration) => declaration.kind === "type" && declaration.name === typeName);
261
+ if (fieldName && coreType?.kind === "type") {
262
+ const field = coreType.fields.find((candidate) => candidate.name === fieldName);
263
+ return field?.span ?? coreType.span ?? null;
264
+ }
265
+ return coreType?.kind === "type" ? coreType.span ?? null : null;
266
+ }
267
+ return null;
268
+ }
269
+
270
+ function findConditionSpan(declaration: PointSemanticDeclaration): PointSourceSpan | null {
271
+ if (declaration.kind === "label") {
272
+ for (const statement of declaration.body) {
273
+ if (statement.kind === "whenReturn") return expressionSpan(statement.condition) ?? statement.span ?? null;
274
+ }
275
+ }
276
+ if (declaration.kind === "view") {
277
+ for (const statement of declaration.body) {
278
+ if (statement.kind === "whenRender") return expressionSpan(statement.condition) ?? statement.span ?? null;
279
+ }
280
+ }
281
+ if (declaration.kind === "rule") {
282
+ for (const statement of declaration.body) {
283
+ if (statement.kind === "addWhen") return expressionSpan(statement.condition) ?? statement.span ?? null;
284
+ }
285
+ }
286
+ if (declaration.kind === "policy") {
287
+ for (const statement of declaration.body) {
288
+ if (statement.kind === "allow" || statement.kind === "deny" || statement.kind === "require") {
289
+ return expressionSpan(statement.condition) ?? statement.span ?? null;
290
+ }
291
+ }
292
+ }
293
+ return null;
294
+ }
295
+
296
+ function findReturnSpan(declaration: PointSemanticDeclaration): PointSourceSpan | null {
297
+ if ("body" in declaration && Array.isArray(declaration.body)) {
298
+ for (const statement of declaration.body) {
299
+ if (statement.kind === "return") return expressionSpan(statement.value) ?? statement.span ?? null;
300
+ if (statement.kind === "otherwiseReturn") return expressionSpan(statement.value) ?? statement.span ?? null;
301
+ if (statement.kind === "render") return expressionSpan(statement.value) ?? statement.span ?? null;
302
+ }
303
+ }
304
+ return null;
305
+ }
306
+
307
+ function expressionSpan(expression: PointSemanticExpression): PointSourceSpan | null {
308
+ return expression.span ?? null;
309
+ }
310
+
311
+ function relatedRefsFor(symbol: PointSemanticSymbol, index: PointSemanticIndex): string[] {
312
+ if (symbol.kind === "field") {
313
+ const ownerPath = symbol.path.split(".").slice(0, 2).join(".");
314
+ return index.refs.filter((candidate) => candidate.path.startsWith(`${ownerPath}.`) && candidate.ref !== symbol.ref).map((candidate) => candidate.ref);
315
+ }
316
+ if (symbol.kind === "record" || symbol.kind === "calculation" || symbol.kind === "rule" || symbol.kind === "label" || symbol.kind === "action" || symbol.kind === "policy" || symbol.kind === "view" || symbol.kind === "route" || symbol.kind === "workflow" || symbol.kind === "command" || symbol.kind === "external") {
317
+ return index.refs.filter((candidate) => candidate.path.startsWith(`${symbol.path}.`)).map((candidate) => candidate.ref);
318
+ }
319
+ return [];
320
+ }
321
+
322
+ function summaryFor(symbol: PointSemanticSymbol): string {
323
+ if (symbol.kind === "module") return `Module ${symbol.name}.`;
324
+ if (symbol.kind === "use") return `Use ${symbol.name}${symbol.from ? ` from ${symbol.from}` : ""}.`;
325
+ if (symbol.kind === "record") return `Semantic record ${symbol.name}.`;
326
+ if (symbol.kind === "field") return `Field ${symbol.name}: ${symbol.type}.`;
327
+ if (symbol.kind === "param") return `Parameter ${symbol.name}: ${symbol.type}.`;
328
+ if (symbol.kind === "external") return `External function ${symbol.name} from ${symbol.from} returns ${symbol.type}.`;
329
+ if (symbol.kind === "calculation") return `Semantic calculation ${symbol.name} returns ${symbol.type}.`;
330
+ if (symbol.kind === "rule") return `Semantic rule ${symbol.name} returns ${symbol.type}.`;
331
+ if (symbol.kind === "label") return `Semantic label ${symbol.name} returns ${symbol.type}.`;
332
+ if (symbol.kind === "action") return `Semantic action ${symbol.name} returns ${symbol.type}; effects: ${(symbol.effects ?? []).join(", ") || "none"}.`;
333
+ if (symbol.kind === "policy") return `Semantic policy ${symbol.name} returns ${symbol.type}.`;
334
+ if (symbol.kind === "view") return `Semantic view ${symbol.name} returns ${symbol.type}.`;
335
+ if (symbol.kind === "route") return `Semantic route ${symbol.name} returns ${symbol.type}.`;
336
+ if (symbol.kind === "workflow") return `Semantic workflow ${symbol.name} returns ${symbol.type}.`;
337
+ if (symbol.kind === "command") return `Semantic command ${symbol.name} returns ${symbol.type}.`;
338
+ return `Point symbol ${symbol.name}.`;
339
+ }
340
+
341
+ function semanticRefFor(moduleName: string, path: string): string {
342
+ return `point://semantic/${moduleName}/${path}`;
343
+ }
344
+
345
+ function boolType(): PointSemanticTypeExpression {
346
+ return { kind: "typeRef", name: "Bool", args: [] };
347
+ }