@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.
Files changed (235) hide show
  1. package/README.md +170 -69
  2. package/dist/src/api-artifacts.d.ts +20 -0
  3. package/dist/src/api-artifacts.js +165 -0
  4. package/dist/src/api-compile.d.ts +8 -2
  5. package/dist/src/api-compile.js +31 -230
  6. package/dist/src/api-register-contracts.d.ts +9 -0
  7. package/dist/src/api-register-contracts.js +77 -0
  8. package/dist/src/api-tooling.d.ts +2 -2
  9. package/dist/src/api-tooling.js +1 -1
  10. package/dist/src/assembly/address-planning.d.ts +1 -2
  11. package/dist/src/assembly/address-planning.js +119 -218
  12. package/dist/src/assembly/address-symbols.d.ts +12 -0
  13. package/dist/src/assembly/address-symbols.js +118 -0
  14. package/dist/src/assembly/fixup-emission.js +30 -48
  15. package/dist/src/assembly/program-emission.js +163 -164
  16. package/dist/src/cli/artifact-files.d.ts +15 -0
  17. package/dist/src/cli/artifact-files.js +86 -0
  18. package/dist/src/cli/parse-args.d.ts +6 -5
  19. package/dist/src/cli/parse-args.js +162 -136
  20. package/dist/src/cli/run.js +4 -1
  21. package/dist/src/cli/usage.d.ts +1 -0
  22. package/dist/src/cli/usage.js +33 -0
  23. package/dist/src/cli/write-artifacts.js +18 -91
  24. package/dist/src/core/compile.js +51 -274
  25. package/dist/src/core/conditional-assembly.d.ts +6 -0
  26. package/dist/src/core/conditional-assembly.js +181 -0
  27. package/dist/src/expansion/op-constant-expression.d.ts +3 -0
  28. package/dist/src/expansion/op-constant-expression.js +52 -0
  29. package/dist/src/expansion/op-expand-selected.d.ts +5 -0
  30. package/dist/src/expansion/op-expand-selected.js +143 -0
  31. package/dist/src/expansion/op-expansion.d.ts +5 -53
  32. package/dist/src/expansion/op-expansion.js +85 -815
  33. package/dist/src/expansion/op-instruction-instantiation.d.ts +3 -0
  34. package/dist/src/expansion/op-instruction-instantiation.js +194 -0
  35. package/dist/src/expansion/op-local-labels.d.ts +8 -0
  36. package/dist/src/expansion/op-local-labels.js +166 -0
  37. package/dist/src/expansion/op-operand-splitting.d.ts +1 -0
  38. package/dist/src/expansion/op-operand-splitting.js +44 -0
  39. package/dist/src/expansion/op-operands.d.ts +53 -0
  40. package/dist/src/expansion/op-operands.js +66 -0
  41. package/dist/src/expansion/op-selection.d.ts +18 -0
  42. package/dist/src/expansion/op-selection.js +172 -0
  43. package/dist/src/index.d.ts +2 -1
  44. package/dist/src/index.js +1 -1
  45. package/dist/src/model/diagnostic.d.ts +4 -0
  46. package/dist/src/model/diagnostic.js +4 -0
  47. package/dist/src/outputs/asm80-expression-evaluation.d.ts +10 -0
  48. package/dist/src/outputs/asm80-expression-evaluation.js +75 -0
  49. package/dist/src/outputs/asm80-expressions.d.ts +5 -0
  50. package/dist/src/outputs/asm80-expressions.js +47 -0
  51. package/dist/src/outputs/asm80-instruction-operands.d.ts +16 -0
  52. package/dist/src/outputs/asm80-instruction-operands.js +38 -0
  53. package/dist/src/outputs/asm80-instructions.d.ts +5 -0
  54. package/dist/src/outputs/asm80-instructions.js +272 -0
  55. package/dist/src/outputs/asm80-ld-operands.d.ts +10 -0
  56. package/dist/src/outputs/asm80-ld-operands.js +157 -0
  57. package/dist/src/outputs/asm80-strings.d.ts +4 -0
  58. package/dist/src/outputs/asm80-strings.js +14 -0
  59. package/dist/src/outputs/d8-files.d.ts +10 -0
  60. package/dist/src/outputs/d8-files.js +103 -0
  61. package/dist/src/outputs/d8-helpers.d.ts +21 -0
  62. package/dist/src/outputs/d8-helpers.js +136 -0
  63. package/dist/src/outputs/hex.js +26 -18
  64. package/dist/src/outputs/types.d.ts +16 -10
  65. package/dist/src/outputs/write-asm80.js +68 -597
  66. package/dist/src/outputs/write-d8.js +6 -216
  67. package/dist/src/register-contracts/accept-output.d.ts +2 -0
  68. package/dist/src/register-contracts/analyze-helpers.d.ts +29 -0
  69. package/dist/src/register-contracts/analyze-helpers.js +162 -0
  70. package/dist/src/{register-care → register-contracts}/analyze.d.ts +6 -6
  71. package/dist/src/register-contracts/analyze.js +73 -0
  72. package/dist/src/register-contracts/annotate.d.ts +11 -0
  73. package/dist/src/{register-care → register-contracts}/annotate.js +3 -3
  74. package/dist/src/register-contracts/annotations.d.ts +8 -0
  75. package/dist/src/{register-care → register-contracts}/annotations.js +3 -3
  76. package/dist/src/register-contracts/boundaryHints.d.ts +3 -0
  77. package/dist/src/register-contracts/boundaryHints.js +24 -0
  78. package/dist/src/register-contracts/carriers.d.ts +2 -0
  79. package/dist/src/register-contracts/constants.d.ts +4 -0
  80. package/dist/src/register-contracts/constants.js +51 -0
  81. package/dist/src/register-contracts/controlFlow.d.ts +5 -0
  82. package/dist/src/register-contracts/controlFlow.js +55 -0
  83. package/dist/src/register-contracts/fix.d.ts +11 -0
  84. package/dist/src/{register-care → register-contracts}/fix.js +47 -30
  85. package/dist/src/register-contracts/instruction-head.d.ts +2 -0
  86. package/dist/src/register-contracts/instruction-head.js +3 -0
  87. package/dist/src/register-contracts/instruction-operands.d.ts +3 -0
  88. package/dist/src/register-contracts/instruction-operands.js +101 -0
  89. package/dist/src/register-contracts/instruction-predicates.d.ts +6 -0
  90. package/dist/src/register-contracts/instruction-predicates.js +44 -0
  91. package/dist/src/register-contracts/interfaceContracts.d.ts +2 -0
  92. package/dist/src/register-contracts/interfaceContracts.js +68 -0
  93. package/dist/src/register-contracts/liveness.d.ts +3 -0
  94. package/dist/src/{register-care → register-contracts}/liveness.js +111 -79
  95. package/dist/src/register-contracts/operand-register-name.d.ts +2 -0
  96. package/dist/src/register-contracts/operand-register-name.js +13 -0
  97. package/dist/src/{register-care → register-contracts}/profiles.d.ts +5 -5
  98. package/dist/src/{register-care → register-contracts}/profiles.js +2 -2
  99. package/dist/src/register-contracts/programModel-boundaries.d.ts +6 -0
  100. package/dist/src/register-contracts/programModel-boundaries.js +64 -0
  101. package/dist/src/register-contracts/programModel-routines.d.ts +7 -0
  102. package/dist/src/register-contracts/programModel-routines.js +128 -0
  103. package/dist/src/register-contracts/programModel.d.ts +3 -0
  104. package/dist/src/register-contracts/programModel.js +14 -0
  105. package/dist/src/register-contracts/report.d.ts +5 -0
  106. package/dist/src/{register-care → register-contracts}/report.js +34 -17
  107. package/dist/src/register-contracts/routine-summaries.d.ts +6 -0
  108. package/dist/src/{register-care → register-contracts}/routine-summaries.js +11 -1
  109. package/dist/src/register-contracts/smartCommentBlocks.d.ts +5 -0
  110. package/dist/src/register-contracts/smartCommentBlocks.js +30 -0
  111. package/dist/src/register-contracts/smartCommentParsing.d.ts +3 -0
  112. package/dist/src/register-contracts/smartCommentParsing.js +80 -0
  113. package/dist/src/register-contracts/smartComments.d.ts +5 -0
  114. package/dist/src/register-contracts/smartComments.js +92 -0
  115. package/dist/src/register-contracts/summaries.d.ts +12 -0
  116. package/dist/src/{register-care → register-contracts}/summaries.js +7 -7
  117. package/dist/src/register-contracts/summary-boundary.d.ts +2 -0
  118. package/dist/src/register-contracts/summary-boundary.js +40 -0
  119. package/dist/src/register-contracts/summary-contract.d.ts +2 -0
  120. package/dist/src/register-contracts/summary-contract.js +45 -0
  121. package/dist/src/register-contracts/summary-result.d.ts +7 -0
  122. package/dist/src/register-contracts/summary-result.js +122 -0
  123. package/dist/src/register-contracts/summary-state.d.ts +23 -0
  124. package/dist/src/register-contracts/summary-state.js +88 -0
  125. package/dist/src/register-contracts/summary-token-transfer.d.ts +3 -0
  126. package/dist/src/register-contracts/summary-token-transfer.js +67 -0
  127. package/dist/src/register-contracts/summary.d.ts +3 -0
  128. package/dist/src/register-contracts/summary.js +266 -0
  129. package/dist/src/register-contracts/tooling.d.ts +57 -0
  130. package/dist/src/{register-care → register-contracts}/tooling.js +8 -6
  131. package/dist/src/register-contracts/types.d.ts +188 -0
  132. package/dist/src/semantics/binary-operators.d.ts +2 -0
  133. package/dist/src/semantics/binary-operators.js +15 -0
  134. package/dist/src/semantics/byte-functions.d.ts +2 -0
  135. package/dist/src/semantics/byte-functions.js +7 -0
  136. package/dist/src/semantics/constant-operator-types.d.ts +10 -0
  137. package/dist/src/semantics/constant-operator-types.js +1 -0
  138. package/dist/src/semantics/constant-operators.d.ts +3 -0
  139. package/dist/src/semantics/constant-operators.js +3 -0
  140. package/dist/src/semantics/diagnostics.d.ts +3 -0
  141. package/dist/src/semantics/diagnostics.js +10 -0
  142. package/dist/src/semantics/expression-evaluation.d.ts +11 -19
  143. package/dist/src/semantics/expression-evaluation.js +22 -334
  144. package/dist/src/semantics/layout-evaluation.d.ts +23 -0
  145. package/dist/src/semantics/layout-evaluation.js +202 -0
  146. package/dist/src/semantics/layout-format.d.ts +5 -0
  147. package/dist/src/semantics/layout-format.js +31 -0
  148. package/dist/src/semantics/layout-path.d.ts +24 -0
  149. package/dist/src/semantics/layout-path.js +58 -0
  150. package/dist/src/semantics/unary-operators.d.ts +2 -0
  151. package/dist/src/semantics/unary-operators.js +8 -0
  152. package/dist/src/source/line-comment-scanner.d.ts +1 -0
  153. package/dist/src/source/line-comment-scanner.js +51 -0
  154. package/dist/src/source/strip-line-comment.js +8 -44
  155. package/dist/src/syntax/directive-aliases.js +36 -22
  156. package/dist/src/syntax/expression-tokenizer.d.ts +30 -0
  157. package/dist/src/syntax/expression-tokenizer.js +310 -0
  158. package/dist/src/syntax/parse-directive-statement.d.ts +14 -0
  159. package/dist/src/syntax/parse-directive-statement.js +307 -0
  160. package/dist/src/syntax/parse-expression.d.ts +2 -2
  161. package/dist/src/syntax/parse-expression.js +7 -568
  162. package/dist/src/syntax/parse-layout-declarations.d.ts +9 -0
  163. package/dist/src/syntax/parse-layout-declarations.js +180 -0
  164. package/dist/src/syntax/parse-layout-expression.d.ts +5 -0
  165. package/dist/src/syntax/parse-layout-expression.js +175 -0
  166. package/dist/src/syntax/parse-line.js +4 -272
  167. package/dist/src/syntax/parse-token-expression.d.ts +3 -0
  168. package/dist/src/syntax/parse-token-expression.js +133 -0
  169. package/dist/src/tooling/case-style.js +47 -30
  170. package/dist/src/z80/effect-groups.d.ts +38 -0
  171. package/dist/src/z80/effect-groups.js +265 -0
  172. package/dist/src/z80/effect-units.d.ts +18 -0
  173. package/dist/src/z80/effect-units.js +165 -0
  174. package/dist/src/z80/effects.d.ts +1 -1
  175. package/dist/src/z80/effects.js +94 -557
  176. package/dist/src/z80/encode-core.d.ts +2 -0
  177. package/dist/src/z80/encode-core.js +42 -0
  178. package/dist/src/z80/encode-ld-helpers.d.ts +25 -0
  179. package/dist/src/z80/encode-ld-helpers.js +172 -0
  180. package/dist/src/z80/encode-ld.d.ts +2 -0
  181. package/dist/src/z80/encode-ld.js +285 -0
  182. package/dist/src/z80/encode.js +190 -542
  183. package/dist/src/z80/ld-support.d.ts +3 -0
  184. package/dist/src/z80/ld-support.js +146 -0
  185. package/dist/src/z80/operand-split-state.d.ts +8 -0
  186. package/dist/src/z80/operand-split-state.js +46 -0
  187. package/dist/src/z80/operand-split.d.ts +1 -0
  188. package/dist/src/z80/operand-split.js +13 -0
  189. package/dist/src/z80/parse-basic.d.ts +4 -0
  190. package/dist/src/z80/parse-basic.js +39 -0
  191. package/dist/src/z80/parse-branch.d.ts +4 -0
  192. package/dist/src/z80/parse-branch.js +218 -0
  193. package/dist/src/z80/parse-conditions.d.ts +6 -0
  194. package/dist/src/z80/parse-conditions.js +10 -0
  195. package/dist/src/z80/parse-exchange.d.ts +2 -0
  196. package/dist/src/z80/parse-exchange.js +30 -0
  197. package/dist/src/z80/parse-instruction.js +224 -1010
  198. package/dist/src/z80/parse-io-control.d.ts +5 -0
  199. package/dist/src/z80/parse-io-control.js +108 -0
  200. package/dist/src/z80/parse-ld.d.ts +2 -0
  201. package/dist/src/z80/parse-ld.js +83 -0
  202. package/dist/src/z80/parse-operands.d.ts +41 -0
  203. package/dist/src/z80/parse-operands.js +259 -0
  204. package/docs/reference/cli.md +42 -35
  205. package/docs/reference/tooling-api.md +20 -16
  206. package/package.json +1 -1
  207. package/dist/src/register-care/accept-output.d.ts +0 -2
  208. package/dist/src/register-care/analyze.js +0 -166
  209. package/dist/src/register-care/annotate.d.ts +0 -11
  210. package/dist/src/register-care/annotations.d.ts +0 -8
  211. package/dist/src/register-care/boundaryHints.d.ts +0 -3
  212. package/dist/src/register-care/boundaryHints.js +0 -80
  213. package/dist/src/register-care/carriers.d.ts +0 -2
  214. package/dist/src/register-care/controlFlow.d.ts +0 -5
  215. package/dist/src/register-care/controlFlow.js +0 -38
  216. package/dist/src/register-care/fix.d.ts +0 -11
  217. package/dist/src/register-care/instruction-shape.d.ts +0 -11
  218. package/dist/src/register-care/instruction-shape.js +0 -129
  219. package/dist/src/register-care/liveness.d.ts +0 -3
  220. package/dist/src/register-care/programModel.d.ts +0 -3
  221. package/dist/src/register-care/programModel.js +0 -266
  222. package/dist/src/register-care/report.d.ts +0 -5
  223. package/dist/src/register-care/routine-summaries.d.ts +0 -6
  224. package/dist/src/register-care/smartComments.d.ts +0 -5
  225. package/dist/src/register-care/smartComments.js +0 -243
  226. package/dist/src/register-care/summaries.d.ts +0 -12
  227. package/dist/src/register-care/summary.d.ts +0 -3
  228. package/dist/src/register-care/summary.js +0 -474
  229. package/dist/src/register-care/tooling.d.ts +0 -43
  230. package/dist/src/register-care/types.d.ts +0 -172
  231. /package/dist/src/{register-care → register-contracts}/accept-output.js +0 -0
  232. /package/dist/src/{register-care → register-contracts}/carriers.js +0 -0
  233. /package/dist/src/{register-care → register-contracts}/sourceText.d.ts +0 -0
  234. /package/dist/src/{register-care → register-contracts}/sourceText.js +0 -0
  235. /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,2 @@
1
+ import type { UnaryOperator } from './constant-operator-types.js';
2
+ export declare function applyUnaryOperator(operator: UnaryOperator, value: number): number;
@@ -0,0 +1,8 @@
1
+ const unaryOperators = {
2
+ '+': (value) => value,
3
+ '-': (value) => -value,
4
+ '~': (value) => ~value,
5
+ };
6
+ export function applyUnaryOperator(operator, value) {
7
+ return unaryOperators[operator](value);
8
+ }
@@ -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
- let quote;
6
- let escaped = false;
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
- let quote;
31
- let escaped = false;
32
- for (let index = 0; index < text.length; index += 1) {
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
- return undefined;
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
- if (profile.extends !== undefined && profile.extends !== 'azm') {
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 = normalizeAliasKey(rawKey);
118
- if (!key)
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;