openlang

invalid command name "Dana" from a plain string in a TSV, what?

Okay, this one has me chasing my tail. Tcl 9.0 on Arch, tclsh main.tcl, small order report for a client's hardware-supply site.

I had one rule in config.tcl (total >= 200) and everything was happy. Then I added a second rule so orders with a customer note get flagged too:

set ::flag_rules {
    {total >= 200}
    {note ne {}}
}

Now the run dies partway through with invalid command name "Dana". The trace points at the expr line in rule_match:

proc rule_match {row rule} {
    lassign $rule field op value
    set have [dict get $row $field]
    return [expr "\"$have\" $op \"$value\""]
}

"Dana" isn't a command anywhere in my code. It's in the data. One of the bigger orders has this note in orders.tsv:

1003	Petrakis Plumbing	1250.75	call [Dana] before shipping

Other notes (like "leave at side door") go through fine. I printed the row dict right before the crash and it looks totally normal. Tabs split right, all four fields in place, note intact. So I've been poking at load_orders for an hour, convinced the client's export has some weird encoding thing or a stray byte. Checked in Neovim with :set list, tabs look like tabs. (Side note: why does their export have a trailing tab on the empty-note rows but not on 1005? Different problem, different day.)

What I can't see is how a string sitting in a dict becomes a command call. Nothing evals it. Why only this row?

The conversation continues 1

Follow the reasoning
  1. Permalink

    Following up on my own thread, because I found it, and it was me the whole time.

    It isn't the data and it isn't the TSV. It's the expr "..." line. Because I build the expression in double quotes, Tcl substitutes $have first. Then expr parses the result, and [Dana] inside a quoted string in an expression gets command-substituted. So the note text was evaluated as code. "leave at side door" just has nothing in it to substitute. (The total rule never blew up because 1250.75 is only a number.)

    The fix is to stop gluing strings together and let the operator be a command:

    proc rule_match {row rule} {
        lassign $rule field op value
        return [::tcl::mathop::$op [dict get $row $field] $value]
    }

    ne and >= both exist there. Docs: https://www.tcl-lang.org/man/tcl9.0/TclCmd/mathop.htm

    Also, the expr page says to brace your expressions for a reason. I read that page years ago, apparently without absorbing it. Funny how an hour in Neovim hunting for a stray byte beats thirty seconds of reading the manual.

Other sides of the same question

  1. Same Int, two targets, two answers

    Same source, same file, two targets. One participant disappears on JS. No error, no warning. I am not happy about that. gleam…

    Replies 0Latest activity gleam
  2. pure host function gets cached, write to its argument just vanishes

    Been telling everyone all week about the memo cache in my toy evaluator's registry (D, weekend project). Rule: if a host func…

    Replies 2Latest activity d
  3. same parseServices, bad line only breaks at build time?

    Changed auto to enum for the built-in table since it never changes, right? Now smtp 25 (no proto) breaks dub build, error end…

    Replies 2Latest activity d