Parcourir la source

Merge pull request #3749 from Feoramund/fix-w

Improve support for `%w`
Jeroen van Rijn il y a 1 an
Parent
commit
f8f6f2dada
3 fichiers modifiés avec 131 ajouts et 6 suppressions
  1. 1 0
      core/fmt/doc.odin
  2. 16 6
      core/fmt/fmt.odin
  3. 114 0
      tests/core/fmt/test_core_fmt.odin

+ 1 - 0
core/fmt/doc.odin

@@ -9,6 +9,7 @@ The verbs:
 General:
 	%v     the value in a default format
 	%#v    an expanded format of %v with newlines and indentation
+	%w     an Odin-syntax representation of the value
 	%T     an Odin-syntax representation of the type of the value
 	%%     a literal percent sign; consumes no value
 	{{     a literal open brace; consumes no value

+ 16 - 6
core/fmt/fmt.odin

@@ -1726,10 +1726,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
 
 		et := runtime.type_info_base(info.elem)
 
-		if name != "" {
-			io.write_string(fi.writer, name, &fi.n)
-		} else {
-			reflect.write_type(fi.writer, type_info, &fi.n)
+		if verb != 'w' {
+			if name != "" {
+				io.write_string(fi.writer, name, &fi.n)
+			} else {
+				reflect.write_type(fi.writer, type_info, &fi.n)
+			}
 		}
 		io.write_byte(fi.writer, '{', &fi.n)
 		defer io.write_byte(fi.writer, '}', &fi.n)
@@ -1746,9 +1748,17 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
 			}
 
 			if is_enum {
+				enum_name: string
+			 	if ti_named, is_named := info.elem.variant.(runtime.Type_Info_Named); is_named {
+					enum_name = ti_named.name
+				}
 				for ev, evi in e.values {
 					v := u64(ev)
 					if v == u64(i) {
+						if verb == 'w' {
+							io.write_string(fi.writer, enum_name, &fi.n)
+							io.write_byte(fi.writer, '.', &fi.n)
+						}
 						io.write_string(fi.writer, e.names[evi], &fi.n)
 						commas += 1
 						continue loop
@@ -2391,7 +2401,6 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
 			     runtime.Type_Info_Dynamic_Array,
 			     runtime.Type_Info_Slice,
 			     runtime.Type_Info_Struct,
-			     runtime.Type_Info_Union,
 			     runtime.Type_Info_Enum,
 			     runtime.Type_Info_Map,
 			     runtime.Type_Info_Bit_Set,
@@ -2498,8 +2507,9 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
 		}
 	} else {
 		// Printed in Row-Major layout to match text layout
+		row_separator := ", " if verb == 'w' else "; "
 		for row in 0..<info.row_count {
-			if row > 0 { io.write_string(fi.writer, "; ", &fi.n) }
+			if row > 0 { io.write_string(fi.writer, row_separator, &fi.n) }
 			for col in 0..<info.column_count {
 				if col > 0 { io.write_string(fi.writer, ", ", &fi.n) }
 

+ 114 - 0
tests/core/fmt/test_core_fmt.odin

@@ -258,6 +258,120 @@ test_pointers :: proc(t: ^testing.T) {
 	check(t, "0xFFFF", "%#4p", d)
 }
 
+@(test)
+test_odin_value_export :: proc(t: ^testing.T) {
+	E :: enum u32 {
+		A, B, C,
+	}
+
+	F :: enum i16 {
+		A, B, F,
+	}
+
+	S :: struct {
+		j, k: int,
+	}
+
+	ST :: struct {
+		x: int    `fmt:"-"`,
+		y: u8     `fmt:"r,0"`,
+		z: string `fmt:"s,0"`,
+	}
+
+	U :: union {
+		i8,
+		i16,
+	}
+
+	UEF :: union { E, F }
+
+	A :: [2]int
+
+	BSE :: distinct bit_set[E]
+
+	i    : int                          = 64
+	f    : f64                          = 3.14
+	c    : complex128                   = 7+3i
+	q    : quaternion256                = 1+2i+3j+4k
+	mat  :               matrix[2,3]f32 = {1.5, 2, 1, 0.777, 0.333, 0.8}
+	matc : #column_major matrix[2,3]f32 = {1.5, 2, 1, 0.777, 0.333, 0.8}
+	e    : enum {A, B, C}               = .B
+	en   : E                            = E.C
+	ena  : [2]E                         = {E.A, E.C}
+	s    : struct { j: int, k: int }    = { j = 16, k = 8 }
+	sn   : S                            = S{ j = 24, k = 12 }
+	st   : ST                           = { 32768, 57, "Hellope" }
+	str  : string                       = "Hellope"
+	strc : cstring                      = "Hellope"
+	bsu  : bit_set[0..<32; u32]         = {0, 1}
+	bs   : bit_set[4..<16]              = {5, 7}
+	bse  : bit_set[E]                   = { .B, .A }
+	bsE  : BSE                          = { .A, .C }
+	arr  : [3]int                       = {1, 2, 3}
+	ars  : [3]S                         = {S{j = 3, k = 2}, S{j = 2, k = 1}, S{j = 1, k = 0}}
+	darr : [dynamic]u8                  = { 128, 64, 32 }
+	dars : [dynamic]S                   = {S{j = 1, k = 2}, S{j = 3, k = 4}}
+	na   : A                            = {7, 5}
+	may0 : Maybe(int)
+	may1 : Maybe(int)                   = 1
+	uz   : union {i8, i16}              = i8(-33)
+	u0   : U                            = U(nil)
+	u1   : U                            = i16(42)
+	uef0 : UEF                          = E.A
+	uefa : [3]UEF                       = { E.A, F.A, F.F }
+	map_ : map[string]u8                = {"foo" = 8, "bar" = 4}
+
+	bf   : bit_field int {
+		a: int | 4,
+		b: int | 4,
+		e: E   | 4,
+	} = {a = 1, b = 2, e = .A}
+
+	defer {
+		delete(darr)
+		delete(dars)
+		delete(map_)
+	}
+
+	check(t, "64",                                                  "%w", i)
+	check(t, "3.14",                                                "%w", f)
+	check(t, "7+3i",                                                "%w", c)
+	check(t, "1+2i+3j+4k",                                          "%w", q)
+	check(t, "{1.5, 2, 1, 0.777, 0.333, 0.8}",                      "%w", mat)
+	check(t, "{1.5, 2, 1, 0.777, 0.333, 0.8}",                      "%w", matc)
+	check(t, ".B",                                                  "%w", e)
+	check(t, "E.C",                                                 "%w", en)
+	check(t, "{E.A, E.C}",                                          "%w", ena)
+	check(t, "{j = 16, k = 8}",                                     "%w", s)
+	check(t, "S{j = 24, k = 12}",                                   "%w", sn)
+	check(t, `ST{y = 57, z = "Hellope"}`,                           "%w", st)
+	check(t, `"Hellope"`,                                           "%w", str)
+	check(t, `"Hellope"`,                                           "%w", strc)
+	check(t, "{0, 1}",                                              "%w", bsu)
+	check(t, "{5, 7}",                                              "%w", bs)
+	check(t, "{E.A, E.B}",                                          "%w", bse)
+	check(t, "{E.A, E.C}",                                          "%w", bsE)
+	check(t, "{1, 2, 3}",                                           "%w", arr)
+	check(t, "{S{j = 3, k = 2}, S{j = 2, k = 1}, S{j = 1, k = 0}}", "%w", ars)
+	check(t, "{128, 64, 32}",                                       "%w", darr)
+	check(t, "{S{j = 1, k = 2}, S{j = 3, k = 4}}",                  "%w", dars)
+	check(t, "{7, 5}",                                              "%w", na)
+	check(t, "nil",                                                 "%w", may0)
+	check(t, "1",                                                   "%w", may1)
+	check(t, "-33",                                                 "%w", uz)
+	check(t, "nil",                                                 "%w", u0)
+	check(t, "42",                                                  "%w", u1)
+	check(t, "E.A",                                                 "%w", uef0)
+	check(t, "{E.A, F.A, F.F}",                                     "%w", uefa)
+	check(t, "{a = 1, b = 2, e = E.A}",                             "%w", bf)
+	// Check this manually due to the non-deterministic ordering of map keys.
+	switch fmt.tprintf("%w", map_) {
+	case `{"foo"=8, "bar"=4}`: break
+	case `{"bar"=4, "foo"=8}`: break
+	case: testing.fail(t)
+	}
+}
+
 @(private)
 check :: proc(t: ^testing.T, exp: string, format: string, args: ..any, loc := #caller_location) {
 	got := fmt.tprintf(format, ..args)