Table of Contents

Namespace Hi.NcParsers.LogicSyntaxs

Classes

BackBoringSyntax

G87 back boring cycle. Supports modal repetition. Cuts upward from Z to R — used to bore the back side of a workpiece.

Cycle sequence:

  1. Oriented spindle stop (OSS) at current position
  2. Rapid (shifted) to init position, then down to bottom Z — tool enters pre-drilled hole without contacting bore wall
  3. Shift back to hole center at bottom
  4. Spindle start (CW)
  5. Feed upward from Z to R-point (back boring cut)
  6. Oriented spindle stop at R
  7. Tool shift, rapid retract (shifted) to final Z
  8. Shift back to center, spindle restart

Q specifies the lateral shift distance (mm). Shift direction defaults to +X (OSS angle 0°).

Reads absolute coordinates from the cycle section, which is resolved by CannedCycleResolveSyntax before this syntax runs.

BoringCycleSyntax

G85/G86/G89 boring cycles.

Cycle sequence:

  1. Rapid to init position (target XY, previous Z)
  2. Rapid from init to R-point
  3. Feed from R-point to bottom Z
  4. [G86 only] Spindle stop at bottom
  5. [G89 only] Dwell P seconds at bottom
  6. Retract: G85/G89 → feed retract, G86 → rapid retract
  7. [G86 only] Spindle restart (CW) after retract

G85: feed to Z, feed retract — smooth bore finish. G86: feed to Z, spindle stop (implicit), rapid retract. G89: feed to Z, dwell P, feed retract — like G85 with bottom dwell.

Reads absolute coordinates from the cycle section, which is resolved by CannedCycleResolveSyntax (modal repetition, G91 conversion, missing-axis fallback) before this syntax runs. Must be placed after CannedCycleResolveSyntax and before IncrementalResolveSyntax in the syntax chain.

CannedCycleResolveSyntax

Resolves the canned-cycle Group-09 state for the current block and writes the result to the CannedCycle section.

  • Active cycle (direct G81..G89 or modal repeat): merges Parsing overrides with previous-cycle stored params, applies G91 incremental-to-absolute conversion and missing-axis fallback, writes CannedCycle with Term, ReturnMode, and Params. The resolved cycle sub-section is left in Parsing under the cycle code for downstream cycle syntaxes (DrillingCycleSyntax, etc.) to read.
  • Explicit cancel (G80 flag present on a non-cycle block): consumes the G80 flag and writes CannedCycle = { Term: "G80" }, acting as a hard sentinel for Hi.NcParsers.LogicSyntaxs.CannedCycleSyntaxUtil modal lookback.
  • No Group-09 activity: leaves the block untouched.

Must be placed after PositioningSyntax and before the individual cycle syntaxes in the chain.

CircularMotionSyntax

Writes McArc motion for circular commands (ISO G02/G03). Detects motion mode from Flags, reads I/J/K center offsets or R radius from Parsing, computes arc center in program coordinates, and writes a one-shot MotionEvent (form + arc params) plus a modal MotionState (Term).

G02/G03 mode is modal (Group 01) — persists across blocks via Term. Arc parameters (I/J/K/R) are per-block and must be present in every arc block.

Must be placed before LinearMotionSyntax in the syntax chain. Both share the Group 01 motion slot; whichever writes a MotionEvent first claims it.

CoolantSyntax

Consumes M07 (mist ON), M08 (flood ON), and M09 (coolant OFF) from Flags and writes the ICoolantDef section with both IsOn (convenience flag) and Mode (abstract mode name: Flood / Mist / Off). Modal — persists via backward lookback.

CoordinateOffsetUtil

Shared utilities for all coordinate offset syntaxes (ISO, Siemens, Heidenhain). Handles section IO, backward lookback, and ProgramToMcTransform composition.

DrillingCycleSyntax

G81/G82 drilling cycle (rapid retract). Supports modal repetition. G82 covers G81 — the only difference is an optional dwell (P) at the bottom.

Cycle sequence:

  1. Rapid to init position (target XY, previous Z)
  2. Rapid from init to R-point
  3. Feed from R-point to bottom Z
  4. [G82 only] Dwell P seconds at bottom
  5. Rapid from bottom to final (G98 → init Z, G99 → R)

Reads absolute coordinates from the cycle section, which is resolved by CannedCycleResolveSyntax (modal repetition, G91 conversion, missing-axis fallback) before this syntax runs. Must be placed after CannedCycleResolveSyntax and before IncrementalResolveSyntax in the syntax chain.

FanucPathSmoothingSyntax

Consumes Fanuc G05.1 (high-precision contour / AICC II / Nano Smoothing) and records the modal state in the PathSmoothing JSON section using the FanucPathSmoothing schema. Q1 enables, Q0 disables; the optional R{n} precision-level is preserved as Level. The simulation does not alter the tool path — this is a controller-internal interpolation black box; the captured state exists for bidirectional NC-text reconstruction.

Modal carry to subsequent blocks is handled by ModalCarrySyntax, which already tracks the PathSmoothing section key and deep-clones it forward.

FeedrateSyntax

Consumes F (feedrate) from Parsing and G94/G95 mode from Flags. Both are modal — persist across blocks via backward node lookback. Writes resolved state to a IFeedrateDef section.

FineBoringSyntax

G76 fine boring cycle. Supports modal repetition.

Cycle sequence:

  1. Rapid to init position (target XY, previous Z)
  2. Rapid from init to R-point
  3. Feed from R-point to bottom Z
  4. Oriented spindle stop (OSS)
  5. Tool shift by Q in +X direction (clear bore wall)
  6. Rapid retract (shifted) to final Z
  7. Tool shift back to center
  8. Spindle restart (CW)

Q specifies the lateral shift distance (mm) to avoid dragging the tool across the finished bore surface during retract. Shift direction defaults to +X (OSS angle 0°).

Reads absolute coordinates from the cycle section, which is resolved by CannedCycleResolveSyntax before this syntax runs.

G43p4RtcpSyntax

Handles G43.4 RTCP (Rotary Tool Center Point) activation. Writes the IToolHeightCompensationDef section and the ToolHeightCompensationSource entry in ProgramToMcTransform — a tool-normal · offset_mm translation at the block endpoint ABC. The chain entry is tagged KindDynamic when RTCP is active and ABC changes across the block, and KindStatic otherwise.

The RTCP kinematic rotary part (Pn→MC rigid transform) is orthogonal to this syntax and is written by PivotTransformationSyntax on every block, because rotary state remains in effect beyond the RTCP modal (e.g. a non-RTCP G01 after G49 still inherits the last ABC from the program).

The "rotary dynamic" distinction lives on the chain entry's KindKey alone and is read via HasDynamicEntry(JsonObject) by LinearMotionSyntax to pick ClLinear vs McLinear.

G43.4 is used by Fanuc, Mazak, Syntec, and Okuma. Siemens (TRAORI) and Heidenhain (M128) are handled by separate syntaxes. Must be placed after ToolHeightOffsetSyntax (to override the ToolHeightCompensation entry when RTCP is active) and before PivotTransformationSyntax (which runs last in the chain).

G53p1RotaryPositionSyntax

G53.1 — non-modal, one-shot rotary axis positioning. Positions the rotary axes (A/B/C) to align the physical tool axis with the active tilted work plane defined by G68.2. XYZ position is unchanged; only rotary axes move via rapid traverse.

Requires IsoG68p2TiltSyntax (or equivalent) to have written the tilt transform. Uses IMachineKinematics to solve for the target A/B/C via inverse kinematics.

Must be placed after IsoG68p2TiltSyntax (needs tilt data) and before ProgramXyzSyntax in the syntax chain. Writes A/B/C into MachineCoordinateState. Motion is handled by LinearMotionSyntax via modal G00/G01.

HighSpeedPeckCycleSyntax

G73 high-speed peck drilling cycle (chip breaking). Supports modal repetition. Drills in increments of depth Q, partially retracting by PeckRetractionDistance_mm between strokes (instead of fully back to R like PeckDrillingCycleSyntax).

Cycle sequence:

  1. Rapid to init position (target XY, previous Z)
  2. Rapid from init to R-point
  3. For each stroke: feed Q deeper, rapid retract by d
  4. If remainder exists: feed to bottom Z, rapid retract by d
  5. Rapid to final (G98 → init Z, G99 → R)

Reads absolute coordinates from the cycle section, which is resolved by CannedCycleResolveSyntax (modal repetition, G91 conversion, missing-axis fallback) before this syntax runs. Must be placed after CannedCycleResolveSyntax and before IncrementalResolveSyntax in the syntax chain.

IncrementalResolveSyntax

Resolves G91 incremental axis values to absolute in-place within Parsing and its sub-sections. Reads Term written by PositioningSyntax.

WorkingPathList specifies which JSON paths contain axis values that need incremental-to-absolute conversion. Default: [["Parsing"], ["Parsing", "G28"]]. All matching paths are converted.

Canned cycle paths (Parsing.G81, G82, G83, …) are intentionally excluded — their Z/R incremental semantics differ from normal axes (R is relative to init level, Z is relative to R-point). Resolution is handled by ResolveCycleCoordinates(JsonObject, Vec3d, double?, double?, double, double) inside each cycle syntax class, which runs before this syntax.

Uses AxisNames to determine which tags are motion axes. Traces backward nodes for last known ProgramXyz to resolve incremental values. After this syntax, all axis values in the working paths are absolute — ProgramXyzSyntax can consume them without incremental logic.

IsoCoordinateOffsetSyntax

ISO/Fanuc/Mazak/Okuma/Syntec: resolves G54–G59.9 work coordinate offset. Reads G54/G55/.../G59.9 from Flags, looks up offset Vec3d via IIsoCoordinateConfig dependencies (e.g. brand parameter table or IsoCoordinateTable), composes into ProgramToMcTransform. Modal — active coordinate persists via backward lookback. Default coordinate ID is set by StaticInitializer.

IsoG68RotationSyntax

ISO/Fanuc: resolves G68 (2D coordinate rotation) and G69 (cancel). Computes a rotation Mat4d around the active plane normal and composes it into ProgramToMcTransform.

No IMachineKinematics dependency needed — G68 is pure geometric rotation.

Managed commands: G68, G69 (idempotent with IsoG68p2TiltSyntax).

IsoG68p2TiltSyntax

ISO/Fanuc: resolves G68.2 (tilted work plane) and G69 (cancel). Computes a tilt Mat4d from I/J/K euler angles (Fanuc ZXZ convention) and composes it into ProgramToMcTransform.

Managed commands: G68.2, G69 (idempotent with IsoG68RotationSyntax). Siemens equivalent: CYCLE800 (separate syntax). Heidenhain equivalent: PLANE SPATIAL (separate syntax).

IsoLocalCoordinateOffsetSyntax

ISO G52: Local coordinate system offset (additive to G54-series).

G52 X10 Y20 Z5 → sets local offset. G52 X0 Y0 Z0 → cancels (resets to zero). M30 (program end) → also cancels.

Reads Parsing.G52 (from G52Syntax), writes IsoLocalCoordinateOffset section, and adds an "IsoLocalCoordinateOffset" entry to the transformation chain. Modal — persists via backward lookback until changed or cancelled.
LinearMotionSyntax

Writes McLinear motion for linear commands (ISO G00/G01, Heidenhain L/LN). Detects motion mode from Flags, writes a one-shot MotionEvent section (form + isRapid) plus a modal MotionState section (Term) when MachineCoordinateState exists on the block.

McLinearMotionSemantic discriminates between XYZ-only and XYZABC motion by checking whether rotary axis values are present in MachineCoordinateState.

Must be placed after McAbcSyntax in the syntax chain.
MachineCoordSelectSyntax

Handles machine coordinate selection — non-modal, one-shot. The axis values (X/Y/Z) in the block are interpreted as machine coordinates, bypassing all work offsets, local coordinates, tool height compensation, and coordinate rotations. If G91 (incremental) is active, the code is ignored per ISO standard.

Defaults to ISO G53. Brands with additional one-shot machine-coordinate codes widen SupportedCodes — the Siemens preset adds G153 and SUPA (both suppress every active frame for one block; in this pipeline all of those reduce to "bypass the composed ProgramToMcTransform", which the ProgramXyz back-derivation below already models). The matched code is stamped verbatim into Term for bidirectional source recovery. Rotary words on the same block (e.g. SUPA G0 B0) are left to McAbcSyntax — same behavior as ISO G53, where rotary axes flow through the normal rotary path.

Must be placed before ProgramXyzSyntax in the syntax chain. When a supported code is active, this syntax consumes X/Y/Z from Parsing and writes MachineCoordinateState directly, preventing ProgramXyzSyntax from processing them as program coordinates.

McAbcCyclicPathSyntax

Resolve modular rotary axes to the shortest cyclic path relative to the previous node. Uses IsModularRotary(string) to determine which axes within MachineCoordinateState need cyclic resolution. Falls back to hardcoded A/B/C if no IMachineAxisConfig is available. Must be placed after ProgramXyzSyntax in NcSyntaxList.

Two stages, mirroring McXyzSyntax:

  1. Root MachineCoordinateState — anchored at the previous block's modal rotary state.
  2. CompoundMotion.ItemsKey[*] — sequential walk through items, anchoring item 0 at the previous block's modal state and item i > 0 at item i-1's post-cycle value (per-axis chain). Items without a rotary MachineCoordinateState are skipped.
The items pass enables rotary motion (e.g. G28 ABC intermediate / home stages) to surface as motion IAct segments rather than a single root-MC stamp.
McAbcSyntax

Writes rotary axis values (A/B/C) into MachineCoordinateState from Parsing and modal lookback.

Only active when IMachineAxisConfig declares rotary axes. Works for both 3+2-axis (no IMachineKinematics) and simultaneous 5-axis configurations.

This syntax is intentionally ABC-only. When the block is rotary-only (no ProgramXyz, e.g. G00 A30.) the section is created with ABC but without X/Y/Z. McAbcXyzFallbackSyntax — placed after McXyzSyntax — copies X/Y/Z from the previous block's MachineCoordinateState to finish the section. Splitting the XYZ fill out lets this syntax run before McXyzSyntax (and before G43p4RtcpSyntax) without accidentally filling X/Y/Z from prev and thereby short-circuiting DeriveMcXyz(JsonObject, Mat4d).

Missing rotary axes are filled from previous MachineCoordinateState lookback, unless the current section already has the value (e.g., from HomeMcInitializer). Values are stored in degrees (matching McAbcCyclicPathSyntax).

Must be placed before McXyzSyntax so syntaxes that need the current-block ABC to compute transforms (e.g. G43p4RtcpSyntax) can see it; and before McAbcCyclicPathSyntax and LinearMotionSyntax.

McAbcXyzFallbackSyntax

Fills missing X/Y/Z on an ABC-only MachineCoordinateState section. Behaviour depends on whether the block is under RTCP with rotary motion, as indicated by HasDynamicEntry(JsonObject):

  • Non-dynamic (no RTCP or RTCP with ABC stable) — the programmed tool tip stays put in MC while rotary axes (if any) are unchanged, so we simply copy X/Y/Z from the previous block's MachineCoordinateState. This matches NC modal XYZ carry-forward for rotary-only blocks such as G00 A30. (non-RTCP pivoting).
  • Dynamic (RTCP active + ABC changing) — the programmed tool tip must stay fixed in program coordinates while MC XYZ shifts to compensate the new rotary state. Looks up the last ProgramXyz and re-derives MC = inheritedProgramXyz × composedTransform, where the composed transform is the block's endpoint chain (now including PivotTransformSource as a full rotation+translation Mat4d, so the chain already encodes the kinematic IK). The carried ProgramXyz is also stamped onto the current block so downstream consumers see a consistent ProgramXyz + MC pair.

Pair with McAbcSyntax, which runs early to write ABC but deliberately leaves X/Y/Z empty so McXyzSyntax can still derive MC XYZ from ProgramXyz via the transform chain when the block carries linear motion. If McXyzSyntax has nothing to derive (no ProgramXyz), this syntax completes the MC section as described above.

Does nothing when the section already carries all three of X/Y/Z (normal linear-motion blocks), or when there is no section at all (pure parse-only block that introduces no MC). Must be placed after McXyzSyntax and before McAbcCyclicPathSyntax / LinearMotionSyntax.

McXyzSyntax

Derives MachineCoordinateState from ProgramXyz by applying the composed ProgramToMcTransform.

Processes two stages:

  1. Root ProgramXyz → root MachineCoordinate
  2. CompoundMotion.ItemsKey[*] — derives MachineCoordinate from ProgramXyz for items that have ProgramXyz but no MachineCoordinate
Must be placed after syntaxes that write ProgramXyz (e.g., ReferenceReturnSyntax) and before syntaxes that read MachineCoordinate (e.g., LinearMotionSyntax).
PeckDrillingCycleSyntax

G83 peck drilling cycle. Supports modal repetition. Drills in increments of depth Q, fully retracting to R between strokes.

Cycle sequence (per stroke):

  1. Rapid to init position (target XY, previous Z)
  2. Rapid from init to R-point
  3. For each stroke: rapid to clearance above previous depth, feed Q deeper, rapid back to R
  4. If remainder exists: feed to bottom Z, rapid to R
  5. Rapid from R/bottom to final (G98 → init Z, G99 → R)

Retraction distance is read from ICannedCycleConfig (Fanuc #4002 / Syntec Pr4002, or FallbackConfig fallback).

Reads absolute coordinates from the cycle section, which is resolved by CannedCycleResolveSyntax (modal repetition, G91 conversion, missing-axis fallback) before this syntax runs. Must be placed after CannedCycleResolveSyntax and before IncrementalResolveSyntax in the syntax chain.

PivotTransformUtil

Shared engine for the brand pivot-gate syntaxes (PivotTransformationSyntax — ISO/Fanuc family, SiemensPivotTransformationSyntax — Siemens). Each brand syntax owns only its gate (which modal terms mean “commanded XYZ needs the Pn→MC kinematic rigid transform”); the endpoint-ABC resolution and the PivotTransform chain entry composition live here so every brand writes the identical JSON vocabulary.

PivotTransformationSyntax

ISO/Fanuc-family pivot gate: writes the PivotTransformSource entry into ProgramToMcTransform on blocks where the controller is interpreting commanded XYZ in a frame that needs the Pn→MC kinematic rigid transform — namely active RTCP (G43.4) or active tilted plane (G68/G68.2). On plain-mode blocks (no RTCP, no tilted plane), the controller treats commanded XYZ as machine-frame directly, so the indexed rotary angle is a positioning value only and must not fold into the linear axes; this syntax skips those blocks and leaves the chain at identity (or whatever non-kinematic offsets earlier syntaxes contributed). Brand variants with their own modal vocabulary gate the same shared engine (PivotTransformUtil): Siemens TRAORI/CYCLE800 → SiemensPivotTransformationSyntax; Heidenhain M128/PLANE SPATIAL would follow the same pattern.

Mirrors real Fanuc semantics: plain G43 offsets along the active tilted-plane normal (or machine Z when no tilt is active), and plain XYZ moves map directly to machine axis registers regardless of indexed table/head rotary position. Only G43.4 follows the live tool vector and only G68.2 redefines the work-plane orientation — both of which this guard detects via the existing chain markers.

Chain position: must run after all Pn-frame writers (IsoG68p2TiltSyntax, ToolHeightOffsetSyntax, G43p4RtcpSyntax, IsoCoordinateOffsetSyntax, brand-specific coord offset syntaxes) so the guard sees the finalised mode markers and the PivotTransform entry — when emitted — naturally lands as the last chain element. Must run before McXyzSyntax / ProgramXyzSyntax so they see the completed chain.

Silently no-ops when IMachineKinematics is absent (3-axis configurations without rotary kinematics).

PlaneSelectSyntax

Consumes G17/G18/G19 plane selection from Flags and writes IPlaneSelectDef section using conventional axis-pair names (XY/ZX/YZ). Modal — persists via backward lookback. Default is XY (G17).

Downstream consumers (CircularMotionSyntax, IsoG68RotationSyntax) call GetPlaneNormalDir(JsonObject) to read the resolved plane.

PolarInterpolationSyntax

Maintains the modal Polar Coordinate Interpolation valve section (PolarInterpolationState) for Fanuc G12.1/G13.1.

On a G12.1 block: consumes the flag, reads the block's own X/C words as the anchor (InitRxcz; the X word is a diameter and is halved), converts the previous program position (program X/Z + machine C angle) onto the polar hypothetical plane via GetProgramPolarRxczByOrdinaryProgramXcz(Vec3d), and writes both the state section and the entry ProgramPolarRxcz position. Mirrors HardNc HardNcLine case 12_100.

On a G13.1 block: consumes the flag and stops carrying the state — the block itself is already Cartesian, matching HardNc case 13_100.

On other blocks: re-materializes the previous block's state section (single-step lookback carry, the PositioningSyntax pattern), and warns FanucPolar--IncompatibleGCode for G-codes outside the Fanuc polar-mode whitelist (mirrors HardNc IsGCodePolarModeCompatible).

Must be placed before McAbcSyntax so the downstream ProgramRxczSyntax can consume the hypothetical C word before it is interpreted as a rotary machine axis.
PositioningSyntax

Detects G90/G91 positioning mode from Flags (or by modal lookback) and writes a Positioning section (Term, Mode) to the block JSON.

Fanuc/ISO: reads G90/G91 from Flags (global modal). Heidenhain: would need a separate implementation reading I-prefix per axis. Siemens: would extend with AC()/IC() per-axis override.

Does NOT convert incremental values — that is handled by IncrementalResolveSyntax which can be placed later in the syntax chain, after canned cycle syntaxes have consumed their parameters with cycle-specific G91 semantics.

ProgramEndCleanSyntax

Clears the per-block Vars.Volatile dictionary on blocks that triggered program end (M02 / M30, identified by the ProgramEnd section written by ProgramEndSyntax).

Real Fanuc clears non-retained common variables (#100-#499) on program end + reset; this syntax models that behaviour at the simulator level. The clear happens on the same block that carried M02/M30 — the next block's VolatileVariableReadingSyntax carry then sees an empty dictionary on the predecessor and starts fresh.

Pipeline placement: must run after both ProgramEndSyntax (which writes the ProgramEnd section this syntax checks) and VolatileVariableReadingSyntax (so the carry has already happened on this block; this syntax overwrites the result).

Retained common variables (#500-#999, owned by RetainedCommonVariableTable) are untouched — they survive program end on real hardware (NV-RAM). Local variables (#1-#33, scope: macro call frame) are also untouched here; their lifecycle belongs to G65/G66/M99 push/pop, not program end.

Also clears any active FanucModalMacro on the same edge: a G66 modal that was still active when M02/M30 hit is implicitly cancelled, matching real Fanuc reset behaviour. The section is overwritten with a G67-shaped cancel marker so the carry mechanism in FanucModalMacroSyntax sees the boundary and does not propagate the modal past the program-end edge.

ProgramEndSyntax

Consumes M02/M30 (program end) from Flags and writes IProgramEndDef section.

Downstream syntaxes that need to reset modal state on program end (e.g. IsoLocalCoordinateOffsetSyntax for G52 reset) should read the ProgramEnd section rather than scanning for M30 in Flags directly.

Must be placed before syntaxes that depend on the ProgramEnd section.
ProgramRxczSyntax

Polar-mode sibling of ProgramXyzSyntax: while the PolarInterpolationState valve section is present, consumes the block's X/C/Z words as polar hypothetical-plane coordinates (X = diameter, halved; C = hypothetical axis in mm) and writes:

  1. ProgramPolarRxcz — the anchor-relative polar position (G90/G91 resolved against the previous block's position, mirroring HardNc NcGroup03.GetNcFromSyntax);
  2. ProgramXyz — the derived ordinary program position (radius, previous program Y, Z), so the downstream McXyzSyntax derives machine XYZ through the normal transform chain — ProgramXyzSyntax itself naturally no-ops because the axis words are already consumed;
  3. the machine C angle (degrees) into MachineCoordinateState — placed before McAbcSyntax, which then preserves the value instead of treating C as a directly-commanded rotary word;
  4. on motion-programmed blocks, MotionState and a MotionEvent with McPolarLinear (G00/G01) or McPolarArc (G02/G03 with R or I/J/K resolved on the hypothetical plane).
The G12.1 entry block is skipped (its position was anchored by PolarInterpolationSyntax). Angle-branch resolution mirrors HardNc: GetOrdinaryProgramXcz_rad(Vec3d, double, Vec3d) chained from the previous machine C angle.
ProgramStopSyntax

Consumes M00 (unconditional stop) and M01 (optional stop) from Flags and writes a IProgramStopDef section on the block that carried the flag. Non-modal: the section is written only on the exact block where the stop code appears.

Siblings with ProgramEndSyntax (M02/M30) which handles end-of-program, not in-program stops.

The parsing layer only records NC intent. Whether M01 actually pauses the run is a runtime/semantic decision gated by the operator's "Optional Stop" switch (analogous to IBlockSkipConfig for block skip).

ProgramXyzSyntax

Resolves ProgramXyz (leaf coordinate) from syntax XYZ tags. Writes ProgramXyz sub-object to SyntaxPiece.JsonObject. Must be placed after BundleSyntax since it uses cross-node lookback for last position.

McXyzSyntax (placed after this in the chain) reads ProgramXyz and writes MachineCoordinateState.

ProgramXyzUtil

Shared utilities for ProgramXyz and MachineCoordinateState lookback and resolution. Used by ProgramXyzSyntax, ReferenceReturnSyntax, and semantic resolvers that need position lookback.

Two strategies for "what's the program coordinate at a block's endpoint?" — both invert an MC value through an ProgramToMcTransform chain, but they pick the chain from different nodes:

  • By current-state transform (ComputeProgramXyzByCurrentTransform(LazyLinkedListNode<SyntaxPiece>, Vec3d)) — modal anchor is MachineCoordinateState. Re-expresses an MC value (typically a predecessor's modal MC) into the current block's program frame using the current block's chain. Suitable for chain-change blocks where the spindle physically stays put while the chain (G54 swap, G68.2 activation, G43.4 toggle, tool-height change, ...) re-anchors the program frame; mirrors legacy HardNcLine.RebuildProgramXyzByMc.
  • By corresponding-state transform (ComputeProgramXyzByCorrespondingTransform(LazyLinkedListNode<SyntaxPiece>)) — modal anchor is ProgramXyz. Recovers the program coordinate that nodeCarryingMc was originally commanded at, by inverting that same node's own transform on its own MC. Suitable for RTCP rotary-dynamic inheritance, where the modal invariant is "tool tip in workpiece frame stays put while rotary axes turn" — the recovered Vec3d carries forward as the next rotary block's modal ProgramXyz unchanged, regardless of how its PivotTransform differs.

Both strategies yield the same Vec3d when prev and current share the same chain modal state; they only diverge across chain boundaries (RTCP toggle, coord-system swap, tilt activation) and at rotary motion (PivotTransform difference). Pick the wrong one and the result lands in a stale frame:

  • Non-RTCP using "corresponding" — leaves the pre-chain-change values, so a block emitted right after G43.4 H03 would inherit ProgramXyz still in the G49 frame and the next motion's MC.Z drifts by the introduced tool-height offset. (This was the 2026-04-25 SoftNc / HardNc divergence on DemoPmcAirPlane/NC/02-ED6L20.NC.)
  • RTCP using "current" — double-counts the rotary PivotTransform difference, so the inherited workpiece anchor rotates by the C delta on every rotary block.

Direct callers of the two strategy helpers are rare — typically you call the dispatcher ResolveBlockProgramXyz(LazyLinkedListNode<SyntaxPiece>, Vec3d) (block's own MC vs predecessor lookback, picks strategy from HasDynamicEntry(JsonObject)) or GetLastProgramXyz(LazyLinkedListNode<SyntaxPiece>) (pure predecessor lookback).

ReferenceReturnSyntax

Writes ICompoundMotionDef section for G28 reference point return. Reads intermediate XYZ from Parsing.G28 (written by G28Syntax) and converts to machine coordinates via ResolveProgramXyz(JsonNode, LazyLinkedListNode<SyntaxPiece>, ISentenceCarrier, NcDiagnosticProgress).

Must be placed after LinearMotionSyntax in the syntax chain. Removes the IMotionEventDef section written by LinearMotionSyntax (G28 handles its own motion). Overwrites root MachineCoordinateState and ProgramXyz with reference position for subsequent block lookback.

RotaryAxisUtil

Shared utilities for rotary axis (A/B/C) resolution. Used by G53p1RotaryPositionSyntax, McAbcSyntax, IsoG68p2TiltSyntax, and other syntaxes that read or write rotary axis values.

SpindleSpeedSyntax

Consumes S (spindle speed) and spindle direction M-codes from Parsing. Both are modal — persist across blocks via backward node lookback. Writes resolved state to a ISpindleSpeedDef section. Direction is converted from M-codes to the conventional SpindleDirection enum at this layer.

Direction M-codes: the ISO defaults M03 (CW) / M04 (CCW) / M05 (STOP) always apply; a machine that starts/stops its spindle with custom M-codes (e.g., ultrasonic M203/M205) declares them on an ISpindleControlConfig dependency (ControllerParameterTableBase), which this syntax consults first — mapped flags are consumed like the ISO ones.

Fallback: an S > 0 with no direction ever issued is contradictory (physics would silently never run). The build assumes CW and emits a one-shot SpindleDirection--AssumedCw validation warning — once is structural, not stateful: the stamped CW propagates modally, so later blocks no longer lack a direction. An explicit M05 (STOP) is a real direction and never triggers the fallback.

TappingCycleSyntax

G84 (right-hand) / G74 (left-hand) tapping cycles. Supports modal repetition.

Cycle sequence:

  1. Rapid to init position (target XY, previous Z)
  2. Rapid from init to R-point
  3. Feed from R-point to bottom Z
  4. Spindle reverse at bottom
  5. Feed retract to final Z (G98 → init Z, G99 → R)
  6. Spindle restore to forward direction

G84: forward = CW (M03), reverse = CCW (M04). G74: forward = CCW (M04), reverse = CW (M03).

Reads absolute coordinates from the cycle section, which is resolved by CannedCycleResolveSyntax (modal repetition, G91 conversion, missing-axis fallback) before this syntax runs. Must be placed after CannedCycleResolveSyntax and before IncrementalResolveSyntax in the syntax chain.

TiltTransformUtil

Shared utilities for all tilt transform syntaxes (ISO, Siemens, Heidenhain). Handles section IO, backward lookback, and ProgramToMcTransform composition.

ToolChangeSyntax

Consumes T (tool number) and M06 (tool change) from Parsing. T is modal — persists across blocks. M06 triggers the change. Writes resolved state to a ToolChange section: { “ToolId”: 1, “IsChange”: true, “Term”: “M06” }. TermKey records the trigger command and is only written when IsChangeKey is true (i.e. the block actually carried the tool-change M code); modal-only blocks omit it.

ToolId is an int for numeric calls (T5) and a string for Siemens string tool calls (T="D8R1", captured by SiemensToolCallSyntax); both shapes carry modally. String names are resolved to tool numbers at the semantic layer (ToolChangeSemantic) — this syntax records the call verbatim.

ToolHeightOffsetSyntax

Resolves ISO tool height offset (G43/G44/G49) to the effective offset value (mm) and composes the offset as a translation into the accumulated ProgramToMcTransform matrix.

RTCP modes (G43.4, TRAORI, M128) are handled by separate brand-specific syntaxes (e.g., G43p4RtcpSyntax).

UnitModeSyntax

Detects the unit-system code (ISO Group 06: G20 inch / G21 metric) from Flags and writes a Unit section (Term, System). Modal — absence of an explicit flag inherits the previous block's unit, defaulting to Metric at program start.

The code vocabulary is configurable per brand: InchCodes / MetricCodes default to ISO G20 / G21; the Siemens preset uses G70+G700 / G710+G71 instead (G70/G71 switch geometry-word interpretation only, G700/G710 also switch feedrate interpretation — a distinction preserved via the verbatim Term but irrelevant to this record-only syntax; both inch variants warn identically). The first MetricCodes entry doubles as the program-start default Term. Note Siemens G70/G71 must never be added to ISO/Fanuc presets — on Fanuc lathes the same codes are finishing/roughing cycles (same collision family as the documented Siemens G74 exclusion).

The HiNC pipeline works exclusively in millimetres. When an inch code is detected this syntax emits an Unit--InchNotSupported Unsupported Error so upstream callers are forced to pre-convert the NC program to metric — while still recording what the program said. Metric codes are accepted as no-op confirmations of the default.

Enums

BareG28Behavior

Configurable handling for a G28 block with no axis specifiers (“bare G28”) — value of BareG28. Real Fanuc-class controllers vary: older 0i-M alarms (PS010), some 30i variants send every configured axis to home. Default to Alarm so silent NC bugs surface; opt into AllAxesHome per syntax instance.