Table of Contents

Namespace Hi.NcParsers.EvaluationSyntaxs.Fanuc

Classes

FanucConditionReader

Shared polymorphic reader for Fanuc Custom Macro B conditional gate expressions (IF [...] GOTO, IF [...] THEN, WHILE [...] DO m). The condition node is read post-evaluation — VariableEvaluatorSyntax's pass-2 tree walk has already substituted the original expression string with a numeric JsonValue when evaluation succeeded; this helper maps that node to a tri-state truthy outcome plus a display form for diagnostics.

Three states, mapping directly to the ConditionEvaluated: true | false | null stamp shape used by all three consumers (see FanucGotoSyntax, FanucIfThenSyntax, and the WHILE-loop syntax):

  • Truthy = true — node is a finite non-zero numeric; gate fires.
  • Truthy = false — node is a finite numeric equal to zero; gate falls through silently.
  • Truthy = null — node is null, still a string (evaluator failed), or non-finite double (NaN / ±∞); gate falls through and the caller emits its own <Syntax>--ConditionNotEvaluated warning.

The Display form is the human-readable expression text for diagnostic messages. For resolved numerics it is the value formatted via InvariantCulture; for unresolved strings it is the original expression text. Diagnostics build their own message text — the helper just provides the source string so the caller can compose "IF [<Display>] GOTO ..." etc.

Callers typically DeepClone() the original node before passing in here, then again before stamping back, so removing the parsing section and writing the host-level stamp can happen in any order without dangling references.

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 FanucGotoSyntax.ReadCondition 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.

FanucWhileDoSyntax

Resolves Fanuc Custom Macro B WHILE [..] DO m ... END m bounded loops. Two phrases dispatched by Term:

  • WHILE [..] DO m — entry: reads the resolved condition via FanucConditionReader, manages the per-LoopId frame in the top-level WhileFrames dict, and either falls through (truthy) or forward-jumps past the matching END m (falsy / unresolved).
  • END m — terminator: unconditionally reverse-jumps to the WHILE block recorded in WhileFrames[LoopId].BeginLineNo so the next iteration re-evaluates the entry condition. Increments the per-loop iteration counter on FanucWhileDoIterationDependency; suppresses the redirect above MaxIterationsPerLoopId.

WhileFrames carrier. The top-level WhileFrames JSON section is a JsonObject keyed by LoopId-as-string whose values are { BeginLineNo: int }. Frames are pushed when a WHILE block first encounters a truthy condition with no existing frame for that LoopId; popped when the condition becomes falsy or unresolved; otherwise carried forward unchanged by ModalCarrySyntax's Logic tracked-key list. Nested loops with distinct LoopIds coexist in the same dict; same-LoopId nesting (spec-undefined) overwrites and is not given special handling.

Pipeline placement. Evaluation bundle, after the variable readers (defensive — WHILE/END blocks per spec do not carry assignments, but the placement is consistent with GOTO). Must run after VariableEvaluatorSyntax so the condition string has been substituted to numeric.

Forward scan to matching END m uses the LabelScanUtil predicate overload — probe runs the brand-default FanucWhileDoParsingSyntax on each candidate, predicate matches on Parsing.FanucWhileDo.Term == "END" && LoopId == target.

Reverse scan to WHILE BeginLineNo does not need a label predicate — the BeginLineNo is a known file-line index recorded in the active frame, so the END side re-segments the file from the top and returns the slice starting at the first piece whose CharIndexSegment.Begin.LineIndex matches.

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.