From 64f84ef9a32b4e24374adc91abffa0104778be1e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 6 Jan 2019 22:11:45 +0000 Subject: [PATCH] fmt.printf("%q", str); (quotes strings) --- core/fmt/fmt.odin | 24 +++++++++++ core/strings/builder.odin | 88 +++++++++++++++++++++++++++++++++------ 2 files changed, 99 insertions(+), 13 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 86772a281..453110fa2 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -593,11 +593,35 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { fmt_bad_verb(fi, verb); } } + + fmt_string :: proc(fi: ^Info, s: string, verb: rune) { switch verb { case 's', 'v': strings.write_string(fi.buf, s); + case 'q': // quoted string + quote: byte = '"'; + strings.write_byte(fi.buf, quote); + for width := 0; len(s) > 0; s = s[width:] { + r := rune(s[0]); + width = 1; + if r >= utf8.RUNE_SELF { + r, width = utf8.decode_rune_in_string(s); + } + if width == 1 && r == utf8.RUNE_ERROR { + strings.write_byte(fi.buf, '\\'); + strings.write_byte(fi.buf, 'x'); + strings.write_byte(fi.buf, __DIGITS_LOWER[s[0]>>4]); + strings.write_byte(fi.buf, __DIGITS_LOWER[s[0]&0xf]); + continue; + } + + strings.write_escaped_rune(fi.buf, r, quote); + + } + strings.write_byte(fi.buf, quote); + case 'x', 'X': space := fi.space; fi.space = false; diff --git a/core/strings/builder.odin b/core/strings/builder.odin index c79352719..389030ba4 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -62,25 +62,25 @@ write_bytes :: proc(b: ^Builder, x: []byte) { } -write_encoded_rune :: proc(b: ^Builder, r: rune) { - write_byte(b, '\''); +write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) { + if write_quote do write_byte(b, '\''); switch r { - case '\a': write_string(b, "\\a"); - case '\b': write_string(b, "\\b"); - case '\e': write_string(b, "\\e"); - case '\f': write_string(b, "\\f"); - case '\n': write_string(b, "\\n"); - case '\r': write_string(b, "\\r"); - case '\t': write_string(b, "\\t"); - case '\v': write_string(b, "\\v"); + case '\a': write_string(b, `\a"`); + case '\b': write_string(b, `\b"`); + case '\e': write_string(b, `\e"`); + case '\f': write_string(b, `\f"`); + case '\n': write_string(b, `\n"`); + case '\r': write_string(b, `\r"`); + case '\t': write_string(b, `\t"`); + case '\v': write_string(b, `\v"`); case: if r < 32 { - write_string(b, "\\x"); + write_string(b, `\x`); buf: [2]byte; s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil); switch len(s) { case 0: write_string(b, "00"); - case 1: write_rune(b, '0'); + case 1: write_byte(b, '0'); case 2: write_string(b, s); } } else { @@ -88,7 +88,69 @@ write_encoded_rune :: proc(b: ^Builder, r: rune) { } } - write_byte(b, '\''); + if write_quote do write_byte(b, '\''); +} + + +write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte) { + static DIGITS_LOWER := "0123456789abcdefx"; + + is_printable :: proc(r: rune) -> bool { + if r <= 0xff { + switch r { + case 0x20..0x7e: + return true; + case 0xa1..0xff: // ¡ through ÿ except for the soft hyphen + return r != 0xad; // + } + } + + // TODO(bill): A proper unicode library will be needed! + return false; + } + + if r == rune(quote) || r == '\\' { + write_byte(b, '\\'); + write_byte(b, byte(r)); + return; + } else if is_printable(r) { + write_encoded_rune(b, r, false); + return; + } + switch r { + case '\a': write_string(b, `\a`); + case '\b': write_string(b, `\b`); + case '\e': write_string(b, `\e`); + case '\f': write_string(b, `\f`); + case '\n': write_string(b, `\n`); + case '\r': write_string(b, `\r`); + case '\t': write_string(b, `\t`); + case '\v': write_string(b, `\v`); + case: + switch { + case r < ' ': + write_byte(b, '\\'); + write_byte(b, 'x'); + write_byte(b, DIGITS_LOWER[byte(r)>>4]); + write_byte(b, DIGITS_LOWER[byte(r)&0xf]); + + case r > utf8.MAX_RUNE: + r = 0xfffd; + fallthrough; + case r < 0x10000: + write_byte(b, '\\'); + write_byte(b, 'u'); + for s := 12; s >= 0; s -= 4 { + write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]); + } + case: + write_byte(b, '\\'); + write_byte(b, 'U'); + for s := 28; s >= 0; s -= 4 { + write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]); + } + } + } }