Skip to content

feat(sprintf): support Go-compatible %q for strings - #809

Open
Vitalii Tverdokhlib (vitaliytv) wants to merge 2 commits into
microsoft:mainfrom
vitaliytv:feat/sprintf-q-verb
Open

feat(sprintf): support Go-compatible %q for strings#809
Vitalii Tverdokhlib (vitaliytv) wants to merge 2 commits into
microsoft:mainfrom
vitaliytv:feat/sprintf-q-verb

Conversation

@vitaliytv

@vitaliytv Vitalii Tverdokhlib (vitaliytv) commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Add support for Go-syntax %q formatting for Regorus string values. The implementation now matches Go 1.27.1 / OPA 1.20.2 for the supported string forms:

  • emits a double-quoted Go string literal;
  • uses Go's short escapes and \\xNN / \\uNNNN / \\UNNNNNNNN escapes;
  • preserves printable non-ASCII characters and does not HTML-escape <, >, or &;
  • applies the width and precision forms already recognized by the current Regorus sprintf parser (%Nq, %0Nq, and %.Nq);
  • appends directly to the sprintf output buffer and propagates allocator memory-limit errors.

Correctness changes

The initial implementation approximated Go's strconv.IsPrint with Rust character predicates. That differs for Unicode format characters, private-use characters, noncharacters, and unassigned code points. This revision ports the compact generated tables and lookup algorithm from Go 1.27.1's strconv/isprint.go (Unicode 17.0.0), so values such as U+00AD, U+200B, U+E000, U+FDD0, and U+0378 are escaped exactly like Go.

Go's %q applies string precision before quoting and field width after quoting, both measured in Unicode code points. The supported Regorus forms now do the same, including Unicode input and zero padding.

Verification

Compared directly with OPA 1.20.2 (built with Go 1.27.1) for regular strings, every escape class, printable Unicode, the Unicode edge cases above, width, zero padding, precision, and emoji width/precision.

Local checks:

  • cargo test --locked
  • cargo test --locked --no-default-features --lib
  • cargo check --locked --no-default-features
  • cargo xtask test-no-std
  • allocator memory-limit regression test for %q
  • cargo clippy --locked --lib --all-features -- -D warnings
  • repository pre-commit hook (all build, fmt, and clippy stages)
  • repository pre-push hook

Scope and full Go %q

This PR intentionally handles Value::String only and preserves the repository's existing partial sprintf parser. A complete implementation of Go's %q would also require:

  • combined width and precision (%8.3q);
  • + for ASCII-only quoting, # for backquoted strings when allowed, and - for right padding;
  • dynamic width/precision and argument indexes (*, [n]);
  • integer/rune formatting as a single-quoted character literal.

OPA adds another compatibility layer before calling fmt.Sprintf: strings remain Go strings, fitting integers become Go integers, while booleans, nulls, arrays, objects, and sets are converted to their OPA string representation. Reproducing all of that is broader than a quoting helper and should be implemented with a proper format-spec parser and explicit OPA value-conversion rules rather than incrementally extending this string-only branch.

Go's `%q` verb (strconv.Quote) is used by OPA's sprintf, but regorus
currently bails on it unconditionally. Add support for `Value::String`
arguments only: wrap in double quotes, escape `"` and `\`, use the
short escapes for the common control characters (\a \b \f \n \r \t \v),
fall back to \xNN/\uNNNN/\UNNNNNNNN for other non-printable characters,
and leave printable (including non-ASCII) characters untouched. Unlike
json.marshal, %q does not HTML-escape < > &.

%q on non-string values (numbers, bools, etc.) still bails as before -
Go's %q on those produces different, single-quoted output that is out
of scope for this change.

Verified byte-for-byte against real OPA output (strconv.Quote
semantics) for quotes, backslashes, control characters, DEL, non-ASCII
printable text, and the < > & non-escaping case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vitaliytv Vitalii Tverdokhlib (vitaliytv) changed the title feat(sprintf): support %q format verb for strings feat(sprintf): support Go-compatible %q for strings Sep 7, 2026
@vitaliytv

Copy link
Copy Markdown
Contributor Author

Follow-up for the complete format-spec parser and broader Go/OPA %q semantics: #810. It is intentionally stacked on this PR.

@vitaliytv

Copy link
Copy Markdown
Contributor Author

Would you prefer this change to be reviewed in two stages, or as one larger PR?

  1. Keep feat(sprintf): support Go-compatible %q for strings #809 focused on correct Go-compatible string quoting (including width and precision), and review feat(sprintf): support full %q format specifications #810 separately for the complete format-spec parser, flags, dynamic/indexed operands, and broader OPA value semantics. This is my preference because it keeps the core fix smaller and makes the additional compatibility scope explicit.
  2. Fold the feat(sprintf): support full %q format specifications #810 commit into feat(sprintf): support Go-compatible %q for strings #809 and close the follow-up, if a single complete implementation would be easier to review.

#810 is currently stacked on this PR, so its diff against main temporarily includes #809. I am happy to reorganize the commits according to whichever approach you prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant