@jhlagado/azm 0.2.7 → 0.2.9

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 (253) hide show
  1. package/README.md +239 -76
  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 +55 -227
  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/assemble-program.js +5 -0
  15. package/dist/src/assembly/fixup-emission.js +30 -48
  16. package/dist/src/assembly/import-visibility.d.ts +3 -0
  17. package/dist/src/assembly/import-visibility.js +204 -0
  18. package/dist/src/assembly/program-emission.js +163 -164
  19. package/dist/src/cli/artifact-files.d.ts +15 -0
  20. package/dist/src/cli/artifact-files.js +86 -0
  21. package/dist/src/cli/parse-args.d.ts +6 -5
  22. package/dist/src/cli/parse-args.js +162 -136
  23. package/dist/src/cli/run.js +4 -1
  24. package/dist/src/cli/usage.d.ts +1 -0
  25. package/dist/src/cli/usage.js +33 -0
  26. package/dist/src/cli/write-artifacts.js +18 -91
  27. package/dist/src/core/compile.js +51 -274
  28. package/dist/src/core/conditional-assembly.d.ts +6 -0
  29. package/dist/src/core/conditional-assembly.js +181 -0
  30. package/dist/src/expansion/op-constant-expression.d.ts +3 -0
  31. package/dist/src/expansion/op-constant-expression.js +52 -0
  32. package/dist/src/expansion/op-expand-selected.d.ts +5 -0
  33. package/dist/src/expansion/op-expand-selected.js +143 -0
  34. package/dist/src/expansion/op-expansion.d.ts +5 -53
  35. package/dist/src/expansion/op-expansion.js +85 -815
  36. package/dist/src/expansion/op-instruction-instantiation.d.ts +3 -0
  37. package/dist/src/expansion/op-instruction-instantiation.js +194 -0
  38. package/dist/src/expansion/op-local-labels.d.ts +8 -0
  39. package/dist/src/expansion/op-local-labels.js +166 -0
  40. package/dist/src/expansion/op-operand-splitting.d.ts +1 -0
  41. package/dist/src/expansion/op-operand-splitting.js +44 -0
  42. package/dist/src/expansion/op-operands.d.ts +53 -0
  43. package/dist/src/expansion/op-operands.js +66 -0
  44. package/dist/src/expansion/op-selection.d.ts +18 -0
  45. package/dist/src/expansion/op-selection.js +172 -0
  46. package/dist/src/index.d.ts +2 -1
  47. package/dist/src/index.js +1 -1
  48. package/dist/src/model/diagnostic.d.ts +4 -0
  49. package/dist/src/model/diagnostic.js +4 -0
  50. package/dist/src/node/source-host.js +40 -13
  51. package/dist/src/outputs/asm80-expression-evaluation.d.ts +10 -0
  52. package/dist/src/outputs/asm80-expression-evaluation.js +75 -0
  53. package/dist/src/outputs/asm80-expressions.d.ts +5 -0
  54. package/dist/src/outputs/asm80-expressions.js +47 -0
  55. package/dist/src/outputs/asm80-instruction-operands.d.ts +16 -0
  56. package/dist/src/outputs/asm80-instruction-operands.js +38 -0
  57. package/dist/src/outputs/asm80-instructions.d.ts +5 -0
  58. package/dist/src/outputs/asm80-instructions.js +272 -0
  59. package/dist/src/outputs/asm80-ld-operands.d.ts +10 -0
  60. package/dist/src/outputs/asm80-ld-operands.js +157 -0
  61. package/dist/src/outputs/asm80-strings.d.ts +4 -0
  62. package/dist/src/outputs/asm80-strings.js +14 -0
  63. package/dist/src/outputs/d8-files.d.ts +10 -0
  64. package/dist/src/outputs/d8-files.js +103 -0
  65. package/dist/src/outputs/d8-helpers.d.ts +21 -0
  66. package/dist/src/outputs/d8-helpers.js +136 -0
  67. package/dist/src/outputs/hex.js +26 -18
  68. package/dist/src/outputs/types.d.ts +16 -10
  69. package/dist/src/outputs/write-asm80.js +72 -597
  70. package/dist/src/outputs/write-d8.js +6 -216
  71. package/dist/src/register-contracts/accept-output.d.ts +2 -0
  72. package/dist/src/register-contracts/analyze-helpers.d.ts +29 -0
  73. package/dist/src/register-contracts/analyze-helpers.js +162 -0
  74. package/dist/src/{register-care → register-contracts}/analyze.d.ts +6 -6
  75. package/dist/src/register-contracts/analyze.js +73 -0
  76. package/dist/src/register-contracts/annotate.d.ts +11 -0
  77. package/dist/src/{register-care → register-contracts}/annotate.js +3 -3
  78. package/dist/src/register-contracts/annotations.d.ts +8 -0
  79. package/dist/src/{register-care → register-contracts}/annotations.js +3 -3
  80. package/dist/src/register-contracts/boundaryHints.d.ts +3 -0
  81. package/dist/src/register-contracts/boundaryHints.js +24 -0
  82. package/dist/src/register-contracts/carriers.d.ts +2 -0
  83. package/dist/src/register-contracts/constants.d.ts +4 -0
  84. package/dist/src/register-contracts/constants.js +51 -0
  85. package/dist/src/register-contracts/controlFlow.d.ts +5 -0
  86. package/dist/src/register-contracts/controlFlow.js +55 -0
  87. package/dist/src/register-contracts/fix.d.ts +11 -0
  88. package/dist/src/{register-care → register-contracts}/fix.js +47 -30
  89. package/dist/src/register-contracts/instruction-head.d.ts +2 -0
  90. package/dist/src/register-contracts/instruction-head.js +3 -0
  91. package/dist/src/register-contracts/instruction-operands.d.ts +3 -0
  92. package/dist/src/register-contracts/instruction-operands.js +101 -0
  93. package/dist/src/register-contracts/instruction-predicates.d.ts +6 -0
  94. package/dist/src/register-contracts/instruction-predicates.js +44 -0
  95. package/dist/src/register-contracts/interfaceContracts.d.ts +2 -0
  96. package/dist/src/register-contracts/interfaceContracts.js +68 -0
  97. package/dist/src/register-contracts/liveness.d.ts +3 -0
  98. package/dist/src/{register-care → register-contracts}/liveness.js +111 -79
  99. package/dist/src/register-contracts/operand-register-name.d.ts +2 -0
  100. package/dist/src/register-contracts/operand-register-name.js +13 -0
  101. package/dist/src/{register-care → register-contracts}/profiles.d.ts +5 -5
  102. package/dist/src/{register-care → register-contracts}/profiles.js +2 -2
  103. package/dist/src/register-contracts/programModel-boundaries.d.ts +6 -0
  104. package/dist/src/register-contracts/programModel-boundaries.js +64 -0
  105. package/dist/src/register-contracts/programModel-routines.d.ts +7 -0
  106. package/dist/src/register-contracts/programModel-routines.js +144 -0
  107. package/dist/src/register-contracts/programModel.d.ts +3 -0
  108. package/dist/src/register-contracts/programModel.js +14 -0
  109. package/dist/src/register-contracts/report.d.ts +5 -0
  110. package/dist/src/{register-care → register-contracts}/report.js +34 -17
  111. package/dist/src/register-contracts/routine-summaries.d.ts +6 -0
  112. package/dist/src/{register-care → register-contracts}/routine-summaries.js +11 -1
  113. package/dist/src/register-contracts/smartCommentBlocks.d.ts +5 -0
  114. package/dist/src/register-contracts/smartCommentBlocks.js +30 -0
  115. package/dist/src/register-contracts/smartCommentParsing.d.ts +3 -0
  116. package/dist/src/register-contracts/smartCommentParsing.js +80 -0
  117. package/dist/src/register-contracts/smartComments.d.ts +5 -0
  118. package/dist/src/register-contracts/smartComments.js +92 -0
  119. package/dist/src/register-contracts/summaries.d.ts +12 -0
  120. package/dist/src/{register-care → register-contracts}/summaries.js +7 -7
  121. package/dist/src/register-contracts/summary-boundary.d.ts +2 -0
  122. package/dist/src/register-contracts/summary-boundary.js +40 -0
  123. package/dist/src/register-contracts/summary-contract.d.ts +2 -0
  124. package/dist/src/register-contracts/summary-contract.js +45 -0
  125. package/dist/src/register-contracts/summary-result.d.ts +7 -0
  126. package/dist/src/register-contracts/summary-result.js +122 -0
  127. package/dist/src/register-contracts/summary-state.d.ts +23 -0
  128. package/dist/src/register-contracts/summary-state.js +88 -0
  129. package/dist/src/register-contracts/summary-token-transfer.d.ts +3 -0
  130. package/dist/src/register-contracts/summary-token-transfer.js +67 -0
  131. package/dist/src/register-contracts/summary.d.ts +3 -0
  132. package/dist/src/register-contracts/summary.js +266 -0
  133. package/dist/src/register-contracts/tooling.d.ts +57 -0
  134. package/dist/src/{register-care → register-contracts}/tooling.js +8 -6
  135. package/dist/src/register-contracts/types.d.ts +188 -0
  136. package/dist/src/semantics/binary-operators.d.ts +2 -0
  137. package/dist/src/semantics/binary-operators.js +15 -0
  138. package/dist/src/semantics/byte-functions.d.ts +2 -0
  139. package/dist/src/semantics/byte-functions.js +7 -0
  140. package/dist/src/semantics/constant-operator-types.d.ts +10 -0
  141. package/dist/src/semantics/constant-operator-types.js +1 -0
  142. package/dist/src/semantics/constant-operators.d.ts +3 -0
  143. package/dist/src/semantics/constant-operators.js +3 -0
  144. package/dist/src/semantics/diagnostics.d.ts +3 -0
  145. package/dist/src/semantics/diagnostics.js +10 -0
  146. package/dist/src/semantics/expression-evaluation.d.ts +11 -19
  147. package/dist/src/semantics/expression-evaluation.js +22 -334
  148. package/dist/src/semantics/layout-evaluation.d.ts +23 -0
  149. package/dist/src/semantics/layout-evaluation.js +202 -0
  150. package/dist/src/semantics/layout-format.d.ts +5 -0
  151. package/dist/src/semantics/layout-format.js +31 -0
  152. package/dist/src/semantics/layout-path.d.ts +24 -0
  153. package/dist/src/semantics/layout-path.js +58 -0
  154. package/dist/src/semantics/unary-operators.d.ts +2 -0
  155. package/dist/src/semantics/unary-operators.js +8 -0
  156. package/dist/src/source/line-comment-scanner.d.ts +1 -0
  157. package/dist/src/source/line-comment-scanner.js +51 -0
  158. package/dist/src/source/logical-lines.d.ts +3 -0
  159. package/dist/src/source/source-span.d.ts +2 -0
  160. package/dist/src/source/strip-line-comment.js +8 -44
  161. package/dist/src/syntax/directive-aliases.js +36 -22
  162. package/dist/src/syntax/expression-tokenizer.d.ts +30 -0
  163. package/dist/src/syntax/expression-tokenizer.js +310 -0
  164. package/dist/src/syntax/parse-directive-statement.d.ts +9 -0
  165. package/dist/src/syntax/parse-directive-statement.js +309 -0
  166. package/dist/src/syntax/parse-expression.d.ts +2 -2
  167. package/dist/src/syntax/parse-expression.js +7 -568
  168. package/dist/src/syntax/parse-layout-declarations.d.ts +9 -0
  169. package/dist/src/syntax/parse-layout-declarations.js +189 -0
  170. package/dist/src/syntax/parse-layout-expression.d.ts +5 -0
  171. package/dist/src/syntax/parse-layout-expression.js +175 -0
  172. package/dist/src/syntax/parse-line.js +21 -273
  173. package/dist/src/syntax/parse-token-expression.d.ts +3 -0
  174. package/dist/src/syntax/parse-token-expression.js +133 -0
  175. package/dist/src/tooling/api.js +1 -1
  176. package/dist/src/tooling/case-style.js +47 -30
  177. package/dist/src/z80/effect-groups.d.ts +38 -0
  178. package/dist/src/z80/effect-groups.js +265 -0
  179. package/dist/src/z80/effect-units.d.ts +18 -0
  180. package/dist/src/z80/effect-units.js +165 -0
  181. package/dist/src/z80/effects.d.ts +1 -1
  182. package/dist/src/z80/effects.js +94 -557
  183. package/dist/src/z80/encode-core.d.ts +2 -0
  184. package/dist/src/z80/encode-core.js +42 -0
  185. package/dist/src/z80/encode-ld-helpers.d.ts +25 -0
  186. package/dist/src/z80/encode-ld-helpers.js +172 -0
  187. package/dist/src/z80/encode-ld.d.ts +2 -0
  188. package/dist/src/z80/encode-ld.js +285 -0
  189. package/dist/src/z80/encode.js +190 -542
  190. package/dist/src/z80/ld-support.d.ts +3 -0
  191. package/dist/src/z80/ld-support.js +146 -0
  192. package/dist/src/z80/operand-split-state.d.ts +8 -0
  193. package/dist/src/z80/operand-split-state.js +46 -0
  194. package/dist/src/z80/operand-split.d.ts +1 -0
  195. package/dist/src/z80/operand-split.js +13 -0
  196. package/dist/src/z80/parse-basic.d.ts +4 -0
  197. package/dist/src/z80/parse-basic.js +39 -0
  198. package/dist/src/z80/parse-branch.d.ts +4 -0
  199. package/dist/src/z80/parse-branch.js +218 -0
  200. package/dist/src/z80/parse-conditions.d.ts +6 -0
  201. package/dist/src/z80/parse-conditions.js +10 -0
  202. package/dist/src/z80/parse-exchange.d.ts +2 -0
  203. package/dist/src/z80/parse-exchange.js +30 -0
  204. package/dist/src/z80/parse-instruction.js +224 -1010
  205. package/dist/src/z80/parse-io-control.d.ts +5 -0
  206. package/dist/src/z80/parse-io-control.js +108 -0
  207. package/dist/src/z80/parse-ld.d.ts +2 -0
  208. package/dist/src/z80/parse-ld.js +83 -0
  209. package/dist/src/z80/parse-operands.d.ts +41 -0
  210. package/dist/src/z80/parse-operands.js +259 -0
  211. package/docs/codebase/01-orientation-and-repository-layout.md +192 -0
  212. package/docs/codebase/02-source-loading-and-parsing.md +263 -0
  213. package/docs/codebase/03-assembly-and-z80-emission.md +251 -0
  214. package/docs/codebase/04-ops-and-register-contracts.md +237 -0
  215. package/docs/codebase/05-interfaces-and-output-artifacts.md +253 -0
  216. package/docs/codebase/06-verification-and-maintenance.md +202 -0
  217. package/docs/codebase/appendices/a-directory-file-reference.md +253 -0
  218. package/docs/codebase/appendices/b-compile-flow-reference.md +103 -0
  219. package/docs/codebase/appendices/c-public-surface-reference.md +106 -0
  220. package/docs/codebase/appendices/index.md +16 -0
  221. package/docs/codebase/index.md +46 -0
  222. package/package.json +2 -3
  223. package/dist/src/register-care/accept-output.d.ts +0 -2
  224. package/dist/src/register-care/analyze.js +0 -166
  225. package/dist/src/register-care/annotate.d.ts +0 -11
  226. package/dist/src/register-care/annotations.d.ts +0 -8
  227. package/dist/src/register-care/boundaryHints.d.ts +0 -3
  228. package/dist/src/register-care/boundaryHints.js +0 -80
  229. package/dist/src/register-care/carriers.d.ts +0 -2
  230. package/dist/src/register-care/controlFlow.d.ts +0 -5
  231. package/dist/src/register-care/controlFlow.js +0 -38
  232. package/dist/src/register-care/fix.d.ts +0 -11
  233. package/dist/src/register-care/instruction-shape.d.ts +0 -11
  234. package/dist/src/register-care/instruction-shape.js +0 -129
  235. package/dist/src/register-care/liveness.d.ts +0 -3
  236. package/dist/src/register-care/programModel.d.ts +0 -3
  237. package/dist/src/register-care/programModel.js +0 -266
  238. package/dist/src/register-care/report.d.ts +0 -5
  239. package/dist/src/register-care/routine-summaries.d.ts +0 -6
  240. package/dist/src/register-care/smartComments.d.ts +0 -5
  241. package/dist/src/register-care/smartComments.js +0 -243
  242. package/dist/src/register-care/summaries.d.ts +0 -12
  243. package/dist/src/register-care/summary.d.ts +0 -3
  244. package/dist/src/register-care/summary.js +0 -474
  245. package/dist/src/register-care/tooling.d.ts +0 -43
  246. package/dist/src/register-care/types.d.ts +0 -172
  247. package/docs/reference/cli.md +0 -151
  248. package/docs/reference/tooling-api.md +0 -316
  249. /package/dist/src/{register-care → register-contracts}/accept-output.js +0 -0
  250. /package/dist/src/{register-care → register-contracts}/carriers.js +0 -0
  251. /package/dist/src/{register-care → register-contracts}/sourceText.d.ts +0 -0
  252. /package/dist/src/{register-care → register-contracts}/sourceText.js +0 -0
  253. /package/dist/src/{register-care → register-contracts}/types.js +0 -0
@@ -0,0 +1,172 @@
1
+ import { expressionFitsKnownImm16, expressionFitsKnownImm8, } from './op-constant-expression.js';
2
+ import { formatOpOperand, isConditionToken, } from './op-operands.js';
3
+ const MATCHER_OPERAND_PREDICATES = {
4
+ reg8: (operand) => operand.kind === 'reg8',
5
+ reg16: (operand) => operand.kind === 'reg16',
6
+ imm8: (operand) => operand.kind === 'imm' && expressionFitsKnownImm8(operand.expression),
7
+ imm16: (operand) => operand.kind === 'imm' && expressionFitsKnownImm16(operand.expression),
8
+ cc: (operand) => isConditionToken(operand.text),
9
+ idx16: (operand) => operand.kind === 'indexed',
10
+ ea: (operand) => operand.kind === 'imm',
11
+ mem8: (operand) => isMemoryOperand(operand),
12
+ mem16: (operand) => isMemoryOperand(operand),
13
+ };
14
+ const MATCHER_EXPECTATIONS = {
15
+ reg8: 'reg8',
16
+ reg16: 'reg16',
17
+ imm8: 'imm8',
18
+ imm16: 'imm16',
19
+ cc: 'condition token NZ/Z/NC/C/PO/PE/P/M',
20
+ idx16: 'IX/IY indexed memory operand',
21
+ ea: 'ea',
22
+ mem8: 'mem8 dereference',
23
+ mem16: 'mem16 dereference',
24
+ };
25
+ export function selectOpOverload(overloads, operands) {
26
+ const arityMatches = overloads.filter((overload) => overload.params.length === operands.length);
27
+ if (arityMatches.length === 0) {
28
+ return { kind: 'arity_mismatch' };
29
+ }
30
+ const matches = arityMatches.filter((overload) => overload.params.every((param, index) => matcherMatchesOperand(param.matcher, operands[index])));
31
+ if (matches.length === 0) {
32
+ return { kind: 'no_match', candidates: arityMatches };
33
+ }
34
+ if (matches.length === 1) {
35
+ return { kind: 'selected', overload: matches[0] };
36
+ }
37
+ const selected = mostSpecificOverload(matches, operands);
38
+ return selected
39
+ ? { kind: 'selected', overload: selected }
40
+ : { kind: 'ambiguous', candidates: matches };
41
+ }
42
+ function mostSpecificOverload(overloads, operands) {
43
+ for (const candidate of overloads) {
44
+ let beatsAll = true;
45
+ for (const other of overloads) {
46
+ if (candidate === other)
47
+ continue;
48
+ if (compareOverloadSpecificity(candidate, other, operands) !== 'x') {
49
+ beatsAll = false;
50
+ break;
51
+ }
52
+ }
53
+ if (beatsAll)
54
+ return candidate;
55
+ }
56
+ return undefined;
57
+ }
58
+ function compareOverloadSpecificity(x, y, operands) {
59
+ const votes = operands.reduce((current, operand, index) => {
60
+ return addSpecificityVote(current, compareMatcherSpecificity(x.params[index].matcher, y.params[index].matcher, operand));
61
+ }, { x: 0, y: 0 });
62
+ return specificityFromVotes(votes);
63
+ }
64
+ function addSpecificityVote(votes, comparison) {
65
+ if (comparison === 'x')
66
+ return { ...votes, x: votes.x + 1 };
67
+ if (comparison === 'y')
68
+ return { ...votes, y: votes.y + 1 };
69
+ return votes;
70
+ }
71
+ function specificityFromVotes(votes) {
72
+ if (votes.x > 0 && votes.y === 0)
73
+ return 'x';
74
+ if (votes.y > 0 && votes.x === 0)
75
+ return 'y';
76
+ if (votes.x === 0 && votes.y === 0)
77
+ return 'equal';
78
+ return 'incomparable';
79
+ }
80
+ function compareMatcherSpecificity(x, y, operand) {
81
+ if (x.kind === y.kind)
82
+ return 'equal';
83
+ return (compareFixedMatcherSpecificity(x, y, operand) ??
84
+ compareImmediateMatcherSpecificity(x, y, operand) ??
85
+ 'equal');
86
+ }
87
+ function compareFixedMatcherSpecificity(x, y, operand) {
88
+ if (x.kind === 'fixed' && fixedTokenBeatsMatcher(x, y, operand))
89
+ return 'x';
90
+ if (y.kind === 'fixed' && fixedTokenBeatsMatcher(y, x, operand))
91
+ return 'y';
92
+ return undefined;
93
+ }
94
+ function compareImmediateMatcherSpecificity(x, y, operand) {
95
+ if (operand.kind !== 'imm' || !expressionFitsKnownImm8(operand.expression))
96
+ return undefined;
97
+ if (x.kind === 'imm8' && y.kind === 'imm16')
98
+ return 'x';
99
+ if (x.kind === 'imm16' && y.kind === 'imm8')
100
+ return 'y';
101
+ return undefined;
102
+ }
103
+ function fixedTokenBeatsMatcher(fixed, other, operand) {
104
+ return ((other.kind === 'reg8' && operand.kind === 'reg8' && operand.text === fixed.token) ||
105
+ (other.kind === 'reg16' && operand.kind === 'reg16' && operand.text === fixed.token) ||
106
+ (other.kind === 'cc' &&
107
+ isConditionToken(fixed.token) &&
108
+ operand.text.toUpperCase() === fixed.token));
109
+ }
110
+ function matcherMatchesOperand(matcher, operand) {
111
+ if (matcher.kind === 'fixed')
112
+ return operand.text.toUpperCase() === matcher.token;
113
+ return MATCHER_OPERAND_PREDICATES[matcher.kind]?.(operand) ?? false;
114
+ }
115
+ function isMemoryOperand(operand) {
116
+ return operand.kind === 'reg-indirect' || operand.kind === 'mem-abs' || operand.kind === 'indexed';
117
+ }
118
+ export function formatOpSelectionDiagnostic(selection, overloads, operands) {
119
+ const name = overloads[0]?.name ?? '<unknown>';
120
+ const operandSummary = `call-site operands: (${operands.map(formatOpOperand).join(', ')})`;
121
+ switch (selection.kind) {
122
+ case 'arity_mismatch':
123
+ return [
124
+ `No op overload of "${name}" accepts ${operands.length} operand(s).`,
125
+ 'available overloads:',
126
+ ...overloads.map((overload) => ` - ${formatOpSignature(overload)}`),
127
+ ].join('\n');
128
+ case 'no_match':
129
+ return noMatchDiagnostic(selection.candidates, operands, name, operandSummary);
130
+ case 'ambiguous':
131
+ return [
132
+ `Ambiguous op overload for "${name}" (${selection.candidates.length} matches).`,
133
+ operandSummary,
134
+ 'equally specific candidates:',
135
+ ...selection.candidates.map((candidate) => ` - ${formatOpSignatureWithLocation(candidate)}`),
136
+ ].join('\n');
137
+ }
138
+ }
139
+ function noMatchDiagnostic(candidates, operands, name, operandSummary) {
140
+ return [
141
+ `No matching op overload for "${name}" with provided operands.`,
142
+ operandSummary,
143
+ 'available overloads:',
144
+ ...candidates.map((candidate) => {
145
+ const mismatch = firstMismatchReason(candidate, operands);
146
+ return ` - ${formatOpSignatureWithLocation(candidate)}${mismatch ? ` ; ${mismatch}` : ''}`;
147
+ }),
148
+ ].join('\n');
149
+ }
150
+ function firstMismatchReason(overload, operands) {
151
+ for (let index = 0; index < overload.params.length; index += 1) {
152
+ const param = overload.params[index];
153
+ const operand = operands[index];
154
+ if (!matcherMatchesOperand(param.matcher, operand)) {
155
+ return `${param.name}: ${matcherMismatchReason(param.matcher, operand)}`;
156
+ }
157
+ }
158
+ return undefined;
159
+ }
160
+ function matcherMismatchReason(matcher, operand) {
161
+ const expected = matcher.kind === 'fixed' ? matcher.token : MATCHER_EXPECTATIONS[matcher.kind];
162
+ return `expects ${expected}, got ${formatOpOperand(operand)}`;
163
+ }
164
+ function formatOpSignature(op) {
165
+ return `${op.name}(${op.params.map((param) => `${param.name} ${formatMatcher(param.matcher)}`).join(', ')})`;
166
+ }
167
+ function formatOpSignatureWithLocation(op) {
168
+ return `${formatOpSignature(op)} (${op.sourceName}:${op.line})`;
169
+ }
170
+ function formatMatcher(matcher) {
171
+ return matcher.kind === 'fixed' ? matcher.token : matcher.kind;
172
+ }
@@ -6,9 +6,10 @@ export { formatDiagnostic, formatNextDiagnostic } from './diagnostics/format.js'
6
6
  export { analyzeProgram, loadProgram, analyzeProgramNext, loadProgramNext } from './tooling/api.js';
7
7
  export { DiagnosticIds } from './model/diagnostic.js';
8
8
  export type { DiagnosticId, DiagnosticSeverity, Diagnostic } from './model/diagnostic.js';
9
- export { analyzeRegisterCareForTools, type AnalyzeRegisterCareForToolsOptions, type AnalyzeRegisterCareForToolsResult, type RegisterCareCandidateDiagnostic, type RegisterCareCodeAction, } from './register-care/tooling.js';
9
+ export { analyzeRegisterCareForTools, analyzeRegisterContractsForTools, type AnalyzeRegisterCareForToolsOptions, type AnalyzeRegisterCareForToolsResult, type AnalyzeRegisterContractsForToolsOptions, type AnalyzeRegisterContractsForToolsResult, type RegisterCareCandidateDiagnostic, type RegisterCareCodeAction, type RegisterCareTextEdit, type RegisterContractsCandidateDiagnostic, type RegisterContractsCodeAction, type RegisterContractsTextEdit, } from './register-contracts/tooling.js';
10
10
  export { compile, defaultFormatWriters, writeHex } from './api-compile.js';
11
11
  export type { AddressRange, Artifact, CompileDependencies, CompileFunctionOptions, CompileResult, CompileNextDependencies, CompileNextFunctionOptions, EmittedByteMap, FormatWriters, CompileNextResult as CompileNextProgramResult, } from './api-compile.js';
12
12
  export type { AnalyzeProgramOptions, AnalyzeProgramResult, LoadProgramOptions, LoadProgramResult, LoadedProgram, AnalyzeProgramNextOptions, AnalyzeProgramNextResult, LoadProgramNextOptions, LoadProgramNextResult, LoadedProgramNext, } from './tooling/api.js';
13
13
  export type { CaseStyleMode } from './tooling/case-style.js';
14
+ export type { RegisterCareMode, RegisterCareOutputCandidate, RegisterCareUnit, RegisterContractsMode, RegisterContractsOutputCandidate, RegisterContractsUnit, } from './register-contracts/types.js';
14
15
  export type { D8mArtifact, D8mGenerator, D8mJson, D8mFileEntry, D8mFileSymbol, D8mSegment, D8mSymbol, SymbolEntry, WriteD8mOptions, } from './outputs/types.js';
package/dist/src/index.js CHANGED
@@ -3,5 +3,5 @@ export { compileArtifacts, compileNextArtifacts } from './core/compile-artifacts
3
3
  export { formatDiagnostic, formatNextDiagnostic } from './diagnostics/format.js';
4
4
  export { analyzeProgram, loadProgram, analyzeProgramNext, loadProgramNext } from './tooling/api.js';
5
5
  export { DiagnosticIds } from './model/diagnostic.js';
6
- export { analyzeRegisterCareForTools, } from './register-care/tooling.js';
6
+ export { analyzeRegisterCareForTools, analyzeRegisterContractsForTools, } from './register-contracts/tooling.js';
7
7
  export { compile, defaultFormatWriters, writeHex } from './api-compile.js';
@@ -44,7 +44,11 @@ export declare const DiagnosticIds: {
44
44
  readonly TypeError: "AZM403";
45
45
  readonly CaseStyleLint: "AZM500";
46
46
  readonly IndexParenRedundant: "AZM501";
47
+ readonly RegisterContractsConflict: "AZM600";
48
+ readonly RegisterContractsUnknownBoundary: "AZM601";
49
+ /** @deprecated Use RegisterContractsConflict. */
47
50
  readonly RegisterCareConflict: "AZM600";
51
+ /** @deprecated Use RegisterContractsUnknownBoundary. */
48
52
  readonly RegisterCareUnknownBoundary: "AZM601";
49
53
  };
50
54
  export type DiagnosticId = (typeof DiagnosticIds)[keyof typeof DiagnosticIds];
@@ -24,6 +24,10 @@ export const DiagnosticIds = {
24
24
  TypeError: 'AZM403',
25
25
  CaseStyleLint: 'AZM500',
26
26
  IndexParenRedundant: 'AZM501',
27
+ RegisterContractsConflict: 'AZM600',
28
+ RegisterContractsUnknownBoundary: 'AZM601',
29
+ /** @deprecated Use RegisterContractsConflict. */
27
30
  RegisterCareConflict: 'AZM600',
31
+ /** @deprecated Use RegisterContractsUnknownBoundary. */
28
32
  RegisterCareUnknownBoundary: 'AZM601',
29
33
  };
@@ -18,6 +18,7 @@ export async function expandSourceForTooling(options) {
18
18
  }
19
19
  const sourceTexts = new Map();
20
20
  const sourceLineComments = new Map();
21
+ const loadedImports = new Set();
21
22
  const includeDirs = (options.includeDirs ?? []).map((path) => normalize(path));
22
23
  const lines = await expandFile({
23
24
  sourcePath: entryFile,
@@ -25,7 +26,10 @@ export async function expandSourceForTooling(options) {
25
26
  sourceTexts,
26
27
  sourceLineComments,
27
28
  diagnostics,
28
- includeStack: [],
29
+ loadedImports,
30
+ sourceStack: [],
31
+ sourceUnit: entryFile,
32
+ sourceRelation: 'entry',
29
33
  ...(options.preloadedText !== undefined ? { preloadedText: options.preloadedText } : {}),
30
34
  ...(options.signal !== undefined ? { signal: options.signal } : {}),
31
35
  });
@@ -37,11 +41,11 @@ export async function expandSourceForTooling(options) {
37
41
  async function expandFile(options) {
38
42
  options.signal?.throwIfAborted();
39
43
  const sourcePath = normalize(options.sourcePath);
40
- if (options.includeStack.includes(sourcePath)) {
44
+ if (options.sourceStack.some((entry) => entry.sourcePath === sourcePath)) {
41
45
  options.diagnostics.push({
42
46
  severity: 'error',
43
47
  code: 'AZMN_SOURCE',
44
- message: `recursive include: ${sourcePath}`,
48
+ message: `recursive ${options.sourceRelation}: ${sourcePath}`,
45
49
  sourceName: sourcePath,
46
50
  });
47
51
  return undefined;
@@ -54,28 +58,36 @@ async function expandFile(options) {
54
58
  const output = [];
55
59
  for (const line of scanLogicalLines(createSourceFile(sourcePath, text))) {
56
60
  recordLineComment(options.sourceLineComments, line);
57
- const includePath = parseIncludePath(line.text);
58
- if (!includePath) {
59
- output.push(line);
61
+ const directive = parseSourceLoadDirective(line.text);
62
+ if (!directive) {
63
+ output.push(withSourceOwnership(line, options.sourceUnit, options.sourceRelation));
60
64
  continue;
61
65
  }
62
- const result = await resolveInclude(sourcePath, includePath, options.includeDirs);
66
+ const result = await resolveSourcePath(sourcePath, directive.path, options.includeDirs);
63
67
  if (result.resolved === undefined) {
64
68
  options.diagnostics.push({
65
69
  severity: 'error',
66
70
  code: 'AZMN_SOURCE',
67
- message: `Failed to resolve include "${includePath}" from "${sourcePath}". Tried:\n${result.searchCandidates.map((candidate) => `- ${candidate}`).join('\n')}`,
71
+ message: `Failed to resolve ${directive.kind} "${directive.path}" from "${sourcePath}". Tried:\n${result.searchCandidates.map((candidate) => `- ${candidate}`).join('\n')}`,
68
72
  sourceName: sourcePath,
69
73
  line: line.line,
70
74
  column: firstColumn(line.text),
71
75
  });
72
76
  continue;
73
77
  }
78
+ if (directive.kind === 'import' && options.loadedImports.has(result.resolved)) {
79
+ continue;
80
+ }
81
+ if (directive.kind === 'import') {
82
+ options.loadedImports.add(result.resolved);
83
+ }
74
84
  const { preloadedText: _preloadedText, ...includeOptions } = options;
75
85
  const included = await expandFile({
76
86
  ...includeOptions,
77
87
  sourcePath: result.resolved,
78
- includeStack: [...options.includeStack, sourcePath],
88
+ sourceStack: [...options.sourceStack, { sourcePath }],
89
+ sourceUnit: directive.kind === 'import' ? result.resolved : options.sourceUnit,
90
+ sourceRelation: directive.kind,
79
91
  });
80
92
  if (included !== undefined) {
81
93
  output.push(...included);
@@ -83,6 +95,13 @@ async function expandFile(options) {
83
95
  }
84
96
  return output;
85
97
  }
98
+ function withSourceOwnership(line, sourceUnit, sourceRelation) {
99
+ return {
100
+ ...line,
101
+ sourceUnit,
102
+ sourceRelation,
103
+ };
104
+ }
86
105
  function recordLineComment(comments, line) {
87
106
  const commentText = lineComment(line.text);
88
107
  if (commentText === undefined) {
@@ -112,7 +131,7 @@ async function readSourceText(options, sourcePath) {
112
131
  return undefined;
113
132
  }
114
133
  }
115
- async function resolveInclude(importer, includePath, includeDirs) {
134
+ async function resolveSourcePath(importer, includePath, includeDirs) {
116
135
  const candidates = [
117
136
  join(dirname(importer), includePath),
118
137
  ...includeDirs.map((dir) => join(dir, includePath)),
@@ -128,9 +147,17 @@ async function resolveInclude(importer, includePath, includeDirs) {
128
147
  }
129
148
  return { searchCandidates: candidates.map(normalize) };
130
149
  }
131
- function parseIncludePath(text) {
132
- const match = /^\.?include\s+"([^"]+)"\s*$/i.exec(stripLineComment(text).trim());
133
- return match?.[1];
150
+ function parseSourceLoadDirective(text) {
151
+ const trimmed = stripLineComment(text).trim();
152
+ const include = /^\.?include\s+"([^"]+)"\s*$/i.exec(trimmed);
153
+ if (include) {
154
+ return { kind: 'include', path: include[1] ?? '' };
155
+ }
156
+ const sourceImport = /^\.import\s+"([^"]+)"\s*$/.exec(trimmed);
157
+ if (sourceImport) {
158
+ return { kind: 'import', path: sourceImport[1] ?? '' };
159
+ }
160
+ return undefined;
134
161
  }
135
162
  function lineComment(text) {
136
163
  const prefix = stripLineComment(text);
@@ -0,0 +1,10 @@
1
+ import type { Expression } from '../model/expression.js';
2
+ import { type LayoutRecord } from '../semantics/expression-evaluation.js';
3
+ export type ConstantMap = ReadonlyMap<string, number>;
4
+ export type LayoutMap = ReadonlyMap<string, LayoutRecord>;
5
+ export type LoweredEvalContext = {
6
+ readonly constants: ConstantMap;
7
+ readonly symbols: ConstantMap;
8
+ readonly layouts: LayoutMap;
9
+ };
10
+ export declare function evaluateLoweredConstant(expression: Expression, evalContext: LoweredEvalContext): number | undefined;
@@ -0,0 +1,75 @@
1
+ import { evaluateExpression, } from '../semantics/expression-evaluation.js';
2
+ import { applyBinaryOperator, applyByteFunction, applyUnaryOperator, } from '../semantics/constant-operators.js';
3
+ const silentSpan = { sourceName: '', line: 0, column: 0 };
4
+ export function evaluateLoweredConstant(expression, evalContext) {
5
+ switch (expression.kind) {
6
+ case 'number':
7
+ return expression.value;
8
+ case 'symbol':
9
+ return evalContext.constants.get(expression.name);
10
+ case 'type-size':
11
+ return evaluateLoweredTypeSize(expression, evalContext);
12
+ case 'offset':
13
+ case 'sizeof':
14
+ return evaluateLayoutExpression(expression, evalContext);
15
+ case 'byte-function':
16
+ return evaluateLoweredByteFunction(expression, evalContext);
17
+ case 'unary':
18
+ return evaluateLoweredUnary(expression, evalContext);
19
+ case 'binary':
20
+ return evaluateLoweredBinary(expression, evalContext);
21
+ default:
22
+ return undefined;
23
+ }
24
+ }
25
+ function evaluateLoweredTypeSize(expression, evalContext) {
26
+ const constant = evalContext.constants.get(expression.typeExpr.name);
27
+ return constant ?? evaluateLayoutExpression(expression, evalContext);
28
+ }
29
+ function evaluateLayoutExpression(expression, evalContext) {
30
+ return evaluateExpression(expression, {}, new Map(), silentSpan, [], {
31
+ currentLocation: 0,
32
+ layouts: evalContext.layouts,
33
+ reportUnknown: false,
34
+ });
35
+ }
36
+ function evaluateLoweredByteFunction(expression, evalContext) {
37
+ const value = evaluateLoweredResolvedConstant(expression.expression, evalContext);
38
+ return value === undefined ? undefined : applyByteFunction(expression.function, value);
39
+ }
40
+ function evaluateLoweredUnary(expression, evalContext) {
41
+ const value = evaluateLoweredConstant(expression.expression, evalContext);
42
+ return value === undefined ? undefined : applyUnaryOperator(expression.operator, value);
43
+ }
44
+ function evaluateLoweredBinary(expression, evalContext) {
45
+ const left = evaluateLoweredConstant(expression.left, evalContext);
46
+ const right = evaluateLoweredConstant(expression.right, evalContext);
47
+ return left === undefined || right === undefined
48
+ ? undefined
49
+ : applyBinaryOperator(expression.operator, left, right);
50
+ }
51
+ function evaluateLoweredResolvedConstant(expression, evalContext) {
52
+ switch (expression.kind) {
53
+ case 'symbol':
54
+ return evalContext.symbols.get(expression.name) ?? evalContext.constants.get(expression.name);
55
+ case 'unary':
56
+ return evaluateResolvedUnary(expression, evalContext);
57
+ case 'binary':
58
+ return evaluateResolvedBinary(expression, evalContext);
59
+ case 'byte-function':
60
+ return evaluateLoweredByteFunction(expression, evalContext);
61
+ default:
62
+ return evaluateLoweredConstant(expression, evalContext);
63
+ }
64
+ }
65
+ function evaluateResolvedUnary(expression, evalContext) {
66
+ const value = evaluateLoweredResolvedConstant(expression.expression, evalContext);
67
+ return value === undefined ? undefined : applyUnaryOperator(expression.operator, value);
68
+ }
69
+ function evaluateResolvedBinary(expression, evalContext) {
70
+ const left = evaluateLoweredResolvedConstant(expression.left, evalContext);
71
+ const right = evaluateLoweredResolvedConstant(expression.right, evalContext);
72
+ return left === undefined || right === undefined
73
+ ? undefined
74
+ : applyBinaryOperator(expression.operator, left, right);
75
+ }
@@ -0,0 +1,5 @@
1
+ import type { Expression } from '../model/expression.js';
2
+ import { type LoweredEvalContext } from './asm80-expression-evaluation.js';
3
+ export { evaluateLoweredConstant, type ConstantMap, type LayoutMap, type LoweredEvalContext, } from './asm80-expression-evaluation.js';
4
+ export declare function formatExpression(expression: Expression, evalContext: LoweredEvalContext, width: 'byte' | 'word' | 'auto'): string | undefined;
5
+ export declare function formatLoweredNumber(value: number, width: 'byte' | 'word' | 'auto'): string;
@@ -0,0 +1,47 @@
1
+ import { evaluateLoweredConstant } from './asm80-expression-evaluation.js';
2
+ export { evaluateLoweredConstant, } from './asm80-expression-evaluation.js';
3
+ const formatUnaryExpression = {
4
+ '+': (inner) => inner,
5
+ '-': (inner) => `-${inner}`,
6
+ '~': () => undefined,
7
+ };
8
+ export function formatExpression(expression, evalContext, width) {
9
+ const value = evaluateLoweredConstant(expression, evalContext);
10
+ if (value !== undefined) {
11
+ return formatLoweredNumber(value, width);
12
+ }
13
+ return formatUnevaluatedExpression(expression, evalContext, width);
14
+ }
15
+ export function formatLoweredNumber(value, width) {
16
+ const normalized = value < 0 ? value & 0xffff : value;
17
+ const digits = normalized.toString(16).toUpperCase();
18
+ const minWidth = width === 'word' || (width === 'auto' && normalized > 0xff) ? 4 : 2;
19
+ return `$${digits.padStart(minWidth, '0')}`;
20
+ }
21
+ function formatUnevaluatedExpression(expression, evalContext, width) {
22
+ switch (expression.kind) {
23
+ case 'symbol':
24
+ return expression.name;
25
+ case 'type-size':
26
+ return expression.typeExpr.name;
27
+ case 'current-location':
28
+ return '$';
29
+ case 'unary':
30
+ return formatUnary(expression, evalContext, width);
31
+ case 'binary':
32
+ return formatBinary(expression, evalContext, width);
33
+ default:
34
+ return undefined;
35
+ }
36
+ }
37
+ function formatUnary(expression, evalContext, width) {
38
+ const inner = formatExpression(expression.expression, evalContext, width);
39
+ return inner === undefined ? undefined : formatUnaryExpression[expression.operator](inner);
40
+ }
41
+ function formatBinary(expression, evalContext, width) {
42
+ const left = formatExpression(expression.left, evalContext, width);
43
+ const right = formatExpression(expression.right, evalContext, width);
44
+ return left === undefined || right === undefined
45
+ ? undefined
46
+ : `${left}${expression.operator}${right}`;
47
+ }
@@ -0,0 +1,16 @@
1
+ import type { Z80Instruction } from '../z80/instruction.js';
2
+ import { type LoweredEvalContext } from './asm80-expressions.js';
3
+ import { formatIndexedMemory, formatLd, type LdOperand } from './asm80-ld-operands.js';
4
+ export type BitInstruction = Extract<Z80Instruction, {
5
+ readonly mnemonic: 'bit' | 'res' | 'set';
6
+ }>;
7
+ export type RotateShiftInstruction = Extract<Z80Instruction, {
8
+ readonly mnemonic: 'rlc' | 'rrc' | 'rl' | 'rr' | 'sla' | 'sra' | 'sll' | 'sls' | 'srl';
9
+ }>;
10
+ export { formatIndexedMemory, formatLd, type LdOperand };
11
+ export declare function formatRotateShift(instruction: RotateShiftInstruction, evalContext: LoweredEvalContext): {
12
+ readonly text: string;
13
+ } | undefined;
14
+ export declare function formatBitOp(instruction: BitInstruction, evalContext: LoweredEvalContext): {
15
+ readonly text: string;
16
+ } | undefined;
@@ -0,0 +1,38 @@
1
+ import { formatLoweredNumber } from './asm80-expressions.js';
2
+ import { formatIndexedMemory, formatLd } from './asm80-ld-operands.js';
3
+ export { formatIndexedMemory, formatLd };
4
+ export function formatRotateShift(instruction, evalContext) {
5
+ const operand = formatBitOperand(instruction.operand, evalContext);
6
+ if (operand === undefined) {
7
+ return undefined;
8
+ }
9
+ const parts = [operand];
10
+ if (instruction.destination) {
11
+ parts.push(instruction.destination.register);
12
+ }
13
+ return { text: `${instruction.mnemonic} ${parts.join(', ')}` };
14
+ }
15
+ export function formatBitOp(instruction, evalContext) {
16
+ const bit = formatLoweredNumber(instruction.bit, 'byte');
17
+ const operand = formatBitOperand(instruction.operand, evalContext);
18
+ if (operand === undefined) {
19
+ return undefined;
20
+ }
21
+ const parts = [bit, operand];
22
+ if (instruction.destination) {
23
+ parts.push(instruction.destination.register);
24
+ }
25
+ return { text: `${instruction.mnemonic} ${parts.join(', ')}` };
26
+ }
27
+ function formatBitOperand(operand, evalContext) {
28
+ if (operand.kind === 'reg8') {
29
+ return operand.register;
30
+ }
31
+ if (operand.kind === 'reg-indirect' && operand.register === 'hl') {
32
+ return '(HL)';
33
+ }
34
+ if (operand.kind === 'indexed') {
35
+ return formatIndexedMemory(operand.register, operand.displacement, evalContext);
36
+ }
37
+ return undefined;
38
+ }
@@ -0,0 +1,5 @@
1
+ import type { Z80Instruction } from '../z80/instruction.js';
2
+ import { type LoweredEvalContext } from './asm80-expressions.js';
3
+ export declare function formatInstruction(instruction: Z80Instruction, evalContext: LoweredEvalContext): {
4
+ readonly text: string;
5
+ } | undefined;