@jhlagado/azm 0.2.7 → 0.2.8
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 +170 -69
- package/dist/src/api-artifacts.d.ts +20 -0
- package/dist/src/api-artifacts.js +165 -0
- package/dist/src/api-compile.d.ts +8 -2
- package/dist/src/api-compile.js +31 -230
- package/dist/src/api-register-contracts.d.ts +9 -0
- package/dist/src/api-register-contracts.js +77 -0
- package/dist/src/api-tooling.d.ts +2 -2
- package/dist/src/api-tooling.js +1 -1
- package/dist/src/assembly/address-planning.d.ts +1 -2
- package/dist/src/assembly/address-planning.js +119 -218
- package/dist/src/assembly/address-symbols.d.ts +12 -0
- package/dist/src/assembly/address-symbols.js +118 -0
- package/dist/src/assembly/fixup-emission.js +30 -48
- package/dist/src/assembly/program-emission.js +163 -164
- package/dist/src/cli/artifact-files.d.ts +15 -0
- package/dist/src/cli/artifact-files.js +86 -0
- package/dist/src/cli/parse-args.d.ts +6 -5
- package/dist/src/cli/parse-args.js +162 -136
- package/dist/src/cli/run.js +4 -1
- package/dist/src/cli/usage.d.ts +1 -0
- package/dist/src/cli/usage.js +33 -0
- package/dist/src/cli/write-artifacts.js +18 -91
- package/dist/src/core/compile.js +51 -274
- package/dist/src/core/conditional-assembly.d.ts +6 -0
- package/dist/src/core/conditional-assembly.js +181 -0
- package/dist/src/expansion/op-constant-expression.d.ts +3 -0
- package/dist/src/expansion/op-constant-expression.js +52 -0
- package/dist/src/expansion/op-expand-selected.d.ts +5 -0
- package/dist/src/expansion/op-expand-selected.js +143 -0
- package/dist/src/expansion/op-expansion.d.ts +5 -53
- package/dist/src/expansion/op-expansion.js +85 -815
- package/dist/src/expansion/op-instruction-instantiation.d.ts +3 -0
- package/dist/src/expansion/op-instruction-instantiation.js +194 -0
- package/dist/src/expansion/op-local-labels.d.ts +8 -0
- package/dist/src/expansion/op-local-labels.js +166 -0
- package/dist/src/expansion/op-operand-splitting.d.ts +1 -0
- package/dist/src/expansion/op-operand-splitting.js +44 -0
- package/dist/src/expansion/op-operands.d.ts +53 -0
- package/dist/src/expansion/op-operands.js +66 -0
- package/dist/src/expansion/op-selection.d.ts +18 -0
- package/dist/src/expansion/op-selection.js +172 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/model/diagnostic.d.ts +4 -0
- package/dist/src/model/diagnostic.js +4 -0
- package/dist/src/outputs/asm80-expression-evaluation.d.ts +10 -0
- package/dist/src/outputs/asm80-expression-evaluation.js +75 -0
- package/dist/src/outputs/asm80-expressions.d.ts +5 -0
- package/dist/src/outputs/asm80-expressions.js +47 -0
- package/dist/src/outputs/asm80-instruction-operands.d.ts +16 -0
- package/dist/src/outputs/asm80-instruction-operands.js +38 -0
- package/dist/src/outputs/asm80-instructions.d.ts +5 -0
- package/dist/src/outputs/asm80-instructions.js +272 -0
- package/dist/src/outputs/asm80-ld-operands.d.ts +10 -0
- package/dist/src/outputs/asm80-ld-operands.js +157 -0
- package/dist/src/outputs/asm80-strings.d.ts +4 -0
- package/dist/src/outputs/asm80-strings.js +14 -0
- package/dist/src/outputs/d8-files.d.ts +10 -0
- package/dist/src/outputs/d8-files.js +103 -0
- package/dist/src/outputs/d8-helpers.d.ts +21 -0
- package/dist/src/outputs/d8-helpers.js +136 -0
- package/dist/src/outputs/hex.js +26 -18
- package/dist/src/outputs/types.d.ts +16 -10
- package/dist/src/outputs/write-asm80.js +68 -597
- package/dist/src/outputs/write-d8.js +6 -216
- package/dist/src/register-contracts/accept-output.d.ts +2 -0
- package/dist/src/register-contracts/analyze-helpers.d.ts +29 -0
- package/dist/src/register-contracts/analyze-helpers.js +162 -0
- package/dist/src/{register-care → register-contracts}/analyze.d.ts +6 -6
- package/dist/src/register-contracts/analyze.js +73 -0
- package/dist/src/register-contracts/annotate.d.ts +11 -0
- package/dist/src/{register-care → register-contracts}/annotate.js +3 -3
- package/dist/src/register-contracts/annotations.d.ts +8 -0
- package/dist/src/{register-care → register-contracts}/annotations.js +3 -3
- package/dist/src/register-contracts/boundaryHints.d.ts +3 -0
- package/dist/src/register-contracts/boundaryHints.js +24 -0
- package/dist/src/register-contracts/carriers.d.ts +2 -0
- package/dist/src/register-contracts/constants.d.ts +4 -0
- package/dist/src/register-contracts/constants.js +51 -0
- package/dist/src/register-contracts/controlFlow.d.ts +5 -0
- package/dist/src/register-contracts/controlFlow.js +55 -0
- package/dist/src/register-contracts/fix.d.ts +11 -0
- package/dist/src/{register-care → register-contracts}/fix.js +47 -30
- package/dist/src/register-contracts/instruction-head.d.ts +2 -0
- package/dist/src/register-contracts/instruction-head.js +3 -0
- package/dist/src/register-contracts/instruction-operands.d.ts +3 -0
- package/dist/src/register-contracts/instruction-operands.js +101 -0
- package/dist/src/register-contracts/instruction-predicates.d.ts +6 -0
- package/dist/src/register-contracts/instruction-predicates.js +44 -0
- package/dist/src/register-contracts/interfaceContracts.d.ts +2 -0
- package/dist/src/register-contracts/interfaceContracts.js +68 -0
- package/dist/src/register-contracts/liveness.d.ts +3 -0
- package/dist/src/{register-care → register-contracts}/liveness.js +111 -79
- package/dist/src/register-contracts/operand-register-name.d.ts +2 -0
- package/dist/src/register-contracts/operand-register-name.js +13 -0
- package/dist/src/{register-care → register-contracts}/profiles.d.ts +5 -5
- package/dist/src/{register-care → register-contracts}/profiles.js +2 -2
- package/dist/src/register-contracts/programModel-boundaries.d.ts +6 -0
- package/dist/src/register-contracts/programModel-boundaries.js +64 -0
- package/dist/src/register-contracts/programModel-routines.d.ts +7 -0
- package/dist/src/register-contracts/programModel-routines.js +128 -0
- package/dist/src/register-contracts/programModel.d.ts +3 -0
- package/dist/src/register-contracts/programModel.js +14 -0
- package/dist/src/register-contracts/report.d.ts +5 -0
- package/dist/src/{register-care → register-contracts}/report.js +34 -17
- package/dist/src/register-contracts/routine-summaries.d.ts +6 -0
- package/dist/src/{register-care → register-contracts}/routine-summaries.js +11 -1
- package/dist/src/register-contracts/smartCommentBlocks.d.ts +5 -0
- package/dist/src/register-contracts/smartCommentBlocks.js +30 -0
- package/dist/src/register-contracts/smartCommentParsing.d.ts +3 -0
- package/dist/src/register-contracts/smartCommentParsing.js +80 -0
- package/dist/src/register-contracts/smartComments.d.ts +5 -0
- package/dist/src/register-contracts/smartComments.js +92 -0
- package/dist/src/register-contracts/summaries.d.ts +12 -0
- package/dist/src/{register-care → register-contracts}/summaries.js +7 -7
- package/dist/src/register-contracts/summary-boundary.d.ts +2 -0
- package/dist/src/register-contracts/summary-boundary.js +40 -0
- package/dist/src/register-contracts/summary-contract.d.ts +2 -0
- package/dist/src/register-contracts/summary-contract.js +45 -0
- package/dist/src/register-contracts/summary-result.d.ts +7 -0
- package/dist/src/register-contracts/summary-result.js +122 -0
- package/dist/src/register-contracts/summary-state.d.ts +23 -0
- package/dist/src/register-contracts/summary-state.js +88 -0
- package/dist/src/register-contracts/summary-token-transfer.d.ts +3 -0
- package/dist/src/register-contracts/summary-token-transfer.js +67 -0
- package/dist/src/register-contracts/summary.d.ts +3 -0
- package/dist/src/register-contracts/summary.js +266 -0
- package/dist/src/register-contracts/tooling.d.ts +57 -0
- package/dist/src/{register-care → register-contracts}/tooling.js +8 -6
- package/dist/src/register-contracts/types.d.ts +188 -0
- package/dist/src/semantics/binary-operators.d.ts +2 -0
- package/dist/src/semantics/binary-operators.js +15 -0
- package/dist/src/semantics/byte-functions.d.ts +2 -0
- package/dist/src/semantics/byte-functions.js +7 -0
- package/dist/src/semantics/constant-operator-types.d.ts +10 -0
- package/dist/src/semantics/constant-operator-types.js +1 -0
- package/dist/src/semantics/constant-operators.d.ts +3 -0
- package/dist/src/semantics/constant-operators.js +3 -0
- package/dist/src/semantics/diagnostics.d.ts +3 -0
- package/dist/src/semantics/diagnostics.js +10 -0
- package/dist/src/semantics/expression-evaluation.d.ts +11 -19
- package/dist/src/semantics/expression-evaluation.js +22 -334
- package/dist/src/semantics/layout-evaluation.d.ts +23 -0
- package/dist/src/semantics/layout-evaluation.js +202 -0
- package/dist/src/semantics/layout-format.d.ts +5 -0
- package/dist/src/semantics/layout-format.js +31 -0
- package/dist/src/semantics/layout-path.d.ts +24 -0
- package/dist/src/semantics/layout-path.js +58 -0
- package/dist/src/semantics/unary-operators.d.ts +2 -0
- package/dist/src/semantics/unary-operators.js +8 -0
- package/dist/src/source/line-comment-scanner.d.ts +1 -0
- package/dist/src/source/line-comment-scanner.js +51 -0
- package/dist/src/source/strip-line-comment.js +8 -44
- package/dist/src/syntax/directive-aliases.js +36 -22
- package/dist/src/syntax/expression-tokenizer.d.ts +30 -0
- package/dist/src/syntax/expression-tokenizer.js +310 -0
- package/dist/src/syntax/parse-directive-statement.d.ts +14 -0
- package/dist/src/syntax/parse-directive-statement.js +307 -0
- package/dist/src/syntax/parse-expression.d.ts +2 -2
- package/dist/src/syntax/parse-expression.js +7 -568
- package/dist/src/syntax/parse-layout-declarations.d.ts +9 -0
- package/dist/src/syntax/parse-layout-declarations.js +180 -0
- package/dist/src/syntax/parse-layout-expression.d.ts +5 -0
- package/dist/src/syntax/parse-layout-expression.js +175 -0
- package/dist/src/syntax/parse-line.js +4 -272
- package/dist/src/syntax/parse-token-expression.d.ts +3 -0
- package/dist/src/syntax/parse-token-expression.js +133 -0
- package/dist/src/tooling/case-style.js +47 -30
- package/dist/src/z80/effect-groups.d.ts +38 -0
- package/dist/src/z80/effect-groups.js +265 -0
- package/dist/src/z80/effect-units.d.ts +18 -0
- package/dist/src/z80/effect-units.js +165 -0
- package/dist/src/z80/effects.d.ts +1 -1
- package/dist/src/z80/effects.js +94 -557
- package/dist/src/z80/encode-core.d.ts +2 -0
- package/dist/src/z80/encode-core.js +42 -0
- package/dist/src/z80/encode-ld-helpers.d.ts +25 -0
- package/dist/src/z80/encode-ld-helpers.js +172 -0
- package/dist/src/z80/encode-ld.d.ts +2 -0
- package/dist/src/z80/encode-ld.js +285 -0
- package/dist/src/z80/encode.js +190 -542
- package/dist/src/z80/ld-support.d.ts +3 -0
- package/dist/src/z80/ld-support.js +146 -0
- package/dist/src/z80/operand-split-state.d.ts +8 -0
- package/dist/src/z80/operand-split-state.js +46 -0
- package/dist/src/z80/operand-split.d.ts +1 -0
- package/dist/src/z80/operand-split.js +13 -0
- package/dist/src/z80/parse-basic.d.ts +4 -0
- package/dist/src/z80/parse-basic.js +39 -0
- package/dist/src/z80/parse-branch.d.ts +4 -0
- package/dist/src/z80/parse-branch.js +218 -0
- package/dist/src/z80/parse-conditions.d.ts +6 -0
- package/dist/src/z80/parse-conditions.js +10 -0
- package/dist/src/z80/parse-exchange.d.ts +2 -0
- package/dist/src/z80/parse-exchange.js +30 -0
- package/dist/src/z80/parse-instruction.js +224 -1010
- package/dist/src/z80/parse-io-control.d.ts +5 -0
- package/dist/src/z80/parse-io-control.js +108 -0
- package/dist/src/z80/parse-ld.d.ts +2 -0
- package/dist/src/z80/parse-ld.js +83 -0
- package/dist/src/z80/parse-operands.d.ts +41 -0
- package/dist/src/z80/parse-operands.js +259 -0
- package/docs/reference/cli.md +42 -35
- package/docs/reference/tooling-api.md +20 -16
- package/package.json +1 -1
- package/dist/src/register-care/accept-output.d.ts +0 -2
- package/dist/src/register-care/analyze.js +0 -166
- package/dist/src/register-care/annotate.d.ts +0 -11
- package/dist/src/register-care/annotations.d.ts +0 -8
- package/dist/src/register-care/boundaryHints.d.ts +0 -3
- package/dist/src/register-care/boundaryHints.js +0 -80
- package/dist/src/register-care/carriers.d.ts +0 -2
- package/dist/src/register-care/controlFlow.d.ts +0 -5
- package/dist/src/register-care/controlFlow.js +0 -38
- package/dist/src/register-care/fix.d.ts +0 -11
- package/dist/src/register-care/instruction-shape.d.ts +0 -11
- package/dist/src/register-care/instruction-shape.js +0 -129
- package/dist/src/register-care/liveness.d.ts +0 -3
- package/dist/src/register-care/programModel.d.ts +0 -3
- package/dist/src/register-care/programModel.js +0 -266
- package/dist/src/register-care/report.d.ts +0 -5
- package/dist/src/register-care/routine-summaries.d.ts +0 -6
- package/dist/src/register-care/smartComments.d.ts +0 -5
- package/dist/src/register-care/smartComments.js +0 -243
- package/dist/src/register-care/summaries.d.ts +0 -12
- package/dist/src/register-care/summary.d.ts +0 -3
- package/dist/src/register-care/summary.js +0 -474
- package/dist/src/register-care/tooling.d.ts +0 -43
- package/dist/src/register-care/types.d.ts +0 -172
- /package/dist/src/{register-care → register-contracts}/accept-output.js +0 -0
- /package/dist/src/{register-care → register-contracts}/carriers.js +0 -0
- /package/dist/src/{register-care → register-contracts}/sourceText.d.ts +0 -0
- /package/dist/src/{register-care → register-contracts}/sourceText.js +0 -0
- /package/dist/src/{register-care → register-contracts}/types.js +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { diagnostic } from './diagnostics.js';
|
|
2
|
+
import { formatOffsetPath, formatTypeExpr, registerIndexName, scalarSize, } from './layout-format.js';
|
|
3
|
+
import { arrayElementOffset, fieldPathOffset } from './layout-path.js';
|
|
4
|
+
export function evaluateSizeof(typeExpr, layouts, span, diagnostics) {
|
|
5
|
+
if (!layouts) {
|
|
6
|
+
diagnostics.push(diagnostic(span, `unknown type: ${formatTypeExpr(typeExpr)}`));
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
return typeExprSize(typeExpr, layouts, span, diagnostics, new Set([typeExpr.name]));
|
|
10
|
+
}
|
|
11
|
+
export function evaluateOffset(typeExpr, path, layouts, span, diagnostics) {
|
|
12
|
+
if (!layouts) {
|
|
13
|
+
diagnostics.push(diagnostic(span, `unknown type: ${formatTypeExpr(typeExpr)}`));
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const diagnosticCount = diagnostics.length;
|
|
17
|
+
const offset = offsetPath(typeExpr, path, layouts, span, diagnostics);
|
|
18
|
+
if (offset !== undefined) {
|
|
19
|
+
return offset;
|
|
20
|
+
}
|
|
21
|
+
if (diagnostics.length === diagnosticCount) {
|
|
22
|
+
diagnostics.push(diagnostic(span, `unknown field "${formatOffsetPath(path)}" in type ${formatTypeExpr(typeExpr)}`));
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
export function evaluateLayoutCast(expression, labels, equates, span, diagnostics, options, evaluateExpression) {
|
|
27
|
+
const base = evaluateExpression(expression.base, labels, equates, span, diagnostics, options);
|
|
28
|
+
if (base === undefined) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
if (!options.layouts) {
|
|
32
|
+
diagnostics.push(diagnostic(span, `unknown type: ${formatTypeExpr(expression.typeExpr)}`));
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const offset = layoutCastOffset(expression.typeExpr, expression.path, labels, equates, options.layouts, span, diagnostics, options, evaluateExpression);
|
|
36
|
+
return offset === undefined ? undefined : base + offset;
|
|
37
|
+
}
|
|
38
|
+
export function validateLayouts(layouts, diagnostics) {
|
|
39
|
+
for (const [name, layout] of layouts) {
|
|
40
|
+
layoutSize(name, layout, layouts, layout.span, diagnostics, new Set([name]));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function typeExprSize(typeExpr, layouts, span, diagnostics, visiting) {
|
|
44
|
+
const resolvedTypeExpr = resolveLayoutAlias(typeExpr, layouts, span, diagnostics, visiting);
|
|
45
|
+
if (!resolvedTypeExpr) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (resolvedTypeExpr !== typeExpr) {
|
|
49
|
+
return typeExprSize(resolvedTypeExpr, layouts, span, diagnostics, visiting);
|
|
50
|
+
}
|
|
51
|
+
const scalar = scalarSize(typeExpr.name);
|
|
52
|
+
const baseSize = scalar ??
|
|
53
|
+
(() => {
|
|
54
|
+
const layout = layouts.get(typeExpr.name);
|
|
55
|
+
if (!layout) {
|
|
56
|
+
diagnostics.push(diagnostic(span, `unknown type: ${typeExpr.name}`));
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return layoutSize(typeExpr.name, layout, layouts, span, diagnostics, visiting);
|
|
60
|
+
})();
|
|
61
|
+
if (baseSize === undefined) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return typeExpr.length === undefined ? baseSize : baseSize * typeExpr.length;
|
|
65
|
+
}
|
|
66
|
+
function layoutSize(typeName, layout, layouts, span, diagnostics, visiting) {
|
|
67
|
+
if (layout.kind === 'alias') {
|
|
68
|
+
return typeExprSize(layout.typeExpr, layouts, span, diagnostics, visiting);
|
|
69
|
+
}
|
|
70
|
+
const fieldSizes = [];
|
|
71
|
+
for (const field of layout.fields) {
|
|
72
|
+
const size = fieldSize(field, layouts, span, diagnostics, visiting);
|
|
73
|
+
if (size === undefined) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
fieldSizes.push(size);
|
|
77
|
+
}
|
|
78
|
+
return layout.kind === 'union'
|
|
79
|
+
? fieldSizes.reduce((largest, size) => Math.max(largest, size), 0)
|
|
80
|
+
: fieldSizes.reduce((sum, size) => sum + size, 0);
|
|
81
|
+
}
|
|
82
|
+
function fieldSize(field, layouts, span, diagnostics, visiting) {
|
|
83
|
+
if (field.typeExpr === undefined) {
|
|
84
|
+
return field.size;
|
|
85
|
+
}
|
|
86
|
+
if (visiting.has(field.typeExpr.name)) {
|
|
87
|
+
diagnostics.push(diagnostic(span, visiting.size === 1
|
|
88
|
+
? `Self-referential field type "${field.typeExpr.name}" has no finite size; use .addr for a pointer field.`
|
|
89
|
+
: `recursive type: ${field.typeExpr.name}`));
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
return typeExprSize(field.typeExpr, layouts, span, diagnostics, new Set([...visiting, field.typeExpr.name]));
|
|
93
|
+
}
|
|
94
|
+
function offsetPath(typeExpr, parts, layouts, span, diagnostics) {
|
|
95
|
+
const resolvedTypeExpr = resolveLayoutAlias(typeExpr, layouts, span, diagnostics, new Set([typeExpr.name]));
|
|
96
|
+
if (!resolvedTypeExpr) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
if (resolvedTypeExpr !== typeExpr) {
|
|
100
|
+
return offsetPath(resolvedTypeExpr, parts, layouts, span, diagnostics);
|
|
101
|
+
}
|
|
102
|
+
const [head, ...tail] = parts;
|
|
103
|
+
if (head === undefined) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
if (head.kind === 'index') {
|
|
107
|
+
return staticArrayOffset(typeExpr, head.index, tail, layouts, span, diagnostics);
|
|
108
|
+
}
|
|
109
|
+
return fieldPathOffset({
|
|
110
|
+
typeExpr,
|
|
111
|
+
head,
|
|
112
|
+
tail,
|
|
113
|
+
layouts,
|
|
114
|
+
span,
|
|
115
|
+
diagnostics,
|
|
116
|
+
fieldSize: (field, ownerTypeExpr) => fieldSize(field, layouts, span, diagnostics, new Set([ownerTypeExpr.name])),
|
|
117
|
+
nestedOffset: (fieldTypeExpr, nestedTail) => offsetPath(fieldTypeExpr, nestedTail, layouts, span, diagnostics),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function layoutCastOffset(typeExpr, parts, labels, equates, layouts, span, diagnostics, options, evaluateExpression) {
|
|
121
|
+
const resolvedTypeExpr = resolveLayoutAlias(typeExpr, layouts, span, diagnostics, new Set([typeExpr.name]));
|
|
122
|
+
if (!resolvedTypeExpr) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
if (resolvedTypeExpr !== typeExpr) {
|
|
126
|
+
return layoutCastOffset(resolvedTypeExpr, parts, labels, equates, layouts, span, diagnostics, options, evaluateExpression);
|
|
127
|
+
}
|
|
128
|
+
const [head, ...tail] = parts;
|
|
129
|
+
if (head === undefined) {
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
if (head.kind === 'index') {
|
|
133
|
+
const index = evaluateLayoutCastIndex(head.expression, typeExpr, labels, equates, layouts, span, diagnostics, options, evaluateExpression);
|
|
134
|
+
if (index === undefined) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
return dynamicArrayOffset(typeExpr, index, tail, labels, equates, layouts, span, diagnostics, options, evaluateExpression);
|
|
138
|
+
}
|
|
139
|
+
return fieldPathOffset({
|
|
140
|
+
typeExpr,
|
|
141
|
+
head,
|
|
142
|
+
tail,
|
|
143
|
+
layouts,
|
|
144
|
+
span,
|
|
145
|
+
diagnostics,
|
|
146
|
+
fieldSize: (field, ownerTypeExpr) => fieldSize(field, layouts, span, diagnostics, new Set([ownerTypeExpr.name])),
|
|
147
|
+
nestedOffset: (fieldTypeExpr, nestedTail) => layoutCastOffset(fieldTypeExpr, nestedTail, labels, equates, layouts, span, diagnostics, options, evaluateExpression),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function staticArrayOffset(typeExpr, index, tail, layouts, span, diagnostics) {
|
|
151
|
+
if (typeExpr.length === undefined) {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
if (index >= typeExpr.length) {
|
|
155
|
+
diagnostics.push(diagnostic(span, `array index ${index} out of range for ${formatTypeExpr(typeExpr)}`));
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
return arrayElementOffset(typeExpr, index, tail, (elementTypeExpr) => typeExprSize(elementTypeExpr, layouts, span, diagnostics, new Set([typeExpr.name])), (elementTypeExpr, nestedTail) => offsetPath(elementTypeExpr, nestedTail, layouts, span, diagnostics));
|
|
159
|
+
}
|
|
160
|
+
function dynamicArrayOffset(typeExpr, index, tail, labels, equates, layouts, span, diagnostics, options, evaluateExpression) {
|
|
161
|
+
return arrayElementOffset(typeExpr, index, tail, (elementTypeExpr) => typeExprSize(elementTypeExpr, layouts, span, diagnostics, new Set([typeExpr.name])), (elementTypeExpr, nestedTail) => layoutCastOffset(elementTypeExpr, nestedTail, labels, equates, layouts, span, diagnostics, options, evaluateExpression));
|
|
162
|
+
}
|
|
163
|
+
function evaluateLayoutCastIndex(expression, typeExpr, labels, equates, layouts, span, diagnostics, options, evaluateExpression) {
|
|
164
|
+
if (typeExpr.length === undefined) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
const registerName = registerIndexName(expression);
|
|
168
|
+
if (registerName) {
|
|
169
|
+
diagnostics.push(diagnostic(span, `runtime register index "${registerName}" is not supported in layout casts`));
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
const index = evaluateExpression(expression, labels, equates, span, diagnostics, {
|
|
173
|
+
...options,
|
|
174
|
+
layouts,
|
|
175
|
+
});
|
|
176
|
+
if (index === undefined) {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
if (index < 0 || index >= typeExpr.length) {
|
|
180
|
+
diagnostics.push(diagnostic(span, `array index ${index} out of range for ${formatTypeExpr(typeExpr)}`));
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
return index;
|
|
184
|
+
}
|
|
185
|
+
function resolveLayoutAlias(typeExpr, layouts, span, diagnostics, visiting) {
|
|
186
|
+
const layout = layouts.get(typeExpr.name);
|
|
187
|
+
if (!layout || layout.kind !== 'alias') {
|
|
188
|
+
return typeExpr;
|
|
189
|
+
}
|
|
190
|
+
if (visiting.has(layout.typeExpr.name)) {
|
|
191
|
+
diagnostics.push(diagnostic(span, `recursive type: ${typeExpr.name}`));
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
const target = resolveLayoutAlias(layout.typeExpr, layouts, span, diagnostics, new Set([...visiting, layout.typeExpr.name]));
|
|
195
|
+
if (!target) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
const length = typeExpr.length === undefined
|
|
199
|
+
? target.length
|
|
200
|
+
: (target.length ?? 1) * typeExpr.length;
|
|
201
|
+
return length === undefined ? { name: target.name } : { name: target.name, length };
|
|
202
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Expression, OffsetPathPart, TypeExpr } from '../model/expression.js';
|
|
2
|
+
export declare function scalarSize(typeName: string): number | undefined;
|
|
3
|
+
export declare function formatTypeExpr(typeExpr: TypeExpr): string;
|
|
4
|
+
export declare function formatOffsetPath(path: readonly OffsetPathPart[]): string;
|
|
5
|
+
export declare function registerIndexName(expression: Expression): string | undefined;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function scalarSize(typeName) {
|
|
2
|
+
switch (typeName.toLowerCase()) {
|
|
3
|
+
case 'byte':
|
|
4
|
+
return 1;
|
|
5
|
+
case 'word':
|
|
6
|
+
case 'addr':
|
|
7
|
+
return 2;
|
|
8
|
+
default:
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function formatTypeExpr(typeExpr) {
|
|
13
|
+
return typeExpr.length === undefined ? typeExpr.name : `${typeExpr.name}[${typeExpr.length}]`;
|
|
14
|
+
}
|
|
15
|
+
export function formatOffsetPath(path) {
|
|
16
|
+
return path.map((part) => (part.kind === 'field' ? part.name : `[${part.index}]`)).join('.');
|
|
17
|
+
}
|
|
18
|
+
export function registerIndexName(expression) {
|
|
19
|
+
switch (expression.kind) {
|
|
20
|
+
case 'symbol':
|
|
21
|
+
return /^(a|b|c|d|e|h|l|af|bc|de|hl|ix|iy|sp|i|r|ixh|ixl|iyh|iyl)$/i.test(expression.name)
|
|
22
|
+
? expression.name.toUpperCase()
|
|
23
|
+
: undefined;
|
|
24
|
+
case 'unary':
|
|
25
|
+
return registerIndexName(expression.expression);
|
|
26
|
+
case 'binary':
|
|
27
|
+
return registerIndexName(expression.left) ?? registerIndexName(expression.right);
|
|
28
|
+
default:
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Diagnostic } from '../model/diagnostic.js';
|
|
2
|
+
import type { LayoutCastPathPart, OffsetPathPart, TypeExpr } from '../model/expression.js';
|
|
3
|
+
import type { LayoutField } from '../model/source-item.js';
|
|
4
|
+
import type { SourceSpan } from '../source/source-span.js';
|
|
5
|
+
import type { LayoutRecord } from './layout-evaluation.js';
|
|
6
|
+
type PathPart = OffsetPathPart | LayoutCastPathPart;
|
|
7
|
+
type FieldPathHead = Extract<PathPart, {
|
|
8
|
+
readonly kind: 'field';
|
|
9
|
+
}>;
|
|
10
|
+
type TypeSize = (typeExpr: TypeExpr) => number | undefined;
|
|
11
|
+
type FieldSize = (field: LayoutField, ownerTypeExpr: TypeExpr) => number | undefined;
|
|
12
|
+
type FieldOffsetOptions<TPart extends PathPart> = {
|
|
13
|
+
readonly typeExpr: TypeExpr;
|
|
14
|
+
readonly head: FieldPathHead;
|
|
15
|
+
readonly tail: readonly TPart[];
|
|
16
|
+
readonly layouts: ReadonlyMap<string, LayoutRecord>;
|
|
17
|
+
readonly span: SourceSpan;
|
|
18
|
+
readonly diagnostics: Diagnostic[];
|
|
19
|
+
readonly fieldSize: FieldSize;
|
|
20
|
+
readonly nestedOffset: (typeExpr: TypeExpr, tail: readonly TPart[]) => number | undefined;
|
|
21
|
+
};
|
|
22
|
+
export declare function arrayElementOffset<TPart extends PathPart>(typeExpr: TypeExpr, index: number, tail: readonly TPart[], typeSize: TypeSize, nestedOffset: (typeExpr: TypeExpr, tail: readonly TPart[]) => number | undefined): number | undefined;
|
|
23
|
+
export declare function fieldPathOffset<TPart extends PathPart>({ typeExpr, head, tail, layouts, span, diagnostics, fieldSize, nestedOffset, }: FieldOffsetOptions<TPart>): number | undefined;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { diagnostic } from './diagnostics.js';
|
|
2
|
+
export function arrayElementOffset(typeExpr, index, tail, typeSize, nestedOffset) {
|
|
3
|
+
const elementTypeExpr = { name: typeExpr.name };
|
|
4
|
+
const stride = typeSize(elementTypeExpr);
|
|
5
|
+
if (stride === undefined) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
if (tail.length === 0) {
|
|
9
|
+
return index * stride;
|
|
10
|
+
}
|
|
11
|
+
const nested = nestedOffset(elementTypeExpr, tail);
|
|
12
|
+
return nested === undefined ? undefined : index * stride + nested;
|
|
13
|
+
}
|
|
14
|
+
export function fieldPathOffset({ typeExpr, head, tail, layouts, span, diagnostics, fieldSize, nestedOffset, }) {
|
|
15
|
+
if (typeExpr.length !== undefined) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const layout = layouts.get(typeExpr.name);
|
|
19
|
+
if (!layout) {
|
|
20
|
+
diagnostics.push(diagnostic(span, `unknown type: ${typeExpr.name}`));
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
if (layout.kind === 'alias') {
|
|
24
|
+
return nestedOffset(layout.typeExpr, [head, ...tail]);
|
|
25
|
+
}
|
|
26
|
+
return findFieldOffset(typeExpr, layout, head, tail, fieldSize, nestedOffset);
|
|
27
|
+
}
|
|
28
|
+
function findFieldOffset(typeExpr, layout, head, tail, fieldSize, nestedOffset) {
|
|
29
|
+
let currentOffset = 0;
|
|
30
|
+
for (const field of layout.fields) {
|
|
31
|
+
const foundOffset = field.name === head.name
|
|
32
|
+
? selectedFieldOffset(typeExpr, layout.kind, field, currentOffset, tail, fieldSize, nestedOffset)
|
|
33
|
+
: undefined;
|
|
34
|
+
if (foundOffset !== undefined) {
|
|
35
|
+
return foundOffset;
|
|
36
|
+
}
|
|
37
|
+
const size = fieldSize(field, typeExpr);
|
|
38
|
+
if (size === undefined) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
currentOffset += size;
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
function selectedFieldOffset(typeExpr, layoutKind, field, currentOffset, tail, fieldSize, nestedOffset) {
|
|
46
|
+
const fieldOffset = layoutKind === 'union' ? 0 : currentOffset;
|
|
47
|
+
if (field.typeExpr !== undefined && fieldSize(field, typeExpr) === undefined) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (tail.length === 0) {
|
|
51
|
+
return fieldOffset;
|
|
52
|
+
}
|
|
53
|
+
if (field.typeExpr === undefined) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const nested = nestedOffset(field.typeExpr, tail);
|
|
57
|
+
return nested === undefined ? undefined : fieldOffset + nested;
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function findLineCommentStart(text: string): number | undefined;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function findLineCommentStart(text) {
|
|
2
|
+
let state = {};
|
|
3
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
4
|
+
const result = scanCommentChar(text, index, state);
|
|
5
|
+
if (result.kind === 'comment') {
|
|
6
|
+
return index;
|
|
7
|
+
}
|
|
8
|
+
state = result.state;
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
function scannerState(quote, escaped = false) {
|
|
13
|
+
return {
|
|
14
|
+
...(quote === undefined ? {} : { quote }),
|
|
15
|
+
...(escaped ? { escaped } : {}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function scanCommentChar(text, index, state) {
|
|
19
|
+
const char = text[index];
|
|
20
|
+
if (state.escaped === true) {
|
|
21
|
+
return { kind: 'continue', state: scannerState(state.quote) };
|
|
22
|
+
}
|
|
23
|
+
if (startsEscape(char, state)) {
|
|
24
|
+
return { kind: 'continue', state: scannerState(state.quote, true) };
|
|
25
|
+
}
|
|
26
|
+
if (startsOrEndsQuote(text, index, state)) {
|
|
27
|
+
return {
|
|
28
|
+
kind: 'continue',
|
|
29
|
+
state: scannerState(state.quote === char ? undefined : (state.quote ?? char)),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (char === ';' && state.quote === undefined) {
|
|
33
|
+
return { kind: 'comment' };
|
|
34
|
+
}
|
|
35
|
+
return { kind: 'continue', state };
|
|
36
|
+
}
|
|
37
|
+
function startsEscape(char, state) {
|
|
38
|
+
return char === '\\' && state.quote !== undefined;
|
|
39
|
+
}
|
|
40
|
+
function startsOrEndsQuote(text, index, state) {
|
|
41
|
+
const char = text[index];
|
|
42
|
+
return isQuote(char) && !isApostropheSuffix(text, index, state);
|
|
43
|
+
}
|
|
44
|
+
function isQuote(char) {
|
|
45
|
+
return char === '"' || char === "'";
|
|
46
|
+
}
|
|
47
|
+
function isApostropheSuffix(text, index, state) {
|
|
48
|
+
return (state.quote === undefined &&
|
|
49
|
+
text[index] === "'" &&
|
|
50
|
+
/[A-Za-z0-9_]/.test(text[index - 1] ?? ''));
|
|
51
|
+
}
|
|
@@ -1,53 +1,17 @@
|
|
|
1
|
+
import { findLineCommentStart } from './line-comment-scanner.js';
|
|
1
2
|
/**
|
|
2
3
|
* Remove an ASM80-style end-of-line comment (`;`), respecting quoted strings.
|
|
3
4
|
*/
|
|
4
5
|
export function stripLineComment(text) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
8
|
-
const char = text[index];
|
|
9
|
-
if (escaped) {
|
|
10
|
-
escaped = false;
|
|
11
|
-
continue;
|
|
12
|
-
}
|
|
13
|
-
if (char === '\\' && quote) {
|
|
14
|
-
escaped = true;
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
if ((char === '"' || char === "'") &&
|
|
18
|
-
!(char === "'" && quote === undefined && /[A-Za-z0-9_]/.test(text[index - 1] ?? ''))) {
|
|
19
|
-
quote = quote === char ? undefined : (quote ?? char);
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
if (char === ';' && !quote) {
|
|
23
|
-
return text.slice(0, index);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return text;
|
|
6
|
+
const commentStart = findLineCommentStart(text);
|
|
7
|
+
return commentStart === undefined ? text : text.slice(0, commentStart);
|
|
27
8
|
}
|
|
28
9
|
/** Trailing `;` comment text, or undefined when absent or whitespace-only. */
|
|
29
10
|
export function extractLineComment(text) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const char = text[index];
|
|
34
|
-
if (escaped) {
|
|
35
|
-
escaped = false;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (char === '\\' && quote) {
|
|
39
|
-
escaped = true;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if ((char === '"' || char === "'") &&
|
|
43
|
-
!(char === "'" && quote === undefined && /[A-Za-z0-9_]/.test(text[index - 1] ?? ''))) {
|
|
44
|
-
quote = quote === char ? undefined : (quote ?? char);
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (char === ';' && !quote) {
|
|
48
|
-
const comment = text.slice(index + 1).trim();
|
|
49
|
-
return comment.length > 0 ? comment : undefined;
|
|
50
|
-
}
|
|
11
|
+
const commentStart = findLineCommentStart(text);
|
|
12
|
+
if (commentStart === undefined) {
|
|
13
|
+
return undefined;
|
|
51
14
|
}
|
|
52
|
-
|
|
15
|
+
const comment = text.slice(commentStart + 1).trim();
|
|
16
|
+
return comment.length > 0 ? comment : undefined;
|
|
53
17
|
}
|
|
@@ -107,34 +107,48 @@ export function buildDirectiveAliasPolicy(projectProfiles = []) {
|
|
|
107
107
|
const directiveAliases = new Map(BUILT_IN_DIRECTIVE_ALIASES);
|
|
108
108
|
const baselineKeys = new Set(directiveAliases.keys());
|
|
109
109
|
for (const profile of projectProfiles) {
|
|
110
|
-
|
|
111
|
-
throw new Error(`Unsupported directive alias base "${String(profile.extends)}"`);
|
|
112
|
-
}
|
|
113
|
-
if (profile.extends === undefined) {
|
|
114
|
-
throw new Error(`Project directive alias files must extend "azm"`);
|
|
115
|
-
}
|
|
110
|
+
validateProjectAliasProfile(profile);
|
|
116
111
|
for (const [rawKey, rawTarget] of Object.entries(profile.directiveAliases ?? {})) {
|
|
117
|
-
const key =
|
|
118
|
-
|
|
119
|
-
throw new Error(`Invalid directive alias head "${rawKey}"`);
|
|
120
|
-
if (baselineKeys.has(key)) {
|
|
121
|
-
throw new Error(`Directive alias "${rawKey}" conflicts with the AZM baseline`);
|
|
122
|
-
}
|
|
123
|
-
const lowerKey = key.toLowerCase();
|
|
124
|
-
if (RESERVED_INSTRUCTION_HEADS.has(lowerKey)) {
|
|
125
|
-
throw new Error(`Directive alias "${rawKey}" conflicts with a Z80 instruction`);
|
|
126
|
-
}
|
|
127
|
-
if (RESERVED_LANGUAGE_HEADS.has(lowerKey)) {
|
|
128
|
-
throw new Error(`Directive alias "${rawKey}" conflicts with an AZM language keyword`);
|
|
129
|
-
}
|
|
130
|
-
const target = normalizeAliasTarget(rawTarget);
|
|
131
|
-
if (!target)
|
|
132
|
-
throw new Error(`Invalid directive alias target "${rawTarget}" for "${rawKey}"`);
|
|
112
|
+
const key = validatedAliasKey(rawKey, baselineKeys);
|
|
113
|
+
const target = validatedAliasTarget(rawKey, rawTarget);
|
|
133
114
|
directiveAliases.set(key, target);
|
|
134
115
|
}
|
|
135
116
|
}
|
|
136
117
|
return { directiveAliases };
|
|
137
118
|
}
|
|
119
|
+
function validateProjectAliasProfile(profile) {
|
|
120
|
+
if (profile.extends !== undefined && profile.extends !== 'azm') {
|
|
121
|
+
throw new Error(`Unsupported directive alias base "${String(profile.extends)}"`);
|
|
122
|
+
}
|
|
123
|
+
if (profile.extends === undefined) {
|
|
124
|
+
throw new Error(`Project directive alias files must extend "azm"`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function validatedAliasKey(rawKey, baselineKeys) {
|
|
128
|
+
const key = normalizeAliasKey(rawKey);
|
|
129
|
+
if (!key)
|
|
130
|
+
throw new Error(`Invalid directive alias head "${rawKey}"`);
|
|
131
|
+
rejectReservedAliasKey(rawKey, key, baselineKeys);
|
|
132
|
+
return key;
|
|
133
|
+
}
|
|
134
|
+
function rejectReservedAliasKey(rawKey, key, baselineKeys) {
|
|
135
|
+
if (baselineKeys.has(key)) {
|
|
136
|
+
throw new Error(`Directive alias "${rawKey}" conflicts with the AZM baseline`);
|
|
137
|
+
}
|
|
138
|
+
const lowerKey = key.toLowerCase();
|
|
139
|
+
if (RESERVED_INSTRUCTION_HEADS.has(lowerKey)) {
|
|
140
|
+
throw new Error(`Directive alias "${rawKey}" conflicts with a Z80 instruction`);
|
|
141
|
+
}
|
|
142
|
+
if (RESERVED_LANGUAGE_HEADS.has(lowerKey)) {
|
|
143
|
+
throw new Error(`Directive alias "${rawKey}" conflicts with an AZM language keyword`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function validatedAliasTarget(rawKey, rawTarget) {
|
|
147
|
+
const target = normalizeAliasTarget(rawTarget);
|
|
148
|
+
if (!target)
|
|
149
|
+
throw new Error(`Invalid directive alias target "${rawTarget}" for "${rawKey}"`);
|
|
150
|
+
return target;
|
|
151
|
+
}
|
|
138
152
|
export async function readDirectiveAliasProfile(path) {
|
|
139
153
|
const text = await readFile(path, 'utf8');
|
|
140
154
|
const parsed = JSON.parse(text);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Expression } from '../model/expression.js';
|
|
2
|
+
import { type ParseNestedExpression } from './parse-layout-expression.js';
|
|
3
|
+
export type Operator = Extract<Expression, {
|
|
4
|
+
readonly kind: 'binary';
|
|
5
|
+
}>['operator'];
|
|
6
|
+
export type UnaryOperator = Extract<Expression, {
|
|
7
|
+
readonly kind: 'unary';
|
|
8
|
+
}>['operator'];
|
|
9
|
+
export type Token = {
|
|
10
|
+
readonly kind: 'expression';
|
|
11
|
+
readonly expression: Expression;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: 'number';
|
|
14
|
+
readonly value: number;
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: 'symbol';
|
|
17
|
+
readonly text: string;
|
|
18
|
+
} | {
|
|
19
|
+
readonly kind: 'current-location';
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'operator';
|
|
22
|
+
readonly text: Operator | UnaryOperator;
|
|
23
|
+
} | {
|
|
24
|
+
readonly kind: 'comma';
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: 'left-paren';
|
|
27
|
+
} | {
|
|
28
|
+
readonly kind: 'right-paren';
|
|
29
|
+
};
|
|
30
|
+
export declare function tokenizeExpression(text: string, parseNestedExpression: ParseNestedExpression): Token[] | undefined;
|