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 caller-supplied probe syntaxes on each candidate block to extractIndexNote.Number, and returns the slice from the first matching block to EOF.Returns
nullwhen 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####.NCform 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).- FileName — bare
- 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 fromParsing.Assignments.#nnnand 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 inParsing.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 matchingO<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 intolayers[0]via PrependSource(IEnumerable<T>); the rest of the pipeline picks them up through ordinarywalkNode.Nexttraversal as if they had always been part of the host file.Pipeline placement: first child of the Fanuc
EvaluationBundleSyntax. By the time this runs, M98Syntax / M198Syntax (each a ParameterizedFlagSyntax) have written aParsing.M98/Parsing.M198sub-object carrying the capturedP/Lparameters. Note: those sub-objects are this syntax's only trigger —"M98"/"M198"never reachParsing.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
Ltimes 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..#26binding and the macro-call frame isolation that M98 deliberately does not provide.
- SubProgramReturnSyntax
Consumes Fanuc-style
M99subprogram-return blocks and pops one CallStack frame. PlainM99relies on the natural pipeline tail — the inlined body's last block is followed inlayers[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'sN{seq}block via ReplaceSource(IEnumerable<T>). The caller's file is resolved from the popped frame's CallerFilePath; the scan uses the same LabelScanUtil.SegmentAndSkipUntilLabelhelper 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 tightM98 → 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.M99sub-object written by M99Syntax (a ParameterizedFlagSyntax) — the keyword"M99"never reachesParsing.Flagsbecause 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:
-
Assignments normalize —
Parsing.Assignments.#nnnentries 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 inParsing.Assignmentsso downstream reader syntaxes consume it as a pure-literal assignment. Iteration follows source order (Parsing.Assignments insertion order). -
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 surfaceVariableExpression--Unevaluatedonly if the tag is actually read.
Lookup chain (first non-null wins, configured per brand preset via RuntimeVariableLookups + IVariableLookup instances on
NcDependencyList):- Current block's own resolved assignments — built-in to
VariableEvaluatorSyntax.ChainLookup; covers same-block forward references in
source order (an earlier
#nnn=literalis visible to a later RHS that mentions#nnn). - Each IRuntimeVariableLookup in
RuntimeVariableLookups, in list order. Typical contents
for a Fanuc-family preset:
LocalVariableLookup (
#1-#33), VolatileVariableLookup (#100-#499), FanucPositionVariableLookup (#5001-#5043). - 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
#nnnthat is also being assigned later in the same block (i.e. listed inParsing.Assignmentsafter 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. AVariableEvaluator--SameBlockForwardReferencewarning 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).Formula mirror tree — when either pass actually performs a non-trivial expression evaluation (i.e. the RHS / tag value was not already a pure literal and the evaluator returned a finite value), the original expression string is mirrored to a parallel
Formula.<same path>entry at the root of the block JSON. TheParsing.*subtree carries the resolved value (R-format string for Assignments; numeric JsonValue for tags); theFormula.*subtree preserves the source-text expression for diagnostics, round-trip reconstruction, and downstream inspection. Pure-literal RHS / tag values produce no Formula entry — the Parsing value is already the original text. Evaluation failures (parse error, vacant variable, non-finite result) also produce no Formula entry — the original string is still inParsing.*untouched, no preservation needed.-
Assignments normalize —
- VolatileVariableReadingSyntax
Obtains values for Fanuc-style non-retained common variables (
#100-#499). Reads literal numeric assignments fromParsing.Assignments.#nnn, dict-merges them with the previous block's volatile state, and writes the resulting per-block dictionary intoVars.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 inParsing.Assignments; VariableEvaluatorSyntax resolves them and writes the result into the same per-block dictionary. The two syntaxes are decoupled — the evaluator's lookup tracebacks viaSyntaxPiecelinkage 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.