AuxPreprocessor: ignore M-codes inside paren comments
Two bugs surfaced when macros got prose like:
(Composed from atoms: M102 = RELEASE (V1 on), M103 = CLAMP)
1. _ATC_M_RE.finditer was being run against the raw line, so the
M102/M103 *inside* the comment fired spurious release/clamp
hooks at file load.
2. The simple _PAREN_COMMENT_RE = re.compile(r'\(\[^)]*\)') is a
greedy non-nested match, so a header with a nested paren
(e.g. 'M102 = RELEASE (V1 on)') only stripped the inner
paren, leaving the trailing 'M103 = CLAMP)' visible to the
matcher.
Fix:
- Add _strip_comments() that walks the line tracking paren depth
and drops the trailing semicolon comment. Handles nested parens
correctly.
- Run _ATC_M_RE.finditer against the comment-stripped 'code'
instead of the raw line, so prose mentions are inert.
- Drop the original line's comments from the rewritten output;
keeping them around led to the M-codes being matched twice
(once stripped, once still in the trailing comment).
- Use _strip_comments in file_uses_aux too.
The grab.nc and drop.nc macros on the controller already had the
prose headers; they now preprocess correctly to clean
release / G4 / clamp and release / N x eject / Z0 / clamp
sequences.
This commit is contained in:
@@ -46,8 +46,42 @@ import tempfile
|
|||||||
|
|
||||||
|
|
||||||
# Strip line comments so we don't get fooled by "(M100 not really)".
|
# Strip line comments so we don't get fooled by "(M100 not really)".
|
||||||
|
# Note this is a simple regex and doesn't handle nested parentheses
|
||||||
|
# - which actually occur in real macro headers like
|
||||||
|
# `(Composed from atoms: M102 = RELEASE (V1 on), M103 = CLAMP)`.
|
||||||
|
# Use _strip_comments() below for a parser that does handle them.
|
||||||
_PAREN_COMMENT_RE = re.compile(r'\([^)]*\)')
|
_PAREN_COMMENT_RE = re.compile(r'\([^)]*\)')
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_comments(line):
|
||||||
|
"""Return `line` with paren comments and the trailing semicolon
|
||||||
|
comment removed. Handles arbitrarily nested parentheses (RS274
|
||||||
|
technically forbids them but real-world gcode comments often
|
||||||
|
contain prose with parens, e.g. `(M102 = RELEASE (V1 on))`).
|
||||||
|
|
||||||
|
Returns just the executable code, with the original whitespace
|
||||||
|
preserved between tokens."""
|
||||||
|
out = []
|
||||||
|
depth = 0
|
||||||
|
i = 0
|
||||||
|
n = len(line)
|
||||||
|
while i < n:
|
||||||
|
c = line[i]
|
||||||
|
if c == ';' and depth == 0:
|
||||||
|
break
|
||||||
|
if c == '(':
|
||||||
|
depth += 1
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
if c == ')':
|
||||||
|
if depth > 0: depth -= 1
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
if depth == 0:
|
||||||
|
out.append(c)
|
||||||
|
i += 1
|
||||||
|
return ''.join(out)
|
||||||
|
|
||||||
# ATC pneumatics M-codes mapped onto hook events. M101 is
|
# ATC pneumatics M-codes mapped onto hook events. M101 is
|
||||||
# deliberately unassigned (see header).
|
# deliberately unassigned (see header).
|
||||||
_ATC_M_CODES = {
|
_ATC_M_CODES = {
|
||||||
@@ -135,8 +169,7 @@ class AuxPreprocessor(object):
|
|||||||
try:
|
try:
|
||||||
with open(path, 'r', encoding='utf-8', errors='replace') as f:
|
with open(path, 'r', encoding='utf-8', errors='replace') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
code = _PAREN_COMMENT_RE.sub('', line)
|
code = _strip_comments(line)
|
||||||
code = code.split(';', 1)[0]
|
|
||||||
if _ATC_M_RE.search(code):
|
if _ATC_M_RE.search(code):
|
||||||
return True
|
return True
|
||||||
if couple_active:
|
if couple_active:
|
||||||
@@ -327,8 +360,7 @@ class AuxPreprocessor(object):
|
|||||||
line = raw.rstrip('\n')
|
line = raw.rstrip('\n')
|
||||||
|
|
||||||
# Comment-only or blank lines pass through verbatim.
|
# Comment-only or blank lines pass through verbatim.
|
||||||
code = _PAREN_COMMENT_RE.sub('', line)
|
code = _strip_comments(line)
|
||||||
code = code.split(';', 1)[0]
|
|
||||||
if not code.strip():
|
if not code.strip():
|
||||||
fout.write(raw)
|
fout.write(raw)
|
||||||
continue
|
continue
|
||||||
@@ -347,10 +379,14 @@ class AuxPreprocessor(object):
|
|||||||
if self._maybe_inject_a_down(code, fout):
|
if self._maybe_inject_a_down(code, fout):
|
||||||
rewrote_any = True
|
rewrote_any = True
|
||||||
|
|
||||||
# ATC M-codes (M100/M102/M103). Each ATC M-code on the line
|
# ATC M-codes (M100/M102/M103). Match against the
|
||||||
# is replaced with its (MSG,HOOK:<event>:) line and
|
# comment-stripped `code` so prose mentions like
|
||||||
# stripped from the residual.
|
# `(M102 = RELEASE)` inside a comment don't spuriously
|
||||||
atc_matches = list(_ATC_M_RE.finditer(line))
|
# fire hooks. Each match emits a (MSG,HOOK:<event>:)
|
||||||
|
# line; the M-code is stripped from the executable
|
||||||
|
# residual but the original line's comments are kept
|
||||||
|
# for log readability.
|
||||||
|
atc_matches = list(_ATC_M_RE.finditer(code))
|
||||||
if atc_matches:
|
if atc_matches:
|
||||||
rewrote_any = True
|
rewrote_any = True
|
||||||
for m in atc_matches:
|
for m in atc_matches:
|
||||||
@@ -359,18 +395,13 @@ class AuxPreprocessor(object):
|
|||||||
event = _ATC_M_CODES.get(num)
|
event = _ATC_M_CODES.get(num)
|
||||||
if event:
|
if event:
|
||||||
fout.write('(MSG,HOOK:%s:)\n' % event)
|
fout.write('(MSG,HOOK:%s:)\n' % event)
|
||||||
line = _ATC_M_RE.sub('', line)
|
code_stripped = _ATC_M_RE.sub('', code).strip()
|
||||||
code = _PAREN_COMMENT_RE.sub('', line)
|
if code_stripped:
|
||||||
code = code.split(';', 1)[0]
|
# Mixed line: keep the residual executable
|
||||||
if not code.strip():
|
# gcode. Drop the comments to keep the
|
||||||
# Nothing meaningful left; preserve any trailing
|
# rewritten file tidy (the original line's
|
||||||
# comment text but skip empty lines.
|
# text already appears once as the input).
|
||||||
rest = line.rstrip()
|
fout.write(code_stripped + '\n')
|
||||||
if rest:
|
|
||||||
fout.write(rest + '\n')
|
|
||||||
continue
|
|
||||||
# Other gcode remains on the line - emit it.
|
|
||||||
fout.write(line + '\n')
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# No rewrite needed.
|
# No rewrite needed.
|
||||||
|
|||||||
Reference in New Issue
Block a user