@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.
- package/LICENSE +5 -0
- package/README.md +5 -0
- package/dist/commentUtils.d.ts +8 -0
- package/dist/commentUtils.js +64 -0
- package/dist/commentUtils.js.map +1 -0
- package/dist/detokenize/detokenize.d.ts +18 -0
- package/dist/detokenize/detokenize.js +57 -0
- package/dist/detokenize/detokenize.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/pack/pack.d.ts +29 -0
- package/dist/pack/pack.js +229 -0
- package/dist/pack/pack.js.map +1 -0
- package/dist/removeComments/removeComments.d.ts +15 -0
- package/dist/removeComments/removeComments.js +98 -0
- package/dist/removeComments/removeComments.js.map +1 -0
- package/dist/renumber/renumber.d.ts +29 -0
- package/dist/renumber/renumber.js +87 -0
- package/dist/renumber/renumber.js.map +1 -0
- package/dist/squash/squash.d.ts +31 -0
- package/dist/squash/squash.js +247 -0
- package/dist/squash/squash.js.map +1 -0
- package/dist/tokenize/errors.d.ts +28 -0
- package/dist/tokenize/errors.js +41 -0
- package/dist/tokenize/errors.js.map +1 -0
- package/dist/tokenize/tokenizeProgram.d.ts +9 -0
- package/dist/tokenize/tokenizeProgram.js +165 -0
- package/dist/tokenize/tokenizeProgram.js.map +1 -0
- package/dist/tokenize/types.d.ts +24 -0
- package/dist/tokenize/types.js +2 -0
- package/dist/tokenize/types.js.map +1 -0
- package/dist/tokens.d.ts +13 -0
- package/dist/tokens.js +151 -0
- package/dist/tokens.js.map +1 -0
- package/dist/utils/programAnalysis.d.ts +41 -0
- package/dist/utils/programAnalysis.js +152 -0
- package/dist/utils/programAnalysis.js.map +1 -0
- 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
|
+
}
|