diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 29ac7d5ed2..b37e470d26 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -111,6 +111,7 @@ pub struct EnumData { pub: name string value i64 + attrs []string } // FieldData holds information about a field. Fields reside on structs. diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 96c7ba2f47..b5bc6ed2b9 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1296,6 +1296,7 @@ pub: comments []Comment // comment after Enumfield in the same line next_comments []Comment // comments between current EnumField and next EnumField has_expr bool // true, when .expr has a value + attrs []Attr pub mut: expr Expr // the value of current EnumField; 123 in `ename = 123` } diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 8ff361067a..32d9621dca 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -512,7 +512,7 @@ pub fn (x Expr) str() string { return "'${x.val}'" } TypeNode { - return 'TypeNode(${x.typ})' + return 'TypeNode(${global_table.type_str(x.typ)})' } TypeOf { if x.is_type { diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index d1800650e6..19cb4683e2 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -193,6 +193,7 @@ pub: is_multi_allowed bool uses_exprs bool typ Type + attrs map[string][]Attr } [minify] diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 875a7c739b..9234baf202 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -986,6 +986,10 @@ pub fn (mut f Fmt) enum_decl(node ast.EnumDecl) { f.write(' = ') f.expr(field.expr) } + if field.attrs.len > 0 { + f.write(' ') + f.single_line_attrs(field.attrs, inline: true) + } f.comments(field.comments, inline: true, has_nl: false, level: .indent) f.writeln('') f.comments(field.next_comments, inline: false, has_nl: true, level: .indent) diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index a5462350ef..c49fc929b2 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -942,6 +942,15 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { } else { g.writeln('${g.typ(g.comptime_for_field_type)}__${g.comptime_enum_field_value};') } + enum_attrs := sym.info.attrs[val] + if enum_attrs.len == 0 { + g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') + } else { + attrs := cgen_attrs(enum_attrs) + g.writeln( + '\t${node.val_var}.attrs = new_array_from_c_array(${attrs.len}, ${attrs.len}, sizeof(string), _MOV((string[${attrs.len}]){' + + attrs.join(', ') + '}));\n') + } g.stmts(node.stmts) g.writeln('}') i++ diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index 941d66b7aa..9072cd23f5 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -205,7 +205,15 @@ fn (mut g Gen) gen_enum_to_str(utyp ast.Type, sym ast.TypeSymbol, enum_var strin enc.writeln('${ident}switch (${enum_var}) {') for val in (sym.info as ast.Enum).vals { enc.write_string('${ident}\tcase ${enum_prefix}${val}:\t') - enc.writeln('${result_var} = json__encode_string(_SLIT("${val}")); break;') + // read [json:] attr from the Enum value + attr := g.table.enum_decls[sym.name].fields.filter(it.name == val)[0].attrs.find_first('json') or { + ast.Attr{} + } + if attr.has_arg { + enc.writeln('${result_var} = json__encode_string(_SLIT("${attr.arg}")); break;') + } else { + enc.writeln('${result_var} = json__encode_string(_SLIT("${val}")); break;') + } } enc.writeln('${ident}}') } @@ -215,11 +223,19 @@ fn (mut g Gen) gen_str_to_enum(utyp ast.Type, sym ast.TypeSymbol, val_var string enum_prefix := g.gen_enum_prefix(utyp.clear_flag(.option)) is_option := utyp.has_flag(.option) for k, val in (sym.info as ast.Enum).vals { - if k == 0 { - dec.write_string('${ident}if (string__eq(_SLIT("${val}"), ${val_var}))\t') - } else { - dec.write_string('${ident}else if (string__eq(_SLIT("${val}"), ${val_var}))\t') + // read [json:] attr from the Enum value + attr := g.table.enum_decls[sym.name].fields.filter(it.name == val)[0].attrs.find_first('json') or { + ast.Attr{} } + if k == 0 { + dec.write_string('${ident}if (string__eq(_SLIT("${val}"), ${val_var})') + } else { + dec.write_string('${ident}else if (string__eq(_SLIT("${val}"), ${val_var})') + } + if attr.has_arg { + dec.write_string(' || string__eq(_SLIT("${attr.arg}"), ${val_var})') + } + dec.write_string(')\t') if is_option { base_typ := g.base_type(utyp) dec.writeln('_option_ok(&(${base_typ}[]){ ${enum_prefix}${val} }, ${result_var}, sizeof(${base_typ}));') diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 03615f9aad..4bdd4bbcf8 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -3922,6 +3922,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { // mut default_exprs := []ast.Expr{} mut fields := []ast.EnumField{} mut uses_exprs := false + mut enum_attrs := map[string][]ast.Attr{} for p.tok.kind != .eof && p.tok.kind != .rcbr { pos := p.tok.pos() val := p.check_name() @@ -3935,6 +3936,13 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { has_expr = true uses_exprs = true } + mut attrs := []ast.Attr{} + if p.tok.kind == .lsbr { + p.attributes() + attrs << p.attrs + enum_attrs[val] = attrs + p.attrs = [] + } fields << ast.EnumField{ name: val pos: pos @@ -3942,6 +3950,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { has_expr: has_expr comments: p.eat_comments(same_line: true) next_comments: p.eat_comments() + attrs: attrs } } p.top_level_statement_end() @@ -3983,6 +3992,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { is_multi_allowed: is_multi_allowed uses_exprs: uses_exprs typ: enum_type + attrs: enum_attrs } is_pub: is_pub }) diff --git a/vlib/v/tests/enum_attr_test.v b/vlib/v/tests/enum_attr_test.v new file mode 100644 index 0000000000..d2595214b3 --- /dev/null +++ b/vlib/v/tests/enum_attr_test.v @@ -0,0 +1,37 @@ +import json + +enum Foo { + yay [json: 'A'; yay] + foo [foo; json: 'B'] +} + +struct FooStruct { + item Foo +} + +fn test_comptime() { + $for f in Foo.values { + println(f) + if f.value == Foo.yay { + assert f.attrs[0] == 'json: A' + assert f.attrs[1] == 'yay' + } + if f.value == Foo.foo { + assert f.attrs[1] == 'json: B' + assert f.attrs[0] == 'foo' + } + } +} + +fn test_json_encode() { + assert dump(json.encode(Foo.yay)) == '"A"' + assert dump(json.encode(Foo.foo)) == '"B"' + + assert dump(json.encode(FooStruct{ item: Foo.yay })) == '{"item":"A"}' + assert dump(json.encode(FooStruct{ item: Foo.foo })) == '{"item":"B"}' +} + +fn test_json_decode() { + dump(json.decode(FooStruct, '{"item": "A"}')!) + dump(json.decode(FooStruct, '{"item": "B"}')!) +}