fix(pp): per-element blockify for list captures

`<{name}>` previously wrapped a list-typed capture's whole
comma-joined string in one code block: `{|| id , name }`. Harbour's
std.ch expects per-element wrapping so `{ <{v}> }` against
`LIST id, name` yields `{ {|| id }, {|| name } }` — an array of
column blocks the call site can evaluate per row.

applyResult now consults the marker table for blockify the same way
it already does for smart-stringify, splits the captured list on
top-level commas, and emits one `{|| expr }` per element.

Prereq for the upcoming LIST / DISPLAY rules; no user-visible
behavior change for the rules already in std.ch (their `<{for}>` /
`<{while}>` markers are scalar).

Gates green:
  go test ./...      : PASS
  FiveSql2 SQL:1999  : 43/43
  Harbour compat     : 56/56

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-30 15:05:50 +09:00
parent 989138d12e
commit 6dbc34b34b

View File

@@ -680,13 +680,26 @@ func (r *Rule) applyResult(captures map[string]string) string {
result = strings.ReplaceAll(result, "<."+name+".>", ".F.")
}
// <{name}> — blockify: wrap captured expression in {|| ... }.
// Empty capture → NIL so the call site sees a nil block, matching
// how Harbour's std.ch expects __dbLocate / dbEval to interpret a
// missing FOR/WHILE clause.
if val != "" {
result = strings.ReplaceAll(result, "<{"+name+"}>", "{|| "+val+" }")
} else {
// For list-typed markers (`<name,...>`) wrap *each* element so
// `{ <{v}> }` against `LIST id, name` expands to
// `{ {|| id }, {|| name } }`, matching Harbour's std.ch
// idiom for column blocks. Empty capture → NIL so the call
// site sees a nil block (missing FOR/WHILE clause).
if val == "" {
result = strings.ReplaceAll(result, "<{"+name+"}>", "NIL")
} else if isList[name] {
parts := splitTopLevelCommas(val)
out := make([]string, 0, len(parts))
for _, p := range parts {
t := strings.TrimSpace(p)
if t == "" {
continue
}
out = append(out, "{|| "+t+" }")
}
result = strings.ReplaceAll(result, "<{"+name+"}>", strings.Join(out, ", "))
} else {
result = strings.ReplaceAll(result, "<{"+name+"}>", "{|| "+val+" }")
}
// <name> — bare substitution (must be LAST, after all wrappers).
result = strings.ReplaceAll(result, "<"+name+">", val)