From f5f65f929f4f50c93c3ce86b6563db9b2594b520 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sat, 6 Feb 2021 21:13:24 +0000 Subject: [PATCH] parser: parse `map{key_expr: val_expr}` (#8608) --- vlib/builtin/map_test.v | 10 +++++++--- vlib/v/checker/checker.v | 16 ++++++++++------ vlib/v/parser/containers.v | 1 + vlib/v/parser/fn.v | 8 +++++++- vlib/v/parser/parser.v | 15 +++++++++++---- vlib/v/parser/pratt.v | 1 + vlib/v/tests/map_key_expr_test.v | 30 ++++++++++++++++++++++++++++++ 7 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 vlib/v/tests/map_key_expr_test.v diff --git a/vlib/builtin/map_test.v b/vlib/builtin/map_test.v index d5ff22c4a8..70296d4cc2 100644 --- a/vlib/builtin/map_test.v +++ b/vlib/builtin/map_test.v @@ -128,13 +128,17 @@ fn test_map() { } fn test_map_init() { - m := { - 'one': 1 + one := 'one' + three := 'three' + m := map{ + one: 1 'two': 2 + three: 1 + 2 } assert m['one'] == 1 assert m['two'] == 2 - assert m['three'] == 0 + assert m['three'] == 3 + assert m['unknown'] == 0 } fn test_string_map() { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 455c12c654..2437f4b8d5 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -5335,16 +5335,20 @@ 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) + key_j := node.keys[j] + if key_j is 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) + key_j := node.keys[j] + if key_j is ast.IntegerLiteral { + if key_i.val == key_j.val { + c.error('duplicate key "$key_i.val" in map literal', key_i.pos) + } } } } diff --git a/vlib/v/parser/containers.v b/vlib/v/parser/containers.v index ab81bdf678..bf53d19e66 100644 --- a/vlib/v/parser/containers.v +++ b/vlib/v/parser/containers.v @@ -152,6 +152,7 @@ fn (mut p Parser) array_init() ast.ArrayInit { } } +// parse tokens between braces fn (mut p Parser) map_init() ast.MapInit { first_pos := p.prev_tok.position() mut keys := []ast.Expr{} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index a80161eb8a..b4fcab29b8 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -128,7 +128,13 @@ pub fn (mut p Parser) call_args() []ast.CallArg { p.next() array_decompose = true } - mut e := p.expr(0) + mut e := ast.Expr{} + if p.tok.kind == .name && p.peek_tok.kind == .colon { + // `foo(key:val, key2:val2)` + e = p.struct_init(true) // short_syntax:true + } else { + e = p.expr(0) + } if array_decompose { e = ast.ArrayDecompose{ expr: e diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index a261e40542..1a2c1067b1 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1314,6 +1314,16 @@ pub fn (mut p Parser) name_expr() ast.Expr { && (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital)) && !p.inside_match_case && (!p.inside_if || p.inside_select) && (!p.inside_for || p.inside_select) { // && (p.tok.lit[0].is_capital() || p.builtin_mod) { + // map.v has struct literal: map{field: expr} + if p.peek_tok.kind == .lcbr && !(p.builtin_mod && p.file_base == 'map.v') + && p.tok.lit == 'map' { + // map{key_expr: val_expr} + p.check(.name) + p.check(.lcbr) + map_init := p.map_init() + p.check(.rcbr) + return map_init + } return p.struct_init(false) // short_syntax: false } else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) { // T.name @@ -1351,11 +1361,8 @@ pub fn (mut p Parser) name_expr() ast.Expr { pos: p.tok.position() mod: mod } - } else if p.peek_tok.kind == .colon && p.prev_tok.kind != .str_dollar { - // `foo(key:val, key2:val2)` - return p.struct_init(true) // short_syntax:true - // JS. function call with more than 1 dot } else if language == .js && p.peek_tok.kind == .dot && p.peek_tok2.kind == .name { + // JS. function call with more than 1 dot node = p.call_expr(language, mod) } else { node = p.parse_ident(language) diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index e75f1b5df7..d40cc0fa45 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -240,6 +240,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { // Map `{"age": 20}` or `{ x | foo:bar, a:10 }` p.next() if p.tok.kind in [.chartoken, .number, .string] { + // TODO deprecate node = p.map_init() } else { // it should be a struct diff --git a/vlib/v/tests/map_key_expr_test.v b/vlib/v/tests/map_key_expr_test.v new file mode 100644 index 0000000000..21bc17db8c --- /dev/null +++ b/vlib/v/tests/map_key_expr_test.v @@ -0,0 +1,30 @@ +const ( + alpha = 'a' + beta = 'b' + m = map{ + alpha : 'Alpha' + beta : 'Beta' + } +) + +fn test_const_keys() { + assert m[alpha] == 'Alpha' + assert m[beta] == 'Beta' +} + +enum Enum { + a b +} + +const ( + m2 = map{ + Enum.a.str() : 'first' + Enum.b.str() : 'second' + } +) + +fn test_method_call() { + assert m2.keys() == ['a', 'b'] + assert m2[Enum.a.str()] == 'first' + assert m2[Enum.b.str()] == 'second' +}