@m100/core 0.8.0

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 (39) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +5 -0
  3. package/dist/commentUtils.d.ts +8 -0
  4. package/dist/commentUtils.js +64 -0
  5. package/dist/commentUtils.js.map +1 -0
  6. package/dist/detokenize/detokenize.d.ts +18 -0
  7. package/dist/detokenize/detokenize.js +57 -0
  8. package/dist/detokenize/detokenize.js.map +1 -0
  9. package/dist/index.d.ts +11 -0
  10. package/dist/index.js +11 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/pack/pack.d.ts +29 -0
  13. package/dist/pack/pack.js +229 -0
  14. package/dist/pack/pack.js.map +1 -0
  15. package/dist/removeComments/removeComments.d.ts +15 -0
  16. package/dist/removeComments/removeComments.js +98 -0
  17. package/dist/removeComments/removeComments.js.map +1 -0
  18. package/dist/renumber/renumber.d.ts +29 -0
  19. package/dist/renumber/renumber.js +87 -0
  20. package/dist/renumber/renumber.js.map +1 -0
  21. package/dist/squash/squash.d.ts +31 -0
  22. package/dist/squash/squash.js +247 -0
  23. package/dist/squash/squash.js.map +1 -0
  24. package/dist/tokenize/errors.d.ts +28 -0
  25. package/dist/tokenize/errors.js +41 -0
  26. package/dist/tokenize/errors.js.map +1 -0
  27. package/dist/tokenize/tokenizeProgram.d.ts +9 -0
  28. package/dist/tokenize/tokenizeProgram.js +165 -0
  29. package/dist/tokenize/tokenizeProgram.js.map +1 -0
  30. package/dist/tokenize/types.d.ts +24 -0
  31. package/dist/tokenize/types.js +2 -0
  32. package/dist/tokenize/types.js.map +1 -0
  33. package/dist/tokens.d.ts +13 -0
  34. package/dist/tokens.js +151 -0
  35. package/dist/tokens.js.map +1 -0
  36. package/dist/utils/programAnalysis.d.ts +41 -0
  37. package/dist/utils/programAnalysis.js +152 -0
  38. package/dist/utils/programAnalysis.js.map +1 -0
  39. package/package.json +26 -0
@@ -0,0 +1,87 @@
1
+ import { collectProgramStructure } from '../utils/programAnalysis.js';
2
+ /**
3
+ * Renumber BASIC program lines.
4
+ *
5
+ * @param basicCode - BASIC source code
6
+ * @param options - Renumbering options
7
+ * @returns Renumbered code
8
+ */
9
+ export function renumberProgram(basicCode, options = {}) {
10
+ const start = options.start ?? 10;
11
+ const increment = options.increment ?? 10;
12
+ const structure = collectProgramStructure(basicCode);
13
+ if (structure.numberedLines.length === 0) {
14
+ return basicCode; // No lines to renumber
15
+ }
16
+ // Build assignment map: old number → new number
17
+ const assignments = [];
18
+ let current = start;
19
+ for (const entry of structure.numberedLines) {
20
+ assignments.push({
21
+ lineIndex: entry.line,
22
+ oldNumber: entry.number,
23
+ newNumber: current,
24
+ });
25
+ current += increment;
26
+ if (current > 65529) {
27
+ throw new Error('Renumbering would exceed maximum line number (65529). Choose a smaller increment.');
28
+ }
29
+ }
30
+ const referenceMap = new Map();
31
+ assignments.forEach(assignment => {
32
+ referenceMap.set(assignment.oldNumber, assignment.newNumber);
33
+ });
34
+ return applyRenumberChanges(basicCode, structure, assignments, referenceMap);
35
+ }
36
+ function applyRenumberChanges(basicCode, structure, assignments, referenceMap) {
37
+ const lines = basicCode.split(/\r?\n/);
38
+ const replacementsByLine = new Map();
39
+ const numberInfoByLine = new Map();
40
+ structure.numberedLines.forEach((info) => {
41
+ numberInfoByLine.set(info.line, info);
42
+ });
43
+ // Queue replacements for line numbers
44
+ for (const assignment of assignments) {
45
+ const info = numberInfoByLine.get(assignment.lineIndex);
46
+ if (!info) {
47
+ continue;
48
+ }
49
+ queueReplacement(replacementsByLine, assignment.lineIndex, info.numberStart, info.numberEnd, assignment.newNumber.toString());
50
+ }
51
+ // Queue replacements for line references (GOTO, GOSUB, etc.)
52
+ for (const reference of structure.references) {
53
+ const newTarget = referenceMap.get(reference.target);
54
+ if (newTarget === undefined) {
55
+ continue;
56
+ }
57
+ queueReplacement(replacementsByLine, reference.lineIndex, reference.start, reference.end, newTarget.toString());
58
+ }
59
+ // Apply replacements to each line
60
+ for (const [lineIndex, replacements] of replacementsByLine.entries()) {
61
+ let text = lines[lineIndex];
62
+ // Sort by position (descending) to avoid offset issues
63
+ const sorted = replacements.sort((a, b) => b.start - a.start);
64
+ for (const replacement of sorted) {
65
+ text =
66
+ text.slice(0, replacement.start) +
67
+ replacement.value +
68
+ text.slice(replacement.end);
69
+ }
70
+ lines[lineIndex] = text;
71
+ }
72
+ return lines.join('\n');
73
+ }
74
+ function queueReplacement(map, line, start, end, value) {
75
+ if (!map.has(line)) {
76
+ map.set(line, []);
77
+ }
78
+ map.get(line).push({ start, end, value });
79
+ }
80
+ /**
81
+ * Renumber a BASIC file (file-based API).
82
+ *
83
+ * @param inPath - Path to input .DO file
84
+ * @param options - Renumbering options
85
+ * @returns Result with output path and stats
86
+ */
87
+ //# sourceMappingURL=renumber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renumber.js","sourceRoot":"","sources":["../../src/renumber/renumber.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * TRS-80 Model 100 BASIC Squash - Core Logic
3
+ *
4
+ * Squashes BASIC code by removing unnecessary whitespace and optimizing PRINT statements.
5
+ * Preserves REM, DATA, and string literals.
6
+ */
7
+ /**
8
+ * Squash BASIC code by removing unnecessary whitespace.
9
+ *
10
+ * @param basicCode - BASIC source code with line numbers
11
+ * @returns Squashed code
12
+ */
13
+ export declare function squashBasicCode(basicCode: string): string;
14
+ /**
15
+ * Squash a single BASIC line.
16
+ *
17
+ * @param line - Line to squash
18
+ * @returns Squashed line, or undefined if line should be removed
19
+ */
20
+ export declare function squashLine(line: string): string | undefined;
21
+ export declare function removeWhitespaceOutsideStrings(text: string): string;
22
+ export declare function collapsePrintSemicolons(text: string): string;
23
+ export declare function removeUnnecessaryTrailingQuote(text: string): string;
24
+ export declare function removeTrailingColon(text: string): string;
25
+ /**
26
+ * Squash a BASIC file (file-based API).
27
+ *
28
+ * @param inPath - Path to input .DO file
29
+ * @param options - Options including output path
30
+ * @returns Result with output path and line count
31
+ */
@@ -0,0 +1,247 @@
1
+ import { findCommentStart } from '../commentUtils.js';
2
+ /**
3
+ * TRS-80 Model 100 BASIC Squash - Core Logic
4
+ *
5
+ * Squashes BASIC code by removing unnecessary whitespace and optimizing PRINT statements.
6
+ * Preserves REM, DATA, and string literals.
7
+ */
8
+ /**
9
+ * Squash BASIC code by removing unnecessary whitespace.
10
+ *
11
+ * @param basicCode - BASIC source code with line numbers
12
+ * @returns Squashed code
13
+ */
14
+ export function squashBasicCode(basicCode) {
15
+ const lines = basicCode.split(/\r?\n/);
16
+ const resultLines = [];
17
+ for (const line of lines) {
18
+ const squashed = squashLine(line);
19
+ if (squashed !== undefined) {
20
+ resultLines.push(squashed);
21
+ }
22
+ }
23
+ return resultLines.join('\n');
24
+ }
25
+ /**
26
+ * Squash a single BASIC line.
27
+ *
28
+ * @param line - Line to squash
29
+ * @returns Squashed line, or undefined if line should be removed
30
+ */
31
+ export function squashLine(line) {
32
+ if (!line.trim()) {
33
+ return undefined;
34
+ }
35
+ const commentStart = findCommentStart(line);
36
+ const comment = commentStart === undefined ? '' : line.slice(commentStart);
37
+ const code = commentStart === undefined ? line : line.slice(0, commentStart);
38
+ const numberMatch = /^\s*(\d+)/.exec(code);
39
+ let remaining = code;
40
+ let lineNumber;
41
+ if (numberMatch) {
42
+ lineNumber = numberMatch[1];
43
+ remaining = code.slice(numberMatch.index + numberMatch[0].length);
44
+ }
45
+ if (!lineNumber && !remaining.trim()) {
46
+ return undefined;
47
+ }
48
+ const trimmedBody = remaining.trimStart();
49
+ if (!trimmedBody && !comment) {
50
+ return lineNumber ? lineNumber : undefined;
51
+ }
52
+ if (shouldSkipBody(trimmedBody)) {
53
+ return rebuildLine(code, comment);
54
+ }
55
+ if (containsKeywordOutsideStrings(trimmedBody, 'DATA')) {
56
+ return rebuildLine(code, comment);
57
+ }
58
+ let body = removeWhitespaceOutsideStrings(trimmedBody);
59
+ body = collapsePrintSemicolons(body);
60
+ body = removeUnnecessaryTrailingQuote(body);
61
+ body = removeTrailingColon(body);
62
+ const rebuilt = (lineNumber ? `${lineNumber}${body ? ' ' : ''}` : '') + body;
63
+ return comment ? rebuilt + (comment.startsWith(' ') ? '' : ' ') + comment : rebuilt;
64
+ }
65
+ function rebuildLine(code, comment) {
66
+ return code.trimEnd() + (comment.startsWith(' ') ? '' : ' ') + comment;
67
+ }
68
+ function shouldSkipBody(body) {
69
+ if (!body) {
70
+ return true;
71
+ }
72
+ const upper = body.trim().toUpperCase();
73
+ if (!upper) {
74
+ return true;
75
+ }
76
+ if (upper.startsWith("'")) {
77
+ return true;
78
+ }
79
+ return upper.startsWith('REM');
80
+ }
81
+ export function removeWhitespaceOutsideStrings(text) {
82
+ let result = '';
83
+ let inString = false;
84
+ for (let i = 0; i < text.length; i++) {
85
+ const char = text[i];
86
+ if (char === '"') {
87
+ if (inString && text[i + 1] === '"') {
88
+ result += '""';
89
+ i += 1;
90
+ continue;
91
+ }
92
+ inString = !inString;
93
+ result += '"';
94
+ continue;
95
+ }
96
+ if (inString) {
97
+ result += char;
98
+ continue;
99
+ }
100
+ if (char === ' ' || char === '\t') {
101
+ continue;
102
+ }
103
+ result += char;
104
+ }
105
+ return result;
106
+ }
107
+ export function collapsePrintSemicolons(text) {
108
+ const statements = splitStatements(text);
109
+ const transformed = statements.map(statement => normalizePrintSemicolons(statement));
110
+ return transformed.join(':');
111
+ }
112
+ function splitStatements(body) {
113
+ const parts = [];
114
+ let lastSplit = 0;
115
+ let inString = false;
116
+ for (let i = 0; i < body.length; i++) {
117
+ const char = body[i];
118
+ if (char === '"') {
119
+ if (inString && body[i + 1] === '"') {
120
+ i += 1;
121
+ continue;
122
+ }
123
+ inString = !inString;
124
+ continue;
125
+ }
126
+ if (!inString && char === ':') {
127
+ parts.push(body.slice(lastSplit, i));
128
+ lastSplit = i + 1;
129
+ }
130
+ }
131
+ parts.push(body.slice(lastSplit));
132
+ return parts;
133
+ }
134
+ function normalizePrintSemicolons(statement) {
135
+ const upper = statement.toUpperCase();
136
+ if (!upper.startsWith('PRINT') && !upper.startsWith('LPRINT')) {
137
+ return statement;
138
+ }
139
+ let result = '';
140
+ let inString = false;
141
+ for (let i = 0; i < statement.length; i++) {
142
+ const char = statement[i];
143
+ if (char === '"') {
144
+ if (inString && statement[i + 1] === '"') {
145
+ result += '""';
146
+ i += 1;
147
+ continue;
148
+ }
149
+ inString = !inString;
150
+ result += '"';
151
+ continue;
152
+ }
153
+ if (!inString && char === ';') {
154
+ if (result.endsWith(';')) {
155
+ continue;
156
+ }
157
+ result += ';';
158
+ continue;
159
+ }
160
+ result += char;
161
+ }
162
+ return result;
163
+ }
164
+ export function removeUnnecessaryTrailingQuote(text) {
165
+ if (!text || text[text.length - 1] !== '"') {
166
+ return text;
167
+ }
168
+ let quotes = 0;
169
+ for (let i = text.length - 1; i >= 0; i--) {
170
+ if (text[i] === '"') {
171
+ quotes += 1;
172
+ }
173
+ else {
174
+ break;
175
+ }
176
+ }
177
+ if (quotes !== 1) {
178
+ return text;
179
+ }
180
+ return text.slice(0, text.length - 1);
181
+ }
182
+ export function removeTrailingColon(text) {
183
+ if (!text) {
184
+ return text;
185
+ }
186
+ let inString = false;
187
+ for (let i = text.length - 1; i >= 0; i--) {
188
+ const char = text[i];
189
+ if (char === '"') {
190
+ if (i > 0 && text[i - 1] === '"') {
191
+ i -= 1;
192
+ continue;
193
+ }
194
+ inString = !inString;
195
+ continue;
196
+ }
197
+ if (inString) {
198
+ continue;
199
+ }
200
+ if (char === ' ' || char === '\t') {
201
+ continue;
202
+ }
203
+ if (char === ':') {
204
+ return text.slice(0, i).replace(/\s+$/, '');
205
+ }
206
+ break;
207
+ }
208
+ return text;
209
+ }
210
+ function containsKeywordOutsideStrings(text, keyword) {
211
+ const upperKeyword = keyword.toUpperCase();
212
+ const keywordLength = upperKeyword.length;
213
+ let inString = false;
214
+ for (let i = 0; i < text.length; i++) {
215
+ const char = text[i];
216
+ if (char === '"') {
217
+ if (inString && text[i + 1] === '"') {
218
+ i += 1;
219
+ continue;
220
+ }
221
+ inString = !inString;
222
+ continue;
223
+ }
224
+ if (inString) {
225
+ continue;
226
+ }
227
+ if (i + keywordLength > text.length) {
228
+ break;
229
+ }
230
+ const slice = text.slice(i, i + keywordLength).toUpperCase();
231
+ if (slice === upperKeyword) {
232
+ const prev = i === 0 ? undefined : text[i - 1];
233
+ if (!prev || !/[A-Za-z]/.test(prev)) {
234
+ return true;
235
+ }
236
+ }
237
+ }
238
+ return false;
239
+ }
240
+ /**
241
+ * Squash a BASIC file (file-based API).
242
+ *
243
+ * @param inPath - Path to input .DO file
244
+ * @param options - Options including output path
245
+ * @returns Result with output path and line count
246
+ */
247
+ //# sourceMappingURL=squash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"squash.js","sourceRoot":"","sources":["../../src/squash/squash.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Error codes for tokenization failures.
3
+ */
4
+ export type TokenizeErrorCode = "PARSE_ERROR" | "LINE_NUMBER_INVALID" | "TOKEN_LIMIT" | "FILE_NOT_FOUND" | "FILE_READ_ERROR" | "FILE_WRITE_ERROR";
5
+ /**
6
+ * Structured error for tokenization failures.
7
+ * Provides machine-readable error codes and optional line/column context.
8
+ */
9
+ export declare class TokenizeError extends Error {
10
+ /**
11
+ * Error code for programmatic error handling.
12
+ */
13
+ readonly code: TokenizeErrorCode;
14
+ /**
15
+ * Optional 1-based line number where error occurred.
16
+ */
17
+ readonly line?: number;
18
+ /**
19
+ * Optional 1-based column number where error occurred.
20
+ */
21
+ readonly col?: number;
22
+ constructor(code: TokenizeErrorCode, message: string, line?: number, col?: number);
23
+ /**
24
+ * Format error for CLI output.
25
+ * Format: [M100:ERROR_CODE] L{line}:C{col} {message}
26
+ */
27
+ formatForCLI(): string;
28
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Structured error for tokenization failures.
3
+ * Provides machine-readable error codes and optional line/column context.
4
+ */
5
+ export class TokenizeError extends Error {
6
+ /**
7
+ * Error code for programmatic error handling.
8
+ */
9
+ code;
10
+ /**
11
+ * Optional 1-based line number where error occurred.
12
+ */
13
+ line;
14
+ /**
15
+ * Optional 1-based column number where error occurred.
16
+ */
17
+ col;
18
+ constructor(code, message, line, col) {
19
+ super(message);
20
+ this.name = 'TokenizeError';
21
+ this.code = code;
22
+ this.line = line;
23
+ this.col = col;
24
+ // Maintain proper stack trace for where error was created (only in V8)
25
+ if (Error.captureStackTrace) {
26
+ Error.captureStackTrace(this, TokenizeError);
27
+ }
28
+ }
29
+ /**
30
+ * Format error for CLI output.
31
+ * Format: [M100:ERROR_CODE] L{line}:C{col} {message}
32
+ */
33
+ formatForCLI() {
34
+ const prefix = `[M100:${this.code}]`;
35
+ const location = this.line
36
+ ? ` L${this.line}${this.col ? `:C${this.col}` : ''}`
37
+ : '';
38
+ return `${prefix}${location} ${this.message}`;
39
+ }
40
+ }
41
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/tokenize/errors.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC;;OAEG;IACa,IAAI,CAAoB;IAExC;;OAEG;IACa,IAAI,CAAU;IAE9B;;OAEG;IACa,GAAG,CAAU;IAE7B,YACE,IAAuB,EACvB,OAAe,EACf,IAAa,EACb,GAAY;QAEZ,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QAEf,uEAAuE;QACvE,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,YAAY;QACV,MAAM,MAAM,GAAG,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI;YACxB,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YACpD,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,GAAG,MAAM,GAAG,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;IAChD,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tokenize BASIC source text to binary .BA format.
3
+ * This is the main API for in-memory tokenization.
4
+ *
5
+ * @param doText - ASCII BASIC source code with numbered lines
6
+ * @param baseAddress - Base memory address for the program (default: 0x8001 for Model 100/102)
7
+ * @returns Tokenized binary data
8
+ */
9
+ export declare function tokenizeDoTextToBaBytes(doText: string, baseAddress?: number): Uint8Array;
@@ -0,0 +1,165 @@
1
+ import { TOKENS } from '../tokens.js';
2
+ /**
3
+ * Tokenize ASCII BASIC line code to binary bytes.
4
+ *
5
+ * @param asciiCode - The BASIC code for one line (without line number)
6
+ * @returns Tokenized bytes for the line
7
+ */
8
+ function tokenizeLine(asciiCode) {
9
+ const tokenized = [];
10
+ let i = 0;
11
+ let inString = false;
12
+ while (i < asciiCode.length) {
13
+ // Track string state
14
+ if (asciiCode[i] === '"' && (i === 0 || asciiCode[i - 1] !== '\\')) {
15
+ inString = !inString;
16
+ tokenized.push('"'.charCodeAt(0));
17
+ i++;
18
+ continue;
19
+ }
20
+ // Handle single quotes (comments) - only outside strings
21
+ if (asciiCode[i] === "'" && !inString) {
22
+ // Expand single quote to: colon (3A) + REM token (8E) + quote token (FF)
23
+ tokenized.push(0x3A); // Colon
24
+ tokenized.push(0x8E); // REM token
25
+ tokenized.push(0xFF); // Quote token
26
+ // Skip rest of line (it's a comment)
27
+ break;
28
+ }
29
+ // If in string, just copy characters as-is
30
+ if (inString) {
31
+ tokenized.push(asciiCode.charCodeAt(i));
32
+ i++;
33
+ continue;
34
+ }
35
+ // Try to match a keyword starting at position i
36
+ let matched = false;
37
+ // Try longer keywords first (multi-character operators and keywords)
38
+ for (let keywordLen = Math.min(10, asciiCode.length - i); keywordLen > 0; keywordLen--) {
39
+ const candidate = asciiCode.slice(i, i + keywordLen);
40
+ // Check for token match (case-insensitive for keywords)
41
+ const upperCandidate = candidate.toUpperCase();
42
+ if (upperCandidate in TOKENS) {
43
+ const token = TOKENS[upperCandidate];
44
+ tokenized.push(token);
45
+ i += keywordLen;
46
+ matched = true;
47
+ // Special handling for REM and DATA: rest of line is literal
48
+ if (upperCandidate === 'REM' || upperCandidate === 'DATA') {
49
+ // Copy rest of line as-is after REM/DATA
50
+ for (let j = i; j < asciiCode.length; j++) {
51
+ tokenized.push(asciiCode.charCodeAt(j));
52
+ }
53
+ i = asciiCode.length;
54
+ break;
55
+ }
56
+ // Special handling: ELSE token must be preceded by colon
57
+ if (upperCandidate === 'ELSE') {
58
+ // Ensure there's a colon before ELSE
59
+ if (tokenized.length > 1 && tokenized[tokenized.length - 2] !== 0x3A) {
60
+ // Insert colon before ELSE token
61
+ tokenized.splice(tokenized.length - 1, 0, 0x3A);
62
+ }
63
+ }
64
+ break;
65
+ }
66
+ }
67
+ if (!matched) {
68
+ // Not a token, copy as ASCII
69
+ tokenized.push(asciiCode.charCodeAt(i));
70
+ i++;
71
+ }
72
+ }
73
+ return new Uint8Array(tokenized);
74
+ }
75
+ /**
76
+ * Parse a BASIC source line into line number and code.
77
+ *
78
+ * @param line - A single line of BASIC source code
79
+ * @returns Parsed line number and code, or null if invalid
80
+ */
81
+ function parseLine(line) {
82
+ const trimmed = line.trim();
83
+ if (!trimmed) {
84
+ return null;
85
+ }
86
+ const match = /^(\d+)\s*(.*)$/.exec(trimmed);
87
+ if (!match) {
88
+ return null;
89
+ }
90
+ const lineNumber = parseInt(match[1], 10);
91
+ const code = match[2] || '';
92
+ return { lineNumber, code };
93
+ }
94
+ /**
95
+ * Create tokenized binary file from ASCII BASIC.
96
+ *
97
+ * @param inputText - ASCII BASIC source code with numbered lines
98
+ * @param baseAddress - Base memory address for the program (default: 0x8001)
99
+ * @returns Tokenized binary data in .BA format
100
+ */
101
+ function createTokenizedFile(inputText, baseAddress = 0x8001) {
102
+ const lines = inputText.split(/\r?\n/);
103
+ // Parse and tokenize lines
104
+ const tokenizedLines = [];
105
+ for (const line of lines) {
106
+ const parsed = parseLine(line);
107
+ if (!parsed) {
108
+ continue;
109
+ }
110
+ try {
111
+ const tokenizedCode = tokenizeLine(parsed.code);
112
+ tokenizedLines.push({
113
+ lineNumber: parsed.lineNumber,
114
+ tokenizedCode,
115
+ });
116
+ }
117
+ catch (error) {
118
+ // Skip invalid lines (but continue processing)
119
+ continue;
120
+ }
121
+ }
122
+ // Calculate addresses for all lines first
123
+ const lineAddresses = [];
124
+ let tempAddress = baseAddress;
125
+ for (const { tokenizedCode } of tokenizedLines) {
126
+ lineAddresses.push(tempAddress);
127
+ // Each line: PL+PH(2) + LL+LH(2) + code + null(1)
128
+ const lineSize = 2 + 2 + tokenizedCode.length + 1;
129
+ tempAddress += lineSize;
130
+ }
131
+ // Build output data
132
+ const outputData = [];
133
+ // Write lines with calculated addresses
134
+ for (let i = 0; i < tokenizedLines.length; i++) {
135
+ const { lineNumber, tokenizedCode } = tokenizedLines[i];
136
+ // Calculate next line address
137
+ // For last line, point to where the end-of-program marker would be
138
+ const nextAddress = i + 1 < tokenizedLines.length
139
+ ? lineAddresses[i + 1]
140
+ : tempAddress;
141
+ // PL PH (next line address, little-endian)
142
+ outputData.push(nextAddress & 0xFF);
143
+ outputData.push((nextAddress >> 8) & 0xFF);
144
+ // LL LH (line number, little-endian)
145
+ outputData.push(lineNumber & 0xFF);
146
+ outputData.push((lineNumber >> 8) & 0xFF);
147
+ // Tokenized code
148
+ outputData.push(...Array.from(tokenizedCode));
149
+ // NULL terminator
150
+ outputData.push(0x00);
151
+ }
152
+ return new Uint8Array(outputData);
153
+ }
154
+ /**
155
+ * Tokenize BASIC source text to binary .BA format.
156
+ * This is the main API for in-memory tokenization.
157
+ *
158
+ * @param doText - ASCII BASIC source code with numbered lines
159
+ * @param baseAddress - Base memory address for the program (default: 0x8001 for Model 100/102)
160
+ * @returns Tokenized binary data
161
+ */
162
+ export function tokenizeDoTextToBaBytes(doText, baseAddress = 0x8001) {
163
+ return createTokenizedFile(doText, baseAddress);
164
+ }
165
+ //# sourceMappingURL=tokenizeProgram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizeProgram.js","sourceRoot":"","sources":["../../src/tokenize/tokenizeProgram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Options for tokenizing BASIC text to .BA bytes.
3
+ */
4
+ export interface TokenizeOptions {
5
+ /**
6
+ * Base memory address for the tokenized program (in decimal).
7
+ * Default: 0x8001 (32769) for Model 100/102
8
+ * Tandy 200 typically uses: 0xA001 (40961)
9
+ */
10
+ baseAddress?: number;
11
+ }
12
+ /**
13
+ * Result of tokenizing BASIC text to .BA bytes.
14
+ */
15
+ export interface TokenizeResult {
16
+ /**
17
+ * Tokenized bytes suitable for .BA output.
18
+ */
19
+ bytes: Uint8Array;
20
+ /**
21
+ * Optional warnings generated during tokenization.
22
+ */
23
+ warnings?: string[];
24
+ }