Table of Contents

Namespace Hi.NcParsers.EvaluationSyntaxs.Fanuc

Classes

FanucGotoSyntax

Resolves Fanuc Custom Macro B GOTO control flow. Triggered by Parsing.FanucGoto (written by FanucGotoParsingSyntax); decides whether to fire, and on fire calls ReplaceSource(IEnumerable<T>) on layers[0] with the re-segmented file content starting at the matching N{target} label. The host block stays materialised (so cache dumps still see the GOTO call site); execution naturally continues from the new source once the pipeline pulls the next block.

Both unconditional GOTO <n> and conditional IF [<expr>] GOTO <n> are implemented. The conditional form leans on VariableEvaluatorSyntax's pass-2 tree walk to substitute Parsing.FanucGoto.Condition with a numeric JsonValue when the expression evaluates successfully — ReadCondition(JsonNode) then reads the node polymorphically. Truthy non-zero fires the redirect; zero falls through silently; a still-string (unresolved) Condition emits FanucGoto--ConditionNotEvaluated and falls through.

Pipeline placement: tail of the Fanuc / Mazak / Syntec Evaluation bundle. Must run after VariableEvaluatorSyntax so any #<var> in the target N (e.g. GOTO #1) has been substituted to a literal in Parsing.FanucGoto.N. Reader syntaxes (VolatileVariableReadingSyntax etc.) are independent — they touch Parsing.Assignments, not Parsing.FanucGoto.

Label scanning uses two hosted helper syntaxes — CommentSyntax and IndexSyntax — applied to each candidate block in turn so the predicate IndexNote.Number == target matches the same way the Parsing bundle would. Both are XML-IO-able so API customers can swap them (e.g. for a controller variant using ;-style comments or a different head symbol). Defaults match Fanuc: QuoteCommentSyntax and HeadIndexSyntax with the "N" symbol.

FanucIfThenSyntax

Resolves Fanuc Custom Macro B IF [<cond>] THEN <body> single-block conditionals. Triggered by Parsing.FanucIfThen (written by FanucIfThenParsingSyntax); reads the now-resolved Condition node, decides whether to fire, and on fire lifts the parsing-stage PendingAssignments sub-object into the canonical Parsing.Assignments bucket so the brand-specific reader syntaxes downstream route each entry to its store the same way they would handle an unconditional #nnn = <literal> on a normal block.

Unlike FanucGotoSyntax there is no source splice, no label scan, no iteration watchdog — the spec restricts the body to the current block. The host block is preserved either way (the stamped FanucIfThen section on the host's top-level JSON keeps the IF-THEN call site visible to cache dumps and diagnostics, with Applied flipped true only on a successful fire).

Pipeline placement: in the Evaluation bundle after VariableEvaluatorSyntax (so the Condition expression has been substituted in place by pass-2 tree walk, and each PendingAssignments RHS string has been evaluated to a numeric JsonValue) and before the reader syntaxes (VolatileVariableReadingSyntax, RetainedCommonVariableReadingSyntax, FanucLocalVariableReadingSyntax, FanucSystemControlVariableSyntax) — that ordering lets the lifted entries reach the readers as if they had been written by TagAssignmentSyntax on a normal block.

Three condition outcomes mirror the ReadCondition(JsonNode) shape:

  • Truthy non-zero → lift assignments, stamp Applied=true.
  • Truthy zero → fall through silently, Applied=false.
  • Truthy null (evaluator failed, condition still a string or non-finite) → warn FanucIfThen--ConditionNotEvaluated, do not lift, Applied=false.
A truthy condition with no PendingAssignments (body did not parse as one or more assignments — e.g. a G-code body, currently unsupported) warns FanucIfThen--UnsupportedBody and falls through.
FanucLocalVariableReadingSyntax

Routes literal-RHS assignments to Fanuc-style local macro variables (#1-#33) from Parsing.Assignments into Vars.Local on the current block, carrying the previous block's Vars.Local dict forward when both blocks share the same MacroFrame id. Mirrors VolatileVariableReadingSyntax for the #100-#499 range, with two differences:

  • Carry is gated by MacroFrame equality, so a caller block after a G65 return does not inherit the macro body's final locals.
  • Writes outside a macro frame (a main-program block doing #11 = 5) emit LocalVariable--MainFrameWriteUnsupported and consume the assignment without persisting — real Fanuc allows main-frame local writes but this simulator only tracks locals inside G65/G66 call frames; surfacing the gap as a diagnostic is more informative than a silent UnconsumedCheckSyntax hit.

Pipeline placement: Evaluation bundle, after VariableEvaluatorSyntax (so any expression RHS such as #11 = #1 + 1 has already been normalised to a literal by the time this reader runs) and after the other range readers (RetainedCommonVariableReadingSyntax, VolatileVariableReadingSyntax) so they all share a similar Reader-stage shape.

Only literal numeric RHS values are consumed here; non-literal entries (which can only persist if VariableEvaluatorSyntax failed to resolve them) are left untouched and surface via the evaluator's own VariableExpression--Unevaluated diagnostic plus UnconsumedCheckSyntax.

FanucMacroArgumentMap

Fanuc Custom Macro B Type-I argument-letter map: which call-line letter binds to which Vars.Local id (#1-#26) inside the macro body. Reserved letters (G, L, N, O, P) are absent — they are consumed by the call itself, not passed through.

Used by FanucMacroCallSyntax (G65, one-shot) and FanucModalMacroSyntax (G66, modal) to translate the argument letters captured by G65Syntax / G66Syntax into the #nnn bindings the macro body's expression evaluator can read.

FanucMacroCallSyntax

Inlines a Fanuc Custom Macro B one-shot call (G65 P_ L_ [letter value …]) into the source layer and binds the call-line argument letters to Vars.Local #1-#26 per the Type-I map (see FanucMacroArgumentMap). Every inlined block carries the binding dict, a clone of the FanucMacroCall diagnostic record, and a MacroFrame id stamp — so LocalVariableLookup resolves arg references in a single-block lookup, a cache dump landing on any block immediately shows which call it belongs to, and downstream FanucLocalVariableReadingSyntax carries body-internal #1-#33 writes forward only within the same frame. The host block itself records FanucMacroCall but stays in the caller's frame (no MacroFrame stamp) and emits no motion act; after the macro body's last inlined block the pipeline continues naturally into the caller's next block (the inlined pieces sit ahead of the host block's successor in layers[0]).

Frame isolation works on two layers. Statically, caller blocks have no MacroFrame stamp (frame id 0 by Get(JsonObject)), so the inlined frame ids (allocated fresh per L-repetition) never collide with main. Dynamically, LocalVariableLookup and FanucLocalVariableReadingSyntax compare frame ids before carrying any Vars.Local entry across a block boundary — a macro body's body-internal writes therefore stay inside the macro and never leak back into the caller's frame.

Filename lookup mirrors SubProgramCallSyntax: O{P:D4}.NC, O{P}.NC, O{P:D4}, O{P}, {P:D4}.NC, {P}.NC — first match wins. The lookup root is InternalFolder (G65 has no "external storage" variant; M198's external root is M98/M198-only).

L > 1 inlines the same macro L times in series. Each repetition is a fresh segmentation pass (so each block gets its own SyntaxPiece JSON object — the downstream pipeline mutates JSON in place and would clobber sibling repetitions if instances were shared) and gets a fresh FileIndex (so (FileIndex, LineIndex) pairs stay unique across the L-copies of the same source lines).

Pipeline placement: ahead of SubProgramCallSyntax inside the Fanuc Evaluation BundleSyntax so a hypothetical G65 P_ + M98 P_ on the same block expands the G65 macro first (would be an unusual but legal composition). Detection is on the Parsing.G65 sub-object written by G65Syntax (a ParameterizedFlagSyntax) — the keyword "G65" never reaches Parsing.Flags because the parameterized match has already consumed the text by the time NumberedFlagSyntax runs.

FanucModalMacroSyntax

Handles Fanuc Custom Macro B modal-call lifecycle (G66 setup, G67 cancel, and per-motion-block implicit macro invocation). The same class is registered twice in the pipeline via Phase — once in the Evaluation bundle (Setup, captures G66/G67 edges and carries the FanucModalMacro state block-to-block) and once in the PostLogic bundle (Expansion, on every motion block within an active G66 modal, inlines the macro body via the same mechanism FanucMacroCallSyntax uses).

Keeping both phases in one class makes the pairing visually explicit: readers see "G66 in one file" and the two methods (DoSetup, DoExpansion) make the lifecycle obvious. The two factory helpers (Setup, Expansion) mirror the ModalCarrySyntax.Logic / .PostLogic pattern already in the codebase.

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(ISentenceCarrier, 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.

Enums

FanucModalMacroSyntax.SyntaxPhase

Identifies which pipeline phase the instance runs in. The two values correspond to the Evaluation-bundle and PostLogic-bundle registrations of this same syntax class.