mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
x.json2,checker,toml: allow field.typ compile-time checking with MatchExpr and add array of option checking (#21171)
This commit is contained in:
parent
ba577e4e5f
commit
625048c5b4
@ -578,18 +578,6 @@ pub fn (s string) bool() bool {
|
||||
return s == 'true' || s == 't' // TODO: t for pg, remove
|
||||
}
|
||||
|
||||
// int returns the value of the string as an integer `'1'.int() == 1`.
|
||||
@[inline]
|
||||
pub fn (s string) int() int {
|
||||
return int(strconv.common_parse_int(s, 0, 32, false, false) or { 0 })
|
||||
}
|
||||
|
||||
// i64 returns the value of the string as i64 `'1'.i64() == i64(1)`.
|
||||
@[inline]
|
||||
pub fn (s string) i64() i64 {
|
||||
return strconv.common_parse_int(s, 0, 64, false, false) or { 0 }
|
||||
}
|
||||
|
||||
// i8 returns the value of the string as i8 `'1'.i8() == i8(1)`.
|
||||
@[inline]
|
||||
pub fn (s string) i8() i8 {
|
||||
@ -602,6 +590,24 @@ pub fn (s string) i16() i16 {
|
||||
return i16(strconv.common_parse_int(s, 0, 16, false, false) or { 0 })
|
||||
}
|
||||
|
||||
// i32 returns the value of the string as i32 `'1'.i32() == i32(1)`.
|
||||
@[inline]
|
||||
pub fn (s string) i32() i32 {
|
||||
return i32(strconv.common_parse_int(s, 0, 32, false, false) or { 0 })
|
||||
}
|
||||
|
||||
// int returns the value of the string as an integer `'1'.int() == 1`.
|
||||
@[inline]
|
||||
pub fn (s string) int() int {
|
||||
return int(strconv.common_parse_int(s, 0, 32, false, false) or { 0 })
|
||||
}
|
||||
|
||||
// i64 returns the value of the string as i64 `'1'.i64() == i64(1)`.
|
||||
@[inline]
|
||||
pub fn (s string) i64() i64 {
|
||||
return strconv.common_parse_int(s, 0, 64, false, false) or { 0 }
|
||||
}
|
||||
|
||||
// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`.
|
||||
@[inline]
|
||||
pub fn (s string) f32() f32 {
|
||||
@ -614,12 +620,6 @@ pub fn (s string) f64() f64 {
|
||||
return strconv.atof64(s) or { 0 }
|
||||
}
|
||||
|
||||
// u8 returns the value of the string as u8 `'1'.u8() == u8(1)`.
|
||||
@[inline]
|
||||
pub fn (s string) u8() u8 {
|
||||
return u8(strconv.common_parse_uint(s, 0, 8, false, false) or { 0 })
|
||||
}
|
||||
|
||||
// u8_array returns the value of the hex/bin string as u8 array.
|
||||
// hex string example: `'0x11223344ee'.u8_array() == [u8(0x11),0x22,0x33,0x44,0xee]`.
|
||||
// bin string example: `'0b1101_1101'.u8_array() == [u8(0xdd)]`.
|
||||
@ -673,6 +673,12 @@ pub fn (s string) u8_array() []u8 {
|
||||
return []u8{}
|
||||
}
|
||||
|
||||
// u8 returns the value of the string as u8 `'1'.u8() == u8(1)`.
|
||||
@[inline]
|
||||
pub fn (s string) u8() u8 {
|
||||
return u8(strconv.common_parse_uint(s, 0, 8, false, false) or { 0 })
|
||||
}
|
||||
|
||||
// u16 returns the value of the string as u16 `'1'.u16() == u16(1)`.
|
||||
@[inline]
|
||||
pub fn (s string) u16() u16 {
|
||||
|
@ -58,70 +58,70 @@ fn decode_struct[T](doc Any, mut typ T) {
|
||||
typ.$(field.name) = value.time()
|
||||
} $else $if field.is_array {
|
||||
arr := value.array()
|
||||
match typeof(typ.$(field.name)).name {
|
||||
'[]string' { typ.$(field.name) = arr.as_strings() }
|
||||
'[]int' { typ.$(field.name) = arr.map(it.int()) }
|
||||
'[]i64' { typ.$(field.name) = arr.map(it.i64()) }
|
||||
'[]u64' { typ.$(field.name) = arr.map(it.u64()) }
|
||||
'[]f32' { typ.$(field.name) = arr.map(it.f32()) }
|
||||
'[]f64' { typ.$(field.name) = arr.map(it.f64()) }
|
||||
'[]bool' { typ.$(field.name) = arr.map(it.bool()) }
|
||||
'[]toml.DateTime' { typ.$(field.name) = arr.map(it.datetime()) }
|
||||
'[]toml.Date' { typ.$(field.name) = arr.map(it.date()) }
|
||||
'[]toml.Time' { typ.$(field.name) = arr.map(it.time()) }
|
||||
match field.typ {
|
||||
[]string { typ.$(field.name) = arr.as_strings() }
|
||||
[]int { typ.$(field.name) = arr.map(it.int()) }
|
||||
[]i64 { typ.$(field.name) = arr.map(it.i64()) }
|
||||
[]u64 { typ.$(field.name) = arr.map(it.u64()) }
|
||||
[]f32 { typ.$(field.name) = arr.map(it.f32()) }
|
||||
[]f64 { typ.$(field.name) = arr.map(it.f64()) }
|
||||
[]bool { typ.$(field.name) = arr.map(it.bool()) }
|
||||
[]DateTime { typ.$(field.name) = arr.map(it.datetime()) }
|
||||
[]Date { typ.$(field.name) = arr.map(it.date()) }
|
||||
[]Time { typ.$(field.name) = arr.map(it.time()) }
|
||||
else {}
|
||||
}
|
||||
} $else $if field.is_map {
|
||||
mut mmap := value.as_map()
|
||||
match typeof(typ.$(field.name)).name {
|
||||
'map[string]string' {
|
||||
match field.typ {
|
||||
map[string]string {
|
||||
typ.$(field.name) = mmap.as_strings()
|
||||
}
|
||||
// Should be cleaned up to use the more modern lambda syntax
|
||||
// |k, v| k, v.int()
|
||||
// Unfortunately lambdas have issues with multiple return at the time of writing
|
||||
'map[string]int' {
|
||||
map[string]int {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, int](mmap, fn (k string, v Any) (string, int) {
|
||||
return k, v.int()
|
||||
})
|
||||
}
|
||||
'map[string]i64' {
|
||||
map[string]i64 {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, i64](mmap, fn (k string, v Any) (string, i64) {
|
||||
return k, v.i64()
|
||||
})
|
||||
}
|
||||
'map[string]u64' {
|
||||
map[string]u64 {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, u64](mmap, fn (k string, v Any) (string, u64) {
|
||||
return k, v.u64()
|
||||
})
|
||||
}
|
||||
'map[string]f32' {
|
||||
map[string]f32 {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, f32](mmap, fn (k string, v Any) (string, f32) {
|
||||
return k, v.f32()
|
||||
})
|
||||
}
|
||||
'map[string]f64' {
|
||||
map[string]f64 {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, f64](mmap, fn (k string, v Any) (string, f64) {
|
||||
return k, v.f64()
|
||||
})
|
||||
}
|
||||
'map[string]bool' {
|
||||
map[string]bool {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, bool](mmap, fn (k string, v Any) (string, bool) {
|
||||
return k, v.bool()
|
||||
})
|
||||
}
|
||||
'map[string]toml.DateTime' {
|
||||
map[string]DateTime {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, DateTime](mmap,
|
||||
fn (k string, v Any) (string, DateTime) {
|
||||
return k, v.datetime()
|
||||
})
|
||||
}
|
||||
'map[string]toml.Date' {
|
||||
map[string]Date {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, Date](mmap, fn (k string, v Any) (string, Date) {
|
||||
return k, v.date()
|
||||
})
|
||||
}
|
||||
'map[string]toml.Time' {
|
||||
map[string]Time {
|
||||
typ.$(field.name) = maps.to_map[string, Any, string, Time](mmap, fn (k string, v Any) (string, Time) {
|
||||
return k, v.time()
|
||||
})
|
||||
|
@ -322,7 +322,15 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
|
||||
c.error('struct instances cannot be matched by type name, they can only be matched to other instances of the same struct type',
|
||||
branch.pos)
|
||||
}
|
||||
if mut expr is ast.TypeNode && cond_sym.is_primitive() {
|
||||
mut is_comptime := false
|
||||
if c.comptime.inside_comptime_for {
|
||||
// it is a compile-time field.typ checking
|
||||
if mut node.cond is ast.SelectorExpr {
|
||||
is_comptime = node.cond.expr_type == c.field_data_type
|
||||
&& node.cond.field_name == 'typ'
|
||||
}
|
||||
}
|
||||
if mut expr is ast.TypeNode && cond_sym.is_primitive() && !is_comptime {
|
||||
c.error('matching by type can only be done for sum types, generics, interfaces, `${node.cond}` is none of those',
|
||||
branch.pos)
|
||||
}
|
||||
@ -442,7 +450,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
|
||||
expect_str := c.table.type_to_str(node.cond_type)
|
||||
c.error('cannot match alias type `${expect_str}` with `${expr_str}`',
|
||||
expr.pos())
|
||||
} else if !c.check_types(expr_type, node.cond_type) {
|
||||
} else if !c.check_types(expr_type, node.cond_type) && !is_comptime {
|
||||
expr_str := c.table.type_to_str(expr_type)
|
||||
expect_str := c.table.type_to_str(node.cond_type)
|
||||
c.error('cannot match `${expect_str}` with `${expr_str}`', expr.pos())
|
||||
|
@ -212,6 +212,8 @@ fn (mut p Parser) is_only_array_type() bool {
|
||||
next_kind := p.peek_token(i + 1).kind
|
||||
if next_kind == .name {
|
||||
return true
|
||||
} else if next_kind == .question && p.peek_token(i + 2).kind == .name {
|
||||
return true
|
||||
} else if next_kind == .lsbr {
|
||||
continue
|
||||
} else {
|
||||
|
@ -603,7 +603,7 @@ fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_dot b
|
||||
p.next()
|
||||
p.check(.dot)
|
||||
}
|
||||
if !p.known_import(mod) && !p.pref.is_fmt {
|
||||
if mod != p.mod && !p.known_import(mod) && !p.pref.is_fmt {
|
||||
mut msg := 'unknown module `${mod}`'
|
||||
if mod.len > mod_last_part.len && p.known_import(mod_last_part) {
|
||||
msg += '; did you mean `${mod_last_part}`?'
|
||||
|
21
vlib/v/tests/comptime_match_type_test.v
Normal file
21
vlib/v/tests/comptime_match_type_test.v
Normal file
@ -0,0 +1,21 @@
|
||||
struct Test {
|
||||
a int
|
||||
b []int
|
||||
c map[int]string
|
||||
d []?int
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
mut i := 1
|
||||
$for f in Test.fields {
|
||||
type_name := typeof(f.$(f.name)).name
|
||||
match f.typ {
|
||||
int { assert i == 1, '1. ${f.name} is ${type_name}' }
|
||||
[]int { assert i == 2, '2. ${f.name} is ${type_name}' }
|
||||
map[int]string { assert i == 3, '3. ${f.name} is ${type_name}' }
|
||||
[]?int { assert i == 4, '4. ${f.name} is ${type_name}' }
|
||||
else {}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
@ -179,7 +179,7 @@ fn decode_struct[T](_ T, res map[string]Any) !T {
|
||||
} $else $if field.typ is i16 {
|
||||
typ.$(field.name) = res[json_name]!.int()
|
||||
} $else $if field.typ is i32 {
|
||||
typ.$(field.name) = i32(res[field.name]!.int())
|
||||
typ.$(field.name) = i32(res[field.name]!.i32())
|
||||
} $else $if field.typ is i64 {
|
||||
typ.$(field.name) = res[json_name]!.i64()
|
||||
} $else $if field.typ is ?u8 {
|
||||
@ -250,38 +250,40 @@ fn decode_struct[T](_ T, res map[string]Any) !T {
|
||||
}
|
||||
} $else $if field.is_array {
|
||||
arr := res[field.name]! as []Any
|
||||
match typeof(typ.$(field.name)).name {
|
||||
'[]bool' { typ.$(field.name) = arr.map(it.bool()) }
|
||||
'[]?bool' { typ.$(field.name) = arr.map(?bool(it.bool())) }
|
||||
'[]f32' { typ.$(field.name) = arr.map(it.f32()) }
|
||||
'[]?f32' { typ.$(field.name) = arr.map(?f32(it.f32())) }
|
||||
'[]f64' { typ.$(field.name) = arr.map(it.f64()) }
|
||||
'[]?f64' { typ.$(field.name) = arr.map(?f64(it.f64())) }
|
||||
'[]i8' { typ.$(field.name) = arr.map(it.i8()) }
|
||||
'[]?i8' { typ.$(field.name) = arr.map(?i8(it.i8())) }
|
||||
'[]i16' { typ.$(field.name) = arr.map(it.i16()) }
|
||||
'[]?i16' { typ.$(field.name) = arr.map(?i16(it.i16())) }
|
||||
'[]i64' { typ.$(field.name) = arr.map(it.i64()) }
|
||||
'[]?i64' { typ.$(field.name) = arr.map(?i64(it.i64())) }
|
||||
'[]int' { typ.$(field.name) = arr.map(it.int()) }
|
||||
'[]?int' { typ.$(field.name) = arr.map(?int(it.int())) }
|
||||
'[]string' { typ.$(field.name) = arr.map(it.str()) }
|
||||
'[]?string' { typ.$(field.name) = arr.map(?string(it.str())) }
|
||||
// NOTE: Using `!` on `to_time()` inside the array method causes a builder error - 2024/04/01.
|
||||
'[]time.Time' { typ.$(field.name) = arr.map(it.to_time() or { time.Time{} }) }
|
||||
// vfmt off
|
||||
'[]?time.Time' { typ.$(field.name) = arr.map(?time.Time(it.to_time() or { time.Time{} })) }
|
||||
// vfmt on
|
||||
'[]u8' { typ.$(field.name) = arr.map(it.u64()) }
|
||||
'[]?u8' { typ.$(field.name) = arr.map(?u8(it.u64())) }
|
||||
'[]u16' { typ.$(field.name) = arr.map(it.u64()) }
|
||||
'[]?u16' { typ.$(field.name) = arr.map(?u16(it.u64())) }
|
||||
'[]u32' { typ.$(field.name) = arr.map(it.u64()) }
|
||||
'[]?u32' { typ.$(field.name) = arr.map(?u32(it.u64())) }
|
||||
'[]u64' { typ.$(field.name) = arr.map(it.u64()) }
|
||||
'[]?u64' { typ.$(field.name) = arr.map(?u64(it.u64())) }
|
||||
match field.typ {
|
||||
[]bool { typ.$(field.name) = arr.map(it.bool()) }
|
||||
[]?bool { typ.$(field.name) = arr.map(?bool(it.bool())) }
|
||||
[]f32 { typ.$(field.name) = arr.map(it.f32()) }
|
||||
[]?f32 { typ.$(field.name) = arr.map(?f32(it.f32())) }
|
||||
[]f64 { typ.$(field.name) = arr.map(it.f64()) }
|
||||
[]?f64 { typ.$(field.name) = arr.map(?f64(it.f64())) }
|
||||
[]i8 { typ.$(field.name) = arr.map(it.i8()) }
|
||||
[]?i8 { typ.$(field.name) = arr.map(?i8(it.i8())) }
|
||||
[]i16 { typ.$(field.name) = arr.map(it.i16()) }
|
||||
[]?i16 { typ.$(field.name) = arr.map(?i16(it.i16())) }
|
||||
[]i32 { typ.$(field.name) = arr.map(it.i32()) }
|
||||
[]?i32 { typ.$(field.name) = arr.map(?i32(it.i32())) }
|
||||
[]i64 { typ.$(field.name) = arr.map(it.i64()) }
|
||||
[]?i64 { typ.$(field.name) = arr.map(?i64(it.i64())) }
|
||||
[]int { typ.$(field.name) = arr.map(it.int()) }
|
||||
[]?int { typ.$(field.name) = arr.map(?int(it.int())) }
|
||||
[]string { typ.$(field.name) = arr.map(it.str()) }
|
||||
[]?string { typ.$(field.name) = arr.map(?string(it.str())) }
|
||||
// NOTE: Using `!` on `to_time()` inside the array method causes a builder error - 2024/04/01.
|
||||
[]time.Time { typ.$(field.name) = arr.map(it.to_time() or { time.Time{} }) }
|
||||
[]?time.Time { typ.$(field.name) = arr.map(?time.Time(it.to_time() or { time.Time{} })) }
|
||||
[]u8 { typ.$(field.name) = arr.map(it.u64()) }
|
||||
[]?u8 { typ.$(field.name) = arr.map(?u8(it.u64())) }
|
||||
[]u16 { typ.$(field.name) = arr.map(it.u64()) }
|
||||
[]?u16 { typ.$(field.name) = arr.map(?u16(it.u64())) }
|
||||
[]u32 { typ.$(field.name) = arr.map(it.u64()) }
|
||||
[]?u32 { typ.$(field.name) = arr.map(?u32(it.u64())) }
|
||||
[]u64 { typ.$(field.name) = arr.map(it.u64()) }
|
||||
[]?u64 { typ.$(field.name) = arr.map(?u64(it.u64())) }
|
||||
else {}
|
||||
}
|
||||
// vfmt on
|
||||
} $else $if field.is_struct {
|
||||
typ.$(field.name) = decode_struct(typ.$(field.name), res[field.name]!.as_map())!
|
||||
} $else $if field.is_alias {
|
||||
|
@ -5,13 +5,13 @@ module json2
|
||||
|
||||
import time
|
||||
|
||||
// i8 - TODO
|
||||
// i8 uses `Any` as a 16-bit integer.
|
||||
pub fn (f Any) i8() i8 {
|
||||
match f {
|
||||
i8 {
|
||||
return f
|
||||
}
|
||||
i16, int, i64, u8, u16, u32, u64, f32, f64, bool {
|
||||
i16, i32, int, i64, u8, u16, u32, u64, f32, f64, bool {
|
||||
return i8(f)
|
||||
}
|
||||
string {
|
||||
@ -23,13 +23,13 @@ pub fn (f Any) i8() i8 {
|
||||
}
|
||||
}
|
||||
|
||||
// i16 - TODO
|
||||
// i16 uses `Any` as a 16-bit integer.
|
||||
pub fn (f Any) i16() i16 {
|
||||
match f {
|
||||
i16 {
|
||||
return f
|
||||
}
|
||||
i8, int, i64, u8, u16, u32, u64, f32, f64, bool {
|
||||
i8, i32, int, i64, u8, u16, u32, u64, f32, f64, bool {
|
||||
return i16(f)
|
||||
}
|
||||
string {
|
||||
@ -47,7 +47,7 @@ pub fn (f Any) int() int {
|
||||
int {
|
||||
return f
|
||||
}
|
||||
i8, i16, i64, u8, u16, u32, u64, f32, f64, bool {
|
||||
i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, bool {
|
||||
return int(f)
|
||||
}
|
||||
string {
|
||||
@ -59,13 +59,31 @@ pub fn (f Any) int() int {
|
||||
}
|
||||
}
|
||||
|
||||
// i32 uses `Any` as a 32-bit integer.
|
||||
pub fn (f Any) i32() i32 {
|
||||
match f {
|
||||
i32 {
|
||||
return f
|
||||
}
|
||||
i8, i16, int, i64, u8, u16, u32, u64, f32, f64, bool {
|
||||
return i32(f)
|
||||
}
|
||||
string {
|
||||
return f.i32()
|
||||
}
|
||||
else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// i64 uses `Any` as a 64-bit integer.
|
||||
pub fn (f Any) i64() i64 {
|
||||
match f {
|
||||
i64 {
|
||||
return f
|
||||
}
|
||||
i8, i16, int, u8, u16, u32, u64, f32, f64, bool {
|
||||
i8, i16, i32, int, u8, u16, u32, u64, f32, f64, bool {
|
||||
return i64(f)
|
||||
}
|
||||
string {
|
||||
@ -83,7 +101,7 @@ pub fn (f Any) u64() u64 {
|
||||
u64 {
|
||||
return f
|
||||
}
|
||||
u8, u16, u32, i8, i16, int, i64, f32, f64, bool {
|
||||
u8, u16, u32, i8, i16, i32, int, i64, f32, f64, bool {
|
||||
return u64(f)
|
||||
}
|
||||
string {
|
||||
@ -101,7 +119,7 @@ pub fn (f Any) f32() f32 {
|
||||
f32 {
|
||||
return f
|
||||
}
|
||||
bool, i8, i16, int, i64, u8, u16, u32, u64, f64 {
|
||||
bool, i8, i16, i32, int, i64, u8, u16, u32, u64, f64 {
|
||||
return f32(f)
|
||||
}
|
||||
string {
|
||||
@ -119,7 +137,7 @@ pub fn (f Any) f64() f64 {
|
||||
f64 {
|
||||
return f
|
||||
}
|
||||
i8, i16, int, i64, u8, u16, u32, u64, f32 {
|
||||
i8, i16, i32, int, i64, u8, u16, u32, u64, f32 {
|
||||
return f64(f)
|
||||
}
|
||||
string {
|
||||
@ -150,7 +168,7 @@ pub fn (f Any) bool() bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
i8, i16, int, i64 {
|
||||
i8, i16, i32, int, i64 {
|
||||
return i64(f) != 0
|
||||
}
|
||||
u8, u16, u32, u64 {
|
||||
@ -266,6 +284,8 @@ pub fn map_from[T](t T) map[string]Any {
|
||||
m[field.name] = t.$(field.name).str().i8()
|
||||
} $else $if field.typ is i16 {
|
||||
m[field.name] = t.$(field.name).str().i16()
|
||||
} $else $if field.typ is i32 {
|
||||
m[field.name] = t.$(field.name).str().i32()
|
||||
} $else $if field.typ is int {
|
||||
m[field.name] = t.$(field.name).str().int()
|
||||
} $else $if field.typ is i64 {
|
||||
|
@ -9,6 +9,7 @@ pub type Any = Null
|
||||
| f32
|
||||
| f64
|
||||
| i16
|
||||
| i32
|
||||
| i64
|
||||
| i8
|
||||
| int
|
||||
|
Loading…
x
Reference in New Issue
Block a user