Browse Source

fmt.printf("%q", str); (quotes strings)

gingerBill 6 years ago
parent
commit
64f84ef9a3
2 changed files with 99 additions and 13 deletions
  1. 24 0
      core/fmt/fmt.odin
  2. 75 13
      core/strings/builder.odin

+ 24 - 0
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;

+ 75 - 13
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]);
+			}
+		}
+	}
 }