x.json2,checker,toml: allow field.typ compile-time checking with MatchExpr and add array of option checking (#21171)

This commit is contained in:
Felipe Pena 2024-04-04 06:43:59 -03:00 committed by GitHub
parent ba577e4e5f
commit 625048c5b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 143 additions and 83 deletions

View File

@ -578,18 +578,6 @@ pub fn (s string) bool() bool {
return s == 'true' || s == 't' // TODO: t for pg, remove 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)`. // i8 returns the value of the string as i8 `'1'.i8() == i8(1)`.
@[inline] @[inline]
pub fn (s string) i8() i8 { 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 }) 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)`. // f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`.
@[inline] @[inline]
pub fn (s string) f32() f32 { pub fn (s string) f32() f32 {
@ -614,12 +620,6 @@ pub fn (s string) f64() f64 {
return strconv.atof64(s) or { 0 } 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. // 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]`. // hex string example: `'0x11223344ee'.u8_array() == [u8(0x11),0x22,0x33,0x44,0xee]`.
// bin string example: `'0b1101_1101'.u8_array() == [u8(0xdd)]`. // bin string example: `'0b1101_1101'.u8_array() == [u8(0xdd)]`.
@ -673,6 +673,12 @@ pub fn (s string) u8_array() []u8 {
return []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)`. // u16 returns the value of the string as u16 `'1'.u16() == u16(1)`.
@[inline] @[inline]
pub fn (s string) u16() u16 { pub fn (s string) u16() u16 {

View File

@ -58,70 +58,70 @@ fn decode_struct[T](doc Any, mut typ T) {
typ.$(field.name) = value.time() typ.$(field.name) = value.time()
} $else $if field.is_array { } $else $if field.is_array {
arr := value.array() arr := value.array()
match typeof(typ.$(field.name)).name { match field.typ {
'[]string' { typ.$(field.name) = arr.as_strings() } []string { typ.$(field.name) = arr.as_strings() }
'[]int' { typ.$(field.name) = arr.map(it.int()) } []int { typ.$(field.name) = arr.map(it.int()) }
'[]i64' { typ.$(field.name) = arr.map(it.i64()) } []i64 { typ.$(field.name) = arr.map(it.i64()) }
'[]u64' { typ.$(field.name) = arr.map(it.u64()) } []u64 { typ.$(field.name) = arr.map(it.u64()) }
'[]f32' { typ.$(field.name) = arr.map(it.f32()) } []f32 { typ.$(field.name) = arr.map(it.f32()) }
'[]f64' { typ.$(field.name) = arr.map(it.f64()) } []f64 { typ.$(field.name) = arr.map(it.f64()) }
'[]bool' { typ.$(field.name) = arr.map(it.bool()) } []bool { typ.$(field.name) = arr.map(it.bool()) }
'[]toml.DateTime' { typ.$(field.name) = arr.map(it.datetime()) } []DateTime { typ.$(field.name) = arr.map(it.datetime()) }
'[]toml.Date' { typ.$(field.name) = arr.map(it.date()) } []Date { typ.$(field.name) = arr.map(it.date()) }
'[]toml.Time' { typ.$(field.name) = arr.map(it.time()) } []Time { typ.$(field.name) = arr.map(it.time()) }
else {} else {}
} }
} $else $if field.is_map { } $else $if field.is_map {
mut mmap := value.as_map() mut mmap := value.as_map()
match typeof(typ.$(field.name)).name { match field.typ {
'map[string]string' { map[string]string {
typ.$(field.name) = mmap.as_strings() typ.$(field.name) = mmap.as_strings()
} }
// Should be cleaned up to use the more modern lambda syntax // Should be cleaned up to use the more modern lambda syntax
// |k, v| k, v.int() // |k, v| k, v.int()
// Unfortunately lambdas have issues with multiple return at the time of writing // 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) { typ.$(field.name) = maps.to_map[string, Any, string, int](mmap, fn (k string, v Any) (string, int) {
return k, v.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) { typ.$(field.name) = maps.to_map[string, Any, string, i64](mmap, fn (k string, v Any) (string, i64) {
return k, v.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) { typ.$(field.name) = maps.to_map[string, Any, string, u64](mmap, fn (k string, v Any) (string, u64) {
return k, v.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) { typ.$(field.name) = maps.to_map[string, Any, string, f32](mmap, fn (k string, v Any) (string, f32) {
return k, v.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) { typ.$(field.name) = maps.to_map[string, Any, string, f64](mmap, fn (k string, v Any) (string, f64) {
return k, v.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) { typ.$(field.name) = maps.to_map[string, Any, string, bool](mmap, fn (k string, v Any) (string, bool) {
return k, v.bool() return k, v.bool()
}) })
} }
'map[string]toml.DateTime' { map[string]DateTime {
typ.$(field.name) = maps.to_map[string, Any, string, DateTime](mmap, typ.$(field.name) = maps.to_map[string, Any, string, DateTime](mmap,
fn (k string, v Any) (string, DateTime) { fn (k string, v Any) (string, DateTime) {
return k, v.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) { typ.$(field.name) = maps.to_map[string, Any, string, Date](mmap, fn (k string, v Any) (string, Date) {
return k, v.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) { typ.$(field.name) = maps.to_map[string, Any, string, Time](mmap, fn (k string, v Any) (string, Time) {
return k, v.time() return k, v.time()
}) })

View File

@ -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', 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) 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', c.error('matching by type can only be done for sum types, generics, interfaces, `${node.cond}` is none of those',
branch.pos) 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) expect_str := c.table.type_to_str(node.cond_type)
c.error('cannot match alias type `${expect_str}` with `${expr_str}`', c.error('cannot match alias type `${expect_str}` with `${expr_str}`',
expr.pos()) 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) expr_str := c.table.type_to_str(expr_type)
expect_str := c.table.type_to_str(node.cond_type) expect_str := c.table.type_to_str(node.cond_type)
c.error('cannot match `${expect_str}` with `${expr_str}`', expr.pos()) c.error('cannot match `${expect_str}` with `${expr_str}`', expr.pos())

View File

@ -212,6 +212,8 @@ fn (mut p Parser) is_only_array_type() bool {
next_kind := p.peek_token(i + 1).kind next_kind := p.peek_token(i + 1).kind
if next_kind == .name { if next_kind == .name {
return true return true
} else if next_kind == .question && p.peek_token(i + 2).kind == .name {
return true
} else if next_kind == .lsbr { } else if next_kind == .lsbr {
continue continue
} else { } else {

View File

@ -603,7 +603,7 @@ fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_dot b
p.next() p.next()
p.check(.dot) 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}`' mut msg := 'unknown module `${mod}`'
if mod.len > mod_last_part.len && p.known_import(mod_last_part) { if mod.len > mod_last_part.len && p.known_import(mod_last_part) {
msg += '; did you mean `${mod_last_part}`?' msg += '; did you mean `${mod_last_part}`?'

View 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++
}
}

View File

@ -179,7 +179,7 @@ fn decode_struct[T](_ T, res map[string]Any) !T {
} $else $if field.typ is i16 { } $else $if field.typ is i16 {
typ.$(field.name) = res[json_name]!.int() typ.$(field.name) = res[json_name]!.int()
} $else $if field.typ is i32 { } $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 { } $else $if field.typ is i64 {
typ.$(field.name) = res[json_name]!.i64() typ.$(field.name) = res[json_name]!.i64()
} $else $if field.typ is ?u8 { } $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 { } $else $if field.is_array {
arr := res[field.name]! as []Any arr := res[field.name]! as []Any
match typeof(typ.$(field.name)).name { // vfmt off
'[]bool' { typ.$(field.name) = arr.map(it.bool()) } match field.typ {
'[]?bool' { typ.$(field.name) = arr.map(?bool(it.bool())) } []bool { typ.$(field.name) = arr.map(it.bool()) }
'[]f32' { typ.$(field.name) = arr.map(it.f32()) } []?bool { typ.$(field.name) = arr.map(?bool(it.bool())) }
'[]?f32' { typ.$(field.name) = arr.map(?f32(it.f32())) } []f32 { typ.$(field.name) = arr.map(it.f32()) }
'[]f64' { typ.$(field.name) = arr.map(it.f64()) } []?f32 { typ.$(field.name) = arr.map(?f32(it.f32())) }
'[]?f64' { typ.$(field.name) = arr.map(?f64(it.f64())) } []f64 { typ.$(field.name) = arr.map(it.f64()) }
'[]i8' { typ.$(field.name) = arr.map(it.i8()) } []?f64 { typ.$(field.name) = arr.map(?f64(it.f64())) }
'[]?i8' { typ.$(field.name) = arr.map(?i8(it.i8())) } []i8 { typ.$(field.name) = arr.map(it.i8()) }
'[]i16' { typ.$(field.name) = arr.map(it.i16()) } []?i8 { typ.$(field.name) = arr.map(?i8(it.i8())) }
'[]?i16' { typ.$(field.name) = arr.map(?i16(it.i16())) } []i16 { typ.$(field.name) = arr.map(it.i16()) }
'[]i64' { typ.$(field.name) = arr.map(it.i64()) } []?i16 { typ.$(field.name) = arr.map(?i16(it.i16())) }
'[]?i64' { typ.$(field.name) = arr.map(?i64(it.i64())) } []i32 { typ.$(field.name) = arr.map(it.i32()) }
'[]int' { typ.$(field.name) = arr.map(it.int()) } []?i32 { typ.$(field.name) = arr.map(?i32(it.i32())) }
'[]?int' { typ.$(field.name) = arr.map(?int(it.int())) } []i64 { typ.$(field.name) = arr.map(it.i64()) }
'[]string' { typ.$(field.name) = arr.map(it.str()) } []?i64 { typ.$(field.name) = arr.map(?i64(it.i64())) }
'[]?string' { typ.$(field.name) = arr.map(?string(it.str())) } []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. // 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(it.to_time() or { time.Time{} }) }
// vfmt off []?time.Time { typ.$(field.name) = arr.map(?time.Time(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()) }
// vfmt on []?u8 { typ.$(field.name) = arr.map(?u8(it.u64())) }
'[]u8' { typ.$(field.name) = arr.map(it.u64()) } []u16 { typ.$(field.name) = arr.map(it.u64()) }
'[]?u8' { typ.$(field.name) = arr.map(?u8(it.u64())) } []?u16 { typ.$(field.name) = arr.map(?u16(it.u64())) }
'[]u16' { typ.$(field.name) = arr.map(it.u64()) } []u32 { typ.$(field.name) = arr.map(it.u64()) }
'[]?u16' { typ.$(field.name) = arr.map(?u16(it.u64())) } []?u32 { typ.$(field.name) = arr.map(?u32(it.u64())) }
'[]u32' { typ.$(field.name) = arr.map(it.u64()) } []u64 { typ.$(field.name) = arr.map(it.u64()) }
'[]?u32' { typ.$(field.name) = arr.map(?u32(it.u64())) } []?u64 { typ.$(field.name) = arr.map(?u64(it.u64())) }
'[]u64' { typ.$(field.name) = arr.map(it.u64()) }
'[]?u64' { typ.$(field.name) = arr.map(?u64(it.u64())) }
else {} else {}
} }
// vfmt on
} $else $if field.is_struct { } $else $if field.is_struct {
typ.$(field.name) = decode_struct(typ.$(field.name), res[field.name]!.as_map())! typ.$(field.name) = decode_struct(typ.$(field.name), res[field.name]!.as_map())!
} $else $if field.is_alias { } $else $if field.is_alias {

View File

@ -5,13 +5,13 @@ module json2
import time import time
// i8 - TODO // i8 uses `Any` as a 16-bit integer.
pub fn (f Any) i8() i8 { pub fn (f Any) i8() i8 {
match f { match f {
i8 { i8 {
return f 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) return i8(f)
} }
string { 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 { pub fn (f Any) i16() i16 {
match f { match f {
i16 { i16 {
return f 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) return i16(f)
} }
string { string {
@ -47,7 +47,7 @@ pub fn (f Any) int() int {
int { int {
return f 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) return int(f)
} }
string { 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. // i64 uses `Any` as a 64-bit integer.
pub fn (f Any) i64() i64 { pub fn (f Any) i64() i64 {
match f { match f {
i64 { i64 {
return f 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) return i64(f)
} }
string { string {
@ -83,7 +101,7 @@ pub fn (f Any) u64() u64 {
u64 { u64 {
return f 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) return u64(f)
} }
string { string {
@ -101,7 +119,7 @@ pub fn (f Any) f32() f32 {
f32 { f32 {
return f 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) return f32(f)
} }
string { string {
@ -119,7 +137,7 @@ pub fn (f Any) f64() f64 {
f64 { f64 {
return f return f
} }
i8, i16, int, i64, u8, u16, u32, u64, f32 { i8, i16, i32, int, i64, u8, u16, u32, u64, f32 {
return f64(f) return f64(f)
} }
string { string {
@ -150,7 +168,7 @@ pub fn (f Any) bool() bool {
return false return false
} }
} }
i8, i16, int, i64 { i8, i16, i32, int, i64 {
return i64(f) != 0 return i64(f) != 0
} }
u8, u16, u32, u64 { 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() m[field.name] = t.$(field.name).str().i8()
} $else $if field.typ is i16 { } $else $if field.typ is i16 {
m[field.name] = t.$(field.name).str().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 { } $else $if field.typ is int {
m[field.name] = t.$(field.name).str().int() m[field.name] = t.$(field.name).str().int()
} $else $if field.typ is i64 { } $else $if field.typ is i64 {

View File

@ -9,6 +9,7 @@ pub type Any = Null
| f32 | f32
| f64 | f64
| i16 | i16
| i32
| i64 | i64
| i8 | i8
| int | int