@jhlagado/azm 0.2.9 → 0.2.10
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 +7 -9
- package/dist/src/register-contracts/report.js +15 -3
- package/dist/src/register-contracts/smartCommentParsing.d.ts +1 -0
- package/dist/src/register-contracts/smartCommentParsing.js +42 -7
- package/dist/src/register-contracts/smartComments.d.ts +2 -2
- package/dist/src/register-contracts/smartComments.js +3 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -120,9 +120,7 @@ Use `@Name:` for callable routine entries. The `@` marks a register contracts
|
|
|
120
120
|
routine boundary; call sites still write the symbol name without `@`:
|
|
121
121
|
|
|
122
122
|
```asm
|
|
123
|
-
;!
|
|
124
|
-
;! out A
|
|
125
|
-
;! clobbers BC
|
|
123
|
+
;! in A; out A; clobbers BC
|
|
126
124
|
@MxMask:
|
|
127
125
|
LD C,A
|
|
128
126
|
OR A
|
|
@@ -406,9 +404,7 @@ register and stack assumptions visible before they become debugger sessions.
|
|
|
406
404
|
Routine entry labels start with `@`:
|
|
407
405
|
|
|
408
406
|
```asm
|
|
409
|
-
;! in A,HL
|
|
410
|
-
;! out carry
|
|
411
|
-
;! clobbers B
|
|
407
|
+
;! in A,HL; out carry; clobbers B
|
|
412
408
|
@CheckTile:
|
|
413
409
|
ld b,(hl)
|
|
414
410
|
cp b
|
|
@@ -423,9 +419,11 @@ name:
|
|
|
423
419
|
```
|
|
424
420
|
|
|
425
421
|
AZMDoc register contract comments use `;!` and may record inputs, outputs,
|
|
426
|
-
clobbered registers and preserved registers.
|
|
427
|
-
|
|
428
|
-
|
|
422
|
+
clobbered registers and preserved registers. Separate clauses on the same line
|
|
423
|
+
with semicolons. Older one-clause-per-line comments are still accepted, but AZM
|
|
424
|
+
generated annotations use the compact single-line form. `clobbers B` means the
|
|
425
|
+
routine may change `B`. `preserves B` means the value that enters in `B` is
|
|
426
|
+
still present when the routine returns.
|
|
429
427
|
|
|
430
428
|
Run the analysis with:
|
|
431
429
|
|
|
@@ -2,6 +2,7 @@ function list(units) {
|
|
|
2
2
|
return units.length === 0 ? '-' : units.join(',');
|
|
3
3
|
}
|
|
4
4
|
const FLAG_UNITS = new Set(['carry', 'zero', 'sign', 'parity', 'halfCarry']);
|
|
5
|
+
const FLAG_UNIT_LIST = ['carry', 'zero', 'sign', 'parity', 'halfCarry'];
|
|
5
6
|
const CONTRACT_CARRIER_PAIRS = [
|
|
6
7
|
{ label: 'BC', hi: 'B', lo: 'C' },
|
|
7
8
|
{ label: 'DE', hi: 'D', lo: 'E' },
|
|
@@ -32,6 +33,14 @@ export function contractCarrierList(units) {
|
|
|
32
33
|
}
|
|
33
34
|
return parts.length === 0 ? '-' : parts.join(',');
|
|
34
35
|
}
|
|
36
|
+
function sourceContractCarrierList(units) {
|
|
37
|
+
const unique = [...new Set(units)];
|
|
38
|
+
const hasAllFlags = FLAG_UNIT_LIST.every((unit) => unique.includes(unit));
|
|
39
|
+
const compacted = hasAllFlags
|
|
40
|
+
? unique.filter((unit) => !FLAG_UNITS.has(unit)).concat('F')
|
|
41
|
+
: unique;
|
|
42
|
+
return contractCarrierList(compacted);
|
|
43
|
+
}
|
|
35
44
|
function relationOutputUnits(relations) {
|
|
36
45
|
return relations.flatMap((rel) => rel.out);
|
|
37
46
|
}
|
|
@@ -59,9 +68,9 @@ function sourceContractEntries(summary) {
|
|
|
59
68
|
const outputUnits = relationOutputUnits(summary.valueRelations);
|
|
60
69
|
if (outputUnits.length > 0)
|
|
61
70
|
out.push({ keyword: 'out', carriers: contractCarrierList(outputUnits) });
|
|
62
|
-
const clobbers = summary.mayWrite.filter((unit) => !relationOut.has(unit)
|
|
71
|
+
const clobbers = summary.mayWrite.filter((unit) => !relationOut.has(unit));
|
|
63
72
|
if (clobbers.length > 0)
|
|
64
|
-
out.push({ keyword: 'clobbers', carriers:
|
|
73
|
+
out.push({ keyword: 'clobbers', carriers: sourceContractCarrierList(clobbers) });
|
|
65
74
|
return out;
|
|
66
75
|
}
|
|
67
76
|
function stackStatus(summary) {
|
|
@@ -152,5 +161,8 @@ export function renderRegisterContractsInterface(summaries) {
|
|
|
152
161
|
return `${lines.join('\n')}\n`;
|
|
153
162
|
}
|
|
154
163
|
export function renderRegisterContractsSourceBlock(summary) {
|
|
155
|
-
|
|
164
|
+
const entries = sourceContractEntries(summary);
|
|
165
|
+
if (entries.length === 0)
|
|
166
|
+
return [];
|
|
167
|
+
return [`;! ${entries.map((entry) => `${entry.keyword} ${entry.carriers}`).join('; ')}`];
|
|
156
168
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { SmartComment } from './types.js';
|
|
2
2
|
export declare function parseSmartCommentLine(line: string): SmartComment | undefined;
|
|
3
|
+
export declare function parseSmartCommentLines(line: string): SmartComment[];
|
|
3
4
|
export declare function isCompactSourceCommentLine(line: string): boolean;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { expandCarrierList } from './carriers.js';
|
|
2
2
|
const COMPACT_SOURCE_TAG_RE = /^;?\s*!\s*(in|out|clobbers|preserves)(?:\s+(.+))?$/i;
|
|
3
|
+
const COMPACT_SOURCE_CLAUSE_RE = /^(in|out|clobbers|preserves)(?:\s+(.+))?$/i;
|
|
3
4
|
const COMPACT_SOURCE_LINE_RE = /^\s*;\s*!\s*(?:in|out|maybe-out|clobbers|preserves)(?:\s|$)/i;
|
|
4
5
|
const CARRIER_RE = /^\{([^}]+)\}(?:\s+(.+))?$/;
|
|
5
6
|
const CONTRACT_COMMENT_KINDS = new Set(['in', 'out', 'clobbers', 'preserves']);
|
|
@@ -35,17 +36,51 @@ function parseCarrierPayload(rest) {
|
|
|
35
36
|
return { carriers, ...(name ? { name } : {}) };
|
|
36
37
|
}
|
|
37
38
|
export function parseSmartCommentLine(line) {
|
|
39
|
+
return parseSmartCommentLines(line)[0];
|
|
40
|
+
}
|
|
41
|
+
export function parseSmartCommentLines(line) {
|
|
38
42
|
const trimmed = line.trim();
|
|
39
43
|
const expectOut = parseExpectOutComment(trimmed);
|
|
40
44
|
if (expectOut !== undefined)
|
|
41
|
-
return expectOut;
|
|
45
|
+
return [expectOut];
|
|
46
|
+
const semicolonSeparated = parseSemicolonSeparatedSourceComments(trimmed);
|
|
47
|
+
if (semicolonSeparated.length > 0)
|
|
48
|
+
return semicolonSeparated;
|
|
42
49
|
const match = COMPACT_SOURCE_TAG_RE.exec(trimmed);
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
if (match) {
|
|
51
|
+
const tag = match[1].toLowerCase();
|
|
52
|
+
if (!CONTRACT_COMMENT_KINDS.has(tag))
|
|
53
|
+
return [];
|
|
54
|
+
const comment = parseCarrierComment(tag, match[2]?.trim());
|
|
55
|
+
return comment === undefined ? [] : [comment];
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
function parseSemicolonSeparatedSourceComments(trimmed) {
|
|
60
|
+
const sourcePrefix = /^;?\s*!\s*/.exec(trimmed);
|
|
61
|
+
if (sourcePrefix === null)
|
|
62
|
+
return [];
|
|
63
|
+
const content = trimmed.slice(sourcePrefix[0].length);
|
|
64
|
+
const parts = content
|
|
65
|
+
.split(';')
|
|
66
|
+
.map((part) => part.trim())
|
|
67
|
+
.filter(Boolean);
|
|
68
|
+
if (parts.length <= 1)
|
|
69
|
+
return [];
|
|
70
|
+
const comments = [];
|
|
71
|
+
for (const part of parts) {
|
|
72
|
+
const match = COMPACT_SOURCE_CLAUSE_RE.exec(part);
|
|
73
|
+
if (!match)
|
|
74
|
+
return [];
|
|
75
|
+
const tag = match[1].toLowerCase();
|
|
76
|
+
if (!CONTRACT_COMMENT_KINDS.has(tag))
|
|
77
|
+
return [];
|
|
78
|
+
const comment = parseCarrierComment(tag, match[2]?.trim());
|
|
79
|
+
if (comment === undefined)
|
|
80
|
+
return [];
|
|
81
|
+
comments.push(comment);
|
|
82
|
+
}
|
|
83
|
+
return comments;
|
|
49
84
|
}
|
|
50
85
|
function parseExpectOutComment(trimmed) {
|
|
51
86
|
const expectOut = /^;?\s*expects\s+out\s+(.+)$/i.exec(trimmed);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LocatedSmartComment, RegisterContractsRoutine, RoutineContract } from './types.js';
|
|
2
|
-
import { parseSmartCommentLine } from './smartCommentParsing.js';
|
|
3
|
-
export { parseSmartCommentLine };
|
|
2
|
+
import { parseSmartCommentLine, parseSmartCommentLines } from './smartCommentParsing.js';
|
|
3
|
+
export { parseSmartCommentLine, parseSmartCommentLines };
|
|
4
4
|
export declare function parseSmartComments(sourceLineComments: ReadonlyMap<string, ReadonlyMap<number, string>>): LocatedSmartComment[];
|
|
5
5
|
export declare function buildRoutineContracts(comments: LocatedSmartComment[], routines?: RegisterContractsRoutine[], sourceTexts?: ReadonlyMap<string, string>): Map<string, RoutineContract>;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { collectPrecedingCommentBlock } from './smartCommentBlocks.js';
|
|
2
|
-
import { parseSmartCommentLine } from './smartCommentParsing.js';
|
|
3
|
-
export { parseSmartCommentLine };
|
|
2
|
+
import { parseSmartCommentLine, parseSmartCommentLines } from './smartCommentParsing.js';
|
|
3
|
+
export { parseSmartCommentLine, parseSmartCommentLines };
|
|
4
4
|
export function parseSmartComments(sourceLineComments) {
|
|
5
5
|
const out = [];
|
|
6
6
|
for (const [file, comments] of sourceLineComments) {
|
|
7
7
|
for (const [line, text] of comments) {
|
|
8
|
-
const parsed
|
|
9
|
-
if (parsed) {
|
|
8
|
+
for (const parsed of parseSmartCommentLines(`;${text}`)) {
|
|
10
9
|
out.push({ file, line, comment: parsed });
|
|
11
10
|
}
|
|
12
11
|
}
|