Skip to content

fix(getter): Tcl braced values score by which command takes them #1382

Description

@dekobon

Summary

A Tcl / iRules braced value scores one operand when the grammar
models the command that takes it, and one per word inside when only
the command's name would identify it. set x {a b} and
lappend x {a b} are the same value in the same language and score
differently.

#1318 fixed the operator half of this — lappend x {a b} no longer
reports a {} block that is not there — and deliberately stopped
short of the operand half. This issue is the remainder.

Evidence

At the pinned grammars, after #1318:

$ printf 'set x {a b}\nlappend y {a b}\n' > two.tcl
$ bca ops --no-config -p two.tcl
   |- operators
   |  `- set
   `- operands
      |- x
      |- {a b}        <- `set` takes a `braced_word_simple`: one operand
      |- lappend
      |- y
      |- a            <- `lappend` takes a `braced_word`: one per word
      `- b

n2 = 6 where the two lines describe two identical values.

Why #1318 did not fix it

The two roles share one kind (braced_word), so the only signal is the
enclosing command's name, and that signal is absent for every command
outside a small core list — including every user proc. Suppressing a
value word's contents on that guess was built and measured, then
rejected: a braced argument to an unrecognised command is as often real
code as a list. On this file —

oo::class create Counter {
    variable n
    constructor {} { set n 0 }
    method bump {} { incr n }
}
test counter-1.1 {bump increments} -body {
    set c [Counter new]
    $c bump
} -result 1
set f [apply {{x} { expr {$x + 1} }} 2]
n1 N1 n2 N2
contents kept (shipped) 5 8 25 32
contents suppressed 2 2 15 15

The class body, the test body and the lambda each collapsed into a
single operand, taking halstead.effort — a gated threshold metric —
down with them. So
#1318's rule only ever withdraws a claim the classifier cannot support
(the block), and never discards code the walk has already read.

The reasoning is recorded on Getter::braced_word_op_type in
src/getter.rs.

What would actually close this

Nothing available from the grammar alone. Options worth weighing:

  1. Leave it, and document it as the contract. A braced argument to
    an unrecognised command is treated as code because it as often is;
    the set / lappend asymmetry is then the grammar's, not the
    classifier's. Cheapest, and arguably correct.
  2. Widen the braced_word_simple treatment to a command allowlist.
    The mirror of fix(getter): Tcl/iRules braced literals still fabricate a block outside special-cased commands #1318's list: lappend, puts, list, lset,
    string map, regsub, array set, … A value word passed to one of
    those is one operand. Bounded and safe for the listed commands,
    incomplete by construction for everything else — and it would make
    three behaviours where there are now two.
  3. Read the contents. A braced word holding no variable_substitution,
    no command_substitution and no nested block is more list-like than
    script-like. A heuristic on top of a heuristic; would need measuring
    against a real Tcl corpus before anyone believed it.

Whichever way it lands, .claude/rules/testing.md applies: assert n1
and N1 and the operand vocabulary, and mirror every row across
both dialects.

Prerequisite

There is no Tcl or iRules source in the integration corpora
(fd -e tcl -e irule tests/repositories returns nothing), so no
snapshot would move for any of these options and the whole question
rests on unit fixtures. Adding a real Tcl corpus is worth doing before
option 2 or 3, and would also give #1318 the coverage it currently
lacks.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions