Browse Source
Both user-approved drafts from the G-041 prework probe findings: - G-071: value-allocation correctness for optional standalone values + trailing optional-member clauses (the lseq "0 10 2" in-form failure), error-blame quality, and parse_status -form support. A G-041 prerequisite: no lseq form accepts those arglists under the current allocator, so form auto-selection alone cannot fix them. - G-072: named compound clause types with per-alternate element typing and arity for heterogeneous repeating clauses (try on/trap handlers), building on the design sketches embedded in the ::try moduledoc notes. Includes a recorded examination of Elixir gradual set-theoretic types (user direction): punk::args -type | and literal() are already a fragment of the set algebra; negation would subsume reserved-word deny-lists; per-alternate arity falls out of union-of-shapes. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.commaster
3 changed files with 127 additions and 0 deletions
@ -0,0 +1,54 @@
|
||||
# G-071 punk::args value-allocation correctness for optional elements (lseq-class arglists) + parse_status -form |
||||
|
||||
Status: proposed |
||||
Scope: src/modules/punk/args-999999.0a1.0.tm (get_dict value allocation, private::get_dict_can_assign_value, parse_status), src/tests/modules/punk/args/testsuites/args/ (new allocation characterization suite), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (::lseq as the proving consumer) |
||||
Goal: argument lists that are valid for a single form parse correctly when optional standalone values and optional-member clauses are skipped or filled in any documented combination - specifically the lseq range shape (an optional single-word choice value between required values, followed by a trailing optional-member clause), where today the allocator force-feeds a word to the skippable optional and fails ('lseq 0 10 2', '0 10 by 2', '1 5 by 0' all fail in-form while '0 to 10 2' parses) - and parse failures blame the genuinely failing element rather than an unrelated optional; parse_status gains -form so per-form status probing works. |
||||
Acceptance: a new allocation characterization suite drives the lseq range matrix under explicit -form range - '0 10', '0 10 2', '0 10 by 2', '0 to 10 2', '0 .. 10 by 2', '1 5 by 0' parse per the lseq.n grammar (the three currently-failing cases fixed) and '0 10 2 4' still fails - plus reduced fixtures isolating the shape (optional choice value between required values + trailing optional-member clause) independent of the moduledoc; the ::if noise-word cases (mid-clause ?literal(then)?, clause-leading ?literal(else)?) keep passing - no regression to optional clause members generally; a genuinely invalid arglist's error names the failing element (the '..|to' misblame case pinned fixed); punk::args::parse_status accepts -form (single form name/index at minimum, consistent with parse) with its status structure reporting the form used; full punk::args and punk::ns suites pass with no expectations weakened; before/after results for the probe matrix recorded in this file. |
||||
|
||||
## Context |
||||
|
||||
Found 2026-07-12 during G-041 activation prework (user concern: variable-length |
||||
clauses with optional elements). Real-vs-model probing of the tclcore moduledoc |
||||
showed the per-form models are doc-faithful (lseq.n's three synopsis lines map 1:1 |
||||
to the modelled range/start_count/count forms; if/switch/try likewise verified), |
||||
but the range form fails IN-FORM whenever the ../to noise word is absent and a |
||||
step is present - the allocator insists on assigning the word after start to the |
||||
optional '..|to' choice value (whose choices then reject it) instead of skipping |
||||
the optional and letting the trailing "by step" clause (-type |
||||
{?literalprefix(by)? number|expr}) absorb the remainder. The start_count form |
||||
proves the optional-member clause itself can absorb a lone word ('1 count 5 2' |
||||
parses as {by step} {{} 2}) when preceding words are pinned by a literal - the |
||||
failure is the combination with a skippable standalone optional value. |
||||
|
||||
No other lseq form accepts '0 10 2', so G-041's form auto-selection cannot fix |
||||
'i lseq 0 10 2' without this - making this goal a G-041 prerequisite. Probe |
||||
results are recorded in goals/G-041-punkargs-form-matching.md Notes (2026-07-12). |
||||
|
||||
parse_status -form: per-form probing during the same session had to fall back to |
||||
punk::args::parse + catch because parse_status does not accept -form. The |
||||
form-candidacy API sketched in G-041 (and the G-044 partial-arglist consumer) |
||||
wants exactly this surface, so it lands here where the characterization suite |
||||
needs it first. |
||||
|
||||
## Approach |
||||
|
||||
- Characterize first: reduced fixtures pinning current allocation behaviour for |
||||
the failing shape (and the working ::if shapes as regression guards), then fix |
||||
the allocator - likely in the lookahead/backtrack decision where |
||||
get_dict_can_assign_value rejects an optional's candidate word: rejection of an |
||||
optional element should yield the word to subsequent elements/clauses rather |
||||
than failing the parse when a consistent allocation exists. |
||||
- Blame quality: when no consistent allocation exists, prefer reporting the |
||||
element whose constraint actually failed over the first optional tried. |
||||
- The G-041 detail file notes the form-attempt logic will exercise these paths |
||||
heavily; landing this first keeps that goal's failures meaningful. |
||||
|
||||
## Notes |
||||
|
||||
- Related: G-041 (this is its prerequisite; probe evidence in its Notes), G-053 |
||||
(occurrence-arity work touches adjacent allocation machinery), G-055 (lseq |
||||
parity pins; TIP 746 note there makes lseq operand types version-conditional - |
||||
this goal's matrix should derive expectations from the live interpreter). |
||||
- Incidental fixes already landed during the probing session: unconditional debug |
||||
puts on the clause type-check path (punk::args 0.8.2), and an earlier companion |
||||
(0.2.3, noted in G-041). |
||||
@ -0,0 +1,65 @@
|
||||
# G-072 punk::args compound clause types: named alternates with per-element typing and per-alternate arity (try-class handlers) |
||||
|
||||
Status: proposed |
||||
Scope: src/modules/punk/args-999999.0a1.0.tm (type-expression parsing, clause allocation, synopsis/help renderers), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (::try as proving consumer; ::if/::switch as touched), src/tests/modules/punk/args/testsuites/args/ |
||||
Goal: heterogeneous repeating clauses - try's on/trap handlers are the motivating case - can be modelled with per-alternate element types, choices and arity: 'try {} on bogus {e} {s}' is rejected with the code choice set displayed, alternates may differ in element count, mixed-order interleaving keeps parsing, and synopsis/help render each alternate's shape distinctly instead of a conflated element - with the definition mechanism (named compound types, composition operators, cross-package scoping) decided and recorded, informed among others by set-theoretic type composition. |
||||
Acceptance: a definition mechanism (decided in the work, starting from the sketches embedded in the ::try moduledoc notes: named compound types with arity, @typeimport, composition operators) lets the ::try handler be modelled so that parse rejects 'on bogus ...' displaying the on-code choices (ok/error/return/break/continue or integer) and types the trap pattern as a list, while mixed-order on/trap interleaving and finally positioning keep parsing (the 2026-07-12 probe matrix stays green); alternates of differing arity are expressible and exercised by at least one fixture (per-alternate arity derived or declared - decision recorded); synopsis and help output render each alternate's element shape distinctly (the deliberately-unpiped oncode_or_trappattern -typesynopsis workaround retired); type-name scoping across documentation packages has a recorded decision (namespacing/@typeimport mechanism, or rejection with rationale); the ::try moduledoc adopts the mechanism with real-vs-model parity probes added (::if/::switch adopt it only where it improves their models); definitions not using compound types behave unchanged (full punk::args suite passes, none weakened). |
||||
|
||||
## Context |
||||
|
||||
The ::try moduledoc models handlers as a single 4-element clause |
||||
{literal(on)|literal(trap) string list string} -multiple 1. Probing (2026-07-12, |
||||
G-041 prework) confirmed this parses everything structural correctly - mixed |
||||
on/trap order, finally positioning - but over-accepts semantically: the second |
||||
element cannot be typed per-alternate, so 'on bogus {e} {s}' passes where Tcl |
||||
errors with the valid completion codes. The moduledoc carries extensive embedded |
||||
design notes at ::try recording the difficulty and sketching mechanisms (named |
||||
compound types with arity indicators such as <on_handler:4>|<other_handler:3>, |
||||
per-definition vs global type registries, @typeimport for cross-package reuse, |
||||
RPN composition like {stringstartswith a stringendswith z AND int OR}). |
||||
Related over-acceptance findings from the same probes are listed in |
||||
goals/G-055-tclcore-regen-workflow.md Notes (reserved-word clause allocation, |
||||
'-' fallthrough position constraints) - candidates to fall out of the same |
||||
mechanism or be recorded as out of scope here. |
||||
|
||||
## Set-theoretic types input (user direction 2026-07-12) |
||||
|
||||
Investigate whether gradual set-theoretic types as adopted by Elixir |
||||
(https://elixir.hexdocs.pm/main/gradual-set-theoretic-types.html) can inform the |
||||
mechanism. Brief examination at drafting time: |
||||
|
||||
- Elixir treats types as SETS of values composed with union (or), intersection |
||||
(and) and negation (not); literal values are singleton types; dynamic() effects |
||||
gradual typing; multi-clause functions are intersections of arrow types. |
||||
- Mapping to punk::args: -type's existing | is already set union and |
||||
literal(word) is already a singleton type - the existing vocabulary is a |
||||
fragment of a set-theoretic algebra, so extension is consistent rather than a |
||||
redesign. punk::args' 'any' plays the dynamic() role. |
||||
- Negation would directly express the reserved-word gap recorded in G-055's |
||||
notes: an if else-script is "script and not literal(elseif)" - i.e. deny-lists |
||||
for clause members become type negation instead of a bespoke feature. |
||||
- Intersection covers the composite constraints the ::try notes sketched as RPN: |
||||
{stringstartswith a stringendswith z AND int OR} reads as |
||||
((startswith(a) and endswith(z)) or int) - infix/functional composition on set |
||||
semantics can replace the RPN idea. |
||||
- Named compound types then become type ALIASES over such compositions, and a |
||||
heterogeneous clause is a UNION of tuple-like clause shapes whose arity derives |
||||
from each shape's length - answering the notes' open question about declaring |
||||
arity for alternates of differing length (arity is per-alternate and computed, |
||||
not separately declared). |
||||
- A full algebra is likely overkill for the immediate try case; the design should |
||||
pick operator spellings consistent with set-theoretic composition so later |
||||
extension (negation for reserved words, intersection for composite string |
||||
constraints) slots in without breaking earlier definitions. |
||||
|
||||
## Notes |
||||
|
||||
- Related: G-041 (form selection and clause allocation interact; its candidacy |
||||
machinery should not need changes from this goal's types), G-071 (allocation |
||||
correctness for optional elements - sequence that first: compound alternates |
||||
ride on the same allocator), G-055 (modelability findings list; its parity |
||||
workflow verifies whatever this goal makes expressible), G-053 (occurrence |
||||
arity - adjacent clause machinery). |
||||
- Display cost matters: the ::try notes warn bracketed alternate forms "get |
||||
unwieldy in synopsis listings" - synopsis rendering of compound types is part |
||||
of the mechanism's acceptance, not an afterthought. |
||||
Loading…
Reference in new issue