@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
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,5 @@
1
+ # @m100/core
2
+
3
+ Shared TRS-80 Model 100/200 BASIC logic used by the CLI and VS Code extension.
4
+
5
+ Exported helpers cover tokenization/detokenization, packing, squashing, renumbering, and comment utilities.
@@ -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"}
@@ -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
+ */