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:
parent
f4b39fbe4f
commit
426421bebb
@ -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*
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
Loading…
Reference in New Issue
Block a user