@jhlagado/azm 0.2.0 → 0.2.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 (36) hide show
  1. package/README.md +95 -70
  2. package/dist/src/api-compile.js +1 -1
  3. package/dist/src/assembly/address-planning.js +2 -0
  4. package/dist/src/assembly/program-emission.js +1 -0
  5. package/dist/src/expansion/op-expansion.js +1 -0
  6. package/dist/src/model/source-item.d.ts +6 -0
  7. package/dist/src/outputs/write-asm80.js +122 -5
  8. package/dist/src/register-care/analyze.js +36 -8
  9. package/dist/src/register-care/annotate.d.ts +11 -0
  10. package/dist/src/register-care/annotate.js +76 -0
  11. package/dist/src/register-care/annotations.js +33 -146
  12. package/dist/src/register-care/fix.d.ts +2 -0
  13. package/dist/src/register-care/fix.js +52 -0
  14. package/dist/src/register-care/instruction-shape.d.ts +11 -0
  15. package/dist/src/register-care/instruction-shape.js +129 -0
  16. package/dist/src/register-care/liveness.js +15 -7
  17. package/dist/src/register-care/profiles.js +4 -0
  18. package/dist/src/register-care/programModel.js +79 -13
  19. package/dist/src/register-care/report.d.ts +2 -1
  20. package/dist/src/register-care/report.js +91 -34
  21. package/dist/src/register-care/routine-summaries.d.ts +6 -0
  22. package/dist/src/register-care/routine-summaries.js +89 -0
  23. package/dist/src/register-care/sourceText.d.ts +8 -0
  24. package/dist/src/register-care/sourceText.js +15 -0
  25. package/dist/src/register-care/summaries.d.ts +3 -3
  26. package/dist/src/register-care/summaries.js +42 -75
  27. package/dist/src/register-care/summary.d.ts +3 -0
  28. package/dist/src/register-care/summary.js +474 -0
  29. package/dist/src/register-care/types.d.ts +6 -1
  30. package/dist/src/source/strip-line-comment.d.ts +2 -0
  31. package/dist/src/source/strip-line-comment.js +26 -0
  32. package/dist/src/syntax/parse-diagnostics.d.ts +12 -0
  33. package/dist/src/syntax/parse-diagnostics.js +18 -0
  34. package/dist/src/syntax/parse-line.js +63 -10
  35. package/docs/reference/tooling-api.md +13 -6
  36. package/package.json +4 -2
@@ -1,23 +1,94 @@
1
- function listUnits(units) {
1
+ function list(units) {
2
2
  return units.length === 0 ? '-' : units.join(',');
3
3
  }
4
+ const FLAG_UNITS = new Set(['carry', 'zero', 'sign', 'parity', 'halfCarry']);
5
+ const CONTRACT_CARRIER_PAIRS = [
6
+ { label: 'BC', hi: 'B', lo: 'C' },
7
+ { label: 'DE', hi: 'D', lo: 'E' },
8
+ { label: 'HL', hi: 'H', lo: 'L' },
9
+ { label: 'IX', hi: 'IXH', lo: 'IXL' },
10
+ { label: 'IY', hi: 'IYH', lo: 'IYL' },
11
+ { label: 'SP', hi: 'SPH', lo: 'SPL' },
12
+ ];
13
+ export function contractCarrierList(units) {
14
+ const unique = [...new Set(units)];
15
+ const unitSet = new Set(unique);
16
+ const emitted = new Set();
17
+ const parts = [];
18
+ for (const unit of unique) {
19
+ if (emitted.has(unit))
20
+ continue;
21
+ const pair = CONTRACT_CARRIER_PAIRS.find((candidate) => (candidate.hi === unit || candidate.lo === unit) &&
22
+ unitSet.has(candidate.hi) &&
23
+ unitSet.has(candidate.lo));
24
+ if (pair) {
25
+ parts.push(pair.label);
26
+ emitted.add(pair.hi);
27
+ emitted.add(pair.lo);
28
+ continue;
29
+ }
30
+ parts.push(unit);
31
+ emitted.add(unit);
32
+ }
33
+ return parts.length === 0 ? '-' : parts.join(',');
34
+ }
35
+ function relationOutputUnits(relations) {
36
+ return relations.flatMap((rel) => rel.out);
37
+ }
38
+ function contractEntries(summary) {
39
+ const out = [];
40
+ if (summary.mayRead.length > 0)
41
+ out.push({ keyword: 'in', carriers: contractCarrierList(summary.mayRead) });
42
+ const outputUnits = relationOutputUnits(summary.valueRelations);
43
+ if (outputUnits.length > 0)
44
+ out.push({ keyword: 'out', carriers: contractCarrierList(outputUnits) });
45
+ const relationOut = relationOutUnits(summary);
46
+ const clobbers = summary.mayWrite.filter((unit) => !relationOut.has(unit));
47
+ if (clobbers.length > 0)
48
+ out.push({ keyword: 'clobbers', carriers: contractCarrierList(clobbers) });
49
+ return out;
50
+ }
51
+ function sourceContractEntries(summary) {
52
+ const out = [];
53
+ if (summary.mayRead.length > 0)
54
+ out.push({ keyword: 'in', carriers: contractCarrierList(summary.mayRead) });
55
+ const relationOut = relationOutUnits(summary);
56
+ const candidates = (summary.outputCandidates ?? []).filter((unit) => !relationOut.has(unit));
57
+ if (candidates.length > 0)
58
+ out.push({ keyword: 'maybe-out', carriers: contractCarrierList(candidates) });
59
+ const outputUnits = relationOutputUnits(summary.valueRelations);
60
+ if (outputUnits.length > 0)
61
+ out.push({ keyword: 'out', carriers: contractCarrierList(outputUnits) });
62
+ const clobbers = summary.mayWrite.filter((unit) => !relationOut.has(unit) && !FLAG_UNITS.has(unit));
63
+ if (clobbers.length > 0)
64
+ out.push({ keyword: 'clobbers', carriers: contractCarrierList(clobbers) });
65
+ return out;
66
+ }
67
+ function stackStatus(summary) {
68
+ const balance = summary.stackBalanced ? 'balanced' : 'unbalanced';
69
+ return summary.hasUnknownStackEffect ? `${balance}, unknown effect` : balance;
70
+ }
71
+ function relationOutUnits(summary) {
72
+ return new Set(summary.valueRelations.flatMap((rel) => rel.out));
73
+ }
4
74
  export function renderRegisterCareReport(model) {
5
- const lines = [
6
- 'AZM Register-Care Report',
7
- `Entry: ${model.entryFile}`,
8
- `Mode: ${model.mode}`,
9
- ...(model.profile !== undefined ? [`Profile: ${model.profile}`] : []),
10
- '',
11
- ];
75
+ const lines = ['AZM Register-Care Report', `Entry: ${model.entryFile}`, `Mode: ${model.mode}`];
76
+ if (model.profile)
77
+ lines.push(`Profile: ${model.profile}`);
78
+ lines.push('');
12
79
  if (model.summaries.length === 0) {
13
80
  lines.push('Routines: none', '');
14
81
  }
15
82
  else {
16
83
  for (const summary of model.summaries) {
17
84
  lines.push(`Routine: ${summary.name}`);
18
- lines.push(` reads: ${listUnits(summary.mayRead)}`);
19
- lines.push(` writes: ${listUnits(summary.mayWrite)}`);
20
- lines.push(` preserves: ${listUnits(summary.preserved)}`);
85
+ lines.push(` reads: ${list(summary.mayRead)}`);
86
+ lines.push(` writes: ${list(summary.mayWrite)}`);
87
+ lines.push(` preserves: ${list(summary.preserved)}`);
88
+ lines.push(` stack: ${stackStatus(summary)}`);
89
+ for (const rel of summary.valueRelations) {
90
+ lines.push(` relation: ${list(rel.out)} <= ${list(rel.from)}`);
91
+ }
21
92
  lines.push('');
22
93
  }
23
94
  }
@@ -27,17 +98,17 @@ export function renderRegisterCareReport(model) {
27
98
  }
28
99
  else {
29
100
  for (const conflict of model.conflicts) {
30
- lines.push(` ${conflict.file}:${conflict.line}:${conflict.column}: ${conflict.callTarget}: ${conflict.message}`);
101
+ lines.push(` ${conflict.file}:${conflict.line}:${conflict.column}: ${conflict.callTarget}: ${list(conflict.carriers)}: ${conflict.message}`);
31
102
  }
32
103
  }
33
104
  lines.push('');
34
105
  lines.push('Output candidates:');
35
- if (model.outputCandidates === undefined || model.outputCandidates.length === 0) {
106
+ if (!model.outputCandidates || model.outputCandidates.length === 0) {
36
107
  lines.push(' none');
37
108
  }
38
109
  else {
39
110
  for (const candidate of model.outputCandidates) {
40
- lines.push(` ${candidate.file}:${candidate.line}:${candidate.column}: ${candidate.routine}: ${candidate.carriers.join(',')}: ${candidate.message}`);
111
+ lines.push(` ${candidate.file}:${candidate.line}:${candidate.column}: ${candidate.routine}: ${list(candidate.carriers)}: ${candidate.message}`);
41
112
  }
42
113
  }
43
114
  lines.push('');
@@ -46,37 +117,23 @@ export function renderRegisterCareReport(model) {
46
117
  lines.push(' none');
47
118
  }
48
119
  else {
49
- for (const call of model.unknownCalls) {
120
+ for (const call of model.unknownCalls)
50
121
  lines.push(` ${call}`);
51
- }
52
122
  }
53
123
  lines.push('');
54
124
  return `${lines.join('\n')}\n`;
55
125
  }
56
- export function contractCarrierList(units) {
57
- return units.length === 0 ? '-' : units.join(',');
58
- }
59
126
  export function renderRegisterCareInterface(summaries) {
60
127
  const lines = [];
61
128
  for (const summary of summaries) {
62
- if (summary.mayRead.length === 0 &&
63
- summary.mayWrite.length === 0 &&
64
- summary.preserved.length === 0) {
65
- continue;
66
- }
67
129
  lines.push(`extern ${summary.name}`);
68
- if (summary.mayRead.length > 0) {
69
- lines.push(`in ${contractCarrierList(summary.mayRead)}`);
70
- }
71
- if (summary.mayWrite.length > 0) {
72
- lines.push(`clobbers ${contractCarrierList(summary.mayWrite)}`);
73
- }
74
- if (summary.preserved.length > 0) {
75
- lines.push(`preserves ${contractCarrierList(summary.preserved)}`);
130
+ for (const entry of contractEntries(summary)) {
131
+ lines.push(`${entry.keyword} ${entry.carriers}`);
76
132
  }
77
133
  lines.push('end', '');
78
134
  }
79
- if (lines.length === 0)
80
- lines.push('No inferred contracts were emitted.', '');
81
135
  return `${lines.join('\n')}\n`;
82
136
  }
137
+ export function renderRegisterCareSourceBlock(summary) {
138
+ return sourceContractEntries(summary).map((entry) => `;! ${entry.keyword.padEnd(10)}${entry.carriers}`);
139
+ }
@@ -0,0 +1,6 @@
1
+ import type { RegisterCareRoutine, RoutineContract, RoutineSummary } from './types.js';
2
+ export declare function summariesWithExternalContracts(summaries: RoutineSummary[], contracts: Map<string, RoutineContract>, routineNameSet: Set<string>): RoutineSummary[];
3
+ export declare function inferRoutineSummariesToFixedPoint(routines: RegisterCareRoutine[], contracts: Map<string, RoutineContract>, routineNameSet: Set<string>, profileSummaries: RoutineSummary[]): Array<{
4
+ routine: RegisterCareRoutine;
5
+ summary: RoutineSummary;
6
+ }>;
@@ -0,0 +1,89 @@
1
+ import { applyRoutineContract, inferRoutineSummary } from './summary.js';
2
+ function emptyRoutineSummary(name) {
3
+ return {
4
+ name,
5
+ mayRead: [],
6
+ mayWrite: [],
7
+ preserved: [],
8
+ valueRelations: [],
9
+ stackBalanced: true,
10
+ hasUnknownStackEffect: false,
11
+ };
12
+ }
13
+ function isLocalLabel(name) {
14
+ return name.startsWith('.');
15
+ }
16
+ function nonLocalLabels(labels) {
17
+ return labels.filter((label) => !isLocalLabel(label));
18
+ }
19
+ function boundaryLabels(routine) {
20
+ return routine.entryLabels.length > 0 ? routine.entryLabels : nonLocalLabels(routine.labels);
21
+ }
22
+ function contractForRoutine(routine, contracts) {
23
+ return boundaryLabels(routine)
24
+ .map((label) => contracts.get(label))
25
+ .find((contract) => contract !== undefined);
26
+ }
27
+ export function summariesWithExternalContracts(summaries, contracts, routineNameSet) {
28
+ const out = [...summaries];
29
+ for (const contract of contracts.values()) {
30
+ if (!routineNameSet.has(contract.name)) {
31
+ out.push(applyRoutineContract(emptyRoutineSummary(contract.name), contract));
32
+ }
33
+ }
34
+ return out;
35
+ }
36
+ function buildBoundarySummaryMap(summaries, routineSummaries, profileSummaries) {
37
+ const boundarySummaryMap = new Map(profileSummaries.map((summary) => [summary.name, summary]));
38
+ for (const summary of summaries) {
39
+ boundarySummaryMap.set(summary.name, summary);
40
+ }
41
+ for (const { routine, summary } of routineSummaries) {
42
+ for (const label of boundaryLabels(routine)) {
43
+ boundarySummaryMap.set(label, summary);
44
+ }
45
+ }
46
+ return boundarySummaryMap;
47
+ }
48
+ function summarizeRoutines(routines, contracts, boundarySummaryMap = new Map()) {
49
+ return routines.map((routine) => {
50
+ const inferred = inferRoutineSummary(routine, boundarySummaryMap);
51
+ const contract = contractForRoutine(routine, contracts);
52
+ return { routine, summary: contract ? applyRoutineContract(inferred, contract) : inferred };
53
+ });
54
+ }
55
+ function sortedUnique(values) {
56
+ return Array.from(new Set(values)).sort();
57
+ }
58
+ function summaryFingerprint(summary) {
59
+ const relations = summary.valueRelations
60
+ .map((relation) => `${relation.out.join(',')}<-${relation.from.join(',')}`)
61
+ .sort();
62
+ return JSON.stringify({
63
+ name: summary.name,
64
+ mayRead: sortedUnique(summary.mayRead),
65
+ mayWrite: sortedUnique(summary.mayWrite),
66
+ preserved: sortedUnique(summary.preserved),
67
+ relations,
68
+ stackBalanced: summary.stackBalanced,
69
+ hasUnknownStackEffect: summary.hasUnknownStackEffect,
70
+ });
71
+ }
72
+ function routineSummariesFingerprint(routineSummaries) {
73
+ return routineSummaries.map((item) => summaryFingerprint(item.summary)).join('\n');
74
+ }
75
+ export function inferRoutineSummariesToFixedPoint(routines, contracts, routineNameSet, profileSummaries) {
76
+ let routineSummaries = summarizeRoutines(routines, contracts);
77
+ const maxPasses = Math.max(2, routines.length + 2);
78
+ for (let pass = 0; pass < maxPasses; pass += 1) {
79
+ const summaries = summariesWithExternalContracts(routineSummaries.map((item) => item.summary), contracts, routineNameSet);
80
+ const boundarySummaryMap = buildBoundarySummaryMap(summaries, routineSummaries, profileSummaries);
81
+ const nextRoutineSummaries = summarizeRoutines(routines, contracts, boundarySummaryMap);
82
+ if (routineSummariesFingerprint(nextRoutineSummaries) ===
83
+ routineSummariesFingerprint(routineSummaries)) {
84
+ return nextRoutineSummaries;
85
+ }
86
+ routineSummaries = nextRoutineSummaries;
87
+ }
88
+ return routineSummaries;
89
+ }
@@ -0,0 +1,8 @@
1
+ type SourceLines = {
2
+ lines: string[];
3
+ trailingNewline: boolean;
4
+ eol: '\n' | '\r\n';
5
+ };
6
+ export declare function splitSourceLines(text: string): SourceLines;
7
+ export declare function joinSourceLines({ lines, trailingNewline, eol }: SourceLines): string;
8
+ export {};
@@ -0,0 +1,15 @@
1
+ function lineEnding(text) {
2
+ return text.includes('\r\n') ? '\r\n' : '\n';
3
+ }
4
+ export function splitSourceLines(text) {
5
+ const eol = lineEnding(text);
6
+ const trailingNewline = text.endsWith('\n');
7
+ const lines = text.split(/\r?\n/);
8
+ if (trailingNewline)
9
+ lines.pop();
10
+ return { lines, trailingNewline, eol };
11
+ }
12
+ export function joinSourceLines({ lines, trailingNewline, eol }) {
13
+ const text = lines.join(eol);
14
+ return trailingNewline ? `${text}${eol}` : text;
15
+ }
@@ -3,10 +3,10 @@ import type { AnalyzeRegisterCareOptions, RegisterCareDirectCall, RegisterCareRo
3
3
  export declare function buildProfileSummaries(profileName: AnalyzeRegisterCareOptions['registerCareProfile']): RoutineSummary[];
4
4
  export declare function buildProfileSummaryLookup(profileName: AnalyzeRegisterCareOptions['registerCareProfile']): Map<string, RoutineSummary>;
5
5
  export declare function routineNames(routines: readonly RegisterCareRoutine[]): string[];
6
- export declare function buildSummaries(routines: readonly RegisterCareRoutine[], contractMap: Map<string, RoutineContract>): RoutineSummary[];
6
+ export declare function buildSummaries(routines: readonly RegisterCareRoutine[], contractMap: Map<string, RoutineContract>, profileSummaries?: readonly RoutineSummary[]): RoutineSummary[];
7
7
  export declare function buildSummaryByName(routines: readonly RegisterCareRoutine[], summaries: readonly RoutineSummary[], profileSummaries?: readonly RoutineSummary[]): Map<string, RoutineSummary>;
8
8
  export declare function withAcceptedOutputs(summaries: readonly RoutineSummary[], acceptedOutputCandidates: ReadonlyMap<string, RegisterCareUnit[]> | undefined): RoutineSummary[];
9
- export declare function unknownBoundaryDiagnostics(directCalls: readonly RegisterCareDirectCall[], knownRoutines: ReadonlySet<string>): Diagnostic[];
10
- export declare function unknownCallList(directCalls: readonly RegisterCareDirectCall[], knownRoutines: ReadonlySet<string>): string[];
9
+ export declare function unknownBoundaryDiagnostics(directBoundaries: readonly RegisterCareDirectCall[], knownRoutines: ReadonlySet<string>): Diagnostic[];
10
+ export declare function unknownCallList(directBoundaries: readonly RegisterCareDirectCall[], knownRoutines: ReadonlySet<string>): string[];
11
11
  export declare function buildOutputCandidateFixability(routines: readonly RegisterCareRoutine[], outputCandidates: readonly RegisterCareOutputCandidate[], autoFixableCandidateKeys: (routines: RegisterCareRoutine[], outputCandidates: RegisterCareOutputCandidate[]) => ReadonlySet<string>): ReadonlyMap<string, boolean>;
12
12
  export declare function outputCandidateKey(file: string, line: number, column: number): string;
@@ -1,5 +1,5 @@
1
1
  import { getRegisterCareProfile } from './profiles.js';
2
- import { getZ80InstructionEffect } from '../z80/effects.js';
2
+ import { inferRoutineSummariesToFixedPoint, summariesWithExternalContracts, } from './routine-summaries.js';
3
3
  function unique(values) {
4
4
  const out = [];
5
5
  for (const value of values) {
@@ -8,22 +8,16 @@ function unique(values) {
8
8
  }
9
9
  return out;
10
10
  }
11
- function inferRoutineSummary(routine) {
12
- const reads = new Set();
13
- const writes = new Set();
14
- for (const instruction of routine.instructions) {
15
- const effect = getZ80InstructionEffect(instruction.instruction);
16
- for (const unit of effect.reads)
17
- reads.add(unit);
18
- for (const unit of effect.writes)
19
- writes.add(unit);
20
- }
21
- return {
22
- name: routine.name,
23
- mayRead: Array.from(reads),
24
- mayWrite: Array.from(writes),
25
- preserved: [],
26
- };
11
+ function isLocalLabel(name) {
12
+ return name.startsWith('.');
13
+ }
14
+ function boundaryLabels(routine) {
15
+ return routine.entryLabels.length > 0
16
+ ? routine.entryLabels
17
+ : routine.labels.filter((label) => !isLocalLabel(label));
18
+ }
19
+ function routineNameSet(routines) {
20
+ return new Set(routines.flatMap((routine) => boundaryLabels(routine)));
27
21
  }
28
22
  export function buildProfileSummaries(profileName) {
29
23
  const profile = getRegisterCareProfile(profileName);
@@ -46,48 +40,13 @@ export function buildProfileSummaryLookup(profileName) {
46
40
  return out;
47
41
  }
48
42
  export function routineNames(routines) {
49
- return routines.flatMap((routine) => routine.entryLabels.length > 0 ? routine.entryLabels : [routine.name]);
43
+ return routines.flatMap((routine) => boundaryLabels(routine));
50
44
  }
51
- function entryContract(routine, contractMap) {
52
- for (const label of routine.entryLabels.length > 0 ? routine.entryLabels : [routine.name]) {
53
- const contract = contractMap.get(label);
54
- if (contract !== undefined)
55
- return contract;
56
- }
57
- return contractMap.get(routine.name);
58
- }
59
- export function buildSummaries(routines, contractMap) {
60
- const out = [];
61
- const written = new Set();
62
- for (const routine of routines) {
63
- const inferred = inferRoutineSummary(routine);
64
- const contract = entryContract(routine, contractMap);
65
- out.push({
66
- name: routine.name,
67
- mayRead: unique([...inferred.mayRead, ...(contract?.in ?? [])]),
68
- mayWrite: unique([
69
- ...inferred.mayWrite,
70
- ...(contract?.out ?? []),
71
- ...(contract?.clobbers ?? []),
72
- ]),
73
- preserved: unique([...inferred.preserved, ...(contract?.preserves ?? [])]),
74
- });
75
- written.add(routine.name);
76
- for (const alias of routine.entryLabels)
77
- written.add(alias);
78
- }
79
- for (const [name, contract] of contractMap) {
80
- if (written.has(name))
81
- continue;
82
- out.push({
83
- name,
84
- mayRead: [...contract.in],
85
- mayWrite: [...contract.out, ...contract.clobbers],
86
- preserved: [...contract.preserves],
87
- });
88
- written.add(name);
89
- }
90
- return out;
45
+ export function buildSummaries(routines, contractMap, profileSummaries = []) {
46
+ const names = routineNameSet(routines);
47
+ const routineSummaries = inferRoutineSummariesToFixedPoint([...routines], contractMap, names, [...profileSummaries]);
48
+ const summaries = routineSummaries.map((item) => item.summary);
49
+ return summariesWithExternalContracts(summaries, contractMap, names);
91
50
  }
92
51
  export function buildSummaryByName(routines, summaries, profileSummaries = []) {
93
52
  const out = new Map();
@@ -103,7 +62,7 @@ export function buildSummaryByName(routines, summaries, profileSummaries = []) {
103
62
  const routineSummary = byRoutine.get(routine.name);
104
63
  if (routineSummary === undefined)
105
64
  continue;
106
- for (const alias of routine.entryLabels.length > 0 ? routine.entryLabels : [routine.name]) {
65
+ for (const alias of boundaryLabels(routine)) {
107
66
  out.set(alias, routineSummary);
108
67
  }
109
68
  }
@@ -118,28 +77,36 @@ export function withAcceptedOutputs(summaries, acceptedOutputCandidates) {
118
77
  if (!accepted || accepted.length === 0) {
119
78
  return summary;
120
79
  }
121
- const merged = unique([...summary.mayWrite, ...accepted]);
122
- return {
123
- ...summary,
124
- mayWrite: merged,
125
- mayOutput: unique([...accepted]),
126
- };
80
+ const written = new Set(summary.mayWrite);
81
+ const promoted = accepted.filter((unit) => written.has(unit));
82
+ if (promoted.length === 0) {
83
+ return summary;
84
+ }
85
+ const valueRelations = [...summary.valueRelations];
86
+ for (const unit of promoted) {
87
+ if (!valueRelations.some((relation) => relation.out.includes(unit))) {
88
+ valueRelations.push({ out: [unit], from: [] });
89
+ }
90
+ }
91
+ return { ...summary, valueRelations };
127
92
  });
128
93
  }
129
- export function unknownBoundaryDiagnostics(directCalls, knownRoutines) {
130
- return directCalls
131
- .filter((call) => !knownRoutines.has(call.target))
132
- .map((call) => ({
94
+ export function unknownBoundaryDiagnostics(directBoundaries, knownRoutines) {
95
+ return directBoundaries
96
+ .filter((boundary) => !knownRoutines.has(boundary.target))
97
+ .map((boundary) => ({
133
98
  severity: 'warning',
134
99
  code: 'AZMN_REGISTER_CARE',
135
- message: `Register-care cannot prove ${call.target}; add a routine body or .asmi extern contract.`,
136
- sourceName: call.file,
137
- line: call.line,
138
- column: call.column,
100
+ message: `Register-care cannot prove ${boundary.subject}; add a routine body or .asmi extern contract.`,
101
+ sourceName: boundary.file,
102
+ line: boundary.line,
103
+ column: boundary.column,
139
104
  }));
140
105
  }
141
- export function unknownCallList(directCalls, knownRoutines) {
142
- return unique(directCalls.filter((call) => !knownRoutines.has(call.target)).map((call) => call.target)).sort();
106
+ export function unknownCallList(directBoundaries, knownRoutines) {
107
+ return unique(directBoundaries
108
+ .filter((boundary) => !knownRoutines.has(boundary.target))
109
+ .map((boundary) => boundary.target)).sort();
143
110
  }
144
111
  export function buildOutputCandidateFixability(routines, outputCandidates, autoFixableCandidateKeys) {
145
112
  const autoFixable = autoFixableCandidateKeys([...routines], [...outputCandidates]);
@@ -0,0 +1,3 @@
1
+ import type { RegisterCareRoutine, RoutineContract, RoutineSummary } from './types.js';
2
+ export declare function inferRoutineSummary(routine: RegisterCareRoutine, boundarySummaries?: ReadonlyMap<string, RoutineSummary>): RoutineSummary;
3
+ export declare function applyRoutineContract(summary: RoutineSummary, contract: RoutineContract): RoutineSummary;