@jhlagado/azm 0.2.9 → 0.2.11

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 (40) hide show
  1. package/README.md +21 -9
  2. package/dist/src/core/compile.js +97 -1
  3. package/dist/src/expansion/op-expansion.js +47 -9
  4. package/dist/src/outputs/d8-files.js +1 -0
  5. package/dist/src/outputs/types.d.ts +1 -0
  6. package/dist/src/register-contracts/report.js +15 -3
  7. package/dist/src/register-contracts/smartCommentParsing.d.ts +1 -0
  8. package/dist/src/register-contracts/smartCommentParsing.js +42 -7
  9. package/dist/src/register-contracts/smartComments.d.ts +2 -2
  10. package/dist/src/register-contracts/smartComments.js +3 -4
  11. package/dist/src/source/instruction-chain.d.ts +5 -0
  12. package/dist/src/source/instruction-chain.js +75 -0
  13. package/dist/src/syntax/names.d.ts +18 -0
  14. package/dist/src/syntax/names.js +44 -0
  15. package/dist/src/syntax/parse-data-directives.d.ts +17 -0
  16. package/dist/src/syntax/parse-data-directives.js +147 -0
  17. package/dist/src/syntax/parse-declaration-directives.d.ts +18 -0
  18. package/dist/src/syntax/parse-declaration-directives.js +90 -0
  19. package/dist/src/syntax/parse-diagnostics.d.ts +8 -0
  20. package/dist/src/syntax/parse-diagnostics.js +15 -0
  21. package/dist/src/syntax/parse-directive-statement.d.ts +1 -5
  22. package/dist/src/syntax/parse-directive-statement.js +19 -259
  23. package/dist/src/syntax/parse-instruction-chain.d.ts +22 -0
  24. package/dist/src/syntax/parse-instruction-chain.js +62 -0
  25. package/dist/src/syntax/parse-layout-declarations.js +9 -18
  26. package/dist/src/syntax/parse-layout-expression.js +4 -3
  27. package/dist/src/syntax/parse-line.js +20 -31
  28. package/dist/src/syntax/parse-location-directives.d.ts +7 -0
  29. package/dist/src/syntax/parse-location-directives.js +15 -0
  30. package/dist/src/syntax/statement-classification.d.ts +2 -0
  31. package/dist/src/syntax/statement-classification.js +24 -0
  32. package/dist/src/tooling/case-style.js +42 -26
  33. package/docs/codebase/02-source-loading-and-parsing.md +28 -8
  34. package/docs/codebase/04-ops-and-register-contracts.md +24 -3
  35. package/docs/codebase/05-interfaces-and-output-artifacts.md +10 -0
  36. package/docs/codebase/06-verification-and-maintenance.md +9 -3
  37. package/docs/codebase/appendices/a-directory-file-reference.md +17 -10
  38. package/docs/codebase/appendices/b-compile-flow-reference.md +3 -2
  39. package/docs/codebase/index.md +4 -0
  40. package/package.json +1 -1
@@ -0,0 +1,147 @@
1
+ import { parseWholeQuotedString } from './parse-declaration-directives.js';
2
+ import { parseLineError } from './parse-diagnostics.js';
3
+ import { parseExpression, parseTypeExpr } from './parse-expression.js';
4
+ export function parseDataDirective(line, directiveText, valueText, span) {
5
+ const directive = directiveText.slice(1).toLowerCase();
6
+ const parts = splitValueList(valueText);
7
+ const values = directive === 'db'
8
+ ? parts.map(parseDataValue).filter((value) => value !== undefined)
9
+ : parts.map(parseExpression).filter((value) => value !== undefined);
10
+ if (values.length !== parts.length) {
11
+ return {
12
+ items: [],
13
+ diagnostics: [parseLineError(line, `invalid .${directive} value list`)],
14
+ };
15
+ }
16
+ return {
17
+ items: directive === 'db'
18
+ ? [{ kind: 'db', values: values, span }]
19
+ : [{ kind: 'dw', values: values, span }],
20
+ diagnostics: [],
21
+ };
22
+ }
23
+ export function parseDsDirective(line, valueText, span) {
24
+ const parts = splitValueList(valueText);
25
+ const listDiagnostic = validateDsValueList(line, parts);
26
+ if (listDiagnostic) {
27
+ return { items: [], diagnostics: [listDiagnostic] };
28
+ }
29
+ const sizeResult = parseDsSize(line, parts[0] ?? '');
30
+ if (sizeResult.diagnostic) {
31
+ return { items: [], diagnostics: [sizeResult.diagnostic] };
32
+ }
33
+ const fillResult = parseDsFill(line, parts[1]);
34
+ if (fillResult.diagnostic) {
35
+ return { items: [], diagnostics: [fillResult.diagnostic] };
36
+ }
37
+ return {
38
+ items: [
39
+ fillResult.fill === undefined
40
+ ? { kind: 'ds', size: sizeResult.size, span }
41
+ : { kind: 'ds', size: sizeResult.size, fill: fillResult.fill, span },
42
+ ],
43
+ diagnostics: [],
44
+ };
45
+ }
46
+ export function parseStringDataDirective(line, directive, valueText, span) {
47
+ const value = parseQuotedString(valueText);
48
+ if (value === undefined) {
49
+ return {
50
+ items: [],
51
+ diagnostics: [parseLineError(line, `.${directive} expects one double-quoted string`)],
52
+ };
53
+ }
54
+ return { items: [{ kind: 'string-data', directive, value, span }], diagnostics: [] };
55
+ }
56
+ function validateDsValueList(line, parts) {
57
+ return parts.length < 1 || parts.length > 2
58
+ ? parseLineError(line, `invalid .ds value list`)
59
+ : undefined;
60
+ }
61
+ function parseDsSize(line, sizeText) {
62
+ const size = parseTypeSizeExpression(sizeText) ?? parseExpression(sizeText);
63
+ if (!size) {
64
+ return {
65
+ diagnostic: parseLineError(line, `invalid .ds size: ${sizeText}`),
66
+ };
67
+ }
68
+ return { size };
69
+ }
70
+ function parseDsFill(line, fillText) {
71
+ if (fillText === undefined)
72
+ return { fill: undefined };
73
+ const fill = parseExpression(fillText);
74
+ if (!fill) {
75
+ return {
76
+ diagnostic: parseLineError(line, `invalid .ds fill: ${fillText}`),
77
+ };
78
+ }
79
+ return { fill };
80
+ }
81
+ function parseTypeSizeExpression(text) {
82
+ const typeExpr = parseTypeExpr(text);
83
+ return typeExpr ? { kind: 'type-size', typeExpr } : undefined;
84
+ }
85
+ function splitValueList(text) {
86
+ const values = [];
87
+ let state = { quote: undefined, escaped: false, parenDepth: 0 };
88
+ let start = 0;
89
+ for (let index = 0; index < text.length; index += 1) {
90
+ if (isValueSeparator(text[index] ?? '', state)) {
91
+ values.push(text.slice(start, index));
92
+ start = index + 1;
93
+ continue;
94
+ }
95
+ state = scanValueListChar(text[index] ?? '', state);
96
+ }
97
+ values.push(text.slice(start));
98
+ return values;
99
+ }
100
+ function isValueSeparator(char, state) {
101
+ return char === ',' && state.quote === undefined && state.parenDepth === 0;
102
+ }
103
+ function scanValueListChar(char, state) {
104
+ const escapedState = scanEscapedValueListChar(char, state);
105
+ if (escapedState)
106
+ return escapedState;
107
+ const quotedState = scanQuotedValueListChar(char, state);
108
+ if (quotedState)
109
+ return quotedState;
110
+ return scanParenthesizedValueListChar(char, state);
111
+ }
112
+ function scanEscapedValueListChar(char, state) {
113
+ if (state.escaped)
114
+ return { ...state, escaped: false };
115
+ if (char === '\\' && state.quote !== undefined)
116
+ return { ...state, escaped: true };
117
+ return undefined;
118
+ }
119
+ function scanQuotedValueListChar(char, state) {
120
+ if (char !== '"' && char !== "'")
121
+ return undefined;
122
+ return { ...state, quote: state.quote === char ? undefined : (state.quote ?? char) };
123
+ }
124
+ function scanParenthesizedValueListChar(char, state) {
125
+ if (state.quote !== undefined)
126
+ return state;
127
+ if (char === '(')
128
+ return { ...state, parenDepth: state.parenDepth + 1 };
129
+ if (char === ')')
130
+ return { ...state, parenDepth: Math.max(0, state.parenDepth - 1) };
131
+ return state;
132
+ }
133
+ function parseQuotedString(text) {
134
+ const input = text.trim();
135
+ if (input[0] !== '"' || input[input.length - 1] !== '"') {
136
+ return undefined;
137
+ }
138
+ return parseWholeQuotedString(input);
139
+ }
140
+ function parseDataValue(text) {
141
+ const expression = parseExpression(text);
142
+ if (expression) {
143
+ return expression;
144
+ }
145
+ const value = parseWholeQuotedString(text);
146
+ return value === undefined ? undefined : { kind: 'string-fragment', value };
147
+ }
@@ -0,0 +1,18 @@
1
+ import type { LogicalLine } from '../source/logical-lines.js';
2
+ import type { ParseLineResult } from './parse-line.js';
3
+ export declare function parseColonDeclaration(line: LogicalLine, name: string, statementText: string, span: {
4
+ readonly sourceName: string;
5
+ readonly line: number;
6
+ readonly column: number;
7
+ }): ParseLineResult | undefined;
8
+ export declare function parseEquItem(line: LogicalLine, name: string, expressionText: string, span: {
9
+ readonly sourceName: string;
10
+ readonly line: number;
11
+ readonly column: number;
12
+ }): ParseLineResult;
13
+ export declare function parseEnumItem(line: LogicalLine, name: string, membersText: string, span: {
14
+ readonly sourceName: string;
15
+ readonly line: number;
16
+ readonly column: number;
17
+ }): ParseLineResult;
18
+ export declare function parseWholeQuotedString(text: string): string | undefined;
@@ -0,0 +1,90 @@
1
+ import { isIdentifier } from './names.js';
2
+ import { parseLineError } from './parse-diagnostics.js';
3
+ import { parseExpression } from './parse-expression.js';
4
+ export function parseColonDeclaration(line, name, statementText, span) {
5
+ const equ = /^\.equ\s+(.+)$/.exec(statementText);
6
+ if (equ) {
7
+ return parseEquItem(line, name, equ[1] ?? '', span);
8
+ }
9
+ const enumDecl = /^\.enum\s+(.+)$/.exec(statementText);
10
+ if (enumDecl) {
11
+ return parseEnumItem(line, name, enumDecl[1] ?? '', span);
12
+ }
13
+ return undefined;
14
+ }
15
+ export function parseEquItem(line, name, expressionText, span) {
16
+ const stringValue = parseWholeQuotedString(expressionText.trim());
17
+ const expression = stringValue !== undefined && stringValue.length > 1
18
+ ? { kind: 'number', value: 0 }
19
+ : parseExpression(expressionText);
20
+ if (!expression) {
21
+ return {
22
+ items: [],
23
+ diagnostics: [parseLineError(line, `invalid .equ expression: ${expressionText}`)],
24
+ };
25
+ }
26
+ return {
27
+ items: [
28
+ {
29
+ kind: 'equ',
30
+ name,
31
+ expression,
32
+ ...(stringValue !== undefined && stringValue.length > 1 ? { stringValue } : {}),
33
+ span,
34
+ },
35
+ ],
36
+ diagnostics: [],
37
+ };
38
+ }
39
+ export function parseEnumItem(line, name, membersText, span) {
40
+ const rawMembers = membersText.split(',').map((member) => member.trim());
41
+ if (membersText.trim().length === 0 || rawMembers.some((member) => member.length === 0)) {
42
+ return {
43
+ items: [],
44
+ diagnostics: [parseLineError(line, `invalid enum member list`)],
45
+ };
46
+ }
47
+ const members = [];
48
+ const diagnostics = [];
49
+ for (const member of rawMembers) {
50
+ if (!isIdentifier(member)) {
51
+ diagnostics.push(parseLineError(line, `Invalid enum member name "${member}": expected <identifier>.`));
52
+ continue;
53
+ }
54
+ members.push(member);
55
+ }
56
+ if (diagnostics.length > 0) {
57
+ return { items: [], diagnostics };
58
+ }
59
+ return { items: [{ kind: 'enum', name, members, span }], diagnostics: [] };
60
+ }
61
+ export function parseWholeQuotedString(text) {
62
+ return parseQuotedStringWithQuotes(text, new Set(['"', "'"]));
63
+ }
64
+ function parseQuotedStringWithQuotes(text, allowedQuotes) {
65
+ const input = text.trim();
66
+ const quote = input[0];
67
+ if (!quote || !allowedQuotes.has(quote) || input[input.length - 1] !== quote) {
68
+ return undefined;
69
+ }
70
+ return parseQuotedStringContent(input, quote);
71
+ }
72
+ function parseQuotedStringContent(input, quote) {
73
+ let value = '';
74
+ for (let index = 1; index < input.length - 1; index += 1) {
75
+ const char = input[index] ?? '';
76
+ if (char === '\\') {
77
+ if (index + 1 >= input.length - 1) {
78
+ return undefined;
79
+ }
80
+ value += input[index + 1] ?? '';
81
+ index += 1;
82
+ continue;
83
+ }
84
+ if (char === quote) {
85
+ return undefined;
86
+ }
87
+ value += char;
88
+ }
89
+ return value;
90
+ }
@@ -3,10 +3,18 @@ type ParseDiagLocation = {
3
3
  line: number;
4
4
  column: number;
5
5
  };
6
+ type ParseDiagLine = {
7
+ readonly sourceName: string;
8
+ readonly line: number;
9
+ readonly text: string;
10
+ };
6
11
  /** Push a parse diagnostic with Next default code/severity (`AZMN_PARSE` / error). */
7
12
  export declare function parseDiag(diagnostics: Diagnostic[], sourceName: string, message: string, where?: ParseDiagLocation): void;
8
13
  /** Push a parse diagnostic at an explicit 1-based line/column. */
9
14
  export declare function parseDiagAt(diagnostics: Diagnostic[], sourceName: string, message: string, line: number, column: number): void;
10
15
  /** Push a diagnostic with explicit code, severity, and optional location. */
11
16
  export declare function parseDiagAtWithId(diagnostics: Diagnostic[], sourceName: string, code: DiagnosticId | string, severity: DiagnosticSeverity, message: string, where?: ParseDiagLocation): void;
17
+ /** Return a parse diagnostic at the first non-whitespace column of a source line. */
18
+ export declare function parseLineError(line: ParseDiagLine, message: string): Diagnostic;
19
+ export declare function firstNonWhitespaceColumn(text: string): number;
12
20
  export {};
@@ -16,3 +16,18 @@ export function parseDiagAtWithId(diagnostics, sourceName, code, severity, messa
16
16
  ...(where ? { line: where.line, column: where.column } : {}),
17
17
  });
18
18
  }
19
+ /** Return a parse diagnostic at the first non-whitespace column of a source line. */
20
+ export function parseLineError(line, message) {
21
+ return {
22
+ code: 'AZMN_PARSE',
23
+ severity: 'error',
24
+ message,
25
+ sourceName: line.sourceName,
26
+ line: line.line,
27
+ column: firstNonWhitespaceColumn(line.text),
28
+ };
29
+ }
30
+ export function firstNonWhitespaceColumn(text) {
31
+ const match = /\S/.exec(text);
32
+ return match ? match.index + 1 : 1;
33
+ }
@@ -1,9 +1,5 @@
1
1
  import type { LogicalLine } from '../source/logical-lines.js';
2
2
  import type { SourceSpan } from '../source/source-span.js';
3
3
  import type { ParseLineResult } from './parse-line.js';
4
+ export { parseColonDeclaration } from './parse-declaration-directives.js';
4
5
  export declare function parseDirectiveStatement(line: LogicalLine, text: string, span: SourceSpan): ParseLineResult | undefined;
5
- export declare function parseColonDeclaration(line: LogicalLine, name: string, statementText: string, span: {
6
- readonly sourceName: string;
7
- readonly line: number;
8
- readonly column: number;
9
- }): ParseLineResult | undefined;
@@ -1,7 +1,11 @@
1
- import { parseExpression, parseTypeExpr } from './parse-expression.js';
1
+ import { IDENTIFIER_PATTERN, LABEL_NAME_PATTERN } from './names.js';
2
+ import { parseEnumItem, parseEquItem } from './parse-declaration-directives.js';
3
+ import { firstNonWhitespaceColumn } from './parse-diagnostics.js';
4
+ import { parseDataDirective, parseDsDirective, parseStringDataDirective, } from './parse-data-directives.js';
5
+ import { parseExpressionDirective } from './parse-location-directives.js';
2
6
  const DIRECTIVE_PARSERS = [
3
7
  {
4
- pattern: /^([A-Za-z_.$?][A-Za-z0-9_.$?]*)\s+\.equ\s+(.+)$/,
8
+ pattern: new RegExp(`^(${LABEL_NAME_PATTERN})\\s+\\.equ\\s+(.+)$`),
5
9
  parse: (line, match, span) => parseEquItem(line, match[1] ?? '', match[2] ?? '', span),
6
10
  },
7
11
  {
@@ -9,14 +13,23 @@ const DIRECTIVE_PARSERS = [
9
13
  parse: (line, match, span) => parseExpressionDirective(line, 'org', match[1] ?? '', span),
10
14
  },
11
15
  {
12
- pattern: /^enum\s+([A-Za-z_][A-Za-z0-9_]*)\s+(.+)$/,
16
+ pattern: new RegExp(`^enum\\s+(${IDENTIFIER_PATTERN})\\s+(.+)$`),
13
17
  parse: (line, match) => ({
14
18
  items: [],
15
- diagnostics: [parseError(line, `Use "${match[1] ?? ''} .enum ..." for enums.`)],
19
+ diagnostics: [
20
+ {
21
+ severity: 'error',
22
+ code: 'AZMN_PARSE',
23
+ message: `Use "${match[1] ?? ''} .enum ..." for enums.`,
24
+ sourceName: line.sourceName,
25
+ line: line.line,
26
+ column: firstNonWhitespaceColumn(line.text),
27
+ },
28
+ ],
16
29
  }),
17
30
  },
18
31
  {
19
- pattern: /^([A-Za-z_][A-Za-z0-9_]*)\s+\.enum\s+(.+)$/,
32
+ pattern: new RegExp(`^(${IDENTIFIER_PATTERN})\\s+\\.enum\\s+(.+)$`),
20
33
  parse: (line, match, span) => parseEnumItem(line, match[1] ?? '', match[2] ?? '', span),
21
34
  },
22
35
  {
@@ -44,6 +57,7 @@ const DIRECTIVE_PARSERS = [
44
57
  parse: (line, match, span) => parseStringDataDirective(line, (match[1] ?? '').slice(1).toLowerCase(), match[2] ?? '', span),
45
58
  },
46
59
  ];
60
+ export { parseColonDeclaration } from './parse-declaration-directives.js';
47
61
  export function parseDirectiveStatement(line, text, span) {
48
62
  for (const parser of DIRECTIVE_PARSERS) {
49
63
  const match = parser.pattern.exec(text);
@@ -53,257 +67,3 @@ export function parseDirectiveStatement(line, text, span) {
53
67
  }
54
68
  return undefined;
55
69
  }
56
- export function parseColonDeclaration(line, name, statementText, span) {
57
- const equ = /^\.equ\s+(.+)$/.exec(statementText);
58
- if (equ) {
59
- return parseEquItem(line, name, equ[1] ?? '', span);
60
- }
61
- const enumDecl = /^\.enum\s+(.+)$/.exec(statementText);
62
- if (enumDecl) {
63
- return parseEnumItem(line, name, enumDecl[1] ?? '', span);
64
- }
65
- return undefined;
66
- }
67
- function parseEquItem(line, name, expressionText, span) {
68
- const stringValue = parseWholeQuotedString(expressionText.trim());
69
- const expression = stringValue !== undefined && stringValue.length > 1
70
- ? { kind: 'number', value: 0 }
71
- : parseExpression(expressionText);
72
- if (!expression) {
73
- return {
74
- items: [],
75
- diagnostics: [parseError(line, `invalid .equ expression: ${expressionText}`)],
76
- };
77
- }
78
- return {
79
- items: [
80
- {
81
- kind: 'equ',
82
- name,
83
- expression,
84
- ...(stringValue !== undefined && stringValue.length > 1 ? { stringValue } : {}),
85
- span,
86
- },
87
- ],
88
- diagnostics: [],
89
- };
90
- }
91
- function parseExpressionDirective(line, kind, expressionText, span) {
92
- const expression = parseExpression(expressionText);
93
- if (!expression) {
94
- return {
95
- items: [],
96
- diagnostics: [parseError(line, `invalid .${kind} expression: ${expressionText}`)],
97
- };
98
- }
99
- if (kind === 'align') {
100
- return { items: [{ kind, alignment: expression, span }], diagnostics: [] };
101
- }
102
- return { items: [{ kind, expression, span }], diagnostics: [] };
103
- }
104
- function parseDataDirective(line, directiveText, valueText, span) {
105
- const directive = directiveText.slice(1).toLowerCase();
106
- const parts = splitValueList(valueText);
107
- const values = directive === 'db'
108
- ? parts.map(parseDataValue).filter((value) => value !== undefined)
109
- : parts.map(parseExpression).filter((value) => value !== undefined);
110
- if (values.length !== parts.length) {
111
- return {
112
- items: [],
113
- diagnostics: [parseError(line, `invalid .${directive} value list`)],
114
- };
115
- }
116
- return {
117
- items: directive === 'db'
118
- ? [{ kind: 'db', values: values, span }]
119
- : [{ kind: 'dw', values: values, span }],
120
- diagnostics: [],
121
- };
122
- }
123
- function parseDsDirective(line, valueText, span) {
124
- const parts = splitValueList(valueText);
125
- const listDiagnostic = validateDsValueList(line, parts);
126
- if (listDiagnostic) {
127
- return { items: [], diagnostics: [listDiagnostic] };
128
- }
129
- const sizeResult = parseDsSize(line, parts[0] ?? '');
130
- if (sizeResult.diagnostic) {
131
- return { items: [], diagnostics: [sizeResult.diagnostic] };
132
- }
133
- const fillResult = parseDsFill(line, parts[1]);
134
- if (fillResult.diagnostic) {
135
- return { items: [], diagnostics: [fillResult.diagnostic] };
136
- }
137
- return {
138
- items: [
139
- fillResult.fill === undefined
140
- ? { kind: 'ds', size: sizeResult.size, span }
141
- : { kind: 'ds', size: sizeResult.size, fill: fillResult.fill, span },
142
- ],
143
- diagnostics: [],
144
- };
145
- }
146
- function validateDsValueList(line, parts) {
147
- return parts.length < 1 || parts.length > 2
148
- ? parseError(line, `invalid .ds value list`)
149
- : undefined;
150
- }
151
- function parseDsSize(line, sizeText) {
152
- const size = parseTypeSizeExpression(sizeText) ?? parseExpression(sizeText);
153
- if (!size) {
154
- return {
155
- diagnostic: parseError(line, `invalid .ds size: ${sizeText}`),
156
- };
157
- }
158
- return { size };
159
- }
160
- function parseDsFill(line, fillText) {
161
- if (fillText === undefined)
162
- return { fill: undefined };
163
- const fill = parseExpression(fillText);
164
- if (!fill) {
165
- return {
166
- diagnostic: parseError(line, `invalid .ds fill: ${fillText}`),
167
- };
168
- }
169
- return { fill };
170
- }
171
- function parseStringDataDirective(line, directive, valueText, span) {
172
- const value = parseQuotedString(valueText);
173
- if (value === undefined) {
174
- return {
175
- items: [],
176
- diagnostics: [parseError(line, `.${directive} expects one double-quoted string`)],
177
- };
178
- }
179
- return { items: [{ kind: 'string-data', directive, value, span }], diagnostics: [] };
180
- }
181
- function parseEnumItem(line, name, membersText, span) {
182
- const rawMembers = membersText.split(',').map((member) => member.trim());
183
- if (membersText.trim().length === 0 || rawMembers.some((member) => member.length === 0)) {
184
- return {
185
- items: [],
186
- diagnostics: [parseError(line, `invalid enum member list`)],
187
- };
188
- }
189
- const members = [];
190
- const diagnostics = [];
191
- for (const member of rawMembers) {
192
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(member)) {
193
- diagnostics.push(parseError(line, `Invalid enum member name "${member}": expected <identifier>.`));
194
- continue;
195
- }
196
- members.push(member);
197
- }
198
- if (diagnostics.length > 0) {
199
- return { items: [], diagnostics };
200
- }
201
- return { items: [{ kind: 'enum', name, members, span }], diagnostics: [] };
202
- }
203
- function parseTypeSizeExpression(text) {
204
- const typeExpr = parseTypeExpr(text);
205
- return typeExpr ? { kind: 'type-size', typeExpr } : undefined;
206
- }
207
- function splitValueList(text) {
208
- const values = [];
209
- let state = { quote: undefined, escaped: false, parenDepth: 0 };
210
- let start = 0;
211
- for (let index = 0; index < text.length; index += 1) {
212
- if (isValueSeparator(text[index] ?? '', state)) {
213
- values.push(text.slice(start, index));
214
- start = index + 1;
215
- continue;
216
- }
217
- state = scanValueListChar(text[index] ?? '', state);
218
- }
219
- values.push(text.slice(start));
220
- return values;
221
- }
222
- function isValueSeparator(char, state) {
223
- return char === ',' && state.quote === undefined && state.parenDepth === 0;
224
- }
225
- function scanValueListChar(char, state) {
226
- const escapedState = scanEscapedValueListChar(char, state);
227
- if (escapedState)
228
- return escapedState;
229
- const quotedState = scanQuotedValueListChar(char, state);
230
- if (quotedState)
231
- return quotedState;
232
- return scanParenthesizedValueListChar(char, state);
233
- }
234
- function scanEscapedValueListChar(char, state) {
235
- if (state.escaped)
236
- return { ...state, escaped: false };
237
- if (char === '\\' && state.quote !== undefined)
238
- return { ...state, escaped: true };
239
- return undefined;
240
- }
241
- function scanQuotedValueListChar(char, state) {
242
- if (char !== '"' && char !== "'")
243
- return undefined;
244
- return { ...state, quote: state.quote === char ? undefined : (state.quote ?? char) };
245
- }
246
- function scanParenthesizedValueListChar(char, state) {
247
- if (state.quote !== undefined)
248
- return state;
249
- if (char === '(')
250
- return { ...state, parenDepth: state.parenDepth + 1 };
251
- if (char === ')')
252
- return { ...state, parenDepth: Math.max(0, state.parenDepth - 1) };
253
- return state;
254
- }
255
- function parseQuotedString(text) {
256
- return parseQuotedStringWithQuotes(text, new Set(['"']));
257
- }
258
- function parseWholeQuotedString(text) {
259
- return parseQuotedStringWithQuotes(text, new Set(['"', "'"]));
260
- }
261
- function parseQuotedStringWithQuotes(text, allowedQuotes) {
262
- const input = text.trim();
263
- const quote = input[0];
264
- if (!quote || !allowedQuotes.has(quote) || input[input.length - 1] !== quote) {
265
- return undefined;
266
- }
267
- return parseQuotedStringContent(input, quote);
268
- }
269
- function parseQuotedStringContent(input, quote) {
270
- let value = '';
271
- for (let index = 1; index < input.length - 1; index += 1) {
272
- const char = input[index] ?? '';
273
- if (char === '\\') {
274
- if (index + 1 >= input.length - 1) {
275
- return undefined;
276
- }
277
- value += input[index + 1] ?? '';
278
- index += 1;
279
- continue;
280
- }
281
- if (char === quote) {
282
- return undefined;
283
- }
284
- value += char;
285
- }
286
- return value;
287
- }
288
- function parseDataValue(text) {
289
- const expression = parseExpression(text);
290
- if (expression) {
291
- return expression;
292
- }
293
- const value = parseWholeQuotedString(text);
294
- return value === undefined ? undefined : { kind: 'string-fragment', value };
295
- }
296
- function firstColumn(text) {
297
- const match = /\S/.exec(text);
298
- return match ? match.index + 1 : 1;
299
- }
300
- function parseError(line, message) {
301
- return {
302
- severity: 'error',
303
- code: 'AZMN_PARSE',
304
- message,
305
- sourceName: line.sourceName,
306
- line: line.line,
307
- column: firstColumn(line.text),
308
- };
309
- }
@@ -0,0 +1,22 @@
1
+ import type { Diagnostic } from '../model/diagnostic.js';
2
+ import type { ParsedLeadingLabel } from './names.js';
3
+ interface InstructionChainLine {
4
+ readonly text: string;
5
+ }
6
+ export interface ParseChainStatementResult<TItem> {
7
+ readonly items: readonly TItem[];
8
+ readonly diagnostics: readonly Diagnostic[];
9
+ }
10
+ export interface ParseInstructionChainOptions<TLine extends InstructionChainLine, TItem> {
11
+ readonly line: TLine;
12
+ readonly parseStatement: (line: TLine, statementText: string, statementColumn: number) => ParseChainStatementResult<TItem>;
13
+ readonly makeLabelItem: (label: ParsedLeadingLabel, line: TLine) => TItem;
14
+ readonly makeDiagnostic: (line: TLine, column: number, message: string) => Diagnostic;
15
+ readonly appendLineComment?: (items: TItem[], line: TLine) => void;
16
+ }
17
+ export interface ParseInstructionChainResult<TItem> {
18
+ readonly items: readonly TItem[];
19
+ readonly diagnostics: readonly Diagnostic[];
20
+ }
21
+ export declare function parseInstructionChain<TLine extends InstructionChainLine, TItem>(options: ParseInstructionChainOptions<TLine, TItem>): ParseInstructionChainResult<TItem> | undefined;
22
+ export {};