From 5b47ec49af9d404e1be6c0040de7b5146abb98a0 Mon Sep 17 00:00:00 2001 From: Ned Palacios Date: Fri, 8 May 2020 21:09:42 +0800 Subject: [PATCH] parser: add support for multiple struct field attributes --- vlib/json/json_test.v | 8 +++-- vlib/v/ast/ast.v | 2 +- .../checker/tests/multiple_fn_attributes.out | 4 +++ .../v/checker/tests/multiple_fn_attributes.vv | 2 ++ .../tests/trailing_comma_struct_attr.out | 6 ++++ .../tests/trailing_comma_struct_attr.vv | 4 +++ vlib/v/gen/json.v | 12 +++++-- vlib/v/parser/parser.v | 31 +++++++++++++++++-- vlib/v/parser/struct.v | 11 ++++--- vlib/v/table/atypes.v | 2 +- 10 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 vlib/v/checker/tests/multiple_fn_attributes.out create mode 100644 vlib/v/checker/tests/multiple_fn_attributes.vv create mode 100644 vlib/v/checker/tests/trailing_comma_struct_attr.out create mode 100644 vlib/v/checker/tests/trailing_comma_struct_attr.vv diff --git a/vlib/json/json_test.v b/vlib/json/json_test.v index cb22c97589..42d0251ad2 100644 --- a/vlib/json/json_test.v +++ b/vlib/json/json_test.v @@ -27,10 +27,11 @@ struct User { last_name string [json:lastName] is_registered bool [json:IsRegistered] typ int [json:'type'] + pets string [raw; json:'pet_animals'] } fn test_parse_user() { - s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true}' + s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true, "pet_animals": {"name": "Bob", "animal": "Dog"}}' u2 := json.decode(User2, s) or { exit(1) } @@ -47,11 +48,12 @@ fn test_parse_user() { assert u.nums[1] == 2 assert u.nums[2] == 3 assert u.typ == 1 + assert u.pets == '{"name":"Bob","animal":"Dog"}' } fn test_encode_user(){ - usr := User{ age: 10, nums: [1,2,3], last_name: 'Johnson', is_registered: true, typ: 0} - expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0}' + usr := User{ age: 10, nums: [1,2,3], last_name: 'Johnson', is_registered: true, typ: 0, pets: 'foo'} + expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"foo"}' out := json.encode(usr) println(out) assert out == expected diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 0404c1c771..5094c8512d 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -109,7 +109,7 @@ pub: comment Comment default_expr Expr has_default_expr bool - attr string + attrs []string mut: typ table.Type } diff --git a/vlib/v/checker/tests/multiple_fn_attributes.out b/vlib/v/checker/tests/multiple_fn_attributes.out new file mode 100644 index 0000000000..74bee779f4 --- /dev/null +++ b/vlib/v/checker/tests/multiple_fn_attributes.out @@ -0,0 +1,4 @@ +vlib/v/checker/tests/multiple_fn_attributes.v:2:1: error: multiple attributes detected + 1 | [inline;deprecated] + 2 | fn foo(name string) string {} + | ~~ \ No newline at end of file diff --git a/vlib/v/checker/tests/multiple_fn_attributes.vv b/vlib/v/checker/tests/multiple_fn_attributes.vv new file mode 100644 index 0000000000..47224e03b9 --- /dev/null +++ b/vlib/v/checker/tests/multiple_fn_attributes.vv @@ -0,0 +1,2 @@ +[inline;deprecated] +fn foo(name string) string {} \ No newline at end of file diff --git a/vlib/v/checker/tests/trailing_comma_struct_attr.out b/vlib/v/checker/tests/trailing_comma_struct_attr.out new file mode 100644 index 0000000000..ecc0493184 --- /dev/null +++ b/vlib/v/checker/tests/trailing_comma_struct_attr.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/trailing_comma_struct_attr.v:3:31: error: unexpected `]`, expecting `name` + 1 | struct User { + 2 | name string + 3 | jobs []string [json:jobss;] + | ^ + 4 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/trailing_comma_struct_attr.vv b/vlib/v/checker/tests/trailing_comma_struct_attr.vv new file mode 100644 index 0000000000..d20a3d46ab --- /dev/null +++ b/vlib/v/checker/tests/trailing_comma_struct_attr.vv @@ -0,0 +1,4 @@ +struct User { + name string + jobs []string [json:jobss;] +} \ No newline at end of file diff --git a/vlib/v/gen/json.v b/vlib/v/gen/json.v index a2a2df38d1..18c1b69139 100644 --- a/vlib/v/gen/json.v +++ b/vlib/v/gen/json.v @@ -70,13 +70,19 @@ cJSON* ${enc_fn_name}($styp val) { } info := sym.info as table.Struct for field in info.fields { - if field.attr == 'skip' { + if 'skip' in field.attrs { continue } - name := if field.attr.starts_with('json:') { field.attr[5..] } else { field.name } + mut name := field.name + for attr in field.attrs { + if attr.starts_with('json:') { + name = attr[5..] + break + } + } field_type := g.typ(field.typ) enc_name := js_enc_name(field_type) - if field.attr == 'raw' { + if 'raw' in field.attrs { dec.writeln(' res . $field.name = tos2(cJSON_PrintUnformatted(' + 'js_get(root, "$name")));') } else { // Now generate decoders for all field types in this struct diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index b934963a73..582e997277 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -310,7 +310,12 @@ pub fn (mut p Parser) top_stmt() ast.Stmt { } } .lsbr { - return p.attribute() + attrs := p.attributes() + + if attrs.len > 1 { + p.error('multiple attributes detected') + } + return attrs[0] } .key_interface { return p.interface_decl() @@ -481,8 +486,29 @@ pub fn (mut p Parser) stmt() ast.Stmt { } } -fn (mut p Parser) attribute() ast.Attr { +fn (mut p Parser) attributes() []ast.Attr { + mut attrs := []ast.Attr{} + p.check(.lsbr) + for p.tok.kind != .rsbr { + attr := p.parse_attr() + attrs << attr + if p.tok.kind != .semicolon { + expected := `;` + if p.tok.kind == .rsbr { + p.next() + break + } + + p.error('unexpected `${p.tok.kind.str()}`, expecting `${expected.str()}`') + } + p.next() + } + + return attrs +} + +fn (mut p Parser) parse_attr() ast.Attr { mut is_if_attr := false if p.tok.kind == .key_if { p.next() @@ -499,7 +525,6 @@ fn (mut p Parser) attribute() ast.Attr { p.next() } } - p.check(.rsbr) p.attr = name if is_if_attr { p.attr_ctdefine = name diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index fbf88558da..405d8b720a 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -117,9 +117,12 @@ fn (mut p Parser) struct_decl() ast.StructDecl { } has_default_expr = true } - mut attr := ast.Attr{} + mut attrs := []string{} if p.tok.kind == .lsbr { - attr = p.attribute() + parsed_attrs := p.attributes() + for attr in parsed_attrs { + attrs << attr.name + } } if p.tok.kind == .comment { comment = p.comment() @@ -132,7 +135,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { comment: comment default_expr: default_expr has_default_expr: has_default_expr - attr: attr.name + attrs: attrs } fields << table.Field{ name: field_name @@ -142,7 +145,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { is_pub: is_field_pub is_mut: is_field_mut is_global: is_field_global - attr: attr.name + attrs: attrs } // println('struct field $ti.name $field_name') } diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 0a7b59e923..e54cbdd137 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -564,7 +564,7 @@ mut: default_expr FExpr has_default_expr bool default_val string - attr string + attrs []string is_pub bool is_mut bool is_global bool