1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

all: anonymous structs (part 2)

This commit is contained in:
Alexander Medvednikov 2022-07-06 21:38:40 +03:00
parent f4b39fbe4f
commit 426421bebb
10 changed files with 151 additions and 99 deletions

View File

@ -1,9 +1,15 @@
## V 0.3.1
*Not released yet*
- Anonymous structs.
- V can now find code in the `src/` directory. This allows making V repos much cleaner.
- `os.mkdir()` now has an optional `mode` paramter.
- Full termux support via `$if termux {`.
- Go backend fixes.
- More type checks.
- New keyword/type: `nil`. Only to be used inside `unsafe`. Replaces `voidptr(0)`.
- DOOM is now translated/compiled and launched on CI servers. A screenshot of the running game
is made via `vgret` and is compared to the expected result.
- VLS performance improvements, especially on Windows.
## V 0.3
*30 Jun 2022*

View File

@ -5,8 +5,12 @@
- [ ] Parallel checker
- [ ] Parallel C compilation
- [ ] `recover()` from panics
- [ ] vfmt: fix common errors automatically (make vars mutable and vice versa, add missing imports)
- [ ] vfmt: add missing imports (like goimports)
- [ ] merge v.c and v_win.c
- [ ] Recursive structs via optionals: `struct Node { next ?Node }`
- [ ] Handle function pointers safely, remove `if function == 0 {`
- [ ] Bundle OpenSSL like GC
- [ ] Anonymous structs
- [ ] -usecache on by default
- [ ] -skip-unused on by default

View File

@ -1020,6 +1020,7 @@ pub mut:
is_union bool
is_heap bool
is_minify bool
is_anon bool
is_generic bool
generic_types []Type
concrete_types []Type

View File

@ -5,12 +5,12 @@ module checker
import v.ast
pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
if node.language == .v && !c.is_builtin_mod {
c.check_valid_pascal_case(node.name, 'struct name', node.pos)
}
mut struct_sym, struct_typ_idx := c.table.find_sym_and_type_idx(node.name)
mut has_generic_types := false
if mut struct_sym.info is ast.Struct {
if node.language == .v && !c.is_builtin_mod && !struct_sym.info.is_anon {
c.check_valid_pascal_case(node.name, 'struct name', node.pos)
}
for embed in node.embeds {
if embed.typ.has_flag(.generic) {
has_generic_types = true

View File

@ -4927,96 +4927,7 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
mut name := sym.cname
match sym.info {
ast.Struct {
if sym.info.is_generic {
continue
}
if name.contains('_T_') {
g.typedefs.writeln('typedef struct $name $name;')
}
// TODO avoid buffer manip
start_pos := g.type_definitions.len
mut pre_pragma := ''
mut post_pragma := ''
for attr in sym.info.attrs {
match attr.name {
'_pack' {
pre_pragma += '#pragma pack(push, $attr.arg)\n'
post_pragma += '#pragma pack(pop)'
}
else {}
}
}
is_minify := sym.info.is_minify
g.type_definitions.writeln(pre_pragma)
if sym.info.is_union {
g.type_definitions.writeln('union $name {')
} else {
g.type_definitions.writeln('struct $name {')
}
if sym.info.fields.len > 0 || sym.info.embeds.len > 0 {
for field in sym.info.fields {
// Some of these structs may want to contain
// optionals that may not be defined at this point
// if this is the case then we are going to
// buffer manip out in front of the struct
// write the optional in and then continue
// FIXME: for parallel cgen (two different files using the same optional in struct fields)
if field.typ.has_flag(.optional) {
// Dont use g.typ() here becuase it will register
// optional and we dont want that
styp, base := g.optional_type_name(field.typ)
lock g.done_optionals {
if base !in g.done_optionals {
g.done_optionals << base
last_text := g.type_definitions.after(start_pos).clone()
g.type_definitions.go_back_to(start_pos)
g.typedefs.writeln('typedef struct $styp $styp;')
g.type_definitions.writeln('${g.optional_type_text(styp,
base)};')
g.type_definitions.write_string(last_text)
}
}
}
type_name := g.typ(field.typ)
field_name := c_name(field.name)
volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
mut size_suffix := ''
if is_minify && !g.is_cc_msvc {
if field.typ == ast.bool_type_idx {
size_suffix = ' : 1'
} else {
field_sym := g.table.sym(field.typ)
if field_sym.info is ast.Enum {
if !field_sym.info.is_flag && !field_sym.info.uses_exprs {
mut bits_needed := 0
mut l := field_sym.info.vals.len
for l > 0 {
bits_needed++
l >>= 1
}
size_suffix = ' : $bits_needed'
}
}
}
}
g.type_definitions.writeln('\t$volatile_prefix$type_name $field_name$size_suffix;')
}
} else {
g.type_definitions.writeln('\tEMPTY_STRUCT_DECLARATION;')
}
// g.type_definitions.writeln('} $name;\n')
//
ti_attrs := if sym.info.attrs.contains('packed') {
'__attribute__((__packed__))'
} else {
''
}
g.type_definitions.writeln('}$ti_attrs;\n')
g.type_definitions.writeln(post_pragma)
g.struct_decl(sym.info, name, false)
}
ast.Alias {
// ast.Alias { TODO

View File

@ -311,3 +311,114 @@ fn (mut g Gen) is_empty_struct(t Type) bool {
}
}
}
fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
if s.is_generic {
return
}
if name.contains('_T_') {
g.typedefs.writeln('typedef struct $name $name;')
}
// TODO avoid buffer manip
start_pos := g.type_definitions.len
mut pre_pragma := ''
mut post_pragma := ''
for attr in s.attrs {
match attr.name {
'_pack' {
pre_pragma += '#pragma pack(push, $attr.arg)\n'
post_pragma += '#pragma pack(pop)'
}
else {}
}
}
is_minify := s.is_minify
g.type_definitions.writeln(pre_pragma)
if is_anon {
g.type_definitions.writeln('struct {')
} else if s.is_union {
g.type_definitions.writeln('union $name {')
} else {
g.type_definitions.writeln('struct $name {')
}
if s.fields.len > 0 || s.embeds.len > 0 {
for field in s.fields {
// Some of these structs may want to contain
// optionals that may not be defined at this point
// if this is the case then we are going to
// buffer manip out in front of the struct
// write the optional in and then continue
// FIXME: for parallel cgen (two different files using the same optional in struct fields)
if field.typ.has_flag(.optional) {
// Dont use g.typ() here becuase it will register
// optional and we dont want that
styp, base := g.optional_type_name(field.typ)
lock g.done_optionals {
if base !in g.done_optionals {
g.done_optionals << base
last_text := g.type_definitions.after(start_pos).clone()
g.type_definitions.go_back_to(start_pos)
g.typedefs.writeln('typedef struct $styp $styp;')
g.type_definitions.writeln('${g.optional_type_text(styp, base)};')
g.type_definitions.write_string(last_text)
}
}
}
type_name := g.typ(field.typ)
field_name := c_name(field.name)
volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
mut size_suffix := ''
if is_minify && !g.is_cc_msvc {
if field.typ == ast.bool_type_idx {
size_suffix = ' : 1'
} else {
field_sym := g.table.sym(field.typ)
if field_sym.info is ast.Enum {
if !field_sym.info.is_flag && !field_sym.info.uses_exprs {
mut bits_needed := 0
mut l := field_sym.info.vals.len
for l > 0 {
bits_needed++
l >>= 1
}
size_suffix = ' : $bits_needed'
}
}
}
}
field_sym := g.table.sym(field.typ)
mut field_is_anon := false
if field_sym.info is ast.Struct {
if field_sym.info.is_anon {
field_is_anon = true
// Recursively generate code for this anon struct (this is the field's type)
g.struct_decl(field_sym.info, field_sym.cname, true)
// Now the field's name
g.type_definitions.writeln(' $field_name$size_suffix;')
}
}
if !field_is_anon {
g.type_definitions.writeln('\t$volatile_prefix$type_name $field_name$size_suffix;')
}
}
} else {
g.type_definitions.writeln('\tEMPTY_STRUCT_DECLARATION;')
}
// g.type_definitions.writeln('} $name;\n')
//
ti_attrs := if s.attrs.contains('packed') {
'__attribute__((__packed__))'
} else {
''
}
g.type_definitions.write_string('}$ti_attrs')
if !is_anon {
g.type_definitions.write_string(';')
}
g.type_definitions.writeln('\n')
g.type_definitions.writeln(post_pragma)
}

View File

@ -425,9 +425,9 @@ pub fn (mut p Parser) parse_type() ast.Type {
}
// Anon structs
if p.tok.kind == .key_struct {
p.struct_decl(true)
p.next()
return p.table.find_type_idx('anon_struct') // TODO
struct_decl := p.struct_decl(true)
// Find the registered anon struct type, it was registered above in `p.struct_decl()`
return p.table.find_type_idx(struct_decl.name)
}
language := p.parse_language()

View File

@ -91,6 +91,7 @@ mut:
if_cond_comments []ast.Comment
script_mode bool
script_mode_start_token token.Token
anon_struct_counter int
pub mut:
scanner &scanner.Scanner
errors []errors.Error

View File

@ -39,7 +39,12 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
if p.disallow_declarations_in_script_mode() {
return ast.StructDecl{}
}
mut name := if is_anon { '' } else { p.check_name() }
mut name := if is_anon {
p.anon_struct_counter++
'_VAnonStruct$p.anon_struct_counter'
} else {
p.check_name()
}
if name.len == 1 && name[0].is_capital() {
p.error_with_pos('single letter capital names are reserved for generic template types.',
name_pos)
@ -61,7 +66,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
return ast.StructDecl{}
}
if language == .v && !p.builtin_mod && !p.is_translated && name.len > 0 && !name[0].is_capital()
&& !p.pref.translated && !p.is_translated {
&& !p.pref.translated && !p.is_translated && !is_anon {
p.error_with_pos('struct name `$name` must begin with capital letter', name_pos)
return ast.StructDecl{}
}
@ -338,6 +343,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
is_generic: generic_types.len > 0
generic_types: generic_types
attrs: attrs
is_anon: is_anon
}
is_pub: is_pub
}

View File

@ -412,3 +412,15 @@ fn test_struct_update() {
assert c2.capital.name == 'city'
assert c2.name == 'test'
}
// Test anon structs
struct Book {
x Foo
title string
author struct {
name string
age int
}
}
fn test_anon() {}