@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,180 @@
1
+ import { stripLineComment } from '../source/strip-line-comment.js';
2
+ import { parseTypeExpr } from './parse-expression.js';
3
+ export function parseLayoutDeclarationAt(lines, index) {
4
+ const line = lines[index];
5
+ if (line === undefined)
6
+ return undefined;
7
+ const text = stripLineComment(line.text).trim();
8
+ const typeAlias = parseTypeAlias(line, text);
9
+ if (typeAlias !== undefined) {
10
+ return { consumedUntilIndex: index, ...typeAlias };
11
+ }
12
+ const prefixLayoutHeader = /^\.(type|union)\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/.exec(text);
13
+ if (prefixLayoutHeader) {
14
+ const directive = prefixLayoutHeader[1] ?? 'type';
15
+ return {
16
+ consumedUntilIndex: skipToLayoutEnd(lines, index, directive),
17
+ diagnostics: [
18
+ parseDiagnostic(line, `Use "${prefixLayoutHeader[2] ?? ''} .${directive}" for layouts.`),
19
+ ],
20
+ };
21
+ }
22
+ const layoutHeader = parseNameLeftLayoutHeader(text);
23
+ if (layoutHeader === undefined) {
24
+ return undefined;
25
+ }
26
+ return parseLayoutBlock(lines, index, layoutHeader);
27
+ }
28
+ function parseTypeAlias(line, text) {
29
+ const nameLeftTypeAlias = /^([A-Za-z_][A-Za-z0-9_]*)(?::\s*|\s+)\.typealias\s+(.+)$/.exec(text);
30
+ if (nameLeftTypeAlias) {
31
+ const typeExprText = nameLeftTypeAlias[2] ?? '';
32
+ const typeExpr = parseTypeExpr(typeExprText);
33
+ if (!typeExpr) {
34
+ return { diagnostics: [parseDiagnostic(line, `invalid .typealias target: ${typeExprText}`)] };
35
+ }
36
+ return {
37
+ item: {
38
+ kind: 'type-alias',
39
+ name: nameLeftTypeAlias[1] ?? '',
40
+ typeExpr,
41
+ span: { sourceName: line.sourceName, line: line.line, column: firstColumn(line.text) },
42
+ },
43
+ diagnostics: [],
44
+ };
45
+ }
46
+ const oldTypeAlias = /^\.type\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+)$/.exec(text);
47
+ if (oldTypeAlias) {
48
+ return {
49
+ diagnostics: [
50
+ parseDiagnostic(line, `Use "${oldTypeAlias[1] ?? ''} .typealias ..." for type aliases.`),
51
+ ],
52
+ };
53
+ }
54
+ return undefined;
55
+ }
56
+ function parseNameLeftLayoutHeader(text) {
57
+ const match = /^([A-Za-z_][A-Za-z0-9_]*)(?::\s*|\s+)\.(type|union)\s*$/.exec(text);
58
+ return match
59
+ ? {
60
+ directive: match[2] ?? '',
61
+ name: match[1] ?? '',
62
+ }
63
+ : undefined;
64
+ }
65
+ function parseLayoutBlock(lines, index, header) {
66
+ const line = lines[index];
67
+ const layoutKind = header.directive === 'union' ? 'union' : 'record';
68
+ const endDirective = layoutKind === 'union' ? '.endunion' : '.endtype';
69
+ const fields = [];
70
+ const diagnostics = [];
71
+ let consumedUntilIndex = index;
72
+ let terminated = false;
73
+ for (let fieldIndex = index + 1; fieldIndex < lines.length; fieldIndex += 1) {
74
+ consumedUntilIndex = fieldIndex;
75
+ const fieldLine = lines[fieldIndex];
76
+ const fieldText = stripLineComment(fieldLine.text).trim();
77
+ if (fieldText.length === 0) {
78
+ continue;
79
+ }
80
+ if (fieldText === endDirective) {
81
+ terminated = true;
82
+ break;
83
+ }
84
+ const field = parseLayoutField(fieldText);
85
+ if (!field) {
86
+ diagnostics.push(parseDiagnostic(fieldLine, `invalid .${header.directive} field declaration`));
87
+ continue;
88
+ }
89
+ fields.push(field);
90
+ }
91
+ if (!terminated) {
92
+ diagnostics.push(parseDiagnostic(line, `.${header.directive} ${header.name} missing ${endDirective}`));
93
+ }
94
+ return {
95
+ consumedUntilIndex,
96
+ diagnostics,
97
+ item: {
98
+ kind: 'type',
99
+ name: header.name,
100
+ layoutKind,
101
+ fields,
102
+ span: { sourceName: line.sourceName, line: line.line, column: firstColumn(line.text) },
103
+ },
104
+ };
105
+ }
106
+ function skipToLayoutEnd(lines, index, directive) {
107
+ const endDirective = directive === 'union' ? '.endunion' : '.endtype';
108
+ for (let next = index + 1; next < lines.length; next += 1) {
109
+ if (stripLineComment(lines[next].text).trim() === endDirective) {
110
+ return next;
111
+ }
112
+ }
113
+ return index;
114
+ }
115
+ function parseLayoutField(text) {
116
+ const match = /^([A-Za-z_][A-Za-z0-9_]*)\s+(\.(?:field|byte|word|addr))(?:\s+(.+))?$/.exec(text);
117
+ if (!match) {
118
+ return undefined;
119
+ }
120
+ const name = match[1] ?? '';
121
+ const directive = (match[2] ?? '').toLowerCase();
122
+ const operand = match[3]?.trim();
123
+ return directive === '.field'
124
+ ? parseNamedField(name, operand)
125
+ : parseScalarDirectiveField(name, directive, operand);
126
+ }
127
+ function parseNamedField(name, operand) {
128
+ return operand === undefined ? undefined : parseFieldOperand(name, operand);
129
+ }
130
+ function parseScalarDirectiveField(name, directive, operand) {
131
+ if (operand !== undefined) {
132
+ return undefined;
133
+ }
134
+ const size = scalarDirectiveSize(directive);
135
+ return size === undefined ? undefined : { name, size };
136
+ }
137
+ function scalarDirectiveSize(directive) {
138
+ if (directive === '.byte')
139
+ return 1;
140
+ if (directive === '.word' || directive === '.addr')
141
+ return 2;
142
+ return undefined;
143
+ }
144
+ function parseFieldOperand(name, operand) {
145
+ const size = /^[0-9]+$/.test(operand) ? Number.parseInt(operand, 10) : undefined;
146
+ if (size !== undefined) {
147
+ return size > 0 ? { name, size } : undefined;
148
+ }
149
+ const scalar = scalarFieldSize(operand);
150
+ if (scalar !== undefined) {
151
+ return { name, size: scalar };
152
+ }
153
+ const typeExpr = parseTypeExpr(operand);
154
+ return typeExpr ? { name, size: 0, typeExpr } : undefined;
155
+ }
156
+ function scalarFieldSize(typeName) {
157
+ switch (typeName.toLowerCase()) {
158
+ case 'byte':
159
+ return 1;
160
+ case 'word':
161
+ case 'addr':
162
+ return 2;
163
+ default:
164
+ return undefined;
165
+ }
166
+ }
167
+ function parseDiagnostic(line, message) {
168
+ return {
169
+ severity: 'error',
170
+ code: 'AZMN_PARSE',
171
+ message,
172
+ sourceName: line.sourceName,
173
+ line: line.line,
174
+ column: firstColumn(line.text),
175
+ };
176
+ }
177
+ function firstColumn(text) {
178
+ const match = /\S/.exec(text);
179
+ return match ? match.index + 1 : 1;
180
+ }
@@ -0,0 +1,5 @@
1
+ import type { Expression, TypeExpr } from '../model/expression.js';
2
+ export type ParseNestedExpression = (text: string) => Expression | undefined;
3
+ export declare function parseTypeExpr(text: string): TypeExpr | undefined;
4
+ export declare function parseLayoutExpression(text: string, parseNestedExpression: ParseNestedExpression): Expression | undefined;
5
+ export declare function findMatchingBracket(text: string): number | undefined;
@@ -0,0 +1,175 @@
1
+ export function parseTypeExpr(text) {
2
+ const trimmed = text.trim();
3
+ const match = /^([A-Za-z_][A-Za-z0-9_]*)(?:\[\s*([0-9]+)\s*\])?$/.exec(trimmed);
4
+ if (!match) {
5
+ return undefined;
6
+ }
7
+ const name = match[1] ?? '';
8
+ const lengthText = match[2];
9
+ if (lengthText === undefined) {
10
+ return { name };
11
+ }
12
+ const length = Number.parseInt(lengthText, 10);
13
+ return length >= 0 ? { name, length } : undefined;
14
+ }
15
+ export function parseLayoutExpression(text, parseNestedExpression) {
16
+ const trimmed = text.trim();
17
+ const layoutCast = parseLayoutCast(trimmed, parseNestedExpression);
18
+ if (layoutCast) {
19
+ return layoutCast;
20
+ }
21
+ const sizeof = /^sizeof\s*\((.*)\)$/.exec(trimmed);
22
+ if (sizeof) {
23
+ const typeExpr = parseTypeExpr(sizeof[1] ?? '');
24
+ return typeExpr ? { kind: 'sizeof', typeExpr } : undefined;
25
+ }
26
+ const offset = /^offset\s*\((.*),(.*)\)$/.exec(trimmed);
27
+ if (offset) {
28
+ const typeExpr = parseTypeExpr(offset[1] ?? '');
29
+ const path = parseOffsetPath(offset[2] ?? '');
30
+ return typeExpr && path ? { kind: 'offset', typeExpr, path } : undefined;
31
+ }
32
+ return undefined;
33
+ }
34
+ export function findMatchingBracket(text) {
35
+ let state = { depth: 0, quote: undefined, escaped: false };
36
+ for (let index = 0; index < text.length; index += 1) {
37
+ const char = text[index] ?? '';
38
+ if (state.quote !== undefined) {
39
+ state = scanQuotedBracketChar(char, state);
40
+ continue;
41
+ }
42
+ const quotedState = startBracketQuote(char, state);
43
+ if (quotedState) {
44
+ state = quotedState;
45
+ continue;
46
+ }
47
+ state = scanBracketDepth(char, state);
48
+ if (char === ']') {
49
+ if (state.depth === 0) {
50
+ return index;
51
+ }
52
+ }
53
+ }
54
+ return undefined;
55
+ }
56
+ function scanQuotedBracketChar(char, state) {
57
+ if (state.escaped)
58
+ return { ...state, escaped: false };
59
+ if (char === '\\')
60
+ return { ...state, escaped: true };
61
+ if (char === state.quote)
62
+ return { ...state, quote: undefined };
63
+ return state;
64
+ }
65
+ function startBracketQuote(char, state) {
66
+ return char === '"' || char === "'" ? { ...state, quote: char } : undefined;
67
+ }
68
+ function scanBracketDepth(char, state) {
69
+ if (char === '[')
70
+ return { ...state, depth: state.depth + 1 };
71
+ if (char === ']')
72
+ return { ...state, depth: state.depth - 1 };
73
+ return state;
74
+ }
75
+ function parseLayoutCast(text, parseNestedExpression) {
76
+ if (!text.startsWith('<')) {
77
+ return undefined;
78
+ }
79
+ const close = text.indexOf('>');
80
+ if (close <= 1) {
81
+ return undefined;
82
+ }
83
+ const typeExpr = parseTypeExpr(text.slice(1, close));
84
+ if (!typeExpr) {
85
+ return undefined;
86
+ }
87
+ const rest = text.slice(close + 1);
88
+ const base = /^(?:[A-Za-z_$][A-Za-z0-9_$?]*|\?[A-Za-z0-9_$?]+)/.exec(rest);
89
+ if (!base) {
90
+ return undefined;
91
+ }
92
+ const path = parseLayoutCastPath(rest.slice(base[0].length), parseNestedExpression);
93
+ if (!path) {
94
+ return undefined;
95
+ }
96
+ return {
97
+ kind: 'layout-cast',
98
+ typeExpr,
99
+ base: { kind: 'symbol', name: base[0] },
100
+ path,
101
+ };
102
+ }
103
+ function parseLayoutCastPath(text, parseNestedExpression) {
104
+ const parts = [];
105
+ let rest = text.trim();
106
+ while (rest.length > 0) {
107
+ const parsed = parseLayoutCastPathPart(rest, parseNestedExpression);
108
+ if (!parsed)
109
+ return undefined;
110
+ parts.push(parsed.part);
111
+ rest = parsed.rest.trim();
112
+ }
113
+ return parts.length > 0 ? parts : undefined;
114
+ }
115
+ function parseLayoutCastPathPart(text, parseNestedExpression) {
116
+ return text.startsWith('.')
117
+ ? parseLayoutCastField(text)
118
+ : parseLayoutCastIndex(text, parseNestedExpression);
119
+ }
120
+ function parseLayoutCastField(text) {
121
+ const field = /^\.([A-Za-z_][A-Za-z0-9_]*)/.exec(text);
122
+ return field ? { part: { kind: 'field', name: field[1] ?? '' }, rest: text.slice(field[0].length) } : undefined;
123
+ }
124
+ function parseLayoutCastIndex(text, parseNestedExpression) {
125
+ if (!text.startsWith('['))
126
+ return undefined;
127
+ const close = findMatchingBracket(text);
128
+ if (close === undefined)
129
+ return undefined;
130
+ const expression = parseNestedExpression(text.slice(1, close));
131
+ return expression
132
+ ? { part: { kind: 'index', expression }, rest: text.slice(close + 1) }
133
+ : undefined;
134
+ }
135
+ function parseOffsetPath(text) {
136
+ const trimmed = text.trim();
137
+ if (trimmed.length === 0) {
138
+ return undefined;
139
+ }
140
+ const parts = [];
141
+ let rest = trimmed;
142
+ while (rest.length > 0) {
143
+ const parsed = parseOffsetPathPart(rest);
144
+ if (!parsed)
145
+ return undefined;
146
+ parts.push(parsed.part);
147
+ rest = parsed.rest;
148
+ if (rest.length === 0) {
149
+ break;
150
+ }
151
+ if (!rest.startsWith('.')) {
152
+ return undefined;
153
+ }
154
+ rest = rest.slice(1);
155
+ }
156
+ return parts.length > 0 ? parts : undefined;
157
+ }
158
+ function parseOffsetPathPart(text) {
159
+ return text.startsWith('[') ? parseOffsetIndex(text) : parseOffsetField(text);
160
+ }
161
+ function parseOffsetIndex(text) {
162
+ const index = /^\[\s*([0-9]+)\s*\]/.exec(text);
163
+ return index
164
+ ? {
165
+ part: { kind: 'index', index: Number.parseInt(index[1] ?? '', 10) },
166
+ rest: text.slice(index[0].length),
167
+ }
168
+ : undefined;
169
+ }
170
+ function parseOffsetField(text) {
171
+ const field = /^[A-Za-z_][A-Za-z0-9_]*/.exec(text);
172
+ return field
173
+ ? { part: { kind: 'field', name: field[0] }, rest: text.slice(field[0].length) }
174
+ : undefined;
175
+ }
@@ -1,6 +1,6 @@
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();
@@ -84,149 +84,10 @@ function withLineComment(line, result) {
84
84
  diagnostics: result.diagnostics,
85
85
  };
86
86
  }
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
87
  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: [] };
88
+ const directive = parseDirectiveStatement(line, text, span);
89
+ if (directive) {
90
+ return directive;
230
91
  }
231
92
  const instruction = parseZ80Instruction(text);
232
93
  if (instruction?.instruction) {
@@ -246,135 +107,6 @@ function parseCanonicalStatement(line, text, span) {
246
107
  }
247
108
  return { items: [], diagnostics: [parseError(line, `unsupported source line: ${text}`)] };
248
109
  }
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;
377
- }
378
110
  function normalizeEntryLabelName(raw) {
379
111
  return raw.startsWith('@') ? raw.slice(1) : raw;
380
112
  }
@@ -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;