feat(sprintf): support full %q format specifications - #810
feat(sprintf): support full %q format specifications#810Vitalii Tverdokhlib (vitaliytv) wants to merge 4 commits into
Conversation
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>
|
Vitalii Tverdokhlib (@vitaliytv) Thank you for the contribution. Going to take a deeper look at this today/tomorrow. |
There was a problem hiding this comment.
🟡 Changes recommended
Format compatibility gaps and unchecked allocation paths can produce incorrect output or substantially exceed configured memory limits.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Completes Go/OPA-compatible %q formatting for sprintf.
Changes:
- Adds format-spec parsing, quoting flags, dynamic/indexed operands, and value conversions.
- Ports Go Unicode printability tables.
- Adds compatibility and memory-limit tests.
File summaries
| File | Description |
|---|---|
src/builtins/strings.rs |
Implements %q formatting and integration. |
src/builtins/strings/sprintf_format.rs |
Parses Go-style format specifications. |
src/builtins/strings/go_is_print.rs |
Provides Go-compatible Unicode classification. |
tests/interpreter/cases/builtins/strings/sprintf.yaml |
Adds interpreter compatibility cases. |
tests/memory_limits.rs |
Tests quoting memory-limit propagation. |
LICENSE |
Attributes Go-derived source. |
Review details
Suppressed comments (1)
src/builtins/strings/sprintf_format.rs:119
- A negative dynamic precision must produce Go's
%!(BADPREC)diagnostic and then format without precision. Withsprintf("%.*q", [-1, "x"]), this silently returns"x"instead of%!(BADPREC)"x", so a valid integer*operand has incompatible OPA behavior. Preserve a bad-precision marker in the parsed spec and emit it before formatting the value.
let precision = take_integer(args, args_idx, args_span)?;
if precision >= 0 {
spec.precision = Some(checked_dynamic_value(precision as u64, args_span)?);
}
- Files reviewed: 6/6 changed files
- Comments generated: 7
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Also addressed the suppressed review note in 3568dac: negative dynamic precision now emits Go/OPA-compatible |
Depends on #809.
This follow-up completes
%qsupport with a dedicatedsprintfformat-spec parser instead of extending the original PR's single-verb parsing incrementally.What this adds
*operands, and explicit argument indexes such as%[3]*.[2]*[1]q.%+q(QuoteToASCIIbehavior),%#q(backquoted strings when valid), left/right alignment, zero padding, and combined width/precision.%qoperands using OPA-compatible value conversion for strings, booleans, null, collections, integral runes, invalid runes, fractional numbers, and out-of-range integers.qverbs. Newly parsed flags that those verbs do not support are rejected explicitly.Compatibility boundary
Regorus currently normalizes numbers into its internal
Numbervariants and does not retain the original Rego numeric token spelling. OPA's%qbehavior can depend on that spelling: for example, OPA distinguishes65.0,65e0, and65, while Regorus may normalize them to different shared numeric variants. Exact parity for those lexical-number edge cases requires a separate change to the number representation; this PR does not disguise or broaden that architectural change.The common
%qmatrix and parser edge cases were compared directly with OPA, including ASCII quoting, raw quoting and fallback, width/precision combinations, dynamic and indexed operands, runes, invalid runes, collections, and diagnostics.Validation
cargo test: 298 passed, 2 ignored.cargo test --locked --no-default-features --lib: 220 passed, 2 ignored.cargo xtask test-no-stdpassed.cargo clippy --locked --all-targets --all-features -- -D warningspassed.This is intentionally stacked on #809. Until #809 is merged, GitHub's diff against
mainincludes that prerequisite commit as well; afterward this PR reduces to the format-spec extension.