diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 434652ecd5..27dbd12faf 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -21,7 +21,7 @@ pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDe GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return | SqlStmt | StructDecl | TypeDecl -pub type ScopeObject = ConstField | GlobalDecl | Var +pub type ScopeObject = ConstField | GlobalField | Var pub struct Type { pub: @@ -346,7 +346,7 @@ pub mut: is_changed bool // to detect mutable vars that are never changed } -pub struct GlobalDecl { +pub struct GlobalField { pub: name string expr Expr @@ -354,6 +354,15 @@ pub: pos token.Position pub mut: typ table.Type + comments []Comment +} + +pub struct GlobalDecl { +pub: + pos token.Position +pub mut: + fields []GlobalField + end_comments []Comment } pub struct File { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 3a11f8f402..9d27f1557b 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -38,6 +38,7 @@ pub mut: const_decl string const_deps []string const_names []string + global_names []string locked_names []string // vars that are currently locked rlocked_names []string // vars that are currently read-locked pref &pref.Preferences // Preferences shared from V struct @@ -1954,7 +1955,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { if left_type != 0 { match mut left.obj as v { ast.Var { v.typ = left_type } - ast.GlobalDecl { v.typ = left_type } + ast.GlobalField { v.typ = left_type } else {} } /* @@ -2372,7 +2373,13 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.in_for_count-- } ast.GlobalDecl { - c.check_valid_snake_case(node.name, 'global name', node.pos) + for field in node.fields { + c.check_valid_snake_case(field.name, 'global name', field.pos) + if field.name in c.global_names { + c.error('duplicate global `$field.name`', field.pos) + } + c.global_names << field.name + } } ast.GoStmt { if node.call_expr !is ast.CallExpr { @@ -2865,7 +2872,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type { start_scope := c.file.scope.innermost(ident.pos.pos) if obj1 := start_scope.find(ident.name) { match mut obj1 as obj { - ast.GlobalDecl { + ast.GlobalField { ident.kind = .global ident.info = ast.IdentVar{ typ: obj.typ diff --git a/vlib/v/checker/tests/globals/assign_no_type.out b/vlib/v/checker/tests/globals/assign_no_type.out new file mode 100644 index 0000000000..abc2238987 --- /dev/null +++ b/vlib/v/checker/tests/globals/assign_no_type.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/globals/assign_no_type.vv:1:21: error: global assign must have a type and value, use `__global ( name = type(value) )` or `__global ( name type )` + 1 | __global ( test = 0 ) + | ^ diff --git a/vlib/v/checker/tests/globals/assign_no_type.vv b/vlib/v/checker/tests/globals/assign_no_type.vv new file mode 100644 index 0000000000..235c6527e0 --- /dev/null +++ b/vlib/v/checker/tests/globals/assign_no_type.vv @@ -0,0 +1 @@ +__global ( test = 0 ) \ No newline at end of file diff --git a/vlib/v/checker/tests/globals/assign_no_value.out b/vlib/v/checker/tests/globals/assign_no_value.out new file mode 100644 index 0000000000..7270daa7fe --- /dev/null +++ b/vlib/v/checker/tests/globals/assign_no_value.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/globals/assign_no_value.vv:1:23: error: global assign must have a type and value, use `__global ( name = type(value) )` or `__global ( name type )` + 1 | __global ( test = int ) + | ^ diff --git a/vlib/v/checker/tests/globals/assign_no_value.vv b/vlib/v/checker/tests/globals/assign_no_value.vv new file mode 100644 index 0000000000..352847e01d --- /dev/null +++ b/vlib/v/checker/tests/globals/assign_no_value.vv @@ -0,0 +1 @@ +__global ( test = int ) \ No newline at end of file diff --git a/vlib/v/checker/tests/globals/incorrect_name_global.out b/vlib/v/checker/tests/globals/incorrect_name_global.out index 150126177b..05d2e264ae 100644 --- a/vlib/v/checker/tests/globals/incorrect_name_global.out +++ b/vlib/v/checker/tests/globals/incorrect_name_global.out @@ -1,3 +1,3 @@ -vlib/v/checker/tests/globals/incorrect_name_global.vv:1:1: error: global name `A` cannot contain uppercase letters, use snake_case instead - 1 | __global A int = 1 - | ~~~~~~~~~~ +vlib/v/checker/tests/globals/incorrect_name_global.vv:1:12: error: global name `A` cannot contain uppercase letters, use snake_case instead + 1 | __global ( A = int(1) ) + | ^ diff --git a/vlib/v/checker/tests/globals/incorrect_name_global.vv b/vlib/v/checker/tests/globals/incorrect_name_global.vv index 0c957ce0e5..c9550d80cf 100644 --- a/vlib/v/checker/tests/globals/incorrect_name_global.vv +++ b/vlib/v/checker/tests/globals/incorrect_name_global.vv @@ -1 +1 @@ -__global A int = 1 +__global ( A = int(1) ) diff --git a/vlib/v/checker/tests/globals/no_type.out b/vlib/v/checker/tests/globals/no_type.out new file mode 100644 index 0000000000..b69a8c175f --- /dev/null +++ b/vlib/v/checker/tests/globals/no_type.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/globals/no_type.vv:1:17: error: bad type syntax + 1 | __global ( test ) + | ^ diff --git a/vlib/v/checker/tests/globals/no_type.vv b/vlib/v/checker/tests/globals/no_type.vv new file mode 100644 index 0000000000..264ed9d555 --- /dev/null +++ b/vlib/v/checker/tests/globals/no_type.vv @@ -0,0 +1 @@ +__global ( test ) diff --git a/vlib/v/checker/tests/globals_error.out b/vlib/v/checker/tests/globals_error.out index b16fd93ec0..c5d0123499 100644 --- a/vlib/v/checker/tests/globals_error.out +++ b/vlib/v/checker/tests/globals_error.out @@ -1,6 +1,6 @@ vlib/v/checker/tests/globals_error.vv:2:1: error: use `v --enable-globals ...` to enable globals 1 | - 2 | __global rfcnt int + 2 | __global ( rfcnt int ) | ~~~~~~~~ 3 | 4 | fn abc(){ diff --git a/vlib/v/checker/tests/globals_error.vv b/vlib/v/checker/tests/globals_error.vv index 1b35df827f..51048dc35a 100644 --- a/vlib/v/checker/tests/globals_error.vv +++ b/vlib/v/checker/tests/globals_error.vv @@ -1,5 +1,5 @@ -__global rfcnt int +__global ( rfcnt int ) fn abc(){ rfcnt = 2 diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 8dcad62d48..a933223261 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -399,13 +399,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { f.writeln('}') } ast.GlobalDecl { - f.write('__global $it.name ') - f.write(f.type_to_str(it.typ)) - if it.has_expr { - f.write(' = ') - f.expr(it.expr) - } - f.writeln('') + f.global_decl(it) } ast.GoStmt { f.write('go ') @@ -1885,6 +1879,58 @@ pub fn (mut f Fmt) const_decl(it ast.ConstDecl) { f.writeln(')\n') } +fn (mut f Fmt) global_decl(it ast.GlobalDecl) { + single := it.fields.len == 1 + if single { + f.write('__global ( ') + } else { + f.write('__global (') + f.writeln('') + f.indent++ + } + mut max := 0 + mut has_assign := false + for field in it.fields { + if field.name.len > max { + max = field.name.len + } + if field.has_expr { + has_assign = true + } + } + for field in it.fields { + comments := field.comments + for comment in comments { + f.comment(comment, { + inline: true + }) + f.writeln('') + } + f.write('$field.name ') + f.write(strings.repeat(` `, max - field.name.len)) + if field.has_expr { + f.write('= ') + f.write(f.type_to_str(field.typ)) + f.write('(') + f.expr(field.expr) + f.write(')') + } else { + if !single && has_assign { + f.write(' ') + } + f.write('${f.type_to_str(field.typ)} ') + } + if !single { + f.writeln('') + } + } + if !single { + f.indent-- + } + f.comments_after_last_field(it.end_comments) + f.writeln(')\n') +} + fn (mut f Fmt) is_external_name(name string) bool { if name.len > 2 && name[0] == `C` && name[1] == `.` { return true diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 7ba796ec41..6f6249cab9 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -937,8 +937,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { g.writeln('}') } ast.GlobalDecl { - styp := g.typ(node.typ) - g.definitions.writeln('$styp $node.name; // global') + g.global_decl(node) } ast.GoStmt { g.go_stmt(node) @@ -3662,6 +3661,17 @@ fn (mut g Gen) const_decl_init_later(mod, name, val string, typ table.Type) { } } +fn (mut g Gen) global_decl(node ast.GlobalDecl) { + for field in node.fields { + styp := g.typ(field.typ) + if field.has_expr { + g.definitions.writeln('$styp $field.name = $field.expr; // global') + } else { + g.definitions.writeln('$styp $field.name; // global') + } + } +} + fn (mut g Gen) go_back_out(n int) { g.out.go_back(n) } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 6461709d8e..06c9e78416 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1676,41 +1676,79 @@ fn (mut p Parser) global_decl() ast.GlobalDecl { p.error('use `v --enable-globals ...` to enable globals') } start_pos := p.tok.position() - p.next() - pos := start_pos.extend(p.tok.position()) - name := p.check_name() - // println(name) - typ := p.parse_type() - mut expr := ast.Expr{} - has_expr := p.tok.kind == .assign - if has_expr { - p.next() - expr = p.expr(0) + end_pos := p.tok.position() + p.check(.key_global) + if p.tok.kind != .lpar { + // Need to work for intermediate V Compiler for PRs process + // p.error('globals must be grouped, e.g. `__global ( a = int(1) )`') + pos := p.tok.position() + name := p.check_name() + typ := p.parse_type() + mut expr := ast.Expr{} + has_expr := p.tok.kind == .assign + if has_expr { + p.next() + expr = p.expr(0) + } + mut fields := []ast.GlobalField{} + field := ast.GlobalField{ + name: name + has_expr: has_expr + expr: expr + pos: pos + typ: typ + comments: []ast.Comment{} + } + fields << field + p.global_scope.register(field.name, field) + return ast.GlobalDecl{ + fields: fields + pos: start_pos.extend(end_pos) + end_comments: []ast.Comment{} + } } - // p.genln(p.table.cgen_name_type_pair(name, typ)) - /* - mut g := p.table.cgen_name_type_pair(name, typ) - if p.tok == .assign { - p.next() - g += ' = ' - _,expr := p.tmp_expr() - g += expr + p.next() // ( + mut fields := []ast.GlobalField{} + mut comments := []ast.Comment{} + for { + comments = p.eat_comments() + if p.tok.kind == .rpar { + break + } + pos := p.tok.position() + name := p.check_name() + has_expr := p.tok.kind == .assign + if has_expr { + p.next() // = + } + typ := p.parse_type() + mut expr := ast.Expr{} + if has_expr { + if p.tok.kind != .lpar { + p.error('global assign must have a type and value, use `__global ( name = type(value) )` or `__global ( name type )`') + } + p.next() // ( + expr = p.expr(0) + p.check(.rpar) + } + field := ast.GlobalField{ + name: name + has_expr: has_expr + expr: expr + pos: pos + typ: typ + comments: comments + } + fields << field + p.global_scope.register(field.name, field) + comments = [] } - // p.genln('; // global') - g += '; // global' - if !p.cgen.nogen { - p.cgen.consts << g + p.check(.rpar) + return ast.GlobalDecl{ + pos: start_pos.extend(end_pos) + fields: fields + end_comments: comments } - */ - glob := ast.GlobalDecl{ - name: name - typ: typ - pos: pos - has_expr: has_expr - expr: expr - } - p.global_scope.register(name, glob) - return glob } fn (mut p Parser) enum_decl() ast.EnumDecl {