@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
package/LICENSE
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Copyright (c) 2025 George M. Rimakis
|
|
2
|
+
|
|
3
|
+
All rights reserved. You may not copy, modify, merge, publish, distribute, sublicense, or sell copies of this software or associated assets unless you have received express written permission from the copyright holder.
|
|
4
|
+
|
|
5
|
+
This software is provided "as-is" without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
|
package/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function findCommentStart(line: string): number | undefined;
|
|
2
|
+
export declare function stripComment(line: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Strips both string literals and comments from a line.
|
|
5
|
+
* String contents are replaced with spaces to preserve column positions.
|
|
6
|
+
* This is useful for variable detection where we want to ignore identifiers inside strings.
|
|
7
|
+
*/
|
|
8
|
+
export declare function stripCommentsAndStrings(line: string): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export function findCommentStart(line) {
|
|
2
|
+
let inString = false;
|
|
3
|
+
const upper = line.toUpperCase();
|
|
4
|
+
for (let i = 0; i < line.length; i++) {
|
|
5
|
+
const char = line[i];
|
|
6
|
+
if (char === '"') {
|
|
7
|
+
inString = !inString;
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (inString) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
if (char === "'") {
|
|
14
|
+
return i;
|
|
15
|
+
}
|
|
16
|
+
// treat REM as a comment start even when immediately following a ':' separator
|
|
17
|
+
if (upper.startsWith('REM', i) && (i === 0 || /[\s:]/.test(line[i - 1]))) {
|
|
18
|
+
return i;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
export function stripComment(line) {
|
|
24
|
+
const commentIndex = findCommentStart(line);
|
|
25
|
+
return commentIndex === undefined ? line : line.substring(0, commentIndex);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Strips both string literals and comments from a line.
|
|
29
|
+
* String contents are replaced with spaces to preserve column positions.
|
|
30
|
+
* This is useful for variable detection where we want to ignore identifiers inside strings.
|
|
31
|
+
*/
|
|
32
|
+
export function stripCommentsAndStrings(line) {
|
|
33
|
+
let result = '';
|
|
34
|
+
let inString = false;
|
|
35
|
+
const upper = line.toUpperCase();
|
|
36
|
+
for (let i = 0; i < line.length; i++) {
|
|
37
|
+
const char = line[i];
|
|
38
|
+
// Handle string delimiters
|
|
39
|
+
if (char === '"') {
|
|
40
|
+
inString = !inString;
|
|
41
|
+
result += ' '; // Replace quote with space
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// Inside a string - replace with spaces to preserve positions
|
|
45
|
+
if (inString) {
|
|
46
|
+
result += ' ';
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// Check for comment start (single quote)
|
|
50
|
+
if (char === "'") {
|
|
51
|
+
// Rest of line is a comment
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
// Check for REM comment
|
|
55
|
+
if (upper.startsWith('REM', i) && (i === 0 || /[\s:]/.test(line[i - 1]))) {
|
|
56
|
+
// Rest of line is a comment
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
// Normal character
|
|
60
|
+
result += char;
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=commentUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commentUtils.js","sourceRoot":"","sources":["../src/commentUtils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,QAAQ,GAAG,CAAC,QAAQ,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,OAAO,CAAC,CAAC;QACX,CAAC;QACD,+EAA+E;QAC/E,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC5C,OAAO,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAC7E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAErB,2BAA2B;QAC3B,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,QAAQ,GAAG,CAAC,QAAQ,CAAC;YACrB,MAAM,IAAI,GAAG,CAAC,CAAC,2BAA2B;YAC1C,SAAS;QACX,CAAC;QAED,8DAA8D;QAC9D,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,CAAC;YACd,SAAS;QACX,CAAC;QAED,yCAAyC;QACzC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,4BAA4B;YAC5B,MAAM;QACR,CAAC;QAED,wBAAwB;QACxB,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,4BAA4B;YAC5B,MAAM;QACR,CAAC;QAED,mBAAmB;QACnB,MAAM,IAAI,IAAI,CAAC;IACjB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core detokenization logic for TRS-80 Model 100 BASIC (.BA files)
|
|
3
|
+
* No VS Code dependencies - works with plain Buffers/Uint8Arrays
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Detokenizes a .BA file buffer into an array of text lines
|
|
7
|
+
*
|
|
8
|
+
* @param buffer - The binary .BA file contents
|
|
9
|
+
* @returns Array of detokenized BASIC lines (e.g., "10 PRINT \"HELLO\"")
|
|
10
|
+
*/
|
|
11
|
+
export declare function detokenizeBaBytes(buffer: Uint8Array): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Detokenizes a .BA file buffer into text
|
|
14
|
+
*
|
|
15
|
+
* @param buffer - The binary .BA file contents
|
|
16
|
+
* @returns Detokenized BASIC program as a string
|
|
17
|
+
*/
|
|
18
|
+
export declare function detokenizeBaToText(buffer: Uint8Array): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core detokenization logic for TRS-80 Model 100 BASIC (.BA files)
|
|
3
|
+
* No VS Code dependencies - works with plain Buffers/Uint8Arrays
|
|
4
|
+
*/
|
|
5
|
+
import { REVERSE_TOKENS } from '../tokens.js';
|
|
6
|
+
/**
|
|
7
|
+
* Detokenizes a single line of tokenized BASIC code
|
|
8
|
+
*/
|
|
9
|
+
function detokenizeLine(bytes) {
|
|
10
|
+
let result = '';
|
|
11
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
12
|
+
const b = bytes[i];
|
|
13
|
+
if (b >= 0x80) {
|
|
14
|
+
const keyword = REVERSE_TOKENS[b];
|
|
15
|
+
result += keyword ?? `<0x${b.toString(16).padStart(2, '0')}>`;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (b === 0x00) {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
result += String.fromCharCode(b);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Detokenizes a .BA file buffer into an array of text lines
|
|
27
|
+
*
|
|
28
|
+
* @param buffer - The binary .BA file contents
|
|
29
|
+
* @returns Array of detokenized BASIC lines (e.g., "10 PRINT \"HELLO\"")
|
|
30
|
+
*/
|
|
31
|
+
export function detokenizeBaBytes(buffer) {
|
|
32
|
+
const lines = [];
|
|
33
|
+
let pos = 0;
|
|
34
|
+
while (pos + 4 <= buffer.length) {
|
|
35
|
+
const lineNumber = buffer[pos + 2] | (buffer[pos + 3] << 8);
|
|
36
|
+
const codeStart = pos + 4;
|
|
37
|
+
let codeEnd = codeStart;
|
|
38
|
+
while (codeEnd < buffer.length && buffer[codeEnd] !== 0x00) {
|
|
39
|
+
codeEnd++;
|
|
40
|
+
}
|
|
41
|
+
const codeBytes = buffer.slice(codeStart, codeEnd);
|
|
42
|
+
const codeText = detokenizeLine(codeBytes);
|
|
43
|
+
lines.push(`${lineNumber} ${codeText}`.trimEnd());
|
|
44
|
+
pos = codeEnd + 1;
|
|
45
|
+
}
|
|
46
|
+
return lines;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Detokenizes a .BA file buffer into text
|
|
50
|
+
*
|
|
51
|
+
* @param buffer - The binary .BA file contents
|
|
52
|
+
* @returns Detokenized BASIC program as a string
|
|
53
|
+
*/
|
|
54
|
+
export function detokenizeBaToText(buffer) {
|
|
55
|
+
return detokenizeBaBytes(buffer).join('\n');
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=detokenize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detokenize.js","sourceRoot":"","sources":["../../src/detokenize/detokenize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;GAEG;AACH,SAAS,cAAc,CAAC,KAAiB;IACvC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,IAAI,OAAO,IAAI,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;YAC9D,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACf,MAAM;QACR,CAAC;QACD,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAkB;IAClD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,GAAG,GAAG,CAAC,CAAC;IAEZ,OAAO,GAAG,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC;QAE1B,IAAI,OAAO,GAAG,SAAS,CAAC;QACxB,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC3D,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAC3C,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,IAAI,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAElD,GAAG,GAAG,OAAO,GAAG,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACnD,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { TOKENS, REVERSE_TOKENS } from './tokens.js';
|
|
2
|
+
export { TokenizeError } from './tokenize/errors.js';
|
|
3
|
+
export type { TokenizeOptions, TokenizeResult } from './tokenize/types.js';
|
|
4
|
+
export { tokenizeDoTextToBaBytes, tokenizeDoTextToBaBytes as tokenize } from './tokenize/tokenizeProgram.js';
|
|
5
|
+
export { detokenizeBaBytes, detokenizeBaToText, detokenizeBaToText as detokenize } from './detokenize/detokenize.js';
|
|
6
|
+
export { removeComments } from './removeComments/removeComments.js';
|
|
7
|
+
export { squashBasicCode } from './squash/squash.js';
|
|
8
|
+
export { packProgram } from './pack/pack.js';
|
|
9
|
+
export { renumberProgram } from './renumber/renumber.js';
|
|
10
|
+
export { findCommentStart, stripComment, stripCommentsAndStrings } from './commentUtils.js';
|
|
11
|
+
export { collectProgramStructure, findLineReferenceSpans, type NumberedLineInfo, type LineReference, type ProgramStructure, type ReferenceKeyword, type ReferenceSpan } from './utils/programAnalysis.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { TOKENS, REVERSE_TOKENS } from './tokens.js';
|
|
2
|
+
export { TokenizeError } from './tokenize/errors.js';
|
|
3
|
+
export { tokenizeDoTextToBaBytes, tokenizeDoTextToBaBytes as tokenize } from './tokenize/tokenizeProgram.js';
|
|
4
|
+
export { detokenizeBaBytes, detokenizeBaToText, detokenizeBaToText as detokenize } from './detokenize/detokenize.js';
|
|
5
|
+
export { removeComments } from './removeComments/removeComments.js';
|
|
6
|
+
export { squashBasicCode } from './squash/squash.js';
|
|
7
|
+
export { packProgram } from './pack/pack.js';
|
|
8
|
+
export { renumberProgram } from './renumber/renumber.js';
|
|
9
|
+
export { findCommentStart, stripComment, stripCommentsAndStrings } from './commentUtils.js';
|
|
10
|
+
export { collectProgramStructure, findLineReferenceSpans } from './utils/programAnalysis.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,IAAI,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,IAAI,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACrH,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5F,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EAMvB,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TRS-80 Model 100 BASIC Pack - Core Logic
|
|
3
|
+
*
|
|
4
|
+
* Packs BASIC code by merging lines together using colons.
|
|
5
|
+
*/
|
|
6
|
+
export interface PackOptions {
|
|
7
|
+
maxLength?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PackResult {
|
|
10
|
+
outPath: string;
|
|
11
|
+
linesBefore: number;
|
|
12
|
+
linesAfter: number;
|
|
13
|
+
overLengthLines: number[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Pack BASIC program by merging lines.
|
|
17
|
+
*
|
|
18
|
+
* @param basicCode - BASIC source code
|
|
19
|
+
* @param options - Packing options
|
|
20
|
+
* @returns Packed code
|
|
21
|
+
*/
|
|
22
|
+
export declare function packProgram(basicCode: string, options?: PackOptions): string;
|
|
23
|
+
/**
|
|
24
|
+
* Pack a BASIC file (file-based API).
|
|
25
|
+
*
|
|
26
|
+
* @param inPath - Path to input .DO file
|
|
27
|
+
* @param options - Packing options
|
|
28
|
+
* @returns Result with output path and stats
|
|
29
|
+
*/
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { collectProgramStructure } from '../utils/programAnalysis.js';
|
|
2
|
+
import { findCommentStart } from '../commentUtils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Pack BASIC program by merging lines.
|
|
5
|
+
*
|
|
6
|
+
* @param basicCode - BASIC source code
|
|
7
|
+
* @param options - Packing options
|
|
8
|
+
* @returns Packed code
|
|
9
|
+
*/
|
|
10
|
+
export function packProgram(basicCode, options = {}) {
|
|
11
|
+
const maxLength = options.maxLength ?? 255;
|
|
12
|
+
const structure = collectProgramStructure(basicCode);
|
|
13
|
+
// Collect GOTO/GOSUB targets
|
|
14
|
+
const targets = new Set();
|
|
15
|
+
structure.references.forEach((ref) => {
|
|
16
|
+
targets.add(ref.target);
|
|
17
|
+
});
|
|
18
|
+
// Parse lines
|
|
19
|
+
const lines = parseLines(basicCode, structure);
|
|
20
|
+
if (!lines.length) {
|
|
21
|
+
return basicCode;
|
|
22
|
+
}
|
|
23
|
+
// Merge lines
|
|
24
|
+
const { merged, mapping } = mergeLines(lines, targets, maxLength);
|
|
25
|
+
// Update line references
|
|
26
|
+
const packed = merged.map(entry => ({
|
|
27
|
+
lineNumber: entry.oldNumber,
|
|
28
|
+
code: updateLineReferences(entry.code, mapping),
|
|
29
|
+
}));
|
|
30
|
+
// Format output
|
|
31
|
+
const result = packed.map(entry => {
|
|
32
|
+
const code = entry.code.trimEnd();
|
|
33
|
+
return code ? `${entry.lineNumber} ${code}` : entry.lineNumber.toString();
|
|
34
|
+
});
|
|
35
|
+
return result.join('\n');
|
|
36
|
+
}
|
|
37
|
+
function parseLines(basicCode, structure) {
|
|
38
|
+
const lines = [];
|
|
39
|
+
const sourceLines = basicCode.split(/\r?\n/);
|
|
40
|
+
for (const info of structure.numberedLines) {
|
|
41
|
+
const lineText = sourceLines[info.line];
|
|
42
|
+
if (!lineText)
|
|
43
|
+
continue;
|
|
44
|
+
const commentStart = findCommentStart(lineText);
|
|
45
|
+
const code = commentStart === undefined
|
|
46
|
+
? lineText
|
|
47
|
+
: lineText.slice(0, commentStart);
|
|
48
|
+
const body = code.slice(info.numberEnd).trim();
|
|
49
|
+
if (body) {
|
|
50
|
+
lines.push({
|
|
51
|
+
lineNumber: info.number,
|
|
52
|
+
code: body,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return lines;
|
|
57
|
+
}
|
|
58
|
+
function mergeLines(lines, targets, maxLength) {
|
|
59
|
+
const merged = [];
|
|
60
|
+
const mapping = new Map();
|
|
61
|
+
let currentStart;
|
|
62
|
+
let currentSegments = [];
|
|
63
|
+
const flush = () => {
|
|
64
|
+
if (currentStart !== undefined && currentSegments.length > 0) {
|
|
65
|
+
merged.push({
|
|
66
|
+
oldNumber: currentStart,
|
|
67
|
+
code: joinSegments(currentSegments),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
currentStart = undefined;
|
|
71
|
+
currentSegments = [];
|
|
72
|
+
};
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
const code = line.code.trim();
|
|
75
|
+
if (!code) {
|
|
76
|
+
flush();
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const isTarget = targets.has(line.lineNumber);
|
|
80
|
+
// Start a new merged line if:
|
|
81
|
+
// - This is the first line
|
|
82
|
+
// - This line is a GOTO/GOSUB target
|
|
83
|
+
// - No current line being built
|
|
84
|
+
if (currentStart === undefined || isTarget) {
|
|
85
|
+
flush();
|
|
86
|
+
currentStart = line.lineNumber;
|
|
87
|
+
currentSegments = [code];
|
|
88
|
+
mapping.set(line.lineNumber, currentStart);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// Try to add to current merged line
|
|
92
|
+
mapping.set(line.lineNumber, currentStart);
|
|
93
|
+
const candidate = joinSegments([...currentSegments, code]);
|
|
94
|
+
// Calculate allowed length (accounting for line number + space + tokenized header)
|
|
95
|
+
const allowed = maxLength - currentStart.toString().length - 1 - 5;
|
|
96
|
+
if (allowed > 0 && candidate.length <= allowed) {
|
|
97
|
+
currentSegments.push(code);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Too long - flush and start new line
|
|
101
|
+
flush();
|
|
102
|
+
currentStart = line.lineNumber;
|
|
103
|
+
currentSegments = [code];
|
|
104
|
+
mapping.set(line.lineNumber, currentStart);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
flush();
|
|
108
|
+
return { merged, mapping };
|
|
109
|
+
}
|
|
110
|
+
function joinSegments(segments) {
|
|
111
|
+
if (segments.length <= 1) {
|
|
112
|
+
return segments[0] ?? '';
|
|
113
|
+
}
|
|
114
|
+
const parts = [];
|
|
115
|
+
for (let i = 0; i < segments.length; i++) {
|
|
116
|
+
if (i === 0) {
|
|
117
|
+
parts.push(segments[i]);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const prev = segments[i - 1];
|
|
121
|
+
const needsQuote = segmentNeedsClosingQuote(prev);
|
|
122
|
+
const trimmedPrev = prev.trimEnd();
|
|
123
|
+
let separator = ':';
|
|
124
|
+
if (trimmedPrev.endsWith(':')) {
|
|
125
|
+
separator = '';
|
|
126
|
+
}
|
|
127
|
+
else if (prev.toUpperCase().startsWith('DATA')) {
|
|
128
|
+
separator = ' :';
|
|
129
|
+
}
|
|
130
|
+
if (needsQuote) {
|
|
131
|
+
parts.push(`"${separator}`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
parts.push(separator);
|
|
135
|
+
}
|
|
136
|
+
parts.push(segments[i]);
|
|
137
|
+
}
|
|
138
|
+
return parts.join('');
|
|
139
|
+
}
|
|
140
|
+
function segmentNeedsClosingQuote(segment) {
|
|
141
|
+
let inString = false;
|
|
142
|
+
for (let i = 0; i < segment.length; i++) {
|
|
143
|
+
const char = segment[i];
|
|
144
|
+
if (char === '"') {
|
|
145
|
+
if (segment[i + 1] === '"') {
|
|
146
|
+
i += 1;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
inString = !inString;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (!inString && char === "'") {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return inString;
|
|
157
|
+
}
|
|
158
|
+
function updateLineReferences(code, mapping) {
|
|
159
|
+
// Find all line number references and update them
|
|
160
|
+
let result = code;
|
|
161
|
+
const references = [];
|
|
162
|
+
// Pattern to find line number references
|
|
163
|
+
const patterns = [
|
|
164
|
+
/\b(GOTO|GOSUB|THEN|ELSE|RUN|RESUME|RESTORE)\s+(\d+)/gi,
|
|
165
|
+
/\bON\s+[^:]*?(GOTO|GOSUB)\s+([0-9][0-9,\s]*)/gi,
|
|
166
|
+
];
|
|
167
|
+
for (const pattern of patterns) {
|
|
168
|
+
pattern.lastIndex = 0;
|
|
169
|
+
let match;
|
|
170
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
171
|
+
const digitsStr = match[2] || match[1];
|
|
172
|
+
if (!digitsStr)
|
|
173
|
+
continue;
|
|
174
|
+
// Handle comma-separated lists (ON...GOTO/GOSUB)
|
|
175
|
+
if (digitsStr.includes(',')) {
|
|
176
|
+
const numbers = digitsStr.split(',').map(s => s.trim());
|
|
177
|
+
for (const numStr of numbers) {
|
|
178
|
+
const oldNum = parseInt(numStr, 10);
|
|
179
|
+
if (!isNaN(oldNum)) {
|
|
180
|
+
const newNum = mapping.get(oldNum);
|
|
181
|
+
if (newNum !== undefined && newNum !== oldNum) {
|
|
182
|
+
const idx = code.indexOf(numStr, match.index);
|
|
183
|
+
if (idx >= 0) {
|
|
184
|
+
references.push({
|
|
185
|
+
start: idx,
|
|
186
|
+
end: idx + numStr.length,
|
|
187
|
+
oldNum,
|
|
188
|
+
newNum,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const oldNum = parseInt(digitsStr, 10);
|
|
197
|
+
if (!isNaN(oldNum)) {
|
|
198
|
+
const newNum = mapping.get(oldNum);
|
|
199
|
+
if (newNum !== undefined && newNum !== oldNum) {
|
|
200
|
+
const offset = match[0].lastIndexOf(digitsStr);
|
|
201
|
+
references.push({
|
|
202
|
+
start: match.index + offset,
|
|
203
|
+
end: match.index + offset + digitsStr.length,
|
|
204
|
+
oldNum,
|
|
205
|
+
newNum,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Apply replacements (in reverse order to maintain positions)
|
|
213
|
+
references.sort((a, b) => b.start - a.start);
|
|
214
|
+
for (const ref of references) {
|
|
215
|
+
result =
|
|
216
|
+
result.slice(0, ref.start) +
|
|
217
|
+
ref.newNum.toString() +
|
|
218
|
+
result.slice(ref.end);
|
|
219
|
+
}
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Pack a BASIC file (file-based API).
|
|
224
|
+
*
|
|
225
|
+
* @param inPath - Path to input .DO file
|
|
226
|
+
* @param options - Packing options
|
|
227
|
+
* @returns Result with output path and stats
|
|
228
|
+
*/
|
|
229
|
+
//# sourceMappingURL=pack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack.js","sourceRoot":"","sources":["../../src/pack/pack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove all comments from BASIC code while preserving line structure.
|
|
3
|
+
* Lines that are referenced (GOTO/GOSUB targets) are kept even if empty.
|
|
4
|
+
*
|
|
5
|
+
* @param basicCode - BASIC source code with line numbers
|
|
6
|
+
* @returns Code with comments removed
|
|
7
|
+
*/
|
|
8
|
+
export declare function removeComments(basicCode: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Remove comments from a .DO file (file-based API).
|
|
11
|
+
*
|
|
12
|
+
* @param inPath - Path to input .DO file
|
|
13
|
+
* @param options - Options including output path
|
|
14
|
+
* @returns Result with output path and line count
|
|
15
|
+
*/
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { findCommentStart } from '../commentUtils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a single BASIC line into line number and code.
|
|
4
|
+
*/
|
|
5
|
+
function parseLine(line) {
|
|
6
|
+
const trimmed = line.trim();
|
|
7
|
+
if (!trimmed) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const match = /^(\d+)\s*(.*)$/.exec(trimmed);
|
|
11
|
+
if (!match) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const lineNumber = parseInt(match[1], 10);
|
|
15
|
+
const code = match[2] || '';
|
|
16
|
+
return { lineNumber, code };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Collect all line references (GOTO, GOSUB targets) from BASIC code.
|
|
20
|
+
*/
|
|
21
|
+
function collectLineReferences(basicCode) {
|
|
22
|
+
const references = new Set();
|
|
23
|
+
const lines = basicCode.split(/\r?\n/);
|
|
24
|
+
// Keywords that reference line numbers
|
|
25
|
+
const referenceKeywords = [
|
|
26
|
+
'GOTO', 'GOSUB', 'THEN', 'ELSE', 'RUN', 'RESUME', 'RESTORE',
|
|
27
|
+
'ON.*GOTO', 'ON.*GOSUB'
|
|
28
|
+
];
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
const commentStart = findCommentStart(line);
|
|
31
|
+
const codeOnly = commentStart === undefined ? line : line.slice(0, commentStart);
|
|
32
|
+
const upper = codeOnly.toUpperCase();
|
|
33
|
+
// Simple line reference detection
|
|
34
|
+
for (const keyword of referenceKeywords) {
|
|
35
|
+
const regex = new RegExp(`\\b${keyword}\\s+(\\d+)`, 'gi');
|
|
36
|
+
let match;
|
|
37
|
+
while ((match = regex.exec(upper)) !== null) {
|
|
38
|
+
const target = parseInt(match[1], 10);
|
|
39
|
+
if (!isNaN(target)) {
|
|
40
|
+
references.add(target);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Handle comma-separated targets (ON...GOTO/GOSUB)
|
|
45
|
+
const onMatch = /\bON\s+.+?\s+(GOTO|GOSUB)\s+([\d,\s]+)/gi.exec(upper);
|
|
46
|
+
if (onMatch) {
|
|
47
|
+
const targets = onMatch[2].split(',');
|
|
48
|
+
for (const target of targets) {
|
|
49
|
+
const num = parseInt(target.trim(), 10);
|
|
50
|
+
if (!isNaN(num)) {
|
|
51
|
+
references.add(num);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return references;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Remove all comments from BASIC code while preserving line structure.
|
|
60
|
+
* Lines that are referenced (GOTO/GOSUB targets) are kept even if empty.
|
|
61
|
+
*
|
|
62
|
+
* @param basicCode - BASIC source code with line numbers
|
|
63
|
+
* @returns Code with comments removed
|
|
64
|
+
*/
|
|
65
|
+
export function removeComments(basicCode) {
|
|
66
|
+
const lines = basicCode.split(/\r?\n/);
|
|
67
|
+
const targets = collectLineReferences(basicCode);
|
|
68
|
+
const resultLines = [];
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
const parsed = parseLine(line);
|
|
71
|
+
if (!parsed) {
|
|
72
|
+
continue; // Skip blank/invalid lines
|
|
73
|
+
}
|
|
74
|
+
const { lineNumber, code } = parsed;
|
|
75
|
+
// Find comment start in the code
|
|
76
|
+
const commentStart = findCommentStart(code);
|
|
77
|
+
const codeNoComment = commentStart === undefined ? code : code.slice(0, commentStart);
|
|
78
|
+
// Remove trailing colons and whitespace left after stripping comment
|
|
79
|
+
const codeTrimmed = codeNoComment.replace(/[\s:]+$/g, '');
|
|
80
|
+
// If line becomes empty, keep it only if it's a GOTO/GOSUB target
|
|
81
|
+
if (!codeTrimmed.trim()) {
|
|
82
|
+
if (targets.has(lineNumber)) {
|
|
83
|
+
resultLines.push(lineNumber.toString());
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
resultLines.push(`${lineNumber} ${codeTrimmed.trim()}`);
|
|
88
|
+
}
|
|
89
|
+
return resultLines.join('\n');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Remove comments from a .DO file (file-based API).
|
|
93
|
+
*
|
|
94
|
+
* @param inPath - Path to input .DO file
|
|
95
|
+
* @param options - Options including output path
|
|
96
|
+
* @returns Result with output path and line count
|
|
97
|
+
*/
|
|
98
|
+
//# sourceMappingURL=removeComments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"removeComments.js","sourceRoot":"","sources":["../../src/removeComments/removeComments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TRS-80 Model 100 BASIC Renumber - Core Logic
|
|
3
|
+
*
|
|
4
|
+
* Renumbers BASIC program lines and updates all line references.
|
|
5
|
+
*/
|
|
6
|
+
export interface RenumberOptions {
|
|
7
|
+
start?: number;
|
|
8
|
+
increment?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface RenumberResult {
|
|
11
|
+
outPath: string;
|
|
12
|
+
linesRenumbered: number;
|
|
13
|
+
referencesUpdated: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Renumber BASIC program lines.
|
|
17
|
+
*
|
|
18
|
+
* @param basicCode - BASIC source code
|
|
19
|
+
* @param options - Renumbering options
|
|
20
|
+
* @returns Renumbered code
|
|
21
|
+
*/
|
|
22
|
+
export declare function renumberProgram(basicCode: string, options?: RenumberOptions): string;
|
|
23
|
+
/**
|
|
24
|
+
* Renumber a BASIC file (file-based API).
|
|
25
|
+
*
|
|
26
|
+
* @param inPath - Path to input .DO file
|
|
27
|
+
* @param options - Renumbering options
|
|
28
|
+
* @returns Result with output path and stats
|
|
29
|
+
*/
|