Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix issue #172 #174

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
365 changes: 193 additions & 172 deletions src/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,194 +4,215 @@ import strings

// |-RecordDecl 0x7fd7c302c560 <a.c:3:1, line:5:1> line:3:8 struct User definition
fn (mut c C2V) record_decl(node &Node) {
vprintln('record_decl("${node.name}")')
// Skip empty structs (extern or forward decls)
if node.kindof(.record_decl) && node.inner.len == 0 {
return
}
mut name := node.name
// Dont generate struct header if it was already generated by typedef
// Confusing, but typedefs in C AST are really messy.
// ...
// If the struct has no name, then it's `typedef struct { ... } name`
// AST: 1) RecordDecl struct definition 2) TypedefDecl struct name
if c.tree.inner.len > c.node_i + 1 {
next_node := c.tree.inner[c.node_i + 1]
if next_node.kind == .typedef_decl {
if c.is_verbose {
c.genln('// typedef struct')
}
name = next_node.name
if name.contains('apthing_t') {
vprintln(node.str())
}
}
}
vprintln('record_decl("${node.name}")')
// Skip empty structs (extern or forward decls)
if node.kindof(.record_decl) && node.inner.len == 0 {
return
}
mut name := node.name
// Dont generate struct header if it was already generated by typedef
// Confusing, but typedefs in C AST are really messy.
// ...
// If the struct has no name, then it's `typedef struct { ... } name`
// AST: 1) RecordDecl struct definition 2) TypedefDecl struct name
if c.tree.inner.len > c.node_i + 1 {
next_node := c.tree.inner[c.node_i + 1]
if next_node.kind == .typedef_decl {
if c.is_verbose {
c.genln('// typedef struct')
}
name = next_node.name
if name.contains('apthing_t') {
vprintln(node.str())
}
}
}

if name in builtin_type_names {
return
}
if c.is_verbose {
c.genln('// struct decl name="${name}"')
}
if name in c.types {
return
}
// Anonymous struct, most likely the next node is a vardecl with this anon struct type, so remember it
if name == '' {
name = 'AnonStruct_${node.location.line}'
c.last_declared_type_name = name
}
if name !in ['struct', 'union'] {
c.types << name
name = capitalize_type(name)
if node.tags.contains('union') {
c.genln('union ${name} { ')
} else {
c.genln('struct ${name} { ')
}
}
mut new_struct := Struct{}
// in V it's `field struct {...}`, but in C we get struct definition first, so save it and use it in the
// next child
mut anon_struct_definition := ''
for field in node.inner {
// Handle anon structs
if field.kind == .record_decl {
anon_struct_definition = c.anon_struct_field_type(field)
continue
}
// There may be comments, skip them
if field.kind != .field_decl {
continue
}
field_type := convert_type(field.ast_type.qualified)
field_name := filter_name(field.name).uncapitalize()
mut field_type_name := field_type.name
if name in builtin_type_names {
return
}
if c.is_verbose {
c.genln('// struct decl name="${name}"')
}
if name in c.types {
return
}
// Anonymous struct, most likely the next node is a vardecl with this anon struct type, so remember it
if name == '' {
name = 'AnonStruct_${node.location.line}'
c.last_declared_type_name = name
}
if name !in ['struct', 'union'] {
c.types << name
name = capitalize_type(name)
if node.tags.contains('union') {
c.genln('union ${name} { ')
} else {
c.genln('struct ${name} { ')
}
}
mut new_struct := Struct{}
// in V it's `field struct {...}`, but in C we get struct definition first, so save it and use it in the
// next child
mut anon_struct_definition := ''
mut ano_arr := []string{}
for field in node.inner {
len := ano_arr.len
i := '${name}${len}'
if field.kind == .record_decl && field.tags.contains('union') {
anon_struct_definition = c.anon_struct_field_type(field, i)
ano_arr << anon_struct_definition
c.genln('\n${i}\n')
continue
}
}
for field in node.inner {
// Handle anon structs
if field.kind == .record_decl && field.tags.contains('union') {
continue
}
if field.kind == .record_decl {
anon_struct_definition = c.anon_struct_field_type(field, '')
continue
}
// There may be comments, skip them
if field.kind != .field_decl {
continue
}
field_type := convert_type(field.ast_type.qualified)
field_name := filter_name(field.name).uncapitalize()
mut field_type_name := field_type.name

// Handle anon structs, the anonymous struct has just been defined above, use its definition
if field_type_name.contains('unnamed at') {
field_type_name = anon_struct_definition
}
if field_type_name.contains('anonymous at') {
continue
}
/*
if field_type.name.contains('union') {
continue // TODO
}
*/
new_struct.fields << field_name
if field_type.name.ends_with('_s') { // TODO doom _t _s hack, remove
n := field_type.name[..field_type.name.len - 2] + '_t'
c.genln('\t${field_name} ${n}')
} else {
c.genln('\t${field_name} ${field_type_name}')
}
}
c.structs[name] = new_struct
c.genln('}')
// Handle anon structs, the anonymous struct has just been defined above, use its definition
if field_type_name.contains('unnamed at') {
field_type_name = anon_struct_definition
}
if field_type_name.contains('anonymous at') {
continue
}
/*
if field_type.name.contains('union') {
continue // TODO
}
*/
new_struct.fields << field_name
if field_type.name.ends_with('_s') { // TODO doom _t _s hack, remove
n := field_type.name[..field_type.name.len - 2] + '_t'
c.genln('\t${field_name} ${n}')
} else {
c.genln('\t${field_name} ${field_type_name}')
}
}
c.structs[name] = new_struct
c.genln('}')
for anon in ano_arr {
c.genln(anon)
}
}

fn (mut c C2V) anon_struct_field_type(node &Node) string {
mut sb := strings.new_builder(50)
sb.write_string(' struct {')
for field in node.inner {
if field.kind != .field_decl {
continue
}
field_type := convert_type(field.ast_type.qualified)
field_name := filter_name(field.name)
sb.write_string('\t${field_name} ${field_type.name}\n')
}
sb.write_string('}\n')
return sb.str()
fn (mut c C2V) anon_struct_field_type(node &Node, i string) string {
mut sb := strings.new_builder(50)
if node.tags.contains('union') {
sb.write_string('union ${i} {\n')
} else {
sb.write_string('struct ${i} {\n')
}
for field in node.inner {
if field.kind != .field_decl {
continue
}
field_type := convert_type(field.ast_type.qualified)
field_name := filter_name(field.name)
sb.write_string('\t${field_name} ${field_type.name}\n')
}
sb.write_string('}')
return sb.str()
}

// Typedef node goes after struct enum, but we need to parse it first, so that "type name { " is
// generated first
fn (mut c C2V) typedef_decl(node &Node) {
mut typ := node.ast_type.qualified
// just a single line typedef: (alias)
// typedef sha1_context_t sha1_context_s ;
// typedef after enum decl, just generate "enum NAME {" header
mut alias_name := node.name // get_val(-2)
vprintln('TYPEDEF "${node.name}" ${node.is_builtin_type} ${typ}')
if alias_name.contains('et_context_t') {
// TODO remove this
return
}
if node.name in builtin_type_names {
return
}
mut typ := node.ast_type.qualified
// just a single line typedef: (alias)
// typedef sha1_context_t sha1_context_s ;
// typedef after enum decl, just generate "enum NAME {" header
mut alias_name := node.name // get_val(-2)
vprintln('TYPEDEF "${node.name}" ${node.is_builtin_type} ${typ}')
if alias_name.contains('et_context_t') {
// TODO remove this
return
}
if node.name in builtin_type_names {
return
}

if alias_name in c.types || alias_name in c.enums {
// This means that this is a struct/enum typedef that has already been defined.
return
}
if alias_name in c.types || alias_name in c.enums {
// This means that this is a struct/enum typedef that has already been defined.
return
}

c.types << alias_name
c.types << alias_name

if typ.starts_with('struct ') && typ.ends_with(' *') {
// Opaque pointer, for example: typedef struct TSTexture_t *TSTexture;
c.genln('type ${alias_name} = voidptr')
return
}
if typ.starts_with('struct ') && typ.ends_with(' *') {
// Opaque pointer, for example: typedef struct TSTexture_t *TSTexture;
c.genln('type ${alias_name} = voidptr')
return
}

if !typ.contains(alias_name) {
if typ.contains('(*)') {
tt := convert_type(typ)
typ = tt.name
}
// Struct types have junk before spaces
else {
alias_name = alias_name.all_after(' ')
tt := convert_type(typ)
typ = tt.name
}
if alias_name.starts_with('__') {
// Skip internal stuff like __builtin_ms_va_list
return
}
if typ in c.enums {
return
}
if !typ.contains(alias_name) {
if typ.contains('(*)') {
tt := convert_type(typ)
typ = tt.name
}
// Struct types have junk before spaces
else {
alias_name = alias_name.all_after(' ')
tt := convert_type(typ)
typ = tt.name
}
if alias_name.starts_with('__') {
// Skip internal stuff like __builtin_ms_va_list
return
}
if typ in c.enums {
return
}

mut cgen_alias := typ
if cgen_alias.starts_with('_') {
cgen_alias = trim_underscores(typ)
}
if typ !in ['int', 'i8', 'i16', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'usize', 'isize', 'bool', 'void', 'voidptr']
&& !typ.starts_with('fn (') {
// TODO handle this better
cgen_alias = cgen_alias.capitalize()
}
c.genln('type ${alias_name.capitalize()} = ${cgen_alias}') // typedef alias (SINGLE LINE)')
return
}
if typ.contains('enum ') {
// enums were alredy handled in enum_decl
return
} else if typ.contains('struct ') {
// structs were already handled in struct_decl
return
} else if typ.contains('union ') {
// unions were alredy handled in struct_decl
return
}
mut cgen_alias := typ
if cgen_alias.starts_with('_') {
cgen_alias = trim_underscores(typ)
}
if typ !in ['int', 'i8', 'i16', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'usize', 'isize', 'bool', 'void', 'voidptr']
&& !typ.starts_with('fn (') {
// TODO handle this better
cgen_alias = cgen_alias.capitalize()
}
c.genln('type ${alias_name.capitalize()} = ${cgen_alias}') // typedef alias (SINGLE LINE)')
return
}
if typ.contains('enum ') {
// enums were alredy handled in enum_decl
return
} else if typ.contains('struct ') {
// structs were already handled in struct_decl
return
} else if typ.contains('union ') {
// unions were alredy handled in struct_decl
return
}
}

// this calls typedef_decl() above
fn (mut c C2V) parse_next_typedef() bool {
// Hack: typedef with the actual enum name is next, parse it and generate "enum NAME {" first
/*
XTODO
next_line := c.lines[c.line_i + 1]
if next_line.contains('TypedefDecl') {
c.line_i++
c.parse_next_node()
return true
}
*/
return false
// Hack: typedef with the actual enum name is next, parse it and generate "enum NAME {" first
/*
XTODO
next_line := c.lines[c.line_i + 1]
if next_line.contains('TypedefDecl') {
c.line_i++
c.parse_next_node()
return true
}
*/
return false
}