parser: allow integer and rune keys for map literal (#7756)
parent
24b18f05c4
commit
9243e06dba
|
@ -485,6 +485,8 @@ fn test_int_keys() {
|
||||||
m[5] += 24
|
m[5] += 24
|
||||||
m[5]++
|
m[5]++
|
||||||
assert m[5] == 25
|
assert m[5] == 25
|
||||||
|
m2 := {3:9 4:16 5:25}
|
||||||
|
assert m2.len == 3
|
||||||
mc := m.clone()
|
mc := m.clone()
|
||||||
assert mc.len == 3
|
assert mc.len == 3
|
||||||
mut all := []int{}
|
mut all := []int{}
|
||||||
|
@ -506,6 +508,21 @@ fn test_voidptr_keys() {
|
||||||
assert m.len == 2
|
assert m.len == 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_rune_keys() {
|
||||||
|
mut m := {`!`:2 `%`:3}
|
||||||
|
assert typeof(m).name == 'map[rune]int'
|
||||||
|
assert m[`!`] == 2
|
||||||
|
m[`@`] = 7
|
||||||
|
assert m.len == 3
|
||||||
|
|
||||||
|
mut a := []rune{}
|
||||||
|
for k, v in m {
|
||||||
|
a << k
|
||||||
|
a << rune(v) + `0`
|
||||||
|
}
|
||||||
|
assert a == [`!`, `2`, `%`, `3`, `@`, `7`]
|
||||||
|
}
|
||||||
|
|
||||||
fn test_eq() {
|
fn test_eq() {
|
||||||
a := {
|
a := {
|
||||||
'a': 1
|
'a': 1
|
||||||
|
|
|
@ -4656,6 +4656,25 @@ pub fn (mut c Checker) chan_init(mut node ast.ChanInit) table.Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut c Checker) check_dup_keys(node &ast.MapInit, i int) {
|
||||||
|
key_i := node.keys[i]
|
||||||
|
if key_i is ast.StringLiteral {
|
||||||
|
for j in 0 .. i {
|
||||||
|
key_j := node.keys[j] as ast.StringLiteral
|
||||||
|
if key_i.val == key_j.val {
|
||||||
|
c.error('duplicate key "$key_i.val" in map literal', key_i.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if key_i is ast.IntegerLiteral {
|
||||||
|
for j in 0 .. i {
|
||||||
|
key_j := node.keys[j] as ast.IntegerLiteral
|
||||||
|
if key_i.val == key_j.val {
|
||||||
|
c.error('duplicate key "$key_i.val" in map literal', key_i.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type {
|
pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type {
|
||||||
// `x := map[string]string` - set in parser
|
// `x := map[string]string` - set in parser
|
||||||
if node.typ != 0 {
|
if node.typ != 0 {
|
||||||
|
@ -4675,14 +4694,8 @@ pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type {
|
||||||
// `{'age': 20}`
|
// `{'age': 20}`
|
||||||
key0_type := c.table.mktyp(c.expr(node.keys[0]))
|
key0_type := c.table.mktyp(c.expr(node.keys[0]))
|
||||||
val0_type := c.table.mktyp(c.expr(node.vals[0]))
|
val0_type := c.table.mktyp(c.expr(node.vals[0]))
|
||||||
|
mut same_key_type := true
|
||||||
for i, key in node.keys {
|
for i, key in node.keys {
|
||||||
key_i := key as ast.StringLiteral
|
|
||||||
for j in 0 .. i {
|
|
||||||
key_j := node.keys[j] as ast.StringLiteral
|
|
||||||
if key_i.val == key_j.val {
|
|
||||||
c.error('duplicate key "$key_i.val" in map literal', key.position())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -4690,16 +4703,18 @@ pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type {
|
||||||
key_type := c.expr(key)
|
key_type := c.expr(key)
|
||||||
val_type := c.expr(val)
|
val_type := c.expr(val)
|
||||||
if !c.check_types(key_type, key0_type) {
|
if !c.check_types(key_type, key0_type) {
|
||||||
key0_type_sym := c.table.get_type_symbol(key0_type)
|
msg := c.expected_msg(key_type, key0_type)
|
||||||
key_type_sym := c.table.get_type_symbol(key_type)
|
c.error('invalid map key: $msg', key.position())
|
||||||
c.error('map init: cannot use `$key_type_sym.name` as `$key0_type_sym.name` for map key',
|
same_key_type = false
|
||||||
node.pos)
|
|
||||||
}
|
}
|
||||||
if !c.check_types(val_type, val0_type) {
|
if !c.check_types(val_type, val0_type) {
|
||||||
val0_type_sym := c.table.get_type_symbol(val0_type)
|
msg := c.expected_msg(val_type, val0_type)
|
||||||
val_type_sym := c.table.get_type_symbol(val_type)
|
c.error('invalid map value: $msg', val.position())
|
||||||
c.error('map init: cannot use `$val_type_sym.name` as `$val0_type_sym.name` for map value',
|
}
|
||||||
node.pos)
|
}
|
||||||
|
if same_key_type {
|
||||||
|
for i in 1 .. node.keys.len {
|
||||||
|
c.check_dup_keys(node, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map_type := table.new_type(c.table.find_or_register_map(key0_type, val0_type))
|
map_type := table.new_type(c.table.find_or_register_map(key0_type, val0_type))
|
||||||
|
|
|
@ -5,3 +5,9 @@ vlib/v/checker/tests/map_init_key_duplicate_err.vv:5:3: error: duplicate key "fo
|
||||||
| ~~~~~
|
| ~~~~~
|
||||||
6 | }
|
6 | }
|
||||||
7 | println(a)
|
7 | println(a)
|
||||||
|
vlib/v/checker/tests/map_init_key_duplicate_err.vv:9:15: error: duplicate key "2" in map literal
|
||||||
|
7 | println(a)
|
||||||
|
8 |
|
||||||
|
9 | _ = {2:0 3:0 2:0}
|
||||||
|
| ^
|
||||||
|
10 | }
|
||||||
|
|
|
@ -5,4 +5,6 @@ fn main() {
|
||||||
'foo': 'bar'
|
'foo': 'bar'
|
||||||
}
|
}
|
||||||
println(a)
|
println(a)
|
||||||
|
|
||||||
|
_ = {2:0 3:0 2:0}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,18 @@ vlib/v/checker/tests/map_init_wrong_type.vv:3:10: error: cannot assign to `a`: e
|
||||||
2 | mut a := map[string]f32{}
|
2 | mut a := map[string]f32{}
|
||||||
3 | a = { 'x': 12.3 }
|
3 | a = { 'x': 12.3 }
|
||||||
| ~~~
|
| ~~~
|
||||||
4 | }
|
4 | _ = {2:0 3:0 "hi":0}
|
||||||
|
5 | _ = {2:0 3:`@` 4:0}
|
||||||
|
vlib/v/checker/tests/map_init_wrong_type.vv:4:17: error: invalid map key: expected `int`, not `string`
|
||||||
|
2 | mut a := map[string]f32{}
|
||||||
|
3 | a = { 'x': 12.3 }
|
||||||
|
4 | _ = {2:0 3:0 "hi":0}
|
||||||
|
| ~~~~
|
||||||
|
5 | _ = {2:0 3:`@` 4:0}
|
||||||
|
6 | }
|
||||||
|
vlib/v/checker/tests/map_init_wrong_type.vv:5:15: error: invalid map value: expected `int`, not `rune`
|
||||||
|
3 | a = { 'x': 12.3 }
|
||||||
|
4 | _ = {2:0 3:0 "hi":0}
|
||||||
|
5 | _ = {2:0 3:`@` 4:0}
|
||||||
|
| ~~~
|
||||||
|
6 | }
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
mut a := map[string]f32{}
|
mut a := map[string]f32{}
|
||||||
a = { 'x': 12.3 }
|
a = { 'x': 12.3 }
|
||||||
|
_ = {2:0 3:0 "hi":0}
|
||||||
|
_ = {2:0 3:`@` 4:0}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2302,7 +2302,7 @@ fn (mut g Gen) map_fn_ptrs(key_typ table.TypeSymbol) (string, string, string, st
|
||||||
key_eq_fn = '&map_eq_int_2'
|
key_eq_fn = '&map_eq_int_2'
|
||||||
clone_fn = '&map_clone_int_2'
|
clone_fn = '&map_clone_int_2'
|
||||||
}
|
}
|
||||||
.int, .u32 {
|
.int, .u32, .rune {
|
||||||
hash_fn = '&map_hash_int_4'
|
hash_fn = '&map_hash_int_4'
|
||||||
key_eq_fn = '&map_eq_int_4'
|
key_eq_fn = '&map_eq_int_4'
|
||||||
clone_fn = '&map_clone_int_4'
|
clone_fn = '&map_clone_int_4'
|
||||||
|
|
|
@ -152,8 +152,10 @@ fn (mut p Parser) map_init() ast.MapInit {
|
||||||
mut keys := []ast.Expr{}
|
mut keys := []ast.Expr{}
|
||||||
mut vals := []ast.Expr{}
|
mut vals := []ast.Expr{}
|
||||||
for p.tok.kind != .rcbr && p.tok.kind != .eof {
|
for p.tok.kind != .rcbr && p.tok.kind != .eof {
|
||||||
// p.check(.str)
|
|
||||||
key := p.expr(0)
|
key := p.expr(0)
|
||||||
|
if key is ast.FloatLiteral {
|
||||||
|
p.error_with_pos('maps do not support floating point keys yet', key.pos)
|
||||||
|
}
|
||||||
keys << key
|
keys << key
|
||||||
p.check(.colon)
|
p.check(.colon)
|
||||||
val := p.expr(0)
|
val := p.expr(0)
|
||||||
|
|
|
@ -193,7 +193,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
.lcbr {
|
.lcbr {
|
||||||
// Map `{"age": 20}` or `{ x | foo:bar, a:10 }`
|
// Map `{"age": 20}` or `{ x | foo:bar, a:10 }`
|
||||||
p.next()
|
p.next()
|
||||||
if p.tok.kind == .string {
|
if p.tok.kind in [.chartoken, .number, .string] {
|
||||||
node = p.map_init()
|
node = p.map_init()
|
||||||
} else {
|
} else {
|
||||||
// it should be a struct
|
// it should be a struct
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub enum Kind {
|
||||||
number // 123
|
number // 123
|
||||||
string // 'foo'
|
string // 'foo'
|
||||||
str_inter // 'name=$user.name'
|
str_inter // 'name=$user.name'
|
||||||
chartoken // `A`
|
chartoken // `A` - rune
|
||||||
plus
|
plus
|
||||||
minus
|
minus
|
||||||
mul
|
mul
|
||||||
|
|
Loading…
Reference in New Issue