Table of Contents

Namespace Hi.NcParsers.EvaluationSyntaxs

Classes

CallStackUtil

Push / pop helpers for the per-block CallStack section. Both produce a fresh deep-cloned JsonObject ready to stamp onto an inlined piece (push site) or onto an M99 return block (pop site); the caller is responsible for deep-cloning again if it distributes the same stamp across multiple pieces of an L-repetition.

Pairs with ModalCarrySyntax at the Logic stage: explicit push / pop writes seed the section at frame boundaries, ModalCarry copies it forward to every block in between so each block is self-contained for cache-dump readers and downstream consumers (notably M99 P{seq} reading the top frame's CallerFilePath).

LabelScanUtil

Shared “re-segment a file and skip pieces until a label matches” scan, used by both FanucGotoSyntax (unconditional GOTO redirect) and SubProgramReturnSyntax (M99 P{seq} jump into the caller file). Reads the file via ReadLines(int, string, string), segments through the provided ISegmenter, runs the probeSyntaxes on each candidate block to extract IndexNote.Number, and returns the slice from the first matching block to EOF.

Returns null when no block matches — the caller's responsibility to surface the appropriate diagnostic. The probes are idempotent because the downstream Parsing bundle re-runs the same syntaxes on the yielded pieces with no-op effect (the regex patterns no longer match once the N-prefix is consumed and the parenthesised comment stripped).

MacroFileResolver

Shared subprogram-/macro-file resolver for Fanuc-style O<n> lookups consumed by SubProgramCallSyntax (M98 / M198) and FanucMacroCallSyntax (G65). Single helper so the three path forms — file name, project-relative path, absolute path — are produced together at one site and each caller gets exactly the form it should consume:

  • FileName — bare O####.NC form the resolver matched. Stored in JSON sections (FanucMacroCall, SubProgramCall) as the structural NC-language identifier; independent of which folder the dependency happened to be pointing at, so the JSON stays portable across environments.
  • RelPath — relative path against the project base directory (e.g. "NC/O1234.NC"). Used as the IndexedFileLine label so diagnostics on inlined blocks align with the relative form already used for the main file label.
  • AbsPath — absolute path. Used only at the ReadLines(int, string, string) call site for actual disk I/O; never persisted, never returned to JSON. Lives inside the resolver's stack frame and the segmenter's enumeration.

Filename lookup order (first match wins) mirrors real Fanuc fallback: O{P:D4}.NC, O{P}.NC, O{P:D4}, O{P}, {P:D4}.NC, {P}.NC. Case-insensitive match is delegated to the host filesystem (Windows is, Linux is not).

MacroInlineUtil

Shared inline mechanism for Fanuc Custom Macro B body expansion — used by both FanucMacroCallSyntax (one-shot) and FanucModalMacroSyntax's expansion phase (modal trigger). Both callers do the same three things on every produced SyntaxPiece: stamp a FanucMacroCall clone, stamp a fresh MacroFrame id, and stamp argument bindings into Vars.Local. Centralising lets the two call sites stay in lock-step — frame allocation, file-index allocation, and the inline-piece JSON shape are guaranteed identical.

Frame ids share the same FileIndexCounterDependency counter as file indices — both just need within-session uniqueness and the counter is rewound on session start in lock-step with the pipeline. The main NC file is allocated index 0 first, so all inline frame ids land at > 0 and never collide with main.

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 and partial-program calls (M98 P{seq}{prog} split encoding). Custom Macro B argument-binding calls (G65 / G66 / G67) live in FanucMacroCallSyntax and FanucModalMacroSyntax — those handle the argument-letter-to-#1..#26 binding and the macro-call frame isolation that M98 deliberately does not provide.

SubProgramReturnSyntax

Consumes Fanuc-style M99 subprogram-return blocks and pops one CallStack frame. Plain M99 relies on the natural pipeline tail — the inlined body's last block is followed in layers[0] by the caller's next block, so the “return” happens implicitly; this syntax only consumes the M99 trigger (so UnconsumedCheckSyntax doesn't warn), stamps a SubProgramReturn diagnostic section, and writes the popped CallStack for downstream blocks to carry.

M99 P{seq} additionally redirects control flow to the caller's N{seq} block via ReplaceSource(IEnumerable<T>). The caller's file is resolved from the popped frame's CallerFilePath; the scan uses the same SegmentAndSkipUntilLabel(ISegmenter, string, string, int, int, int, List<ISituNcSyntax>, NcDiagnosticProgress) helper as FanucGotoSyntax, with hardcoded Fanuc-default probes (QuoteCommentSyntax + HeadIndexSyntax with symbol "N") because the M99 P semantic itself is Fanuc-family-only and Mazak / Syntec follow the same conventions. The iteration is counted against FanucGotoIterationDependency, sharing the same runaway-loop guard as GOTO — keyed on the same (FileName, TargetN) bucket so a tight M98 → M99 P → M98 … loop trips the same threshold.

Pipeline placement: same Evaluation bundle slot it always occupied, right after SubProgramCallSyntax at the head. Needs FanucGotoIterationDependency, ProjectFolderDependency, SegmenterDependency, SyntaxPieceLayerDependency, FileIndexCounterDependency on the dep list when M99 P{seq} is to fire; without them the plain-M99 path still works and the P-jump emits a configuration warning.

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

Structs

MacroFileResolver.ResolvedFile

Tri-form resolution result. FileName is the bare matched name; RelPath is that name joined with the folder portion of the dependency (relative when the folder is configured relative, absolute fallback when it isn't); AbsPath is the fully-resolved I/O target.