From 141f5bce6b90a6b1bb989f70575f38dfd327d2e9 Mon Sep 17 00:00:00 2001 From: cheton Date: Sun, 8 Dec 2024 21:08:03 +0800 Subject: [PATCH] feat: exports `stripComments` --- src/__tests__/index.test.js | 6 +- src/index.js | 152 ++++++++++++++++++------------------ 2 files changed, 81 insertions(+), 77 deletions(-) diff --git a/src/__tests__/index.test.js b/src/__tests__/index.test.js index ad386a3..5d37d98 100644 --- a/src/__tests__/index.test.js +++ b/src/__tests__/index.test.js @@ -6,7 +6,8 @@ import { parseString, parseStringSync, parseFile, - parseFileSync + parseFileSync, + stripComments, } from '..'; describe('Pass a null value as the first argument', () => { @@ -148,6 +149,7 @@ describe('Commands', () => { describe('Stripping comments', () => { it('should correctly parse a semicolon comment before parentheses', () => { const line = 'M6 ; comment (tool change) T1'; + expect(stripComments(line)).toBe('M6'); const data = parseLine(line, { lineMode: 'stripped' }); expect(data.line).toBe('M6'); expect(data.comments).toEqual([ @@ -157,6 +159,7 @@ describe('Stripping comments', () => { it('should correctly parse nested parentheses containing a semicolon', () => { const line = 'M6 (outer (inner;)) T1 ; comment'; + expect(stripComments(line)).toBe('M6 T1'); const data = parseLine(line, { lineMode: 'stripped' }); expect(data.line).toBe('M6 T1'); expect(data.comments).toEqual([ @@ -167,6 +170,7 @@ describe('Stripping comments', () => { it('should correctly parse multiple comments in a line', () => { const line = 'M6 (first comment) T1 ; second comment'; + expect(stripComments(line)).toBe('M6 T1'); const data = parseLine(line, { lineMode: 'stripped' }); expect(data.line).toBe('M6 T1'); expect(data.comments).toEqual([ diff --git a/src/index.js b/src/index.js index cb48bfd..a02b8f6 100644 --- a/src/index.js +++ b/src/index.js @@ -60,87 +60,86 @@ const iterateArray = (arr = [], opts = {}, iteratee = noop, done = noop) => { loop(); }; -// @param {string} line The G-code line -const parseLine = (() => { - // http://reprap.org/wiki/G-code#Special_fields - // The checksum "cs" for a GCode string "cmd" (including its line number) is computed - // by exor-ing the bytes in the string up to and not including the * character. - const computeChecksum = (s) => { - s = s || ''; - if (s.lastIndexOf('*') >= 0) { - s = s.substr(0, s.lastIndexOf('*')); - } +// http://reprap.org/wiki/G-code#Special_fields +// The checksum "cs" for a GCode string "cmd" (including its line number) is computed +// by exor-ing the bytes in the string up to and not including the * character. +const computeChecksum = (s) => { + s = s || ''; + if (s.lastIndexOf('*') >= 0) { + s = s.substr(0, s.lastIndexOf('*')); + } - let cs = 0; - for (let i = 0; i < s.length; ++i) { - const c = s[i].charCodeAt(0); - cs ^= c; - } - return cs; - }; + let cs = 0; + for (let i = 0; i < s.length; ++i) { + const c = s[i].charCodeAt(0); + cs ^= c; + } + return cs; +}; + +// Strips comments from a G-code line and returns stripped line and comments. +const stripCommentsEx = (line) => { // http://linuxcnc.org/docs/html/gcode/overview.html#gcode:comments - // Comments can be embedded in a line using parentheses () or for the remainder of a lineusing a semi-colon. - // The semi-colon is not treated as the start of a comment when enclosed in parentheses. - const stripComments = (() => { - const _stripComments = (line) => { - let result = ''; - let currentComment = ''; - let comments = []; - let openParens = 0; - - // Detect semicolon comments before parentheses - for (let i = 0; i < line.length; i++) { - const char = line[i]; - - if (char === ';' && openParens === 0) { - // Start semicolon comment outside parentheses - comments.push(line.slice(i + 1).trim()); - openParens = 0; // Reset parentheses counter - break; // Stop further processing after a semicolon comment - } - - if (char === '(') { - // Start parentheses comment - if (openParens === 0) { - currentComment = ''; - } else if (openParens > 0) { - currentComment += char; - } - openParens = Math.min(openParens + 1, Number.MAX_SAFE_INTEGER); - } else if (char === ')') { - // End parentheses comment - openParens = Math.max(0, openParens - 1); - if (openParens === 0) { - comments.push(currentComment.trim()); - currentComment = ''; - } else if (openParens > 0) { - currentComment += char; - } - } else if (openParens > 0) { - // Inside parentheses comment - currentComment += char; - } else { - // Normal text outside comments - result += char; - } + // Comments can be included in a line using either parentheses "()" or a semicolon ";" to mark the rest of the line. + // If a semicolon is enclosed within parentheses, it is not interpreted as the start of a comment. + let strippedLine = ''; + let currentComment = ''; + let comments = []; + let openParens = 0; + + // Detect semicolon comments before parentheses + for (let i = 0; i < line.length; i++) { + const char = line[i]; + + if (char === ';' && openParens === 0) { + // Start semicolon comment outside parentheses + comments.push(line.slice(i + 1).trim()); + openParens = 0; // Reset parentheses counter + break; // Stop further processing after a semicolon comment + } + + if (char === '(') { + // Start parentheses comment + if (openParens === 0) { + currentComment = ''; + } else if (openParens > 0) { + currentComment += char; + } + openParens = Math.min(openParens + 1, Number.MAX_SAFE_INTEGER); + } else if (char === ')') { + // End parentheses comment + openParens = Math.max(0, openParens - 1); + if (openParens === 0) { + comments.push(currentComment.trim()); + currentComment = ''; + } else if (openParens > 0) { + currentComment += char; } + } else if (openParens > 0) { + // Inside parentheses comment + currentComment += char; + } else { + // Normal text outside comments + strippedLine += char; + } + } - result = result.trim(); - return [result, comments]; - }; + strippedLine = strippedLine.trim(); - return (line) => { - const [strippedLine, comments] = _stripComments(line); - return [strippedLine, comments]; - }; - })(); + return [strippedLine, comments]; +}; - const stripWhitespace = (line) => { - // Remove whitespace characters - const re = new RegExp(/\s+/g); - return line.replace(re, ''); - }; +// Returns the stripped line without comments. +const stripComments = (line) => stripCommentsEx(line)[0]; + +// Removes whitespace characters. +const stripWhitespace = (line) => { + const re = new RegExp(/\s+/g); + return line.replace(re, ''); +}; +// @param {string} line The G-code line +const parseLine = (() => { // eslint-disable-next-line no-useless-escape const re = /(%.*)|({.*)|((?:\$\$)|(?:\$[a-zA-Z0-9#]*))|([a-zA-Z][0-9\+\-\.]+)|(\*[0-9]+)/igm; @@ -159,7 +158,7 @@ const parseLine = (() => { let ln; // Line number let cs; // Checksum const originalLine = line; - const [strippedLine, comments] = stripComments(line); + const [strippedLine, comments] = stripCommentsEx(line); const compactLine = stripWhitespace(strippedLine); if (lineMode === 'compact') { @@ -418,5 +417,6 @@ export { parseLine, parseStream, parseString, - parseStringSync + parseStringSync, + stripComments, };