Table of Contents

Namespace Hi.NcParsers.EvaluationSyntaxs

Classes

FanucSystemControlVariableSyntax

Consumes Fanuc-style system-control variable assignments (#3000-#3999) — alarm trigger (#3000), millisecond and hour clocks (#3001 / #3002), single-block / feed-hold bypass flags (#3003 / #3004), pause-with-message (#3006), mirror-image flags (#3007), date / time (#3011 / #3012), tool-life data (#3030 / #3032), etc.

Every id in this range is a controller-side state variable — its authoritative value lives on the real hardware (RTC, alarm bus, override switches, …) and an NC write at most triggers a side effect (clock reset, alarm raise, message-pause prompt). Offline simulation has none of that machinery, so this syntax does not emulate the effect. Instead it:

  1. records the literal write on the block JSON under Vars.SystemControl (round-trip and cache-dump visibility);
  2. emits a FanucSystemControl--Unsupported UnsupportedMessage(Sentence, string, string, object) so the user knows the assignment was recognised but its controller-side effect is not simulated. Message-severity (not Warning) because these writes are safe no-ops offline — every consumed assignment would emit a Warning per block, which would be noisy without signalling anything the user must act on;
  3. removes the entry from Parsing.Assignments so it does not re-surface as a generic Parsing--Unconsumed diagnostic.

The dictionary carries forward block-by-block (same dict-merge pattern as VolatileVariableReadingSyntax) so a downstream consumer can read the most recent recorded value via SyntaxPiece linkage.

Only literal numeric RHS values are consumed; non-literal RHS (e.g. #3002 = #500) is left in Parsing.Assignments for VariableEvaluatorSyntax to resolve, mirroring the retained / volatile reading syntaxes.

Fanuc-family only — Siemens uses named system variables ($AC_TIME, $A_DAY, …) and Heidenhain uses FN18: SYSREAD; neither flows through Parsing.Assignments.#nnn.

RetainedCommonVariableReadingSyntax

Obtains values for Fanuc-style retained common variables (#500-#999) by consuming literal numeric assignments from Parsing.Assignments.#nnn and writing them straight to a registered RetainedCommonVariableTable.

No SyntaxPiece JSON mirror is created — the table is the single source of truth for retained values, and VariableEvaluatorSyntax reads from the table directly. The hincproj round-trip preserves writes across project sessions.

Only literal numeric RHS values are consumed by this syntax (#500 = 1.234 ✓; #600 = #500 + 1 ✗). Non-literal RHS entries are left untouched in Parsing.Assignments; VariableEvaluatorSyntax resolves them and writes the result through the same table. The two syntaxes are decoupled.

If no RetainedCommonVariableTable is registered on the runner's NcDependencyList, this syntax is a no-op.

SubProgramCallSyntax

Inlines a Fanuc-style subprogram into the source layer when an M98 or M198 host block is reached. M98 P_ L_ reads the matching O<P> file from InternalFolder; M198 P_ reads from ExternalFolder (Fanuc external-storage call — same mechanism as M98, different lookup root). The file is segmented through the host runner's segmenter (SegmenterDependency) and the resulting SyntaxPieces are prepended into layers[0] via PrependSource(IEnumerable<T>); the rest of the pipeline picks them up through ordinary walkNode.Next traversal as if they had always been part of the host file.

Pipeline placement: first child of the Fanuc Evaluation BundleSyntax. By the time this runs, M98Syntax / M198Syntax (each a ParameterizedFlagSyntax) have written a Parsing.M98 / Parsing.M198 sub-object carrying the captured P / L parameters. Note: those sub-objects are this syntax's only trigger — "M98" / "M198" never reach Parsing.Flags, because the parameterized match has already consumed the text by the time NumberedFlagSyntax runs.

Filename lookup uses a fallback chain: O{P:D4}.NC, O{P}.NC, O{P:D4}, O{P}, {P:D4}.NC, {P}.NC — first match wins. Case-insensitive match is delegated to the host filesystem (Windows is, Linux is not).

L > 1 inlines the same subprogram L times in series. Each repetition is a fresh segmentation pass so each block gets its own SyntaxPiece with an independent JSON object — the downstream pipeline mutates JSON in place and would clobber sibling repetitions if instances were shared.

Not yet supported: M99 P{seq} early return inside a subprogram, partial-program calls (M98 P{seq}{prog} split encoding), and arg binding (G65 macro is a separate syntax).

SubProgramReturnSyntax

Consumes Fanuc-style M99 subprogram-return blocks.

In the inline model used by SubProgramCallSyntax, a plain M99 at the end of a subprogram is implicit: the inlined blocks are followed in layers[0] by the caller's next block, so the natural pipeline traversal already does the "return". This syntax therefore only consumes the M99 flag (so UnconsumedCheckSyntax doesn't warn) and records a SubProgramReturn section for cache-dump visibility.

M99 P{seq} (return to caller's N{seq} sequence number) is captured in P but not yet honoured — the subprogram tail still proceeds straight into the caller's next block. Implementing the jump requires a forward scan of the post-host caller blocks for a matching N{seq} head index, then dropping the intervening blocks; deferred until the caller-side walk semantics are designed.

Pipeline placement: anywhere after Parsing has populated Parsing.M99. Conventionally placed alongside SubProgramCallSyntax at the head of the Evaluation bundle so call/return live next to each other.

Detection is on the Parsing.M99 sub-object written by M99Syntax (a ParameterizedFlagSyntax) — the keyword "M99" never reaches Parsing.Flags because the parameterized match has already consumed the text by the time NumberedFlagSyntax runs.

VariableEvaluatorSyntax

Pure expression normalizer for Custom Macro B syntax. Walks the parser-stage residue on a single block and inlines numeric values wherever a Fanuc-style variable reference or bracket expression appears — but does not write to any specific store. Routing “where the resolved literal lands” stays in the brand-specific reader syntaxes (VolatileVariableReadingSyntax, RetainedCommonVariableTable's reader, FanucSystemControlVariableSyntax, …) which run after this syntax on the same block.

Two passes per block:

  1. Assignments normalizeParsing.Assignments.#nnn entries whose RHS is non-literal (e.g. "#500+1", "SQRT[#100]") are evaluated via the VariableEvaluatorSyntax.ChainLookup and the RHS string is replaced with the resolved literal (round-trip-safe "R"-format). The entry stays in Parsing.Assignments so downstream reader syntaxes consume it as a pure-literal assignment. Iteration follows source order (Parsing.Assignments insertion order).
  2. Parsing tree substitution — every string-typed value reachable from Parsing.<tag> (axis tags, canned-cycle sub-objects) is parsed; on a successful evaluation the string is replaced with a numeric JsonValue. Failures silently leave the original string and rely on downstream GetParsedDouble(JsonObject, string, Sentence, NcDiagnosticProgress) at consumer sites to surface VariableExpression--Unevaluated only if the tag is actually read.

Lookup chain (first non-null wins, configured per brand preset via RuntimeVariableLookups + IVariableLookup instances on NcDependencyList):

  1. Current block's own resolved assignments — built-in to VariableEvaluatorSyntax.ChainLookup; covers same-block forward references in source order (an earlier #nnn=literal is visible to a later RHS that mentions #nnn).
  2. Each IRuntimeVariableLookup in RuntimeVariableLookups, in list order. Typical contents for a Fanuc-family preset: LocalVariableLookup (#1-#33), VolatileVariableLookup (#100-#499), FanucPositionVariableLookup (#5001-#5043).
  3. Each IVariableLookup on the runner's NcDependencyList, in registration order (RetainedCommonVariableTable, FanucParameterTable, FanucToolOffsetVariableLookup).

Each lookup self-gates its id range; the evaluator stays brand- and range-agnostic. Adding a new variable surface is additive: register an IVariableLookup on a dependency or push an IRuntimeVariableLookup onto the per-preset list.

Same-block forward reference — when an Assignment RHS references a #nnn that is also being assigned later in the same block (i.e. listed in Parsing.Assignments after the RHS being evaluated), the VariableEvaluatorSyntax.ChainLookup cannot pick up the not-yet-resolved value and falls back to traceback / dependency-table reads — effectively the pre-block value. A VariableEvaluator--SameBlockForwardReference warning is emitted per such RHS so the user is told the source-order semantics were not honoured. Practical impact is near-zero for typical CAM-emitted NC (one assignment per line).

VolatileVariableReadingSyntax

Obtains values for Fanuc-style non-retained common variables (#100-#499). Reads literal numeric assignments from Parsing.Assignments.#nnn, dict-merges them with the previous block's volatile state, and writes the resulting per-block dictionary into Vars.Volatile.

Lifetime is bounded by MachiningSession: within one session the dictionary carries forward block-by-block via this syntax; session restart abandons the SyntaxPiece JSON dataflow and starts fresh. Program-end (M02/M30) clearing is handled by ProgramEndCleanSyntax.

Only literal numeric RHS values are consumed by this syntax (#124 = 15. ✓; #100 = #1 + 5 ✗). Non-literal RHS entries are left untouched in Parsing.Assignments; VariableEvaluatorSyntax resolves them and writes the result into the same per-block dictionary. The two syntaxes are decoupled — the evaluator's lookup tracebacks via SyntaxPiece linkage so it does not depend on having run before or after this syntax.