From 6dbc34b34b4d6bb9040df0c1e2bc399e4339b2cb Mon Sep 17 00:00:00 2001 From: CharlesKWON Date: Thu, 30 Apr 2026 15:05:50 +0900 Subject: [PATCH] fix(pp): per-element blockify for list captures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `<{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) --- compiler/pp/command.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/compiler/pp/command.go b/compiler/pp/command.go index 0f30120..3199e17 100644 --- a/compiler/pp/command.go +++ b/compiler/pp/command.go @@ -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 (``) 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+" }") } // — bare substitution (must be LAST, after all wrappers). result = strings.ReplaceAll(result, "<"+name+">", val)