From 24a84671ace373ae331fa83a1ff484990f4dff0e Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Sat, 12 Sep 2026 12:58:14 +0200 Subject: krino: planning — chains, placeholders, conflicts, JSON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/plan/index.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 internal/plan/index.go (limited to 'internal/plan/index.go') diff --git a/internal/plan/index.go b/internal/plan/index.go new file mode 100644 index 0000000..abe073f --- /dev/null +++ b/internal/plan/index.go @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package plan + +import ( + "fmt" + "strconv" + "strings" +) + +// MaxIndex returns the highest {N} used in s, 0 when none. It reports the +// same errors Expand does for a malformed placeholder: an unclosed +// placeholder, or {0} (capture groups are numbered from 1). It shares +// Expand's scanner shape: "{{" and "}}" each emit one literal brace, and any +// other "{" opens a placeholder that runs to the next "}". +func MaxIndex(s string) (int, error) { + max := 0 + for i := 0; i < len(s); { + switch { + case s[i] == '{' && i+1 < len(s) && s[i+1] == '{': + i += 2 + case s[i] == '}' && i+1 < len(s) && s[i+1] == '}': + i += 2 + case s[i] == '{': + end := strings.IndexByte(s[i+1:], '}') + if end < 0 { + return 0, fmt.Errorf("unclosed placeholder") + } + body := s[i+1 : i+1+end] + if n, err := strconv.Atoi(body); err == nil { + if n == 0 { + return 0, fmt.Errorf("capture groups are numbered from 1") + } + if n >= 1 && n <= 9 && n > max { + max = n + } + } + i += end + 2 + default: + i++ + } + } + return max, nil +} -- cgit v1.3