Table of Contents

Namespace Hi.NcParsers.EvaluationSyntaxs.Heidenhain

Classes

HeidenhainExpressionParser

Recursive-descent parser for Heidenhain klartext FN value expressions. Produces the same NcExpr AST as the Fanuc NcExpressionParser so NcExpressionEvaluator is reused unchanged. Pure: no variable lookup, no evaluation.

Grammar (lowest precedence at top):

expr     := add-expr
add-expr := term (('+' | '-') term)*
term     := factor (('*' | '/' | 'DIV') factor)*
factor   := ('+' | '-')? primary
primary  := number
         | '(' expr ')'
         | 'Q' digits | 'QR' digits | 'QL' digits | 'QS' digits
         | func primary                    → prefix form "SQRT 4" (FN 5)
         | func '(' arglist ')'            → paren form "SQRT(Q2)"

Dialect notes: Q-family tokens are canonicalised to uppercase (q1Q1) so Parsing.Assignments keys, table lookups and same-block references agree. (Caveat shared with the Siemens R canonicalisation: a lowercase-captured Assignments key keeps its raw spelling in the evaluator's same-block dictionary, so a later same-block reference resolves to the pre-block value instead — klartext posts emit uppercase, corpus-zero.) DIV is the FN 4 division spelling (FN 4: Q4 = +8 DIV +Q2) and maps onto the shared divide operator. Function names are limited to the klartext math vocabulary (SQRT SIN COS TAN ASIN ACOS ATAN ABS INT FRAC SGN NEG LN LOG EXP); INT normalises to the evaluator's FIX (truncate toward zero), names the shared evaluator lacks (FRAC/SGN/NEG/LOG) parse fine and fail soft at evaluation. Any other identifier is a parse error — klartext has no named variables, and rejecting bare words keeps the evaluator's Parsing-tree pass from touching non-expression strings (MM, MAX, tool-axis letters). Comparison/logical operators are deliberately absent: FN 9–12 jump conditions are pre-normalised by the control-flow syntaxes (P5), not parsed here.

HeidenhainQParameterReadingSyntax

Obtains values for Heidenhain Q parameters by consuming literal numeric assignments from Parsing.Assignments.Qn/QRn and routing them by id range — one reader for the single Q key shape, range routing inside (the Heidenhain analogue of the Fanuc range-routed reader trio):

  • Q0-Q99HeidenhainQParameterTable free range (hincproj-persisted; the table is the single source of truth — no JSON mirror).
  • QRn → the same table's permanent QR store.
  • Q100-Q199 → controller-written system parameters: the write is consumed but not applied, with a HeidenhainQ--SystemReadOnly warning (no fabricated values).
  • Q200+ → volatile range: dict-merged into Vars.Volatile with canonical Q+id keys, carried block-to-block like the Fanuc VolatileVariableReadingSyntax; cleared at program end by ProgramEndCleanSyntax.

The carry of the previous block's Vars.Volatile happens on every block regardless of assignments, so the single-step traceback contract of HeidenhainVolatileQLookup holds.

Only literal numeric RHS values are consumed (Q1 = 5000 ✓; Q1 = Q1*.75 ✗). Non-literal RHS entries are left untouched in Parsing.Assignments; VariableEvaluatorSyntax resolves them to literals earlier on the same block, so by the time this syntax runs, every evaluable RHS is literal. The two syntaxes are decoupled.

HeidenhainVolatileQLookup

Reads Heidenhain volatile Q parameters (Q200+, the cycle/user range that resets at program start) from Vars.Volatile. Self-gates the id range so the evaluator's RuntimeVariableLookups chain can fall through for other keys (Q0-Q99/QRn resolve on the HeidenhainQParameterTable further down the chain; Q100-Q199 stay vacant by design). Sibling of the Fanuc VolatileVariableLookup with the same single-step traceback contract: HeidenhainQParameterReadingSyntax dict-merges every block's Vars.Volatile into the next block, so the entry — if it exists — is on the current block or the immediately previous one.

Keys are stored canonically as Q + id by the reader; this lookup re-canonicalises the incoming key the same way, so lowercase raw captures still resolve. Stateless and dependency-free.