Class NcPatchUtil
- Namespace
- Hi.NcParsers.NcWriteback
- Assembly
- HiMech.dll
Positional text-edit primitives for NcPatchWriter (the patch-mode INcPieceConverter): anchored token edits on a verbatim BlockText draft.
This is the SoftNc dual of HardNcUtil.SetTagNumber/AddBySeq
with the two known defects fixed while keeping every other semantic:
- Edits splice by match index (
Remove/Insert) instead ofstring.Replace(m.Value, …), so a literal that repeats elsewhere in the text is never also rewritten (the positional-reverse convention of GrabTagValue(ref string, IEnumerable<string>, string, bool)). - Numbers are formatted with InvariantCulture (HardNc used the current culture).
Faithfully kept from HardNc: NaN deletes the token; an absent tag is
inserted at the conventional word position
(ConventionalTagOrder: X,Y,Z,I,J,K,A,B,C,F,S — X scans
forward for the first present successor and lands before it, other tags
scan backward for the nearest present predecessor and land after it,
otherwise the token is appended at the end of the code region);
formatted values trim trailing zeros but keep the decimal point
(10.0000 → 10.), which also guards the value against
no-decimal-point integer shrinking on replay.
Tag matching mirrors the parser's lexing exactly (GrabTagValue(ref string, IEnumerable<string>, string, bool): case-insensitive, RegexFlagPrefix guard, and the full value grammar number|variable|bracket-expression) so that patch and parse agree on what a token is. A tag whose value is a variable or bracket expression is refused, never rewritten.
public static class NcPatchUtil
- Inheritance
-
NcPatchUtil
- Inherited Members
Fields
ConventionalTagOrder
Conventional Fanuc-style word order used when inserting an absent tag
(mirrors HardNcUtil.AddBySeq).
public static readonly string[] ConventionalTagOrder
Field Value
- string[]
Properties
OmitLeadingZero
Process-wide leading-zero style for rewritten numbers below one:
false (the default) writes 0.348,
true writes .348. Posts that diff the
optimized program against its source compare text, so a program
authored in the bare-dot style comes back changed on every word below
one even when the value is identical — set this to
true to keep the source's spelling.
The default is the frozen HardNc writeback convention, which the Soft-vs-Hard byte parity gates assume; changing it changes the output bytes of BOTH optimization legs. Ask ScanOmitsLeadingZero(IEnumerable<string>, Func<string, IReadOnlyList<(int Start, int Length)>>) what the source program itself does rather than guessing.
Not a knob a caller sets by hand in production: both optimization legs
assign it on every output pass from the project's
NcOptOption.OmitLeadingZero, so the spelling travels with the
.hincproj and the same project produces the same bytes on every
machine. Direct assignment is for tests and one-off tooling.
public static bool OmitLeadingZero { get; set; }
Property Value
Methods
FormatTagNumber(double, string)
Formats a tag number per the HardNc writeback convention:
InvariantCulture, trailing zeros after the
decimal point trimmed while the dot itself is kept
(-50.2500 → -50.25, 10.0000 → 10.).
Shared by the patch primitives and FanucNcSentenceComposer.
OmitLeadingZero additionally drops the single zero left
of the point (0.348 → .348) for values below one. Zero
itself keeps its digit: 0. stays, because a lone . does
not read as a number.
public static string FormatTagNumber(double value, string format)
Parameters
Returns
GetTagNumber(string, string, string, IReadOnlyList<(int Start, int Length)>, IReadOnlyList<string>)
Reads the numeric literal of the first unprotected token of
tag in blockText; NaN when the
tag is absent or carries a variable/bracket/keyword value. Uses
exactly the value grammar of SetTagNumber(ref string, string, double, string, string, IReadOnlyList<(int Start, int Length)>, IReadOnlyList<string>, bool, bool, IReadOnlyList<string>)
(TagValuePattern), so a read and a subsequent edit agree on
the token. The literal is parsed raw
(InvariantCulture) — no dialect
integer-shrink scaling is applied, unlike the parser's
SoftNcUtil.GrabDouble(…, enableIntegerShrink); splition-plan
callers read decimal-pointed arc words (I/J/K/R) where the raw
literal equals the parsed value.
public static double GetTagNumber(string blockText, string tag, string varPrefix = "#", IReadOnlyList<(int Start, int Length)> protectedSpans = null, IReadOnlyList<string> keywordValues = null)
Parameters
blockTextstringNC block text to read; null counts as empty.
tagstringTag to look for, e.g.
“I”.varPrefixstringVariable index prefix of the dialect.
protectedSpansIReadOnlyList<(int Start, int Length)>Comment spans to skip.
keywordValuesIReadOnlyList<string>Dialect keywords that can stand where the tag's number would be; null for dialects without keyword values.
Returns
IsCarriedSection(JsonObject, string)
True when the piece's section was injected by the pipeline
(AddedByKey present: a
ModalCarrySyntax clone or a backfill) rather than authored from
the block's own text. A carried section's token is guaranteed absent
from BlockText — a patch must not anchor on it,
and a strip is a no-op.
One-way signal only: an AddedBy-less section can still lack a literal
token (modal-lookback syntaxes such as FeedrateSyntax re-author
sections without stamping). The final authority on literal presence is
the anchored text match itself.
public static bool IsCarriedSection(JsonObject json, string sectionKey)
Parameters
jsonJsonObjectsectionKeystring
Returns
ProbeTagValue(string, string, string, IReadOnlyList<(int Start, int Length)>, IReadOnlyList<string>)
Probes what the first unprotected literal token of tag
carries in blockText, using exactly the value grammar
(TagValuePattern: number | variable/bracket expression | dialect
keyword) that SetTagNumber(ref string, string, double, string, string, IReadOnlyList<(int Start, int Length)>, IReadOnlyList<string>, bool, bool, IReadOnlyList<string>) matches against — so a probe
and a subsequent edit can never disagree on token presence or
editability.
public static NcTagValueKind ProbeTagValue(string blockText, string tag, string varPrefix = "#", IReadOnlyList<(int Start, int Length)> protectedSpans = null, IReadOnlyList<string> keywordValues = null)
Parameters
blockTextstringNC block text to probe; null counts as empty.
tagstringTag to look for, e.g.
“F”.varPrefixstringVariable index prefix of the dialect (Fanuc
#, HeidenhainQ).protectedSpansIReadOnlyList<(int Start, int Length)>Comment spans to skip (see GetFanucCommentSpans(string)).
keywordValuesIReadOnlyList<string>Dialect keywords that can stand where the tag's number would be; null for dialects without keyword values.
Returns
RemoveFlag(ref string, string, IReadOnlyList<(int Start, int Length)>)
Removes the first whole-token occurrence of flagRegex
from blockText by positional splice, absorbing one
adjacent separator space. Matching is case-insensitive with the
RegexFlagPrefix prefix guard and a
(?![0-9]) tail guard — the numbered-flag lexing: the token ends
at its digits, so glued words match (G01X1.) while a longer
number does not (G1 never bites G17). Dotted variants
are the caller's responsibility (strip G43.4 before
G43).
This is the text half of modal-token stripping (one line split into many keeps the modal G word only on the first fragment); use IsCarriedSection(JsonObject, string) first — a carried section's token is not in the source text at all, so no removal should be attempted.
public static bool RemoveFlag(ref string blockText, string flagRegex, IReadOnlyList<(int Start, int Length)> protectedSpans = null)
Parameters
Returns
- bool
True when a token was found and removed.
RemoveFlagTrimmingTail(ref string, string, IReadOnlyList<(int Start, int Length)>)
Removes the first whole-token occurrence of flagRegex
with the HardNc splition byte shape: the token span is cut out and the
REMAINDER after it is TrimStart()ed — the exact splice of the
oracle's RebuildFlagSyntaxToNcLine (frozen baseline,
NcOptProc), which differs from
RemoveFlag(ref string, string, IReadOnlyList<(int Start, int Length)>) when the token sits at the end of the code
region (the oracle keeps the preceding space; the absorbing removal
would eat it). The tail guard here is (?![0-9.]) — one
character stronger than the oracle's guard-less regex — so a plain
name never bites its dotted variant (G54 vs G54.1);
the oracle avoids those bites through its flag-state gate instead.
public static bool RemoveFlagTrimmingTail(ref string blockText, string flagRegex, IReadOnlyList<(int Start, int Length)> protectedSpans = null)
Parameters
blockTextstringNC block text to edit.
flagRegexstringEscaped literal flag token, e.g.
“G94”.protectedSpansIReadOnlyList<(int Start, int Length)>Comment spans to skip.
Returns
- bool
True when a token was found and removed.
RemoveFlagTrimmingTail(ref string, string, out int, IReadOnlyList<(int Start, int Length)>)
RemoveFlagTrimmingTail(ref string, string, IReadOnlyList<(int Start, int Length)>) that also reports where the token was cut: the index the trimmed remainder now starts at, which is where a parameter scope opened by that token begins (see RemoveScopedParamWordTrimmingTail(ref string, int, string, string, IReadOnlyList<(int Start, int Length)>)).
public static bool RemoveFlagTrimmingTail(ref string blockText, string flagRegex, out int removedAt, IReadOnlyList<(int Start, int Length)> protectedSpans = null)
Parameters
blockTextstringNC block text to edit.
flagRegexstringEscaped literal flag token, e.g.
“G94”.removedAtintIndex the token was cut at; -1 when nothing was removed.
protectedSpansIReadOnlyList<(int Start, int Length)>Comment spans to skip.
Returns
- bool
True when a token was found and removed.
RemoveScopedParamWordTrimmingTail(ref string, int, string, string, IReadOnlyList<(int Start, int Length)>)
Removes the first paramTag word inside the
parameter scope that opens at scopeStart and closes
at the next G or M word — the scope a parameterized flag such as
G54.1 Pn (or its G54 Pn alias) reads its parameters
from: ParameterizedFlagSyntax grabs the
word anywhere after the code, up to its G/M terminate
words, so G54 X0. P48 selects P48 as much as G54 P48 X0.
does, while a dwell's G04 P100 or a call's M98 P1000
after the token is out of scope. The word is cut with the
RemoveFlagTrimmingTail(ref string, string, IReadOnlyList<(int Start, int Length)>)
splice (cut the span, TrimStart() the remainder). The value
grammar is the patch grammar (number, variable, bracket expression),
so a bare letter is not a word. Used by the splition modal strip to
take the index word of an additional work coordinate selection out
together with its G54-family token — a fragment that kept the P alone
re-parses it as an unconsumed word.
public static bool RemoveScopedParamWordTrimmingTail(ref string blockText, int scopeStart, string paramTag, string varPrefix, IReadOnlyList<(int Start, int Length)> protectedSpans = null)
Parameters
blockTextstringNC block text to edit.
scopeStartintIndex where the scope opens — where the stripped flag token stood.
paramTagstringParameter tag, e.g.
“P”.varPrefixstringVariable index prefix of the dialect (Fanuc
#, HeidenhainQ).protectedSpansIReadOnlyList<(int Start, int Length)>Comment spans to skip.
Returns
- bool
True when a word was found and removed.
SetTagNumber(ref string, string, double, string, string, IReadOnlyList<(int Start, int Length)>, IReadOnlyList<string>, bool, bool, IReadOnlyList<string>)
Sets, replaces, or deletes a single tag value token in
blockText by positional splice.
Dual of HardNcUtil.SetTagNumber: a found tag is rewritten in
place (NaN removes the whole token together with one adjacent
separator space); an absent tag with a non-NaN value is inserted at
its conventional word position. The original tag letter casing is
preserved on replace; only the addressed token's span changes, so an
edit to F can never diff anything but F.
public static NcPatchTagResult SetTagNumber(ref string blockText, string tag, double tagNumber, string tagNumberFormat, string varPrefix = "#", IReadOnlyList<(int Start, int Length)> protectedSpans = null, IReadOnlyList<string> keywordValues = null, bool absorbSeparatorSpaceOnRemove = true, bool rewriteEqualLiteral = false, IReadOnlyList<string> preFeedWordPatterns = null)
Parameters
blockTextstringNC block text to edit (verbatim draft).
tagstringTag to address, e.g.
“F”,“X”.tagNumberdoubleNew value; NaN deletes the token.
tagNumberFormatstringNumeric format, e.g.
“F2”/“F4”(see FormatTagNumber(double, string)).varPrefixstringVariable index prefix of the dialect (Fanuc
#, HeidenhainQ) — used to recognize, and refuse, variable values.protectedSpansIReadOnlyList<(int Start, int Length)>Comment spans to skip; matches and insertions never land inside them (see GetFanucCommentSpans(string)).
keywordValuesIReadOnlyList<string>Dialect keywords that can stand where the tag's number would be (e.g. Heidenhain
MAX/AUTOafterF); a keyword-valued tag is refused, never treated as absent.absorbSeparatorSpaceOnRemoveboolWhether a NaN-value removal also absorbs one adjacent separator space (the default). Pass false for the HardNc splition-fragment byte shape: the oracle's
HardNcUtil.SetTagNumberdeletes withstring.Replace(match, ""), leaving both separators in place — e.g. deleting the trailing F of"… Z-1.9 F4000."keeps the space, so the embedded-log note lands as"… Z-1. (src(…))"with a doubled space, which the fragment rewrite must reproduce byte-for-byte.rewriteEqualLiteralboolWhether to rewrite a literal that already means the requested value, re-spelling it in canonical format instead of keeping the original bytes (the default keeps them). The HardNc oracle always rewrites (
HardNcUtil.SetTagNumber), so the splition fragment path passes true for byte parity — e.g. a sourceI0.0under an unchanged center re-spells asI0.in the reference output, which the byte-stability shortcut would otherwise keep asI0.0.preFeedWordPatternsIReadOnlyList<string>Regex patterns of the dialect's bare words that precede the F word (Heidenhain klartext
DR±,RL/RR/R0; see PreFeedWordPatterns). When an absent F is inserted and one of them is present, the token lands after the rightmost of those words and of the conventional predecessor words instead of after the nearest conventional predecessor alone. Null (the default) keeps the conventional placement byte-for-byte.