v/compiler/cgen.v

386 lines
8.3 KiB
V
Raw Normal View History

2019-06-23 04:21:30 +02:00
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
2019-06-22 20:20:28 +02:00
module main
2019-07-12 07:37:54 +02:00
import os
2019-08-28 16:35:44 +02:00
import strings
import time
2019-07-12 07:37:54 +02:00
2019-06-22 20:20:28 +02:00
struct CGen {
out os.File
out_path string
//types []string
thread_fns []string
//buf strings.Builder
is_user bool
mut:
lines []string
2019-06-22 20:20:28 +02:00
typedefs []string
type_aliases []string
includes []string
thread_args []string
consts []string
fns []string
so_fns []string
consts_init []string
2019-07-29 18:21:36 +02:00
pass Pass
2019-07-26 16:45:16 +02:00
nogen bool
tmp_line string
cur_line string
prev_line string
is_tmp bool
fn_main string
stash string
file string
line int
line_directives bool
2019-08-28 16:35:44 +02:00
cut_pos int
2019-06-22 20:20:28 +02:00
}
fn new_cgen(out_name_c string) *CGen {
path := out_name_c
out := os.create(path) or {
2019-08-28 16:35:44 +02:00
println('failed to create $path')
return &CGen{}
}
2019-06-22 20:20:28 +02:00
gen := &CGen {
2019-08-28 16:35:44 +02:00
out_path: path
out: out
//buf: strings.new_builder(10000)
2019-07-26 16:45:16 +02:00
lines: _make(0, 1000, sizeof(string))
2019-06-22 20:20:28 +02:00
}
return gen
}
fn (g mut CGen) genln(s string) {
2019-07-29 18:21:36 +02:00
if g.nogen || g.pass != .main {
2019-06-22 20:20:28 +02:00
return
}
if g.is_tmp {
g.tmp_line = '$g.tmp_line $s\n'
return
}
g.cur_line = '$g.cur_line $s'
if g.cur_line != '' {
2019-07-26 16:45:16 +02:00
if g.line_directives && g.cur_line.trim_space() != '' {
g.lines << '#line $g.line "$g.file"'
}
2019-06-22 20:20:28 +02:00
g.lines << g.cur_line
g.prev_line = g.cur_line
g.cur_line = ''
}
}
fn (g mut CGen) gen(s string) {
2019-07-29 18:21:36 +02:00
if g.nogen || g.pass != .main {
2019-06-22 20:20:28 +02:00
return
}
if g.is_tmp {
g.tmp_line = '$g.tmp_line $s'
}
else {
g.cur_line = '$g.cur_line $s'
}
}
fn (g mut CGen) resetln(s string) {
2019-07-29 18:21:36 +02:00
if g.nogen || g.pass != .main {
return
}
if g.is_tmp {
g.tmp_line = s
}
else {
g.cur_line = s
}
}
2019-06-22 20:20:28 +02:00
fn (g mut CGen) save() {
s := g.lines.join('\n')
2019-06-30 16:11:55 +02:00
g.out.writeln(s)
2019-06-22 20:20:28 +02:00
g.out.close()
}
fn (g mut CGen) start_tmp() {
if g.is_tmp {
println(g.tmp_line)
println('start_tmp() already started. cur_line="$g.cur_line"')
exit(1)
2019-06-22 20:20:28 +02:00
}
// kg.tmp_lines_pos++
g.tmp_line = ''
g.is_tmp = true
}
fn (g mut CGen) end_tmp() string {
g.is_tmp = false
res := g.tmp_line
g.tmp_line = ''
return res
}
fn (g mut CGen) add_placeholder() int {
if g.is_tmp {
return g.tmp_line.len
}
return g.cur_line.len
}
2019-08-13 13:50:19 +02:00
fn (g mut CGen) start_cut() {
2019-08-28 16:35:44 +02:00
g.cut_pos = g.add_placeholder()
}
2019-08-13 13:50:19 +02:00
fn (g mut CGen) cut() string {
2019-08-28 16:35:44 +02:00
pos := g.cut_pos
g.cut_pos = 0
2019-08-13 13:50:19 +02:00
if g.is_tmp {
2019-08-28 16:35:44 +02:00
res := g.tmp_line.right(pos)
g.tmp_line = g.tmp_line.left(pos)
return res
2019-08-13 13:50:19 +02:00
}
2019-08-28 16:35:44 +02:00
res := g.cur_line.right(pos)
g.cur_line = g.cur_line.left(pos)
return res
}
2019-08-13 13:50:19 +02:00
2019-06-22 20:20:28 +02:00
fn (g mut CGen) set_placeholder(pos int, val string) {
2019-07-29 18:21:36 +02:00
if g.nogen || g.pass != .main {
2019-06-22 20:20:28 +02:00
return
}
// g.lines.set(pos, val)
if g.is_tmp {
left := g.tmp_line.left(pos)
right := g.tmp_line.right(pos)
g.tmp_line = '${left}${val}${right}'
return
}
left := g.cur_line.left(pos)
right := g.cur_line.right(pos)
g.cur_line = '${left}${val}${right}'
// g.genln('')
}
fn (g mut CGen) insert_before(val string) {
2019-08-28 16:35:44 +02:00
prev := g.lines[g.lines.len - 1]
g.lines[g.lines.len - 1] = '$prev \n $val \n'
2019-06-22 20:20:28 +02:00
}
fn (g mut CGen) register_thread_fn(wrapper_name, wrapper_text, struct_text string) {
for arg in g.thread_args {
if arg.contains(wrapper_name) {
return
}
}
g.thread_args << struct_text
g.thread_args << wrapper_text
}
fn (c mut V) prof_counters() string {
mut res := []string
// Global fns
//for f in c.table.fns {
//res << 'double ${c.table.cgen_name(f)}_time;'
//}
2019-06-22 20:20:28 +02:00
// Methods
/*
2019-06-22 20:20:28 +02:00
for typ in c.table.types {
// println('')
for f in typ.methods {
// res << f.cgen_name()
res << 'double ${c.table.cgen_name(f)}_time;'
// println(f.cgen_name())
}
}
*/
2019-06-22 20:20:28 +02:00
return res.join(';\n')
}
fn (p mut Parser) print_prof_counters() string {
mut res := []string
// Global fns
//for f in p.table.fns {
//counter := '${p.table.cgen_name(f)}_time'
//res << 'if ($counter) printf("%%f : $f.name \\n", $counter);'
//}
2019-06-22 20:20:28 +02:00
// Methods
/*
2019-06-22 20:20:28 +02:00
for typ in p.table.types {
// println('')
for f in typ.methods {
counter := '${p.table.cgen_name(f)}_time'
res << 'if ($counter) printf("%%f : ${p.table.cgen_name(f)} \\n", $counter);'
// res << 'if ($counter) printf("$f.name : %%f\\n", $counter);'
// res << f.cgen_name()
// res << 'double ${f.cgen_name()}_time;'
// println(f.cgen_name())
}
}
*/
2019-06-22 20:20:28 +02:00
return res.join(';\n')
}
fn (p mut Parser) gen_typedef(s string) {
2019-07-29 18:21:36 +02:00
if !p.first_pass() {
2019-06-22 20:20:28 +02:00
return
}
p.cgen.typedefs << s
}
fn (p mut Parser) gen_type_alias(s string) {
2019-07-29 18:21:36 +02:00
if !p.first_pass() {
2019-06-22 20:20:28 +02:00
return
}
p.cgen.type_aliases << s
}
fn (g mut CGen) add_to_main(s string) {
g.fn_main = g.fn_main + s
}
2019-08-28 16:35:44 +02:00
fn build_thirdparty_obj_file(flag string) {
obj_path := flag.all_after(' ')
if os.file_exists(obj_path) {
2019-08-28 16:35:44 +02:00
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 {
2019-08-28 16:35:44 +02:00
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 {
2019-08-29 02:30:17 +02:00
cerror(err)
return
}
2019-08-28 16:35:44 +02:00
println(res.output)
}
2019-08-28 16:35:44 +02:00
fn os_name_to_ifdef(name string) string {
2019-08-13 13:50:19 +02:00
switch name {
case 'windows': return '_WIN32'
case 'mac': return '__APPLE__'
2019-08-28 16:35:44 +02:00
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'
}
2019-08-29 02:30:17 +02:00
cerror('bad os ifdef name "$name"')
return ''
2019-08-28 16:35:44 +02:00
}
2019-08-13 13:50:19 +02:00
fn platform_postfix_to_ifdefguard(name string) string {
switch name {
case '.v': return '' // no guard needed
case '_win.v': return '#ifdef _WIN32'
case '_nix.v': return '#ifndef _WIN32'
case '_lin.v': return '#ifdef __linux__'
case '_mac.v': return '#ifdef __APPLE__'
}
2019-08-29 02:30:17 +02:00
cerror('bad platform_postfix "$name"')
return ''
2019-08-13 13:50:19 +02:00
}
2019-08-28 16:35:44 +02:00
// C struct definitions, ordered
2019-08-29 00:52:32 +02:00
// Sort the types, make sure types that are referenced by other types
// are added before them.
2019-08-28 16:35:44 +02:00
fn (v mut V) c_type_definitions() string {
2019-08-29 00:52:32 +02:00
mut types := []Type // structs that need to be sorted
mut top_types := []Type // builtin types and types that only have primitive fields
mut builtin_types := []Type
// builtin types need to be on top
builtins := ['string', 'array', 'map', 'Option']
for builtin in builtins {
typ := v.table.typesmap[builtin]
builtin_types << typ
v.table.typesmap.delete(builtin)
}
// split all types
for _, t in v.table.typesmap {
2019-08-29 00:52:32 +02:00
if !t.name[0].is_capital() {
top_types << t
continue
}
mut only_builtin_fields := true
for field in t.fields {
// user types start with a capital or contain __ (defined in another module)
if field.typ[0].is_capital() || field.typ.contains('__') {
2019-08-29 00:52:32 +02:00
only_builtin_fields = false
break
2019-08-28 16:35:44 +02:00
}
}
2019-08-29 00:52:32 +02:00
if only_builtin_fields {
top_types << t
continue
}
types << t
2019-08-28 16:35:44 +02:00
}
sort_structs(mut top_types)
2019-08-29 00:52:32 +02:00
sort_structs(mut types)
2019-08-28 16:35:44 +02:00
// Generate C code
return types_to_c(builtin_types,v.table) + '\n//----\n' +
types_to_c(top_types, v.table) + '\n/*----*/\n' +
types_to_c(types, v.table)
2019-08-29 00:52:32 +02:00
}
fn types_to_c(types []Type, table &Table) string {
2019-08-28 16:35:44 +02:00
mut sb := strings.new_builder(10)
2019-08-29 00:52:32 +02:00
for t in types {
2019-08-28 16:35:44 +02:00
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 {
2019-08-29 00:52:32 +02:00
sb.writeln(table.cgen_name_type_pair(field.name,
2019-08-28 16:35:44 +02:00
field.typ) + ';')
}
sb.writeln('};\n')
//if is_objc {
2019-08-29 00:52:32 +02:00
//sb.writeln('@end')
2019-08-28 16:35:44 +02:00
//}
}
return sb.str()
}
2019-08-29 00:52:32 +02:00
// pretty inefficient algo, works fine with N < 1000 (TODO optimize)
fn sort_structs(types mut []Type) {
mut cnt := 0
for i := 0; i < types.len; i++ {
for j in 0 .. i {
t := types[i]
//t2 := types[j]
// check if any of the types before `t` reference `t`
if types[j].contains_field_type(t.name) {
//println('moving up: $t.name len=$types.len')
types.insert(j, t)
types.delete(i+1)
i = 0 // Start from scratch
cnt++
if cnt > 500 {
println('infinite type loop (perhaps you have a recursive struct `$t.name`?)')
exit(1)
}
continue
}
}
}
}