@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
@@ -1,13 +1,13 @@
1
1
  import { extractLineComment, stripLineComment } from '../source/strip-line-comment.js';
2
2
  import { normalizeDirectiveAlias } from './directive-aliases.js';
3
- import { parseExpression, parseTypeExpr } from './parse-expression.js';
3
+ import { parseColonDeclaration, parseDirectiveStatement } from './parse-directive-statement.js';
4
4
  import { parseZ80Instruction } from '../z80/parse-instruction.js';
5
5
  export function parseLogicalLine(line, options = {}) {
6
6
  const text = normalizeDirectiveAlias(stripLineComment(line.text), options.directiveAliasPolicy).trim();
7
7
  if (text.length === 0) {
8
8
  return commentOnlyLine(line);
9
9
  }
10
- const span = { sourceName: line.sourceName, line: line.line, column: firstColumn(line.text) };
10
+ const span = spanForLine(line);
11
11
  const labelWithStatement = /^(@?[A-Za-z_.$?][A-Za-z0-9_.$?]*):\s*(.+)$/.exec(text);
12
12
  if (labelWithStatement) {
13
13
  const rawLabel = labelWithStatement[1] ?? '';
@@ -20,7 +20,10 @@ export function parseLogicalLine(line, options = {}) {
20
20
  }
21
21
  const parsedStatement = parseCanonicalStatement(line, statementText, span);
22
22
  return withLineComment(line, {
23
- items: [{ kind: 'label', name: labelName, ...(isEntry ? { isEntry: true } : {}), span }, ...parsedStatement.items],
23
+ items: [
24
+ { kind: 'label', name: labelName, ...(isEntry ? { isEntry: true } : {}), span },
25
+ ...parsedStatement.items,
26
+ ],
24
27
  diagnostics: parsedStatement.diagnostics,
25
28
  });
26
29
  }
@@ -56,6 +59,8 @@ function commentOnlyLine(line) {
56
59
  sourceName: line.sourceName,
57
60
  line: line.line,
58
61
  column: firstColumn(line.text),
62
+ ...(line.sourceUnit !== undefined ? { sourceUnit: line.sourceUnit } : {}),
63
+ ...(line.sourceRelation !== undefined ? { sourceRelation: line.sourceRelation } : {}),
59
64
  },
60
65
  },
61
66
  ],
@@ -78,155 +83,18 @@ function withLineComment(line, result) {
78
83
  sourceName: line.sourceName,
79
84
  line: line.line,
80
85
  column: firstColumn(line.text),
86
+ ...(line.sourceUnit !== undefined ? { sourceUnit: line.sourceUnit } : {}),
87
+ ...(line.sourceRelation !== undefined ? { sourceRelation: line.sourceRelation } : {}),
81
88
  },
82
89
  },
83
90
  ],
84
91
  diagnostics: result.diagnostics,
85
92
  };
86
93
  }
87
- function parseEquItem(line, name, expressionText, span) {
88
- const stringValue = parseWholeQuotedString(expressionText.trim());
89
- const expression = stringValue !== undefined && stringValue.length > 1
90
- ? { kind: 'number', value: 0 }
91
- : parseExpression(expressionText);
92
- if (!expression) {
93
- return {
94
- items: [],
95
- diagnostics: [parseError(line, `invalid .equ expression: ${expressionText}`)],
96
- };
97
- }
98
- return {
99
- items: [
100
- {
101
- kind: 'equ',
102
- name,
103
- expression,
104
- ...(stringValue !== undefined && stringValue.length > 1 ? { stringValue } : {}),
105
- span,
106
- },
107
- ],
108
- diagnostics: [],
109
- };
110
- }
111
94
  function parseCanonicalStatement(line, text, span) {
112
- const equ = /^([A-Za-z_.$?][A-Za-z0-9_.$?]*)\s+\.equ\s+(.+)$/.exec(text);
113
- if (equ) {
114
- return parseEquItem(line, equ[1] ?? '', equ[2] ?? '', span);
115
- }
116
- const org = /^\.org\s+(.+)$/.exec(text);
117
- if (org) {
118
- const expressionText = org[1] ?? '';
119
- const expression = parseExpression(expressionText);
120
- if (!expression) {
121
- return {
122
- items: [],
123
- diagnostics: [parseError(line, `invalid .org expression: ${expressionText}`)],
124
- };
125
- }
126
- return { items: [{ kind: 'org', expression, span }], diagnostics: [] };
127
- }
128
- const enumDecl = /^enum\s+([A-Za-z_][A-Za-z0-9_]*)\s+(.+)$/.exec(text);
129
- if (enumDecl) {
130
- return {
131
- items: [],
132
- diagnostics: [parseError(line, `Use "${enumDecl[1] ?? ''} .enum ..." for enums.`)],
133
- };
134
- }
135
- const nameLeftEnum = /^([A-Za-z_][A-Za-z0-9_]*)\s+\.enum\s+(.+)$/.exec(text);
136
- if (nameLeftEnum) {
137
- return parseEnumItem(line, nameLeftEnum[1] ?? '', nameLeftEnum[2] ?? '', span);
138
- }
139
- const data = /^(\.db|\.dw)\s+(.+)$/.exec(text);
140
- if (data) {
141
- const directive = (data[1] ?? '').slice(1).toLowerCase();
142
- const valueText = data[2] ?? '';
143
- const parts = splitValueList(valueText);
144
- const values = directive === 'db'
145
- ? parts.map(parseDataValue).filter((value) => value !== undefined)
146
- : parts.map(parseExpression).filter((value) => value !== undefined);
147
- if (values.length !== parts.length) {
148
- return {
149
- items: [],
150
- diagnostics: [parseError(line, `invalid .${directive} value list`)],
151
- };
152
- }
153
- return {
154
- items: directive === 'db'
155
- ? [{ kind: 'db', values: values, span }]
156
- : [{ kind: 'dw', values: values, span }],
157
- diagnostics: [],
158
- };
159
- }
160
- const ds = /^\.ds\s+(.+)$/.exec(text);
161
- if (ds) {
162
- const parts = splitValueList(ds[1] ?? '');
163
- if (parts.length < 1 || parts.length > 2) {
164
- return {
165
- items: [],
166
- diagnostics: [parseError(line, `invalid .ds value list`)],
167
- };
168
- }
169
- const sizeText = parts[0] ?? '';
170
- const size = parseTypeSizeExpression(sizeText) ?? parseExpression(sizeText);
171
- if (!size) {
172
- return {
173
- items: [],
174
- diagnostics: [parseError(line, `invalid .ds size: ${sizeText}`)],
175
- };
176
- }
177
- const fillText = parts[1];
178
- const fill = fillText === undefined ? undefined : parseExpression(fillText);
179
- if (fillText !== undefined && !fill) {
180
- return {
181
- items: [],
182
- diagnostics: [parseError(line, `invalid .ds fill: ${fillText}`)],
183
- };
184
- }
185
- return {
186
- items: [fill === undefined ? { kind: 'ds', size, span } : { kind: 'ds', size, fill, span }],
187
- diagnostics: [],
188
- };
189
- }
190
- const align = /^\.align\s+(.+)$/.exec(text);
191
- if (align) {
192
- const expressionText = align[1] ?? '';
193
- const alignment = parseExpression(expressionText);
194
- if (!alignment) {
195
- return {
196
- items: [],
197
- diagnostics: [parseError(line, `invalid .align expression: ${expressionText}`)],
198
- };
199
- }
200
- return { items: [{ kind: 'align', alignment, span }], diagnostics: [] };
201
- }
202
- if (/^\.end\s*$/.test(text)) {
203
- return { items: [{ kind: 'end', span }], diagnostics: [] };
204
- }
205
- const rangeControl = /^(\.binfrom|\.binto)\s+(.+)$/.exec(text);
206
- if (rangeControl) {
207
- const kind = (rangeControl[1] ?? '').slice(1).toLowerCase();
208
- const expressionText = rangeControl[2] ?? '';
209
- const expression = parseExpression(expressionText);
210
- if (!expression) {
211
- return {
212
- items: [],
213
- diagnostics: [parseError(line, `invalid .${kind} expression: ${expressionText}`)],
214
- };
215
- }
216
- return { items: [{ kind, expression, span }], diagnostics: [] };
217
- }
218
- const stringData = /^(\.cstr|\.pstr|\.istr)\s+(.+)$/.exec(text);
219
- if (stringData) {
220
- const directive = (stringData[1] ?? '').slice(1).toLowerCase();
221
- const valueText = stringData[2] ?? '';
222
- const value = parseQuotedString(valueText);
223
- if (value === undefined) {
224
- return {
225
- items: [],
226
- diagnostics: [parseError(line, `.${directive} expects one double-quoted string`)],
227
- };
228
- }
229
- return { items: [{ kind: 'string-data', directive, value, span }], diagnostics: [] };
95
+ const directive = parseDirectiveStatement(line, text, span);
96
+ if (directive) {
97
+ return directive;
230
98
  }
231
99
  const instruction = parseZ80Instruction(text);
232
100
  if (instruction?.instruction) {
@@ -246,134 +114,14 @@ function parseCanonicalStatement(line, text, span) {
246
114
  }
247
115
  return { items: [], diagnostics: [parseError(line, `unsupported source line: ${text}`)] };
248
116
  }
249
- function parseColonDeclaration(line, name, statementText, span) {
250
- const equ = /^\.equ\s+(.+)$/.exec(statementText);
251
- if (equ) {
252
- return parseEquItem(line, name, equ[1] ?? '', span);
253
- }
254
- const enumDecl = /^\.enum\s+(.+)$/.exec(statementText);
255
- if (enumDecl) {
256
- return parseEnumItem(line, name, enumDecl[1] ?? '', span);
257
- }
258
- return undefined;
259
- }
260
- function parseEnumItem(line, name, membersText, span) {
261
- const rawMembers = membersText.split(',').map((member) => member.trim());
262
- if (membersText.trim().length === 0 || rawMembers.some((member) => member.length === 0)) {
263
- return {
264
- items: [],
265
- diagnostics: [parseError(line, `invalid enum member list`)],
266
- };
267
- }
268
- const members = [];
269
- const diagnostics = [];
270
- for (const member of rawMembers) {
271
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(member)) {
272
- diagnostics.push(parseError(line, `Invalid enum member name "${member}": expected <identifier>.`));
273
- continue;
274
- }
275
- members.push(member);
276
- }
277
- if (diagnostics.length > 0) {
278
- return { items: [], diagnostics };
279
- }
280
- return { items: [{ kind: 'enum', name, members, span }], diagnostics: [] };
281
- }
282
- function parseTypeSizeExpression(text) {
283
- const typeExpr = parseTypeExpr(text);
284
- return typeExpr ? { kind: 'type-size', typeExpr } : undefined;
285
- }
286
- function splitValueList(text) {
287
- const values = [];
288
- let quote;
289
- let escaped = false;
290
- let parenDepth = 0;
291
- let start = 0;
292
- for (let index = 0; index < text.length; index += 1) {
293
- const char = text[index];
294
- if (escaped) {
295
- escaped = false;
296
- continue;
297
- }
298
- if (char === '\\' && quote) {
299
- escaped = true;
300
- continue;
301
- }
302
- if (char === '"' || char === "'") {
303
- quote = quote === char ? undefined : (quote ?? char);
304
- continue;
305
- }
306
- if (!quote && char === '(') {
307
- parenDepth += 1;
308
- continue;
309
- }
310
- if (!quote && char === ')') {
311
- parenDepth = Math.max(0, parenDepth - 1);
312
- continue;
313
- }
314
- if (char === ',' && !quote && parenDepth === 0) {
315
- values.push(text.slice(start, index));
316
- start = index + 1;
317
- }
318
- }
319
- values.push(text.slice(start));
320
- return values;
321
- }
322
- function parseQuotedString(text) {
323
- const input = text.trim();
324
- const quote = input[0];
325
- if (quote !== '"' || input[input.length - 1] !== quote) {
326
- return undefined;
327
- }
328
- let value = '';
329
- for (let index = 1; index < input.length - 1; index += 1) {
330
- const char = input[index] ?? '';
331
- if (char === '\\') {
332
- if (index + 1 >= input.length - 1) {
333
- return undefined;
334
- }
335
- value += input[index + 1] ?? '';
336
- index += 1;
337
- continue;
338
- }
339
- if (char === quote) {
340
- return undefined;
341
- }
342
- value += char;
343
- }
344
- return value;
345
- }
346
- function parseDataValue(text) {
347
- const expression = parseExpression(text);
348
- if (expression) {
349
- return expression;
350
- }
351
- const value = parseWholeQuotedString(text);
352
- return value === undefined ? undefined : { kind: 'string-fragment', value };
353
- }
354
- function parseWholeQuotedString(text) {
355
- const input = text.trim();
356
- const quote = input[0];
357
- if ((quote !== '"' && quote !== "'") || input[input.length - 1] !== quote) {
358
- return undefined;
359
- }
360
- let value = '';
361
- for (let index = 1; index < input.length - 1; index += 1) {
362
- const char = input[index] ?? '';
363
- if (char === '\\') {
364
- if (index + 1 >= input.length - 1) {
365
- return undefined;
366
- }
367
- value += input[index + 1] ?? '';
368
- index += 1;
369
- continue;
370
- }
371
- if (char === quote) {
372
- return undefined;
373
- }
374
- value += char;
375
- }
376
- return value;
117
+ function spanForLine(line) {
118
+ return {
119
+ sourceName: line.sourceName,
120
+ line: line.line,
121
+ column: firstColumn(line.text),
122
+ ...(line.sourceUnit !== undefined ? { sourceUnit: line.sourceUnit } : {}),
123
+ ...(line.sourceRelation !== undefined ? { sourceRelation: line.sourceRelation } : {}),
124
+ };
377
125
  }
378
126
  function normalizeEntryLabelName(raw) {
379
127
  return raw.startsWith('@') ? raw.slice(1) : raw;
@@ -0,0 +1,3 @@
1
+ import type { Expression } from '../model/expression.js';
2
+ import type { Token } from './expression-tokenizer.js';
3
+ export declare function parseTokenExpression(tokenList: readonly Token[]): Expression | undefined;
@@ -0,0 +1,133 @@
1
+ const PRECEDENCE = new Map([
2
+ ['|', 1],
3
+ ['^', 2],
4
+ ['&', 3],
5
+ ['<<', 4],
6
+ ['>>', 4],
7
+ ['+', 5],
8
+ ['-', 5],
9
+ ['*', 6],
10
+ ['/', 6],
11
+ ['%', 6],
12
+ ]);
13
+ export function parseTokenExpression(tokenList) {
14
+ let index = 0;
15
+ function consume() {
16
+ const token = tokenList[index];
17
+ if (!token) {
18
+ throw new Error('parseExpression consumed past end of token list');
19
+ }
20
+ index += 1;
21
+ return token;
22
+ }
23
+ function parsePrimary() {
24
+ const token = tokenList[index];
25
+ if (!token)
26
+ return undefined;
27
+ if (token.kind === 'number')
28
+ return parseNumberToken(consume());
29
+ if (token.kind === 'expression')
30
+ return parseNestedExpressionToken(consume());
31
+ if (token.kind === 'symbol')
32
+ return parseSymbolPrimary();
33
+ if (token.kind === 'current-location')
34
+ return parseCurrentLocationPrimary();
35
+ if (token.kind === 'operator' && isUnaryOperator(token.text)) {
36
+ return parseUnaryPrimary(token.text);
37
+ }
38
+ if (token.kind === 'left-paren')
39
+ return parseParenthesizedPrimary();
40
+ return undefined;
41
+ }
42
+ function parseNumberToken(token) {
43
+ return token.kind === 'number' ? { kind: 'number', value: token.value } : undefined;
44
+ }
45
+ function parseNestedExpressionToken(token) {
46
+ return token.kind === 'expression' ? token.expression : undefined;
47
+ }
48
+ function parseCurrentLocationPrimary() {
49
+ consume();
50
+ return { kind: 'current-location' };
51
+ }
52
+ function parseSymbolPrimary() {
53
+ const token = tokenList[index];
54
+ if (token?.kind !== 'symbol')
55
+ return undefined;
56
+ if (token.text === 'sizeof' && tokenList[index + 1]?.kind === 'left-paren') {
57
+ return parseSizeofPrimary();
58
+ }
59
+ if (token.text === 'offset' && tokenList[index + 1]?.kind === 'left-paren') {
60
+ return parseOffsetPrimary();
61
+ }
62
+ index += 1;
63
+ return { kind: 'symbol', name: token.text };
64
+ }
65
+ function parseSizeofPrimary() {
66
+ index += 2;
67
+ const typeName = tokenList[index];
68
+ if (typeName?.kind !== 'symbol' || tokenList[index + 1]?.kind !== 'right-paren') {
69
+ return undefined;
70
+ }
71
+ index += 2;
72
+ return { kind: 'sizeof', typeExpr: { name: typeName.text } };
73
+ }
74
+ function parseOffsetPrimary() {
75
+ index += 2;
76
+ const typeName = tokenList[index];
77
+ const comma = tokenList[index + 1];
78
+ const fieldName = tokenList[index + 2];
79
+ const rightParen = tokenList[index + 3];
80
+ if (typeName?.kind !== 'symbol' ||
81
+ comma?.kind !== 'comma' ||
82
+ fieldName?.kind !== 'symbol' ||
83
+ rightParen?.kind !== 'right-paren') {
84
+ return undefined;
85
+ }
86
+ index += 4;
87
+ return {
88
+ kind: 'offset',
89
+ typeExpr: { name: typeName.text },
90
+ path: [{ kind: 'field', name: fieldName.text }],
91
+ };
92
+ }
93
+ function parseUnaryPrimary(operator) {
94
+ index += 1;
95
+ const expression = parsePrimary();
96
+ return expression ? { kind: 'unary', operator, expression } : undefined;
97
+ }
98
+ function parseParenthesizedPrimary() {
99
+ index += 1;
100
+ const expression = parseBinary(1);
101
+ if (!expression || tokenList[index]?.kind !== 'right-paren')
102
+ return undefined;
103
+ index += 1;
104
+ return expression;
105
+ }
106
+ function parseBinary(minPrecedence) {
107
+ let left = parsePrimary();
108
+ if (!left)
109
+ return undefined;
110
+ while (true) {
111
+ const token = tokenList[index];
112
+ if (!token || token.kind !== 'operator' || !isBinaryOperator(token.text))
113
+ break;
114
+ const precedence = PRECEDENCE.get(token.text) ?? 0;
115
+ if (precedence < minPrecedence)
116
+ break;
117
+ index += 1;
118
+ const right = parseBinary(precedence + 1);
119
+ if (!right)
120
+ return undefined;
121
+ left = { kind: 'binary', operator: token.text, left, right };
122
+ }
123
+ return left;
124
+ }
125
+ const expression = parseBinary(1);
126
+ return expression && index === tokenList.length ? expression : undefined;
127
+ }
128
+ function isUnaryOperator(operator) {
129
+ return operator === '+' || operator === '-' || operator === '~';
130
+ }
131
+ function isBinaryOperator(operator) {
132
+ return PRECEDENCE.has(operator);
133
+ }
@@ -34,7 +34,7 @@ export function analyzeProgramNext(loadedProgram, options = {}) {
34
34
  mode: options.caseStyle ?? 'off',
35
35
  });
36
36
  return {
37
- diagnostics: caseStyleDiagnostics,
37
+ diagnostics: [...assembly.diagnostics, ...caseStyleDiagnostics],
38
38
  env: { symbols: assembly.symbols },
39
39
  };
40
40
  }
@@ -40,29 +40,38 @@ function lintInstructionLine(rawLine, sourceName, line, mode, state, diagnostics
40
40
  }
41
41
  function lintSourceLines(sourceLines, instructionLines, mode, state, diagnostics) {
42
42
  for (const [sourceName, lines] of sourceLines) {
43
- let inOpBody = false;
43
+ const lineState = { inOpBody: false };
44
44
  for (let index = 0; index < lines.length; index += 1) {
45
45
  const line = index + 1;
46
46
  const rawLine = lines[index] ?? '';
47
- const text = stripLeadingLabels(stripLineComment(rawLine)).trim();
48
- if (text.length === 0)
49
- continue;
50
- if (/^op\s+[A-Za-z_][A-Za-z0-9_]*\s*\(/i.test(text)) {
51
- inOpBody = true;
52
- continue;
53
- }
54
- if (inOpBody && /^end\s*$/i.test(text)) {
55
- inOpBody = false;
56
- continue;
57
- }
58
- if (inOpBody ||
59
- instructionLines.has(lineKey(sourceName, line)) ||
60
- isPotentialOpInvocationLine(text)) {
47
+ if (shouldLintCaseStyleLine(rawLine, sourceName, line, instructionLines, lineState)) {
61
48
  lintInstructionLine(rawLine, sourceName, line, mode, state, diagnostics);
62
49
  }
63
50
  }
64
51
  }
65
52
  }
53
+ function shouldLintCaseStyleLine(rawLine, sourceName, line, instructionLines, state) {
54
+ const text = stripLeadingLabels(stripLineComment(rawLine)).trim();
55
+ if (text.length === 0)
56
+ return false;
57
+ if (isOpHeaderLine(text)) {
58
+ state.inOpBody = true;
59
+ return false;
60
+ }
61
+ if (state.inOpBody && isOpEndLine(text)) {
62
+ state.inOpBody = false;
63
+ return false;
64
+ }
65
+ return (state.inOpBody ||
66
+ instructionLines.has(lineKey(sourceName, line)) ||
67
+ isPotentialOpInvocationLine(text));
68
+ }
69
+ function isOpHeaderLine(text) {
70
+ return /^op\s+[A-Za-z_][A-Za-z0-9_]*\s*\(/i.test(text);
71
+ }
72
+ function isOpEndLine(text) {
73
+ return /^end\s*$/i.test(text);
74
+ }
66
75
  function isPotentialOpInvocationLine(text) {
67
76
  if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\s+.*)?$/.test(text))
68
77
  return false;
@@ -86,23 +95,31 @@ function lintToken(mode, state, token, category, sourceName, line, diagnostics)
86
95
  if (!style)
87
96
  return;
88
97
  if (mode === 'consistent') {
89
- if (!state.consistentStyle && (style === 'upper' || style === 'lower')) {
90
- state.consistentStyle = style;
91
- return;
92
- }
93
- const expected = state.consistentStyle;
94
- if (!expected || style === expected)
95
- return;
96
- diagnostics.push({
97
- severity: 'warning',
98
- code: 'AZMN_CASE_STYLE',
99
- message: `Case-style lint: ${category} "${token}" does not match established ${expected}case style under --case-style=consistent.`,
100
- sourceName,
101
- line,
102
- column: 1,
103
- });
98
+ lintConsistentToken(state, style, token, category, sourceName, line, diagnostics);
104
99
  return;
105
100
  }
101
+ if (mode === 'off')
102
+ return;
103
+ lintFixedStyleToken(mode, style, token, category, sourceName, line, diagnostics);
104
+ }
105
+ function lintConsistentToken(state, style, token, category, sourceName, line, diagnostics) {
106
+ if (!state.consistentStyle && (style === 'upper' || style === 'lower')) {
107
+ state.consistentStyle = style;
108
+ return;
109
+ }
110
+ const expected = state.consistentStyle;
111
+ if (!expected || style === expected)
112
+ return;
113
+ diagnostics.push({
114
+ severity: 'warning',
115
+ code: 'AZMN_CASE_STYLE',
116
+ message: `Case-style lint: ${category} "${token}" does not match established ${expected}case style under --case-style=consistent.`,
117
+ sourceName,
118
+ line,
119
+ column: 1,
120
+ });
121
+ }
122
+ function lintFixedStyleToken(mode, style, token, category, sourceName, line, diagnostics) {
106
123
  if (style === mode)
107
124
  return;
108
125
  const expectedText = mode === 'upper' ? 'uppercase' : 'lowercase';
@@ -0,0 +1,38 @@
1
+ import type { ControlEffect, InstructionEffect, RegisterContractsUnit } from '../register-contracts/types.js';
2
+ import type { Z80Instruction, Z80StackRegister16 } from './instruction.js';
3
+ export declare function callControl(target: string | undefined, conditional: boolean): ControlEffect;
4
+ export declare function rstControl(vector: number | undefined): ControlEffect;
5
+ export declare function jumpControl(target: string | undefined, conditional: boolean): ControlEffect;
6
+ export declare function controlEffect(control: ControlEffect, reads?: RegisterContractsUnit[]): InstructionEffect;
7
+ export declare function stackControlEffect(control: ControlEffect, reads?: RegisterContractsUnit[]): InstructionEffect;
8
+ export declare function ldEffect(instruction: Extract<Z80Instruction, {
9
+ mnemonic: 'ld';
10
+ }>): InstructionEffect;
11
+ export declare function incDecEffect(instruction: Extract<Z80Instruction, {
12
+ mnemonic: 'inc' | 'dec';
13
+ }>): InstructionEffect;
14
+ export declare function aluEffect(instruction: Extract<Z80Instruction, {
15
+ mnemonic: 'add' | 'adc' | 'sbc';
16
+ }>): InstructionEffect;
17
+ export declare function aluEffect(instruction: Extract<Z80Instruction, {
18
+ mnemonic: 'sub' | 'and' | 'or' | 'xor' | 'cp' | 'add' | 'adc' | 'sbc';
19
+ }>): InstructionEffect;
20
+ export declare function pushEffect(register: Z80StackRegister16): InstructionEffect;
21
+ export declare function popEffect(register: Z80StackRegister16): InstructionEffect;
22
+ export declare function rotateShiftEffect(instruction: Extract<Z80Instruction, {
23
+ mnemonic: 'rlc' | 'rrc' | 'rl' | 'rr' | 'sla' | 'sra' | 'sll' | 'sls' | 'srl';
24
+ }>): InstructionEffect;
25
+ export declare function bitEffect(instruction: Extract<Z80Instruction, {
26
+ mnemonic: 'bit' | 'res' | 'set';
27
+ }>): InstructionEffect;
28
+ export declare function inEffect(instruction: Extract<Z80Instruction, {
29
+ mnemonic: 'in';
30
+ }>): InstructionEffect;
31
+ export declare function outEffect(instruction: Extract<Z80Instruction, {
32
+ mnemonic: 'out';
33
+ }>): InstructionEffect;
34
+ export declare function blockTransferEffect(): InstructionEffect;
35
+ export declare function exEffect(instruction: Extract<Z80Instruction, {
36
+ mnemonic: 'ex';
37
+ }>): InstructionEffect;
38
+ export declare function accumulatorRotateEffect(mnemonic: 'rlca' | 'rrca' | 'rla' | 'rra'): InstructionEffect;