Experimental G-code Parser Docs
This mdBook is the canonical documentation entry point for this repository.
Use these sections in this reading order:
- Requirements: the review-first source of truth for what the project should support.
- Product Reference: product goals, public behavior contract, and implemented program reference.
- Execution Workflow: the supported public execution
model built around
ExecutionSession. - Execution Contract Review: the reviewed public fixture model and generated review site.
- Development Reference: workflow, OODA, testing, architecture, and design notes for maintainers.
- Project Planning: roadmap, backlog, and work-unit selection.
Relationship between the sections:
Requirementssays what the project should support.Product Referencesays what the public surface means today.Execution Workflowexplains how to drive the public execution API.Execution Contract Reviewshows reviewed trace-based public behavior.Development Referenceis for maintainers, not users of the library.Project Planningtracks 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.mdis the repository entry page. - Root
AGENTS.mdis 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:
WantedReviewedDeferredRejected
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
- Line and Program Structure
- Comments and Whitespace
- Motion, Modal, Dwell, and M Functions
- Variables, Control Flow, Subprograms, and Tools
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:
\nand\r\n - optional block delete
/ - optional skip levels
/0../9 - optional
Nline 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-PROGPROC 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:
G0G1G2G3
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 F100G1 AP=90 RP=10 F40G2 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 F3G4 S30
Review note:
- family status is reviewed; detailed value and block-shape constraints belong in the semantic requirements document and should be completed there
Modal and Mode Syntax Families
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
Mwords
Examples:
M3M30M17
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 = 2R1 = R2 + 1R1 = $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,GOTOCIF cond GOTO ... [ELSE GOTO ...]- structured
IF / ELSE / ENDIF WHILE / ENDWHILEFOR / ENDFORREPEAT / UNTILLOOP / 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 RETM17- ISO-compatible
M98 P... PROCdeclaration forms
Examples:
THE_SHAPE"THE_SHAPE"THE_SHAPE P2P=2 THE_SHAPERETM17
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
M6interaction 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:
WantedReviewedDeferredRejected
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:
G1must not mix Cartesian and polar endpoint modes in one blockG0must 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:
G4must be in a separate blockG4requires exactly one dwell mode wordG4 F...dwell value must be positiveG4 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
M6with 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)orABS(R1)
- general function-call-style expressions such as
Control-Flow Validation
Status: Reviewed (2026-03-10 review pass)
Rules to review:
- malformed
IF/GOTOshapes - 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
PROCdeclarations - 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:
- Modal State and Streaming
- Runtime Boundaries and Action Commands
- Motion and Timing
- Variables, Control Flow, and Calls
- Tools, Diagnostics, and Validation
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:
WantedReviewedDeferredRejected
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:
ReadyToExecuteBlockedWaitingForInputCompletedCancelledFaulted
Reviewed decisions:
pushChunk(...)only buffers input and prepares internal buffered state; it does not autonomously drive executionpump()advances execution until a meaningful boundary, not just exactly one commandfinish():- 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 blockingcancel():- attempts to cancel any pending runtime wait/action
- moves the engine to
Cancelled - prevents further execution progress afterward
BlockedandWaitingForInputare distinct states:Blockedmeans runtime/external waitingWaitingForInputmeans more G-code input/context is required
- unresolved forward targets should behave as:
- before EOF:
WaitingForInput - after
finish():Faulted
- before EOF:
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:
IRuntimeis the primitive machine/service boundaryIExecutionRuntimeis the richer language-aware execution boundary
- plain
IRuntimemust not own parsing, expression interpretation, condition interpretation, or control-flow semantics - plain
IRuntimemust 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;
IExecutionRuntimesits aboveIRuntime, 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
IRuntimeremains the primitive substrate used for operations such as:- variable reads/writes
- system-variable reads
- motion/dwell submission
- wait cancellation
IExecutionRuntimemay useIRuntimeoperations 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 requiredPending: accepted, but execution flow must wait for later resolutionError: rejected or failed
Pendingmeans 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/G3become 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:
G0andG1should normalize into explicit linear-move command structuresG2andG3should 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 continuePending: accepted and engine entersBlockedError: execution faults
- in the async model,
Pendingmeans the motion command was accepted by the runtime boundary, not necessarily physically completed Pendingdoes 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 commandresume(...)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
Pendingquickly and manage the wait asynchronously on the runtime side cancel()should attempt runtime cancellation of in-flight motion/wait and then move the engine toCancelled
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
G4becomes 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:
G4should 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 continuePending: accepted and engine entersBlockedError: execution faults
- in the async model,
Pendingmeans the dwell/timing command was accepted by the runtime boundary, not necessarily fully completed Pendingdoes 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 commandcancel()should attempt runtime cancellation of in-flight dwell/timing wait and then move the engine toCancelled
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
IRuntimeshould 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
Blockeduntilresume(...) - 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
IExecutionRuntimesubset
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
WaitingForInputvs 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
- before EOF:
- 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
- before EOF:
- 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
IRuntimebehavior
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_selecttool_change- deferred vs immediate timing
- pending tool state
M6behavior with/without pending selection- substitution/fallback policy model
Reviewed decisions:
tool_selectandtool_changeare 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
M6or 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
Pendingaction-command behavior - pending/current/selected tool state should remain explicit in execution state
- if
M6occurs 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
M6occurs 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
M6no-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.shremains 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
WaitingForInputBlockedresume(...)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:
- Goals and Scope
- API Requirements
- Functional Scope Overview
- Siemens Motion and Modal Domains
- Variables and Program Structure
- Quality and Release
Suggested review order:
- Read Goals and Scope.
- Confirm API Requirements.
- Review the functional scope pages by domain.
- Finish with Quality and Release.
PRD: Goals and Scope
Review Guide
Suggested review order:
- confirm scope and non-goals
- confirm API constraints
- review Siemens functional requirements by domain
- mark each major subsection as:
ApprovedNeeds ClarificationOut 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::parseTextGCodeParser::parseFileGCodeParser::parseAndLowerTextGCodeParser::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,G4syntax, 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
G40G41G42
Requirement:
- model Group 7 explicitly and track it independently from motion state
5.6 Working Plane Selection
Required commands:
G17G18G19
Requirement:
- working plane must propagate into arc interpretation and compensation-related behavior
5.7 Feedrate Semantics
Required Group 15 coverage includes:
G93G94G95G96G97G931G961G971G942G952G962G972G973
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:
G90G91
- local overrides:
AC(...)IC(...)
- rotary targeting:
DC(...)ACP(...)ACN(...)
- unit modes:
G70G71G700G710
- turning diameter/radius families:
DIAMONDIAM90DIAMOF- related axis-specific and value-override forms
5.9 Settable Work Offsets
Required Group 8 coverage:
G500G54G55G56G57G505 .. G599
Required suppress commands:
G53G153SUPA
5.10 Exact-Stop and Continuous-Path Modes
Required modal groups:
- Group 10:
G60G64G641G642G643G644G645
- Group 11:
G9
- Group 12:
G601G602G603
Required parameter support:
ADIS=...ADISPOS=...
5.11 Rapid Traverse
Required coverage:
G0RTLIONRTLIOF
Requirement:
- preserve rapid-mode state and effective override decisions explicitly
5.12 M-Code Syntax and Semantics
Required baseline predefined families include:
- flow control:
M0M1M2M17M30
- spindle:
M3M4M5M19M70
- 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:
- parse and stop at the first failing line under fail-fast policy
- edit the failing line
- 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 M17and optionalRET- 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
Gcodes:- implement only families explicitly listed in spec and backlog
Mcodes:- 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.shmust 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.mddocs/src/product/spec/index.mddocs/src/product/program_reference/index.mddocs/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.shgreen
SPEC
This section is the product behavior contract for the parser and the current public execution model.
Use these pages:
- Input and Output
- Motion and Program Syntax
- Control Flow and Variables Syntax
- Diagnostics and Lowering
- Execution and Runtime Contract
- Testing, Architecture, and Documentation Policy
Review order:
- Start with Input and Output.
- Read the syntax pages for accepted surface language.
- Read the lowering and execution pages for runtime-facing behavior.
- 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
ExecutionSessionexecution for the supported motion subset
2. Input and Output
2.1 Input
- UTF-8 text
- line endings:
\nor\r\n - tabs are whitespace
- comments:
; ...to end of line( ... )with no nesting(* ... *)block comments- optional
// ...whenParseOptions.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
ExecutionSessionsurface - injected execution sink, runtime, and cancellation interfaces
- explicit blocked, resumed, cancelled, rejected, faulted, and completed states
- current public execution coverage:
G0/G1/G2/G3/G4plus 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_parsegcode_stream_execgcode_exec_sessiongcode_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 ofLineLine:- optional
block_delete - optional
line_number - ordered
items
- optional
Word:texthead- optional
value has_equal
Comment:text
SPEC: Motion and Program Syntax
3. Supported Syntax
3.1 Line structure
- optional block delete:
/ - optional skip level:
/0through/9 - optional line number:
N<integer> - one motion command per line in
GGroup1 - word letters are case-insensitive
Nblock 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:
G0G1- 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
G0is represented as a linear-motion family member with modal codeG0RTLIONandRTLIOFcontrol 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]
- simple
- deferred in this slice:
- multi-selector forms like
$P_UIFR[1,X,TR] - feed expressions
- arc-word expressions
- multi-selector forms like
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:
G2clockwise arcG3counterclockwise arc- endpoints:
X/Y/Z - center words:
I/J/K - radius words:
CR,AR - polar endpoints:
AP,RP - intermediate-point form:
CIPwithI1/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
G4must be programmed in a separate NC block- exactly one dwell value is required:
F...secondsS...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:
RETM17
- baseline declarations:
PROC <name>PROC "<name>"
Execution/runtime baseline:
subprogram_callresolves 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:
errorwarningignore
3.11 Tool radius compensation
Supported words:
G40G41G42
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:
G17G18G19
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
=
- legacy numeric shortcut without
Runtime baseline:
- deferred mode stores pending tool selection until
M6 - direct mode submits immediately
M6without pending selection falls back to active selection when present- runtime tool acceptance follows the same
Ready|Pending|Errorcontract 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 asR1
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:
GOTOFGOTOBGOTOGOTOC
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 ... ENDIFWHILE ... ENDWHILEFOR ... TO ... ENDFORREPEAT ... UNTILLOOP ... ENDLOOP
Supported condition operators:
==><>=<=<>
Supported logical composition:
ANDacross condition terms- parenthesized terms are preserved for runtime resolver handling
Notes:
- label names follow word-style identifiers and may include underscores
- duplicate
Nline 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/G1line - multiple motion commands in one line
- invalid
G4block 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 -> AILparse -> AIL -> packetsparse -> messagesas 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:
packetsdiagnosticsrejected_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:
labelgotobranch_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:
truefalsependingerror
- variable references are preserved structurally for runtime resolution
- skip-level execution is applied at lowering/execution time from configured active levels
Executor state model:
readyblockedcompletedfault
Target resolution behavior:
GOTOF: forward-onlyGOTOB: backward-onlyGOTO: forward, then backwardGOTOC: 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
G1is 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:
readypendingerror
Blocked/resume rules:
- on
pending, execution becomes blocked pendingmeans the runtime accepted responsibility for the commandpendingdoes not mean the library should resubmit the same command laterresume(token)means the wait for that exact token is already satisfiedresume(token)continues the blocked state and must not resubmit the original command- queue-backed runtimes should return
pendingquickly 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:
readypendingerror
Tracing rule:
- every read attempt emits a
system_variable_readevent - if resume retries the same read, the retry emits a fresh read event
Supported runtime-backed public cases include:
if_system_variable_false_branchG1 X=$P_ACT_XG1 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, andcancel - 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:
filenameactive_skip_levelstool_change_modeenable_iso_m98_calls
Driver actions currently supported:
finishresume_blockedcancel_blocked
Linear-move result scripting currently supports:
readypendingwith explicit wait token
System-variable read scripting currently supports:
outcome: readywithvalueoutcome: pendingwithtokenoutcome: errorwithmessage
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.mddocs/src/product/prd/index.mddocs/src/product/spec/index.mddocs/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:
ImplementedPartialPlanned
- 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:
- APIs and Status Matrix
- Motion and Modal Commands
- Control Flow and Runtime
- Diagnostics and Invalid Programs
Suggested reading order:
- Start with APIs and Status Matrix.
- Read Motion and Modal Commands for the command-family coverage that is already implemented.
- Read Control Flow and Runtime for the current execution boundary and runtime behavior.
- 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/IExecutionRuntimemotion contract for motion-capable AIL instructions AilExecutorOptions.initial_statecan seed modal context when an executor instance needs to inherit prior plane, rapid, or tool-comp state
Public parser and lowering APIs:
parse(...) -> ParseResultparseAndLowerAil(...) -> 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 structuredIF/ELSE/ENDIF, but not loop families
Modal Metadata
Every emitted message includes modal metadata:
group:GGroup1orGGroup2code: emitted function code such asG0,G1,G2,G3,G4updates_state: whether this message updates modal state
Current supported baseline:
G0/G1/G2/G3->GGroup1,updates_state=trueG4->GGroup2,updates_state=false
Command Status Matrix
| Command | Status | Notes |
|---|---|---|
G0 rapid baseline | Implemented | Emits G0Message with target pose and feed. |
RTLION / RTLIOF rapid interpolation mode | Partial | Lowered to AIL rapid_mode; full machine-actuation semantics still pending. |
G40 / G41 / G42 tool radius compensation | Partial | Lowered to AIL tool_radius_comp; executor tracks modal state only. |
G17 / G18 / G19 working plane | Partial | Lowered to AIL working_plane; executor tracks active plane state only. |
G1 linear | Implemented | Compatibility surfaces emit G1Message; execution emits normalized linear-move commands. |
G2 arc CW | Implemented | Emits G2Message; execution can dispatch normalized arc commands. |
G3 arc CCW | Implemented | Emits G3Message; execution can dispatch normalized arc commands. |
G4 dwell | Implemented | Emits G4Message; execution can dispatch normalized dwell commands. |
M functions (M<value>, M<ext>=<value>) | Partial | Parse + validation + AIL boundary only; runtime machine actions are not fully mapped. |
R... = expr assignment | Partial | Parsed and lowered to AIL assign; richer expression and write semantics are deferred. |
N... line number at block start | Implemented | Parsed into source metadata with duplicate-warning support. |
Block delete / and /0.. /9 | Implemented | Parsed with skip-level metadata; lowering applies active_skip_levels. |
GOTO/GOTOF/GOTOB/GOTOC + labels | Implemented | Parsed, lowered, and executable through public ExecutionSession. |
IF cond GOTO ... [ELSE GOTO ...] | Partial | Parsed/lowered to branch_if; public execution handles the baseline expression subset. |
Structured IF/ELSE/ENDIF | Partial | Lowered into label/goto control flow; public execution supports the baseline branch subset. |
WHILE/ENDWHILE, FOR/ENDFOR, REPEAT/UNTIL, LOOP/ENDLOOP | Partial | Parse-only in current implementation. |
Comments ;..., ( ... ), (* ... *) | Implemented | Preserved as comment items in parse output. |
Comments // ... | Partial | Supported only when ParseOptions.enable_double_slash_comments=true. |
Subprogram call by name (THE_SHAPE, "THE_SHAPE") | Planned | Siemens-style call model planned; parser/runtime integration pending. |
Subprogram repeat (P=<n> NAME, NAME P<n>) | Planned | Repeat-count call semantics planned. |
Subprogram return (M17 baseline, RET optional) | Planned | Return-to-caller runtime semantics planned. |
ISO compat call (M98 P...) | Planned | Enabled only by ISO-compatibility profile option. |
Parse and Lower Options
Parse options:
ParseOptions.enable_double_slash_commentsfalse(default):// ...emits a diagnostictrue:// ...is accepted as a comment
Lower options:
LowerOptions.active_skip_levels- block-delete lines (
/=> level0,/n=> leveln) are skipped when the level is active
- block-delete lines (
Program Reference: Motion and Modal Commands
Motion Packets
Status:
Implementedfor the motion subset
Rules:
- packetization consumes AIL and emits packets for
G0/G1/G2/G3/G4 packet_idis 1-based and deterministic per result order- non-motion AIL instructions are skipped with warning diagnostics where appropriate
G0andG1both use packet typelinear_move; distinguish intent throughpacket.modal.code
Output fields:
packet_idtypesourcemodalpayload
Packet types:
linear_movearc_movedwell
G1
Syntax examples:
G1 X10 Y20 Z30 F100G1 AP=90 RP=10 F40
Rules:
- case-insensitive words are accepted
- mixing Cartesian and polar words in one
G1line 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:
- a complete
G1line is assembled - the line is parsed and lowered into a normalized linear-move command
- the execution sink receives the normalized command
- the runtime receives the same command via linear-move submission
- the runtime returns
ready,pending, orerror pendingblocks execution until explicit resume
G0
Syntax examples:
G0 X10 Y20 Z30G0 AP=90 RP=10
Rules:
- case-insensitive words are accepted
- mixing Cartesian and polar words in one
G0line 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:
RTLIONRTLIOF
Current behavior:
- lowering emits AIL
rapid_mode - JSON includes:
opcodemode
- following
G0AIL instructions includerapid_mode_effectivewhen 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:
G40G41G42
Current behavior:
- lowering emits AIL
tool_radius_comp - JSON includes:
opcodemode:off,left,right
AilExecutortrackstool_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:
G17G18G19
Current behavior:
- lowering emits AIL
working_plane - JSON includes:
opcodeplane:xy,zx,yz
AilExecutortracksworking_plane_currentG2/G3lowering 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 F100G3 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 F3G4 S30
Rules:
- must be programmed in its own NC block
- exactly one of
ForSmust be present
Output fields:
- source: filename, line, line_number
- modal:
group=GGroup2,code=G4,updates_state=false - dwell_mode:
secondsorrevolutions - 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 ENDandEND:into control-flow instructions - execution jumps to
END N20 G1 X10is skipped- only
N30 G1 X20reaches 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, onlyG1 X0reaches the runtime - otherwise only
G1 X10reaches 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:
M0M1M2M17M30
AIL output:
- emits
m_functioninstructions 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:
errorwarningignore
Current limitation:
- runtime machine-function execution mapping is not implemented in this slice
Control Flow and Runtime
Implemented behavior:
GOTOtarget search:GOTOF: forward onlyGOTOB: backward onlyGOTO: forward then backwardGOTOC: same search asGOTO, unresolved target does not fault
- target kinds:
- label
Nline number- numeric line number
- system-variable token target
AilExecutorbranch resolver contract:- injected resolver interface returns
true,false,pending, orerror pendingsupportswait_tokenand retry timestamp- function/lambda overload remains as a compatibility adapter
- injected resolver interface returns
- structured
IF/ELSE/ENDIFlowering usesbranch_ifplus 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.. /9enforced) - invalid or misplaced
Naddress words - duplicate
Nwarning when line-number jumps are present G4block-shape checks- assignment-shape baseline (
AP90rejected;AP=90,X1=10accepted)
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.hgcode/execution_interfaces.hgcode/execution_runtime.hgcode/execution_session.h
Use these additional headers only if you separately need syntax or IR access:
gcode/gcode_parser.hgcode/ast.hgcode/ail.h
What You Need To Implement
To execute G-code you usually provide three objects:
IExecutionSink- receives normalized commands and diagnostics
- receives explicit modal-only updates through
onModalUpdate(...)
IRuntimeorIExecutionRuntime- performs slow or external work
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:
ExecutionSessionpreserves the exact runtime-facing variable name and resolves it throughIRuntime.readSystemVariable(...)- supported name shapes in this slice are:
- simple
$NAME - single-selector
$NAME[part]
- simple
- if the read is
Ready, the sink/runtime receive a normal numericLinearMoveCommand - if the read is
Pending, the session becomesBlockedbefore anyLinearMoveCommandis 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:
ModalUpdateEventLinearMoveCommandArcMoveCommandDwellCommandToolChangeCommand
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:
- session emits a normalized command to the sink
- session calls the runtime
- runtime returns:
ReadyPendingError
Meaning:
Ready- accepted and execution can continue now
Pending- accepted by the runtime boundary, but execution must pause until you later
call
resume(...)
- accepted by the runtime boundary, but execution must pause until you later
call
Error- execution becomes
Faulted
- execution becomes
Important async contract details:
Pendingdoes 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 satisfiedresume(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 ownsExecutionSessioncancel()is mainly intended for a session that is alreadyBlocked, so the runtime can abort the pending work throughcancelWait(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 structuredIF/ELSE/ENDIFon the public path
Practical consequences:
- unresolved forward
GOTO/branch targets can wait for later input before EOF - structured
IF/ELSE/ENDIFruns throughExecutionSession - branch conditions in the baseline expression subset do not need
IExecutionRuntime; the plainIRuntimeadapter 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
Recommended Starting Point
If you are integrating this library into a controller:
- implement
IExecutionSink - implement
IRuntime - create
ExecutionSession - use:
pushChunk(...)pump()finish()resume(...)cancel()
- 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
.ngcinput files - persistent approved
.events.yamlreference traces - generated
.actual.yamltraces 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_variablesruntime.system_variable_readsruntime.linear_move_results
The current fixture-level options mirror the public LowerOptions fields that
can affect observable execution behavior:
filenameactive_skip_levelstool_change_modeenable_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, anderroroutcomes 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:
readypendingwith 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_updatelinear_move_completedlinear_move_blockedlinear_move_cancelledlinear_move_block_resumepending_system_variable_readerror_system_variable_readlinear_move_system_variable_xlinear_move_system_variable_selector_xmotion_then_condition_system_variable_readsrepeated_system_variable_readsrapid_move_system_variable_zresumed_system_variable_readdwell_seconds_completedtool_change_deferred_m6rejected_invalid_linefault_unresolved_targetgoto_skips_lineif_else_branchif_system_variable_false_branchif_system_variable_selector_false_branch
Event Model
Reference traces are ordered event lists. Step 1 uses:
diagnosticrejectedmodal_updatesystem_variable_readlinear_movedwelltool_changefaultedcompleted
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->blockedlinear_move->blocked->linear_move->completedlinear_move->blocked->cancelled
The driver now supports:
finishresume_blockedcancel_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:
readypendingerror
- 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:
- Docs Policy
- Workflow
- OODA
- Dual-Agent Workflow
- Dual-Agent Feature Checklist
- G-code Text Flow
- Testing Reference
- Architecture and Design
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.mdAGENTS.mdCHANGELOG_AGENT.md
Rules:
README.mdis the repository entry page.AGENTS.mdis the agent startup map, not the full docs body.CHANGELOG_AGENT.mdis 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:
RequirementsProduct ReferenceExecution WorkflowExecution Contract ReviewDevelopment ReferenceProject 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
developerorintegration 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.mdshould 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 behaviorproduct/= public-facing meaning and behaviordevelopment/= 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 ascheck.shandbench.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 parsefor AST + diagnostics output--mode ailfor intermediate AIL instruction output (jsonor debug summary)--mode packetfor AIL->packet output (jsonor debug summary)--mode lowerfor lowered message output (jsonor 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
- Observe current repo and CI state.
- Orient against
../project/roadmap.md,../project/backlog.md, and../product/spec/index.md. - Decide one scoped backlog slice.
- 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.mdif behavior changes. - Update
CHANGELOG_AGENT.mdin 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:
gitstate: branch, uncommitted changes, ahead/behind.- CI status: latest workflow result and failing jobs.
- Test health:
./dev/check.shoutput. - 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.mdwhen 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.mdupdated (task moved/marked done).../project/roadmap.mdupdated 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:
developerintegration 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:
ExecutionSessionpublic behavior- execution-contract review fixtures
- integration/regression tests across parser/lowering/execution boundaries
Recommended Loop
- User and developer discuss the feature, contract, and implementation intent.
- Developer implements production code and commits it.
- Integration tester checks out that committed developer state.
- Integration tester adds public-behavior and contract tests only.
- If tests fail:
- developer fixes production code, or
- tester fixes an incorrect test
- Repeat until:
- production code
- tests
- docs/spec
./dev/check.share 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>-devfeature/<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.shpasses
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.shpasses- 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.g4src/gcode_parser.cppsrc/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
PROCdeclaration shape - invalid
G4block 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_selecttool_changem_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
Related Docs
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.cpptest/streaming_tests.cpptest/messages_json_tests.cpptest/regression_tests.cpptest/semantic_rules_tests.cpptest/lowering_family_g4_tests.cpptest/packet_tests.cpptest/cli_tests.cpp
Representative Test Data
testdata/packets/*.ngctestdata/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
Tvs deferredT+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/G3as 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
- Group 1: motion type family (
- Related non-group/aux state (modeled alongside modal groups):
- rapid interpolation control (
RTLION/RTLIOF) affectingG0behavior - block-scope suppress contexts (
G53/G153/SUPA)
- rapid interpolation control (
6.1 Incremental Parse Session Strategy
ParseSessionowns 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())
- explicit line (
- 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
- resolve meaning under
- 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
- Implemented:
- core
G1/G2/G3/G4flow - control flow core (
goto,if_goto, structuredif/else/endiflowering) - line-number targeting runtime
- architecture contract for future line-by-line streaming execution
- core
- Partial:
G0rapid semantics- M-code semantics
- comment compatibility extensions
- current callback streaming path still parses the entire input first
- Detailed exact-stop/continuous-path architecture note:
- Detailed M-code architecture note:
- Detailed rapid-traverse architecture note:
- Detailed incremental parse-session API architecture note:
- Detailed work-offset architecture note:
- Detailed dimensions/units architecture note:
- Detailed tool-change architecture note:
- Planned:
- modal groups 6/7/8/10/11/12/13/14/15 full state model
- tool management behaviors
- full feed/dimension/unit/work-offset integration
9. Implementation Sequence (High Level)
- Build modal registry + machine profile skeleton.
- Move existing modal logic into modal engine.
- Introduce semantic normalizer output schema.
- Add one feature family at a time (comments, M, groups, feed, dimensions).
- 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.g4src/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/G3baseline) - 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) G641coupling withADIS/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/ADISPOSwithG641 - 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):
G60exact-stop modeG64,G641,G642,G643,G644,G645continuous-path family
Group 11 (non-modal):
G9exact-stop for current block only
Group 12 (modal criterion):
G601exact-stop fineG602exact-stop coarseG603IPO end
Auxiliary parameters:
ADIS=<value>(path transitions, usually G1/G2/G3 path moves withG641)ADISPOS=<value>(positioning/rapid transitions,G0withG641)
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
- Group 11 (
G9) is block-local and has highest effect for that block. - Otherwise Group 10 modal state defines transition behavior.
- Group 12 criterion is meaningful only when effective exact-stop is active
(
G60modal orG9current block). - 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 appliedG9+ (G601|G602|G603) => criterion applied in current blockG64..G645+ (G601|G602|G603) => criterion inactive (stored, not used)
G641 + ADIS/ADISPOS Coupling
Rules:
ADISandADISPOSare only behavior-relevant when effective Group 10 mode isG641.- if omitted, default value is
0(profile default may override) ADIStargets path transitions;ADISPOStargets 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_defaultsupports_g644_with_transform(bool; false -> policy fallback tog642)
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)
- Modal registry/state completion
- ensure Groups 10/11/12 are fully represented with scope semantics
- Parser/semantic validation
- robust parsing and validation for
ADIS/ADISPOS - criterion applicability diagnostics/policy notes
- AIL/runtime effective state
- emit and consume transition metadata for each block
- implement Group 11 block-scope override behavior
- 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
- syntax/validation for Group 10/11/12 and
- modal-engine tests:
- block-local
G9override behavior - Group 12 applicability under exact-stop only
- block-local
- 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
Freprogramming 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, orFL - unit coupling to
G700/G710is not yet explicit
Migration target:
- keep the existing scalar
feedfield 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, andFL - validates statement shape and same-block conflicts
- parses feed mode words,
- 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 feedg94: path feed per minuteg95: feed per spindle revolutiong96: constant-cutting-rate coupled modeg97: constant-rpm coupled modeg931,g961,g971,g942,g952,g962,g972,g973: Siemens-specific distinct Group 15 modal members
Associated persistent feed context:
- programmed
Fvalue fgroup_axes: active path-axis setfgref_by_axis: rotary effective-radius mapfl_by_axis: axis speed-limit mapfeed_requires_reprogramming: latch set when Group 15 transition requires a freshF
Defaulting rules:
- startup Group 15 mode is profile-defined
- startup
FGROUPcomes from machine profile default path grouping - startup
FGREF/FLmaps are empty unless profile seeds them
Transition and Persistence Rules
- Group 15 members are mutually exclusive and persist until another Group 15 word is programmed.
- Changing between Group 15 members does not erase stored
F, but it does setfeed_requires_reprogrammingwhen the contract requires an explicit newF. Fupdates the programmed feed value for the current feed mode.FGROUP(...)replaces the active path-axis set.FGROUP()with empty arguments restores the machine-profile default group.FGREF[axis] = valueupdates only the named rotary-axis reference entry.FL[axis] = valueupdates 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:
Fis interpreted primarily against the active path group- synchronized axes inherit block duration from the resolved path duration
FLcan 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:
- determine active path axes from
FGROUP - identify rotary axes participating in feed calculation
- apply
FGREFeffective radius for those rotary axes - combine linear and converted rotary contributions under the current Group 15 mode
Policy boundary:
- missing
FGREFfor 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/G71affects geometric lengths only - Group 13
G700/G710affects 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
Fwas 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
FGREFandFLentries
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_modedefault_fgroup_axesrequire_explicit_f_after_group15_changemissing_fgref_policyallow_rotary_path_feed_without_fgrefdefault_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-042dimensions/units:- Group 13 unit scope changes feed-length interpretation
T-040working plane:- arc/helical path-length resolution consumes both plane and feed state
T-039Group 7 compensation:- compensated path geometry changes runtime path-length calculation inputs
T-044Groups 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)
- Feed-state schema scaffold
- introduce explicit Group 15 enum/state structures and AIL feed-state instruction skeletons
Fand reprogramming contract
- define transition behavior and diagnostics for mode changes that require a
fresh
F
FGROUPrepresentation
- model path-axis membership and synchronized-axis partition metadata
FGREFand mixed rotary feed
- add rotary reference-radius representation and policy-driven diagnostics
FLcoordinated limit behavior
- integrate axis speed-limit metadata into runtime feed resolution
- 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, andFL
- acceptance/rejection for Group 15 words,
- modal-engine tests:
- Group 15 transitions and persistence
Freprogramming latch behaviorFGROUP()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-
FGREFpolicy 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, andG42are 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, andG42 - AIL lowers them to explicit
tool_radius_compinstructions - 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
- Group 7 is modal and persists until another Group 7 command is programmed.
- Group 7 transitions are independent from Group 1 motion selection.
- same-block conflicting Group 7 words are handled by the central modal conflict policy.
- repeating the same Group 7 code in one block is allowed under the existing modal-registry baseline unless policy says otherwise.
G40changes 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/G19decide 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_modeallow_group7_without_geometry_enginegroup7_conflict_policyrequire_plane_for_compensation_resolutioncompensation_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_compAIL instruction - executor state tracking only
- packet-stage ignore for standalone Group 7 commands
Follow-up migration:
- modal-engine ownership
- lift Group 7 into centralized modal-state transitions explicitly
- motion metadata propagation
- attach declared Group 7 state to motion outputs/messages/packets
- plane coupling
- resolve Group 7 semantics against active working plane
- policy-driven effective behavior
- separate declared state from effective geometry/runtime behavior
- optional geometry engine integration
- allow future compensated path generation without changing parse/AIL schema
Implementation Slices (follow-up)
- Modal-engine consolidation
- route Group 7 transitions through central modal engine APIs
- Output metadata exposure
- carry declared Group 7 state on motion/message/packet outputs
- Plane-aware runtime contract
- integrate Group 7 with working-plane state for effective behavior resolution
- 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
- recognition of
- modal-engine tests:
- persistence and deterministic transitions for Group 7
- AIL tests:
- stable
tool_radius_compinstruction schema - motion metadata includes declared Group 7 state when added
- stable
- 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, andG19are 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, andG19 - 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 directionZg18: ZX plane, infeed/tool-length directionYg19: YZ plane, infeed/tool-length directionX
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
- Group 6 is modal and persists until another Group 6 command is programmed.
- Group 6 transitions are independent from Group 1 motion-family state.
- same-block conflicting Group 6 words are handled through the central modal conflict policy.
- repeating the same plane word in one block is allowed under the current modal-registry baseline unless policy says otherwise.
- 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/Jallowed,Krejected
- contour plane
G18:- contour plane
ZX I/Kallowed,Jrejected
- contour plane
G19:- contour plane
YZ J/Kallowed,Irejected
- contour plane
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-> infeedZG18-> infeedYG19-> infeedX
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_planegroup6_conflict_policyplane_axis_mapping_policyallow_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:
- modal-engine consolidation
- route Group 6 transitions through central modal-engine ownership explicitly
- normalized plane metadata
- expose contour plane and infeed-axis metadata consistently across AIL and message/packet outputs
- Group 7 integration
- resolve tool-radius-compensation interpretation against active plane state
- tool-length / future consumers
- reuse the same plane contract for infeed direction and related semantics
- policy boundary for nonstandard kinematics
- isolate any machine-specific plane reinterpretation behind profile/policy
Implementation Slices (follow-up)
- Modal-engine consolidation
- centralize Group 6 transitions and conflict handling
- Arc consumer unification
- ensure all arc validation/lowering paths read the same plane mapping source
- Output metadata completion
- add stable declared/effective plane metadata to motion outputs where needed
- Group 7 coupling
- wire working plane into compensation runtime/policy resolution
- 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
- recognition of
- 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_functioninstructions
- emits normalized
- 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 range0..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
- baseline policy default:
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:
M6integrates with tool-selection/preselect model from tool-change design (T-038).
- Spindle subsystem:
M3/M4/M5/M19/M70map to spindle direction/stop/position/axis-mode events.
- Coolant/custom PLC:
- non-predefined values are policy-dispatched; no core hardcoding.
- Control-flow subsystem:
M17return semantics integrate with subprogram call stack model (T-050).M2/M30terminate 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)
- Parser/semantic hardening
- enforce full extended-address family restrictions
- add per-block M-count validation with profile default
- AIL schema extension
- add normalized class/timing metadata on
m_functioninstructions
- Executor classification path
- classify known predefined M values into control/machine events
- keep unknown policy behavior configurable
- Timing semantics
- enforce post-motion trigger for
M0/M1/M2/M17/M30
- Integration points
- connect
M6with tool-change policy path - connect
M17/M2/M30with 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, anderror - 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 assystem_variable - runtime branch resolver contract already works for simple
$...conditions - runtime does not resolve
$...control-flow targets such asGOTO $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, anderroroutcomes 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:
R1ns = UserRbase_name = "R1"selectors = []
$P_ACT_Xns = Systembase_name = "$P_ACT_X"selectors = []
$A_IN[1]ns = Systembase_name = "$A_IN"selectors = [{Index, "1"}]
$P_UIFR[1,X,TR]ns = Systembase_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
- missing closing
Examples of desired future messages:
system variable selector requires closing ']'system variable '$A_IN' requires exactly one numeric selectorsystem 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 AilExecutorconsumes 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:
- Variable-backed conditions
- supported path already exists for simple
$...tokens - structured selectors should extend the same
ConditionResolvercontract
- 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_nameandselectors - preserve legacy
namefield for a deprecation window if CLI goldens need schema stability during migration
Implementation Slices
- AST normalization
- add structured variable-reference node for system selectors
- Parser diagnostics
- replace generic bracket/comma syntax failures with selector-aware messages
- AIL JSON migration
- preserve structured selector metadata in expression/condition nodes
- Runtime resolver abstraction
- introduce shared variable-read interface for expressions/conditions
- Policy integration
- add timing/access classification hooks and write restrictions
- 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
- variable-backed condition
- 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
- future parser selector slices for
Design: Rapid Traverse Model (G0, RTLION, RTLIOF)
Task: T-045 (architecture/design)
Goal
Define Siemens-compatible rapid-traverse semantics for:
G0positioning 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
G0representation 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
G0moves - validates syntax and same-block mode declarations
- captures declared rapid mode words and
- AIL:
- emits
rapid_modestate instructions andmotion_linearforG0 - carries both declared and effective rapid mode metadata for
G0
- emits
- 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 (
lineardefault for safety)
State transition:
RTLIONandRTLIOFare modal control words affecting subsequentG0moves 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.
G64path mode behavior) - active tool-radius compensation state requiring path interpolation
- active transformation/compressor mode flagged by runtime context
Precedence order:
- safety/compatibility forced-linear conditions
- declared rapid mode (
RTLION/RTLIOF) - 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)
- Schema and metadata alignment
- ensure
G0carries declared+effective rapid mode in AIL/packet outputs
- Executor effective-mode resolution
- centralize forced-linear logic in policy/context layer
- Group/state integration
- wire Group 10 and Group 7 state into effective-mode resolver inputs
- Diagnostics and explainability
- optional runtime diagnostic/event when declared mode is overridden
Test Matrix (implementation PRs)
- parser tests:
RTLION/RTLIOFsyntax 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:
- construct engine with injected interfaces
- feed text with
pushChunk(...) - drive execution with
pump() - if blocked, wait externally and call
resume(...) - call
cancel()for cooperative stop - 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;valueis available when neededPending: engine must enterBlockedstate and wait forwait_tokenError: 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
- processes available complete lines until one boundary is reached:
finish()- marks end-of-input
- flushes final unterminated line if allowed
- returns
Completedonce all work is done
resume(...)- resumes a previously blocked operation only when token matches current wait
Per-Line Execution Contract
For each complete line:
- parse the line into a line-level syntax/result form
- emit parse diagnostics for that line
- if line has an error diagnostic:
- emit
onRejectedLine(...) - enter
Faultedor stop according to policy
- emit
- lower the line using current modal/runtime context
- emit execution event through
IExecutionSink - call the required
IRuntimeoperation - if runtime returns
Pending, enterBlocked - if runtime returns
Ready, continue - 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:
- line is parsed
- line is lowered into a linear-motion command
sink.onLinearMove(...)is called with normalized parametersruntime.submitLinearMove(...)is called with the same normalized command- if pending, engine returns
Blocked - only after
resume(...)may the next line proceed
Minimum command fields:
source:- filename if known
- physical line number
Nnumber if present
opcode:"G1"or"G0"as applicable
target_posefeed- 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 $...:
- parser preserves the variable reference in syntax/IR
- execution requests value through
IRuntime::readSystemVariable(...) - if ready, evaluation proceeds immediately
- if pending, engine enters
Blocked - 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()becomestruebefore starting a new line, the engine transitions toCancelled - if cancelled while blocked, the engine calls
IRuntime::cancelWait(...)best-effort and then transitions toCancelled - 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 numberevent: stable event name- source data when applicable
- normalized parameters
Recommended event names:
chunk_receivedline_completeddiagnosticrejected_linesink.linear_movesink.arc_movesink.dwellruntime.read_system_variableruntime.read_system_variable.resultruntime.submit_linear_moveruntime.submit_arc_moveruntime.submit_dwellengine.blockedengine.resumedengine.cancelledengine.completedengine.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.cpptest/streaming_cancellation_tests.cpptest/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
sinkevent ordering must remain deterministic under blocking/resume paths- line/chunk semantics must define whether CRLF normalization occurs before or during line assembly
Recommended First Implementation Slice
Smallest coherent slice:
- add the architecture interfaces and state types
- add event-log sink and scripted mock runtime
- implement motion-only
G0/G1/G2/G3/G4line execution - add integration tests with blocking/resume/cancel coverage
- 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_lineandfrom_first_errorare set, explicitfrom_linewins. - If
from_first_error=trueand no prior error exists, boundary is line 1. - Invalid boundary (
0or greater thanline_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 withline < B; replace all diagnostics withline >= Bby 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 fromB.
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_lineis updated to that line- prefix outputs remain valid and reusable
fail_fast=false:- parser continues collecting errors in suffix
first_error_lineremains earliest suffix hard error
Recommended editor loop:
- parse with
fail_fast=true - user fixes
first_error_line - call
reparse(from_line=fixed_line)
File-backed and Text-backed Modes
setText(...): in-memory source of truthsetFile(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)
ParseSessionApiTest:- invalid boundary handling
- explicit boundary precedence over first-error mode
ParseSessionMergeTest:- prefix diagnostic/instruction preservation
- suffix replacement determinism
ParseSessionFailFastTest:- first-error discovery and resume workflow
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,g57g505..g599(machine-dependent subset)
Block-scope suppression controls (non-modal):
G53: suppress settable and programmable work offsetsG153:G53suppression plus basic-frame suppressionSUPA:G153suppression 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
- Group 8 selection is modal and persists across blocks until changed.
G53/G153/SUPAare non-modal; they affect only the current block.- Suppression controls do not mutate stored Group 8 modal selection.
- If multiple suppression controls occur in one block, effective mask uses
strongest suppression level (
SUPA>G153>G53) and emits a warning.
Applicability examples:
G54then motion in next block =>G54remains selected.G54with same-blockG53=> selected state remainsG54, 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.g500org54)supported_group8_offsets(range set; supports 828D-style variants)legacy_aliases(optional mapping such asG58/G59handling 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)
- Group 8 modal registry alignment
- ensure full Group 8 code set and profile-backed validation paths
- Suppression context representation
- add explicit block-level suppression metadata for
G53/G153/SUPA
- AIL/runtime propagation
- expose selected offset + effective suppression metadata per block
- 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: absoluteg91: incremental
Group 13 (modal units):
g70/g71: geometry-only unit switchg700/g710: geometry + technological-length unit switch
Per-value overrides (non-modal value decorators):
AC(...): force absolute for the decorated valueIC(...): force incremental for the decorated value
Rotary absolute targeting (non-modal):
DC(...): shortest-path absolute rotary targetACP(...): positive-direction absolute rotary targetACN(...): 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
- Effective value interpretation starts from modal Group 14.
- If a value has
AC/IC, that local override wins for that value only. DC/ACP/ACNapply only to rotary-target forms and do not modify Group 14.- Diameter/radius forms apply only on profile-eligible axes.
- 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
- Group 13 broad-unit mode (
- 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)
- Modal-state completion
- finalize Group 13/14 representation and transition handling
- Value-override lowering
- normalize
AC/ICand rotary decorators into explicit value metadata
- Diameter/radius policy wiring
- enforce axis eligibility and emit deterministic diagnostics
- 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 (
Ttriggers immediate tool change) - preselect + deferred-change mode (
Tselects,M6executes) - 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
- parses tool-select forms and
- AIL:
- emits normalized
tool_selectandtool_changeinstructions - includes timing (
immediateordeferred_until_m6) metadata
- emits normalized
- 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;M6executes 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;M6commits
State Model
Runtime tool-related state:
active_tool(currently effective tool)pending_tool_selection(optional, for deferred mode)tool_change_mode(directordeferred_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
- Machine profile decides direct-vs-deferred mode.
- Selector grammar validity depends on
tool_management_enabled. - In deferred mode:
T...updatespending_tool_selectionM6consumes pending selection and activates it
- In direct mode:
T...immediately activates resolved tool and clears pending state- standalone
M6is policy-controlled (warn/error/ignore)
- If multiple
T...appear beforeM6, last valid selection wins.
Policy and Resolution Contracts
Tool resolution responsibilities:
- normalize selector (
number/location/name) - resolve substitution candidates where allowed
- produce
resolved | unresolved | ambiguousoutcome
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):M6classification 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)
- Parse/AST normalization
- normalize all supported
T...forms into one selector model
- AIL instruction split
- emit explicit
tool_selectandtool_changeinstructions with timing metadata
- Executor pending-state semantics
- implement direct vs deferred activation behavior
- Policy integration
- add pluggable resolver/substitution path with deterministic diagnostics
- Cross-feature integration
- align
M6runtime 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 fortool_select/tool_change+ JSON/debug outputsT-053: executor pending-state + direct/deferred semanticsT-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
M6without 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.mdarchitecture.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.shgreen 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/.cppmodal_engine.h/.cppmachine_profile.h/.cppruntime_policy.h/.cpp
- existing:
src/ail.h,src/ail.cppsrc/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-037comments ((*...*),;, optional//compatibility mode)T-046M-code syntax + normalization baselineT-045rapid-traverse syntax/state (G0,RTLION,RTLIOF)
Expected code touch points:
grammar/GCode.g4src/gcode_parser.cppsrc/semantic_rules.cppsrc/semantic_normalizer.h/.cpp(new)
Tests:
test/parser_tests.cppadditions- 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-039Group 7T-040working planeT-044Groups 10/11/12T-043Group 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.cppsrc/messages*.cppsrc/packet*.cpp
Tests:
test/modal_engine_tests.cpptest/ail_tests.cpptest/ail_executor_tests.cpp- targeted new suites:
test/work_offset_tests.cpptest/feed_mode_tests.cpptest/transition_mode_tests.cpp
Phase D — Tool/runtime behavior
Target outcomes:
- configurable Siemens-style tool behavior and runtime-executable semantics.
Planned tasks:
T-038tool-change semantics with/without tool managementT-046runtime-executable M-code subset mapping
Expected code touch points:
src/ail.cpp,src/ail.hsrc/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:
T-041feed model architecture (introduce modal/config scaffold)T-042dimensions/units architecture (Group 13/14 integration)T-037comments syntax completionT-046M-code syntax/model baselineT-045rapid traverse modelT-039Group 7 compensation stateT-040plane selection stateT-044exact-stop/continuous-path stateT-043work offsets and suppression contextsT-038tool change + tool management policiesT-047incremental 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:
- one narrow behavior slice
- tests first/with code
- docs updated in same PR (
../../product/spec/index.md,CHANGELOG_AGENT.md, optionally../../product/prd/index.md) - evidence of
./dev/check.shpass
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)
- run parse + ail/lower on
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.shgreen- 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.shgreen - merge only after explicit user approval
Phase Summary
- Foundation
- machine profile scaffolding
- modal registry scaffolding
- Syntax Extensions
- comments, M-code syntax baseline, rapid-traverse syntax
- Modal Integration
- grouped modal behavior rollout
- 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 mergedWU-2 Command Schema Redesign: completed and mergedWU-3 Runtime Dispatch Cleanup: completed and mergedWU-4 Executor State Cleanup: completed and mergedWU-5 Tool Execution Completion: completed and mergedWU-6 Diagnostics/Recovery Alignment: completedWU-7 Final Public API Cleanup: completedWU-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.mdto a short startup map
- move canonical project docs into
- 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
Requirementsinto smaller subtrees - start with
docs/src/requirements/execution/index.md - keep syntax/semantic as later follow-ups
- split
- Slice A:
WU-10 Execution Contract Fixtures Step 1- add human-readable execution contract fixtures for the public
ExecutionSessionAPI - 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, andif_else_branch
- add human-readable execution contract fixtures for the public
WU-11 Execution Contract Fixtures Step 2- extend the same system to asynchronous
blocked/resume/cancelledcases
- extend the same system to asynchronous
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()BlockedvsWaitingForInputkept 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
- richer language-level evaluation built on top of
General action-command rule:
- action submission returns
Ready,Pending, orError Pendingmeans 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
AilExecutoralready 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:
ModalStateExecutorStateEffectiveModalSnapshot- 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
targetobject for motion endpoints - one
effectivesnapshot for modal state - no duplicated emitted
opcode/modalfields 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
M6no-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
StreamingExecutionEnginefocused on internal execution - provide
ExecutionSessionas the public editable rejected-suffix recovery API - use a recoverable
Rejectedstate distinct from unrecoverableFaulted
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
Recommended Work Units
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
ExecutionSessionAPI 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:
- 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.mdis updated./dev/check.shis 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.shpasses.- 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-termP2: important but can waitP3: optional/enhancement
Ready Queue
-
Architecture planning queue
T-037..T-047is completed and moved toDone. -
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>formsM17andRET- ISO-gated
M98 P...compatibility syntax
- (planned extension) parse model for
PROCsignatures 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)
- parser/AST support for:
-
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.cpptest/ail_tests.cpptest/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.. /9metadata capture
- Siemens-compatible program-name forms (including
- define runtime boundary for skip-level execution policy
- add parser support/validation for:
-
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.cpptest/ail_tests.cpptest/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)
- extend variable reference model to support structured system-variable forms
(including selector syntax like
-
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/errorbehavior 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.cpptest/ail_tests.cpptest/ail_executor_tests.cpp
-
ID: T-051 -
Priority: P1 -
Title: Tool selector normalization for SiemensT...forms -
Why:T-038requires 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>
- management-off forms:
- mode-aware diagnostics for invalid selector forms
- parse/AST normalization for:
-
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)
- runtime activation semantics (
-
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-038requires explicittool_selectvstool_changeboundaries and timing metadata (immediatevsdeferred_until_m6). -
Scope:- AIL schema + lowering updates for
tool_selectandtool_change - JSON/debug output updates for new instruction shapes
- AIL schema + lowering updates for
-
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.cpptest/cli_tests.cpp
-
ID: T-053 -
Priority: P1 -
Title: Executor pending-tool state and direct/deferred semantics -
Why:T-038requires runtime behavior for both directTactivation and deferredT+M6execution. -
Scope:- executor state for
active_toolandpending_tool_selection - behavior rules for direct mode, deferred mode, and last-selection-wins
M6without pending selection policy hook- runtime execution-path wiring for
tool_select/tool_changeas executable control instructions (non-motion path; no standalone motion packets)
- executor state for
-
Acceptance Criteria:- executor behavior is deterministic across direct/deferred configurations
- diagnostics/policy behavior is explicit for
M6without 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.cpptest/ail_tests.cpp(if metadata coupling changes)
-
ID: T-054 -
Priority: P1 -
Title: Tool resolver/substitution policy integration -
Why:T-038requires 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.cpptest/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-XXXPriority: P0/P1/P2/P3Title: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; seeCHANGELOG_AGENT.mdslices on 2026-03-03) - T-038 (merged to
main; seeCHANGELOG_AGENT.mdentry 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; seeCHANGELOG_AGENT.mdentry on 2026-03-04) - T-043 (merged to
main; seeCHANGELOG_AGENT.mdentry on 2026-03-04) - T-044 (merged to
main; seeCHANGELOG_AGENT.mdentry on 2026-03-04) - T-045 (merged to
main; seeCHANGELOG_AGENT.mdentry on 2026-03-04) - T-046 (merged to
main; seeCHANGELOG_AGENT.mdentry on 2026-03-04) - T-047 (merged to
main; seeCHANGELOG_AGENT.mdentry 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)