diff --git a/compiler/cgen.v b/compiler/cgen.v index b83f3b497b..1ef3acdf30 100644 --- a/compiler/cgen.v +++ b/compiler/cgen.v @@ -5,7 +5,8 @@ module main import os -import strings +import strings +import time struct CGen { out os.File @@ -13,14 +14,14 @@ struct CGen { typedefs []string type_aliases []string includes []string - types []string + //types []string thread_args []string thread_fns []string consts []string fns []string so_fns []string consts_init []string - //buf strings.Builder + //buf strings.Builder is_user bool mut: lines []string @@ -35,19 +36,19 @@ mut: file string line int line_directives bool - cut_pos int + cut_pos int } fn new_cgen(out_name_c string) *CGen { path := out_name_c out := os.create(path) or { - println('failed to create $path') - return &CGen{} - } + println('failed to create $path') + return &CGen{} + } gen := &CGen { - out_path: path - out: out - //buf: strings.new_builder(10000) + out_path: path + out: out + //buf: strings.new_builder(10000) lines: _make(0, 1000, sizeof(string)) } return gen @@ -128,21 +129,21 @@ fn (g mut CGen) add_placeholder() int { } fn (g mut CGen) start_cut() { - g.cut_pos = g.add_placeholder() -} + g.cut_pos = g.add_placeholder() +} fn (g mut CGen) cut() string { - pos := g.cut_pos - g.cut_pos = 0 + pos := g.cut_pos + g.cut_pos = 0 if g.is_tmp { - res := g.tmp_line.right(pos) - g.tmp_line = g.tmp_line.left(pos) - return res + res := g.tmp_line.right(pos) + g.tmp_line = g.tmp_line.left(pos) + return res } - res := g.cur_line.right(pos) - g.cur_line = g.cur_line.left(pos) - return res -} + res := g.cur_line.right(pos) + g.cur_line = g.cur_line.left(pos) + return res +} fn (g mut CGen) set_placeholder(pos int, val string) { if g.nogen || g.pass != .main { @@ -162,8 +163,8 @@ fn (g mut CGen) set_placeholder(pos int, val string) { } fn (g mut CGen) insert_before(val string) { - prev := g.lines[g.lines.len - 1] - g.lines[g.lines.len - 1] = '$prev \n $val \n' + prev := g.lines[g.lines.len - 1] + g.lines[g.lines.len - 1] = '$prev \n $val \n' } fn (g mut CGen) register_thread_fn(wrapper_name, wrapper_text, struct_text string) { @@ -216,12 +217,14 @@ fn (p mut Parser) print_prof_counters() string { return res.join(';\n') } +/* fn (p mut Parser) gen_type(s string) { if !p.first_pass() { return } p.cgen.types << s } +*/ fn (p mut Parser) gen_typedef(s string) { if !p.first_pass() { @@ -242,42 +245,42 @@ fn (g mut CGen) add_to_main(s string) { } -fn build_thirdparty_obj_file(flag string) { - obj_path := flag.all_after(' ') +fn build_thirdparty_obj_file(flag string) { + obj_path := flag.all_after(' ') if os.file_exists(obj_path) { - return - } - println('$obj_path not found, building it...') - parent := obj_path.all_before_last('/').trim_space() - files := os.ls(parent) - //files := os.ls(parent).filter(_.ends_with('.c')) TODO - mut cfiles := '' + return + } + println('$obj_path not found, building it...') + parent := obj_path.all_before_last('/').trim_space() + files := os.ls(parent) + //files := os.ls(parent).filter(_.ends_with('.c')) TODO + mut cfiles := '' for file in files { - if file.ends_with('.c') { - cfiles += parent + '/' + file + ' ' - } - } + if file.ends_with('.c') { + cfiles += parent + '/' + file + ' ' + } + } cc := find_c_compiler() cc_thirdparty_options := find_c_compiler_thirdparty_options() res := os.exec('$cc $cc_thirdparty_options -c -o $obj_path $cfiles') or { panic(err) } - println(res.output) -} + println(res.output) +} -fn os_name_to_ifdef(name string) string { +fn os_name_to_ifdef(name string) string { switch name { case 'windows': return '_WIN32' case 'mac': return '__APPLE__' - case 'linux': return '__linux__' - case 'freebsd': return '__FreeBSD__' - case 'openbsd': return '__OpenBSD__' - case 'netbsd': return '__NetBSD__' - case 'dragonfly': return '__DragonFly__' - case 'msvc': return '_MSC_VER' - } - panic('bad os ifdef name "$name"') -} + case 'linux': return '__linux__' + case 'freebsd': return '__FreeBSD__' + case 'openbsd': return '__OpenBSD__' + case 'netbsd': return '__NetBSD__' + case 'dragonfly': return '__DragonFly__' + case 'msvc': return '_MSC_VER' + } + panic('bad os ifdef name "$name"') +} fn platform_postfix_to_ifdefguard(name string) string { switch name { @@ -290,3 +293,45 @@ fn platform_postfix_to_ifdefguard(name string) string { panic('bad platform_postfix "$name"') } +// C struct definitions, ordered +fn (v mut V) c_type_definitions() string { + mut types := v.table.types + // Sort the types, make sure types that are referenced by other types + // are added before them. + for i in 0 .. types.len { + for j in 0 .. i { + t := types[i] + if types[j].contains_field_type(t.name) { + types[i] = types[j] + types[j] = t + continue + } + + } + } + // Generate C code + mut sb := strings.new_builder(10) + for t in v.table.types { + if t.cat != .union_ && t.cat != .struct_ { + continue + } + //if is_objc { + //sb.writeln('@interface $name : $objc_parent { @public') + //} + //if is_atomic { + //sb.write('_Atomic ') + //} + kind := if t.cat == .union_ {'union'} else {'struct'} + sb.writeln('$kind $t.name {') + for field in t.fields { + sb.writeln(v.table.cgen_name_type_pair(field.name, + field.typ) + ';') + } + sb.writeln('};\n') + //if is_objc { + //p.gen_type('@end') + //} + } + return sb.str() +} + diff --git a/compiler/main.v b/compiler/main.v index 400e2c537f..40aec307d8 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -256,12 +256,6 @@ fn (v mut V) compile() { (v.pref.build_mode == .build && v.dir.contains('/ui'))) { cgen.genln('id defaultFont = 0; // main.v') } - // TODO remove ugly .c include once V has its own json parser - // Embed cjson either in embedvlib or in json.o - if (imports_json && v.pref.build_mode == .embed_vlib) || - (v.pref.build_mode == .build && v.out_name.contains('json.o')) { - //cgen.genln('#include "cJSON.c" ') - } // We need the cjson header for all the json decoding user will do in default mode if v.pref.build_mode == .default_mode { if imports_json { @@ -300,7 +294,8 @@ fn (v mut V) compile() { mut d := strings.new_builder(10000)// Avoid unnecessary allocations d.writeln(cgen.includes.join_lines()) d.writeln(cgen.typedefs.join_lines()) - d.writeln(cgen.types.join_lines()) + //d.writeln(cgen.types.join_lines()) + d.writeln(v.c_type_definitions()) d.writeln('\nstring _STR(const char*, ...);\n') d.writeln('\nstring _STR_TMP(const char*, ...);\n') d.writeln(cgen.fns.join_lines()) @@ -655,7 +650,7 @@ fn new_v(args[]string) *V { joined_args := args.join(' ') target_os := get_arg(joined_args, 'os', '') mut out_name := get_arg(joined_args, 'o', 'a.out') - + mut dir := args.last() if args.contains('run') { dir = get_all_after(joined_args, 'run', '') diff --git a/compiler/parser.v b/compiler/parser.v index 2fd592420f..b36b3141c6 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -491,8 +491,8 @@ fn key_to_type_cat(tok Token) TypeCategory { fn (p mut Parser) struct_decl() { // V can generate Objective C for integration with Cocoa // `[interface:ParentInterface]` - is_objc := p.attr.starts_with('interface') - objc_parent := if is_objc { p.attr.right(10) } else { '' } + //is_objc := p.attr.starts_with('interface') + //objc_parent := if is_objc { p.attr.right(10) } else { '' } // interface, union, struct is_interface := p.tok == .key_interface is_union := p.tok == .key_union @@ -527,17 +527,9 @@ fn (p mut Parser) struct_decl() { if p.pass == .decl && p.table.known_type(name) { p.error('`$name` redeclared') } - // Generate type definitions - if is_objc { - p.gen_type('@interface $name : $objc_parent { @public') - } - else { - // type alias is generated later - if !is_c { - kind := if is_union {'union'} else {'struct'} - p.gen_typedef('typedef $kind $name $name;') - p.gen_type('$kind $name {') - } + if !is_c { + kind := if is_union {'union'} else {'struct'} + p.gen_typedef('typedef $kind $name $name;') } // Register the type mut typ := p.table.find_type(name) @@ -634,10 +626,6 @@ fn (p mut Parser) struct_decl() { is_atomic := p.tok == .key_atomic if is_atomic { p.next() - p.gen_type('_Atomic ') - } - if !is_c { - p.gen_type(p.table.cgen_name_type_pair(field_name, field_type) + ';') } // [ATTR] mut attr := '' @@ -665,17 +653,6 @@ fn (p mut Parser) struct_decl() { } p.check(.rcbr) - if !is_c { - if !did_gen_something { - p.gen_type('EMPTY_STRUCT_DECLARATION };') - p.fgenln('') - } else { - p.gen_type('}; ') - } - } - if is_objc { - p.gen_type('@end') - } p.fgenln('\n') } @@ -2790,7 +2767,7 @@ fn (p mut Parser) array_init() string { // Due to a tcc bug, the length needs to be specified. // GCC crashes if it is. cast := if p.pref.ccompiler == 'tcc' { '($typ[$i])' } else { '($typ[])' } - p.cgen.set_placeholder(new_arr_ph, + p.cgen.set_placeholder(new_arr_ph, '$new_arr($i, $i, sizeof($typ), $cast { ') //} } @@ -3352,7 +3329,7 @@ fn (p mut Parser) switch_statement() { p.returns = false // only get here when no default, so return is not guaranteed } -// Returns typ if used as expession +// Returns typ if used as expession fn (p mut Parser) match_statement(is_expr bool) string { p.check(.key_match) p.cgen.start_tmp() @@ -3367,7 +3344,7 @@ fn (p mut Parser) match_statement(is_expr bool) string { mut i := 0 mut all_cases_return := true - // stores typ of resulting variable + // stores typ of resulting variable mut res_typ := '' defer { @@ -3375,8 +3352,8 @@ fn (p mut Parser) match_statement(is_expr bool) string { } for p.tok != .rcbr { - if p.tok == .key_else { - p.check(.key_else) + if p.tok == .key_else { + p.check(.key_else) p.check(.arrow) // unwrap match if there is only else @@ -3461,7 +3438,7 @@ fn (p mut Parser) match_statement(is_expr bool) string { p.gen(') || (') } - if typ == 'string' { + if typ == 'string' { // TODO: use tmp variable // p.gen('string_eq($tmp_var, ') p.gen('string_eq($tmp_var, ') diff --git a/compiler/table.v b/compiler/table.v index 3938b8651f..82a310b22d 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -44,6 +44,7 @@ enum AccessMod { } enum TypeCategory { + builtin struct_ func interface_ // 2 @@ -901,3 +902,12 @@ fn (fit &FileImportTable) is_aliased(mod string) bool { fn (fit &FileImportTable) resolve_alias(alias string) string { return fit.imports[alias] } + +fn (t &Type) contains_field_type(typ string) bool { + for field in t.fields { + if field.typ == typ { + return true + } + } + return false +}