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 | nullstamp 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>--ConditionNotEvaluatedwarning.
The
Displayform 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>) onlayers[0]with the re-segmented file content starting at the matchingN{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 conditionalIF [<expr>] GOTO <n>are implemented. The conditional form leans on VariableEvaluatorSyntax's pass-2 tree walk to substituteParsing.FanucGoto.Conditionwith 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 emitsFanucGoto--ConditionNotEvaluatedand 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 inParsing.FanucGoto.N. Reader syntaxes (VolatileVariableReadingSyntax etc.) are independent — they touchParsing.Assignments, notParsing.FanucGoto.Label scanning uses two hosted helper syntaxes —
CommentSyntaxandIndexSyntax— applied to each candidate block in turn so the predicateIndexNote.Number == targetmatches 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 byParsing.FanucIfThen(written by FanucIfThenParsingSyntax); reads the now-resolved Condition node, decides whether to fire, and on fire lifts the parsing-stagePendingAssignmentssub-object into the canonicalParsing.Assignmentsbucket 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
FanucIfThensection 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
PendingAssignmentsRHS 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.ReadConditionshape:- 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.
FanucIfThen--UnsupportedBodyand falls through.
- FanucLocalVariableReadingSyntax
Routes literal-RHS assignments to Fanuc-style local macro variables (
#1-#33) fromParsing.AssignmentsintoVars.Localon the current block, carrying the previous block'sVars.Localdict forward when both blocks share the same MacroFrame id. Mirrors VolatileVariableReadingSyntax for the#100-#499range, 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) emitLocalVariable--MainFrameWriteUnsupportedand 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 + 1has 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--Unevaluateddiagnostic plus UnconsumedCheckSyntax.
- FanucMacroArgumentMap
Fanuc Custom Macro B Type-I argument-letter map: which call-line letter binds to which
Vars.Localid (#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
#nnnbindings 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 toVars.Local#1-#26per 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-#33writes 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 inlayers[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 anyVars.Localentry 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 > 1inlines the same macroLtimes 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 freshFileIndex(so(FileIndex, LineIndex)pairs stay unique across theL-copies of the same source lines).Pipeline placement: ahead of SubProgramCallSyntax inside the Fanuc
EvaluationBundleSyntax so a hypotheticalG65 P_+M98 P_on the same block expands the G65 macro first (would be an unusual but legal composition). Detection is on theParsing.G65sub-object written by G65Syntax (a ParameterizedFlagSyntax) — the keyword"G65"never reachesParsing.Flagsbecause the parameterized match has already consumed the text by the time NumberedFlagSyntax runs.
- FanucModalMacroSyntax
Handles Fanuc Custom Macro B modal-call lifecycle (
G66setup,G67cancel, 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 theModalCarrySyntax.Logic/.PostLogicpattern 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:
- records the literal write on the block JSON under
Vars.SystemControl(round-trip and cache-dump visibility); - emits a
FanucSystemControl--UnsupportedUnsupportedMessage(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; - removes the entry from
Parsing.Assignmentsso it does not re-surface as a genericParsing--Unconsumeddiagnostic.
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
SyntaxPiecelinkage.Only literal numeric RHS values are consumed; non-literal RHS (e.g.
#3002 = #500) is left inParsing.Assignmentsfor VariableEvaluatorSyntax to resolve, mirroring the retained / volatile reading syntaxes.Fanuc-family only — Siemens uses named system variables (
$AC_TIME,$A_DAY, …) and Heidenhain usesFN18: SYSREAD; neither flows throughParsing.Assignments.#nnn.- records the literal write on the block JSON under
- FanucWhileDoSyntax
Resolves Fanuc Custom Macro B
WHILE [..] DO m ... END mbounded loops. Two phrases dispatched by Term:WHILE [..] DO m— entry: reads the resolved condition via FanucConditionReader, manages the per-LoopId frame in the top-levelWhileFramesdict, 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 inWhileFrames[LoopId].BeginLineNoso 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
WhileFramesJSON 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.LineIndexmatches.
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.