From f238890fe903af23170dbca8394e8680db14e598 Mon Sep 17 00:00:00 2001 From: Enzo Date: Tue, 4 Aug 2020 20:10:22 +0200 Subject: [PATCH] compiler: move attributes to declarations (#6026) --- vlib/v/ast/ast.v | 39 ++---- vlib/v/checker/checker.v | 15 ++- vlib/v/fmt/fmt.v | 48 +++++-- vlib/v/fmt/tests/attrs_keep.vv | 5 + vlib/v/gen/cgen.v | 18 +-- vlib/v/gen/comptime.v | 4 +- vlib/v/gen/fn.v | 15 ++- vlib/v/gen/js/js.v | 27 ++-- vlib/v/gen/json.v | 8 +- vlib/v/gen/profile.v | 33 +++-- vlib/v/parser/fn.v | 19 ++- vlib/v/parser/parser.v | 226 +++++++++++++++++---------------- vlib/v/parser/struct.v | 21 ++- vlib/v/table/attr.v | 21 +++ vlib/v/table/atypes.v | 2 +- vlib/v/table/table.v | 3 +- 16 files changed, 275 insertions(+), 229 deletions(-) create mode 100644 vlib/v/fmt/tests/attrs_keep.vv create mode 100644 vlib/v/table/attr.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 13881670a3..2327cbc75a 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -15,10 +15,10 @@ pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | C None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr -pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | CompFor | CompIf | - ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | - GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | - Return | SqlStmt | StructDecl | TypeDecl +pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | CompIf | ConstDecl | + DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | + GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return | + SqlStmt | StructDecl | TypeDecl pub type ScopeObject = ConstField | GlobalDecl | Var @@ -36,7 +36,7 @@ pub: // `{stmts}` or `unsafe {stmts}` pub struct Block { pub: - stmts []Stmt + stmts []Stmt is_unsafe bool } @@ -129,7 +129,7 @@ pub: comments []Comment default_expr Expr has_default_expr bool - attrs []string + attrs []table.Attr is_public bool pub mut: typ table.Type @@ -174,7 +174,7 @@ pub: pub_mut_pos int // pub mut: language table.Language is_union bool - attrs []string + attrs []table.Attr end_comments []Comment } @@ -224,9 +224,9 @@ pub enum ImportSymbolKind { pub struct ImportSymbol { pub: - pos token.Position - name string - kind ImportSymbolKind + pos token.Position + name string + kind ImportSymbolKind } pub struct AnonFn { @@ -253,11 +253,11 @@ pub: language table.Language no_body bool // just a definition `fn C.malloc()` is_builtin bool // this function is defined in builtin/strconv - ctdefine string // has [if myflag] tag pos token.Position body_pos token.Position file string is_generic bool + attrs []table.Attr pub mut: stmts []Stmt return_type table.Type @@ -537,6 +537,7 @@ pub enum CompIfKind { platform typecheck } + pub struct CompIf { pub: val string @@ -641,22 +642,6 @@ pub mut: expr_type table.Type } -// e.g. `[unsafe_fn]` -pub struct Attr { -pub: - name string - is_string bool // `['xxx']` -} - -pub fn (attrs []Attr) contains(attr Attr) bool { - for a in attrs { - if attr.name == a.name { - return true - } - } - return false -} - pub struct EnumVal { pub: enum_name string diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index bdcdf79d5f..ba6c44a673 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -196,10 +196,13 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool { c.warn('function `$stmt.name` $no_pub_in_main_warning', stmt.pos) } } - if stmt.ctdefine.len > 0 { - if stmt.return_type != table.void_type { - c.error('only functions that do NOT return values can have `[if $stmt.ctdefine]` tags', - stmt.pos) + if stmt.return_type != table.void_type { + for attr in stmt.attrs { + if attr.is_ctdefine { + c.error('only functions that do NOT return values can have `[if $attr.name]` tags', + stmt.pos) + break + } } } } @@ -1938,7 +1941,6 @@ fn (mut c Checker) stmt(node ast.Stmt) { ast.AssignStmt { c.assign_stmt(mut node) } - ast.Attr {} ast.Block { if node.is_unsafe { assert !c.inside_unsafe @@ -3300,8 +3302,7 @@ fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) table.Type { fn (mut c Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Position, table_name string) []table.Field { fields := info.fields.filter(it.typ in - [table.string_type, table.int_type, table.bool_type] && - 'skip' !in it.attrs) + [table.string_type, table.int_type, table.bool_type] && !it.attrs.contains('skip')) if fields.len == 0 { c.error('V orm: select: empty fields in `$table_name`', pos) } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 4cf57aea51..1cb5f701ce 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -286,13 +286,6 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { f.expr(node.expr) f.writeln('') } - ast.Attr { - if node.is_string { - f.writeln("['$node.name']") - } else { - f.writeln('[$node.name]') - } - } ast.Block { if node.is_unsafe { f.write('unsafe ') @@ -593,6 +586,7 @@ pub fn (mut f Fmt) type_decl(node ast.TypeDecl) { } pub fn (mut f Fmt) struct_decl(node ast.StructDecl) { + f.attrs(node.attrs) if node.is_pub { f.write('pub ') } @@ -630,9 +624,7 @@ pub fn (mut f Fmt) struct_decl(node ast.StructDecl) { f.write('\t$field.name ') f.write(strings.repeat(` `, max - field.name.len)) f.write(f.type_to_str(field.typ)) - if field.attrs.len > 0 { - f.write(' [' + field.attrs.join(';') + ']') - } + f.inline_attrs(field.attrs) if field.has_default_expr { f.write(' = ') f.prefix_expr_cast_expr(field.default_expr) @@ -663,9 +655,7 @@ pub fn (mut f Fmt) struct_decl(node ast.StructDecl) { } f.write(strings.repeat(` `, max - field.name.len - comments_len)) f.write(f.type_to_str(field.typ)) - if field.attrs.len > 0 { - f.write(' [' + field.attrs.join(';') + ']') - } + f.inline_attrs(field.attrs) if field.has_default_expr { f.write(' = ') f.prefix_expr_cast_expr(field.default_expr) @@ -1123,6 +1113,37 @@ pub fn (mut f Fmt) or_expr(or_block ast.OrExpr) { } } +fn (mut f Fmt) attrs(attrs []table.Attr) { + for attr in attrs { + f.write('[') + if attr.is_ctdefine { + f.write('if ') + } + if attr.is_string { + f.write("'") + } + f.write(attr.name) + if attr.is_string { + f.write("'") + } + f.writeln(']') + } +} + +fn (mut f Fmt) inline_attrs(attrs []table.Attr) { + if attrs.len == 0 { + return + } + f.write(' [') + for i, attr in attrs { + if i > 0 { + f.write(';') + } + f.write(attr.name) + } + f.write(']') +} + enum CommentsLevel { keep indent @@ -1181,6 +1202,7 @@ pub fn (mut f Fmt) comments(comments []ast.Comment, options CommentsOptions) { pub fn (mut f Fmt) fn_decl(node ast.FnDecl) { // println('$it.name find_comment($it.pos.line_nr)') // f.find_comment(it.pos.line_nr) + f.attrs(node.attrs) f.write(node.stringify(f.table, f.cur_mod)) // `Expr` instead of `ast.Expr` in mod ast if node.language == .v { f.writeln(' {') diff --git a/vlib/v/fmt/tests/attrs_keep.vv b/vlib/v/fmt/tests/attrs_keep.vv new file mode 100644 index 0000000000..78346edcc0 --- /dev/null +++ b/vlib/v/fmt/tests/attrs_keep.vv @@ -0,0 +1,5 @@ +[inline] +[if debug] +fn keep_attributes() { + println('hi !') +} diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 22aabc1ef7..032d97fc1e 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -83,7 +83,6 @@ mut: is_json_fn bool // inside json.encode() json_types []string // to avoid json gen duplicates pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name - attrs []string // attributes before next decl stmt is_builtin_mod bool hotcode_fn_names []string // cur_fn ast.FnDecl @@ -629,10 +628,6 @@ fn (mut g Gen) stmts(stmts []ast.Stmt) { if g.inside_ternary > 0 && i < stmts.len - 1 { g.write(',') } - // clear attrs on next non Attr stmt - if stmt !is ast.Attr { - g.attrs = [] - } } g.indent-- if g.inside_ternary > 0 { @@ -674,10 +669,6 @@ fn (mut g Gen) stmt(node ast.Stmt) { ast.AssignStmt { g.gen_assign_stmt(node) } - ast.Attr { - g.attrs << node.name - g.writeln('// Attr: [$node.name]') - } ast.Block { if node.is_unsafe { g.writeln('{ // Unsafe block') @@ -868,6 +859,9 @@ fn (mut g Gen) stmt(node ast.Stmt) { } ast.StructDecl { name := if node.language == .c { util.no_dots(node.name) } else { c_name(node.name) } + // TODO For some reason, build fails with autofree with this line + // as it's only informative, comment it for now + // g.gen_attrs(node.attrs) // g.writeln('typedef struct {') // for field in it.fields { // field_type_sym := g.table.get_type_symbol(field.typ) @@ -1070,6 +1064,12 @@ fn ctoslit(s string) string { return 'tos_lit("' + cestring(s) + '")' } +fn (mut g Gen) gen_attrs(attrs []table.Attr) { + for attr in attrs { + g.writeln('// Attr: [$attr.name]') + } +} + fn (mut g Gen) gen_assert_stmt(a ast.AssertStmt) { g.writeln('// assert') g.inside_ternary++ diff --git a/vlib/v/gen/comptime.v b/vlib/v/gen/comptime.v index f03c43f368..146a9062cd 100644 --- a/vlib/v/gen/comptime.v +++ b/vlib/v/gen/comptime.v @@ -177,7 +177,7 @@ fn (mut g Gen) comp_for(node ast.CompFor) { } else { mut attrs := []string{} for attrib in method.attrs { - attrs << 'tos_lit("$attrib")' + attrs << 'tos_lit("$attrib.name")' } g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' + attrs.join(', ') + '}));') @@ -212,7 +212,7 @@ fn (mut g Gen) comp_for(node ast.CompFor) { } else { mut attrs := []string{} for attrib in field.attrs { - attrs << 'tos_lit("$attrib")' + attrs << 'tos_lit("$attrib.name")' } g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' + attrs.join(', ') + '}));') diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index f031a24006..040b33aa02 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -11,6 +11,9 @@ pub fn kek_cheburek() { } fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) { + // TODO For some reason, build fails with autofree with this line + // as it's only informative, comment it for now + // g.gen_attrs(it.attrs) if it.language == .c { // || it.no_body { return @@ -33,9 +36,9 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) { } // g.cur_fn = it fn_start_pos := g.out.len - msvc_attrs := g.write_fn_attrs() + msvc_attrs := g.write_fn_attrs(it.attrs) // Live - is_livefn := 'live' in g.attrs + is_livefn := it.attrs.contains('live') is_livemain := g.pref.is_livemain && is_livefn is_liveshared := g.pref.is_liveshared && is_livefn is_livemode := g.pref.is_livemain || g.pref.is_liveshared @@ -142,7 +145,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) { } // Profiling mode? Start counting at the beginning of the function (save current time). if g.pref.is_prof { - g.profile_fn(it.name) + g.profile_fn(it) } g.stmts(it.stmts) // @@ -711,10 +714,10 @@ fn (g &Gen) fileis(s string) bool { return g.file.path.contains(s) } -fn (mut g Gen) write_fn_attrs() string { +fn (mut g Gen) write_fn_attrs(attrs []table.Attr) string { mut msvc_attrs := '' - for attr in g.attrs { - match attr { + for attr in attrs { + match attr.name { 'inline' { g.write('inline ') } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index fa59ec4990..d8e3511109 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -10,11 +10,13 @@ import v.depgraph const ( // https://ecma-international.org/ecma-262/#sec-reserved-words js_reserved = ['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', - 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'finally', 'for', 'function', - 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'package', 'private', - 'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof', - 'var', 'void', 'while', 'with', 'yield'] - tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t'] + 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'finally', 'for', 'function', 'if', + 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'package', 'private', 'protected', + 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', + 'while', 'with', 'yield'] + tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t', + '\t\t\t\t\t\t\t\t', + ] ) struct JsGen { @@ -425,9 +427,6 @@ fn (mut g JsGen) stmt(node ast.Stmt) { ast.AssignStmt { g.gen_assign_stmt(node) } - ast.Attr { - g.gen_attr(node) - } ast.Block { g.gen_block(node) g.writeln('') @@ -740,8 +739,10 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) { } } -fn (mut g JsGen) gen_attr(it ast.Attr) { - g.writeln('/* [$it.name] */') +fn (mut g JsGen) gen_attrs(attrs []table.Attr) { + for attr in attrs { + g.writeln('/* [$attr.name] */') + } } fn (mut g JsGen) gen_block(it ast.Block) { @@ -830,6 +831,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { g.fn_decl = &it has_go := fn_has_go(it) is_main := it.name == 'main.main' + g.gen_attrs(it.attrs) if is_main { // there is no concept of main in JS but we do have iife g.writeln('/* program entry point */') @@ -1042,7 +1044,10 @@ fn (mut g JsGen) gen_hash_stmt(it ast.HashStmt) { } fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { - if node.name.starts_with('JS.') { return } + if node.name.starts_with('JS.') { + return + } + g.gen_attrs(node.attrs) g.doc.gen_fac_fn(node.fields) g.write('function ${g.js_name(node.name)}({ ') for i, field in node.fields { diff --git a/vlib/v/gen/json.v b/vlib/v/gen/json.v index 9b9efa9dd5..505ce06a63 100644 --- a/vlib/v/gen/json.v +++ b/vlib/v/gen/json.v @@ -78,18 +78,18 @@ $enc_fn_dec { } info := sym.info as table.Struct for field in info.fields { - if 'skip' in field.attrs { + if field.attrs.contains('skip') { continue } mut name := field.name for attr in field.attrs { - if attr.starts_with('json:') { - name = attr[5..] + if attr.name.starts_with('json:') { + name = attr.name[5..] break } } field_type := g.typ(field.typ) - if 'raw' in field.attrs { + if field.attrs.contains('raw') { dec.writeln(' res . ${c_name(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/gen/profile.v b/vlib/v/gen/profile.v index 3e2e007bf1..693463ec20 100644 --- a/vlib/v/gen/profile.v +++ b/vlib/v/gen/profile.v @@ -1,28 +1,35 @@ module gen -pub struct ProfileCounterMeta{ - fn_name string - vpc_name string +import v.ast + +pub struct ProfileCounterMeta { + fn_name string + vpc_name string vpc_calls string } -fn (mut g Gen) profile_fn(fn_name string){ - if g.pref.profile_no_inline && 'inline' in g.attrs { +fn (mut g Gen) profile_fn(fn_decl ast.FnDecl) { + if g.pref.profile_no_inline && fn_decl.attrs.contains('inline') { g.defer_profile_code = '' return } + fn_name := fn_decl.name if fn_name.starts_with('time.vpc_now') { g.defer_profile_code = '' } else { measure_fn_name := if g.pref.os == .mac { 'time__vpc_now_darwin' } else { 'time__vpc_now' } - fn_profile_counter_name := 'vpc_${g.last_fn_c_name}' + fn_profile_counter_name := 'vpc_$g.last_fn_c_name' fn_profile_counter_name_calls := '${fn_profile_counter_name}_calls' g.writeln('') - g.writeln('\tdouble _PROF_FN_START = ${measure_fn_name}(); ${fn_profile_counter_name_calls}++; // $fn_name') + g.writeln('\tdouble _PROF_FN_START = ${measure_fn_name}(); $fn_profile_counter_name_calls++; // $fn_name') g.writeln('') - g.defer_profile_code = '\t${fn_profile_counter_name} += ${measure_fn_name}() - _PROF_FN_START;' - g.pcs_declarations.writeln('double ${fn_profile_counter_name} = 0.0; u64 ${fn_profile_counter_name_calls} = 0;') - g.pcs << ProfileCounterMeta{ g.last_fn_c_name, fn_profile_counter_name, fn_profile_counter_name_calls } + g.defer_profile_code = '\t$fn_profile_counter_name += ${measure_fn_name}() - _PROF_FN_START;' + g.pcs_declarations.writeln('double $fn_profile_counter_name = 0.0; u64 $fn_profile_counter_name_calls = 0;') + g.pcs << ProfileCounterMeta{ + fn_name: g.last_fn_c_name + vpc_name: fn_profile_counter_name + vpc_calls: fn_profile_counter_name_calls + } } } @@ -31,13 +38,13 @@ pub fn (mut g Gen) gen_vprint_profile_stats() { fstring := '"%14lu %14.3fms %14.0fns %s \\n"' if g.pref.profile_file == '-' { for pc_meta in g.pcs { - g.pcs_declarations.writeln('\tif (${pc_meta.vpc_calls}) printf($fstring, ${pc_meta.vpc_calls}, ${pc_meta.vpc_name}/1000000.0, ${pc_meta.vpc_name}/${pc_meta.vpc_calls}, "${pc_meta.fn_name}" );') + g.pcs_declarations.writeln('\tif ($pc_meta.vpc_calls) printf($fstring, $pc_meta.vpc_calls, $pc_meta.vpc_name/1000000.0, $pc_meta.vpc_name/$pc_meta.vpc_calls, "$pc_meta.fn_name" );') } } else { g.pcs_declarations.writeln('\tFILE * fp;') - g.pcs_declarations.writeln('\tfp = fopen ("${g.pref.profile_file}", "w+");') + g.pcs_declarations.writeln('\tfp = fopen ("$g.pref.profile_file", "w+");') for pc_meta in g.pcs { - g.pcs_declarations.writeln('\tif (${pc_meta.vpc_calls}) fprintf(fp, $fstring, ${pc_meta.vpc_calls}, ${pc_meta.vpc_name}/1000000.0, ${pc_meta.vpc_name}/${pc_meta.vpc_calls}, "${pc_meta.fn_name}" );') + g.pcs_declarations.writeln('\tif ($pc_meta.vpc_calls) fprintf(fp, $fstring, $pc_meta.vpc_calls, $pc_meta.vpc_name/1000000.0, $pc_meta.vpc_name/$pc_meta.vpc_calls, "$pc_meta.fn_name" );') } g.pcs_declarations.writeln('\tfclose(fp);') } diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 092dffac4e..f90ebde7dc 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -131,8 +131,8 @@ pub fn (mut p Parser) call_args() []ast.CallArg { fn (mut p Parser) fn_decl() ast.FnDecl { p.top_level_statement_start() start_pos := p.tok.position() - is_deprecated := 'deprecated' in p.attrs - mut is_unsafe := 'unsafe_fn' in p.attrs + is_deprecated := p.attrs.contains('deprecated') + mut is_unsafe := p.attrs.contains('unsafe_fn') is_pub := p.tok.kind == .key_pub if is_pub { p.next() @@ -141,7 +141,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { p.open_scope() // C. || JS. language := if p.tok.kind == .name && p.tok.lit == 'C' { - is_unsafe = !('trusted_fn' in p.attrs) + is_unsafe = !p.attrs.contains('trusted_fn') table.Language.c } else if p.tok.kind == .name && p.tok.lit == 'JS' { table.Language.js @@ -244,12 +244,11 @@ fn (mut p Parser) fn_decl() ast.FnDecl { mut end_pos := p.prev_tok.position() // Return type mut return_type := table.void_type - if p.tok.kind.is_start_of_type() || (p.tok.kind == .key_fn && - p.tok.line_nr == p.prev_tok.line_nr) { + if p.tok.kind.is_start_of_type() || + (p.tok.kind == .key_fn && p.tok.line_nr == p.prev_tok.line_nr) { end_pos = p.tok.position() return_type = p.parse_type() } - ctdefine := p.attr_ctdefine // Register if is_method { mut type_sym := p.table.get_type_symbol(rec_type) @@ -263,7 +262,6 @@ fn (mut p Parser) fn_decl() ast.FnDecl { is_pub: is_pub is_deprecated: is_deprecated is_unsafe: is_unsafe - ctdefine: ctdefine mod: p.mod attrs: p.attrs }) @@ -288,8 +286,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl { is_pub: is_pub is_deprecated: is_deprecated is_unsafe: is_unsafe - ctdefine: ctdefine mod: p.mod + attrs: p.attrs language: language }) } @@ -328,7 +326,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { body_pos: body_start_pos file: p.file_name is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts - ctdefine: ctdefine + attrs: p.attrs } } @@ -394,8 +392,7 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) { // `int, int, string` (no names, just types) argname := if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() { p.prepend_mod(p.tok.lit) } else { p.tok.lit } types_only := p.tok.kind in [.amp, .ellipsis, .key_fn] || - (p.peek_tok.kind == .comma && p.table.known_type(argname)) || - p.peek_tok.kind == .rpar + (p.peek_tok.kind == .comma && p.table.known_type(argname)) || p.peek_tok.kind == .rpar // TODO copy pasta, merge 2 branches if types_only { // p.warn('types only') diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index ae09ec1d78..238c665ad5 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -36,8 +36,7 @@ mut: pref &pref.Preferences builtin_mod bool // are we in the `builtin` module? mod string // current module name - attrs []string // attributes before next decl stmt - attr_ctdefine string + attrs []table.Attr // attributes before next decl stmt expr_mod string // for constructing full type names in parse_type() scope &ast.Scope global_scope &ast.Scope @@ -183,13 +182,9 @@ fn (mut p Parser) parse() ast.File { break } // println('stmt at ' + p.tok.str()) - stmt := p.top_stmt() - // clear the attribtes at the end of next non Attr top level stmt - if stmt !is ast.Attr { - p.attrs = [] - p.attr_ctdefine = '' - } - stmts << stmt + stmts << p.top_stmt() + // clear the attributes after each statement + p.attrs = [] } // println('nr stmts = $stmts.len') // println(stmts[0]) @@ -408,96 +403,102 @@ pub fn (mut p Parser) top_stmt() ast.Stmt { tok_pos := p.tok.position() eprintln('parsing file: ${p.file_name:-30} | tok.kind: ${p.tok.kind:-10} | tok.lit: ${p.tok.lit:-10} | tok_pos: ${tok_pos.str():-45} | top_stmt') } - match p.tok.kind { - .key_pub { - match p.peek_tok.kind { - .key_const { - return p.const_decl() + for { + match p.tok.kind { + .key_pub { + match p.peek_tok.kind { + .key_const { + return p.const_decl() + } + .key_fn { + return p.fn_decl() + } + .key_struct, .key_union { + return p.struct_decl() + } + .key_interface { + return p.interface_decl() + } + .key_enum { + return p.enum_decl() + } + .key_type { + return p.type_decl() + } + else { + p.error('wrong pub keyword usage') + return ast.Stmt{} + } } - .key_fn { - return p.fn_decl() - } - .key_struct, .key_union { - return p.struct_decl() - } - .key_interface { - return p.interface_decl() - } - .key_enum { - return p.enum_decl() - } - .key_type { - return p.type_decl() - } - else { - p.error('wrong pub keyword usage') + } + .lsbr { + // attrs are stores in `p.attrs()` + p.attributes() + continue + } + .key_interface { + return p.interface_decl() + } + .key_import { + p.error_with_pos('`import x` can only be declared at the beginning of the file', + p.tok.position()) + return p.import_stmt() + } + .key_global { + return p.global_decl() + } + .key_const { + return p.const_decl() + } + .key_fn { + return p.fn_decl() + } + .key_struct { + return p.struct_decl() + } + .dollar { + return p.comp_if() + } + .hash { + return p.hash() + } + .key_type { + return p.type_decl() + } + .key_enum { + return p.enum_decl() + } + .key_union { + return p.struct_decl() + } + .comment { + return p.comment_stmt() + } + else { + if p.pref.is_script && !p.pref.is_test { + mut stmts := []ast.Stmt{} + for p.tok.kind != .eof { + stmts << p.stmt(false) + } + return ast.FnDecl{ + name: 'main.main' + mod: 'main' + stmts: stmts + file: p.file_name + return_type: table.void_type + } + } else if p.pref.is_fmt { + return p.stmt(false) + } else { + p.error('bad top level statement ' + p.tok.str()) return ast.Stmt{} } } } - .lsbr { - attrs := p.attributes(true) - return attrs[0] - } - .key_interface { - return p.interface_decl() - } - .key_import { - p.error_with_pos('`import x` can only be declared at the beginning of the file', - p.tok.position()) - return p.import_stmt() - } - .key_global { - return p.global_decl() - } - .key_const { - return p.const_decl() - } - .key_fn { - return p.fn_decl() - } - .key_struct { - return p.struct_decl() - } - .dollar { - return p.comp_if() - } - .hash { - return p.hash() - } - .key_type { - return p.type_decl() - } - .key_enum { - return p.enum_decl() - } - .key_union { - return p.struct_decl() - } - .comment { - return p.comment_stmt() - } - else { - if p.pref.is_script && !p.pref.is_test { - mut stmts := []ast.Stmt{} - for p.tok.kind != .eof { - stmts << p.stmt(false) - } - return ast.FnDecl{ - name: 'main.main' - mod: 'main' - stmts: stmts - file: p.file_name - return_type: table.void_type - } - } else if p.pref.is_fmt { - return p.stmt(false) - } else { - p.error('bad top level statement ' + p.tok.str()) - return ast.Stmt{} - } - } } + // TODO remove dummy return statement + // the compiler complains if it's not there + return ast.Stmt{} } // TODO [if vfmt] @@ -691,40 +692,43 @@ fn (mut p Parser) expr_list() ([]ast.Expr, []ast.Comment) { } // when is_top_stmt is true attrs are added to p.attrs -fn (mut p Parser) attributes(is_top_stmt bool) []ast.Attr { - mut attrs := []ast.Attr{} +fn (mut p Parser) attributes() { p.check(.lsbr) + mut has_ctdefine := false for p.tok.kind != .rsbr { start_pos := p.tok.position() attr := p.parse_attr() - if attr in attrs || (is_top_stmt && attr.name in p.attrs) { + if p.attrs.contains(attr.name) { p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position())) } - if is_top_stmt { - p.attrs << attr.name + if attr.is_ctdefine { + if has_ctdefine { + p.error_with_pos('only one `[if flag]` may be applied at a time `$attr.name`', + start_pos.extend(p.prev_tok.position())) + } else { + has_ctdefine = true + } } - attrs << attr + p.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.error('unexpected `$p.tok.kind.str()`, expecting `;`') } p.next() } - if attrs.len == 0 { + if p.attrs.len == 0 { p.error_with_pos('attributes cannot be empty', p.prev_tok.position().extend(p.tok.position())) } - return attrs } -fn (mut p Parser) parse_attr() ast.Attr { - mut is_if_attr := false +fn (mut p Parser) parse_attr() table.Attr { + mut is_ctdefine := false if p.tok.kind == .key_if { p.next() - is_if_attr = true + is_ctdefine = true } mut name := '' is_string := p.tok.kind == .string @@ -744,12 +748,10 @@ fn (mut p Parser) parse_attr() ast.Attr { } } } - if is_if_attr { - p.attr_ctdefine = name - } - return ast.Attr{ + return table.Attr{ name: name is_string: is_string + is_ctdefine: is_ctdefine } } @@ -1663,8 +1665,8 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { } p.top_level_statement_end() p.check(.rcbr) - is_flag := 'flag' in p.attrs - is_multi_allowed := '_allow_multiple_values' in p.attrs + is_flag := p.attrs.contains('flag') + is_multi_allowed := p.attrs.contains('_allow_multiple_values') if is_flag { if fields.len > 32 { p.error('when an enum is used as bit field, it must have a max of 32 fields') diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 7b044392d5..c306d9a51a 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -10,6 +10,8 @@ import v.util fn (mut p Parser) struct_decl() ast.StructDecl { p.top_level_statement_start() + // save attributes, they will be changed later in fields + attrs := p.attrs start_pos := p.tok.position() is_pub := p.tok.kind == .key_pub if is_pub { @@ -32,7 +34,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl { p.next() // C || JS p.next() // . } - is_typedef := 'typedef' in p.attrs name_pos := p.tok.position() mut name := p.check_name() if name.len == 1 && name[0].is_capital() { @@ -168,12 +169,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl { break } } - mut attrs := []string{} if p.tok.kind == .lsbr { - parsed_attrs := p.attributes(false) - for attr in parsed_attrs { - attrs << attr.name - } + // attrs are stored in `p.attrs` + p.attributes() } mut default_expr := ast.Expr{} mut has_default_expr := false @@ -198,7 +196,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { comments: comments default_expr: default_expr has_default_expr: has_default_expr - attrs: attrs + attrs: p.attrs is_public: is_field_pub } fields << table.Field{ @@ -209,8 +207,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl { is_pub: is_field_pub is_mut: is_field_mut is_global: is_field_global - attrs: attrs + attrs: p.attrs } + p.attrs = [] // println('struct field $ti.name $field_name') } p.top_level_statement_end() @@ -228,9 +227,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl { name: name info: table.Struct{ fields: fields - is_typedef: is_typedef + is_typedef: attrs.contains('typedef') is_union: is_union - is_ref_only: 'ref_only' in p.attrs + is_ref_only: attrs.contains('ref_only') generic_types: generic_types } mod: p.mod @@ -260,7 +259,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { pub_mut_pos: pub_mut_pos language: language is_union: is_union - attrs: p.attrs + attrs: attrs end_comments: end_comments } } diff --git a/vlib/v/table/attr.v b/vlib/v/table/attr.v new file mode 100644 index 0000000000..ad97d5d0c5 --- /dev/null +++ b/vlib/v/table/attr.v @@ -0,0 +1,21 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module table + +// e.g. `[unsafe_fn]` +pub struct Attr { +pub: + name string + is_string bool // `['xxx']` + is_ctdefine bool // `[if flag]` +} + +pub fn (attrs []Attr) contains(str string) bool { + for a in attrs { + if a.name == str { + return true + } + } + return false +} diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 67b1ee5cdf..608ae4e1c0 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -687,7 +687,7 @@ pub mut: default_expr FExpr has_default_expr bool default_val string - attrs []string + attrs []Attr is_pub bool is_mut bool is_global bool diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index f93c001ae0..f4d785774d 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -33,7 +33,7 @@ pub: is_placeholder bool mod string ctdefine string // compile time define. myflag, when [if myflag] tag - attrs []string + attrs []Attr pub mut: name string } @@ -527,7 +527,6 @@ pub fn (table &Table) sumtype_has_variant(parent, variant Type) bool { } } return false - } pub fn (table &Table) known_type_names() []string {