Give every table its path in one pass, name it in every message, read the layout probes through predicates, and leave the shell to the CLI

This commit is contained in:
2026-09-18 20:02:08 +02:00
parent 2d08bf4b32
commit d5d9c2655e
7 changed files with 72 additions and 37 deletions
+24 -13
View File
@@ -472,8 +472,9 @@ func iban(r rng, cc string) string {
const dayLayout = "2006-01-02"
// Alike in no field; layoutDay differs from layoutProbe in its date fields only,
// layoutClock in its clock fields only.
// The instants a layout is proved against: layoutProbe2 is alike in no field, while
// layoutDay differs from layoutProbe in its date fields alone and layoutClock in its
// clock fields alone, so formatting two of them tells which kind a layout names.
var (
layoutProbe = time.Date(2001, 2, 3, 4, 5, 6, 0, time.UTC)
layoutProbe2 = time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC)
@@ -481,6 +482,18 @@ var (
layoutClock = time.Date(2001, 2, 3, 13, 14, 15, 0, time.UTC)
)
func namesAField(layout string) bool {
return layoutProbe.Format(layout) != layoutProbe2.Format(layout)
}
func namesADateField(layout string) bool {
return layoutProbe.Format(layout) != layoutDay.Format(layout)
}
func namesAClockField(layout string) bool {
return layoutProbe.Format(layout) != layoutClock.Format(layout)
}
// quotedLayout reports whether an arg carries the single quotes a layout is written in.
func quotedLayout(a string) bool {
return len(a) >= 2 && a[0] == '\'' && a[len(a)-1] == '\''
@@ -493,10 +506,10 @@ func layoutArg(a string) (string, error) {
if strings.HasPrefix(a, `"`) || strings.HasSuffix(a, `"`) {
return "", fmt.Errorf("layout %s is double-quoted; write '%s'", a, bare)
}
return "", fmt.Errorf("layout %s is not quoted; write '%s', which a shell keeps only inside a double-quoted argument", a, bare)
return "", fmt.Errorf("layout %s is not quoted; write '%s'", a, bare)
}
layout := a[1 : len(a)-1]
if layoutProbe.Format(layout) == layoutProbe2.Format(layout) {
if !namesAField(layout) {
return "", fmt.Errorf("layout '%s' names no field, so it is the constant %q; write it as text", layout, layout)
}
return layout, nil
@@ -518,15 +531,15 @@ func layoutArity(name string, n int, a []string) error {
return nil
}
hint := ""
if len(a) > n && !split(a[n-1:]) {
if len(a) > n && !holdsQuotedLayout(a[n-1:]) {
hint = fmt.Sprintf("; a layout holding a comma is quoted: '%s'", strings.Join(a[n-1:], ", "))
}
return fmt.Errorf("%s takes %d argument%s, got %d%s", name, n, plural(n), len(a), hint)
}
// split reports whether the surplus args already hold a quoted layout, which no
// comma split apart.
func split(a []string) bool {
// holdsQuotedLayout reports whether the surplus args already carry a quoted layout,
// which no comma split apart.
func holdsQuotedLayout(a []string) bool {
for _, arg := range a {
if quotedLayout(arg) {
return true
@@ -535,8 +548,6 @@ func split(a []string) bool {
return false
}
func plural(n int) string { return map[bool]string{true: "s"}[n != 1] }
func dateArgs(_ map[string]node, a []string) error {
if err := layoutArity("date", 3, a); err != nil {
return err
@@ -556,10 +567,10 @@ func dateArgs(_ map[string]node, a []string) error {
if err != nil {
return err
}
if layoutProbe.Format(layout) == layoutDay.Format(layout) {
if !namesADateField(layout) {
return fmt.Errorf("date(from,to,layout): '%s' names no date field; write time('%s')", layout, layout)
}
if from.Equal(to) && layoutProbe.Format(layout) == layoutClock.Format(layout) {
if from.Equal(to) && !namesAClockField(layout) {
return fmt.Errorf("date(%s,%s,'%s') is the constant %q; write it as text", a[0], a[1], layout, from.Format(layout))
}
return nil
@@ -573,7 +584,7 @@ func timeArg(_ map[string]node, a []string) error {
if err != nil {
return err
}
if layoutProbe.Format(layout) != layoutDay.Format(layout) {
if namesADateField(layout) {
return fmt.Errorf("time(layout): '%s' names a date field; write date(from,to,layout)", layout)
}
return nil