Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Experimental G-code Parser Docs

This mdBook is the canonical documentation entry point for this repository.

Use these sections in this reading order:

Relationship between the sections:

  • Requirements says what the project should support.
  • Product Reference says what the public surface means today.
  • Execution Workflow explains how to drive the public execution API.
  • Execution Contract Review shows reviewed trace-based public behavior.
  • Development Reference is for maintainers, not users of the library.
  • Project Planning tracks prioritization and unfinished work.

Direct generated review entry: Open the generated execution contract review site

Policy:

  • Canonical project docs live in docs/src/.
  • Root README.md is the repository entry page.
  • Root AGENTS.md is the agent startup map, not the canonical docs body.
  • CI builds this book and publishes it from main.

Requirements

This section is the review-first source of truth for what the project should support, independent of current implementation status.

Use these documents before starting implementation work:

  • review target syntax/input forms
  • review modal and execution behavior
  • identify missing decisions and ambiguities
  • derive work units and test plans

Current requirement sets:

G-code Syntax Requirements

This subtree collects the review-first parser-input requirements for G-code syntax. It describes what input forms should be accepted or rejected, not only what is implemented today.

For each syntax family, review should answer:

  • what input forms must be accepted
  • what input forms must be rejected
  • what diagnostics are required
  • what parse structure should be preserved
  • what tests and fixtures must exist

Review Status

Status values used in this subtree:

  • Wanted
  • Reviewed
  • Deferred
  • Rejected

Current state:

  • Wanted: initial consolidated checklist extracted from the product spec (../../product/spec/index.md)
  • not yet fully reviewed item-by-item

Syntax Requirement Areas

Requirement Inventory Output

When review is complete, each syntax family should eventually carry:

  • reviewed status
  • canonical examples
  • invalid examples
  • owning tests and fixtures
  • linked work unit IDs

Syntax: Line and Program Structure

Status: Reviewed (2026-03-10 review pass)

Requirements:

  • line endings: \n and \r\n
  • optional block delete /
  • optional skip levels /0 .. /9
  • optional N line number at block start
  • words are case-insensitive
  • support file/program metadata forms such as leading %...
  • preserve comments as parse items where applicable
  • support unquoted program/subprogram names using only [A-Za-z0-9_]
  • document the unquoted-name restriction explicitly for users
  • support quoted subprogram names and targets such as "LIB/DRILL_CYCLE"
  • preserve quoted subprogram target text verbatim
  • block length baseline: 512 characters per block, excluding line ending

Reviewed decisions:

  • leading %... program metadata line is wanted
  • Siemens-style underscore-heavy names are wanted through the unquoted-name rule [A-Za-z0-9_]
  • quoted subprogram names and targets are wanted and may contain path-like text such as "LIB/DRILL_CYCLE"
  • quoted path-like names are syntax-level preserved strings; later execution policy may interpret them as file/path-like targets
  • block length baseline is 512 characters per block, excluding line ending
  • skip-level syntax and behavior must be stated explicitly in requirements

Examples:

  • valid: %MAIN
  • valid: %_N_MAIN_MPF
  • valid: PROC MAIN
  • valid: PROC _N_MAIN_SPF
  • valid: PROC "LIB/DRILL_CYCLE"
  • invalid unquoted name examples if these are used as identifiers:
    • PROC MAIN-PROG
    • PROC MAIN.PROG

Open follow-up questions:

  • exact accepted %... metadata character policy beyond the current baseline still needs a dedicated program-name review

Syntax: Comments and Whitespace

Status: Reviewed (2026-03-10 review pass)

Requirements:

  • ; ...
  • (* ... *)
  • optional // ... mode gate
  • whitespace and tabs should not change semantics beyond token separation
  • ( ... ) is reserved for expressions and grouping, not for comments

Reviewed decisions:

  • nested comments are rejected for now
  • text after ; is comment text to end of line
  • parenthesized text inside ; ... is just literal comment content
  • standalone ( ... ) comment syntax is not part of the target requirement set
  • in expressions and conditions, ( ... ) is reserved for grouping semantics, not comments

Examples:

  • valid line comment:
    • N0010 ;(CreateDate:Thu Aug 26 08:52:37 2021 by ZK)
  • valid grouping intent:
    • IF (R1 == 1) AND (R2 > 10)
  • rejected as comment syntax target:
    • (standalone comment)

Open follow-up questions:

  • whether (* ... *) remains wanted as a Siemens-style block-comment form
  • whether // ... should remain optional or become required in some mode

Syntax: Motion, Modal, Dwell, and M Functions

Motion Commands

Status: Reviewed (2026-03-10 review pass)

Families to review:

  • G0
  • G1
  • G2
  • G3

Per-family review checklist:

  • accepted address words
  • optional vs required words
  • invalid combinations
  • modal-group membership
  • parse output fields to preserve
  • representative valid and invalid fixtures

Examples to review:

  • G1 X10 Y20 Z30 F100
  • G1 AP=90 RP=10 F40
  • G2 X10 Y20 I1 J2 F50

Review note:

  • command-family status is reviewed, but detailed accepted and rejected address combinations still need to be written explicitly in a follow-up pass

Dwell and Timing Commands

Status: Reviewed (2026-03-10 review pass)

Families to review:

  • G4

Per-family review checklist:

  • accepted dwell words
  • mutually exclusive dwell modes
  • block-shape restrictions
  • parse output fields to preserve
  • representative valid and invalid fixtures

Examples to review:

  • G4 F3
  • G4 S30

Review note:

  • family status is reviewed; detailed value and block-shape constraints belong in the semantic requirements document and should be completed there

Status: Reviewed (2026-03-10 review pass)

Families to review:

  • rapid mode: RTLION, RTLIOF
  • tool radius compensation: G40, G41, G42
  • working plane: G17, G18, G19
  • dimensions, units, work offsets, and feed mode if wanted later

This page only covers the text forms. Runtime meaning belongs in the execution requirements subtree.

Review note:

  • syntax-family scope is reviewed; per-family accepted text forms still need more explicit examples where the spellings differ materially

M Functions

Status: Reviewed (2026-03-10 review pass)

Requirements to review:

  • M<value>
  • extended forms such as M<ext>=<value>
  • predefined Siemens baseline values wanted
  • invalid extension forms
  • parser diagnostics for malformed M words

Examples:

  • M3
  • M30
  • M17

Review note:

  • baseline family status is reviewed; detailed extension-shape rules still need to be written explicitly

Syntax: Variables, Control Flow, Subprograms, and Tools

Variables and Assignments

Status: Reviewed (2026-03-10 review pass)

Requirements to review:

  • user variables such as R1
  • system variables such as $P_ACT_X
  • selector forms such as $A_IN[1], $P_UIFR[1,X,TR]
  • assignment syntax:
    • R1 = 2
    • R1 = R2 + 1
    • R1 = $P_ACT_X + 1

Reviewed decisions:

  • selector shapes required for now:
    • $NAME[<int>]
    • $NAME[<int>,<ident>]
    • $NAME[<int>,<ident>,<ident>]
  • selector part rules for now:
    • first part must be a decimal integer
    • second and third parts, when present, must be identifiers [A-Za-z0-9_]+
  • reject for now:
    • nested brackets
    • quoted strings in selectors
    • arithmetic expressions in selectors
    • empty selector parts
    • more than 3 selector parts
    • non-integer first selector part
  • parenthesized subexpressions are wanted
  • parentheses are for grouping in expressions and conditions, not comments
  • general function-call-style expressions are not wanted for now
  • command-specific special forms, if any, should be reviewed separately instead of being treated as general expression syntax

Control Flow Syntax

Status: Reviewed (2026-03-10 review pass)

Families to review:

  • labels
  • GOTO, GOTOF, GOTOB, GOTOC
  • IF cond GOTO ... [ELSE GOTO ...]
  • structured IF / ELSE / ENDIF
  • WHILE / ENDWHILE
  • FOR / ENDFOR
  • REPEAT / UNTIL
  • LOOP / ENDLOOP

Review questions:

  • full condition grammar wanted?
  • AND, OR, and parentheses support?
  • indirect or system-variable jump targets wanted?

Review note:

  • family status is reviewed; the remaining questions should be converted into explicit accepted and rejected condition and target forms in a detailed pass

Subprogram Syntax

Status: Reviewed (2026-03-10 review pass)

Families to review:

  • direct subprogram call by name
  • quoted-name subprogram call
  • repeat count P
  • RET
  • M17
  • ISO-compatible M98 P...
  • PROC declaration forms

Examples:

  • THE_SHAPE
  • "THE_SHAPE"
  • THE_SHAPE P2
  • P=2 THE_SHAPE
  • RET
  • M17

Review note:

  • family status is reviewed; declaration and call-shape details should be expanded from the already-reviewed naming rules above

Tool Syntax

Status: Reviewed (2026-03-10 review pass)

Requirements to review:

  • T<number>
  • T=<number>
  • T<n>=<number>
  • management-on selector forms by location or name if wanted
  • relationship to M6

Review note:

  • family status is reviewed; mode-dependent selector shapes and M6 interaction still need detailed semantic follow-up

G-code Semantic Validation Requirements

This subtree collects non-syntax validation rules. These rules apply after text has been parsed into a structured form, but before execution.

Use this subtree for questions like:

  • the text parses, but is it allowed?
  • is a numeric value in range?
  • are these words legal together?
  • should this produce a warning or an error?

Review Status

Status values used in this subtree:

  • Wanted
  • Reviewed
  • Deferred
  • Rejected

Current state:

  • Wanted: initial checklist extracted from current parser and semantic behavior +- not yet fully reviewed item-by-item

+## Semantic Validation Areas + +- Motion, Dwell, and Modal Validation +- Variables, Control Flow, and Subprogram Validation +- Diagnostics Policy and Classification + +## Scope Reminder + +- syntax requirements answer whether text can be parsed into structured form +- semantic requirements answer whether a parsed command, value, or combination

  • is allowed +- execution requirements answer what should happen for valid input at runtime +EOF cat > docs/src/requirements/semantic/motion_dwell_and_modal_validation.md <<‘EOF’

Semantic: Motion, Dwell, and Modal Validation

Motion Command Validation

Status: Reviewed (2026-03-10 review pass)

Rules to review:

  • G1 must not mix Cartesian and polar endpoint modes in one block
  • G0 must not mix Cartesian and polar endpoint modes in one block, if that rule is desired consistently
  • one conflicting motion-family command per block should produce a diagnostic
  • arc-center words must match the effective working plane

Examples:

  • valid: G1 X10 Y20 F100
  • invalid: G1 X10 AP=90 RP=10
  • invalid under G17: G2 X1 Y2 K3

Review note:

  • family status is reviewed; detailed per-command invalid-combination rules still need to be expanded explicitly

Dwell Validation

Status: Reviewed (2026-03-10 review pass)

Rules to review:

  • G4 must be in a separate block
  • G4 requires exactly one dwell mode word
  • G4 F... dwell value must be positive
  • G4 S... dwell value must be positive
  • non-numeric dwell values are invalid

Examples:

  • valid: G4 F3
  • valid: G4 S30
  • invalid: G4 F-3
  • invalid: G4 F3 S30
  • invalid: G4
  • invalid: G4 X10

Review note:

  • family status is reviewed; exact diagnostic wording and severity still belong in the diagnostics-policy follow-up

Tool and Modal Validation

Status: Reviewed (2026-03-10 review pass)

Rules to review:

  • invalid tool selector form by tool-management mode
  • M6 with no pending tool selection policy classification
  • invalid modal family conflicts inside one block
  • duplicate modal codes in one block: warning, error, or allowed

Review note:

  • family status is reviewed; duplicate-modal severity and tool-policy classification still need narrower rule text

Semantic: Motion, Dwell, and Modal Validation

Semantic: Variables, Control Flow, and Subprogram Validation

Variable and Assignment Validation

Status: Reviewed (2026-03-10 review pass)

Rules to review:

  • allowed assignment left-hand sides
  • invalid selector forms
  • malformed expression structure
  • unsupported parenthesized or function-like expression forms
  • read-only vs writable variable categories, if enforced before execution

Examples:

  • valid: R1 = 2
  • valid: R1 = R2 + 1
  • invalid: malformed selector syntax

Reviewed decisions:

  • valid selector baseline:
    • $NAME[1]
    • $NAME[1,X]
    • $NAME[1,X,TR]
  • parenthesized subexpressions are part of the target expression language
  • general function-call-style expressions are not part of the baseline expression language for now
  • invalid selector baseline:
    • nested brackets
    • quoted-string selector parts
    • arithmetic inside selector parts
    • empty selector parts
    • more than 3 selector parts
    • non-integer first selector part
  • invalid expression baseline for now:
    • general function-call-style expressions such as SIN(30) or ABS(R1)

Control-Flow Validation

Status: Reviewed (2026-03-10 review pass)

Rules to review:

  • malformed IF/GOTO shapes
  • invalid jump target forms
  • unsupported condition operators and forms
  • duplicate or ambiguous labels / line-number targets
  • loop-family parse-only vs executable-lowering diagnostics

Important distinction:

  • unresolved forward targets during streaming input are usually execution-time or buffering concerns, not syntax concerns
  • malformed target text is still a syntax and semantic issue

Review note:

  • family status is reviewed; malformed-shape and invalid-target examples still need to be expanded into explicit accepted and rejected diagnostics

Subprogram Validation

Status: Reviewed (2026-03-10 review pass)

Rules to review:

  • malformed PROC declarations
  • malformed call shapes
  • invalid repeat counts
  • ISO-mode gated syntax and validation
  • unsupported inline arguments and signatures
  • unquoted subprogram names containing characters outside [A-Za-z0-9_] should be rejected with an explicit diagnostic
  • block length over 512 characters should be rejected as a validation error

Review note:

  • family status is reviewed; ISO-mode gating and repeat-count details still need explicit examples

Comment and Grouping Disambiguation

Status: Reviewed (2026-03-10 review pass)

Rules:

  • ; starts comment text to end of line
  • parenthesized text after ; is just part of the comment payload
  • standalone ( ... ) is not a target comment form
  • in expression and condition contexts, ( ... ) is reserved for grouping

Examples:

  • valid comment:
    • N0010 ;(CreateDate:Thu Aug 26 08:52:37 2021 by ZK)
  • valid grouping intent:
    • IF (R1 == 1) AND (R2 > 10)

Semantic: Diagnostics Policy and Classification

Diagnostics Policy

Status: Reviewed (2026-03-10 review pass)

For each semantic rule, review should define:

  • error vs warning
  • exact source attribution
  • fail-fast vs continue
  • whether the line is rejected

Review note:

  • policy status is reviewed; individual rule severities still need to be attached to specific semantic families over time

Classification Rule

Status: Reviewed (2026-03-10 review pass)

Use this split during review:

  • Syntax requirement:
    • can the text be parsed into structured form?
  • Semantic validation requirement:
    • the text parsed, but the command, value, or combination is invalid
  • Execution requirement:
    • what should happen for a valid command at runtime?

Example:

  • G4 F-3
    • syntax: valid
    • semantic: invalid, dwell value must be positive
    • execution: not reached

Execution Requirements

This subtree is the review-first source of truth for execution behavior, runtime boundaries, blocking semantics, diagnostics policy, and validation expectations.

Use these pages in this reading order:

The goal is to keep execution requirements reviewable by domain instead of in a single monolithic page.

Execution Requirements: Modal State and Streaming

This document collects modal-state and runtime/execution requirements. It is separate from syntax requirements on purpose.

For each execution family, review should answer:

  • what state must exist
  • when that state changes
  • what must block or wait
  • what runtime interface is responsible
  • what deterministic tests must exist

Review Status

Status values to use during review:

  • Wanted
  • Reviewed
  • Deferred
  • Rejected

Current state of this document:

  • Wanted: initial consolidated checklist extracted from current execution notes and streaming refactor work
  • not yet fully reviewed item-by-item

1. Modal State Model

Status: Reviewed (2026-03-10 review pass)

State families to review:

  • motion mode (G0/G1/G2/G3)
  • dwell/non-motion modal families where relevant
  • working plane
  • rapid interpolation mode
  • tool radius compensation
  • tool selection/change state
  • feed mode / units / dimensions / work offsets if wanted

Reviewed decisions:

  • every supported modal group should be explicit in the final execution model
  • future modal groups should be added by extending the explicit modal-state model, not by ad hoc side channels
  • if the parser/runtime accepts a modal family, the execution-state model should have a place to preserve it explicitly, even if runtime behavior is still partial
  • emitted execution commands should carry the effective values of the execution-relevant modal groups needed for correct downstream execution

Review note:

  • this decision sets the structural rule for modal modeling; the exact list of currently supported groups and which ones must be copied onto each command still need to be filled in explicitly

Reviewed decisions:

  • emitted command families should carry all effective supported modal-group values, not just a selected subset
  • no supported modal group should be treated as executor-internal-only by default; modal state is part of execution input and must remain visible in the modeled execution state
  • all supported modal groups must be preserved in execution state even when current runtime behavior for some groups is still partial or deferred

Follow-up implementation note:

  • the baseline field layout for emitted command snapshots is now established (motion_code, working_plane, rapid_mode, tool_radius_comp, active_tool_selection, pending_tool_selection), but future work still needs to extend that snapshot until it covers every supported modal group

2. Streaming Execution Model

Status: Reviewed (2026-03-11 review pass)

Requirements to review:

  • chunk input
  • line assembly
  • pushChunk(...)
  • pump()
  • finish()
  • resume(...)
  • cancel()

Required engine states to review:

  • ReadyToExecute
  • Blocked
  • WaitingForInput
  • Completed
  • Cancelled
  • Faulted

Reviewed decisions:

  • pushChunk(...) only buffers input and prepares internal buffered state; it does not autonomously drive execution
  • pump() advances execution until a meaningful boundary, not just exactly one command
  • finish():
    • marks EOF
    • flushes a final unterminated line if it forms a valid block
    • converts unresolved forward-target waits into faults if still unresolved at EOF
  • resume(...) is only for continuing from runtime/external blocking
  • cancel():
    • attempts to cancel any pending runtime wait/action
    • moves the engine to Cancelled
    • prevents further execution progress afterward
  • Blocked and WaitingForInput are distinct states:
    • Blocked means runtime/external waiting
    • WaitingForInput means more G-code input/context is required
  • unresolved forward targets should behave as:
    • before EOF: WaitingForInput
    • after finish(): Faulted

Review note:

  • the high-level state-machine contract is now reviewed; the remaining follow-up work is mostly about per-feature execution semantics, not the streaming API shape itself

Execution Requirements: Runtime Boundaries and Action Commands

3. Runtime Interface Boundaries

Status: Reviewed (2026-03-11 partial boundary review)

Current intended split to review:

  • IRuntime
    • primitive machine/runtime operations
    • read variable
    • read system variable
    • write variable
    • submit motion/dwell
    • cancel wait
  • IExecutionRuntime
    • richer language-level execution semantics
    • condition evaluation
    • richer expression evaluation

Reviewed decisions:

  • the current split is kept:
    • IRuntime is the primitive machine/service boundary
    • IExecutionRuntime is the richer language-aware execution boundary
  • plain IRuntime must not own parsing, expression interpretation, condition interpretation, or control-flow semantics
  • plain IRuntime must remain limited to primitive runtime actions such as:
    • variable reads/writes
    • system-variable reads
    • motion/dwell submission
    • wait cancellation

Examples of logic that must not be added to plain IRuntime:

  • full condition evaluation such as IF (R1 == 1) AND (R2 > 10)
  • full expression interpretation such as (R1 + 2) * ABS(R3)
  • control-flow target resolution
  • AST- or parser-shape-specific execution entry points

Review note:

  • richer execution semantics still rely on primitive runtime operations in many cases; IExecutionRuntime sits above IRuntime, not beside it as a fully independent machine layer

Reviewed decisions:

  • all language-level evaluation that cannot be resolved as simple primitive runtime access should go through IExecutionRuntime
  • plain IRuntime remains the primitive substrate used for operations such as:
    • variable reads/writes
    • system-variable reads
    • motion/dwell submission
    • wait cancellation
  • IExecutionRuntime may use IRuntime operations underneath while performing richer evaluation or execution semantics

Examples of richer semantics that should go through IExecutionRuntime:

  • full condition evaluation
  • richer expression evaluation
  • parser-limited or context-dependent expression forms
  • execution semantics that depend on interpreter context rather than primitive machine/service access alone

General action-command pattern:

  • action-type execution commands should use a runtime submission model rather than assuming synchronous completion
  • runtime submission outcomes are:
    • Ready: accepted and no execution wait is required
    • Pending: accepted, but execution flow must wait for later resolution
    • Error: rejected or failed
  • Pending means the action was accepted by the runtime boundary, not necessarily that the physical/device-side activity is already complete
  • once the pending action is later resolved by runtime/external logic, resume(...) continues engine execution

This pattern applies broadly to action commands such as:

  • motion
  • dwell/timing
  • tool change
  • other runtime-managed machine/device actions added later

Execution Requirements: Motion and Timing

4. Motion Execution Requirements

Status: Reviewed (2026-03-11 review pass)

Per-command review:

  • how G0/G1/G2/G3 become normalized execution commands
  • what source information must be carried
  • what effective modal state must be attached
  • when motion submission blocks
  • what completion/cancel behavior is required

Reviewed decisions:

  • G0 and G1 should normalize into explicit linear-move command structures
  • G2 and G3 should normalize into explicit arc-move command structures
  • normalized motion commands should be sent through the runtime interface boundary rather than exposing raw parser words downstream
  • motion commands must carry source information including:
    • source file/path if known
    • physical input line number
    • block number N... if present
  • motion commands must carry all effective supported modal-group values
  • motion submission follows the general async action-command pattern:
    • Ready: accepted and execution can continue
    • Pending: accepted and engine enters Blocked
    • Error: execution faults
  • in the async model, Pending means the motion command was accepted by the runtime boundary, not necessarily physically completed
  • Pending does not mean “retry the same motion submission later from the engine/session side”
  • once runtime/external logic resolves the pending motion, resume(...) continues execution
  • readiness for a pending motion token is determined by the embedding runtime or run manager, not by the library
  • resume(...) must continue from the existing blocked execution state; it must not resubmit the original motion command
  • resume(...) should be invoked by the thread that owns the execution session/engine
  • for queue-backed runtimes, a valid readiness condition is that the held move was successfully pushed into the downstream queue
  • the runtime boundary is a poor fit for long blocking inside motion-submission calls; the preferred design is to return Pending quickly and manage the wait asynchronously on the runtime side
  • cancel() should attempt runtime cancellation of in-flight motion/wait and then move the engine to Cancelled

Review note:

  • the remaining follow-up is mainly command-field layout design, since the behavioral contract is now reviewed

5. Dwell / Timing Execution Requirements

Status: Reviewed (2026-03-11 review pass)

Requirements to review:

  • how G4 becomes a normalized dwell/timing command
  • what source information must be carried
  • what effective modal/timing state must be attached, if any
  • when dwell submission blocks
  • what completion/cancel behavior is required

Reviewed decisions:

  • G4 should normalize into an explicit dwell/timing command structure
  • normalized dwell/timing commands should be sent through the runtime interface boundary rather than exposing raw parser words downstream
  • dwell/timing commands must carry source information including:
    • source file/path if known
    • physical input line number
    • block number N... if present
  • dwell/timing commands must carry all effective supported modal-group values under the general execution-state rule
  • dwell/timing submission follows the general async action-command pattern:
    • Ready: accepted and execution can continue
    • Pending: accepted and engine enters Blocked
    • Error: execution faults
  • in the async model, Pending means the dwell/timing command was accepted by the runtime boundary, not necessarily fully completed
  • Pending does not mean “retry the same dwell/timing submission later from the engine/session side”
  • once runtime/external logic resolves the pending dwell/timing action, resume(...) continues execution
  • readiness for a pending dwell/timing token is determined by the embedding runtime or run manager, not by the library
  • resume(...) must continue from the existing blocked execution state; it must not resubmit the original dwell/timing command
  • cancel() should attempt runtime cancellation of in-flight dwell/timing wait and then move the engine to Cancelled

Review note:

  • the remaining follow-up is mainly dwell-command field layout design, since the behavioral contract is now reviewed

Execution Requirements: Variables, Control Flow, and Calls

6. Variable and Expression Runtime Requirements

Status: Reviewed (2026-03-11 review pass)

Requirements to review:

  • user-variable reads
  • system-variable reads
  • variable writes
  • simple numeric expression evaluation
  • richer expression evaluation
  • pending/error behavior

Explicit boundary to review:

  • plain IRuntime should cover only primitive and simple runtime cases
  • richer or parser-limited expression semantics should use IExecutionRuntime

Reviewed decisions:

  • user variables are executor-internal by default and may be evaluated directly by the executor
  • system-variable reads and writes must go through the runtime interface
  • runtime reads and writes may return Pending
  • executor should directly evaluate the supported baseline expression subset rather than routing all evaluation through runtime
  • richer or unsupported expression semantics must go through IExecutionRuntime
  • pending read/evaluation/write behavior should move the engine to Blocked until resume(...)
  • read/evaluation/write errors should fault execution with deterministic source attribution
  • assignment execution should:
    • evaluate the right-hand side first
    • then perform the write
    • if the write is pending, block after submission and continue on resume(...)

Review note:

  • the remaining follow-up is to enumerate the exact baseline executor-direct expression subset versus the richer IExecutionRuntime subset

7. Control-Flow Execution Requirements

Status: Reviewed (2026-03-11 review pass)

Families to review:

  • label resolution
  • GOTO/GOTOF/GOTOB/GOTOC
  • branch execution
  • structured IF/ELSE/ENDIF
  • loops
  • unresolved forward target behavior
  • WaitingForInput vs fault-at-EOF behavior

Review questions:

  • which control-flow families must be fully supported in streaming mode?
  • what should remain deferred?

Reviewed decisions:

  • all supported control-flow families should also be supported in streaming mode; the project should not intentionally maintain separate batch-only versus streaming-only control-flow semantics
  • labels and control-flow targets must be preserved in buffered execution state so they can be resolved incrementally
  • branch conditions are evaluated at execution time, not at parse time
  • unresolved forward jump/call targets behave as:
    • before EOF: WaitingForInput
    • after finish(): Faulted
  • backward or already-known targets should resolve immediately from buffered execution state

Review note:

  • the remaining follow-up is to confirm detailed behavior family by family (IF/ELSE/ENDIF, loops, and other structured forms) without changing the reviewed rule that streaming mode should support the full accepted control-flow set

8. Subprogram and Call-Stack Requirements

Status: Reviewed (2026-03-11 review pass)

Requirements to review:

  • call stack model
  • target resolution
  • repeat count behavior
  • return behavior
  • unresolved target policy
  • interaction with streaming incremental input

Reviewed decisions:

  • subprogram calls must use a real nested call stack
  • each subprogram call pushes a return frame
  • each return pops a return frame and resumes at the saved return point
  • nested subprogram calls must be supported naturally through the call stack
  • subprogram targets should be resolved from buffered execution state
  • unresolved forward subprogram targets behave as:
    • before EOF: WaitingForInput
    • after finish(): Faulted
  • repeat count means repeated subprogram invocation
  • each repeat uses normal call/return behavior
  • a return with an empty call stack is invalid and must fault
  • subprogram/call-stack behavior should be supported in streaming mode under the same buffered execution model as other control-flow features
  • subprogram target resolution and call-stack control are executor behavior, not plain IRuntime behavior

Review note:

  • the remaining follow-up is mostly detailed call-frame layout and any controller-specific subprogram-mode differences, not the core behavioral contract

Execution Requirements: Tools, Diagnostics, and Validation

9. Tool Execution Requirements

Status: Reviewed (2026-03-12 review pass)

Requirements to review:

  • tool_select
  • tool_change
  • deferred vs immediate timing
  • pending tool state
  • M6 behavior with/without pending selection
  • substitution/fallback policy model

Reviewed decisions:

  • tool_select and tool_change are separate execution concepts
  • tool selection prepares the tool state but does not mount the tool onto the spindle
  • tool change performs the actual spindle-mounted tool change
  • actual tool change should be triggered later by M6 or the equivalent change-trigger command, rather than by selection alone
  • tool-change execution should go through the runtime interface boundary
  • tool-change execution should be modeled as asynchronous and normally use the general Pending action-command behavior
  • pending/current/selected tool state should remain explicit in execution state
  • if M6 occurs with no pending tool selection, the executor should use the current active/default tool selection as the tool-change target
  • if the active/default tool is already mounted, runtime may treat that tool change as a no-op action
  • if M6 occurs with neither a pending tool selection nor an active/default tool selection, execution should fault

Review note:

  • the main tool-selection/tool-change execution split and M6 no-pending policy are now reviewed; further controller-specific substitution/fallback details can still be refined later

10. Diagnostics and Failure Policy

Status: Reviewed (2026-03-12 review pass)

Requirements to review:

  • syntax diagnostic vs runtime diagnostic
  • rejected line vs executor fault
  • warning/error/ignore policies
  • deterministic source attribution
  • duplicate or repeated diagnostics policy

Reviewed decisions:

  • syntax/semantic problems should produce syntax/semantic diagnostics
  • runtime/execution failures should produce runtime/execution diagnostics or faults
  • the engine should support halting at a line with syntax/semantic failure while preserving enough buffered state that execution can continue once that line is corrected and reprocessed
  • unsupported constructs should default to Error
  • duplicate constructs that are still executable should default to Warning
  • anything ambiguous or unsafe for execution should default to Error
  • diagnostics and faults must carry deterministic source attribution including:
    • source file/path if known
    • physical input line number
    • block number N... if present
  • diagnostics should not be globally deduplicated across the whole program
  • diagnostics may be deduplicated within the same line/block/execution event to avoid repeated identical reports for the same underlying problem

Review note:

  • the remaining follow-up is to attach exact warning/error choices to specific semantic families over time, but the default policy is now reviewed

11. Test and Validation Requirements

Status: Reviewed (2026-03-12 review pass)

Required test families to review:

  • parser unit tests
  • lowering/AIL tests
  • executor tests
  • streaming execution tests
  • fake-log CLI traces
  • golden fixtures
  • fuzz/sanitizer gate via ./dev/check.sh

Reviewed decisions:

  • each work unit should declare which test layers it affects rather than forcing every work unit to add every possible test type
  • golden files should be used for:
    • parser output
    • CLI output
    • fake-log/event traces
    • serialized structured output
  • direct assertions should be used for:
    • executor/runtime API behavior
    • call sequence and state-machine behavior
    • focused semantic checks
  • execution/runtime/streaming changes should include a reviewable fake-log CLI trace or equivalent event-trace coverage
  • every work unit should record:
    • exact test names
    • exact commands
    • fixture files used
  • ./dev/check.sh remains the required final validation gate for implementation slices
  • every work unit should map back to:
    • requirement section(s)
    • test file(s)
    • fixture(s)
  • every feature/work unit should include invalid/failure cases where applicable
  • streaming-related changes should explicitly cover:
    • chunk split cases
    • no trailing newline
    • WaitingForInput
    • Blocked
    • resume(...)
    • cancel()
    • EOF unresolved-target fault

Review note:

  • the test policy is now reviewed; the remaining work is applying it consistently to future work units and implementation slices

12. Work-Unit Readiness

A work unit should not start implementation until the relevant requirement subset is:

  • reviewed
  • assigned clear in-scope/out-of-scope boundaries
  • paired with expected tests
  • paired with unresolved-question list if decisions are still missing

Product Reference

Use this section for product-level behavior, scope, API expectations, and the implemented program reference.

Put content here when it explains:

  • supported syntax
  • meaning and semantics
  • public behavior and user-visible diagnostics
  • examples of valid or invalid program usage

Do not put maintainer-only content here, such as:

  • test file inventories
  • verification command recipes
  • implementation-only rationale
  • repo-internal ownership notes

Pages in this section:

PRD

This section describes product goals, scope, API expectations, release criteria, and the major Siemens-aligned feature domains.

Use these pages:

Suggested review order:

  1. Read Goals and Scope.
  2. Confirm API Requirements.
  3. Review the functional scope pages by domain.
  4. Finish with Quality and Release.

PRD: Goals and Scope

Review Guide

Suggested review order:

  1. confirm scope and non-goals
  2. confirm API constraints
  3. review Siemens functional requirements by domain
  4. mark each major subsection as:
    • Approved
    • Needs Clarification
    • Out of Scope

Functional Scope Index

The old monolithic PRD Section 5 no longer appears as a single chapter; its content is now split across these pages:

The major requirement areas moved from that old Section 5 are:

  • comments
  • tool change
  • Group 7 tool radius compensation
  • working plane
  • Group 15 feed semantics
  • dimensions and units
  • work offsets
  • exact-stop and continuous-path modes
  • rapid traverse
  • M-code semantics
  • incremental parse/edit-resume
  • system and user variables

Acceptance Fixture

Reserved integrated acceptance scenario:

  • testdata/integration/simple_integrated_case.ngc

Purpose:

  • validate mixed modal, motion, and program-flow syntax in one realistic file
  • verify deterministic parse/lower/runtime mapping

1. Product Purpose

Provide a production-usable C++ library that converts G-code text/files into:

  • precise diagnostics
  • typed lowering/runtime artifacts for downstream integrations

Primary users:

  • CNC application and backend developers integrating parsing into planners, simulators, editors, and execution pipelines

2. Product Goals

  • deterministic parsing and lowering
  • actionable diagnostics
  • stable integration surface
  • test-first delivery with golden, regression, and fuzz-smoke coverage

3. Non-Goals

  • executing machine motion directly
  • full controller runtime semantics in the original v0 scope
  • full macro and subprogram runtime completeness in the original v0 scope

PRD: API Requirements

4. API Product Requirements

4.1 Main API Shape

Preferred public integration shape:

  • a class facade as the main surface
  • free-function entry points retained for compatibility

Target facade:

  • GCodeParser::parseText
  • GCodeParser::parseFile
  • GCodeParser::parseAndLowerText
  • GCodeParser::parseAndLowerFile
  • streaming variants with callback or pull APIs
  • cancellation and early-stop support

4.2 File Input Requirement

  • library API must support reading directly from file paths
  • file read failures must return diagnostics rather than crash

4.3 Output Requirement

Parser output:

  • ParseResult
    • AST
    • diagnostics

Lowering output:

  • MessageResult
    • typed messages
    • diagnostics
    • rejected_lines

Output access points:

  • library return values
  • optional JSON conversion helpers
  • CLI output via gcode_parse --format debug|json

PRD: Functional Scope Overview

5. Functional Scope

5.1 Current Scope

  • G1, G2, G3, G4 syntax, diagnostics, and typed lowering
  • fail-fast lowering at the first error line
  • JSON round-trip support for message results

5.2 Next Scope

  • prioritized extension of additional G and M families
  • richer syntax coverage and diagnostics
  • API facade hardening and file-based API convenience

5.2.1 Executable-Action Modeling Contract

Architecture rule:

  • every machine-carried action must be represented as an explicit executable instruction in IR
  • packetization is optional per instruction family
  • parse-only metadata may remain non-executable

Each new machine-action feature must define:

  • instruction kind and payload
  • execution path:
    • packet
    • runtime/control path
  • deterministic diagnostic and policy behavior

PRD: Siemens Motion and Modal Domains

This page groups the major Siemens-aligned motion and modal requirements that shape parser, AIL, and runtime design.

5.3 Siemens Comment Syntax

Required forms:

  • ; ...
  • (* ... *)
  • optional // ... behind explicit compatibility mode

Acceptance expectation:

  • comment text preserved in parse output
  • comments do not alter executable token order outside the comment span
  • malformed or unclosed block comment returns actionable diagnostics

5.4 Siemens Tool-Change Semantics

Required behavior families:

  • without tool management
    • direct-change mode
    • preselect plus M6
  • with tool management
    • selection by location or name
    • substitution handled by runtime policy

Parser/runtime must preserve:

  • raw command form
  • resolved intent
  • timing
  • optional spindle extension
  • selector kind

5.5 Tool Radius Compensation

Required modal group:

  • Group 7
    • G40
    • G41
    • G42

Requirement:

  • model Group 7 explicitly and track it independently from motion state

5.6 Working Plane Selection

Required commands:

  • G17
  • G18
  • G19

Requirement:

  • working plane must propagate into arc interpretation and compensation-related behavior

5.7 Feedrate Semantics

Required Group 15 coverage includes:

  • G93
  • G94
  • G95
  • G96
  • G97
  • G931
  • G961
  • G971
  • G942
  • G952
  • G962
  • G972
  • G973

Required supporting forms:

  • F...
  • FGROUP(...)
  • FGREF[...]
  • FL[...]

Requirement:

  • feed state must remain explicit and composable with motion, plane, and compensation state

5.8 Dimensions and Unit Semantics

Required coverage:

  • Group 14:
    • G90
    • G91
  • local overrides:
    • AC(...)
    • IC(...)
  • rotary targeting:
    • DC(...)
    • ACP(...)
    • ACN(...)
  • unit modes:
    • G70
    • G71
    • G700
    • G710
  • turning diameter/radius families:
    • DIAMON
    • DIAM90
    • DIAMOF
    • related axis-specific and value-override forms

5.9 Settable Work Offsets

Required Group 8 coverage:

  • G500
  • G54
  • G55
  • G56
  • G57
  • G505 .. G599

Required suppress commands:

  • G53
  • G153
  • SUPA

5.10 Exact-Stop and Continuous-Path Modes

Required modal groups:

  • Group 10:
    • G60
    • G64
    • G641
    • G642
    • G643
    • G644
    • G645
  • Group 11:
    • G9
  • Group 12:
    • G601
    • G602
    • G603

Required parameter support:

  • ADIS=...
  • ADISPOS=...

5.11 Rapid Traverse

Required coverage:

  • G0
  • RTLION
  • RTLIOF

Requirement:

  • preserve rapid-mode state and effective override decisions explicitly

5.12 M-Code Syntax and Semantics

Required baseline predefined families include:

  • flow control:
    • M0
    • M1
    • M2
    • M17
    • M30
  • spindle:
    • M3
    • M4
    • M5
    • M19
    • M70
  • tool:
    • M6
  • gear:
    • M40 .. M45

Requirement:

  • parsing preserves raw code, normalized identity, and source
  • runtime semantics for machine-specific free M numbers must remain pluggable

PRD: Variables and Program Structure

5.13 Incremental Parse/Edit-Resume

Primary workflow:

  1. parse and stop at the first failing line under fail-fast policy
  2. edit the failing line
  3. resume from that line while preserving the accepted prefix

Expected session operations:

  • firstErrorLine()
  • applyLineEdit(...)
  • reparseFromLine(...)
  • reparseFromFirstError()

Requirement:

  • prefix behavior remains stable
  • resumed suffix diagnostics and outputs are deterministic

5.14 Variables

Required categories:

  • user-defined variables such as R1
  • system variables such as $P_ACT_X
  • structured system-variable forms such as $A_IN[1] and $P_UIFR[1,X,TR]

Required usage forms:

  • assignment
  • expressions
  • conditions

Behavior boundary:

  • parse/lowering preserves variable references structurally
  • runtime resolves values and access policy

Diagnostics must distinguish:

  • parse-time syntax errors
  • runtime access/protection failures
  • runtime pending and unavailable outcomes

Siemens Fundamental NC Syntax Principles

Required coverage:

  • program naming
  • block numbering and block length limits
  • Siemens = rules
  • comments
  • skip blocks and skip levels

Behavior boundary:

  • parse stage recognizes and preserves the structure
  • runtime decides effective skip behavior

Siemens Subprograms and Call Semantics

Required coverage:

  • direct subprogram calls by name
  • repeat counts with P
  • optional ISO-compat M98
  • M17 and optional RET
  • optional future parameterized declaration/call forms

Behavior boundary:

  • parse/lowering preserves call/declaration structure
  • runtime resolves targets and manages call stack behavior

PRD: Quality and Release

6. Command Family Policy

  • G codes:
    • implement only families explicitly listed in spec and backlog
  • M codes:
    • parsing visibility is allowed, but lowering/execution behavior must be specified before support is claimed
  • vendor extensions:
    • require explicit spec entry and tests
  • variable families:
    • require explicit product/spec treatment before runtime semantics are enabled

7. Quality Requirements

  • no crashes or hangs on malformed input
  • deterministic diagnostics and output ordering
  • ./dev/check.sh must pass for merge
  • every bug fix gets regression coverage
  • streaming APIs must support large-input workflows without requiring full output buffering
  • maintain benchmark visibility for large inputs

8. Documentation and Traceability

Canonical linked product docs:

  • docs/src/product/prd/index.md
  • docs/src/product/spec/index.md
  • docs/src/product/program_reference/index.md
  • docs/src/project/backlog.md

Every feature PR updates all impacted docs in the same PR.

9. Release Acceptance

  • API usable for text and file workflows
  • supported families documented and test-proven
  • no open P0/P1 correctness or crash issues
  • CI and ./dev/check.sh green

SPEC

This section is the product behavior contract for the parser and the current public execution model.

Use these pages:

Review order:

  1. Start with Input and Output.
  2. Read the syntax pages for accepted surface language.
  3. Read the lowering and execution pages for runtime-facing behavior.
  4. Finish with testing and documentation policy.

SPEC: Input and Output

1. Purpose

Parse G-code text into a simple syntax-level model with diagnostics and supported downstream lowering/execution paths.

Current implementation focus:

  • parser and diagnostics
  • AIL lowering
  • packet/message compatibility surfaces
  • public ExecutionSession execution for the supported motion subset

2. Input and Output

2.1 Input

  • UTF-8 text
  • line endings: \n or \r\n
  • tabs are whitespace
  • comments:
    • ; ... to end of line
    • ( ... ) with no nesting
    • (* ... *) block comments
    • optional // ... when ParseOptions.enable_double_slash_comments=true

2.2 Output

The parser returns:

  • AST (program -> lines -> items)
  • diagnostics with 1-based line and column

Supported downstream stages:

  • AST -> typed AIL instructions
  • AIL -> motion packets
  • public streaming execution through ExecutionSession

Normative execution-model contract:

  • machine-executable semantics must be represented as explicit typed AIL instructions
  • packet emission is an execution transport, not a requirement for every instruction
  • non-motion control instructions may execute through runtime/control paths without standalone motion packets

Current primary execution API slice:

  • line-by-line streaming execution engine under the public ExecutionSession surface
  • injected execution sink, runtime, and cancellation interfaces
  • explicit blocked, resumed, cancelled, rejected, faulted, and completed states
  • current public execution coverage: G0/G1/G2/G3/G4 plus the supported runtime-backed control/runtime subset

CLI surface

gcode_parse supports:

  • --mode parse|ail|packet|lower
  • parse mode:
    • --format debug
    • --format json
  • lower mode:
    • --format json
    • --format debug
  • ail mode:
    • --format json
    • --format debug
  • packet mode:
    • --format json
    • --format debug

Installed public distribution

The project supports installation to a public prefix for downstream consumers and hidden black-box validation.

Installed public surface:

  • public headers under include/gcode/
  • the public parser library target
  • installed CLI binaries:
    • gcode_parse
    • gcode_stream_exec
    • gcode_exec_session
    • gcode_execution_contract_review
  • exported CMake package metadata under lib/cmake/gcode/
  • generated mdBook docs under share/gcode/docs/ when built
  • generated execution-contract review HTML under share/gcode/execution-contract-review/ when built

Downstream consumers should use the installed prefix rather than repository relative source paths.

AST shape

  • Program: ordered list of Line
  • Line:
    • optional block_delete
    • optional line_number
    • ordered items
  • Word:
    • text
    • head
    • optional value
    • has_equal
  • Comment:
    • text

SPEC: Motion and Program Syntax

3. Supported Syntax

3.1 Line structure

  • optional block delete: /
  • optional skip level: /0 through /9
  • optional line number: N<integer>
  • one motion command per line in GGroup1
  • word letters are case-insensitive
  • N block number must appear at block start
  • block length overflow is rejected deterministically

3.2 Numbers

  • integers: 0, 10, -3, +7
  • decimals: 1.0, -2.5, .5, 5., +.25
  • scientific notation is not supported in v0

3.3 G0 / G1

Supported words:

  • G0
  • G1
  • Cartesian endpoints: X/Y/Z/A/B/C
  • polar endpoints: AP=..., RP=...
  • feed: F...

Rules:

  • mixed Cartesian and polar endpoints in one line are rejected
  • G0 is represented as a linear-motion family member with modal code G0
  • RTLION and RTLIOF control rapid interpolation mode in the execution model
  • current runtime-backed axis support is limited to X/Y/Z/A/B/C
  • supported runtime-facing variable names in this slice:
    • simple $NAME
    • single-selector $NAME[part]
  • deferred in this slice:
    • multi-selector forms like $P_UIFR[1,X,TR]
    • feed expressions
    • arc-word expressions

Examples:

G0 X20 Y10
G1 Z-2 F40
G1 X100 Y100 Z200
G1 AP=90 RP=10 F40
G1 G94 X100 Y20 Z30 A40 F100
G1 X=$P_ACT_X
G1 X=$AA_IM[X]

3.4 G2 / G3

Supported words:

  • G2 clockwise arc
  • G3 counterclockwise arc
  • endpoints: X/Y/Z
  • center words: I/J/K
  • radius words: CR, AR
  • polar endpoints: AP, RP
  • intermediate-point form: CIP with I1/J1/K1
  • tangential-circle form: CT

Plane behavior:

  • AIL arc instructions carry plane_effective
  • packet arc payloads carry plane_effective
  • working-plane state constrains legal center words

3.5 G4

  • G4 must be programmed in a separate NC block
  • exactly one dwell value is required:
    • F... seconds
    • S... spindle revolutions
  • prior feed and spindle state remain valid after dwell

3.8 Program naming and metadata

Current baseline:

  • leading %... metadata line is accepted as external program metadata
  • normalized name must remain non-empty and alphanumeric-led after normalization
  • inline trailing comments are excluded from normalized metadata name
  • the metadata line is preserved but not lowered as an executable block

Planned follow-up:

  • stricter Siemens naming rules
  • richer invalid-name diagnostics under compatibility modes

3.9 Subprogram syntax

Current baseline:

  • direct call by bare or quoted name
  • repeat count forms:
    • <name> P<count>
    • P=<count> <name>
  • optional ISO call:
    • M98 P<id> when ISO mode is enabled
  • returns:
    • RET
    • M17
  • baseline declarations:
    • PROC <name>
    • PROC "<name>"

Execution/runtime baseline:

  • subprogram_call resolves against labels
  • repeat count loops the same target before resuming caller
  • unresolved target or empty-stack return faults

3.10 M functions

Baseline syntax:

  • M<value>
  • M<address_extension>=<value>

Baseline rules:

  • value range: 0..2147483647
  • extended-address form requires =
  • core flow M codes reject extended-address form
  • known predefined Siemens M functions execute as accepted no-op control instructions in v0
  • unknown M functions follow executor policy:
    • error
    • warning
    • ignore

3.11 Tool radius compensation

Supported words:

  • G40
  • G41
  • G42

Current scope:

  • parse
  • AIL emission
  • executor state tracking

Current limitation:

  • no geometric cutter compensation is applied in v0

3.12 Working plane selection

Supported words:

  • G17
  • G18
  • G19

Current scope:

  • parse
  • AIL emission
  • executor state tracking
  • arc-center validation against active plane

3.13 Tool selector syntax

Machine mode flag:

  • tool_management

When tool_management=false:

  • accepted:
    • T<number>
    • T=<number>
    • T<n>=<number>
  • rejected:
    • non-numeric selector values
    • indexed form without =

When tool_management=true:

  • accepted:
    • T=<location_or_name>
    • T<n>=<location_or_name>
  • rejected:
    • legacy numeric shortcut without =

Runtime baseline:

  • deferred mode stores pending tool selection until M6
  • direct mode submits immediately
  • M6 without pending selection falls back to active selection when present
  • runtime tool acceptance follows the same Ready|Pending|Error contract as motion and dwell

SPEC: Control Flow and Variables Syntax

3.6 Assignment Expressions

Statement form:

  • <lhs> = <expr>

Current lhs support:

  • R-style variables such as R1

Current expression operands:

  • numeric literals
  • variable words such as R2
  • system variables such as $P_ACT_X

Current system-variable scope:

  • simple token form such as $P_ACT_X
  • single-selector form such as $A_IN[1] or $AA_IM[X]
  • simple and single-selector forms are supported in assignment expressions and control-flow conditions
  • multi-selector forms such as $P_UIFR[1,X,TR] are deferred

Supported operators:

  • unary: +, -
  • binary: +, -, *, /

Parenthesized subexpressions are not supported in v0 expression syntax.

Implemented Siemens baseline checks:

  • = is required for multi-letter or numeric-extension address values
  • single-letter plus single-constant omission remains accepted

Planned extension

  • structured variable references for richer Siemens forms
  • preserved variable-reference structure in AIL
  • fuller Siemens expression support

3.7 Control Flow Syntax

Supported goto directions:

  • GOTOF
  • GOTOB
  • GOTO
  • GOTOC

Supported targets:

  • labels
  • block numbers such as N100
  • numeric targets such as 200
  • system-variable tokens for runtime indirection

Supported forms:

  • IF <condition> GOTOx <target>
  • optional THEN
  • optional ELSE GOTOx <target>
  • structured blocks:
    • IF ... ELSE ... ENDIF
    • WHILE ... ENDWHILE
    • FOR ... TO ... ENDFOR
    • REPEAT ... UNTIL
    • LOOP ... ENDLOOP

Supported condition operators:

  • ==
  • >
  • <
  • >=
  • <=
  • <>

Supported logical composition:

  • AND across condition terms
  • parenthesized terms are preserved for runtime resolver handling

Notes:

  • label names follow word-style identifiers and may include underscores
  • duplicate N line numbers are accepted with warning
  • parse stage is syntax-first; resolution happens later
  • AIL preserves simple system-variable references in conditions and goto targets

Example:

N100 G00 X10 Y10
GOTO END_CODE
N110 (This block is skipped)
N120 (This block is skipped)
END_CODE:
N130 G01 X20 Y20

SPEC: Diagnostics and Lowering

4. Non-goals

  • full modal-group validation beyond the current supported motion rule
  • units and distance-mode completeness
  • full RS274NGC expression/macro support
  • full subprogram execution semantics in the original v0.1 scope

5. Diagnostics

  • syntax errors are reported with 1-based line and column
  • syntax messages should be actionable and prefixed with syntax error:
  • semantic diagnostics include explicit correction hints

Current semantic error examples:

  • mixed Cartesian and polar coordinates in one G0/G1 line
  • multiple motion commands in one line
  • invalid G4 block composition
  • invalid dwell mode/value combinations

Planned Siemens compatibility diagnostics:

  • malformed skip-level marker
  • invalid assignment-shape diagnostics for Siemens = rules

6. Message Lowering

Current supported paths:

  • parse -> AIL
  • parse -> AIL -> packets
  • parse -> messages as a compatibility surface

Legacy note:

  • older batch message/session APIs remain for compatibility and tooling
  • the public execution architecture is centered on ExecutionSession

Result model

  • diagnostics
  • lowered instructions or packets/messages
  • rejected lines for fail-fast policies

JSON schema notes

  • top-level schema_version
  • ordered payload lists
  • deterministic diagnostics
  • deterministic rejected_lines

Packet stage

Packet result shape:

  • packets
  • diagnostics
  • rejected_lines

Current packetization converts motion and dwell AIL instructions. Non-motion instructions are skipped with warning diagnostics where appropriate.

6.1 Control-Flow AIL and Executor

AIL may include:

  • label
  • goto
  • branch_if

Structured control flow is lowered into explicit linear control flow with generated labels and gotos.

Runtime/executor contract:

  • branch resolution uses injected resolver interfaces
  • condition evaluation may be:
    • true
    • false
    • pending
    • error
  • variable references are preserved structurally for runtime resolution
  • skip-level execution is applied at lowering/execution time from configured active levels

Executor state model:

  • ready
  • blocked
  • completed
  • fault

Target resolution behavior:

  • GOTOF: forward-only
  • GOTOB: backward-only
  • GOTO: forward, then backward
  • GOTOC: non-faulting unresolved-target variant

Simple system-variable goto targets are still unresolved in the v0 executor model and follow the fault-or-continue behavior of the opcode.

SPEC: Execution and Runtime Contract

6.2 Streaming Execution Engine

Primary interfaces:

  • execution sink
  • runtime
  • cancellation

Per-line motion execution contract

  • G1 is normalized into a line-scoped linear-move command
  • the engine emits the normalized command to the sink
  • the engine submits the same command to the runtime
  • runtime returns:
    • ready
    • pending
    • error

Blocked/resume rules:

  • on pending, execution becomes blocked
  • pending means the runtime accepted responsibility for the command
  • pending does not mean the library should resubmit the same command later
  • resume(token) means the wait for that exact token is already satisfied
  • resume(token) continues the blocked state and must not resubmit the original command
  • queue-backed runtimes should return pending quickly and resume only after the held command has actually been pushed downstream

Runtime read contract

System-variable reads may return:

  • ready value
  • pending token
  • error

Execution-contract fixtures support:

  • fixed ready-valued runtime.system_variables
  • ordered runtime.system_variable_reads

Ordered read scripts support per-attempt outcomes:

  • ready
  • pending
  • error

Tracing rule:

  • every read attempt emits a system_variable_read event
  • if resume retries the same read, the retry emits a fresh read event

Supported runtime-backed public cases include:

  • if_system_variable_false_branch
  • G1 X=$P_ACT_X
  • G1 X=$AA_IM[X]

Current name support:

  • simple $NAME
  • single-selector $NAME[part]

Deferred:

  • multi-selector forms
  • feed expressions
  • arc-word expressions

Public session status

ExecutionSession supports:

  • buffered text input
  • line-by-line parse/lower/execute
  • explicit finish, pump, resume, and cancel
  • editable suffix recovery after rejected lines

Public-session behavior currently includes:

  • supported motion execution
  • cross-line GOTO
  • baseline structured IF/ELSE/ENDIF
  • baseline system-variable conditions through runtime reads

Execution-contract fixture baseline

Persistent public fixture files live under:

  • testdata/execution_contract/core/

Observable fixture options include:

  • filename
  • active_skip_levels
  • tool_change_mode
  • enable_iso_m98_calls

Driver actions currently supported:

  • finish
  • resume_blocked
  • cancel_blocked

Linear-move result scripting currently supports:

  • ready
  • pending with explicit wait token

System-variable read scripting currently supports:

  • outcome: ready with value
  • outcome: pending with token
  • outcome: error with message

Reference-vs-actual comparison is exact semantic equality.

Generated actual traces are written under:

  • output/execution_contract_review/

Generated review HTML can be published under:

  • docs/book/execution-contract-review/

Additional runtime boundaries

The current public execution model also includes explicit contracts for:

  • M-function execution policy
  • subprogram call and return boundaries
  • tool selection and tool change behavior
  • combined motion/runtime and condition/runtime integrations through IExecutionRuntime

The detailed architecture remains in the development design docs. This page is the product-facing summary of the supported runtime contract.

SPEC: Testing, Architecture, and Documentation Policy

7. Testing Expectations

  • golden tests cover representative behavior documented in the product docs
  • CLI tests cover parse, ail, packet, and lower modes in debug and JSON forms
  • unit test framework for new tests is GoogleTest
  • regression rule:
    • every fixed bug gets a failing-first test
  • parser fuzz/property smoke tests must not crash, hang, or grow without bound
  • streaming execution tests validate:
    • chunk assembly
    • sink/runtime call order
    • normalized command arguments
    • blocked/resume/cancel state transitions
    • deterministic event logs
  • maintain benchmark visibility for large deterministic corpora

8. Code Architecture Requirements

  • use modular object-oriented design
  • major function families live in separate classes/files
  • do not accumulate all family behavior in one monolithic source file
  • new families should be added as modules, not long branching chains

9. Documentation Policy

Canonical docs live under docs/src/.

Key documentation surfaces:

  • docs/src/product/program_reference/index.md
  • docs/src/product/prd/index.md
  • docs/src/product/spec/index.md
  • docs/src/development/

Documentation rules:

  • every feature PR updates impacted docs in the same PR
  • the program reference describes implemented behavior, not only plans
  • every documented command/function includes status:
    • Implemented
    • Partial
    • Planned
  • if a page gets too long, split it and link the subtree through docs/src/SUMMARY.md
  • planned behavior must be marked as planned until code and tests land
  • CI must build the mdBook and publish it from main

PR gate expectation:

  • list updated product sections
  • list updated development/reference sections
  • list tests proving the documented behavior

Program Reference

This section describes the currently implemented parser, lowering, packet, and public execution behavior.

Use this section for public reference material only:

  • supported syntax and command families
  • current public runtime behavior
  • user-visible diagnostics and invalid-program behavior

Maintainer-only test inventories live in Development: Testing Reference.

Use these pages:

Suggested reading order:

  1. Start with APIs and Status Matrix.
  2. Read Motion and Modal Commands for the command-family coverage that is already implemented.
  3. Read Control Flow and Runtime for the current execution boundary and runtime behavior.
  4. Use Diagnostics and Invalid Programs for public error behavior and common invalid shapes.

Program Reference: APIs and Status Matrix

Output APIs

Primary public APIs:

  • ExecutionSession
  • injected interfaces:
    • execution sink
    • runtime
    • cancellation

Additive executable IR API:

  • AilExecutor::step(now_ms, sink, runtime)
  • uses the same IExecutionSink / IExecutionRuntime motion contract for motion-capable AIL instructions
  • AilExecutorOptions.initial_state can seed modal context when an executor instance needs to inherit prior plane, rapid, or tool-comp state

Public parser and lowering APIs:

  • parse(...) -> ParseResult
  • parseAndLowerAil(...) -> AilResult

Current limitations:

  • the public execution path is ExecutionSession; lower-level engine control is internal
  • AilExecutor::step(...) currently dispatches motion and dwell instructions through the shared runtime path
  • loops remain parse-only; the public execution path currently implements buffered GOTO/label flow and structured IF/ELSE/ENDIF, but not loop families

Every emitted message includes modal metadata:

  • group: GGroup1 or GGroup2
  • code: emitted function code such as G0, G1, G2, G3, G4
  • updates_state: whether this message updates modal state

Current supported baseline:

  • G0/G1/G2/G3 -> GGroup1, updates_state=true
  • G4 -> GGroup2, updates_state=false

Command Status Matrix

CommandStatusNotes
G0 rapid baselineImplementedEmits G0Message with target pose and feed.
RTLION / RTLIOF rapid interpolation modePartialLowered to AIL rapid_mode; full machine-actuation semantics still pending.
G40 / G41 / G42 tool radius compensationPartialLowered to AIL tool_radius_comp; executor tracks modal state only.
G17 / G18 / G19 working planePartialLowered to AIL working_plane; executor tracks active plane state only.
G1 linearImplementedCompatibility surfaces emit G1Message; execution emits normalized linear-move commands.
G2 arc CWImplementedEmits G2Message; execution can dispatch normalized arc commands.
G3 arc CCWImplementedEmits G3Message; execution can dispatch normalized arc commands.
G4 dwellImplementedEmits G4Message; execution can dispatch normalized dwell commands.
M functions (M<value>, M<ext>=<value>)PartialParse + validation + AIL boundary only; runtime machine actions are not fully mapped.
R... = expr assignmentPartialParsed and lowered to AIL assign; richer expression and write semantics are deferred.
N... line number at block startImplementedParsed into source metadata with duplicate-warning support.
Block delete / and /0.. /9ImplementedParsed with skip-level metadata; lowering applies active_skip_levels.
GOTO/GOTOF/GOTOB/GOTOC + labelsImplementedParsed, lowered, and executable through public ExecutionSession.
IF cond GOTO ... [ELSE GOTO ...]PartialParsed/lowered to branch_if; public execution handles the baseline expression subset.
Structured IF/ELSE/ENDIFPartialLowered into label/goto control flow; public execution supports the baseline branch subset.
WHILE/ENDWHILE, FOR/ENDFOR, REPEAT/UNTIL, LOOP/ENDLOOPPartialParse-only in current implementation.
Comments ;..., ( ... ), (* ... *)ImplementedPreserved as comment items in parse output.
Comments // ...PartialSupported only when ParseOptions.enable_double_slash_comments=true.
Subprogram call by name (THE_SHAPE, "THE_SHAPE")PlannedSiemens-style call model planned; parser/runtime integration pending.
Subprogram repeat (P=<n> NAME, NAME P<n>)PlannedRepeat-count call semantics planned.
Subprogram return (M17 baseline, RET optional)PlannedReturn-to-caller runtime semantics planned.
ISO compat call (M98 P...)PlannedEnabled only by ISO-compatibility profile option.

Parse and Lower Options

Parse options:

  • ParseOptions.enable_double_slash_comments
    • false (default): // ... emits a diagnostic
    • true: // ... is accepted as a comment

Lower options:

  • LowerOptions.active_skip_levels
    • block-delete lines (/ => level 0, /n => level n) are skipped when the level is active

Program Reference: Motion and Modal Commands

Motion Packets

Status:

  • Implemented for the motion subset

Rules:

  • packetization consumes AIL and emits packets for G0/G1/G2/G3/G4
  • packet_id is 1-based and deterministic per result order
  • non-motion AIL instructions are skipped with warning diagnostics where appropriate
  • G0 and G1 both use packet type linear_move; distinguish intent through packet.modal.code

Output fields:

  • packet_id
  • type
  • source
  • modal
  • payload

Packet types:

  • linear_move
  • arc_move
  • dwell

G1

Syntax examples:

  • G1 X10 Y20 Z30 F100
  • G1 AP=90 RP=10 F40

Rules:

  • case-insensitive words are accepted
  • mixing Cartesian and polar words in one G1 line is rejected

Output fields:

  • source: filename, line, line_number
  • modal: group=GGroup1, code=G1, updates_state=true
  • target_pose: optional x/y/z/a/b/c
  • feed: optional F

Execution call sequence:

  1. a complete G1 line is assembled
  2. the line is parsed and lowered into a normalized linear-move command
  3. the execution sink receives the normalized command
  4. the runtime receives the same command via linear-move submission
  5. the runtime returns ready, pending, or error
  6. pending blocks execution until explicit resume

G0

Syntax examples:

  • G0 X10 Y20 Z30
  • G0 AP=90 RP=10

Rules:

  • case-insensitive words are accepted
  • mixing Cartesian and polar words in one G0 line is rejected

Output fields:

  • source: filename, line, line_number
  • modal: group=GGroup1, code=G0, updates_state=true
  • target_pose: optional x/y/z/a/b/c
  • feed: optional F

Current limitations:

  • Siemens rapid interpolation override behavior is not fully implemented in the runtime or packet path

RTLION / RTLIOF

Syntax examples:

  • RTLION
  • RTLIOF

Current behavior:

  • lowering emits AIL rapid_mode
  • JSON includes:
    • opcode
    • mode
  • following G0 AIL instructions include rapid_mode_effective when a rapid-mode command is active earlier in program order

Current limitations:

  • runtime execution tracks rapid-mode state transitions, but full machine interpolation override behavior is not implemented yet
  • packetization does not emit standalone packets for rapid_mode

G40 / G41 / G42

Syntax examples:

  • G40
  • G41
  • G42

Current behavior:

  • lowering emits AIL tool_radius_comp
  • JSON includes:
    • opcode
    • mode: off, left, right
  • AilExecutor tracks tool_radius_comp_current

Current limitations:

  • cutter-path geometric compensation is not implemented in v0
  • packetization does not emit standalone packets for tool_radius_comp

G17 / G18 / G19

Syntax examples:

  • G17
  • G18
  • G19

Current behavior:

  • lowering emits AIL working_plane
  • JSON includes:
    • opcode
    • plane: xy, zx, yz
  • AilExecutor tracks working_plane_current
  • G2/G3 lowering validates center words against the effective plane

Current limitations:

  • deeper plane-dependent geometric behavior is not implemented in v0
  • packetization does not emit standalone packets for working_plane

G2 / G3

Syntax examples:

  • G2 X10 Y20 I1 J2 K3 R40 F100
  • G3 X30 Y40 I4 J5 K6 CR=50 F200

Output fields:

  • source: filename, line, line_number
  • modal: group=GGroup1, code=G2|G3, updates_state=true
  • target_pose: optional x/y/z/a/b/c
  • arc: optional i/j/k/r
  • feed: optional F
  • effective plane metadata derived from the active working plane

G4

Syntax examples:

  • G4 F3
  • G4 S30

Rules:

  • must be programmed in its own NC block
  • exactly one of F or S must be present

Output fields:

  • source: filename, line, line_number
  • modal: group=GGroup2, code=G4, updates_state=false
  • dwell_mode: seconds or revolutions
  • dwell_value: numeric value

Program Reference: Control Flow and Runtime

AilExecutor Control-Flow Examples

These examples describe the current AilExecutor runtime contract, not the current public ExecutionSession buffered-session behavior.

GOTO

Input:

N10 GOTO END
N20 G1 X10
END:
N30 G1 X20

Runtime effect in AilExecutor:

  • parser lowers GOTO END and END: into control-flow instructions
  • execution jumps to END
  • N20 G1 X10 is skipped
  • only N30 G1 X20 reaches the runtime as a move command

IF / ELSE / ENDIF

Input:

IF $P_ACT_X > 100
  G1 X0
ELSE
  G1 X10
ENDIF

Runtime effect in AilExecutor:

  • the condition is evaluated at execution time, not parse time
  • if $P_ACT_X > 100, only G1 X0 reaches the runtime
  • otherwise only G1 X10 reaches the runtime

See Execution Workflow for the current ExecutionSession boundary and when to drop down to AilExecutor.

M Functions

Status:

  • Partial (parse + validation + AIL emission + executor boundary policy)

Supported syntax:

  • M<value>
  • M<address_extension>=<value>

Validation:

  • M value must be integer 0..2147483647
  • extended form must use equals syntax
  • extended form is rejected for predefined stop/end families:
    • M0
    • M1
    • M2
    • M17
    • M30

AIL output:

  • emits m_function instructions with:
    • source
    • value
    • optional address extension

Executor boundary behavior:

  • known predefined Siemens M values are accepted and advanced without machine actuation in v0
  • unknown M values follow executor policy:
    • error
    • warning
    • ignore

Current limitation:

  • runtime machine-function execution mapping is not implemented in this slice

Control Flow and Runtime

Implemented behavior:

  • GOTO target search:
    • GOTOF: forward only
    • GOTOB: backward only
    • GOTO: forward then backward
    • GOTOC: same search as GOTO, unresolved target does not fault
  • target kinds:
    • label
    • N line number
    • numeric line number
    • system-variable token target
  • AilExecutor branch resolver contract:
    • injected resolver interface returns true, false, pending, or error
    • pending supports wait_token and retry timestamp
    • function/lambda overload remains as a compatibility adapter
  • structured IF/ELSE/ENDIF lowering uses branch_if plus internal labels and gotos

Current limitations:

  • loop-family statements remain parse-only
  • live system-variable evaluation remains a runtime-resolver responsibility, not parser/lowering behavior

Program Reference: Diagnostics and Invalid Programs

This page covers user-visible parser diagnostics and common invalid program shapes.

Parser Diagnostics Highlights

Implemented checks include:

  • block length limit: 512 chars including LF
  • invalid skip-level value (/0.. /9 enforced)
  • invalid or misplaced N address words
  • duplicate N warning when line-number jumps are present
  • G4 block-shape checks
  • assignment-shape baseline (AP90 rejected; AP=90, X1=10 accepted)

Use the requirements and SPEC pages for the broader diagnostic contract:

Execution Workflow

This page answers the practical question:

If I want to execute G-code with this library, what do I create, what interfaces do I implement, and what loop do I run?

The supported public execution entry point is:

  • ExecutionSession

StreamingExecutionEngine still exists inside the implementation, but it is an internal building block and not the intended integration surface.

Public Headers

For execution, start with these public headers:

  • gcode/execution_commands.h
  • gcode/execution_interfaces.h
  • gcode/execution_runtime.h
  • gcode/execution_session.h

Use these additional headers only if you separately need syntax or IR access:

  • gcode/gcode_parser.h
  • gcode/ast.h
  • gcode/ail.h

What You Need To Implement

To execute G-code you usually provide three objects:

  1. IExecutionSink
    • receives normalized commands and diagnostics
    • receives explicit modal-only updates through onModalUpdate(...)
  2. IRuntime or IExecutionRuntime
    • performs slow or external work
  3. ICancellation
    • tells the session whether execution should stop

1. Execution Sink

The sink receives normalized commands, not raw G1 X10 Y20 words.

#include "gcode/execution_interfaces.h"

class MySink : public gcode::IExecutionSink {
public:
  void onDiagnostic(const gcode::Diagnostic &diag) override {
    // Log or display diagnostics.
  }

  void onRejectedLine(const gcode::RejectedLineEvent &event) override {
    // Show a recoverable rejected-line error in the UI.
  }

  void onModalUpdate(const gcode::ModalUpdateEvent &event) override {
    // Observe modal-only changes such as G17/G18/G19 or G40/G41/G42.
  }

  void onLinearMove(const gcode::LinearMoveCommand &cmd) override {
    // Observe the normalized move command.
  }

  void onArcMove(const gcode::ArcMoveCommand &cmd) override {}
  void onDwell(const gcode::DwellCommand &cmd) override {}
  void onToolChange(const gcode::ToolChangeCommand &cmd) override {}
};

2. Runtime Interface

The runtime performs external work such as motion submission, dwell, tool change, and system-variable access.

class MyRuntime : public gcode::IRuntime {
public:
  gcode::RuntimeResult<gcode::WaitToken>
  submitLinearMove(const gcode::LinearMoveCommand &cmd) override;

  gcode::RuntimeResult<gcode::WaitToken>
  submitArcMove(const gcode::ArcMoveCommand &cmd) override;

  gcode::RuntimeResult<gcode::WaitToken>
  submitDwell(const gcode::DwellCommand &cmd) override;

  gcode::RuntimeResult<gcode::WaitToken>
  submitToolChange(const gcode::ToolChangeCommand &cmd) override;

  gcode::RuntimeResult<double>
  readSystemVariable(std::string_view name) override;

  gcode::RuntimeResult<gcode::WaitToken>
  cancelWait(const gcode::WaitToken &token) override;
};

If you also want one object that can handle richer language-aware evaluation, implement IExecutionRuntime instead of plain IRuntime.

Runtime-Backed G0/G1 Axis Words

The public execution path also supports runtime-backed system variables in G0/G1 axis words with explicit = form, for example:

G1 X=$P_ACT_X
G0 Z=$P_SET_Z
G1 X=$AA_IM[X]
G0 Z=$A_IN[1]

Execution behavior is:

  • ExecutionSession preserves the exact runtime-facing variable name and resolves it through IRuntime.readSystemVariable(...)
  • supported name shapes in this slice are:
    • simple $NAME
    • single-selector $NAME[part]
  • if the read is Ready, the sink/runtime receive a normal numeric LinearMoveCommand
  • if the read is Pending, the session becomes Blocked before any LinearMoveCommand is emitted or submitted
  • resume(token) re-evaluates that same motion instruction; it does not resubmit a previously accepted move

Current limits:

  • supported only on G0/G1
  • supported only on axis words X/Y/Z/A/B/C
  • multi-selector forms like $P_UIFR[1,X,TR], feed expressions, and arc-word expressions remain deferred

3. Cancellation Interface

class MyCancellation : public gcode::ICancellation {
public:
  bool isCancelled() const override { return cancelled_; }
  bool cancelled_ = false;
};

What Data You Receive

The session emits normalized command structs:

  • ModalUpdateEvent
  • LinearMoveCommand
  • ArcMoveCommand
  • DwellCommand
  • ToolChangeCommand

Each command carries:

  • source
    • file name if known
    • physical line number
    • N... block number if present
  • command payload
    • modal changes, pose target, feed, arc parameters, dwell mode/value, or tool target
  • EffectiveModalSnapshot
    • effective modal state attached to that command

That means your runtime does not need to reinterpret raw G-code text.

StepResult Meanings

ExecutionSession methods return StepResult.

Important values:

  • Progress
    • execution advanced, keep going
  • Blocked
    • runtime accepted an async action and execution must wait
  • Rejected
    • rejected line is recoverable
  • Completed
    • execution finished successfully
  • Cancelled
    • caller requested stop
  • Faulted
    • unrecoverable failure

Important distinction:

  • Rejected
    • recoverable by editing the rejected suffix
  • Faulted
    • not recoverable through the normal edit-and-continue path

Simple Use Case 1: Execute A Program

#include "gcode/execution_session.h"

MySink sink;
MyRuntime runtime;
MyCancellation cancellation;

gcode::ExecutionSession session(sink, runtime, cancellation);

session.pushChunk("N10 G1 X10 Y20 F100\n");
session.pushChunk("N20 G4 F3\n");

gcode::StepResult step = session.finish();
while (step.status == gcode::StepStatus::Progress) {
  step = session.pump();
}

if (step.status == gcode::StepStatus::Blocked) {
  // Your runtime/planner completed the async action later.
  step = session.resume(step.blocked->token);
}

Use this when:

  • you want the normal execution API
  • you do not want to build your own low-level engine wrapper
  • you may later need recovery behavior

Simple Use Case 2: Feed Live Text Chunks

If input arrives in pieces, keep feeding chunks and pumping.

session.pushChunk("N10 G1 X");
session.pushChunk("10 Y20 F100\n");

for (;;) {
  const auto step = session.pump();
  if (step.status == gcode::StepStatus::Progress) {
    continue;
  }
  break;
}

Rules:

  • pushChunk(...)
    • only buffers/prepares more input
  • pump()
    • advances execution using input already buffered
  • finish()
    • says no more input is coming

Simple Use Case 3: Halt-Fix-Continue Recovery

session.pushChunk("G1 X10\nG1 G2 X15\nG1 X20\n");

gcode::StepResult step = session.finish();
while (step.status == gcode::StepStatus::Progress) {
  step = session.pump();
}

if (step.status == gcode::StepStatus::Rejected) {
  session.replaceEditableSuffix("G1 X15\nG1 X20\n");
  do {
    step = session.pump();
  } while (step.status == gcode::StepStatus::Progress);
}

Recovery rules:

  • the rejected line and all later lines form the editable suffix
  • the accepted prefix before the rejected line stays locked
  • replaceEditableSuffix(...) replaces the editable suffix
  • retry uses the stored rejected boundary automatically

Simple Use Case 4: Asynchronous Runtime

Most external actions follow one pattern:

  1. session emits a normalized command to the sink
  2. session calls the runtime
  3. runtime returns:
    • Ready
    • Pending
    • Error

Meaning:

  • Ready
    • accepted and execution can continue now
  • Pending
    • accepted by the runtime boundary, but execution must pause until you later call resume(...)
  • Error
    • execution becomes Faulted

Important async contract details:

  • Pending does not mean “call the same submission again later”
  • once the runtime returns Pending(token), the runtime side owns the wait for that command
  • resume(token) means the external/runtime wait for that exact token is now satisfied
  • resume(token) must continue the existing blocked execution state; it does not resubmit the original command
  • readiness for a token is determined by the embedding runtime or run manager, not by the library
  • resume(token) should be invoked by the thread that owns ExecutionSession
  • cancel() is mainly intended for a session that is already Blocked, so the runtime can abort the pending work through cancelWait(token)

For a queue-backed runtime, a valid definition of “token satisfied” is:

  • the runtime held the move because the downstream queue was full
  • later, queue space became available
  • the runtime successfully pushed the held move into that queue
  • then the session-owning thread calls resume(token)

That means the API is a poor fit for long blocking inside submitLinearMove(...). A better integration shape is:

  • return Pending(token) quickly
  • manage the wait asynchronously in the runtime/run manager
  • notify the session-owning thread when the token is ready to resume

The library does not provide that notification channel; it belongs to the embedding application.

This pattern applies to:

  • motion
  • dwell
  • tool change
  • other runtime-managed actions

Current Control-Flow Boundary

Today ExecutionSession:

  • buffers the editable suffix as one executable program view
  • handles motion/dwell/tool-change dispatch, async waiting, rejection recovery, modal carry-forward, and buffered control-flow resolution
  • executes forward GOTO/label flows and structured IF/ELSE/ENDIF on the public path

Practical consequences:

  • unresolved forward GOTO/branch targets can wait for later input before EOF
  • structured IF/ELSE/ENDIF runs through ExecutionSession
  • branch conditions in the baseline expression subset do not need IExecutionRuntime; the plain IRuntime adapter only reports condition resolution as unavailable when the condition falls outside the executor-owned supported subset
const auto lowered = gcode::parseAndLowerAil(program_text);
gcode::AilExecutor exec(lowered.instructions);

while (exec.state().status == gcode::ExecutorStatus::Ready) {
  exec.step(0, sink, execution_runtime);
}

Demo CLIs

Main public-workflow demo:

./build/gcode_exec_session --format debug testdata/execution/session_reject.ngc

Recover by replacing the editable suffix:

./build/gcode_exec_session --format debug \
  --replace-editable-suffix testdata/execution/session_fix_suffix.ngc \
  testdata/execution/session_reject.ngc

Internal engine-inspection demo:

./build/gcode_stream_exec --format debug testdata/execution/plane_switch.ngc

If you are integrating this library into a controller:

  1. implement IExecutionSink
  2. implement IRuntime
  3. create ExecutionSession
  4. use:
    • pushChunk(...)
    • pump()
    • finish()
    • resume(...)
    • cancel()
  5. add replaceEditableSuffix(...) only if you need recoverable operator-style correction after a rejected line

Execution Contract Review

This page documents the human-reviewable execution contract fixture workflow.

Direct entry:

The fixture system validates the public ExecutionSession behavior with:

  • persistent .ngc input files
  • persistent approved .events.yaml reference traces
  • generated .actual.yaml traces from the current code
  • a generated static HTML comparison site

Source Of Truth

Reference fixtures live in:

  • testdata/execution_contract/core/

Each supported case uses:

  • <case>.ngc
  • <case>.events.yaml

Fixtures also declare explicit execution/lowering inputs under:

  • options

Fixtures may also declare an explicit session driver for multi-step public interactions:

  • driver

Fixtures may also declare deterministic runtime inputs in the reference trace:

  • runtime.system_variables
  • runtime.system_variable_reads
  • runtime.linear_move_results

The current fixture-level options mirror the public LowerOptions fields that can affect observable execution behavior:

  • filename
  • active_skip_levels
  • tool_change_mode
  • enable_iso_m98_calls

For simple fixed-value cases, fixtures may keep using:

  • runtime.system_variables

When trace timing matters, fixtures may declare an ordered runtime-read script:

  • runtime.system_variable_reads

The read script is consumed in strict execution order, so it can model:

  • repeated reads of the same variable with different values
  • motion reads followed by later condition reads
  • post-resume reads that should appear later in the trace
  • per-attempt ready, pending, and error outcomes for the same variable

For the first async fixture slice, runtime.linear_move_results can declare a deterministic sequence of linear-move submission outcomes such as:

  • ready
  • pending with an explicit wait token

If a fixture omits driver, the review runner uses the current default single-step behavior:

  • one implicit finish

Enforced Scope

The current enforced core suite covers:

  • modal_update
  • linear_move_completed
  • linear_move_blocked
  • linear_move_cancelled
  • linear_move_block_resume
  • pending_system_variable_read
  • error_system_variable_read
  • linear_move_system_variable_x
  • linear_move_system_variable_selector_x
  • motion_then_condition_system_variable_reads
  • repeated_system_variable_reads
  • rapid_move_system_variable_z
  • resumed_system_variable_read
  • dwell_seconds_completed
  • tool_change_deferred_m6
  • rejected_invalid_line
  • fault_unresolved_target
  • goto_skips_line
  • if_else_branch
  • if_system_variable_false_branch
  • if_system_variable_selector_false_branch

Event Model

Reference traces are ordered event lists. Step 1 uses:

  • diagnostic
  • rejected
  • modal_update
  • system_variable_read
  • linear_move
  • dwell
  • tool_change
  • faulted
  • completed

modal_update events contain only the changed modal fields, so every fixture declares an explicit initial_state.

The first async extension keeps the same event model and adds a fixture-level driver so reviewed traces can cover:

  • linear_move -> blocked
  • linear_move -> blocked -> linear_move -> completed
  • linear_move -> blocked -> cancelled

The driver now supports:

  • finish
  • resume_blocked
  • cancel_blocked

system_variable_read is a contract-trace event, not a new public sink callback. It records:

  • the source line where execution performed the read
  • the variable name
  • the per-attempt outcome:
    • ready
    • pending
    • error
  • for ready, the returned value
  • for pending, the wait token
  • for error, the runtime error message

If a pending read later resumes and execution retries that same read, the retry emits a fresh system_variable_read event instead of mutating the earlier pending event in place.

Review CLI

Build:

cmake -S . -B build
cmake --build build -j --target gcode_execution_contract_review

Generate actual traces and a local HTML site:

./build/gcode_execution_contract_review \
  --fixtures-root testdata/execution_contract/core \
  --output-root output/execution_contract_review

This writes:

  • actual traces under output/execution_contract_review/core/
  • a local HTML site under output/execution_contract_review/site/

For the normal combined docs build, use:

./dev/build_docs_site.sh

When published, the generated review site is available under:

That generated subtree is written into docs/src/generated/ during the docs build, ignored by git, and then copied into the final mdBook output so one mdbook serve instance can host both the handwritten pages and the generated review site on the same port.

Equality Rule

Automated tests compare reference and actual traces with exact semantic equality.

Allowed normalization is limited to serialization details such as key order and line endings. If behavior changes intentionally, update the reference fixture explicitly.

Development Reference

This section collects repository workflow, contribution rules, OODA guidance, and developer architecture notes for the parser project.

Use this section for:

  • local build and validation workflow
  • contribution expectations
  • task selection and OODA execution
  • docs policy and documentation placement rules
  • test strategy, test references, and verification entry points
  • architecture and design rationale

Key subsections:

Documentation Policy

This page records the repository’s documentation structure rules and the writing preferences the team expects agents to follow.

Canonical Locations

Canonical project documentation lives in docs/src/.

Allowed root markdown files:

  • README.md
  • AGENTS.md
  • CHANGELOG_AGENT.md

Rules:

  • README.md is the repository entry page.
  • AGENTS.md is the agent startup map, not the full docs body.
  • CHANGELOG_AGENT.md is the agent-facing change record.
  • All other canonical documentation belongs under docs/src/.

mdBook Is Canonical

The mdBook under docs/src/ is the canonical reading surface.

Rules:

  • important docs should be reachable from docs/src/SUMMARY.md
  • chapter order should reflect reading order, not repo history
  • index pages should explain where to read next

Current intended top-level reading order:

  1. Requirements
  2. Product Reference
  3. Execution Workflow
  4. Execution Contract Review
  5. Development Reference
  6. Project Planning

AGENTS.md Role

AGENTS.md must stay short and stable.

It should:

  • explain the repo purpose briefly
  • identify the command gate: ./dev/check.sh
  • require the session to act as either developer or integration tester
  • tell the agent to ask if the role is not already clear
  • point into docs/src/ for the real documentation body

It should not become an encyclopedia.

Tree Structure Preference

Documentation should stay small and tree-structured.

Rules:

  • prefer short index pages plus focused child pages
  • if a page grows too large or mixes several concerns, split it into a subtree
  • avoid large monolithic pages when a topic naturally separates into domains

Recommended limits:

  • AGENTS.md should remain short
  • chapter index pages should remain short and navigational
  • large content pages should be split once they become difficult to scan in one pass

Chapter Ownership

Keep chapter ownership clear at a high level:

  • requirements/ = review-first target behavior
  • product/ = public-facing meaning and behavior
  • development/ = workflow, testing, verification, architecture

Avoid mixing maintainer-only material into public reference pages.

Non-Redundancy

Prefer one canonical home for each topic.

Rules:

  • do not duplicate the same explanation across multiple chapters
  • move material to the section that owns the concern
  • prefer deletion or a link over repeating the same framing twice

Agent Guidance

Repo-local agent guidance for documentation lives in:

  • .agents/skills/write-docs/SKILL.md

Use that skill for writing or reorganizing docs. Use the linter for mechanical enforcement.

Generated Docs

Generated docs are outputs, not canonical sources.

Rules:

  • checked-in source docs live under docs/src/
  • generated HTML output is validated by mdbook build docs
  • review sites and generated book output must not become the only source of truth

Development Workflow

Repository Layout

  • src/: parser, AST, diagnostics, lowering, streaming APIs.
  • test/: GoogleTest unit, regression, and fuzz-smoke suites.
  • testdata/: golden and fixture assets.
  • dev/: local scripts such as check.sh and bench.sh.
  • docs/: mdBook sources and checked-in Mermaid assets.

Build

cmake -S . -B build
cmake --build build -j

Quality Gate

./dev/check.sh

./dev/check.sh runs format check, configure/build, tests, tidy checks, and sanitizer tests.

Documentation Build

cargo install mdbook mdbook-mermaid
mdbook build docs
mdbook serve docs --open

Mermaid diagrams in docs/src/development/design/*.md require mdbook-mermaid.

Public Install Prefix

The repo can also install a public prefix for downstream consumers and external black-box validation:

cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/tmp/gcode-install
cmake --build build -j
cmake --install build

The installed prefix includes:

  • public headers under include/gcode/
  • public CLI binaries under bin/
  • the public library under lib/
  • exported CMake package metadata under lib/cmake/gcode/

When generated outputs are present, install also publishes:

  • mdBook docs under share/gcode/docs/
  • execution-contract review HTML under share/gcode/execution-contract-review/

CLI Modes

gcode_parse supports:

  • --mode parse for AST + diagnostics output
  • --mode ail for intermediate AIL instruction output (json or debug summary)
  • --mode packet for AIL->packet output (json or debug summary)
  • --mode lower for lowered message output (json or debug summary)
  • CLI stage goldens are stored in testdata/cli/ and should be updated in the same PR when stage output intentionally changes.

OODA Development Loop

  1. Observe current repo and CI state.
  2. Orient against ../project/roadmap.md, ../project/backlog.md, and ../product/spec/index.md.
  3. Decide one scoped backlog slice.
  4. Act with code, tests, and docs updates in one PR.

Contribution Requirements

  • Keep C++17.
  • Add or adjust tests for every feature.
  • Update ../product/spec/index.md if behavior changes.
  • Update CHANGELOG_AGENT.md in every change.
  • Keep docs/ aligned with implementation changes.

OODA

Objective

Run repository development as an explicit Observe-Orient-Decide-Act loop so work is directed, testable, and non-random.

Loop

1) Observe

Collect current signals before choosing work:

  • git state: branch, uncommitted changes, ahead/behind.
  • CI status: latest workflow result and failing jobs.
  • Test health: ./dev/check.sh output.
  • Spec drift: differences between implementation/tests and ../product/spec/index.md.
  • Quality signals: diagnostics quality, parser failures, fuzz/crash findings.
  • Open work: unblocked items at top of ../project/backlog.md.

Required artifacts:

  • Short observation summary in PR description or task notes.

2) Orient

Interpret observations against priorities:

  • First priority: broken mainline, failing CI, crash/hang risk.
  • Second priority: SPEC compliance gaps.
  • Third priority: roadmap milestone progress.

Output:

  • selected task ID(s) from ../project/backlog.md
  • explicit risks and assumptions

3) Decide

Choose one small slice with clear acceptance criteria.

Decision rules:

  • Prefer smallest change that reduces highest risk.
  • Avoid combining unrelated backlog items in one PR.
  • If behavior changes, update SPEC in same PR.

Output:

  • branch name (feature/<task-or-scope>)
  • acceptance criteria checklist copied from backlog item

4) Act

Implement and validate.

Mandatory actions per feature PR:

  • code changes
  • tests in test/
  • updates to ../product/spec/index.md when behavior changes
  • update CHANGELOG_AGENT.md
  • run ./dev/check.sh

PR must include:

  • evidence of passing ./dev/check.sh
  • list of new/updated tests
  • list of SPEC sections covered

Merge Policy

A PR can merge only if all are true:

  • CI required checks pass.
  • Acceptance criteria are satisfied.
  • No unresolved P0/P1 concerns introduced by the PR.
  • SPEC/tests/changelog requirements are satisfied.
  • Scope is a single coherent backlog slice.
  • Human reviewer/user explicitly requests merge approval.

Agent workflow rule:

  • The agent may create PRs, but must not execute merge until the user explicitly asks to merge that specific PR.

Definition of Done

A task is done only when:

  • Implementation merged to main.
  • Tests proving behavior exist and pass.
  • Regression test added for every bug fixed.
  • PR links bug/issue identifier to regression test name(s).
  • ../project/backlog.md updated (task moved/marked done).
  • ../project/roadmap.md updated if milestone status changed.

Cadence

  • Run OODA loop at least once per PR.
  • Re-run Observe/Orient immediately when CI fails or new blocker appears.

Development: Dual-Agent Workflow

This is lightweight workflow guidance for using two Codex sessions on the same feature:

  • developer
  • integration tester

This is guidance, not a hard repo policy.

Goal

Use the split to get:

  • clearer production-code ownership
  • stronger black-box testing
  • faster discovery of contract mismatches
  • less self-confirming implementation/test behavior

Roles

Developer

Primary ownership:

  • include/
  • src/
  • behavior docs and spec updates when behavior changes

Developer may also write:

  • internal unit tests
  • white-box regression tests for internal classes/helpers
  • refactor-safety tests that depend on implementation detail

Developer should generally avoid:

  • editing execution-contract fixtures in testdata/execution_contract/
  • owning public black-box contract expectations unless explicitly intended

Integration Tester

Primary ownership:

  • public-behavior tests
  • integration tests
  • execution-contract fixtures
  • testdata/
  • test-only helpers under test/

Integration tester should generally avoid:

  • editing include/
  • editing src/
  • changing production behavior to satisfy tests

Test Boundary

Use this rule:

  • if a test depends on private structure or internal helper behavior, it belongs to the developer
  • if a test checks user-visible behavior, public contracts, fixtures, CLI behavior, or end-to-end execution flow, it belongs to the integration tester

Examples:

  • developer-owned:

    • parser helper unit tests
    • executor internal state transition tests
    • refactor regressions for internal support types
  • integration-tester-owned:

    • ExecutionSession public behavior
    • execution-contract review fixtures
    • integration/regression tests across parser/lowering/execution boundaries
  1. User and developer discuss the feature, contract, and implementation intent.
  2. Developer implements production code and commits it.
  3. Integration tester checks out that committed developer state.
  4. Integration tester adds public-behavior and contract tests only.
  5. If tests fail:
    • developer fixes production code, or
    • tester fixes an incorrect test
  6. Repeat until:
    • production code
    • tests
    • docs/spec
    • ./dev/check.sh are all green.

Branch And Workspace Model

Recommended setup:

  • developer workspace:
    • main repo working tree or a dedicated developer worktree
  • integration tester workspace:
    • separate git worktree or standalone clone

Recommended branch pattern:

  • feature/<topic>-dev
  • feature/<topic>-test

Or, if you prefer a simpler flow:

  • one shared feature branch
  • two separate worktrees
  • only committed handoffs between roles

The important rule is:

  • the integration tester should test committed developer code, not uncommitted local edits

Disagreement Rule

When developer and integration tester disagree:

  • public contract/spec wins
  • reviewed fixture behavior wins over assumptions
  • if contract wording is unclear, escalate to the human owner before changing production code or reference fixtures

Completion Rule

A feature is done only when:

  • production code is in the intended state
  • tests are accepted
  • docs/spec are updated where behavior changed
  • ./dev/check.sh passes

Notes

  • This workflow is especially useful for execution-contract and regression-heavy work.
  • It is less important for tiny docs-only or formatting-only changes.
  • If the split becomes cumbersome, keep the role boundary by test purpose rather than forcing every test change through the integration tester.

Related page:

Development: Dual-Agent Feature Checklist

Use this checklist when running a feature through the developer + integration tester workflow.

1. Define The Feature

  • name the feature or work unit
  • identify the user-visible contract
  • identify any required spec or doc updates
  • decide whether the work is:
    • internal/refactor-heavy
    • public-behavior-heavy
    • both

2. Developer Setup

  • create or choose the developer branch/worktree
  • implement production code in:
    • include/
    • src/
  • add internal/unit tests if needed for implementation safety
  • update behavior docs/spec if behavior changed
  • commit the developer slice before handoff

3. Integration Tester Setup

  • check out the committed developer state in a separate worktree/session
  • add only:
    • public-behavior tests
    • integration tests
    • contract fixtures
    • testdata/
  • avoid editing production code
  • commit the tester slice before handing failures back

4. Failure Triage

If tests fail, decide which side owns the fix:

  • developer owns:

    • production bugs
    • missing implementation
    • public behavior that does not match the approved contract
  • integration tester owns:

    • incorrect assertions
    • bad fixture expectations
    • test bugs or invalid setup

If unclear:

  • check the public contract/spec first
  • escalate to the human owner if the contract is ambiguous

5. Repeat Loop

  • developer fixes code and commits
  • integration tester reruns/extends tests and commits
  • repeat until the behavior and tests converge

6. Completion Checklist

  • production code is complete
  • internal tests are acceptable
  • public/integration/contract tests are acceptable
  • docs/spec are updated where behavior changed
  • ./dev/check.sh passes
  • merge/cleanup plan is clear

Suggested Commit Labels

These are optional but useful:

  • developer:

    • dev: implement <feature>
    • dev: fix <feature> after tester feedback
  • integration tester:

    • test: add public coverage for <feature>
    • test: fix fixture/assertion for <feature>

G-code Text Flow

This page explains the end-to-end path of input G-code text through the current library implementation and the planned streaming-first execution direction.

End-to-End Flow

flowchart LR
  A[G-code text chunks] --> B[StreamingExecutionEngine]
  B --> C[Line assembly]
  C --> D[Parser]
  D --> E[AST + syntax diagnostics]
  E --> F[Semantic rules]
  F --> G[Normalized line result + semantic diagnostics]
  G --> H[AIL lowering]
  H --> I[Execution sink]
  H --> J[Runtime]
  J --> K[Blocked / resume / cancel]
  H --> L[Packetization / legacy message output]

Step 1: Accept Input Chunks

Planned primary runtime path:

  • input arrives in arbitrary chunks
  • engine buffers text until a complete logical line is available
  • only complete lines are eligible for parse/lower/execute

Current limitation:

  • current callback streaming API still parses whole input text first

Step 2: Parse Input Text

Main code:

  • grammar/GCode.g4
  • src/gcode_parser.cpp
  • src/ast.h

What happens:

  • raw text is tokenized and parsed
  • source positions are attached to parsed structures
  • syntax diagnostics are emitted when grammar shape is invalid

Outputs:

  • AST-like parse structures
  • syntax diagnostics with line and column

Step 3: Run Semantic Rules

Main code:

  • src/semantic_rules.cpp

What happens:

  • parsed lines are checked for controller-specific shape constraints
  • compatibility rules and malformed forms are diagnosed
  • parser output is kept, but diagnostics become more actionable

Examples:

  • malformed PROC declaration shape
  • invalid G4 block shape
  • mode conflicts in a block

Outputs:

  • parse result plus semantic diagnostics

Step 4: Lower to AIL

Main code:

  • src/ail.cpp

What happens:

  • supported constructs are converted into typed AIL instructions
  • AIL is the executable intermediate representation for runtime/control-flow
  • non-motion state changes are preserved as explicit instructions, not only as text diagnostics

Planned streaming-first direction:

  • line execution may also expose normalized command objects directly to injected sink/runtime interfaces without requiring whole-program buffering

Examples:

  • motion instructions
  • tool_select
  • tool_change
  • m_function
  • control-flow labels and jumps

Outputs:

  • result.instructions
  • lowering diagnostics and warnings

Step 5: Execute Through Injected Interfaces

Planned primary runtime shape:

  • engine emits deterministic execution events through an execution sink
  • engine invokes runtime interfaces for slow or blocking machine work
  • runtime may return immediate completion, pending/blocking, or error
  • no later line executes while the current line is blocked

Examples:

  • G1 -> normalized linear-move command -> sink event -> runtime submission
  • system variable read -> runtime read request -> ready/pending/error

Step 6: Execute AIL (Current Compatibility Path)

Main code:

  • src/ail.cpp

What happens:

  • control-flow and stateful instructions are stepped in order
  • policies resolve runtime decisions such as missing targets or tool-selection fallback behavior
  • executor state tracks things like active tool, plane, rapid mode, and call stack state

Outputs:

  • runtime state transitions
  • executor diagnostics

Step 7: Convert AIL to Packets

Main code:

  • src/packet.cpp

What happens:

  • motion-oriented AIL instructions become packets
  • non-motion instructions are not emitted as motion packets
  • packet output is for transport/planner-style motion consumers, not the full execution model

Outputs:

  • packet list
  • packet-stage diagnostics or skip warnings when relevant

Step 8: Lower to Messages

Main code:

  • src/messages.cpp

What happens:

  • supported motion/dwell constructs can also be lowered into message output
  • this is a separate output path from AIL packetization

Outputs:

  • typed message results
  • diagnostics and rejected lines

Current Rule of Thumb

Use these modes depending on what you need:

  • parse: syntax and semantic understanding
  • streaming engine: planned primary execution surface
  • ail: executable intermediate representation / compatibility inspection path
  • packet: motion packet transport view
  • lower: typed message output view for legacy integrations

Development: Testing Reference

Use this page for maintainer-facing test entry points and representative verification locations in the repository.

Representative Test Files

  • test/messages_tests.cpp
  • test/streaming_tests.cpp
  • test/messages_json_tests.cpp
  • test/regression_tests.cpp
  • test/semantic_rules_tests.cpp
  • test/lowering_family_g4_tests.cpp
  • test/packet_tests.cpp
  • test/cli_tests.cpp

Representative Test Data

  • testdata/packets/*.ngc
  • testdata/cli/
  • testdata/execution_contract/core/

Primary Local Gate

Run:

./dev/check.sh

Use Workflow for the full local validation flow.

Architecture and Design

This section contains canonical developer-facing architecture decisions and implementation-planning references for the parser project.

Top-level pages in this section:

Pages in this section are split by topic for easier review:

  • pipeline/modules
  • modal/context strategy
  • exact-stop/continuous-path modes (Groups 10/11/12)
  • Siemens feedrate model (Modal Group 15)
  • Siemens modal Group 7 tool-radius compensation
  • Siemens working-plane modal semantics (Group 6)
  • Siemens M-code model and execution boundaries
  • system-variable selector and runtime evaluation model
  • rapid traverse model (G0, RTLION, RTLIOF)
  • line-by-line streaming execution with blocking and cancellation
  • incremental parse session API
  • work-offset model (Group 8 + suppression commands)
  • dimensions/units model (Groups 13/14 + AC/IC + DIAM*)
  • tool-change semantics (direct T vs deferred T+M6)
  • implementation phases and task ordering
  • requirements-driven current-vs-target execution plan

Architecture

This is the canonical architecture page for the repository.

Parser and Runtime Design (v1 Draft)

1. Purpose

This document defines how the codebase will be organized to satisfy the current PRD requirements, with a Siemens-840D-compatible syntax model and configuration-driven runtime semantics.

2. Confirmed v1 Decisions

  • Siemens syntax compatibility: Yes
  • Siemens runtime semantics: Optional via profile/config
  • Baseline controller profile: 840D
  • Unknown M-code policy: Error
  • Modal conflicts: Error
  • Machine-related behavior: Config-driven (not hardcoded)

3. High-Level Pipeline

flowchart LR
  A[NC Text Chunks] --> B[StreamingExecutionEngine]
  B --> C[Line Assembly]
  C --> D[Parser<br/>grammar + AST]
  D --> E[Semantic Normalizer<br/>canonical statements]
  E --> F[Modal/Context Engine<br/>group states + validation]
  F --> G[AIL Lowering<br/>deterministic executable IR]
  G --> H[IExecutionSink / IRuntime]
  H --> I[Blocked / Resume / Cancel]
  G --> J[Legacy Message/Packet Output]
  A --> PS[ParseSession<br/>line edits + resume]
  PS --> D
  PS --> E
  K[MachineProfile + Policies] --> E
  K --> F
  K --> G
  K --> H

Streaming-first direction:

  • the primary execution surface is a stateful streaming engine
  • batch parse/lower APIs remain transitional compatibility surfaces
  • runtime work must cross injected interfaces, not direct global bindings

4. Code Organization (Target)

flowchart TB
  subgraph parser["Parser Layer"]
    G4["grammar/GCode.g4"]
    GP["src/gcode_parser.cpp"]
    AST["src/ast.h"]
  end

  subgraph semantic["Semantic Layer (new/expanded)"]
    SN["src/semantic_normalizer.*"]
    SR["src/semantic_rules.cpp"]
  end

  subgraph modal["Modal/Context Layer (new)"]
    ME["src/modal_engine.*"]
    MR["src/modal_registry.*"]
  end

  subgraph ail["AIL Layer"]
    AL["src/ail.cpp"]
    AJ["src/ail_json.cpp"]
  end

  subgraph runtime["Runtime Layer"]
    SE["StreamingExecutionEngine"]
    RI["Runtime / Sink Interfaces"]
    EX["AilExecutor (compatibility path)"]
    PL["Policy Interfaces"]
  end

  G4 --> GP --> AST --> SN --> ME --> AL --> SE
  SR --> SN
  MR --> ME
  AL --> AJ
  PL --> SE
  RI --> SE

5. Config and Policy Model

classDiagram
  class MachineProfile {
    +controller: ControllerKind
    +tool_change_mode: ToolChangeMode
    +tool_management: bool
    +unknown_mcode_policy: ErrorPolicy
    +modal_conflict_policy: ErrorPolicy
    +supported_work_offsets: RangeSet
  }

  class ModalRules {
    +group_conflict_matrix
    +group_precedence
    +validateTransition(...)
  }

  class ToolPolicy {
    <<interface>>
    +resolveToolSelection(...)
  }

  class MCodePolicy {
    <<interface>>
    +validateAndMapMCode(...)
  }

  class RuntimePolicy {
    +tool_policy: ToolPolicy
    +mcode_policy: MCodePolicy
  }

  MachineProfile --> ModalRules
  MachineProfile --> RuntimePolicy

6. Modal State Strategy

  • Keep a single modal registry keyed by Siemens group IDs.
  • Grouped state includes at minimum:
    • Group 1: motion type family (G0/G1/G2/G3 as baseline in current scope)
    • Group 6: working-plane selection (G17/G18/G19)
    • Group 7: G40/G41/G42
    • Group 8: G500/G54..G57/G505..G599
    • Group 10: G60/G64..G645
    • Group 11: G9 (block-scope)
    • Group 12: G601/G602/G603
    • Group 13: G70/G71/G700/G710
    • Group 14: G90/G91
    • Group 15: feed type family
  • Related non-group/aux state (modeled alongside modal groups):
    • rapid interpolation control (RTLION / RTLIOF) affecting G0 behavior
    • block-scope suppress contexts (G53/G153/SUPA)

6.1 Incremental Parse Session Strategy

  • ParseSession owns editable source lines and cached lowering boundary metadata.
  • Resume contract:
    • preserve immutable prefix before resume line
    • reparse + relower suffix from resume line
    • merge diagnostics/instructions/messages deterministically by 1-based line
  • Resume entry points:
    • explicit line (reparseFromLine(line))
    • first error (reparseFromFirstError())
  • v1 scope:
    • deterministic prefix/suffix merge semantics and API behavior
    • parser-internal tree-reuse optimization is a later performance enhancement
  • Detailed API/merge design:
stateDiagram-v2
  [*] --> BlockStart
  BlockStart --> ApplyModalWords
  ApplyModalWords --> ValidateConflicts
  ValidateConflicts --> EmitError: conflict/invalid
  ValidateConflicts --> ApplyBlockScope
  ApplyBlockScope --> LowerInstruction
  LowerInstruction --> BlockEnd
  BlockEnd --> BlockStart

7. Parse vs Runtime Responsibility

  • Parser layer:
    • recognizes syntax and preserves source form/location
    • does not hardcode machine-specific behaviors
  • Semantic/modal layers:
    • resolve meaning under MachineProfile
    • enforce modal/conflict policies
  • Runtime layer:
    • executes lowered line/AIL results with injected policy/runtime hooks
    • applies machine-specific actions (tool/mcode behavior)

7.0 Streaming Execution Boundary

  • streaming engine owns:
    • chunk intake
    • logical-line assembly
    • sequencing
    • blocked/resume/cancel state
  • runtime interfaces own:
    • machine-visible side effects
    • slow/blocking operations such as motion submission and variable reads
  • sink interfaces own:
    • deterministic emitted event visibility for tests/integration

7.1 Instruction-First Execution Contract

  • Any machine-visible action must exist as an explicit executable instruction in AIL (or future equivalent runtime IR).
  • Execution transport is instruction-dependent:
    • motion instructions: may emit motion packets for planner/queue consumers
    • non-motion control instructions: may execute via runtime control-command path without standalone motion packets
  • Therefore, “not packetized” does not imply “not executable”; it means the instruction is routed through a non-motion execution path.

8. Current Coverage Snapshot

9. Implementation Sequence (High Level)

  1. Build modal registry + machine profile skeleton.
  2. Move existing modal logic into modal engine.
  3. Introduce semantic normalizer output schema.
  4. Add one feature family at a time (comments, M, groups, feed, dimensions).
  5. Keep each slice test-first with ./dev/check.sh.

Parser/Runtime Pipeline

High-Level Flow

flowchart LR
  A[NC Text] --> B[Parser\ngrammar + AST]
  B --> C[Semantic Rules\nnormalization/validation]
  C --> D[AIL Lowering]
  D --> E[AilExecutor]
  D --> F[Message Output]
  D --> G[Packet Output]
  A --> PS[ParseSession\nline edits + resume]
  PS --> B

Current Module Layout

  • Parser layer:
    • grammar/GCode.g4
    • src/gcode_parser.*
    • src/ast.h
  • Semantic layer:
    • src/semantic_rules.*
  • AIL/runtime layer:
    • src/ail.*
  • Message/packet output:
    • src/messages*
    • src/packet*

Responsibility Boundaries

  • Parser:
    • recognize syntax and locations
    • preserve source structure
  • Semantic rules:
    • enforce shape/compatibility rules
    • generate actionable diagnostics
  • Lowering/runtime:
    • build executable IR (AIL)
    • execute control-flow semantics

Notes

This is a summary of architecture.md sections 3, 4, and 7.

Modal and Context Strategy

Group-Oriented State Model

Modal/context behavior is organized by Siemens-aligned groups with selective current implementation coverage.

Target registry model includes:

  • Group 1: motion type family (G0/G1/G2/G3 baseline)
  • Group 6: working plane (G17/G18/G19)
  • Group 7: compensation (G40/G41/G42)
  • Group 8: settable offsets (G500, G54..)
  • Group 10/11/12: exact-stop/continuous-path and criteria
  • Group 13/14/15: unit/dimensions/feed families

Auxiliary context state:

  • rapid interpolation control (RTLION/RTLIOF)
  • block-scope suppress contexts (G53/G153/SUPA)

Runtime Boundary

  • Parse/semantic layers capture declarative modal/control intent.
  • Runtime/executor applies dynamic policy-dependent behavior.
  • Machine-specific behavior is intended to be config/policy-driven.

Incremental Session Contract

ParseSession model goals:

  • preserve immutable prefix before resume line
  • reparse/re-lower suffix from resume line
  • merge diagnostics and outputs deterministically

This is a summary of architecture.md sections 6 and 6.1.

Design: Exact-Stop and Continuous-Path Modes (Groups 10/11/12)

Task: T-044 (architecture/design)

Goal

Define Siemens-compatible architecture for transition control modes:

  • Group 10: exact-stop vs continuous-path (G60, G64..G645)
  • Group 11: non-modal exact-stop (G9)
  • Group 12: exact-stop block-change criteria (G601/G602/G603)
  • G641 coupling with ADIS / ADISPOS

This design maps PRD Section 5.10.

Scope

  • modal/non-modal state model and precedence rules
  • criterion applicability rules for Group 12 under exact-stop
  • representation and semantics of ADIS/ADISPOS with G641
  • interaction points with feed and motion execution layers
  • output schema expectations for transition mode metadata

Out of scope:

  • servo control-law internals and real controller lookahead implementation
  • machine-vendor trajectory kernel details

Pipeline Boundaries

flowchart LR
  A[G60/G64..G645/G9/G601..G603/ADIS/ADISPOS] --> B[Parser + Semantic]
  B --> C[Modal Engine]
  C --> D[AIL Lowering]
  D --> E[AilExecutor]

  P[MachineProfile + TransitionPolicy] --> C
  P --> E
  • Parser/semantic:
    • parses mode words and validates value forms
  • Modal engine:
    • resolves active Group 10 mode, Group 11 block-scope flag, Group 12 criterion
    • validates cross-group applicability
  • AIL/executor:
    • carries transition-state metadata and applies runtime behavior boundaries

Group State Model

Group 10 (modal):

  • G60 exact-stop mode
  • G64, G641, G642, G643, G644, G645 continuous-path family

Group 11 (non-modal):

  • G9 exact-stop for current block only

Group 12 (modal criterion):

  • G601 exact-stop fine
  • G602 exact-stop coarse
  • G603 IPO end

Auxiliary parameters:

  • ADIS=<value> (path transitions, usually G1/G2/G3 path moves with G641)
  • ADISPOS=<value> (positioning/rapid transitions, G0 with G641)
stateDiagram-v2
  [*] --> CP_G64
  CP_G64 --> ES_G60: G60
  ES_G60 --> CP_G64: G64
  CP_G64 --> CP_G641: G641
  CP_G641 --> CP_G642: G642
  CP_G642 --> CP_G643: G643
  CP_G643 --> CP_G644: G644
  CP_G644 --> CP_G645: G645
  CP_G645 --> ES_G60: G60

Precedence and Applicability Rules

  1. Group 11 (G9) is block-local and has highest effect for that block.
  2. Otherwise Group 10 modal state defines transition behavior.
  3. Group 12 criterion is meaningful only when effective exact-stop is active (G60 modal or G9 current block).
  4. Under continuous-path effective mode, Group 12 is retained as modal state but not applied for block-change timing decisions.

Applicability matrix:

  • G60 + (G601|G602|G603) => criterion applied
  • G9 + (G601|G602|G603) => criterion applied in current block
  • G64..G645 + (G601|G602|G603) => criterion inactive (stored, not used)

G641 + ADIS/ADISPOS Coupling

Rules:

  • ADIS and ADISPOS are only behavior-relevant when effective Group 10 mode is G641.
  • if omitted, default value is 0 (profile default may override)
  • ADIS targets path transitions; ADISPOS targets positioning (G0) moves

Validation:

  • negative values are invalid
  • parser reports clear diagnostics for malformed assignments
flowchart TD
  G[G641 active?] -->|no| I[ignore ADIS/ADISPOS behavior; retain values]
  G -->|yes| A[apply ADIS to path transitions]
  G -->|yes| B[apply ADISPOS to rapid transitions]

Runtime Boundary and Output Semantics

AIL transition instruction concept:

{
  "kind": "transition_mode",
  "group10_mode": "g641",
  "group11_block_exact_stop": false,
  "group12_criterion": "g602",
  "adis": 0.5,
  "adispos": 0.0,
  "source": {"line": 30}
}

Runtime effective-state concept per executed block:

{
  "effective_transition_mode": "exact_stop",
  "effective_source": "group11_g9",
  "effective_criterion": "g601",
  "smoothing_mode": "none"
}

Notes:

  • packet/motion outputs should carry effective transition metadata as needed by downstream planner, without embedding full servo behavior.

Interaction Points

  • Feed model (Group 15):
    • effective transition mode may reduce/shape path velocity at boundaries
  • Rapid traverse model (T-045):
    • Group 10 state can constrain effective rapid interpolation behavior
  • Compensation/transform states:
    • can affect whether smoothing/transition elements are allowed

Machine Profile / Policy Hooks

Suggested profile fields:

  • default_group10_mode (e.g., g64)
  • default_group12_criterion (e.g., g602)
  • adis_default, adispos_default
  • supports_g644_with_transform (bool; false -> policy fallback to g642)

Policy interface sketch:

struct TransitionPolicy {
  virtual EffectiveTransition resolve(const ModalState& modal,
                                      const BlockContext& block,
                                      const RuntimeContext& ctx,
                                      const MachineProfile& profile) const = 0;
};

Implementation Slices (follow-up)

  1. Modal registry/state completion
  • ensure Groups 10/11/12 are fully represented with scope semantics
  1. Parser/semantic validation
  • robust parsing and validation for ADIS/ADISPOS
  • criterion applicability diagnostics/policy notes
  1. AIL/runtime effective state
  • emit and consume transition metadata for each block
  • implement Group 11 block-scope override behavior
  1. Integration coupling
  • connect transition state to rapid/feed behaviors via policy boundaries

Test Matrix (implementation PRs)

  • parser tests:
    • syntax/validation for Group 10/11/12 and ADIS/ADISPOS
  • modal-engine tests:
    • block-local G9 override behavior
    • Group 12 applicability under exact-stop only
  • AIL/executor tests:
    • effective mode resolution per block
    • criterion and smoothing metadata stability
  • docs/spec sync:
    • update SPEC/program reference as each slice lands

Traceability

  • PRD: Section 5.10 (exact-stop and continuous-path modes)
  • Backlog: T-044
  • Coupled tasks: T-045 (rapid traverse), T-041 (feed model)

Design: Siemens Feedrate Model (Modal Group 15)

Task: T-041 (architecture/design)

Goal

Define Siemens-compatible architecture for:

  • Group 15 feed-mode state (G93, G94, G95, G96, G97, G931, G961, G971, G942, G952, G962, G972, G973)
  • feed value programming via F
  • path-group selection via FGROUP(...)
  • rotary effective-radius input via FGREF[axis] = value
  • axis speed limits via FL[axis] = value

This design maps PRD Section 5.7.

Scope

  • feed-state ownership and propagation through parse -> modal -> AIL -> runtime
  • Group 15 transition model and F reprogramming rules
  • path-axis versus synchronized-axis partitioning via FGROUP
  • mixed linear/rotary feed interpretation via FGREF
  • coordinated timing limits via FL
  • machine-profile and policy hooks for controller-specific runtime behavior

Out of scope:

  • low-level interpolator, servo, and spindle control implementation
  • full Siemens machine-data database replication
  • cycle-specific feed overrides outside the baseline Group 15 model

Current Baseline and Migration Need

Current implementation is intentionally smaller than the PRD requirement:

  • motion messages, AIL instructions, and packets carry a single optional scalar feed
  • no explicit Group 15 modal state is represented yet
  • no model exists yet for FGROUP, FGREF, or FL
  • unit coupling to G700/G710 is not yet explicit

Migration target:

  • keep the existing scalar feed field as the user-programmed value carrier for simple consumers
  • add explicit feed-state instructions and effective feed metadata so runtime and downstream consumers can distinguish declared mode from resolved behavior

Pipeline Boundaries

flowchart LR
  A[Group 15 words and feed commands] --> B[Parser + Semantic]
  B --> C[Modal Engine]
  C --> D[AIL Lowering]
  D --> E[AilExecutor]
  D --> F[Messages / Packets]

  P[MachineProfile + FeedPolicy] --> C
  P --> E
  • Parser/semantic:
    • parses feed mode words, F, FGROUP, FGREF, and FL
    • validates statement shape and same-block conflicts
  • Modal engine:
    • owns persistent Group 15 state
    • owns active path-group membership and axis feed-limit state
  • AIL lowering:
    • emits explicit feed-state instructions and motion instructions carrying feed metadata references
  • Executor/runtime:
    • resolves effective feed semantics for path axes and synchronized axes
    • applies profile and policy rules to compute explainable runtime behavior

State Model

Group 15 should be represented as explicit modal state with distinct members:

  • g93: inverse-time feed
  • g94: path feed per minute
  • g95: feed per spindle revolution
  • g96: constant-cutting-rate coupled mode
  • g97: constant-rpm coupled mode
  • g931, g961, g971, g942, g952, g962, g972, g973: Siemens-specific distinct Group 15 modal members

Associated persistent feed context:

  • programmed F value
  • fgroup_axes: active path-axis set
  • fgref_by_axis: rotary effective-radius map
  • fl_by_axis: axis speed-limit map
  • feed_requires_reprogramming: latch set when Group 15 transition requires a fresh F

Defaulting rules:

  • startup Group 15 mode is profile-defined
  • startup FGROUP comes from machine profile default path grouping
  • startup FGREF/FL maps are empty unless profile seeds them

Transition and Persistence Rules

  1. Group 15 members are mutually exclusive and persist until another Group 15 word is programmed.
  2. Changing between Group 15 members does not erase stored F, but it does set feed_requires_reprogramming when the contract requires an explicit new F.
  3. F updates the programmed feed value for the current feed mode.
  4. FGROUP(...) replaces the active path-axis set.
  5. FGROUP() with empty arguments restores the machine-profile default group.
  6. FGREF[axis] = value updates only the named rotary-axis reference entry.
  7. FL[axis] = value updates only the named axis speed-limit entry.
stateDiagram-v2
  [*] --> profile_default
  profile_default --> g93: G93
  profile_default --> g94: G94
  profile_default --> g95: G95
  g93 --> g94: G94
  g94 --> g95: G95
  g95 --> g96: G96
  g96 --> g97: G97
  g97 --> g931: G931

The full Group 15 enum is explicit in code; the diagram only illustrates the state-machine pattern.

Path-Axis and Synchronized-Axis Model

FGROUP partitions axes into:

  • path axes: axes whose geometric contribution defines commanded path feed
  • synchronized axes: axes that must finish at the same block end time but do not directly define path feed
flowchart TD
  A[Motion block] --> B{Axis in active FGROUP?}
  B -- yes --> C[path-axis feed consumer]
  B -- no --> D[synchronized-axis timing consumer]
  C --> E[resolved coordinated duration]
  D --> E

Implications:

  • F is interpreted primarily against the active path group
  • synchronized axes inherit block duration from the resolved path duration
  • FL can force a longer duration if an axis cannot satisfy the requested coordinated timing

Mixed Linear/Rotary Interpretation

FGREF is needed when a rotary axis participates in path-feed interpretation.

Resolution concept:

  1. determine active path axes from FGROUP
  2. identify rotary axes participating in feed calculation
  3. apply FGREF effective radius for those rotary axes
  4. combine linear and converted rotary contributions under the current Group 15 mode

Policy boundary:

  • missing FGREF for a required rotary axis should be profile/policy driven: error, warning with fallback, or machine-default fallback

Unit Coupling

This design must compose with T-042 dimensions/units architecture.

Rules:

  • Group 13 G70/G71 affects geometric lengths only
  • Group 13 G700/G710 affects geometry and technological length data
  • therefore Group 15 feed interpretation must consume the effective unit scope, not just raw numeric F

Practical effect:

  • feed-state resolution depends on both Group 15 mode and Group 13 unit scope
  • output metadata should preserve enough information to explain how numeric F was interpreted

Runtime Resolution Contract

Runtime should separate declared feed state from effective feed execution.

Declared state includes:

  • current Group 15 member
  • programmed F
  • active FGROUP
  • stored FGREF and FL entries

Effective state includes:

  • resolved feed interpretation kind
  • resolved coordinated block duration or path feed
  • limit/override reasons such as axis-limit throttling
flowchart TD
  A[Declared feed state] --> B[FeedPolicy]
  C[Motion shape + active axes] --> B
  D[Dimension/Unit state] --> B
  E[MachineProfile] --> B
  B --> F[Effective feed result]

Output Schema Expectations

AIL feed-state instruction concept:

{
  "kind": "feed_state",
  "group15_mode": "g95",
  "feed_value": 0.2,
  "requires_reprogramming": false,
  "source": {"line": 24}
}

AIL path-group instruction concept:

{
  "kind": "feed_group",
  "path_axes": ["X", "Y", "C"],
  "source": {"line": 25}
}

AIL axis feed constraint concept:

{
  "kind": "axis_feed_limit",
  "axis": "C",
  "limit": 120.0,
  "source": {"line": 26}
}

Motion metadata concept:

{
  "kind": "motion_linear",
  "opcode": "G1",
  "feed_mode": "g94",
  "feed_value": 150.0,
  "path_axes": ["X", "Y"],
  "effective_unit_scope": "geometry_and_technology"
}

Machine Profile / Policy Hooks

Suggested profile/config fields:

  • default_group15_mode
  • default_fgroup_axes
  • require_explicit_f_after_group15_change
  • missing_fgref_policy
  • allow_rotary_path_feed_without_fgref
  • default_fl_limits

Policy interface sketch:

struct FeedResolutionResult {
  std::string effective_mode;
  std::optional<double> effective_feed_value;
  std::optional<double> coordinated_duration_ms;
  std::vector<std::string> reasons;
};

struct FeedPolicy {
  virtual FeedResolutionResult resolve(const FeedState& state,
                                       const MotionContext& motion,
                                       const DimensionContext& dims,
                                       const MachineProfile& profile) const = 0;
};

Integration Points

  • T-042 dimensions/units:
    • Group 13 unit scope changes feed-length interpretation
  • T-040 working plane:
    • arc/helical path-length resolution consumes both plane and feed state
  • T-039 Group 7 compensation:
    • compensated path geometry changes runtime path-length calculation inputs
  • T-044 Groups 10/11/12:
    • transition/exact-stop policies can interact with effective coordinated timing
  • future tool/runtime policies:
    • spindle-coupled feed modes and constant-cutting-rate logic need runtime interfaces rather than parser-only behavior

Implementation Slices (follow-up)

  1. Feed-state schema scaffold
  • introduce explicit Group 15 enum/state structures and AIL feed-state instruction skeletons
  1. F and reprogramming contract
  • define transition behavior and diagnostics for mode changes that require a fresh F
  1. FGROUP representation
  • model path-axis membership and synchronized-axis partition metadata
  1. FGREF and mixed rotary feed
  • add rotary reference-radius representation and policy-driven diagnostics
  1. FL coordinated limit behavior
  • integrate axis speed-limit metadata into runtime feed resolution
  1. Packet/message exposure
  • add stable machine-readable feed metadata once runtime semantics are wired

Test Matrix (implementation PRs)

  • parser tests:
    • acceptance/rejection for Group 15 words, FGROUP, FGREF, and FL
  • modal-engine tests:
    • Group 15 transitions and persistence
    • F reprogramming latch behavior
    • FGROUP() default reset behavior
  • AIL tests:
    • explicit feed-state / feed-group / axis-limit instruction shapes
  • executor tests:
    • path-axis versus synchronized-axis timing behavior
    • FL-driven duration throttling
    • missing-FGREF policy outcomes
  • packet/message tests:
    • stable schema for effective feed metadata
  • docs/spec sync:
    • update SPEC sections for Group 15 and feed runtime behavior per slice

Traceability

  • PRD: Section 5.7 (Siemens feedrate semantics)
  • Backlog: T-041
  • Coupled tasks: T-042 (dimensions), T-040 (plane), T-039 (comp), T-044 (transition modes)

Design: Siemens Modal Group 7 (G40, G41, G42)

Task: T-039 (architecture/design)

Goal

Define Siemens-compatible architecture for Group 7 tool-radius-compensation state so that:

  • G40, G41, and G42 are represented as explicit modal state
  • modal transitions are deterministic and independent from motion state
  • parse, AIL, executor, message, and packet layers carry the right state boundaries
  • future geometric cutter-compensation behavior can be layered on without breaking the existing baseline

This design maps PRD Section 5.5.

Scope

  • Group 7 state ownership across parse -> modal -> AIL -> runtime
  • parse/lowering/runtime behavior for G40, G41, G42
  • interaction points with motion, working plane, and rapid/transition states
  • output schema expectations for declared compensation state
  • migration path from current baseline to fuller Siemens behavior

Out of scope:

  • full compensated toolpath geometry generation
  • controller-specific contour entry/exit heuristics
  • machine-specific wear/radius table integration beyond policy boundaries

Current Baseline

Current implementation already provides a v0 Group 7 baseline:

  • parser recognizes G40, G41, and G42
  • AIL lowers them to explicit tool_radius_comp instructions
  • executor tracks tool_radius_comp_current
  • packet stage does not emit standalone packets for Group 7 instructions
  • no geometric path offsetting is performed yet

This architecture note formalizes that baseline and defines the extension path.

Pipeline Boundaries

flowchart LR
  A[G40 / G41 / G42] --> B[Parser + Semantic]
  B --> C[Modal Engine]
  C --> D[AIL Lowering]
  D --> E[AilExecutor]
  D --> F[Messages / Packets]

  P[MachineProfile + CompensationPolicy] --> C
  P --> E
  • Parser/semantic:
    • recognizes Group 7 commands and validates same-block conflicts
    • preserves source form and normalized Group 7 interpretation
  • Modal engine:
    • owns persistent Group 7 state independently from motion group state
  • AIL lowering:
    • emits explicit state-transition instructions for Group 7
    • attaches active compensation metadata to motion consumers when needed
  • Executor/runtime:
    • tracks current declared compensation mode
    • later resolves effective behavior with plane/profile/policy context

State Model

Group 7 members:

  • off (G40)
  • left (G41)
  • right (G42)

Representation requirement:

  • Group 7 must be a first-class modal group, not overloaded into motion state or ad hoc booleans.

Minimal runtime state:

  • current Group 7 mode
  • source of last transition
  • optional future effective-compensation details resolved by policy
stateDiagram-v2
  [*] --> off
  off --> left: G41
  off --> right: G42
  left --> off: G40
  right --> off: G40
  left --> right: G42
  right --> left: G41

Transition Rules

  1. Group 7 is modal and persists until another Group 7 command is programmed.
  2. Group 7 transitions are independent from Group 1 motion selection.
  3. same-block conflicting Group 7 words are handled by the central modal conflict policy.
  4. repeating the same Group 7 code in one block is allowed under the existing modal-registry baseline unless policy says otherwise.
  5. G40 changes declared state to compensation off but does not itself imply a motion or packet event.

Declared State vs Effective Behavior

The architecture must distinguish:

  • declared compensation mode: off|left|right
  • effective geometric behavior: what runtime actually does with the path

Reason:

  • v0 already supports declared-state tracking
  • future Siemens behavior needs plane-aware and motion-aware geometry changes
  • downstream tooling must be able to inspect declared modal state even when no geometric compensation engine is active
flowchart TD
  A[Declared Group 7 state] --> B{Geometry compensation engine enabled?}
  B -- no --> C[track state only]
  B -- yes --> D[resolve effective compensated path]

Interaction Points

  • Working plane (T-040):
    • Group 7 semantics are plane-dependent
    • G17/G18/G19 decide which contour plane compensation is interpreted in
  • Motion family:
    • compensation meaning applies to contouring motion, not as a standalone packetized event
  • Rapid traverse (T-045):
    • rapid policy may force linear/effective behavior constraints while Group 7 is active
  • Exact-stop / continuous-path (T-044):
    • transition behavior can affect how compensated path segments are executed
  • Feed model (T-041):
    • effective path length under compensation feeds into future feed resolution

Output Schema Expectations

AIL Group 7 instruction concept:

{
  "kind": "tool_radius_comp",
  "opcode": "G41",
  "mode": "left",
  "source": {"line": 42}
}

Executor/runtime state concept:

{
  "tool_radius_comp_current": "left",
  "source": {"line": 42}
}

Future motion metadata concept:

{
  "kind": "motion_linear",
  "opcode": "G1",
  "tool_radius_comp_declared": "left",
  "tool_radius_comp_effective": "left",
  "working_plane": "g17"
}

Packet-stage rule:

  • Group 7 instructions do not emit standalone motion packets
  • their effect is represented through modal state and later motion metadata

Machine Profile / Policy Hooks

Suggested profile/config fields:

  • default_tool_radius_comp_mode
  • allow_group7_without_geometry_engine
  • group7_conflict_policy
  • require_plane_for_compensation_resolution
  • compensation_geometry_policy

Policy interface sketch:

struct CompensationResolution {
  std::string declared_mode;
  std::string effective_mode;
  std::vector<std::string> reasons;
};

struct CompensationPolicy {
  virtual CompensationResolution resolve(const ModalState& modal,
                                         const MotionContext& motion,
                                         const MachineProfile& profile) const = 0;
};

Migration Plan

Current baseline:

  • explicit tool_radius_comp AIL instruction
  • executor state tracking only
  • packet-stage ignore for standalone Group 7 commands

Follow-up migration:

  1. modal-engine ownership
  • lift Group 7 into centralized modal-state transitions explicitly
  1. motion metadata propagation
  • attach declared Group 7 state to motion outputs/messages/packets
  1. plane coupling
  • resolve Group 7 semantics against active working plane
  1. policy-driven effective behavior
  • separate declared state from effective geometry/runtime behavior
  1. optional geometry engine integration
  • allow future compensated path generation without changing parse/AIL schema

Implementation Slices (follow-up)

  1. Modal-engine consolidation
  • route Group 7 transitions through central modal engine APIs
  1. Output metadata exposure
  • carry declared Group 7 state on motion/message/packet outputs
  1. Plane-aware runtime contract
  • integrate Group 7 with working-plane state for effective behavior resolution
  1. Policy boundary for geometry behavior
  • introduce pluggable compensation policy without hardcoding controller details

Test Matrix (implementation PRs)

  • parser tests:
    • recognition of G40/G41/G42
    • same-block conflict handling for Group 7 words
  • modal-engine tests:
    • persistence and deterministic transitions for Group 7
  • AIL tests:
    • stable tool_radius_comp instruction schema
    • motion metadata includes declared Group 7 state when added
  • executor tests:
    • runtime state tracking
    • future plane-aware/effective behavior resolution
  • packet/message tests:
    • standalone Group 7 commands remain non-packet control-state updates
    • stable schema when motion metadata grows
  • docs/spec sync:
    • update SPEC once declared vs effective behavior contracts expand

SPEC Update Plan

When implementation moves beyond the current baseline, update:

  • supported G-code/modal sections for explicit Group 7 semantics
  • AIL/message/packet schema sections for declared/effective compensation state
  • runtime behavior notes for plane-aware compensation resolution boundaries

Traceability

  • PRD: Section 5.5 (Group 7 tool radius compensation)
  • Backlog: T-039
  • Coupled tasks: T-040 (working plane), T-045 (rapid), T-044 (transition), T-041 (feed)

Design: Working-Plane Modal Semantics (G17, G18, G19)

Task: T-040 (architecture/design)

Goal

Define Siemens-compatible architecture for modal working-plane semantics so that:

  • G17, G18, and G19 are represented as explicit Group 6 modal state
  • plane state propagates consistently through parse -> modal -> AIL -> runtime
  • arc interpretation and compensation consumers can depend on one normalized plane model
  • future tool-length and compensation features can compose on top of the same plane contract

This design maps PRD Section 5.6.

Scope

  • Group 6 state model for G17/G18/G19
  • plane-state ownership and propagation across pipeline stages
  • plane-aware interaction points for arcs, tool radius compensation, and future tool-length infeed direction
  • migration from the current working-plane baseline to fuller Siemens semantics
  • output schema expectations for declared and effective plane context

Out of scope:

  • full machine-kinematics interpretation beyond standard geometry axes
  • full geometric cutter-compensation algorithm
  • low-level interpolator details beyond the plane contract

Current Baseline

Current implementation already provides a meaningful Group 6 baseline:

  • parser recognizes G17, G18, and G19
  • AIL lowers them to explicit working-plane instructions
  • executor tracks current working plane
  • arc validation and lowering already use the active plane to decide allowed center words and emitted arc metadata
  • packet stage preserves effective working plane on arc outputs

This design note formalizes that baseline and defines the remaining expansion points.

Pipeline Boundaries

flowchart LR
  A[G17 / G18 / G19] --> B[Parser + Semantic]
  B --> C[Modal Engine]
  C --> D[AIL Lowering]
  D --> E[AilExecutor]
  D --> F[Messages / Packets]

  P[MachineProfile + PlanePolicy] --> C
  P --> E
  • Parser/semantic:
    • recognizes Group 6 words and same-block conflicts
    • validates plane-dependent syntax consumers such as arc center words
  • Modal engine:
    • owns persistent working-plane state independently from motion and Group 7
  • AIL lowering:
    • emits explicit plane-state instructions
    • attaches effective plane metadata to plane-sensitive motion outputs
  • Executor/runtime:
    • tracks current plane state
    • resolves declared plane into downstream behavior contracts for arcs, compensation, and future infeed-direction consumers

State Model

Group 6 members:

  • g17: XY plane, infeed/tool-length direction Z
  • g18: ZX plane, infeed/tool-length direction Y
  • g19: YZ plane, infeed/tool-length direction X

Required representation:

  • Group 6 must be first-class modal state, not implicit in arc parsing logic alone.

Minimal runtime state:

  • current declared plane
  • source of last transition
  • optional future effective plane context after policy/kinematics resolution
stateDiagram-v2
  [*] --> g17
  g17 --> g18: G18
  g18 --> g19: G19
  g19 --> g17: G17
  g17 --> g19: G19
  g18 --> g17: G17
  g19 --> g18: G18

Transition Rules

  1. Group 6 is modal and persists until another Group 6 command is programmed.
  2. Group 6 transitions are independent from Group 1 motion-family state.
  3. same-block conflicting Group 6 words are handled through the central modal conflict policy.
  4. repeating the same plane word in one block is allowed under the current modal-registry baseline unless policy says otherwise.
  5. plane changes do not emit standalone motion packets; they change the context in which later plane-sensitive operations are interpreted.

Declared Plane vs Effective Consumer Behavior

The architecture must preserve both:

  • declared plane state: g17|g18|g19
  • effective consumer interpretation: how arcs, compensation, and later tool-length logic use that plane

Reason:

  • some consumers need only the declared modal state
  • others need derived axis mappings such as contour plane and infeed axis
  • future kinematics/policy layers may further constrain effective behavior
flowchart TD
  A[Declared Group 6 state] --> B[PlanePolicy / axis mapping]
  B --> C[Arc interpretation]
  B --> D[Group 7 compensation interpretation]
  B --> E[Future tool-length infeed direction]

Arc Coupling

Arc interpretation is the clearest existing plane-dependent consumer.

Per-plane center-word mapping baseline:

  • G17:
    • contour plane XY
    • I/J allowed, K rejected
  • G18:
    • contour plane ZX
    • I/K allowed, J rejected
  • G19:
    • contour plane YZ
    • J/K allowed, I rejected

Architecture rule:

  • arc validation and arc lowering must not hardcode disconnected plane tables in multiple places; they should consume one normalized plane model.

Compensation Coupling

Group 7 tool-radius compensation is plane-dependent.

Requirements:

  • Group 7 declared state remains independent from Group 6 declared state
  • effective compensation interpretation consumes active plane context
  • future geometry compensation logic should resolve contour side (left/right) against the active contour plane rather than raw axis assumptions

This is the main coupling point between T-039 and T-040.

Tool-Length / Infeed Direction Coupling

Future tool-length-related behavior needs the infeed direction implied by the active plane:

  • G17 -> infeed Z
  • G18 -> infeed Y
  • G19 -> infeed X

The architecture should expose this as derived metadata rather than requiring later features to recompute it ad hoc.

Output Schema Expectations

AIL plane-state instruction concept:

{
  "kind": "working_plane",
  "opcode": "G18",
  "plane": "zx",
  "infeed_axis": "Y",
  "source": {"line": 10}
}

Arc motion metadata concept:

{
  "kind": "motion_arc",
  "opcode": "G2",
  "working_plane": "zx",
  "contour_axes": ["Z", "X"],
  "center_axes": ["I", "K"]
}

Future compensation metadata concept:

{
  "kind": "motion_linear",
  "opcode": "G1",
  "working_plane": "xy",
  "tool_radius_comp_declared": "left"
}

Packet-stage rule:

  • standalone plane-state instructions do not emit standalone motion packets
  • plane context is carried through plane-sensitive motion payloads where needed

Machine Profile / Policy Hooks

Suggested profile/config fields:

  • default_working_plane
  • group6_conflict_policy
  • plane_axis_mapping_policy
  • allow_nonstandard_plane_kinematics

Policy interface sketch:

struct PlaneResolution {
  std::string declared_plane;
  std::string contour_plane;
  std::string infeed_axis;
  std::vector<std::string> allowed_center_words;
};

struct PlanePolicy {
  virtual PlaneResolution resolve(const ModalState& modal,
                                  const MotionContext& motion,
                                  const MachineProfile& profile) const = 0;
};

Migration Plan

Current baseline:

  • explicit Group 6 instructions
  • executor tracking of working plane
  • plane-aware arc validation/lowering and packet metadata

Follow-up migration:

  1. modal-engine consolidation
  • route Group 6 transitions through central modal-engine ownership explicitly
  1. normalized plane metadata
  • expose contour plane and infeed-axis metadata consistently across AIL and message/packet outputs
  1. Group 7 integration
  • resolve tool-radius-compensation interpretation against active plane state
  1. tool-length / future consumers
  • reuse the same plane contract for infeed direction and related semantics
  1. policy boundary for nonstandard kinematics
  • isolate any machine-specific plane reinterpretation behind profile/policy

Implementation Slices (follow-up)

  1. Modal-engine consolidation
  • centralize Group 6 transitions and conflict handling
  1. Arc consumer unification
  • ensure all arc validation/lowering paths read the same plane mapping source
  1. Output metadata completion
  • add stable declared/effective plane metadata to motion outputs where needed
  1. Group 7 coupling
  • wire working plane into compensation runtime/policy resolution
  1. Future infeed-direction consumers
  • expose infeed-axis metadata for tool-length and related subsystems

Test Matrix (implementation PRs)

  • parser tests:
    • recognition of G17/G18/G19
    • same-block Group 6 conflict handling
    • plane-dependent arc center-word validation
  • modal-engine tests:
    • Group 6 persistence and deterministic transitions
  • AIL tests:
    • stable working-plane instruction schema
    • plane metadata propagation to arc outputs
  • executor tests:
    • runtime state tracking of plane transitions
    • future plane-aware Group 7 interpretation
  • packet/message tests:
    • stable arc payload/schema with working-plane metadata
    • no standalone packet emission for plane-state instructions
  • docs/spec sync:
    • update SPEC when declared/effective plane contracts expand beyond baseline

SPEC Update Plan

When implementation moves beyond the current baseline, update:

  • supported G-code/modal sections for full Group 6 semantics
  • arc/output schema sections for declared/effective plane metadata
  • runtime behavior sections for Group 6 coupling with Group 7 and future tool-length direction handling

Traceability

  • PRD: Section 5.6 (working-plane selection)
  • Backlog: T-040
  • Coupled tasks: T-039 (Group 7), T-041 (feed), T-044 (transition), T-042 (dimensions)

Design: Siemens M-Code Model and Execution Boundaries

Task: T-046 (architecture/design)

Goal

Define an end-to-end Siemens-compatible M-code model for:

  • syntax and validation (M<value>, M<ext>=<value>)
  • AIL lowering/output shape
  • runtime classification and execution boundaries
  • machine/profile policy hooks

This design maps PRD Section 5.12.

Scope

  • syntax model and integer-range validation (0..2147483647)
  • extended-address validity by M-function family
  • executable vs parse-only classification model
  • baseline predefined Siemens M set:
    • M0/M1/M2/M3/M4/M5/M6/M17/M19/M30/M40..M45/M70
  • block-level M-count policy model (machine/config driven)
  • timing model for stop/end functions (after traversing movement)
  • subsystem interaction points (tool/spindle/coolant/control-flow)
  • output schema expectations for AIL/runtime events

Out of scope:

  • vendor PLC-specific custom M implementation
  • machine I/O transport stack and actuator drivers

Pipeline Boundaries

flowchart LR
  A[M words in NC block] --> B[Parser]
  B --> C[Semantic validation]
  C --> D[AIL m_function instruction]
  D --> E[AilExecutor]
  E --> F[Runtime event stream]

  P[MachineProfile + MCodePolicy] --> C
  P --> E
  • Parser:
    • tokenizes and captures raw M forms and source locations
    • does not decide machine action behavior
  • Semantic layer:
    • validates numeric range and form constraints
    • validates extended-address allowed/forbidden families
  • AIL:
    • emits normalized m_function instructions
  • Executor:
    • resolves instruction class (spindle/tool/flow/other)
    • applies policy for unknown/custom M functions
    • enforces timing constraints (post-motion trigger semantics)

Syntax and Validation Model

Accepted forms:

  • M<value>
  • M<ext>=<value>

Validation rules:

  • <value> type: signed integer text parsed as 32-bit non-negative range 0..2147483647
  • <ext> type: non-negative integer extension
  • forbidden extended-address functions:
    • M0, M1, M2, M17, M30
  • configurable limit for count of M words in one block:
    • baseline policy default: max_m_per_block = 5
    • machine profile may override
flowchart TD
  M[Raw M word] --> F{Form valid?}
  F -- no --> E1[Diagnostic: invalid M syntax]
  F -- yes --> R{Value in INT range?}
  R -- no --> E2[Diagnostic: value out of range]
  R -- yes --> X{Extended form allowed for this value?}
  X -- no --> E3[Diagnostic: extended address forbidden]
  X -- yes --> O[Emit normalized M node]

Runtime Classification Model

Instruction classes:

  • program-flow:
    • M0, M1, M2, M17, M30
  • spindle:
    • M3, M4, M5, M19, M70
  • tool:
    • M6
  • gearbox:
    • M40..M45
  • custom/other:
    • any other allowed integer value

Execution class meaning:

  • parse-only:
    • represented in AIL/output, no actuator behavior in core runtime
  • executable-control:
    • affects runtime state machine (stop/end/return/tool-change request)
  • executable-machine:
    • emits typed machine events for downstream adapter/policy handling

Default v1 policy:

  • known predefined set: executable-control or executable-machine event
  • unknown set: governed by unknown_mcode_policy (error|warning|ignore)

Timing Model (Post-Motion Trigger)

For M0, M1, M2, M17, M30:

  • if block contains traversing motion and these M functions, runtime triggers stop/end semantics after traversing movement completion.
sequenceDiagram
  participant EX as Executor
  participant MOT as Motion layer
  participant EVT as Runtime event sink

  EX->>MOT: execute block motion
  MOT-->>EX: motion complete
  EX->>EVT: emit stop/end/return event for M0/M1/M2/M17/M30

Interaction Points

  • Tool subsystem:
    • M6 integrates with tool-selection/preselect model from tool-change design (T-038).
  • Spindle subsystem:
    • M3/M4/M5/M19/M70 map to spindle direction/stop/position/axis-mode events.
  • Coolant/custom PLC:
    • non-predefined values are policy-dispatched; no core hardcoding.
  • Control-flow subsystem:
    • M17 return semantics integrate with subprogram call stack model (T-050).
    • M2/M30 terminate current main-program execution context.

Output Schema Expectations

AIL normalized shape (conceptual):

{
  "kind": "m_function",
  "source": {"line": 120},
  "m_value": 3,
  "address_extension": null,
  "class": "spindle",
  "timing": "in_block_or_post_motion",
  "raw": "M3"
}

Runtime event shape (conceptual):

{
  "event": "m_function",
  "m_value": 30,
  "class": "program_flow",
  "phase": "post_motion",
  "action": "program_end"
}

Machine Profile / Policy Contracts

Proposed profile fields:

  • max_m_per_block (integer)
  • unknown_mcode_policy (error|warning|ignore)
  • enable_extended_m_address (bool)
  • mcode_class_overrides (map<int, class>)

Policy interface sketch:

struct MCodeExecutionPolicy {
  virtual MCodeResolution resolve(int value,
                                  std::optional<int> extension,
                                  const MachineProfile& profile) const = 0;
};

Implementation Slices (follow-up tasks)

  1. Parser/semantic hardening
  • enforce full extended-address family restrictions
  • add per-block M-count validation with profile default
  1. AIL schema extension
  • add normalized class/timing metadata on m_function instructions
  1. Executor classification path
  • classify known predefined M values into control/machine events
  • keep unknown policy behavior configurable
  1. Timing semantics
  • enforce post-motion trigger for M0/M1/M2/M17/M30
  1. Integration points
  • connect M6 with tool-change policy path
  • connect M17/M2/M30 with subprogram/main-program control flow

Test Matrix (implementation PRs)

  • parser tests:
    • valid/invalid forms, range, extension restrictions, per-block count
  • AIL tests:
    • normalized instruction metadata for class/timing/raw/source
  • executor tests:
    • known vs unknown policy outcomes
    • post-motion timing for stop/end/return set
  • docs/spec sync:
    • program reference + SPEC sections updated per feature slice

Traceability

  • PRD: Section 5.12 (M functions)
  • Backlog: T-046
  • Coupled tasks: T-038 (tool change), T-050 (subprogram return flow)

Design: System Variables and Selector Model ($..., selectors, runtime evaluation)

Task: T-048 (architecture/design)

Goal

Define Siemens-compatible architecture for:

  • simple system-variable tokens such as $P_ACT_X
  • structured selector forms such as $A_IN[1] and $P_UIFR[1,X,TR]
  • runtime resolver behavior for variable-backed conditions and expressions
  • policy hooks for access restrictions and timing-class differences

This design maps PRD Section 5 variable/resolver requirements and the backlog scope for T-048.

Scope

  • parser/AST representation for simple and selector-style system variables
  • AIL/runtime boundaries for variable evaluation
  • resolver result model for value, pending, and error
  • policy hooks for read/write restrictions and timing classes
  • staged implementation plan for syntax, lowering, and runtime integration

Out of scope:

  • full Siemens parameter catalog implementation
  • controller-specific hardware data sources for every variable family
  • PLC/HMI authoring workflows

Current Baseline

Already shipped in v0:

  • simple $... token form parses in expressions and control-flow conditions
  • selector-style forms are rejected as syntax
  • AIL preserves simple $... references as system_variable
  • runtime branch resolver contract already works for simple $... conditions
  • runtime does not resolve $... control-flow targets such as GOTO $DEST

This note covers the next architecture step beyond that baseline.

Pipeline Boundaries

flowchart LR
  A[Source text with $ variables] --> B[Parser + Semantic]
  B --> C[AST variable reference model]
  C --> D[AIL lowering]
  D --> E[AilExecutor / runtime resolver]

  P[MachineProfile + VariablePolicy] --> B
  P --> E
  • Parser/semantic:
    • parses simple token form and selector arguments
    • validates selector shape and family-specific admissible selector patterns
  • AST:
    • preserves one normalized variable-reference node model
    • distinguishes user variables (R...) from system variables ($...)
  • AIL:
    • preserves variable-reference structure for runtime evaluation
    • does not resolve live values during lowering
  • Executor/runtime:
    • delegates variable reads/evaluation to resolver/policy interfaces
    • handles value, pending, and error outcomes deterministically

Variable Reference Model

Recommended normalized AST/AIL concept:

struct VariableSelectorPart {
  enum class Kind { Index, Axis, Attribute, Name };
  Kind kind;
  std::string text;
};

struct VariableReference {
  enum class Namespace { UserR, System };
  Namespace ns;
  std::string base_name;  // e.g. "R1", "$A_IN", "$P_UIFR"
  std::vector<VariableSelectorPart> selectors;
};

Examples:

  • R1
    • ns = UserR
    • base_name = "R1"
    • selectors = []
  • $P_ACT_X
    • ns = System
    • base_name = "$P_ACT_X"
    • selectors = []
  • $A_IN[1]
    • ns = System
    • base_name = "$A_IN"
    • selectors = [{Index, "1"}]
  • $P_UIFR[1,X,TR]
    • ns = System
    • base_name = "$P_UIFR"
    • selectors = [{Index, "1"}, {Axis, "X"}, {Attribute, "TR"}]

Selector Grammar Strategy

Do not model selector content as raw string forever. Normalize it.

Recommended staged parser rule shape:

  • simple variable:
    • $<word>
  • selector variable:
    • $<word>[<part>(,<part>)*]

Selector-part categories in first implementation:

  • numeric literal
  • bare identifier token

Family-specific semantics should remain semantic-layer validation, not grammar explosion. That keeps parsing stable while allowing:

  • $A_IN[1]
  • $P_UIFR[1,X,TR]
  • future family-specific restriction tables

Diagnostics Model

Current baseline:

  • selector attempts fail as generic syntax diagnostics at the first unsupported bracket/comma token

Target architecture:

  • parser/semantic emits structured diagnostics for:
    • missing closing ]
    • empty selector list
    • empty selector item
    • too many selector parts for a family
    • invalid selector token class for a family

Examples of desired future messages:

  • system variable selector requires closing ']'
  • system variable '$A_IN' requires exactly one numeric selector
  • system variable '$P_UIFR' requires selector form [index,axis,attribute]

Runtime Resolver Contract

Variable resolution should be explicit and shared across:

  • arithmetic expressions
  • branch conditions
  • future assignment/write validation

Recommended read result model:

enum class VariableReadKind { Value, Pending, Error };

struct VariableReadResult {
  VariableReadKind kind = VariableReadKind::Error;
  double value = 0.0;
  std::optional<WaitToken> wait_token;
  std::optional<int64_t> retry_at_ms;
  std::optional<std::string> error_message;
};

Recommended resolver interface:

struct VariableResolver {
  virtual VariableReadResult read(const VariableReference& ref,
                                  const SourceInfo& source) const = 0;
};

Current code organization note:

  • branch/condition pending/error results and the injected condition-resolver interface now live in src/condition_runtime.h
  • AilExecutor consumes that shared contract rather than defining its own condition-runtime result types inline

Semantics:

  • Value
    • expression/condition evaluation continues
  • Pending
    • branch executor can block on condition
    • future expression-evaluation surfaces need the same pending contract
  • Error
    • runtime faults or policy-warns depending on evaluation surface

Timing Classes and Policy Hooks

Not all variables have the same access characteristics.

Policy should be able to classify variables by timing/access:

  • preprocessing-safe constants
  • main-run live machine state
  • write-protected runtime state
  • channel-scoped vs global variables

Suggested policy sketch:

enum class VariableTimingClass {
  PreprocessStable,
  RuntimeLive,
  RuntimeBlocking
};

struct VariablePolicy {
  virtual VariableTimingClass timingClass(
      const VariableReference& ref) const = 0;
  virtual bool canWrite(const VariableReference& ref) const = 0;
};

This keeps parser core independent from controller-specific catalogs while making policy decisions explicit.

Control-Flow Interaction

Separate two cases:

  1. Variable-backed conditions
  • supported path already exists for simple $... tokens
  • structured selectors should extend the same ConditionResolver contract
  1. Variable-backed branch targets
  • currently unresolved at runtime in v0
  • future support should not be added accidentally
  • if added later, it needs a dedicated target-resolution contract, not implicit reuse of label/line-number lookup

That distinction prevents variable read semantics from being conflated with control-flow target resolution.

Output Schema Expectations

AST / AIL JSON should stop flattening selector forms into opaque strings once selector support lands.

Future stable JSON concept:

{
  "kind": "system_variable",
  "base_name": "$P_UIFR",
  "selectors": [
    {"kind": "index", "text": "1"},
    {"kind": "axis", "text": "X"},
    {"kind": "attribute", "text": "TR"}
  ]
}

Backward-compatibility strategy:

  • keep existing kind: "system_variable" for simple token form
  • add base_name and selectors
  • preserve legacy name field for a deprecation window if CLI goldens need schema stability during migration

Implementation Slices

  1. AST normalization
  • add structured variable-reference node for system selectors
  1. Parser diagnostics
  • replace generic bracket/comma syntax failures with selector-aware messages
  1. AIL JSON migration
  • preserve structured selector metadata in expression/condition nodes
  1. Runtime resolver abstraction
  • introduce shared variable-read interface for expressions/conditions
  1. Policy integration
  • add timing/access classification hooks and write restrictions
  1. Future target indirection decision
  • explicitly decide whether $... branch targets will ever be runtime-resolved

Test Matrix

  • parser tests:
    • accepted/rejected selector shapes
    • diagnostic locations and selector-specific messages
  • AIL tests:
    • structured selector JSON shape
    • parity between simple and selector-backed variable references
  • executor tests:
    • variable-backed condition pending/error/value
    • policy-driven rejection for unsupported timing/access classes
  • docs/spec sync:
    • syntax, JSON shape, runtime resolver sections updated together

Traceability

  • Backlog: T-048
  • Coupled tasks:
    • future parser selector slices for $...[...]
    • future runtime resolver abstraction slices
    • policy/model slices for access restrictions and timing classes

Design: Rapid Traverse Model (G0, RTLION, RTLIOF)

Task: T-045 (architecture/design)

Goal

Define Siemens-compatible rapid-traverse semantics for:

  • G0 positioning moves
  • rapid interpolation mode control (RTLION / RTLIOF)
  • effective-behavior overrides where nonlinear rapid is forced back to linear

This design maps PRD Section 5.11.

Scope

  • G0 representation and parse/lowering/runtime behavior
  • modal rapid interpolation state model (RTLION/RTLIOF)
  • effective-behavior override precedence rules
  • interaction points with Group 10 transitions and compensation/transform states
  • output schema expectations for configured vs effective rapid behavior

Out of scope:

  • low-level trajectory generation/servo tuning
  • machine-specific acceleration, jerk, and path planner internals

Pipeline Boundaries

flowchart LR
  A[G0 / RTLION / RTLIOF words] --> B[Parser + Semantic]
  B --> C[AIL Lowering]
  C --> D[AilExecutor]
  D --> E[Runtime motion events]

  P[MachineProfile + RapidPolicy] --> B
  P --> D
  • Parser/semantic:
    • captures declared rapid mode words and G0 moves
    • validates syntax and same-block mode declarations
  • AIL:
    • emits rapid_mode state instructions and motion_linear for G0
    • carries both declared and effective rapid mode metadata for G0
  • Executor:
    • maintains current rapid mode state
    • computes effective rapid behavior using override conditions

State Model

Rapid interpolation mode state:

  • linear (RTLION)
  • nonlinear (RTLIOF)

Baseline defaults:

  • startup/default rapid mode: profile-defined (linear default for safety)

State transition:

  • RTLION and RTLIOF are modal control words affecting subsequent G0 moves until changed.
stateDiagram-v2
  [*] --> linear
  linear --> nonlinear: RTLIOF
  nonlinear --> linear: RTLION

Declared vs Effective Rapid Mode

G0 output should differentiate:

  • declared rapid mode: current modal state (linear/nonlinear)
  • effective rapid mode: actual runtime mode after override rules

Reason:

  • Siemens behavior may force linear interpolation despite RTLIOF.
  • downstream consumers need both intent and effective execution behavior.

Override/Precedence Rules

If any forced-linear condition is active at G0 execution point, effective rapid mode becomes linear even when declared mode is nonlinear.

Forced-linear conditions (initial set):

  • active Group 10 continuous-path mode requiring linear rapid coupling (profile-defined mapping, e.g. G64 path mode behavior)
  • active tool-radius compensation state requiring path interpolation
  • active transformation/compressor mode flagged by runtime context

Precedence order:

  1. safety/compatibility forced-linear conditions
  2. declared rapid mode (RTLION/RTLIOF)
  3. profile default
flowchart TD
  R[Resolve G0 effective mode] --> O{Forced-linear condition active?}
  O -- yes --> L[effective = linear]
  O -- no --> D[effective = declared rapid mode]

Interaction Points

  • Group 10 (G60/G64..G645):
    • transition mode may constrain rapid interpolation behavior
  • Group 7 (G40/G41/G42):
    • active compensation may disable nonlinear rapid behavior
  • transformation/compressor runtime state:
    • runtime context can force linear mode by policy

This design keeps these as policy/context hooks, not hardcoded parser rules.

Output Schema Expectations

AIL rapid_mode instruction (conceptual):

{
  "kind": "rapid_mode",
  "mode": "nonlinear",
  "source": {"line": 42}
}

AIL/packet G0 motion metadata (conceptual):

{
  "kind": "motion_linear",
  "opcode": "G0",
  "rapid_mode_declared": "nonlinear",
  "rapid_mode_effective": "linear",
  "source": {"line": 43}
}

Runtime event (conceptual):

{
  "event": "rapid_move",
  "declared_mode": "nonlinear",
  "effective_mode": "linear",
  "reason": "forced_linear_due_to_comp_or_transform"
}

Machine Profile / Policy Hooks

Suggested config fields:

  • rapid_default_mode (linear|nonlinear)
  • force_linear_with_continuous_path (bool or group-map)
  • force_linear_with_tool_radius_comp (bool)
  • force_linear_with_transform (bool)

Policy interface sketch:

struct RapidTraversePolicy {
  virtual RapidMode resolveEffectiveMode(RapidMode declared,
                                         const RuntimeContext& ctx,
                                         const MachineProfile& profile) const = 0;
};

Implementation Slices (follow-up)

  1. Schema and metadata alignment
  • ensure G0 carries declared+effective rapid mode in AIL/packet outputs
  1. Executor effective-mode resolution
  • centralize forced-linear logic in policy/context layer
  1. Group/state integration
  • wire Group 10 and Group 7 state into effective-mode resolver inputs
  1. Diagnostics and explainability
  • optional runtime diagnostic/event when declared mode is overridden

Test Matrix (implementation PRs)

  • parser tests:
    • RTLION/RTLIOF syntax acceptance and ordering
  • AIL tests:
    • modal rapid-mode instruction emission
    • declared/effective metadata on G0
  • executor tests:
    • mode state transitions
    • forced-linear override scenarios
  • packet tests:
    • stable schema for rapid metadata

Traceability

  • PRD: Section 5.11 (rapid traverse movement)
  • Backlog: T-045
  • Coupled tasks: T-044 (Group 10), T-039 (Group 7)

Streaming Execution Architecture

This note defines the target contract for a true line-by-line streaming execution pipeline. It is an architecture-only slice intended to guide the refactor away from whole-input parse/lower APIs.

Problem Statement

Current parseAndLowerStream(...) behavior is callback-based delivery after the entire input has already been parsed into a full ParseResult.

Current flow:

whole input text -> parse() full AST -> lowerToMessagesStream() callbacks

Target flow:

chunk input -> assemble complete line -> parse line -> lower line -> execute or block

The target pipeline must:

  • accept arbitrary text chunks
  • emit deterministic per-line execution events
  • support blocking runtime operations
  • support resume after blocking completes
  • support cancellation while blocked or between lines
  • preserve source attribution and deterministic diagnostics

Scope

In scope for the refactor:

  • line/chunk input handling
  • streaming parse/lower/execute boundaries
  • injected interfaces for sink/runtime/cancellation
  • blocking and resume state machine
  • event-log contract for integration tests

Out of scope for this note:

  • parser grammar changes
  • Siemens feature expansion
  • transport-specific servo/planner internals
  • coroutine/threading implementation details

Public Direction

The new primary public execution API should be streaming-first.

Recommended high-level object:

class StreamingExecutionEngine;

Recommended usage model:

  1. construct engine with injected interfaces
  2. feed text with pushChunk(...)
  3. drive execution with pump()
  4. if blocked, wait externally and call resume(...)
  5. call cancel() for cooperative stop
  6. call finish() after end-of-input

Batch APIs may remain temporarily as adapters during migration, but the target public surface is incremental streaming.

Core Data Model

Message and instruction payloads should remain plain value types. Runtime behavior should be injected through interfaces.

Rationale:

  • value types are simpler to serialize and diff
  • closed message families map naturally to std::variant
  • behavior boundaries belong in services, not payload objects

Therefore:

  • keep G1Message, G2Message, G4Message, AilLinearMoveInstruction, etc. as structs
  • inject execution/runtime behavior via abstract interfaces

Injected Interfaces

IExecutionSink

Observability boundary for deterministic event delivery.

class IExecutionSink {
public:
  virtual ~IExecutionSink() = default;

  virtual void onDiagnostic(const gcode::Diagnostic &diag) = 0;
  virtual void onRejectedLine(const RejectedLineEvent &event) = 0;

  virtual void onLinearMove(const LinearMoveCommand &cmd) = 0;
  virtual void onArcMove(const ArcMoveCommand &cmd) = 0;
  virtual void onDwell(const DwellCommand &cmd) = 0;
  virtual void onControl(const ControlCommandEvent &event) = 0;
};

IExecutionSink is for logging, trace review, test capture, and optional application-facing event delivery. It should not own blocking machine work.

IRuntime

Slow or external machine-facing operations.

class IRuntime {
public:
  virtual ~IRuntime() = default;

  virtual RuntimeResult<double>
  readSystemVariable(std::string_view name) = 0;

  virtual RuntimeResult<void>
  writeVariable(std::string_view name, double value) = 0;

  virtual RuntimeResult<WaitToken>
  submitLinearMove(const LinearMoveCommand &cmd) = 0;

  virtual RuntimeResult<WaitToken>
  submitArcMove(const ArcMoveCommand &cmd) = 0;

  virtual RuntimeResult<WaitToken>
  submitDwell(const DwellCommand &cmd) = 0;

  virtual RuntimeResult<void>
  cancelWait(const WaitToken &token) = 0;
};

IRuntime handles operations that may complete immediately, block, or fail.

ICancellation

Cooperative cancellation boundary.

class ICancellation {
public:
  virtual ~ICancellation() = default;
  virtual bool isCancelled() const = 0;
};

Cancellation must be checked:

  • before starting a new line
  • after emitted diagnostics/events
  • while blocked
  • before accepting resume(...)

Runtime Result Contract

Blocking must be explicit in return values.

enum class RuntimeCallStatus {
  Ready,
  Pending,
  Error,
};

template <typename T>
struct RuntimeResult {
  RuntimeCallStatus status = RuntimeCallStatus::Error;
  std::optional<T> value;
  std::optional<WaitToken> wait_token;
  std::string error_message;
};

Rules:

  • Ready: operation completed synchronously; value is available when needed
  • Pending: engine must enter Blocked state and wait for wait_token
  • Error: engine emits diagnostic/fault and stops current execution path

Engine State Machine

Recommended top-level states:

enum class EngineState {
  AcceptingInput,
  ReadyToExecute,
  Blocked,
  Completed,
  Cancelled,
  Faulted,
};

Blocked state should retain explicit context:

struct WaitToken {
  std::string kind;
  std::string id;
};

struct BlockedState {
  int line = 0;
  WaitToken token;
  std::string reason;
};

Step result contract:

enum class StepStatus {
  Progress,
  Blocked,
  Completed,
  Cancelled,
  Faulted,
};

struct StepResult {
  StepStatus status = StepStatus::Progress;
  std::optional<BlockedState> blocked;
  std::optional<gcode::Diagnostic> fault;
};

Streaming Engine API

Recommended public API:

class StreamingExecutionEngine {
public:
  StreamingExecutionEngine(IExecutionSink &sink,
                           IRuntime &runtime,
                           ICancellation &cancellation);

  bool pushChunk(std::string_view chunk);
  StepResult pump();
  StepResult finish();
  StepResult resume(const WaitToken &token);
  void cancel();

  EngineState state() const;
};

Behavior summary:

  • pushChunk(...)
    • appends raw bytes
    • extracts complete logical lines
    • does not itself block on runtime work
  • pump()
    • processes available complete lines until one boundary is reached:
      • one line executes and returns progress
      • runtime blocks
      • cancellation triggers
      • fault occurs
      • no work remains
  • finish()
    • marks end-of-input
    • flushes final unterminated line if allowed
    • returns Completed once all work is done
  • resume(...)
    • resumes a previously blocked operation only when token matches current wait

Per-Line Execution Contract

For each complete line:

  1. parse the line into a line-level syntax/result form
  2. emit parse diagnostics for that line
  3. if line has an error diagnostic:
    • emit onRejectedLine(...)
    • enter Faulted or stop according to policy
  4. lower the line using current modal/runtime context
  5. emit execution event through IExecutionSink
  6. call the required IRuntime operation
  7. if runtime returns Pending, enter Blocked
  8. if runtime returns Ready, continue
  9. if runtime returns Error, emit diagnostic and fault

No subsequent line may execute while the engine is in Blocked.

G1 Contract

For a line containing G1:

  1. line is parsed
  2. line is lowered into a linear-motion command
  3. sink.onLinearMove(...) is called with normalized parameters
  4. runtime.submitLinearMove(...) is called with the same normalized command
  5. if pending, engine returns Blocked
  6. only after resume(...) may the next line proceed

Minimum command fields:

  • source:
    • filename if known
    • physical line number
    • N number if present
  • opcode:
    • "G1" or "G0" as applicable
  • target_pose
  • feed
  • modal metadata needed by downstream consumers

System-Variable Read Contract

System-variable reads are runtime-resolved, not parser-owned.

For a line or condition using $...:

  1. parser preserves the variable reference in syntax/IR
  2. execution requests value through IRuntime::readSystemVariable(...)
  3. if ready, evaluation proceeds immediately
  4. if pending, engine enters Blocked
  5. if error, engine emits diagnostic/fault

This contract applies equally to:

  • assignment expressions
  • branch/condition evaluation
  • future runtime-evaluated selector forms

Cancellation Contract

Cancellation is cooperative and observable.

Rules:

  • if ICancellation::isCancelled() becomes true before starting a new line, the engine transitions to Cancelled
  • if cancelled while blocked, the engine calls IRuntime::cancelWait(...) best-effort and then transitions to Cancelled
  • once cancelled, no further line execution occurs
  • resume(...) after cancellation is invalid

Event Log Contract for Integration Tests

The refactor should include a deterministic event-log sink for integration tests. JSON Lines is the recommended format.

Each record should contain:

  • seq: monotonically increasing event number
  • event: stable event name
  • source data when applicable
  • normalized parameters

Recommended event names:

  • chunk_received
  • line_completed
  • diagnostic
  • rejected_line
  • sink.linear_move
  • sink.arc_move
  • sink.dwell
  • runtime.read_system_variable
  • runtime.read_system_variable.result
  • runtime.submit_linear_move
  • runtime.submit_arc_move
  • runtime.submit_dwell
  • engine.blocked
  • engine.resumed
  • engine.cancelled
  • engine.completed
  • engine.faulted

Example:

{"seq":1,"event":"line_completed","line":1,"text":"N10 G1 X10 Y20 F100"}
{"seq":2,"event":"sink.linear_move","line":1,"params":{"opcode":"G1","x":10.0,"y":20.0,"feed":100.0}}
{"seq":3,"event":"runtime.submit_linear_move","line":1,"params":{"opcode":"G1","x":10.0,"y":20.0,"feed":100.0}}
{"seq":4,"event":"engine.blocked","line":1,"token":{"kind":"motion","id":"m1"}}
{"seq":5,"event":"engine.resumed","token":{"kind":"motion","id":"m1"}}
{"seq":6,"event":"engine.completed"}

Integration Test Structure

Recommended new test files:

  • test/streaming_execution_tests.cpp
  • test/streaming_cancellation_tests.cpp
  • test/runtime_integration_tests.cpp

Recommended fixture directories:

  • testdata/execution/
  • testdata/execution_logs/

Each integration test case should define:

  • input G-code
  • mock runtime script/config
  • expected event log

Migration Plan

Phase 1

  • add the new streaming engine and interface set
  • add event-log sink and mock runtime
  • keep current batch/stream APIs unchanged

Phase 2

  • adapt current parseAndLowerStream(...) to the new engine where possible
  • add parity tests between old callback behavior and new engine behavior for supported motion-only flows

Phase 3

  • move downstream callers to StreamingExecutionEngine
  • deprecate direct whole-input execution surfaces

Phase 4

  • remove legacy execution APIs only after parity and integration coverage are green

Risks and Open Questions

  • true line-by-line parsing may require a line-scoped parser entry point rather than current whole-program parse flow
  • modal state and structured multi-line constructs must define whether they are syntax-validated incrementally or buffered until closing statements arrive
  • sink event ordering must remain deterministic under blocking/resume paths
  • line/chunk semantics must define whether CRLF normalization occurs before or during line assembly

Smallest coherent slice:

  1. add the architecture interfaces and state types
  2. add event-log sink and scripted mock runtime
  3. implement motion-only G0/G1/G2/G3/G4 line execution
  4. add integration tests with blocking/resume/cancel coverage
  5. leave variable-evaluation execution and structured control flow for follow-up

Design: Incremental Parse Session API

Task: T-047 (architecture/design)

Goal

Define a deterministic session API for editor-style NC line edits where parsing can resume from an explicit line or the first error line without rebuilding unchanged prefix results.

Scope

  • API contract for line-based edits and resume controls
  • deterministic prefix/suffix merge rules for diagnostics and outputs
  • fail-fast behavior and first-error-line discovery
  • compatibility with text-backed and file-backed workflows

Out of scope:

  • parser-internal parse-tree reuse optimization
  • LSP/editor protocol transport details

Session Model

flowchart LR
  S[ParseSession] --> D[Editable Document Lines]
  S --> C[Cached Prefix Checkpoint]
  S --> R[Resume Engine]
  R --> P[Parse + Semantic]
  P --> L[Lower AIL/Message/Packet]
  L --> M[Deterministic Merge]
  M --> O[Session Output]

Core invariants:

  • Line numbering is 1-based and stable for unchanged lines.
  • Prefix lines before resume boundary are immutable in this parse cycle.
  • Suffix lines from boundary to EOF are fully recomputed.
  • Output ordering is always by source line and in-line statement order.

API Contract (Design)

struct SessionEdit {
  enum class Kind { ReplaceLine, InsertLine, DeleteLine };
  Kind kind;
  std::size_t line;      // 1-based target line
  std::string text;      // used by Replace/Insert
};

struct ResumeOptions {
  std::optional<std::size_t> from_line; // explicit resume boundary
  bool from_first_error = false;        // fallback boundary selector
  bool fail_fast = true;                // stop parse at first hard error
};

class ParseSession {
public:
  void setText(std::string full_text);
  void setFile(std::string path);
  void applyEdit(const SessionEdit& edit);
  void applyEdits(std::span<const SessionEdit> edits);

  ParseLowerResult reparse(const ResumeOptions& options);
  std::optional<std::size_t> firstErrorLine() const;
};

Behavior notes:

  • If both from_line and from_first_error are set, explicit from_line wins.
  • If from_first_error=true and no prior error exists, boundary is line 1.
  • Invalid boundary (0 or greater than line_count + 1) returns diagnostic error and does not mutate cached outputs.

Merge Semantics

Boundary B:

  • prefix: lines [1, B-1] (reused from cache)
  • suffix: lines [B, EOF] (reparsed/re-lowered)

Merging:

  • diagnostics: keep prefix diagnostics with line < B; replace all diagnostics with line >= B by freshly computed suffix diagnostics.
  • instructions/messages/packets: same rule as diagnostics; deterministic sort key is (line, statement_index, instruction_index).
  • rejected_lines: suffix entries fully replaced from B.
sequenceDiagram
  participant E as Editor
  participant S as ParseSession
  participant C as Cache
  participant P as Parser+Lowering

  E->>S: applyEdit(line=K, text=...)
  E->>S: reparse(from_line=K)
  S->>C: read prefix result [1..K-1]
  S->>P: parse/lower suffix [K..EOF]
  P-->>S: new suffix result
  S->>S: merge(prefix, suffix)
  S-->>E: ParseLowerResult + first_error_line

Fail-Fast and Error Recovery

  • fail_fast=true:
    • parser stops at first hard syntax/semantic error in suffix
    • first_error_line is updated to that line
    • prefix outputs remain valid and reusable
  • fail_fast=false:
    • parser continues collecting errors in suffix
    • first_error_line remains earliest suffix hard error

Recommended editor loop:

  1. parse with fail_fast=true
  2. user fixes first_error_line
  3. call reparse(from_line=fixed_line)

File-backed and Text-backed Modes

  • setText(...): in-memory source of truth
  • setFile(path): session loads file content into line buffer and then behaves identically to text-backed mode
  • Editing is always line-buffer based; file mode is not incremental I/O mode

Test Plan Slices (follow-up implementation tasks)

  1. ParseSessionApiTest:
    • invalid boundary handling
    • explicit boundary precedence over first-error mode
  2. ParseSessionMergeTest:
    • prefix diagnostic/instruction preservation
    • suffix replacement determinism
  3. ParseSessionFailFastTest:
    • first-error discovery and resume workflow
  4. ParseSessionFileBackedTest:
    • setFile parity with setText behavior

Traceability

  • PRD: Section 5.13 (incremental parse session API)
  • SPEC: Section 6 (resume session API / incremental contract)
  • Backlog: T-047

Design: Work-Offset Model (Group 8 + G53/G153/SUPA)

Task: T-043 (architecture/design)

Goal

Define Siemens-compatible architecture for:

  • modal Group 8 settable work offsets (G500, G54..G57, G505..G599)
  • non-modal suppression controls (G53, G153, SUPA)
  • machine-configuration variability for offset availability/ranges

This design maps PRD Section 5.9.

Scope

  • Group 8 modal state ownership/propagation
  • block-scope suppression context semantics and precedence
  • configuration model for machine-dependent offset ranges/aliases
  • interaction points with coordinate/frame/dimension state pipeline
  • output schema expectations for selected offset and effective suppression

Out of scope:

  • full controller frame-chain internals and kinematic transforms
  • HMI tooling for zero-offset table editing

Pipeline Boundaries

flowchart LR
  A[G500/G54..G57/G505..G599/G53/G153/SUPA] --> B[Parser + Semantic]
  B --> C[Modal Engine]
  C --> D[AIL Lowering]
  D --> E[AilExecutor]

  P[MachineProfile + WorkOffsetPolicy] --> C
  P --> E
  • Parser/semantic:
    • captures modal Group 8 words and suppression commands per block
    • validates supported offset codes against profile range set
  • Modal engine:
    • maintains persistent Group 8 selected offset code
    • resolves block-local suppression mask from G53/G153/SUPA
  • AIL/executor:
    • carries both selected offset state and effective suppression context
    • exposes runtime-consumable metadata, without embedding full frame math

State Model

Persistent Group 8 modal state:

  • g500 (deactivate settable work offset; base-frame behavior by profile)
  • g54, g55, g56, g57
  • g505..g599 (machine-dependent subset)

Block-scope suppression controls (non-modal):

  • G53: suppress settable and programmable work offsets
  • G153: G53 suppression plus basic-frame suppression
  • SUPA: G153 suppression plus DRF/overlay/external-zero/PRESET suppression
flowchart TD
  S[Current modal Group 8 selection] --> B[Current block]
  B --> X{Has suppression control?}
  X -- no --> E[effective uses modal selection]
  X -- yes --> M[effective suppression mask from G53/G153/SUPA]
  M --> E

Precedence and Scope Rules

  1. Group 8 selection is modal and persists across blocks until changed.
  2. G53/G153/SUPA are non-modal; they affect only the current block.
  3. Suppression controls do not mutate stored Group 8 modal selection.
  4. If multiple suppression controls occur in one block, effective mask uses strongest suppression level (SUPA > G153 > G53) and emits a warning.

Applicability examples:

  • G54 then motion in next block => G54 remains selected.
  • G54 with same-block G53 => selected state remains G54, effective suppression applies for that block only.
  • next block without suppression => effective context returns to selected G54.

Configuration / Machine-Profile Model

Required profile fields:

  • default_group8_offset (startup selection, e.g. g500 or g54)
  • supported_group8_offsets (range set; supports 828D-style variants)
  • legacy_aliases (optional mapping such as G58/G59 handling policy)
  • duplicate_suppression_policy (warn|error|first_wins)

Validation behavior:

  • unsupported Group 8 code emits diagnostic and does not update modal state
  • suppression controls always parse; profile may tighten same-block policy

Coordinate Pipeline Integration

Work-offset state composes with other coordinate states:

  • dimensions/units (T-042) influence numeric interpretation, not Group 8 ID
  • transition/rapid states (T-044/T-045) consume effective coordinate context
  • runtime motion/planner uses effective suppression mask plus selected offset identifier to resolve actual frame chain outside parser core

Output Schema Expectations

AIL state instruction concept:

{
  "kind": "work_offset",
  "group8_selected": "g54",
  "suppression": "g53",
  "suppression_scope": "block",
  "source": {"line": 120}
}

Runtime effective context concept:

{
  "effective_work_offset_selected": "g54",
  "effective_work_offset_enabled": false,
  "effective_suppression_mask": "suppress_settable_and_programmable"
}

Notes:

  • outputs should include both selected modal code and effective block behavior
  • runtime/planner can decide frame-chain math using machine-specific policies

Policy Interface Sketch

struct WorkOffsetPolicy {
  virtual EffectiveWorkOffset resolve(const ModalState& modal,
                                      const BlockContext& block,
                                      const RuntimeContext& ctx,
                                      const MachineProfile& profile) const = 0;
};

Implementation Slices (follow-up)

  1. Group 8 modal registry alignment
  • ensure full Group 8 code set and profile-backed validation paths
  1. Suppression context representation
  • add explicit block-level suppression metadata for G53/G153/SUPA
  1. AIL/runtime propagation
  • expose selected offset + effective suppression metadata per block
  1. Diagnostics/policy wiring
  • validate unsupported offsets and duplicate suppression controls per policy

Test Matrix (implementation PRs)

  • parser tests:
    • Group 8 syntax acceptance/rejection with profile range variants
    • suppression command parse coverage
  • modal-engine tests:
    • Group 8 persistence across blocks
    • block-local suppression precedence and non-mutation of modal selection
  • AIL/executor tests:
    • selected/effective work-offset metadata stability
    • policy behavior for duplicate suppression words
  • docs/spec sync:
    • add work-offset syntax/runtime sections as slices are implemented

Traceability

  • PRD: Section 5.9 (settable work offsets + suppression controls)
  • Backlog: T-043
  • Coupled tasks: T-042 (dimensions), T-044 (transition modes)

Design: Dimensions and Units Model (Groups 13/14 + AC/IC + DIAM*)

Task: T-042 (architecture/design)

Goal

Define Siemens-compatible architecture for:

  • Group 14 modal absolute/incremental state (G90/G91)
  • Group 13 unit state (G70/G71/G700/G710)
  • per-value absolute/incremental overrides (AC/IC)
  • rotary absolute targeting (DC/ACP/ACN)
  • turning diameter/radius programming family (DIAM*, DAC/DIC/RAC/RIC)

This design maps PRD Section 5.8.

Scope

  • state ownership and propagation through parse -> AIL -> runtime
  • precedence rules for modal state vs per-value overrides
  • model boundaries for units impacting geometry-only vs geometry+technology
  • axis/channel policy hooks for turning diameter/radius semantics
  • integration touchpoints with feed and plane/compensation states

Out of scope:

  • full machine-data database implementation
  • controller-specific cycle internals and kinematic solvers

Pipeline Boundaries

flowchart LR
  A[G90/G91/G70/G71/G700/G710/AC/IC/DC/ACP/ACN/DIAM*] --> B[Parser + Semantic]
  B --> C[Modal Engine]
  C --> D[AIL Lowering]
  D --> E[AilExecutor]

  P[MachineProfile + DimensionPolicy] --> C
  P --> E
  • Parser/semantic:
    • parses modal words, overrides, and axis-value forms
    • validates grammar shape and impossible combinations in one block
  • Modal engine:
    • holds Group 14 and Group 13 persistent state
    • resolves effective interpretation mode per value (modal + local override)
  • AIL/executor:
    • carries normalized interpretation metadata for motion/assignment consumers

State Model

Group 14 (modal distance interpretation):

  • g90: absolute
  • g91: incremental

Group 13 (modal units):

  • g70 / g71: geometry-only unit switch
  • g700 / g710: geometry + technological-length unit switch

Per-value overrides (non-modal value decorators):

  • AC(...): force absolute for the decorated value
  • IC(...): force incremental for the decorated value

Rotary absolute targeting (non-modal):

  • DC(...): shortest-path absolute rotary target
  • ACP(...): positive-direction absolute rotary target
  • ACN(...): negative-direction absolute rotary target

Turning diameter/radius family:

  • channel/axis mode words (DIAMON, DIAMOF, DIAM90, DIAMCYCOF)
  • axis-value forms (DAC(...), DIC(...), RAC(...), RIC(...))

Precedence Rules

  1. Effective value interpretation starts from modal Group 14.
  2. If a value has AC/IC, that local override wins for that value only.
  3. DC/ACP/ACN apply only to rotary-target forms and do not modify Group 14.
  4. Diameter/radius forms apply only on profile-eligible axes.
  5. Unit interpretation applies before runtime feed/planner consumption, with Group 13 scope deciding which quantities are unit-switched.

Unit Scope Model

  • G70/G71:
    • affects geometry quantities (positions, arc data, pitch, programmable zero offset geometry terms, polar radius)
    • does not directly switch technological feed-length interpretation
  • G700/G710:
    • affects geometry quantities plus technological-length quantities
    • must couple with feed model consumers
flowchart TD
  U[Group 13 state] --> S{Geometry only?}
  S -- yes --> G[apply to geometric fields]
  S -- no --> G2[apply to geometric + technological length fields]

Axis/Channel Policy Hooks

Machine/profile configuration should provide:

  • axis eligibility for diameter/radius semantics
  • channel turning-mode defaults
  • rotary-axis targeting constraints for DC/ACP/ACN
  • conflict behavior policy for mixed incompatible decorators in one block

Invalid policy cases (examples):

  • DAC(...) on non-turning axis -> diagnostic and ignore value
  • incompatible rotary decorators in same value -> diagnostic

Integration Points

  • Feed model (T-041):
    • Group 13 broad-unit mode (G700/G710) influences feed-length semantics
  • Working plane (T-040) and tool compensation (T-039):
    • axis/plane interpretation of center and compensated paths consumes effective dimension interpretation metadata
  • Work offsets (T-043):
    • coordinate-frame selection/suppression occurs independently from absolute/incremental and units interpretation, then composes in runtime

Output Schema Expectations

AIL modal-state concept:

{
  "kind": "dimension_state",
  "group14_mode": "g90",
  "group13_mode": "g710",
  "source": {"line": 80}
}

Value-level effective interpretation concept:

{
  "axis": "X",
  "effective_distance_mode": "incremental",
  "mode_source": "local_ic",
  "effective_unit_scope": "geometry_and_technology"
}

Rotary targeting concept:

{
  "axis": "C",
  "target_mode": "absolute_positive_direction",
  "source_decorator": "ACP"
}

Policy Interface Sketch

struct DimensionPolicy {
  virtual EffectiveDimension resolve(const ModalState& modal,
                                     const ValueContext& value,
                                     const RuntimeContext& ctx,
                                     const MachineProfile& profile) const = 0;
};

Implementation Slices (follow-up)

  1. Modal-state completion
  • finalize Group 13/14 representation and transition handling
  1. Value-override lowering
  • normalize AC/IC and rotary decorators into explicit value metadata
  1. Diameter/radius policy wiring
  • enforce axis eligibility and emit deterministic diagnostics
  1. Feed/plane coupling
  • integrate unit-scope effects with feed and plane consumers

Test Matrix (implementation PRs)

  • parser tests:
    • valid/invalid Group 13/14 transitions and decorator syntax
  • modal-engine tests:
    • Group 14 persistence + per-value override precedence
    • Group 13 scope behavior contracts
  • AIL/executor tests:
    • stable effective metadata for distance/unit/rotary modes
    • diameter/radius policy diagnostics by axis eligibility
  • docs/spec sync:
    • update SPEC sections for dimensions/units/turning semantics per slice

Traceability

  • PRD: Section 5.8 (dimensions, units, rotary targeting, DIAM*)
  • Backlog: T-042
  • Coupled tasks: T-041 (feed), T-039 (comp), T-040 (plane), T-043 (offsets)

Design: Siemens Tool-Change Semantics (T... + M6, with/without Tool Management)

Task: T-038 (architecture/design)

Goal

Define configurable Siemens-compatible tool-change behavior for:

  • direct-change mode (T triggers immediate tool change)
  • preselect + deferred-change mode (T selects, M6 executes)
  • tool-management off/on variants (number vs location/name selectors)
  • substitution/resolution policy integration

This design maps PRD Section 5.4.

Scope

  • AST/AIL/runtime representation boundaries for tool-select and tool-change
  • machine-configuration model for direct vs deferred mode
  • selection forms for management-off and management-on workflows
  • resolver callbacks / policy configuration for tool resolution, substitution, and error behavior
  • staged implementation plan and test matrix

Out of scope:

  • full tool life management database implementation
  • PLC or machine-specific toolchanger hardware integration

Pipeline Boundaries

flowchart LR
  A[T words + M6] --> B[Parser + Semantic]
  B --> C[AIL Lowering]
  C --> D[AilExecutor]
  D --> E[Runtime tool events]

  P[MachineProfile + Tool policy config] --> B
  P --> D
  • Parser/semantic:
    • parses tool-select forms and M6
    • validates selector shape per configured tool-management mode
  • AIL:
    • emits normalized tool_select and tool_change instructions
    • includes timing (immediate or deferred_until_m6) metadata
  • Executor:
    • tracks pending selection state
    • resolves selection/substitution via executor config/callbacks and commits active tool state

Instruction Contract

  • Tool actions are machine-visible behavior, so they must be explicit executable instructions (tool_select, tool_change) in AIL.
  • These instructions are not motion packets by default.
    • Motion packets remain a transport for geometry/motion families.
    • Tool instructions execute through runtime control-command handling.
  • This keeps parser/lowering deterministic while allowing hardware-specific actuation mapping in runtime policy layers.

Command Forms by Mode

Without Tool Management

Supported selection forms:

  • T<number>
  • T=<number>
  • T<n>=<number> (indexed selector)

Mode behaviors:

  • direct-change mode: T... selects and immediately activates tool/offset
  • preselect+M6 mode: T... stores pending selection; M6 executes swap/activate

With Tool Management

Supported selection forms:

  • T=<location>
  • T=<name>
  • T<n>=<location>
  • T<n>=<name>

Mode behaviors:

  • direct-change mode: T=... resolves location/name and activates immediately
  • preselect+M6 mode: T=... stores pending resolved candidate; M6 commits

State Model

Runtime tool-related state:

  • active_tool (currently effective tool)
  • pending_tool_selection (optional, for deferred mode)
  • tool_change_mode (direct or deferred_m6)
  • tool_management_enabled (bool)
stateDiagram-v2
  [*] --> NoPending
  NoPending --> Pending: T select in deferred mode
  Pending --> ActiveChanged: M6 execute
  NoPending --> ActiveChanged: T select in direct mode
  ActiveChanged --> NoPending

Precedence and Semantics

  1. Machine profile decides direct-vs-deferred mode.
  2. Selector grammar validity depends on tool_management_enabled.
  3. In deferred mode:
  • T... updates pending_tool_selection
  • M6 consumes pending selection and activates it
  1. In direct mode:
  • T... immediately activates resolved tool and clears pending state
  • standalone M6 is policy-controlled (warn/error/ignore)
  1. If multiple T... appear before M6, last valid selection wins.

Policy and Resolution Contracts

Tool resolution responsibilities:

  • normalize selector (number/location/name)
  • resolve substitution candidates where allowed
  • produce resolved | unresolved | ambiguous outcome

Policy controls:

  • on_unresolved_tool (error|warning|fallback)
  • allow_substitution (bool)
  • m6_without_pending_policy (error|warning|ignore)

Policy interface sketch:

struct ToolChangePolicy {
  virtual ToolSelectionResolution resolveSelection(
      const ToolSelector& selector,
      const MachineProfile& profile,
      const RuntimeContext& ctx) const = 0;
};

Interaction Points

  • M-code model (T-046):
    • M6 classification remains in M-code domain, but execution routes through tool-change boundary contracts from this design.
  • Work offsets (T-043):
    • tool activation may imply tool-offset state changes in runtime context.
  • Subprogram/runtime flow (T-050):
    • pending-tool state should be channel/program-context scoped by policy.

Output Schema Expectations

AIL tool_select concept:

{
  "kind": "tool_select",
  "selector_kind": "number_or_name_or_location",
  "selector_value": "T=DRILL_10",
  "timing": "deferred_until_m6",
  "source": {"line": 200}
}

AIL tool_change concept:

{
  "kind": "tool_change",
  "trigger": "m6",
  "source": {"line": 201}
}

Runtime event concept:

{
  "event": "tool_change",
  "previous_tool": "T12",
  "new_tool": "T7",
  "selection_source": "pending_then_m6",
  "substituted": false
}

Migration Plan (Small Slices)

  1. Parse/AST normalization
  • normalize all supported T... forms into one selector model
  1. AIL instruction split
  • emit explicit tool_select and tool_change instructions with timing metadata
  1. Executor pending-state semantics
  • implement direct vs deferred activation behavior
  1. Policy integration
  • add pluggable resolver/substitution path with deterministic diagnostics
  1. Cross-feature integration
  • align M6 runtime path with M-code classification and offset side effects

Backlog Tasks Generated (follow-up implementation)

  • T-051: parser/AST tool-selector normalization (T... forms by mode)
  • T-052: AIL schema for tool_select/tool_change + JSON/debug outputs
  • T-053: executor pending-state + direct/deferred semantics
  • T-054: tool policy resolver/substitution + unresolved/ambiguous diagnostics

Each follow-up task should include tests in test/, SPEC updates for behavior changes, and CHANGELOG_AGENT.md entries.

Test Matrix (implementation PRs)

  • parser tests:
    • accepted/rejected selector forms by management mode
  • AIL tests:
    • normalized instruction shape + timing metadata
  • executor tests:
    • direct-change flow
    • preselect+M6 flow
    • M6 without pending selection policies
    • last-selection-wins before M6
  • integration tests:
    • interaction with M-code path and tool-offset side effects

Traceability

  • PRD: Section 5.4 (tool-change semantics)
  • Backlog: T-038
  • Coupled tasks: T-046 (M-code), T-043 (offset context), T-050 (subprogram)

Repository Implementation Plan

This page is the canonical preserved copy of the older repository-level implementation plan.

Status: summary/reference only.

The authoritative current plan is implementation_plan_from_requirements.md.

Siemens-Compatible Parser and Runtime

1. Goal

Turn PRD Section 5 requirements into incremental, testable code changes while keeping main stable and each PR small.

Inputs:

  • ../../product/prd/index.md
  • architecture.md
  • ../../project/backlog.md (T-037..T-047)
  • acceptance fixture:
    • testdata/integration/simple_integrated_case.ngc

2. Delivery Strategy

Principles:

  • Build shared infrastructure first (modal/config/policy scaffolding).
  • Then add one feature family per slice.
  • Keep strict test-first and ./dev/check.sh green at each step.
  • PRs are created only; merge after explicit user approval.
  • Model every machine-carried behavior as an explicit executable instruction; decide per-instruction execution transport (motion packet vs runtime control).

3. Phase Plan

Phase A — Foundation (architecture scaffolding)

Target outcomes:

  • explicit modal registry with group IDs
  • machine profile/config objects
  • policy interfaces (tool/M-code/runtime hooks)

Planned tasks:

  • T-041 (feed model architecture; supplies modal/group scaffolding)
  • T-042 (dimensions/units architecture integration)

Expected code touch points:

  • src/ new files:
    • modal_registry.h/.cpp
    • modal_engine.h/.cpp
    • machine_profile.h/.cpp
    • runtime_policy.h/.cpp
  • existing:
    • src/ail.h, src/ail.cpp
    • src/ast.h (state-carrying metadata expansions)

Tests:

  • new test/modal_engine_tests.cpp
  • new test/machine_profile_tests.cpp

Phase B — Syntax and normalization extensions

Target outcomes:

  • parser coverage for required syntax families and normalized statement schema.

Planned tasks:

  • T-037 comments ((*...*), ;, optional // compatibility mode)
  • T-046 M-code syntax + normalization baseline
  • T-045 rapid-traverse syntax/state (G0, RTLION, RTLIOF)

Expected code touch points:

  • grammar/GCode.g4
  • src/gcode_parser.cpp
  • src/semantic_rules.cpp
  • src/semantic_normalizer.h/.cpp (new)

Tests:

  • test/parser_tests.cpp additions
  • parser/CLI golden updates in testdata/

Phase C — Modal behavior integration

Target outcomes:

  • modal groups represented and validated centrally with deterministic conflict handling.

Planned tasks:

  • T-039 Group 7
  • T-040 working plane
  • T-044 Groups 10/11/12
  • T-043 Group 8 offsets/suppress contexts
  • complete Group 14/13 and Group 15 behavior from T-041/T-042

Expected code touch points:

  • src/modal_engine.*
  • src/ail.cpp
  • src/messages*.cpp
  • src/packet*.cpp

Tests:

  • test/modal_engine_tests.cpp
  • test/ail_tests.cpp
  • test/ail_executor_tests.cpp
  • targeted new suites:
    • test/work_offset_tests.cpp
    • test/feed_mode_tests.cpp
    • test/transition_mode_tests.cpp

Phase D — Tool/runtime behavior

Target outcomes:

  • configurable Siemens-style tool behavior and runtime-executable semantics.

Planned tasks:

  • T-038 tool-change semantics with/without tool management
  • T-046 runtime-executable M-code subset mapping

Expected code touch points:

  • src/ail.cpp, src/ail.h
  • src/runtime_policy.*
  • possible new src/tool_policy.*, src/mcode_policy.*

Tests:

  • test/tool_change_tests.cpp (new)
  • test/mcode_tests.cpp (new)
  • executor integration tests

4. Backlog Dependency Order

Recommended execution order:

  1. T-041 feed model architecture (introduce modal/config scaffold)
  2. T-042 dimensions/units architecture (Group 13/14 integration)
  3. T-037 comments syntax completion
  4. T-046 M-code syntax/model baseline
  5. T-045 rapid traverse model
  6. T-039 Group 7 compensation state
  7. T-040 plane selection state
  8. T-044 exact-stop/continuous-path state
  9. T-043 work offsets and suppression contexts
  10. T-038 tool change + tool management policies
  11. T-047 incremental parse session API

Reasoning:

  • Steps 1-2 create common state/config foundation.
  • Steps 3-5 strengthen syntax/model inputs.
  • Steps 6-9 implement grouped modal behavior.
  • Step 10 applies the most machine-dependent runtime semantics last.

5. PR Slicing Policy

For each task:

  1. one narrow behavior slice
  2. tests first/with code
  3. docs updated in same PR (../../product/spec/index.md, CHANGELOG_AGENT.md, optionally ../../product/prd/index.md)
  4. evidence of ./dev/check.sh pass

Suggested PR size:

  • 200-500 LOC preferred
  • avoid mixing unrelated modal groups in one PR

6. Validation Matrix (minimum per feature)

  • Parser tests:
    • syntax acceptance/rejection
    • source-location diagnostics
  • AIL tests:
    • normalized instruction shape
    • modal metadata correctness
  • Executor tests:
    • runtime branch/state behavior where applicable
  • CLI tests:
    • stable debug/json outputs if schemas change
  • Integration fixture:
    • run parse + ail/lower on testdata/integration/simple_integrated_case.ngc
    • compare key invariants (no unintended diagnostics, stable line mapping, expected modal/motion/control items present)

7. Risk and Mitigation

Risk:

  • Modal logic spread across parser/lowering/runtime causing regressions. Mitigation:
  • centralize modal transitions in modal engine; add unit tests before feature rollout.

Risk:

  • machine-specific semantics hardcoded too early. Mitigation:
  • require MachineProfile + policy interfaces before advanced features.

Risk:

  • golden fixture churn. Mitigation:
  • update goldens only in focused PRs and include change rationale.

8. Review Checklist (per PR)

  • requirement trace to PRD subsection
  • architecture alignment with architecture.md
  • tests added/updated and meaningful
  • ./dev/check.sh green
  • no unintended schema break without doc updates

Implementation Plan

Status: summary only.

The authoritative current plan is implementation_plan_from_requirements.md.

Delivery Principles

  • small coherent PR slices
  • tests + docs in same PR
  • keep ./dev/check.sh green
  • merge only after explicit user approval

Phase Summary

  1. Foundation
  • machine profile scaffolding
  • modal registry scaffolding
  1. Syntax Extensions
  • comments, M-code syntax baseline, rapid-traverse syntax
  1. Modal Integration
  • grouped modal behavior rollout
  1. Tool/Runtime
  • tool-change policies
  • runtime-executable M-code subset

Dependency Order

Current preferred order is maintained in implementation_plan_root.md Section 4, including:

  • comments before deeper modal/runtime families
  • grouped modal rollout before full tool/runtime integration

Validation Matrix

Per feature slice:

  • parser tests
  • AIL tests
  • executor tests (if runtime behavior changes)
  • CLI/stage-output tests when schemas/output change
  • docs updates (../../product/spec/index.md, program/development references)

This is a split summary of implementation_plan_root.md, not the primary requirements-driven execution plan.

Development: Implementation Plan From Requirements

#Implementation Plan From Requirements

This note converts the reviewed requirements into a concrete architecture plan. It is intentionally more specific than the older phase summary in implementation_plan.md.

This is the authoritative implementation-planning document for the current requirements-reviewed architecture.

The following documents are summary/reference companions only:

Use this document to answer:

  • what the target architecture should be
  • what the current code already does correctly
  • what the main gaps are
  • what order the remaining work should happen in

Current Work-Unit Status

  • WU-1 Modal Snapshot Design: completed and merged
  • WU-2 Command Schema Redesign: completed and merged
  • WU-3 Runtime Dispatch Cleanup: completed and merged
  • WU-4 Executor State Cleanup: completed and merged
  • WU-5 Tool Execution Completion: completed and merged
  • WU-6 Diagnostics/Recovery Alignment: completed
  • WU-7 Final Public API Cleanup: completed
  • WU-8 Public Execution API Simplification: completed

Follow-Up Optimization Track

The original refactor work units are complete. The next optimization work should be tracked separately:

  • WU-9 Documentation Information Architecture Cleanup
    • Slice A:
      • move canonical project docs into docs/src/
      • make mdBook the main reading surface
      • reduce root AGENTS.md to a short startup map
    • Slice A.5:
      • remove duplicated explanations inside the mdBook
      • shorten oversized pages and split them into subtrees
      • improve concision and progressive disclosure
    • Slice B:
      • add CMake docs targets
      • move generated docs into build/docs/...
      • update install/export to consume build-tree docs outputs
    • Slice C:
      • split Requirements into smaller subtrees
      • start with docs/src/requirements/execution/index.md
      • keep syntax/semantic as later follow-ups
  • WU-10 Execution Contract Fixtures Step 1
    • add human-readable execution contract fixtures for the public ExecutionSession API
    • generate actual results and a GitHub Pages review subsite
    • current completed skeleton enforces the supported baseline cases: modal_update, linear_move_completed, rejected_invalid_line, fault_unresolved_target, goto_skips_line, and if_else_branch
  • WU-11 Execution Contract Fixtures Step 2
    • extend the same system to asynchronous blocked / resume / cancelled cases

Goal

Move the project to one clear execution-oriented architecture that is requirements-driven:

chunk input -> buffered parse/lower -> buffered executor state ->
normalized execution commands -> runtime interfaces -> resume/cancel

The parser, semantic validation, execution/runtime behavior, and test policy are already reviewed in the requirements documents. This plan maps those decisions onto code and work units.

Target Architecture

1. Syntax/Semantic Front End

Responsibilities:

  • parse text into structured form
  • preserve source information
  • emit syntax/semantic diagnostics
  • lower accepted text into AIL instructions

Key rule:

  • syntax/semantic layers must not perform runtime execution decisions

2. Buffered Execution Core

Primary public execution entry point:

  • ExecutionSession
  • buffered incremental input
  • pushChunk(...), pump(), finish(), resume(...), cancel()
  • Blocked vs WaitingForInput kept distinct

Internal execution core:

  • StreamingExecutionEngine

Execution semantics:

  • executor owns control flow, call stack, and incremental target resolution
  • forward targets wait before EOF and fault after EOF
  • execution-related state remains explicit

3. Execution IR

AilInstruction remains the executable IR.

Direction:

  • parser/lowering produces AIL
  • streaming engine buffers AIL
  • executor advances through AIL
  • normalized runtime-facing command data is emitted from execution

4. Runtime Boundary

Two-level split:

  • IRuntime
    • primitive machine/service operations only
  • IExecutionRuntime
    • richer language-level evaluation built on top of IRuntime

General action-command rule:

  • action submission returns Ready, Pending, or Error
  • Pending means accepted by runtime, not necessarily physically complete

5. Explicit Modal State

Requirements already fixed:

  • every supported modal group is explicit in execution state
  • all supported modal groups are preserved
  • emitted commands carry effective modal values for all supported groups

This implies a full modal snapshot model, not a partial ad hoc set of fields.

Current Architecture Assessment

What Already Matches The Target

  • public execution-session API already exists in execution_session.h
  • buffered streaming state already exists:
    • pending lines
    • buffered instructions
    • executor reuse
    • Blocked / WaitingForInput
  • AilExecutor already owns:
    • control-flow execution
    • call stack
    • unresolved-target deferral
    • runtime-aware stepping
  • runtime split already exists:
  • motion/dwell normalization and dispatch already exist
  • reviewed fake-log/testing pattern already exists

Main Architectural Gaps

1. Command schema is still partial

Current execution_commands.h now carries a baseline explicit modal subset:

  • motion code
  • working plane
  • rapid mode
  • tool radius compensation
  • active tool selection
  • pending tool selection

That is a useful first step, but it still does not satisfy the reviewed requirement that emitted commands carry all supported modal-group values.

2. Modal state is modeled in multiple shapes

Today modal state is split across:

  • ModalState
  • ExecutorState
  • EffectiveModalSnapshot
  • AIL per-instruction effective fields

This is workable, but it is not yet one clean source of truth.

3. Public execution command model is not finalized

We now have a normalized baseline for motion payloads:

  • one target object for motion endpoints
  • one effective snapshot for modal state
  • no duplicated emitted opcode / modal fields on execution commands

But the model is still not final for:

  • future tool/non-motion action payloads
  • full effective modal snapshot

4. Runtime boundary is correct in principle but not fully normalized

The split is right, but the exact inventory of:

  • executor-direct evaluation
  • IExecutionRuntime-only semantics
  • command submission types

still needs one consolidated design pass.

5. Tool execution is still policy-light

Tool execution requirements are reviewed, but current behavior still needs:

  • explicit command schema
  • explicit current/pending/selected tool snapshot model
  • final M6 no-pending-selection policy

6. Diagnostics/recovery contract is reviewed but not yet formalized in API shape

The requirements now include halt-fix-continue behavior for line failures, but the exact edit/reprocess session mechanics are not yet tied cleanly to the streaming/executor API surface.

Direction chosen for WU-6:

  • keep StreamingExecutionEngine focused on internal execution
  • provide ExecutionSession as the public editable rejected-suffix recovery API
  • use a recoverable Rejected state distinct from unrecoverable Faulted

Design Decisions To Apply Next

A. Introduce a full effective modal snapshot type

Add one explicit execution snapshot type, for example:

  • EffectiveModalSnapshot

It should become the single command-facing modal payload.

It should eventually include all supported groups, not just current motion ones.

B. Reduce duplicated modal state shapes

Target:

  • executor keeps canonical modal state internally
  • normalized commands expose a copied EffectiveModalSnapshot
  • ad hoc per-command modal subsets are retired where possible

C. Treat AIL as the only execution IR

Do not add a second interpreter path.

Direction:

  • syntax/semantic -> AIL
  • buffered streaming engine -> AIL executor
  • executor -> normalized command builder -> runtime interfaces

D. Keep IRuntime primitive

Do not grow IRuntime into a language interpreter.

Keep these on IExecutionRuntime:

  • richer expression evaluation
  • richer condition evaluation
  • parser-limited/context-heavy execution semantics

E. Keep one streaming execution model

Do not intentionally split semantics into:

  • batch-only execution behavior
  • streaming-only execution behavior

The buffered streaming engine should be the primary execution path.

Work Graph

R1 Full modal snapshot design
  -> R2 Command schema redesign
  -> R3 Command builder/runtime dispatch cleanup

R1 Full modal snapshot design
  -> R4 Executor state cleanup

R2 Command schema redesign
  -> R5 Tool execution schema/policy completion

R3 Command builder/runtime dispatch cleanup
  -> R6 Diagnostics/recovery API alignment

R4 Executor state cleanup
  -> R6 Diagnostics/recovery API alignment

R5 Tool execution schema/policy completion
  -> R7 Final public execution API cleanup

R6 Diagnostics/recovery API alignment
  -> R7 Final public execution API cleanup

WU-1 Modal Snapshot Design

Status: Completed

Deliver:

  • one explicit full modal snapshot type
  • mapping from executor state to command snapshot
  • requirements/doc updates if field inventory changes

Tests:

  • executor tests
  • streaming execution tests
  • CLI/event trace updates if payload changes

WU-2 Command Schema Redesign

Status: Completed

Deliver:

  • finalized command structs for:
    • motion
    • dwell
    • tool/action commands where needed
  • normalized field layout for source + modal snapshot

Tests:

  • executor tests
  • CLI/event trace goldens
  • public header tests

WU-3 Runtime Dispatch Cleanup

Status: Completed

Deliver:

  • consistent command building from executor state
  • less duplication between executor, command builder, and dispatcher

Tests:

  • executor runtime tests
  • streaming tests
  • fake-log trace tests

WU-4 Executor State Cleanup

Status: Completed

Deliver:

  • one clearer canonical modal/runtime state model inside executor
  • reduced duplicate state between executor and command payload helpers

Tests:

  • executor tests
  • state-transition tests

WU-5 Tool Execution Completion

Deliver:

  • explicit tool command schema
  • pending/current/selected tool state contract
  • reviewed M6-without-pending-selection policy

Tests:

  • executor tests
  • streaming tests
  • trace tests

WU-6 Diagnostics/Recovery Alignment

Deliver:

  • clear halt-fix-continue path for line failures
  • consistent rejected-line vs fault behavior through streaming execution
  • new ExecutionSession API for editable rejected-suffix recovery

Tests:

  • streaming tests
  • session/incremental tests
  • trace tests

WU-7 Final Public API Cleanup

Status: Completed

Deliver:

  • align public headers with final execution architecture
  • deprecate or adapt legacy surfaces that conflict with the primary model

Tests:

  • public header tests
  • integration tests
  • ./dev/check.sh

Remaining Implementation Order

Recommended order:

  1. No remaining refactor work units in the current requirements-derived plan

Definition Of Ready For Code Work

A work unit is ready only when:

  • the relevant requirement sections are already reviewed
  • current-vs-target gap is stated
  • in-scope and out-of-scope are explicit
  • test layers are declared
  • fixtures/trace expectations are known

Definition Of Done

For each work unit:

  • code matches the reviewed requirements
  • tests cover affected layers
  • fake-log/trace coverage exists for execution changes
  • public docs are updated if behavior/API changes
  • CHANGELOG_AGENT.md is updated
  • ./dev/check.sh is green

Project Planning

Use this section for project direction, prioritization, and executable work selection.

Pages in this section:

ROADMAP

Purpose

Define where the parser project is heading in ordered milestones. This is the single source of truth for direction and sequencing.

Current State

  • v0 parser exists with AST + diagnostics.
  • Golden tests and CI/dev checks are in place.
  • ANTLR-based pipeline is operational.

Milestones

M1: Harden v0 (Current)

Goal: make v0 reliable and maintainable.

  • Stabilize grammar behavior and diagnostics quality.
  • Add targeted regression tests for parser edge cases.
  • Add baseline fuzz/property testing and CI gate. Exit criteria:
  • ./dev/check.sh passes.
  • No open P0/P1 parser crash bugs.
  • Fuzz smoke runs in CI without crashes.

M2: v0.2 Syntax Coverage

Goal: expand syntax support while preserving AST/diagnostic quality.

  • Add additional common word/address patterns per SPEC updates.
  • Improve diagnostics (clearer messages, context hints).
  • Keep compatibility with existing golden tests. Exit criteria:
  • SPEC updated with new syntax and examples.
  • New golden tests for all added syntax.
  • Regression tests for all bug fixes merged during milestone.

M3: API and Tooling Usability

Goal: make parser easier to consume by downstream tools.

  • Introduce stable library API boundaries (parse options/result schema).
  • Add machine-friendly output mode (JSON) in CLI.
  • Improve docs for embedding parser in other C++ projects. Exit criteria:
  • Documented API contract.
  • Backward compatibility notes in SPEC/CHANGELOG.
  • Example integration in README.

M4: Quality and Scale

Goal: increase confidence under heavy/invalid input.

  • Extend fuzz corpus and long-input stress tests.
  • Add performance baseline and regression budget.
  • Add coverage trend visibility in CI. Exit criteria:
  • Fuzz and stress suite runs regularly.
  • Performance baseline captured and compared.
  • Coverage threshold policy agreed and enforced.

Prioritization Rules

  • Reliability over feature breadth.
  • Parser correctness over CLI convenience.
  • SPEC alignment over ad-hoc behavior.

Roadmap Update Policy

  • Update ROADMAP when milestone scope or order changes.
  • Every roadmap change must reference one or more BACKLOG items.

BACKLOG

How To Use

  • Agent picks by priority and dependency order.
  • For the current architecture-planning queue (T-037..T-047), follow Repository Implementation Plan Section 4 execution order.
  • Each task must include acceptance criteria before work starts.
  • Completed tasks are moved to a done section in this file or linked from PR.

Priority Legend

  • P0: must fix now (blocker, broken CI, crash/data-loss/security risk)
  • P1: high-value near-term
  • P2: important but can wait
  • P3: optional/enhancement

Ready Queue

  • Architecture planning queue T-037..T-047 is completed and moved to Done.

  • Next ready work should be selected from the remaining non-completed backlog entries by priority and dependency order.

  • ID: T-050

  • Priority: P1

  • Title: Siemens subprogram model (MPF/SPF, direct call, P-repeat, M17/RET, ISO M98)

  • Why: Subprogram organization and call semantics are required for practical Siemens compatibility and reusable NC structure.

  • Scope:

    • parser/AST support for:
      • direct call by subprogram name
      • quoted-name call form
      • repeat count P
      • P=<count> <name> and <name> P<count> forms
      • M17 and RET
      • ISO-gated M98 P... compatibility syntax
    • (planned extension) parse model for PROC signatures and call arguments
    • AIL/runtime execution model for call stack, return, and unresolved-target diagnostics/policies
    • search-path policy model (same-folder bare name vs qualified/full path)
  • Acceptance Criteria:

    • SPEC documents syntax + parse/runtime responsibilities
    • parser tests cover call/return syntax and ISO-mode gating
    • executor tests cover call stack return flow and unresolved call behavior
  • Out of Scope:

    • full program-manager filesystem replication
    • controller-specific PLC integration details
  • SPEC Sections:

    • Section 3.9
    • Section 6.1 (runtime control-flow execution notes)
  • Tests To Add/Update:

    • test/parser_tests.cpp
    • test/ail_tests.cpp
    • test/ail_executor_tests.cpp
  • ID: T-049

  • Priority: P1

  • Title: Siemens Chapter-2 syntax baseline (naming/blocks/assignment/comments/skip levels)

  • Why: We need explicit Siemens fundamental grammar coverage for NC program structure and skip behavior before higher-level runtime features.

  • Scope:

    • add parser support/validation for:
      • Siemens-compatible program-name forms (including %... compatibility mode)
      • block length limit diagnostics (Siemens baseline 512 chars)
      • assignment-shape = rules and numeric-extension disambiguation
      • skip-level markers /0.. /9 metadata capture
    • define runtime boundary for skip-level execution policy
  • Acceptance Criteria:

    • SPEC sections updated with concrete syntax/diagnostics behavior
    • parser tests cover valid/invalid chapter-2 baseline examples
    • runtime tests cover skip-level on/off behavior once execution hook is added
  • Out of Scope:

    • full subprogram file-management implementation
    • HMI-specific UI configuration behavior for skip levels
  • SPEC Sections:

    • Section 3.1, Section 3.6, Section 3.8, Section 5, Section 6.1
  • Tests To Add/Update:

    • test/parser_tests.cpp
    • test/ail_tests.cpp
    • test/ail_executor_tests.cpp
  • ID: T-048

  • Priority: P1

  • Title: System variables and user-defined variables model (Siemens-compatible)

  • Why: PRD now requires explicit handling for both user-defined variables (R...) and Siemens-style system variables ($...) with clear parse/runtime responsibility boundaries.

  • Scope:

    • extend variable reference model to support structured system-variable forms (including selector syntax like [1,X,TR])
    • define parser diagnostics for malformed variable selectors
    • define runtime resolver contract for variable read/eval outcomes (value|pending|error) in expressions/conditions
    • define policy hooks for access/write restrictions and timing classes (preprocessing vs main-run variables)
  • Acceptance Criteria:

    • SPEC documents syntax and boundary behavior for variable evaluation
    • parser/AIL tests cover user-defined + system-variable references
    • executor tests cover resolver pending/error behavior with variable-backed conditions
  • Out of Scope:

    • full Siemens parameter-manual variable catalog implementation
    • PLC-side variable transport internals
  • SPEC Sections:

    • Section 3.6 (assignment expressions / variable references)
    • Section 6.1 (runtime resolver behavior)
  • Tests To Add/Update:

    • test/parser_tests.cpp
    • test/ail_tests.cpp
    • test/ail_executor_tests.cpp
  • ID: T-051

  • Priority: P1

  • Title: Tool selector normalization for Siemens T... forms

  • Why: T-038 requires one normalized selector model across number/location/ name forms with mode-aware validation.

  • Scope:

    • parse/AST normalization for:
      • management-off forms: T<number>, T=<number>, T<n>=<number>
      • management-on forms: T=<location|name>, T<n>=<location|name>
    • mode-aware diagnostics for invalid selector forms
  • Acceptance Criteria:

    • parser emits normalized selector representation with stable locations
    • invalid-by-mode forms produce clear actionable diagnostics
  • Out of Scope:

    • runtime activation semantics (M6 / pending state)
  • SPEC Sections:

    • future: tool command syntax + selector rules
  • Tests To Add/Update:

    • test/parser_tests.cpp
    • golden fixtures affected by selector output shape
  • ID: T-052

  • Priority: P1

  • Title: AIL instruction split for tool select/change

  • Why: T-038 requires explicit tool_select vs tool_change boundaries and timing metadata (immediate vs deferred_until_m6).

  • Scope:

    • AIL schema + lowering updates for tool_select and tool_change
    • JSON/debug output updates for new instruction shapes
  • Acceptance Criteria:

    • AIL outputs preserve timing metadata and source attribution
    • CLI JSON/debug outputs remain deterministic
  • Out of Scope:

    • policy-based resolver/substitution behavior
  • SPEC Sections:

    • future: AIL/tool instruction schema
  • Tests To Add/Update:

    • test/ail_tests.cpp
    • test/cli_tests.cpp
  • ID: T-053

  • Priority: P1

  • Title: Executor pending-tool state and direct/deferred semantics

  • Why: T-038 requires runtime behavior for both direct T activation and deferred T+M6 execution.

  • Scope:

    • executor state for active_tool and pending_tool_selection
    • behavior rules for direct mode, deferred mode, and last-selection-wins
    • M6 without pending selection policy hook
    • runtime execution-path wiring for tool_select/tool_change as executable control instructions (non-motion path; no standalone motion packets)
  • Acceptance Criteria:

    • executor behavior is deterministic across direct/deferred configurations
    • diagnostics/policy behavior is explicit for M6 without pending tool
    • execution boundary is documented and test-covered:
      • instruction exists in AIL
      • runtime path executes it
      • packet stage remains motion-focused
  • Out of Scope:

    • tool database substitution resolution internals
  • SPEC Sections:

    • future: runtime tool-change behavior
  • Tests To Add/Update:

    • test/ail_executor_tests.cpp
    • test/ail_tests.cpp (if metadata coupling changes)
  • ID: T-054

  • Priority: P1

  • Title: Tool resolver/substitution policy integration

  • Why: T-038 requires machine-policy control for unresolved/ambiguous tool selectors and substitution behavior.

  • Scope:

    • tool policy interface and default policy implementation
    • unresolved/ambiguous selector outcomes (error|warning|fallback)
    • substitution-enabled vs substitution-disabled behavior
  • Acceptance Criteria:

    • policy outcomes are pluggable and test-covered
    • diagnostics clearly identify unresolved/ambiguous selectors
  • Out of Scope:

    • full tool life/wear optimization algorithms
  • SPEC Sections:

    • future: policy and diagnostics behavior for tool management
  • Tests To Add/Update:

    • test/machine_profile_tests.cpp
    • test/ail_executor_tests.cpp

Icebox

  • Coverage threshold policy and badge.
  • Multi-file include/subprogram parsing (future SPEC).

Task Template

Use this template for new backlog items:

  • ID: T-XXX
  • Priority: P0/P1/P2/P3
  • Title:
  • Why:
  • Scope:
  • Acceptance Criteria:
  • Out of Scope:
  • SPEC Sections:
  • Tests To Add/Update:

In Progress

(List tasks currently being worked on; only one assignee/task per PR)

  • (none)

Done

(Move completed tasks here with PR link)

  • T-010 (PR #17)
  • T-009 (PR #16)
  • T-008 (PR #15)
  • T-013 (PR #14)
  • T-012 (PR #13)
  • T-007 (PR #3)
  • T-006 (PR #3)
  • T-005 (PR #12)
  • T-004 (PR #11)
  • T-003 (PR #10)
  • T-002 (PR #9)
  • T-001 (PR #8)
  • T-011 (PR #6)
  • T-014 (PR #20)
  • T-015 (PR #21)
  • T-016 (PR #22)
  • T-017 (PR #23)
  • T-018 (PR #25)
  • T-019 (PR #26)
  • T-020 (PR #27)
  • T-021 (PR #28)
  • T-022 (PR #31)
  • T-023 (PR #33)
  • T-024 (PR #35)
  • T-025 (PR #36)
  • T-026 (PR #37)
  • T-029 (PR #38)
  • T-028 (PR #39)
  • T-030 (PR #40)
  • T-027 (PR #42)
  • T-031 (local, unmerged)
  • T-032 (local, unmerged)
  • T-033 (local, unmerged)
  • T-034 (local, unmerged)
  • T-035 (local, unmerged)
  • T-036 (local, unmerged)
  • T-037 (merged to main; see CHANGELOG_AGENT.md slices on 2026-03-03)
  • T-038 (merged to main; see CHANGELOG_AGENT.md entry on 2026-03-04)
  • T-039 (merged to main; PR #131)
  • T-040 (merged to main; PR #132)
  • T-041 (merged to main; PR #130)
  • T-042 (merged to main; see CHANGELOG_AGENT.md entry on 2026-03-04)
  • T-043 (merged to main; see CHANGELOG_AGENT.md entry on 2026-03-04)
  • T-044 (merged to main; see CHANGELOG_AGENT.md entry on 2026-03-04)
  • T-045 (merged to main; see CHANGELOG_AGENT.md entry on 2026-03-04)
  • T-046 (merged to main; see CHANGELOG_AGENT.md entry on 2026-03-04)
  • T-047 (merged to main; see CHANGELOG_AGENT.md entry on 2026-03-04)
  • T-050 (merged to main; see PR series #107-#129)
  • T-051 (local, unmerged)
  • T-052 (local, unmerged)
  • T-053 (local, unmerged)
  • T-054 (local, unmerged)