v/vlib/v/gen/cgen.v

2682 lines
63 KiB
V
Raw Normal View History

2019-12-30 08:30:24 +01:00
module gen
2019-12-24 18:54:43 +01:00
import (
strings
v.ast
v.table
2020-03-05 23:27:21 +01:00
v.depgraph
v.token
2020-03-30 17:21:32 +02:00
v.pref
term
2019-12-24 18:54:43 +01:00
)
const (
c_reserved = ['delete', 'exit', 'unix',
// 'print',
// 'ok',
2020-03-24 14:46:00 +01:00
'error', 'calloc', 'malloc', 'free', 'panic',
// Full list of C reserved words, from: https://en.cppreference.com/w/c/keyword
'auto', 'char', 'default', 'do', 'double', 'extern', 'float', 'inline', 'int', 'long', 'register', 'restrict', 'short', 'signed', 'sizeof', 'static', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while', ]
)
2019-12-24 18:54:43 +01:00
struct Gen {
2020-03-07 16:39:15 +01:00
out strings.Builder
typedefs strings.Builder
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
inits strings.Builder // contents of `void _vinit(){}`
2020-03-07 16:39:15 +01:00
table &table.Table
2020-03-30 17:21:32 +02:00
pref &pref.Preferences
mut:
2020-03-21 19:52:19 +01:00
file ast.File
2020-03-07 16:39:15 +01:00
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
tmp_count int
2020-03-24 09:42:16 +01:00
variadic_args map[string]int
2020-03-07 16:39:15 +01:00
is_c_call bool // e.g. `C.printf("v")`
2020-03-31 10:11:47 +02:00
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
is_assign_rhs bool // inside right part of assign after `=` (val expr)
2020-03-07 16:39:15 +01:00
is_array_set bool
2020-03-10 23:21:26 +01:00
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
2020-03-15 08:18:42 +01:00
optionals []string // to avoid duplicates TODO perf, use map
inside_ternary bool // ?: comma separated statements on a single line
2020-03-19 15:18:29 +01:00
stmt_start_pos int
2020-03-20 13:03:42 +01:00
right_is_opt bool
2020-03-21 19:52:19 +01:00
autofree bool
indent int
empty_line bool
is_test bool
assign_op token.Kind // *=, =, etc (for array_set)
defer_stmts []ast.DeferStmt
defer_ifdef string
str_types []int // types that need automatic str() generation
2019-12-24 18:54:43 +01:00
}
const (
tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t',
'\t\t\t\t\t\t\t\t']
)
2020-03-30 17:21:32 +02:00
pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string {
2020-03-22 12:06:33 +01:00
// println('start cgen2')
2019-12-28 11:02:06 +01:00
mut g := Gen{
out: strings.new_builder(100)
typedefs: strings.new_builder(100)
2020-01-02 08:30:15 +01:00
definitions: strings.new_builder(100)
2020-03-21 09:29:16 +01:00
inits: strings.new_builder(100)
table: table
2020-03-30 17:21:32 +02:00
pref: pref
fn_decl: 0
2020-03-21 19:52:19 +01:00
autofree: true
indent: -1
2019-12-28 11:02:06 +01:00
}
g.init()
2020-03-22 12:06:33 +01:00
mut autofree_used := false
2019-12-30 12:10:46 +01:00
for file in files {
2020-03-21 19:52:19 +01:00
g.file = file
2020-03-22 12:06:33 +01:00
// println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len')
building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v'))
is_test := g.file.path.ends_with('.vv') || g.file.path.ends_with('_test.v')
2020-03-24 22:35:05 +01:00
if g.file.path.ends_with('_test.v') {
g.is_test = is_test
}
if g.file.path == '' || is_test || building_v {
// cgen test or building V
2020-03-22 12:06:33 +01:00
// println('autofree=false')
2020-03-21 19:52:19 +01:00
g.autofree = false
}
2020-03-22 12:06:33 +01:00
else {
g.autofree = true
autofree_used = true
}
2020-02-17 22:50:04 +01:00
g.stmts(file.stmts)
2019-12-24 18:54:43 +01:00
}
2020-03-22 12:06:33 +01:00
if autofree_used {
g.autofree = true // so that void _vcleanup is generated
}
2020-03-14 13:15:07 +01:00
g.write_variadic_types()
2020-03-21 11:47:23 +01:00
// g.write_str_definitions()
2020-03-21 10:22:16 +01:00
g.write_init_function()
if g.is_test {
g.write_tests_main()
}
return g.typedefs.str() + g.definitions.str() + g.out.str()
}
pub fn (g mut Gen) init() {
g.definitions.writeln('// Generated by the V compiler')
g.definitions.writeln('#include <inttypes.h>') // int64_t etc
g.definitions.writeln(c_builtin_types)
g.definitions.writeln(c_headers)
2020-03-21 07:10:53 +01:00
g.definitions.writeln('\nstring _STR(const char*, ...);\n')
g.definitions.writeln('\nstring _STR_TMP(const char*, ...);\n')
2020-03-10 23:21:26 +01:00
g.write_builtin_types()
g.write_typedef_types()
2020-03-28 17:37:22 +01:00
g.write_typeof_functions()
2020-03-21 11:47:23 +01:00
g.write_str_definitions()
2020-03-05 23:27:21 +01:00
g.write_sorted_types()
g.write_multi_return_types()
2020-03-06 12:06:41 +01:00
g.definitions.writeln('// end of definitions #endif')
2020-03-05 23:27:21 +01:00
}
2020-03-28 17:37:22 +01:00
pub fn (g mut Gen) write_typeof_functions() {
g.writeln('// >> typeof() support for sum types')
for typ in g.table.types {
if typ.kind == .sum_type {
sum_info := typ.info as table.SumType
2020-03-31 06:34:59 +02:00
tidx := g.table.find_type_idx(typ.name)
2020-03-28 17:37:22 +01:00
g.writeln('char * v_typeof_sumtype_${tidx}(int sidx) { /* ${typ.name} */ ')
g.writeln(' switch(sidx) {')
g.writeln(' case $tidx: return "$typ.name";')
for v in sum_info.variants {
subtype := g.table.get_type_symbol(v)
g.writeln(' case $v: return "$subtype.name";')
}
g.writeln(' default: return "unknown ${typ.name}";')
g.writeln(' }')
g.writeln('}')
}
}
g.writeln('// << typeof() support for sum types')
}
2020-03-06 13:43:22 +01:00
// V type to C type
2020-03-13 05:57:51 +01:00
pub fn (g mut Gen) typ(t table.Type) string {
2020-03-06 22:36:51 +01:00
nr_muls := table.type_nr_muls(t)
sym := g.table.get_type_symbol(t)
2020-03-29 10:09:27 +02:00
mut styp := sym.name.replace('.', '__')
2020-03-06 22:36:51 +01:00
if nr_muls > 0 {
styp += strings.repeat(`*`, nr_muls)
}
if styp.starts_with('C__') {
styp = styp[3..]
2020-03-31 15:18:25 +02:00
if sym.kind == .struct_ {
styp = 'struct $styp'
}
}
2020-03-13 05:57:51 +01:00
if table.type_is_optional(t) {
styp = 'Option_' + styp
2020-03-15 08:18:42 +01:00
if !(styp in g.optionals) {
g.definitions.writeln('typedef Option $styp;')
g.optionals << styp
}
2020-03-13 05:57:51 +01:00
}
2020-03-06 22:36:51 +01:00
return styp
}
//
pub fn (g mut Gen) write_typedef_types() {
2020-03-06 13:43:22 +01:00
for typ in g.table.types {
match typ.kind {
.alias {
parent := &g.table.types[typ.parent_idx]
styp := typ.name.replace('.', '__')
parent_styp := parent.name.replace('.', '__')
g.definitions.writeln('typedef $parent_styp $styp;')
}
.array {
styp := typ.name.replace('.', '__')
g.definitions.writeln('typedef array $styp;')
}
.array_fixed {
styp := typ.name.replace('.', '__')
// array_fixed_char_300 => char x[300]
mut fixed := styp[12..]
len := styp.after('_')
fixed = fixed[..fixed.len - len.len - 1]
g.definitions.writeln('typedef $fixed $styp [$len];')
}
.map {
styp := typ.name.replace('.', '__')
g.definitions.writeln('typedef map $styp;')
}
2020-03-16 10:12:03 +01:00
.function {
info := typ.info as table.FnType
func := info.func
if !info.has_decl && !info.is_anon {
fn_name := if func.is_c { func.name.replace('.', '__') } else { c_name(func.name) }
2020-03-16 10:12:03 +01:00
g.definitions.write('typedef ${g.typ(func.return_type)} (*$fn_name)(')
for i, arg in func.args {
2020-03-16 10:12:03 +01:00
g.definitions.write(g.typ(arg.typ))
if i < func.args.len - 1 {
g.definitions.write(',')
}
}
g.definitions.writeln(');')
}
}
else {
continue
}
2020-03-31 06:34:59 +02:00
}
2020-03-06 13:43:22 +01:00
}
}
2020-03-05 23:27:21 +01:00
pub fn (g mut Gen) write_multi_return_types() {
g.definitions.writeln('// multi return structs')
for typ in g.table.types {
// sym := g.table.get_type_symbol(typ)
if typ.kind != .multi_return {
continue
}
name := typ.name.replace('.', '__')
info := typ.info as table.MultiReturn
g.definitions.writeln('typedef struct {')
// TODO copy pasta StructDecl
// for field in struct_info.fields {
for i, mr_typ in info.types {
type_name := g.typ(mr_typ)
2020-03-05 00:43:02 +01:00
g.definitions.writeln('\t$type_name arg${i};')
}
g.definitions.writeln('} $name;\n')
// g.typedefs.writeln('typedef struct $name $name;')
}
2019-12-24 18:54:43 +01:00
}
2020-03-14 13:15:07 +01:00
pub fn (g mut Gen) write_variadic_types() {
2020-03-24 09:42:16 +01:00
if g.variadic_args.size > 0 {
2020-03-14 13:15:07 +01:00
g.definitions.writeln('// variadic structs')
}
2020-03-24 09:42:16 +01:00
for type_str, arg_len in g.variadic_args {
2020-03-14 13:15:07 +01:00
typ := table.Type(type_str.int())
type_name := g.typ(typ)
struct_name := 'varg_' + type_name.replace('*', '_ptr')
g.definitions.writeln('struct $struct_name {')
g.definitions.writeln('\tint len;')
g.definitions.writeln('\t$type_name args[$arg_len];')
g.definitions.writeln('};\n')
g.typedefs.writeln('typedef struct $struct_name $struct_name;')
}
}
2019-12-28 11:02:06 +01:00
pub fn (g &Gen) save() {}
2019-12-24 18:54:43 +01:00
pub fn (g mut Gen) write(s string) {
if g.indent > 0 && g.empty_line {
g.out.write(tabs[g.indent])
// g.line_len += g.indent * 4
}
2019-12-24 18:54:43 +01:00
g.out.write(s)
g.empty_line = false
2019-12-24 18:54:43 +01:00
}
pub fn (g mut Gen) writeln(s string) {
if g.indent > 0 && g.empty_line {
g.out.write(tabs[g.indent])
}
2019-12-24 18:54:43 +01:00
g.out.writeln(s)
g.empty_line = true
2019-12-24 18:54:43 +01:00
}
pub fn (g mut Gen) new_tmp_var() string {
g.tmp_count++
return 'tmp$g.tmp_count'
}
pub fn (g mut Gen) reset_tmp_count() {
g.tmp_count = 0
}
2020-02-07 14:49:14 +01:00
fn (g mut Gen) stmts(stmts []ast.Stmt) {
g.indent++
2020-02-07 14:49:14 +01:00
for stmt in stmts {
g.stmt(stmt)
// if !g.inside_ternary {
// g.writeln('')
// }
2020-02-07 14:49:14 +01:00
}
g.indent--
2020-02-07 14:49:14 +01:00
}
2019-12-28 14:11:05 +01:00
fn (g mut Gen) stmt(node ast.Stmt) {
2020-03-19 15:18:29 +01:00
g.stmt_start_pos = g.out.len
2020-01-06 16:13:12 +01:00
// println('cgen.stmt()')
// g.writeln('//// stmt start')
2019-12-28 14:11:05 +01:00
match node {
2020-03-02 18:43:41 +01:00
ast.AssertStmt {
g.gen_assert_stmt(it)
2020-03-26 10:27:46 +01:00
}
ast.AssignStmt {
g.gen_assign_stmt(it)
2020-03-02 18:43:41 +01:00
}
ast.Attr {
2020-03-05 00:43:02 +01:00
g.writeln('//[$it.name]')
2020-03-02 18:43:41 +01:00
}
2020-03-25 14:24:48 +01:00
ast.Block {
g.writeln('{')
g.stmts(it.stmts)
g.writeln('}')
}
2020-03-02 18:43:41 +01:00
ast.BranchStmt {
// continue or break
2020-03-05 00:43:02 +01:00
g.write(it.tok.kind.str())
g.writeln(';')
2020-03-02 18:43:41 +01:00
}
ast.ConstDecl {
2020-03-06 14:03:35 +01:00
g.const_decl(it)
}
2020-03-02 18:43:41 +01:00
ast.CompIf {
2020-03-27 14:44:30 +01:00
g.comp_if(it)
2020-03-02 18:43:41 +01:00
}
ast.DeferStmt {
mut defer_stmt := *it
defer_stmt.ifdef = g.defer_ifdef
g.defer_stmts << defer_stmt
}
2020-02-25 11:52:41 +01:00
ast.EnumDecl {
g.writeln('//')
/*
name := it.name.replace('.', '__')
g.definitions.writeln('typedef enum {')
2020-02-25 11:52:41 +01:00
for i, val in it.vals {
g.definitions.writeln('\t${name}_$val, // $i')
2020-02-25 11:52:41 +01:00
}
g.definitions.writeln('} $name;')
*/
2020-02-25 11:52:41 +01:00
}
2020-03-02 18:43:41 +01:00
ast.ExprStmt {
g.expr(it.expr)
2020-03-18 19:56:59 +01:00
expr := it.expr
match expr {
2020-03-02 18:43:41 +01:00
// no ; after an if expression
ast.IfExpr {}
else {
if !g.inside_ternary {
g.writeln(';')
}
2020-03-02 18:43:41 +01:00
}
}
}
2019-12-28 14:11:05 +01:00
ast.FnDecl {
2020-02-03 11:13:36 +01:00
g.fn_decl = it // &it
g.gen_fn_decl(it)
g.writeln('')
2019-12-28 14:11:05 +01:00
}
2020-03-02 18:43:41 +01:00
ast.ForCStmt {
g.write('for (')
if !it.has_init {
g.write('; ')
}
else {
g.stmt(it.init)
}
2020-03-02 18:43:41 +01:00
g.expr(it.cond)
g.write('; ')
2020-03-05 01:20:36 +01:00
// g.stmt(it.inc)
2020-03-05 00:43:02 +01:00
g.expr(it.inc)
2020-03-02 18:43:41 +01:00
g.writeln(') {')
g.stmts(it.stmts)
2020-03-02 18:43:41 +01:00
g.writeln('}')
}
ast.ForInStmt {
g.for_in(it)
2020-03-02 18:43:41 +01:00
}
ast.ForStmt {
g.write('while (')
2020-03-10 23:21:26 +01:00
if it.is_inf {
g.write('1')
}
else {
g.expr(it.cond)
}
2020-03-07 06:02:32 +01:00
g.writeln(') {')
g.stmts(it.stmts)
2020-03-02 18:43:41 +01:00
g.writeln('}')
}
ast.GlobalDecl {
2020-03-06 22:36:51 +01:00
styp := g.typ(it.typ)
2020-03-06 16:31:40 +01:00
g.definitions.writeln('$styp $it.name; // global')
2020-03-02 18:43:41 +01:00
}
ast.GotoLabel {
g.writeln('$it.name:')
}
2020-03-25 17:24:55 +01:00
ast.GotoStmt {
g.writeln('goto $it.name;')
}
2020-03-02 18:43:41 +01:00
ast.HashStmt {
2020-03-05 00:43:02 +01:00
// #include etc
2020-03-15 00:46:08 +01:00
typ := it.val.all_before(' ')
if typ in ['include', 'define'] {
2020-03-15 00:46:08 +01:00
g.definitions.writeln('#$it.val')
}
2020-03-02 18:43:41 +01:00
}
ast.Import {}
2019-12-28 14:11:05 +01:00
ast.Return {
if g.defer_stmts.len > 0 {
g.write_defer_stmts()
}
2020-03-13 05:57:51 +01:00
g.return_statement(it)
2019-12-28 14:11:05 +01:00
}
ast.StructDecl {
name := if it.is_c { it.name.replace('.', '__') } else { c_name(it.name) }
2020-03-05 23:27:21 +01:00
// g.writeln('typedef struct {')
// for field in it.fields {
// field_type_sym := g.table.get_type_symbol(field.typ)
// g.writeln('\t$field_type_sym.name $field.name;')
// }
// g.writeln('} $name;')
if !it.is_c {
g.typedefs.writeln('typedef struct $name $name;')
}
}
ast.TypeDecl {
2020-03-16 03:19:26 +01:00
g.writeln('// TypeDecl')
}
2020-03-02 18:43:41 +01:00
ast.UnsafeStmt {
g.stmts(it.stmts)
}
2019-12-28 14:11:05 +01:00
else {
2020-02-25 11:52:41 +01:00
verror('cgen.stmt(): unhandled node ' + typeof(node))
2019-12-28 14:11:05 +01:00
}
}
}
fn (g mut Gen) write_defer_stmts() {
for defer_stmt in g.defer_stmts {
g.writeln('// defer')
if defer_stmt.ifdef.len > 0 {
g.writeln(defer_stmt.ifdef)
g.stmts(defer_stmt.stmts)
g.writeln('#endif')
}
else {
g.stmts(defer_stmt.stmts)
}
}
}
fn (g mut Gen) for_in(it ast.ForInStmt) {
if it.is_range {
// `for x in 1..10 {`
i := g.new_tmp_var()
g.write('for (int $i = ')
g.expr(it.cond)
g.write('; $i < ')
g.expr(it.high)
g.writeln('; $i++) { ')
g.writeln('\tint $it.val_var = $i;')
g.stmts(it.stmts)
g.writeln('}')
}
// TODO:
else if it.kind == .array {
// `for num in nums {`
g.writeln('// FOR IN')
i := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
styp := g.typ(it.val_type)
g.write('for (int $i = 0; $i < ')
g.expr(it.cond)
g.writeln('.len; $i++) {')
g.write('\t$styp $it.val_var = (($styp*)')
g.expr(it.cond)
g.writeln('.data)[$i];')
g.stmts(it.stmts)
g.writeln('}')
}
else if it.kind == .map {
// `for num in nums {`
g.writeln('// FOR IN')
key_styp := g.typ(it.key_type)
val_styp := g.typ(it.val_type)
keys_tmp := 'keys_' + g.new_tmp_var()
idx := g.new_tmp_var()
key := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
zero := g.type_default(it.val_type)
g.write('array_$key_styp $keys_tmp = map_keys(&')
g.expr(it.cond)
g.writeln(');')
g.writeln('for (int $idx = 0; $idx < ${keys_tmp}.len; $idx++) {')
g.writeln('\t$key_styp $key = (($key_styp*)${keys_tmp}.data)[$idx];')
g.write('\t$val_styp $it.val_var = (*($val_styp*)map_get3(')
g.expr(it.cond)
g.writeln(', $key, &($val_styp[]){ $zero }));')
g.stmts(it.stmts)
g.writeln('}')
}
else if table.type_is_variadic(it.cond_type) {
g.writeln('// FOR IN')
i := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
styp := g.typ(it.cond_type)
g.write('for (int $i = 0; $i < ')
g.expr(it.cond)
g.writeln('.len; $i++) {')
g.write('$styp $it.val_var = ')
g.expr(it.cond)
g.writeln('.args[$i];')
g.stmts(it.stmts)
g.writeln('}')
}
else if it.kind == .string {
i := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
g.write('for (int $i = 0; $i < ')
g.expr(it.cond)
g.writeln('.len; $i++) {')
g.write('byte $it.val_var = ')
g.expr(it.cond)
g.writeln('.str[$i];')
g.stmts(it.stmts)
g.writeln('}')
}
}
2020-03-17 15:13:55 +01:00
// use instead of expr() when you need to cast to sum type (can add other casts also)
2020-03-19 09:40:21 +01:00
fn (g mut Gen) expr_with_cast(expr ast.Expr, got_type table.Type, exp_type table.Type) {
2020-03-17 15:13:55 +01:00
// cast to sum type
2020-03-24 12:55:41 +01:00
if exp_type != table.void_type {
2020-03-16 07:42:45 +01:00
exp_sym := g.table.get_type_symbol(exp_type)
if exp_sym.kind == .sum_type {
sum_info := exp_sym.info as table.SumType
if got_type in sum_info.variants {
got_sym := g.table.get_type_symbol(got_type)
2020-03-17 15:13:55 +01:00
got_styp := g.typ(got_type)
exp_styp := g.typ(exp_type)
got_idx := table.type_idx(got_type)
2020-03-17 16:40:41 +01:00
g.write('/* sum type cast */ ($exp_styp) {.obj = memdup(&(${got_styp}[]) {')
2020-03-17 15:13:55 +01:00
g.expr(expr)
g.write('}, sizeof($got_styp)), .typ = $got_idx /* $got_sym.name */}')
2020-03-18 10:42:56 +01:00
return
2020-03-16 07:42:45 +01:00
}
}
}
2020-03-17 15:13:55 +01:00
// no cast
2020-03-16 07:42:45 +01:00
g.expr(expr)
}
fn (g mut Gen) gen_assert_stmt(a ast.AssertStmt) {
g.writeln('// assert')
g.write('if( ')
g.expr(a.expr)
s_assertion := a.expr.str().replace('"', "\'")
g.write(' )')
if g.is_test {
g.writeln('{')
g.writeln(' g_test_oks++;')
2020-03-30 17:21:32 +02:00
g.writeln(' cb_assertion_ok( _STR("${g.file.path}"), ${a.pos.line_nr}, _STR("assert ${s_assertion}"), _STR("${g.fn_decl.name}()") );')
// g.writeln(' println(_STR("OK ${g.file.path}:${a.pos.line_nr}: fn ${g.fn_decl.name}(): assert $s_assertion"));')
g.writeln('}else{')
g.writeln(' g_test_fails++;')
2020-03-30 17:21:32 +02:00
g.writeln(' cb_assertion_failed( _STR("${g.file.path}"), ${a.pos.line_nr}, _STR("assert ${s_assertion}"), _STR("${g.fn_decl.name}()") );')
g.writeln(' exit(1);')
2020-03-30 17:21:32 +02:00
g.writeln(' // TODO')
g.writeln(' // Maybe print all vars in a test function if it fails?')
g.writeln('}')
2020-03-30 17:21:32 +02:00
return
}
2020-03-30 17:21:32 +02:00
g.writeln('{}else{')
g.writeln(' eprintln(_STR("${g.file.path}:${a.pos.line_nr}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion"));')
g.writeln(' exit(1);')
g.writeln('}')
}
fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
2020-03-22 13:19:45 +01:00
// g.write('/*assign_stmt*/')
if assign_stmt.left.len > assign_stmt.right.len {
2020-03-26 14:58:11 +01:00
// multi return
2020-03-31 06:34:59 +02:00
mut or_stmts := []ast.Stmt
mut return_type := table.void_type
match assign_stmt.right[0] {
ast.CallExpr {
2020-03-31 06:34:59 +02:00
or_stmts = it.or_block.stmts
return_type = it.return_type
}
2020-03-31 06:34:59 +02:00
else {}
}
2020-03-31 06:34:59 +02:00
is_optional := table.type_is_optional(return_type)
mr_var_name := 'mr_$assign_stmt.pos.pos'
2020-03-31 06:34:59 +02:00
mr_styp := g.typ(return_type)
g.write('$mr_styp $mr_var_name = ')
2020-03-31 10:11:47 +02:00
g.is_assign_rhs = true
2020-03-31 06:34:59 +02:00
g.expr(assign_stmt.right[0])
2020-03-31 10:11:47 +02:00
g.is_assign_rhs = false
2020-03-31 06:34:59 +02:00
if is_optional {
g.or_block(mr_var_name, or_stmts, return_type)
2020-03-20 04:59:06 +01:00
}
g.writeln(';')
for i, ident in assign_stmt.left {
if ident.kind == .blank_ident {
continue
}
ident_var_info := ident.var_info()
styp := g.typ(ident_var_info.typ)
if assign_stmt.op == .decl_assign {
g.write('$styp ')
}
g.expr(ident)
2020-03-31 06:34:59 +02:00
if is_optional {
mr_styp2 := mr_styp[7..] // remove Option_
g.writeln(' = (*(${mr_styp2}*)${mr_var_name}.data).arg$i;')
}
else {
g.writeln(' = ${mr_var_name}.arg$i;')
}
}
}
// `a := 1` | `a,b := 1,2`
else {
for i, ident in assign_stmt.left {
val := assign_stmt.right[i]
ident_var_info := ident.var_info()
2020-03-10 23:21:26 +01:00
styp := g.typ(ident_var_info.typ)
2020-03-31 06:34:59 +02:00
mut is_call := false
mut or_stmts := []ast.Stmt
mut return_type := table.void_type
match val {
ast.CallExpr {
is_call = true
or_stmts = it.or_block.stmts
return_type = it.return_type
}
else {}
}
gen_or := is_call && table.type_is_optional(return_type)
2020-03-31 10:11:47 +02:00
g.is_assign_rhs = true
if ident.kind == .blank_ident {
2020-03-31 06:34:59 +02:00
if is_call {
2020-03-10 23:21:26 +01:00
g.expr(val)
}
else {
2020-03-10 23:21:26 +01:00
g.write('{$styp _ = ')
g.expr(val)
g.writeln(';}')
}
}
else {
2020-03-22 13:19:45 +01:00
right_sym := g.table.get_type_symbol(assign_stmt.right_types[i])
mut is_fixed_array_init := false
2020-03-12 10:07:42 +01:00
match val {
ast.ArrayInit {
2020-03-22 13:19:45 +01:00
is_fixed_array_init = right_sym.kind == .array_fixed
}
2020-03-12 10:07:42 +01:00
else {}
}
2020-03-17 15:13:55 +01:00
is_decl := assign_stmt.op == .decl_assign
g.write('/*assign_stmt*/')
2020-03-17 15:13:55 +01:00
if is_decl {
g.write('$styp ')
}
g.expr(ident)
if g.autofree && right_sym.kind in [.array, .string] {
if g.gen_clone_assignment(val, right_sym, true) {
g.writeln(';')
// g.expr_var_name = ''
return
}
2020-03-22 13:40:53 +01:00
}
if !is_fixed_array_init {
2020-03-12 10:07:42 +01:00
g.write(' = ')
2020-03-17 15:13:55 +01:00
if !is_decl {
2020-03-19 09:40:21 +01:00
g.expr_with_cast(val, assign_stmt.left_types[i], ident_var_info.typ)
}
else {
2020-03-16 07:42:45 +01:00
g.expr(val)
}
2020-03-12 10:07:42 +01:00
}
else if is_fixed_array_init {
g.write('= {0}')
}
2020-03-31 06:34:59 +02:00
if gen_or {
g.or_block(ident.name, or_stmts, return_type)
}
}
2020-03-31 10:11:47 +02:00
g.is_assign_rhs = false
g.writeln(';')
}
}
}
fn (g mut Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, add_eq bool) bool {
mut is_ident := false
match val {
ast.Ident {
is_ident = true
}
ast.SelectorExpr {
is_ident = true
}
else {
return false
}
}
if g.autofree && right_sym.kind == .array && is_ident {
// `arr1 = arr2` => `arr1 = arr2.clone()`
if add_eq {
g.write('=')
}
g.write(' array_clone(&')
g.expr(val)
g.write(')')
}
else if g.autofree && right_sym.kind == .string && is_ident {
if add_eq {
g.write('=')
}
// `str1 = str2` => `str1 = str2.clone()`
g.write(' string_clone(')
g.expr(val)
g.write(')')
}
return true
}
fn (g mut Gen) gen_fn_decl(it ast.FnDecl) {
2020-03-24 14:46:00 +01:00
if it.is_c {
2020-03-22 14:47:43 +01:00
// || it.no_body {
return
}
g.reset_tmp_count()
is_main := it.name == 'main'
if is_main {
2020-03-21 10:22:16 +01:00
g.write('int ${it.name}(int argc, char** argv')
}
else {
mut name := it.name
if it.is_method {
name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name
}
if it.is_c {
name = name.replace('.', '__')
}
else {
name = c_name(name)
}
if name.starts_with('_op_') {
name = op_to_fn_name(name)
2020-03-21 19:52:19 +01:00
}
// type_name := g.table.type_to_str(it.return_type)
type_name := g.typ(it.return_type)
g.write('$type_name ${name}(')
g.definitions.write('$type_name ${name}(')
}
// Receiver is the first argument
2020-03-10 23:21:26 +01:00
/*
if it.is_method {
2020-03-07 22:37:03 +01:00
mut styp := g.typ(it.receiver.typ)
// if table.type_nr_muls(it.receiver.typ) > 0 {
2020-03-08 19:38:27 +01:00
// if it.rec_mut {
// styp += '*'
// }
2020-03-10 23:21:26 +01:00
g.write('$styp $it.receiver.name ')
2020-03-07 04:45:35 +01:00
// TODO mut
g.definitions.write('$styp $it.receiver.name')
if it.args.len > 0 {
g.write(', ')
g.definitions.write(', ')
}
}
2020-03-10 23:21:26 +01:00
*/
//
g.fn_args(it.args, it.is_variadic)
2020-03-22 14:47:43 +01:00
if it.no_body {
// Just a function header.
g.definitions.writeln(');')
g.writeln(');')
return
}
g.writeln(') { ')
if !is_main {
g.definitions.writeln(');')
}
2020-03-21 10:22:16 +01:00
if is_main {
2020-03-31 16:47:55 +02:00
g.writeln('\t_vinit();')
if g.is_importing_os() {
if g.autofree {
g.writeln('free(_const_os__args.data); // empty, inited in _vinit()')
}
g.writeln('_const_os__args = os__init_os_args(argc, (byteptr*)argv);')
2020-03-21 19:52:19 +01:00
}
2020-03-21 10:22:16 +01:00
}
g.stmts(it.stmts)
2020-03-21 19:52:19 +01:00
// ////////////
2020-03-22 08:59:44 +01:00
if g.autofree {
2020-03-25 10:26:54 +01:00
g.free_scope_vars(it.pos.pos - 1)
2020-03-21 19:52:19 +01:00
}
// /////////
if is_main {
2020-03-21 19:52:19 +01:00
if g.autofree {
2020-03-31 16:47:55 +02:00
g.writeln('\t_vcleanup();')
2020-03-21 19:52:19 +01:00
}
if g.is_test {
verror('test files cannot have function `main`')
}
2020-03-31 16:47:55 +02:00
g.writeln('\treturn 0;')
}
if g.defer_stmts.len > 0 {
g.write_defer_stmts()
}
g.writeln('}')
g.defer_stmts = []
g.fn_decl = 0
}
2020-03-25 10:26:54 +01:00
fn (g mut Gen) free_scope_vars(pos int) {
scope := g.file.scope.innermost(pos)
for _, var in scope.vars {
2020-03-25 16:00:22 +01:00
// println('//////')
// println(var.name)
// println(var.typ)
2020-03-28 15:15:10 +01:00
// if var.typ == 0 {
2020-03-31 06:34:59 +02:00
// // TODO why 0?
// continue
2020-03-28 15:15:10 +01:00
// }
2020-03-25 10:26:54 +01:00
sym := g.table.get_type_symbol(var.typ)
if sym.kind == .array && !table.type_is_optional(var.typ) {
g.writeln('array_free($var.name); // autofreed')
}
if sym.kind == .string && !table.type_is_optional(var.typ) {
// Don't free simple string literals.
t := typeof(var.expr)
match var.expr {
ast.StringLiteral {
g.writeln('// str literal')
continue
}
else {
2020-03-31 06:34:59 +02:00
// NOTE/TODO: assign_stmt multi returns variables have no expr
// since the type comes from the called fns return type
g.writeln('// other ' + t)
2020-03-31 06:34:59 +02:00
continue
2020-03-25 10:26:54 +01:00
}
2020-03-31 06:34:59 +02:00
}
2020-03-25 10:26:54 +01:00
g.writeln('string_free($var.name); // autofreed')
}
}
}
fn (g mut Gen) fn_args(args []table.Arg, is_variadic bool) {
no_names := args.len > 0 && args[0].name == 'arg_1'
for i, arg in args {
arg_type_sym := g.table.get_type_symbol(arg.typ)
2020-03-15 00:46:08 +01:00
mut arg_type_name := g.typ(arg.typ) // arg_type_sym.name.replace('.', '__')
is_varg := i == args.len - 1 && is_variadic
2020-03-14 13:15:07 +01:00
if is_varg {
2020-03-24 09:42:16 +01:00
varg_type_str := int(arg.typ).str()
if !(varg_type_str in g.variadic_args) {
2020-03-24 12:55:41 +01:00
g.variadic_args[varg_type_str] = 0
2020-03-24 09:42:16 +01:00
}
2020-03-14 13:15:07 +01:00
arg_type_name = 'varg_' + g.typ(arg.typ).replace('*', '_ptr')
}
2020-03-12 04:34:00 +01:00
if arg_type_sym.kind == .function {
2020-03-16 10:12:03 +01:00
info := arg_type_sym.info as table.FnType
func := info.func
if !info.is_anon {
g.write(arg_type_name + ' ' + arg.name)
g.definitions.write(arg_type_name + ' ' + arg.name)
}
else {
g.write('${g.typ(func.return_type)} (*$arg.name)(')
g.definitions.write('${g.typ(func.return_type)} (*$arg.name)(')
g.fn_args(func.args, func.is_variadic)
g.write(')')
g.definitions.write(')')
}
2020-03-12 04:34:00 +01:00
}
else if no_names {
g.write(arg_type_name)
g.definitions.write(arg_type_name)
}
else {
2020-03-11 21:11:27 +01:00
mut nr_muls := table.type_nr_muls(arg.typ)
2020-03-15 00:46:08 +01:00
s := arg_type_name + ' ' + arg.name
2020-03-11 21:11:27 +01:00
if arg.is_mut {
// mut arg needs one *
nr_muls = 1
}
2020-03-15 00:46:08 +01:00
// if nr_muls > 0 && !is_varg {
// s = arg_type_name + strings.repeat(`*`, nr_muls) + ' ' + arg.name
// }
2020-03-06 20:22:58 +01:00
g.write(s)
g.definitions.write(s)
}
if i < args.len - 1 {
g.write(', ')
g.definitions.write(', ')
}
}
}
fn (g mut Gen) expr(node ast.Expr) {
// println('cgen expr() line_nr=$node.pos.line_nr')
2019-12-24 18:54:43 +01:00
match node {
2020-02-17 22:50:04 +01:00
ast.ArrayInit {
type_sym := g.table.get_type_symbol(it.typ)
2020-03-12 09:29:52 +01:00
if type_sym.kind != .array_fixed {
elem_sym := g.table.get_type_symbol(it.elem_type)
2020-03-15 07:42:45 +01:00
elem_type_str := g.typ(it.elem_type)
2020-03-21 19:52:19 +01:00
if it.exprs.len == 0 {
g.write('new_array($it.exprs.len, $it.exprs.len, sizeof($elem_type_str))')
}
else {
len := it.exprs.len
g.write('new_array_from_c_array($len, $len, sizeof($elem_type_str), ')
g.writeln('($elem_type_str[$len]){\t')
2020-03-21 19:52:19 +01:00
for expr in it.exprs {
g.expr(expr)
g.write(', ')
}
g.write('\n})')
2020-03-12 09:29:52 +01:00
}
2020-02-17 22:50:04 +01:00
}
2020-03-12 09:29:52 +01:00
else {}
2020-02-17 22:50:04 +01:00
}
ast.AsCast {
2020-03-18 13:55:46 +01:00
styp := g.typ(it.typ)
expr_type_sym := g.table.get_type_symbol(it.expr_type)
if expr_type_sym.kind == .sum_type {
g.write('/* as */ *($styp*)')
g.expr(it.expr)
g.write('.obj')
}
}
2020-01-06 16:13:12 +01:00
ast.AssignExpr {
2020-03-31 06:34:59 +02:00
g.assign_expr(it)
2020-01-06 16:13:12 +01:00
}
ast.Assoc {
2020-03-19 07:59:01 +01:00
g.assoc(it)
}
2020-02-17 22:50:04 +01:00
ast.BoolLiteral {
g.write(it.val.str())
}
2019-12-29 07:24:17 +01:00
ast.CallExpr {
2020-03-21 19:52:19 +01:00
g.call_expr(it)
}
ast.CastExpr {
2020-03-10 23:21:26 +01:00
// g.write('/*cast*/')
if g.is_amp {
// &Foo(0) => ((Foo*)0)
g.out.go_back(1)
}
2020-03-07 16:23:10 +01:00
if it.typ == table.string_type_idx {
// `tos(str, len)`, `tos2(str)`
2020-03-10 23:21:26 +01:00
if it.has_arg {
g.write('tos(')
}
else {
g.write('tos2(')
}
2020-03-07 16:23:10 +01:00
g.expr(it.expr)
sym := g.table.get_type_symbol(it.expr_type)
if sym.kind == .array {
// if we are casting an array, we need to add `.data`
g.write('.data')
}
2020-03-07 16:23:10 +01:00
if it.has_arg {
// len argument
g.write(', ')
2020-03-07 16:23:10 +01:00
g.expr(it.arg)
}
g.write(')')
}
else {
2020-03-10 23:21:26 +01:00
// styp := g.table.type_to_str(it.typ)
styp := g.typ(it.typ)
// g.write('($styp)(')
g.write('(($styp)(')
// if g.is_amp {
// g.write('*')
// }
// g.write(')(')
2020-03-07 16:23:10 +01:00
g.expr(it.expr)
2020-03-10 23:21:26 +01:00
g.write('))')
2020-03-07 16:23:10 +01:00
}
2019-12-29 07:24:17 +01:00
}
ast.CharLiteral {
g.write("'$it.val'")
}
ast.EnumVal {
2020-03-15 00:46:08 +01:00
// g.write('/*EnumVal*/${it.mod}${it.enum_name}_$it.val')
styp := g.typ(it.typ)
g.write(styp)
2020-03-15 02:51:31 +01:00
g.write('_$it.val')
}
ast.FloatLiteral {
g.write(it.val)
}
2019-12-28 14:11:05 +01:00
ast.Ident {
2020-03-13 05:57:51 +01:00
g.ident(it)
2019-12-24 18:54:43 +01:00
}
ast.IfExpr {
2020-03-17 16:40:41 +01:00
g.if_expr(it)
}
ast.IfGuardExpr {
g.write('/* guard */')
}
ast.IndexExpr {
g.index_expr(it)
}
ast.InfixExpr {
2020-03-07 16:39:15 +01:00
g.infix_expr(it)
}
ast.IntegerLiteral {
2020-03-26 17:14:24 +01:00
if it.val.starts_with('0o') {
g.write('0')
g.write(it.val[2..])
}
else {
g.write(it.val) // .int().str())
}
}
2020-02-07 14:49:14 +01:00
ast.MatchExpr {
2020-03-16 03:56:38 +01:00
g.match_expr(it)
2020-02-07 14:49:14 +01:00
}
2020-03-07 08:13:00 +01:00
ast.MapInit {
key_typ_sym := g.table.get_type_symbol(it.key_type)
value_typ_sym := g.table.get_type_symbol(it.value_type)
2020-03-17 15:13:55 +01:00
key_typ_str := key_typ_sym.name.replace('.', '__')
value_typ_str := value_typ_sym.name.replace('.', '__')
2020-03-07 08:13:00 +01:00
size := it.vals.len
2020-03-07 14:31:40 +01:00
if size > 0 {
2020-03-17 15:13:55 +01:00
g.write('new_map_init($size, sizeof($value_typ_str), (${key_typ_str}[$size]){')
2020-03-07 08:13:00 +01:00
for expr in it.keys {
g.expr(expr)
g.write(', ')
}
2020-03-17 15:13:55 +01:00
g.write('}, (${value_typ_str}[$size]){')
2020-03-07 08:13:00 +01:00
for expr in it.vals {
g.expr(expr)
g.write(', ')
}
g.write('})')
}
else {
2020-03-17 15:13:55 +01:00
g.write('new_map(1, sizeof($value_typ_str))')
2020-03-07 08:13:00 +01:00
}
}
ast.None {
2020-03-13 05:57:51 +01:00
g.write('opt_none()')
}
ast.ParExpr {
g.write('(')
g.expr(it.expr)
g.write(')')
}
ast.PostfixExpr {
g.expr(it.expr)
g.write(it.op.str())
}
ast.PrefixExpr {
2020-03-10 23:21:26 +01:00
if it.op == .amp {
g.is_amp = true
}
2020-03-06 20:25:38 +01:00
// g.write('/*pref*/')
2020-03-10 23:21:26 +01:00
g.write(it.op.str())
g.expr(it.right)
2020-03-10 23:21:26 +01:00
g.is_amp = false
}
/*
ast.UnaryExpr {
// probably not :D
if it.op in [.inc, .dec] {
g.expr(it.left)
g.write(it.op.str())
}
else {
g.write(it.op.str())
g.expr(it.left)
}
}
*/
ast.SizeOf {
styp := g.typ(it.typ)
g.write('sizeof($styp)')
}
ast.StringLiteral {
escaped_val := it.val.replace_each(['"', '\\"',
'\r\n', '\\n',
'\n', '\\n'])
if g.is_c_call {
2020-03-21 07:10:53 +01:00
// In C calls we have to generate C strings
// `C.printf("hi")` => `printf("hi");`
g.write('"$escaped_val"')
}
else {
g.write('tos3("$escaped_val")')
}
}
2020-03-21 07:01:06 +01:00
ast.StringInterLiteral {
g.string_inter_literal(it)
}
// `user := User{name: 'Bob'}`
ast.StructInit {
g.struct_init(it)
}
ast.SelectorExpr {
g.expr(it.expr)
2020-03-08 19:38:27 +01:00
// if table.type_nr_muls(it.expr_type) > 0 {
if table.type_is_ptr(it.expr_type) {
2020-03-07 04:45:35 +01:00
g.write('->')
}
else {
2020-03-11 05:25:11 +01:00
// g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /')
2020-03-07 04:45:35 +01:00
g.write('.')
}
2020-03-11 05:25:11 +01:00
if it.expr_type == 0 {
verror('cgen: SelectorExpr typ=0 field=$it.field')
}
g.write(c_name(it.field))
}
ast.Type {
2020-03-16 03:19:26 +01:00
// match sum Type
// g.write('/* Type */')
type_idx := table.type_idx(it.typ)
sym := g.table.get_type_symbol(it.typ)
g.write('$type_idx /* $sym.name */')
}
2020-03-19 12:15:39 +01:00
ast.TypeOf {
2020-03-28 17:37:22 +01:00
g.typeof_expr(it)
2020-03-19 12:15:39 +01:00
}
2019-12-24 18:54:43 +01:00
else {
// #printf("node=%d\n", node.typ);
println(term.red('cgen.expr(): bad node ' + typeof(node)))
2019-12-24 18:54:43 +01:00
}
}
}
2020-03-28 17:37:22 +01:00
fn (g mut Gen) typeof_expr(node ast.TypeOf) {
sym := g.table.get_type_symbol(node.expr_type)
if sym.kind == .sum_type {
// When encountering a .sum_type, typeof() should be done at runtime,
// because the subtype of the expression may change:
2020-03-31 06:34:59 +02:00
sum_type_idx := table.type_idx(node.expr_type)
2020-03-28 17:37:22 +01:00
g.write('tos3( /* ${sym.name} */ v_typeof_sumtype_${sum_type_idx}( (')
g.expr(node.expr)
g.write(').typ ))')
2020-03-31 06:34:59 +02:00
}
else {
2020-03-28 17:37:22 +01:00
g.write('tos3("${sym.name}")')
}
}
2020-03-31 06:34:59 +02:00
fn (g mut Gen) assign_expr(node ast.AssignExpr) {
// g.write('/*assign_expr*/')
mut is_call := false
mut or_stmts := []ast.Stmt
mut return_type := table.void_type
match node.val {
ast.CallExpr {
is_call = true
or_stmts = it.or_block.stmts
return_type = it.return_type
}
else {}
}
gen_or := is_call && table.type_is_optional(return_type)
tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
if gen_or {
rstyp := g.typ(return_type)
g.write('$rstyp $tmp_opt =')
}
2020-03-31 10:11:47 +02:00
g.is_assign_rhs = true
2020-03-31 06:34:59 +02:00
if ast.expr_is_blank_ident(node.left) {
if is_call {
g.expr(node.val)
}
else {
g.write('{${g.typ(node.left_type)} _ = ')
g.expr(node.val)
g.writeln(';}')
}
}
else {
2020-03-31 10:11:47 +02:00
g.is_assign_lhs = true
2020-03-31 06:34:59 +02:00
if table.type_is_optional(node.right_type) {
g.right_is_opt = true
}
mut str_add := false
if node.left_type == table.string_type_idx && node.op == .plus_assign {
// str += str2 => `str = string_add(str, str2)`
g.expr(node.left)
g.write(' = string_add(')
str_add = true
}
g.assign_op = node.op
g.expr(node.left)
// arr[i] = val => `array_set(arr, i, val)`, not `array_get(arr, i) = val`
if !g.is_array_set && !str_add {
g.write(' $node.op.str() ')
}
else if str_add {
g.write(', ')
}
2020-03-31 10:11:47 +02:00
g.is_assign_lhs = false
right_sym := g.table.get_type_symbol(node.right_type)
// left_sym := g.table.get_type_symbol(node.left_type)
mut cloned := false
// !g.is_array_set
if g.autofree && right_sym.kind in [.array, .string] {
if g.gen_clone_assignment(node.val, right_sym, false) {
cloned = true
}
}
if !cloned {
g.expr_with_cast(node.val, node.right_type, node.left_type)
}
2020-03-31 06:34:59 +02:00
if g.is_array_set {
g.write(' })')
g.is_array_set = false
}
else if str_add {
g.write(')')
}
g.right_is_opt = false
}
if gen_or {
g.or_block(tmp_opt, or_stmts, return_type)
}
2020-03-31 10:11:47 +02:00
g.is_assign_rhs = false
2020-03-31 06:34:59 +02:00
}
2020-03-12 10:07:42 +01:00
fn (g mut Gen) infix_expr(node ast.InfixExpr) {
// println('infix_expr() op="$node.op.str()" line_nr=$node.pos.line_nr')
// g.write('/*infix*/')
2020-03-07 16:39:15 +01:00
// if it.left_type == table.string_type_idx {
2020-03-12 10:07:42 +01:00
// g.write('/*$node.left_type str*/')
2020-03-07 16:39:15 +01:00
// }
// string + string, string == string etc
// g.infix_op = node.op
if node.left_type == table.string_type_idx && node.op != .key_in {
2020-03-12 10:07:42 +01:00
fn_name := match node.op {
2020-03-07 16:39:15 +01:00
.plus{
'string_add('
}
.eq{
'string_eq('
}
.ne{
'string_ne('
}
.lt{
'string_lt('
}
.le{
'string_le('
}
.gt{
'string_gt('
}
.ge{
'string_ge('
}
2020-03-07 16:39:15 +01:00
else {
2020-03-12 10:07:42 +01:00
'/*node error*/'}
2020-03-07 16:39:15 +01:00
}
g.write(fn_name)
2020-03-12 10:07:42 +01:00
g.expr(node.left)
2020-03-07 16:39:15 +01:00
g.write(', ')
2020-03-12 10:07:42 +01:00
g.expr(node.right)
2020-03-07 16:39:15 +01:00
g.write(')')
}
2020-03-12 10:07:42 +01:00
else if node.op == .key_in {
right_sym := g.table.get_type_symbol(node.right_type)
if right_sym.kind == .array {
2020-03-19 16:12:46 +01:00
match node.right {
ast.ArrayInit {
// `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3`
// avoids an allocation
// g.write('/*in opt*/')
g.write('(')
2020-03-19 16:12:46 +01:00
g.in_optimization(node.left, it)
g.write(')')
2020-03-19 16:12:46 +01:00
return
}
else {}
}
styp := g.typ(node.left_type)
g.write('_IN($styp, ')
g.expr(node.left)
g.write(', ')
g.expr(node.right)
g.write(')')
}
else if right_sym.kind == .map {
g.write('_IN_MAP(')
g.expr(node.left)
g.write(', ')
g.expr(node.right)
g.write(')')
}
else if right_sym.kind == .string {
g.write('string_contains(')
g.expr(node.right)
g.write(', ')
g.expr(node.left)
g.write(')')
}
2020-03-10 23:21:26 +01:00
}
2020-03-07 16:39:15 +01:00
// arr << val
2020-03-12 10:07:42 +01:00
else if node.op == .left_shift && g.table.get_type_symbol(node.left_type).kind == .array {
2020-03-10 23:21:26 +01:00
tmp := g.new_tmp_var()
2020-03-15 00:46:08 +01:00
sym := g.table.get_type_symbol(node.left_type)
info := sym.info as table.Array
2020-03-15 00:46:08 +01:00
right_sym := g.table.get_type_symbol(node.right_type)
if right_sym.kind == .array && info.elem_type != node.right_type {
// push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`)
2020-03-15 00:46:08 +01:00
g.write('_PUSH_MANY(&')
2020-03-19 09:40:21 +01:00
g.expr_with_cast(node.left, node.right_type, node.left_type)
2020-03-15 00:46:08 +01:00
g.write(', (')
g.expr(node.right)
styp := g.typ(node.left_type)
g.write('), $tmp, $styp)')
}
else {
// push a single element
elem_type_str := g.typ(info.elem_type)
// g.write('array_push(&')
g.write('_PUSH(&')
2020-03-19 09:40:21 +01:00
g.expr_with_cast(node.left, node.right_type, info.elem_type)
2020-03-15 00:46:08 +01:00
g.write(', (')
g.expr(node.right)
g.write('), $tmp, $elem_type_str)')
}
2020-03-07 16:39:15 +01:00
}
else {
2020-03-26 10:44:59 +01:00
need_par := node.op == .amp // `x & y == 0` => `(x & y) == 0` in C
if need_par {
g.write('(')
}
2020-03-12 10:07:42 +01:00
g.expr(node.left)
g.write(' $node.op.str() ')
g.expr(node.right)
2020-03-26 10:44:59 +01:00
if need_par {
g.write(')')
}
2020-03-07 16:39:15 +01:00
}
}
2020-03-16 03:56:38 +01:00
fn (g mut Gen) match_expr(node ast.MatchExpr) {
// println('match expr typ=$it.expr_type')
// TODO
2020-03-18 12:23:32 +01:00
if node.cond_type == 0 {
2020-03-16 03:56:38 +01:00
g.writeln('// match 0')
return
}
2020-03-18 12:18:48 +01:00
is_expr := node.is_expr && node.return_type != table.void_type
if is_expr {
g.inside_ternary = true
2020-03-18 12:34:27 +01:00
// g.write('/* EM ret type=${g.typ(node.return_type)} expected_type=${g.typ(node.expected_type)} */')
}
2020-03-18 12:23:32 +01:00
type_sym := g.table.get_type_symbol(node.cond_type)
2020-03-16 03:56:38 +01:00
mut tmp := ''
if type_sym.kind != .void {
tmp = g.new_tmp_var()
}
// styp := g.typ(node.expr_type)
// g.write('$styp $tmp = ')
// g.expr(node.cond)
// g.writeln(';') // $it.blocks.len')
2020-03-16 03:56:38 +01:00
// mut sum_type_str = ''
for j, branch in node.branches {
if j == node.branches.len - 1 {
// last block is an `else{}`
if is_expr {
// TODO too many branches. maybe separate ?: matches
g.write(' : ')
}
else {
g.writeln('else {')
}
2020-03-16 03:56:38 +01:00
}
else {
if j > 0 {
if is_expr {
g.write(' : ')
}
else {
g.write('else ')
}
}
if is_expr {
g.write('(')
}
else {
g.write('if (')
2020-03-16 03:56:38 +01:00
}
for i, expr in branch.exprs {
if node.is_sum_type {
g.expr(node.cond)
g.write('.typ == ')
// g.write('${tmp}.typ == ')
2020-03-16 03:56:38 +01:00
// sum_type_str
}
else if type_sym.kind == .string {
g.write('string_eq(')
//
g.expr(node.cond)
g.write(', ')
// g.write('string_eq($tmp, ')
2020-03-16 03:56:38 +01:00
}
else {
g.expr(node.cond)
g.write(' == ')
// g.write('$tmp == ')
2020-03-16 03:56:38 +01:00
}
g.expr(expr)
if type_sym.kind == .string {
g.write(')')
}
if i < branch.exprs.len - 1 {
g.write(' || ')
}
}
if is_expr {
g.write(') ? ')
}
else {
g.writeln(') {')
}
2020-03-16 03:56:38 +01:00
}
// g.writeln('/* M sum_type=$node.is_sum_type is_expr=$node.is_expr exp_type=${g.typ(node.expected_type)}*/')
if node.is_sum_type && branch.exprs.len > 0 && !node.is_expr {
2020-03-16 03:56:38 +01:00
// The first node in expr is an ast.Type
// Use it to generate `it` variable.
first_expr := branch.exprs[0]
match first_expr {
2020-03-16 03:56:38 +01:00
ast.Type {
it_type := g.typ(it.typ)
// g.writeln('$it_type* it = ($it_type*)${tmp}.obj; // ST it')
g.write('\t$it_type* it = ($it_type*)')
g.expr(node.cond)
g.writeln('.obj; // ST it')
2020-03-16 03:56:38 +01:00
}
else {
verror('match sum type')
}
}
}
g.stmts(branch.stmts)
if !g.inside_ternary {
g.writeln('}')
}
2020-03-16 03:56:38 +01:00
}
g.inside_ternary = false
2020-03-16 03:56:38 +01:00
}
2020-03-13 05:57:51 +01:00
fn (g mut Gen) ident(node ast.Ident) {
if node.name == 'lld' {
2020-03-20 20:46:42 +01:00
return
}
if node.name.starts_with('C.') {
g.write(node.name[2..].replace('.', '__'))
return
2020-03-13 05:57:51 +01:00
}
if node.kind == .constant && !node.name.starts_with('g_') {
// TODO globals hack
g.write('_const_')
}
name := c_name(node.name)
// TODO `is`
match node.info {
ast.IdentVar {
// x ?int
// `x = 10` => `x.data = 10` (g.right_is_opt == false)
// `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true)
// `println(x)` => `println(*(int*)x.data)`
2020-03-31 10:11:47 +02:00
if it.is_optional && !(g.is_assign_lhs && g.right_is_opt) {
g.write('/*opt*/')
styp := g.typ(it.typ)[7..] // Option_int => int TODO perf?
g.write('(*($styp*)${name}.data)')
return
2020-03-13 05:57:51 +01:00
}
}
else {}
2020-03-13 05:57:51 +01:00
}
g.write(name)
2020-03-13 05:57:51 +01:00
}
2020-03-17 16:40:41 +01:00
fn (g mut Gen) if_expr(node ast.IfExpr) {
2020-03-21 11:17:17 +01:00
// println('if_expr pos=$node.pos.line_nr')
2020-03-18 16:47:37 +01:00
// g.writeln('/* if is_expr=$node.is_expr */')
2020-03-17 16:40:41 +01:00
// If expression? Assign the value to a temp var.
// Previously ?: was used, but it's too unreliable.
type_sym := g.table.get_type_symbol(node.typ)
mut tmp := ''
if type_sym.kind != .void {
tmp = g.new_tmp_var()
// g.writeln('$ti.name $tmp;')
}
// one line ?:
// TODO clean this up once `is` is supported
// TODO: make sure only one stmt in each branch
if node.is_expr && node.branches.len >= 2 && node.has_else && type_sym.kind != .void {
g.inside_ternary = true
g.write('(')
for i, branch in node.branches {
if i > 0 {
2020-03-17 16:40:41 +01:00
g.write(' : ')
}
if i < node.branches.len - 1 || !node.has_else {
g.expr(branch.cond)
g.write(' ? ')
}
g.stmts(branch.stmts)
}
g.write(')')
g.inside_ternary = false
2020-03-17 16:40:41 +01:00
}
else {
guard_ok := g.new_tmp_var()
mut is_guard := false
for i, branch in node.branches {
if i == 0 {
match branch.cond {
ast.IfGuardExpr {
is_guard = true
g.writeln('bool $guard_ok;')
g.write('{ /* if guard */ ${g.typ(it.expr_type)} $it.var_name = ')
g.expr(it.expr)
g.writeln(';')
g.writeln('if (($guard_ok = ${it.var_name}.ok)) {')
}
else {
g.write('if (')
g.expr(branch.cond)
g.writeln(') {')
}
}
2020-03-17 16:40:41 +01:00
}
else if i < node.branches.len - 1 || !node.has_else {
g.write('} else if (')
g.expr(branch.cond)
g.writeln(') {')
}
else if i == node.branches.len - 1 && node.has_else {
if is_guard {
g.writeln('} if (!$guard_ok) { /* else */')
}
else {
g.writeln('} else {')
}
2020-03-17 16:40:41 +01:00
}
// Assign ret value
// if i == node.stmts.len - 1 && type_sym.kind != .void {}
2020-03-17 16:40:41 +01:00
// g.writeln('$tmp =')
g.stmts(branch.stmts)
2020-03-17 16:40:41 +01:00
}
if is_guard {
g.write('}')
}
g.writeln('}')
}
}
2020-02-02 14:31:54 +01:00
fn (g mut Gen) index_expr(node ast.IndexExpr) {
// TODO else doesn't work with sum types
mut is_range := false
match node.index {
ast.RangeExpr {
2020-03-24 12:55:41 +01:00
sym := g.table.get_type_symbol(node.container_type)
is_range = true
if sym.kind == .string {
g.write('string_substr(')
2020-03-31 16:47:55 +02:00
g.expr(node.left)
2020-03-24 12:55:41 +01:00
}
else if sym.kind == .array {
g.write('array_slice(')
2020-03-31 16:47:55 +02:00
g.expr(node.left)
}
else if sym.kind == .array_fixed {
// Convert a fixed array to V array when doing `fixed_arr[start..end]`
g.write('array_slice(new_array_from_c_array(sizeof(')
g.expr(node.left)
g.write('), sizeof(')
g.expr(node.left)
g.write('), sizeof(')
g.expr(node.left)
g.write('[0]), ')
g.expr(node.left)
g.write(')')
}
else {
g.expr(node.left)
2020-03-07 05:19:15 +01:00
}
2020-02-02 14:31:54 +01:00
g.write(', ')
2020-03-06 22:24:39 +01:00
if it.has_low {
g.expr(it.low)
}
else {
g.write('0')
}
2020-02-02 14:31:54 +01:00
g.write(', ')
2020-03-06 22:24:39 +01:00
if it.has_high {
g.expr(it.high)
}
else {
g.expr(node.left)
g.write('.len')
}
2020-02-02 14:31:54 +01:00
g.write(')')
2020-03-06 22:24:39 +01:00
return
2020-02-02 14:31:54 +01:00
}
else {}
}
2020-03-24 12:55:41 +01:00
if !is_range {
sym := g.table.get_type_symbol(node.container_type)
2020-03-14 13:42:27 +01:00
if table.type_is_variadic(node.container_type) {
g.expr(node.left)
g.write('.args')
g.write('[')
g.expr(node.index)
g.write(']')
}
else if sym.kind == .array {
2020-03-11 21:11:27 +01:00
info := sym.info as table.Array
elem_type_str := g.typ(info.elem_type)
2020-03-20 17:03:41 +01:00
// `vals[i].field = x` is an exception and requires `array_get`:
// `(*(Val*)array_get(vals, i)).field = x ;`
mut is_selector := false
match node.left {
ast.SelectorExpr {
is_selector = true
}
else {}
}
2020-03-31 10:11:47 +02:00
if g.is_assign_lhs && !is_selector {
2020-03-07 16:39:15 +01:00
g.is_array_set = true
2020-03-26 22:28:24 +01:00
g.write('array_set(&')
2020-03-07 16:39:15 +01:00
g.expr(node.left)
g.write(', ')
g.expr(node.index)
2020-03-11 21:11:27 +01:00
g.write(', &($elem_type_str[]) { ')
// `x[0] *= y`
if g.assign_op in [.mult_assign] {
g.write('*($elem_type_str*)array_get(')
g.expr(node.left)
g.write(', ')
g.expr(node.index)
g.write(') ')
op := match g.assign_op {
.mult_assign{
'*'
}
else {
''}
}
g.write(op)
}
2020-03-07 16:39:15 +01:00
}
else {
2020-03-11 21:11:27 +01:00
g.write('(*($elem_type_str*)array_get(')
2020-03-07 16:39:15 +01:00
g.expr(node.left)
g.write(', ')
g.expr(node.index)
2020-03-11 03:52:01 +01:00
g.write('))')
2020-03-07 16:39:15 +01:00
}
}
2020-03-19 10:07:31 +01:00
else if sym.kind == .map {
info := sym.info as table.Map
elem_type_str := g.typ(info.value_type)
2020-03-31 10:11:47 +02:00
if g.is_assign_lhs {
2020-03-19 10:07:31 +01:00
g.is_array_set = true
g.write('map_set(&')
g.expr(node.left)
g.write(', ')
g.expr(node.index)
g.write(', &($elem_type_str[]) { ')
}
else {
/*
2020-03-19 10:07:31 +01:00
g.write('(*($elem_type_str*)map_get2(')
g.expr(node.left)
g.write(', ')
g.expr(node.index)
g.write('))')
*/
zero := g.type_default(info.value_type)
g.write('(*($elem_type_str*)map_get3(')
g.expr(node.left)
g.write(', ')
g.expr(node.index)
g.write(', &($elem_type_str[]){ $zero }))')
2020-03-19 10:07:31 +01:00
}
}
2020-03-18 16:47:37 +01:00
else if sym.kind == .string && !table.type_is_ptr(node.container_type) {
2020-03-06 22:24:39 +01:00
g.write('string_at(')
2020-03-06 20:22:58 +01:00
g.expr(node.left)
g.write(', ')
g.expr(node.index)
g.write(')')
}
2020-03-07 00:19:27 +01:00
else {
g.expr(node.left)
g.write('[')
g.expr(node.index)
g.write(']')
}
2020-02-02 14:31:54 +01:00
}
}
fn (g mut Gen) return_statement(node ast.Return) {
2020-03-19 12:27:47 +01:00
g.write('return')
if g.fn_decl.name == 'main' {
2020-03-19 12:27:47 +01:00
g.writeln(' 0;')
return
}
2020-03-19 12:13:17 +01:00
fn_return_is_optional := table.type_is_optional(g.fn_decl.return_type)
2020-03-13 05:57:51 +01:00
// multiple returns
if node.exprs.len > 1 {
2020-03-19 12:27:47 +01:00
g.write(' ')
2020-03-16 07:42:45 +01:00
typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
mr_info := typ_sym.info as table.MultiReturn
2020-03-19 12:13:17 +01:00
mut styp := g.typ(g.fn_decl.return_type)
if fn_return_is_optional {
styp = styp[7..] // remove 'Option_'
g.write('opt_ok(& ($styp []) { ')
}
g.write('($styp){')
for i, expr in node.exprs {
2020-03-13 05:57:51 +01:00
g.write('.arg$i=')
g.expr(expr)
if i < node.exprs.len - 1 {
2020-03-13 05:57:51 +01:00
g.write(',')
}
}
g.write('}')
2020-03-19 12:13:17 +01:00
if fn_return_is_optional {
2020-03-31 06:34:59 +02:00
g.write(' }, sizeof($styp))')
2020-03-19 12:13:17 +01:00
}
2020-03-13 05:57:51 +01:00
}
// normal return
else if node.exprs.len == 1 {
2020-03-19 12:27:47 +01:00
g.write(' ')
2020-03-13 05:57:51 +01:00
// `return opt_ok(expr)` for functions that expect an optional
if fn_return_is_optional && !table.type_is_optional(node.types[0]) {
2020-03-13 05:57:51 +01:00
mut is_none := false
mut is_error := false
expr0 := node.exprs[0]
2020-03-18 12:23:32 +01:00
match expr0 {
2020-03-13 05:57:51 +01:00
ast.None {
is_none = true
}
ast.CallExpr {
// TODO: why?
if !it.is_method {
is_error = true // TODO check name 'error'
}
2020-03-13 05:57:51 +01:00
}
else {}
}
if !is_none && !is_error {
styp := g.typ(g.fn_decl.return_type)[7..] // remove 'Option_'
2020-03-13 12:22:36 +01:00
g.write('opt_ok(& ($styp []) { ')
g.expr(node.exprs[0])
2020-03-13 12:22:36 +01:00
g.writeln(' }, sizeof($styp));')
2020-03-13 05:57:51 +01:00
return
}
// g.write('/*OPTIONAL*/')
}
if !table.type_is_ptr(g.fn_decl.return_type) && table.type_is_ptr(node.types[0]) {
// Automatic Dereference
g.write('*')
}
g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
2020-03-13 05:57:51 +01:00
}
g.writeln(';')
}
2020-03-06 14:03:35 +01:00
fn (g mut Gen) const_decl(node ast.ConstDecl) {
for i, field in node.fields {
name := c_name(field.name)
2020-03-06 14:03:35 +01:00
expr := node.exprs[i]
2020-03-21 09:29:16 +01:00
// TODO hack. Cut the generated value and paste it into definitions.
pos := g.out.len
g.expr(expr)
val := g.out.after(pos)
g.out.go_back(val.len)
2020-03-06 14:03:35 +01:00
match expr {
ast.CharLiteral, ast.IntegerLiteral {
2020-03-21 09:29:16 +01:00
// Simple expressions should use a #define
// so that we don't pollute the binary with unnecessary global vars
// Do not do this when building a module, otherwise the consts
// will not be accessible.
g.definitions.write('#define _const_$name ')
g.definitions.writeln(val)
2020-03-06 14:03:35 +01:00
}
else {
// Initialize more complex consts in `void _vinit(){}`
2020-03-21 09:29:16 +01:00
// (C doesn't allow init expressions that can't be resolved at compile time).
2020-03-06 22:36:51 +01:00
styp := g.typ(field.typ)
g.definitions.writeln('$styp _const_$name; // inited later') // = ')
g.inits.write('_const_$name = ')
2020-03-21 09:29:16 +01:00
g.inits.write(val)
g.inits.writeln(';')
2020-03-06 14:03:35 +01:00
}
}
}
}
fn (g mut Gen) struct_init(it ast.StructInit) {
mut info := table.Struct{}
mut is_struct := false
sym := g.table.get_type_symbol(it.typ)
if sym.kind == .struct_ {
is_struct = true
info = sym.info as table.Struct
}
// info := g.table.get_type_symbol(it.typ).info as table.Struct
2020-03-23 09:17:32 +01:00
// println(info.fields.len)
styp := g.typ(it.typ)
if g.is_amp {
g.out.go_back(1) // delete the & already generated in `prefix_expr()
g.write('($styp*)memdup(&($styp){')
}
else {
g.writeln('($styp){')
}
mut fields := []string
mut inited_fields := []string
if it.fields.len == 0 && it.exprs.len > 0 {
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
for f in info.fields {
fields << f.name
}
}
else {
fields = it.fields
}
// / User set fields
for i, field in fields {
field_name := c_name(field)
inited_fields << field
g.write('\t.$field_name = ')
g.expr_with_cast(it.exprs[i], it.expr_types[i], it.expected_types[i])
2020-03-23 11:57:54 +01:00
g.writeln(',')
}
// The rest of the fields are zeroed.
if is_struct {
for field in info.fields {
if field.name in inited_fields {
continue
}
field_name := c_name(field.name)
zero := g.type_default(field.typ)
g.writeln('\t.$field_name = $zero,') // zer0')
}
}
2020-03-23 11:57:54 +01:00
if it.fields.len == 0 && info.fields.len == 0 {
g.write('0')
}
g.write('}')
if g.is_amp {
g.write(', sizeof($styp))')
}
}
2020-03-19 07:59:01 +01:00
// { user | name: 'new name' }
2020-03-19 08:14:09 +01:00
fn (g mut Gen) assoc(node ast.Assoc) {
2020-03-19 08:49:47 +01:00
g.writeln('// assoc')
2020-03-19 08:14:09 +01:00
if node.typ == 0 {
return
}
styp := g.typ(node.typ)
g.writeln('($styp){')
for i, field in node.fields {
field_name := c_name(field)
g.write('\t.$field_name = ')
2020-03-19 08:14:09 +01:00
g.expr(node.exprs[i])
g.writeln(', ')
}
// Copy the rest of the fields.
sym := g.table.get_type_symbol(node.typ)
info := sym.info as table.Struct
for field in info.fields {
if field.name in node.fields {
continue
}
field_name := c_name(field.name)
g.writeln('\t.$field_name = ${node.var_name}.$field_name,')
2020-03-19 08:14:09 +01:00
}
g.write('}')
if g.is_amp {
g.write(', sizeof($styp))')
}
}
2020-03-19 07:59:01 +01:00
fn (g mut Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
is_variadic := expected_types.len > 0 && table.type_is_variadic(expected_types[expected_types.len - 1])
mut arg_no := 0
for arg in args {
if is_variadic && arg_no == expected_types.len - 1 {
2020-03-14 13:15:07 +01:00
break
}
2020-03-24 12:55:41 +01:00
// some c fn definitions dont have args (cfns.v) or are not updated in checker
2020-03-26 11:11:56 +01:00
// when these are fixed we wont need this check
if arg_no < expected_types.len {
g.ref_or_deref_arg(arg, expected_types[arg_no])
}
else {
g.expr(arg.expr)
2020-03-10 23:21:26 +01:00
}
if arg_no < args.len - 1 || is_variadic {
g.write(', ')
}
arg_no++
}
if is_variadic {
varg_type := expected_types[expected_types.len - 1]
struct_name := 'varg_' + g.typ(varg_type).replace('*', '_ptr')
len := args.len - arg_no
varg_type_str := int(varg_type).str()
if len > g.variadic_args[varg_type_str] {
g.variadic_args[varg_type_str] = len
}
g.write('($struct_name){.len=$len,.args={')
for j in arg_no .. args.len {
g.ref_or_deref_arg(args[j], varg_type)
if j < args.len - 1 {
g.write(', ')
}
}
g.write('}}')
}
}
2020-03-14 13:15:07 +01:00
[inline]
fn (g mut Gen) ref_or_deref_arg(arg ast.CallArg, expected_type table.Type) {
arg_is_ptr := table.type_is_ptr(expected_type) || table.type_idx(expected_type) in table.pointer_type_idxs
2020-03-23 07:23:10 +01:00
expr_is_ptr := table.type_is_ptr(arg.typ) || table.type_idx(arg.typ) in table.pointer_type_idxs
2020-03-14 13:15:07 +01:00
if arg.is_mut && !arg_is_ptr {
g.write('&/*mut*/')
}
2020-03-23 07:23:10 +01:00
else if arg_is_ptr && !expr_is_ptr {
2020-03-25 17:04:13 +01:00
if arg.is_mut {
2020-03-26 11:11:56 +01:00
sym := g.table.get_type_symbol(expected_type)
2020-03-25 17:04:13 +01:00
if sym.kind == .array {
// Special case for mutable arrays. We can't `&` function
// results, have to use `(array[]){ expr }[0]` hack.
g.write('&/*111*/(array[]){')
g.expr(arg.expr)
2020-03-25 17:04:13 +01:00
g.write('}[0]')
return
}
}
g.write('&/*qq*/')
2020-03-14 13:15:07 +01:00
}
else if !arg_is_ptr && expr_is_ptr {
// Dereference a pointer if a value is required
g.write('*/*d*/')
}
g.expr_with_cast(arg.expr, arg.typ, expected_type)
2020-03-14 13:15:07 +01:00
}
fn verror(s string) {
println('cgen error: $s')
exit(1)
2019-12-24 18:54:43 +01:00
}
2020-03-10 23:21:26 +01:00
2020-03-21 09:29:16 +01:00
fn (g mut Gen) write_init_function() {
g.writeln('void _vinit() {')
2020-03-21 10:22:16 +01:00
g.writeln(g.inits.str())
2020-03-21 09:29:16 +01:00
g.writeln('}')
2020-03-21 19:52:19 +01:00
if g.autofree {
g.writeln('void _vcleanup() {')
g.writeln('puts("cleaning up...");')
if g.is_importing_os() {
g.writeln('free(_const_os__args.data);')
g.writeln('string_free(_const_os__wd_at_startup);')
}
g.writeln('free(_const_strconv__ftoa__powers_of_10.data);')
2020-03-21 19:52:19 +01:00
g.writeln('}')
}
2020-03-21 09:29:16 +01:00
}
2020-03-21 07:10:53 +01:00
fn (g mut Gen) write_str_definitions() {
// _STR function can't be defined in vlib
g.writeln('
string _STR(const char *fmt, ...) {
va_list argptr;
va_start(argptr, fmt);
size_t len = vsnprintf(0, 0, fmt, argptr) + 1;
va_end(argptr);
byte* buf = malloc(len);
va_start(argptr, fmt);
vsprintf((char *)buf, fmt, argptr);
va_end(argptr);
#ifdef DEBUG_ALLOC
puts("_STR:");
puts(buf);
#endif
return tos2(buf);
}
string _STR_TMP(const char *fmt, ...) {
va_list argptr;
va_start(argptr, fmt);
//size_t len = vsnprintf(0, 0, fmt, argptr) + 1;
va_end(argptr);
va_start(argptr, fmt);
vsprintf((char *)g_str_buf, fmt, argptr);
va_end(argptr);
#ifdef DEBUG_ALLOC
//puts("_STR_TMP:");
//puts(g_str_buf);
#endif
return tos2(g_str_buf);
2020-03-21 11:47:23 +01:00
} // endof _STR_TMP
2020-03-21 07:10:53 +01:00
')
}
2020-03-10 23:21:26 +01:00
const (
builtins = ['string', 'array', 'KeyValue', 'DenseArray', 'map', 'Option']
2020-03-10 23:21:26 +01:00
)
fn (g mut Gen) write_builtin_types() {
2020-03-05 23:27:21 +01:00
mut builtin_types := []table.TypeSymbol // builtin types
// builtin types need to be on top
// everything except builtin will get sorted
2020-03-06 16:57:27 +01:00
for builtin_name in builtins {
builtin_types << g.table.types[g.table.type_idxs[builtin_name]]
}
2020-03-10 23:21:26 +01:00
g.write_types(builtin_types)
}
// C struct definitions, ordered
// Sort the types, make sure types that are referenced by other types
// are added before them.
fn (g mut Gen) write_sorted_types() {
mut types := []table.TypeSymbol // structs that need to be sorted
2020-03-06 12:01:56 +01:00
for typ in g.table.types {
2020-03-06 16:57:27 +01:00
if !(typ.name in builtins) {
types << typ
2020-03-05 23:27:21 +01:00
}
}
// sort structs
types_sorted := g.sort_structs(types)
// Generate C code
g.definitions.writeln('// builtin types:')
2020-03-06 20:22:58 +01:00
g.definitions.writeln('//------------------ #endbuiltin')
2020-03-05 23:27:21 +01:00
g.write_types(types_sorted)
}
fn (g mut Gen) write_types(types []table.TypeSymbol) {
for typ in types {
if typ.name.starts_with('C.') {
continue
}
2020-03-05 23:27:21 +01:00
// sym := g.table.get_type_symbol(typ)
name := typ.name.replace('.', '__')
2020-03-05 23:27:21 +01:00
match typ.info {
table.Struct {
info := typ.info as table.Struct
// g.definitions.writeln('typedef struct {')
g.definitions.writeln('struct $name {')
for field in info.fields {
type_name := g.typ(field.typ)
field_name := c_name(field.name)
g.definitions.writeln('\t$type_name $field_name;')
2020-03-05 23:27:21 +01:00
}
// g.definitions.writeln('} $name;\n')
//
g.definitions.writeln('};\n')
}
2020-03-18 19:56:59 +01:00
// table.Alias, table.SumType { TODO
2020-03-22 14:54:31 +01:00
table.Alias {}
table.Enum {
g.definitions.writeln('typedef enum {')
2020-03-16 03:19:26 +01:00
for j, val in it.vals {
g.definitions.writeln('\t${name}_$val, // $j')
}
g.definitions.writeln('} $name;\n')
}
table.SumType {
g.definitions.writeln('// Sum type')
g.definitions.writeln('
typedef struct {
void* obj;
int typ;
} $name;')
}
else {}
2020-03-05 23:27:21 +01:00
}
}
}
// sort structs by dependant fields
fn (g &Gen) sort_structs(types []table.TypeSymbol) []table.TypeSymbol {
mut dep_graph := depgraph.new_dep_graph()
// types name list
mut type_names := []string
for typ in types {
type_names << typ.name
}
// loop over types
for t in types {
2020-03-06 12:01:56 +01:00
// create list of deps
mut field_deps := []string
2020-03-05 23:27:21 +01:00
match t.info {
table.Struct {
info := t.info as table.Struct
for field in info.fields {
// Need to handle fixed size arrays as well (`[10]Point`)
// ft := if field.typ.starts_with('[') { field.typ.all_after(']') } else { field.typ }
2020-03-06 12:01:56 +01:00
dep := g.table.get_type_symbol(field.typ).name
2020-03-05 23:27:21 +01:00
// skip if not in types list or already in deps
2020-03-06 20:22:58 +01:00
if !(dep in type_names) || dep in field_deps || table.type_is_ptr(field.typ) {
2020-03-06 12:01:56 +01:00
continue
}
field_deps << dep
2020-03-05 23:27:21 +01:00
}
}
else {}
2020-03-06 14:03:35 +01:00
}
2020-03-06 12:01:56 +01:00
// add type and dependant types to graph
dep_graph.add(t.name, field_deps)
2020-03-05 23:27:21 +01:00
}
// sort graph
dep_graph_sorted := dep_graph.resolve()
if !dep_graph_sorted.acyclic {
verror('cgen.sort_structs(): the following structs form a dependency cycle:\n' + dep_graph_sorted.display_cycles() + '\nyou can solve this by making one or both of the dependant struct fields references, eg: field &MyStruct' + '\nif you feel this is an error, please create a new issue here: https://github.com/vlang/v/issues and tag @joe-conigliaro')
}
// sort types
mut types_sorted := []table.TypeSymbol
for node in dep_graph_sorted.nodes {
2020-03-06 16:57:27 +01:00
types_sorted << g.table.types[g.table.type_idxs[node.name]]
2020-03-05 23:27:21 +01:00
}
return types_sorted
}
2020-03-21 07:01:06 +01:00
fn (g mut Gen) string_inter_literal(node ast.StringInterLiteral) {
g.write('_STR("')
// Build the string with %
for i, val in node.vals {
2020-03-21 07:10:53 +01:00
escaped_val := val.replace_each(['"', '\\"',
'\r\n', '\\n',
'\n', '\\n'])
g.write(escaped_val)
2020-03-21 07:01:06 +01:00
if i >= node.exprs.len {
continue
}
// TODO: fix match, sum type false positive
// match node.expr_types[i] {
2020-03-21 19:52:19 +01:00
// table.string_type {
// g.write('%.*s')
// }
// table.int_type {
// g.write('%d')
// }
// else {}
// }
sfmt := node.expr_fmts[i]
if sfmt.len > 0 {
fspec := sfmt[sfmt.len - 1]
if fspec == `s` && node.expr_types[i] != table.string_type {
verror('only V strings can be formatted with a ${sfmt} format')
}
g.write('%' + sfmt[1..])
}
else if node.expr_types[i] == table.string_type {
g.write('%.*s')
}
else {
g.write('%d')
}
2020-03-21 07:01:06 +01:00
}
g.write('", ')
// Build args
for i, expr in node.exprs {
sfmt := node.expr_fmts[i]
if sfmt.len > 0 {
fspec := sfmt[sfmt.len - 1]
if fspec == `s` && node.expr_types[i] == table.string_type {
g.expr(expr)
g.write('.str')
}
else {
g.expr(expr)
}
}
else if node.expr_types[i] == table.string_type {
2020-03-21 07:01:06 +01:00
// `name.str, name.len,`
2020-03-21 07:04:53 +01:00
g.expr(expr)
2020-03-21 07:01:06 +01:00
g.write('.len, ')
2020-03-21 07:04:53 +01:00
g.expr(expr)
2020-03-21 07:01:06 +01:00
g.write('.str')
}
else {
2020-03-21 07:04:53 +01:00
g.expr(expr)
2020-03-21 07:01:06 +01:00
}
if i < node.exprs.len - 1 {
g.write(', ')
}
}
g.write(')')
}
// `nums.filter(it % 2 == 0)`
fn (g mut Gen) gen_filter(node ast.CallExpr) {
2020-03-19 15:18:29 +01:00
tmp := g.new_tmp_var()
buf := g.out.buf[g.stmt_start_pos..]
s := string(buf.clone()) // the already generated part of current statement
g.out.go_back(s.len)
// println('filter s="$s"')
sym := g.table.get_type_symbol(node.return_type)
if sym.kind != .array {
verror('filter() requires an array')
}
info := sym.info as table.Array
styp := g.typ(node.return_type)
elem_type_str := g.typ(info.elem_type)
g.write('\nint ${tmp}_len = ')
g.expr(node.left)
2020-03-19 15:18:29 +01:00
g.writeln('.len;')
g.writeln('$styp $tmp = new_array(0, ${tmp}_len, sizeof($elem_type_str));')
g.writeln('for (int i = 0; i < ${tmp}_len; i++) {')
g.write(' $elem_type_str it = (($elem_type_str*) ')
g.expr(node.left)
2020-03-19 15:18:29 +01:00
g.writeln('.data)[i];')
g.write('if (')
g.expr(node.args[0].expr) // the first arg is the filter condition
g.writeln(') array_push(&$tmp, &it); \n }')
g.write(s)
g.write(' ')
g.write(tmp)
}
fn (g mut Gen) insert_before(s string) {
cur_line := g.out.after(g.stmt_start_pos)
g.out.go_back(cur_line.len)
g.writeln(s)
g.write(cur_line)
}
fn (g mut Gen) call_expr(node ast.CallExpr) {
2020-03-31 10:11:47 +02:00
gen_or := !g.is_assign_rhs && node.or_block.stmts.len > 0
2020-03-31 06:34:59 +02:00
tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
if gen_or {
styp := g.typ(node.return_type)
g.write('$styp $tmp_opt = ')
}
if node.is_method {
g.method_call(node)
}
else {
g.fn_call(node)
}
if gen_or {
g.or_block(tmp_opt, node.or_block.stmts, node.return_type)
}
}
fn (g mut Gen) method_call(node ast.CallExpr) {
// TODO: there are still due to unchecked exprs (opt/some fn arg)
if node.left_type == 0 {
verror('method receiver type is 0, this means there are some uchecked exprs')
}
typ_sym := g.table.get_type_symbol(node.receiver_type)
// rec_sym := g.table.get_type_symbol(node.receiver_type)
mut receiver_name := typ_sym.name
if typ_sym.kind == .array && node.name == 'filter' {
g.gen_filter(node)
return
}
if typ_sym.kind == .array && node.name in
// TODO performance, detect `array` method differently
['repeat', 'sort_with_compare', 'free', 'push_many', 'trim',
//
'first', 'last', 'clone', 'reverse', 'slice'] {
// && rec_sym.name == 'array' {
// && rec_sym.name == 'array' && receiver_name.starts_with('array') {
// `array_byte_clone` => `array_clone`
receiver_name = 'array'
if node.name in ['last', 'first'] {
return_type_str := g.typ(node.return_type)
g.write('*($return_type_str*)')
}
}
name := '${receiver_name}_$node.name'.replace('.', '__')
// if node.receiver_type != 0 {
// g.write('/*${g.typ(node.receiver_type)}*/')
// g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/')
// }
g.write('${name}(')
if table.type_is_ptr(node.receiver_type) && !table.type_is_ptr(node.left_type) {
// The receiver is a reference, but the caller provided a value
// Add `&` automatically.
// TODO same logic in call_args()
g.write('&')
}
else if !table.type_is_ptr(node.receiver_type) && table.type_is_ptr(node.left_type) {
g.write('/*rec*/*')
}
g.expr(node.left)
is_variadic := node.exp_arg_types.len > 0 && table.type_is_variadic(node.exp_arg_types[node.exp_arg_types.len - 1])
if node.args.len > 0 || is_variadic {
g.write(', ')
}
// /////////
/*
if name.contains('subkeys') {
println('call_args $name $node.arg_types.len')
for t in node.arg_types {
sym := g.table.get_type_symbol(t)
print('$sym.name ')
}
println('')
}
*/
// ///////
g.call_args(node.args, node.exp_arg_types)
g.write(')')
// if node.or_block.stmts.len > 0 {
// g.or_block(node.or_block.stmts, node.return_type)
// }
}
fn (g mut Gen) fn_call(node ast.CallExpr) {
mut name := node.name
is_print := name == 'println'
if node.is_c {
// Skip "C."
g.is_c_call = true
name = name[2..].replace('.', '__')
}
else {
name = c_name(name)
}
// Generate tmp vars for values that have to be freed.
/*
mut tmps := []string
for arg in node.args {
if arg.typ == table.string_type_idx || is_print {
tmp := g.new_tmp_var()
tmps << tmp
g.write('string $tmp = ')
g.expr(arg.expr)
g.writeln('; //memory')
}
}
*/
2020-03-21 19:52:19 +01:00
if is_print && node.args[0].typ != table.string_type_idx {
typ := node.args[0].typ
mut styp := g.typ(typ)
sym := g.table.get_type_symbol(typ)
if !sym.has_method('str') && !(int(typ) in g.str_types) {
// Generate an automatic str() method if this type doesn't have it already
if table.type_is_ptr(typ) {
styp = styp.replace('*', '')
}
g.str_types << typ
g.definitions.writeln('string ${styp}_str($styp* x) { return tos3("TODO_str"); }')
}
if g.autofree && !table.type_is_optional(typ) {
tmp := g.new_tmp_var()
// tmps << tmp
g.write('string $tmp = ${styp}_str(')
g.expr(node.args[0].expr)
g.writeln('); println($tmp); string_free($tmp); //MEM2 $styp')
2020-03-21 19:52:19 +01:00
}
else {
// `println(int_str(10))`
// sym := g.table.get_type_symbol(node.args[0].typ)
g.write('println(${styp}_str(')
g.expr(node.args[0].expr)
g.write('))')
2020-03-21 19:52:19 +01:00
}
}
else {
g.write('${name}(')
g.call_args(node.args, node.exp_arg_types)
g.write(')')
2020-03-31 06:34:59 +02:00
}
// if node.or_block.stmts.len > 0 {
// g.or_block(node.or_block.stmts, node.return_type)
// }
g.is_c_call = false
2020-03-21 19:52:19 +01:00
}
2020-03-31 06:34:59 +02:00
// If user is accessing the return value eg. in assigment, pass the variable name.
// If the user is not using the optional return value. We need to pass a temp var
// to access its fields (`.ok`, `.error` etc)
// `os.cp(...)` => `Option bool tmp = os__cp(...); if (!tmp.ok) { ... }`
fn (g mut Gen) or_block(var_name string, stmts []ast.Stmt, return_type table.Type) {
2020-03-26 14:42:14 +01:00
g.writeln(';') // or')
g.writeln('if (!${var_name}.ok) {')
g.writeln('string err = ${var_name}.v_error;')
g.writeln('int errcode = ${var_name}.ecode;')
g.stmts(stmts)
2020-03-31 06:34:59 +02:00
g.write('}')
2020-03-26 14:42:14 +01:00
}
2020-03-19 16:12:46 +01:00
// `a in [1,2,3]` => `a == 1 || a == 2 || a == 3`
fn (g mut Gen) in_optimization(left ast.Expr, right ast.ArrayInit) {
is_str := right.elem_type == table.string_type
for i, array_expr in right.exprs {
if is_str {
g.write('string_eq(')
}
g.expr(left)
if is_str {
g.write(', ')
}
else {
g.write(' == ')
}
g.expr(array_expr)
if is_str {
g.write(')')
}
if i < right.exprs.len - 1 {
g.write(' || ')
}
}
}
fn op_to_fn_name(name string) string {
return match name {
'+'{
'_op_plus'
}
'-'{
'_op_minus'
}
'*'{
'_op_mul'
}
'/'{
'_op_div'
}
'%'{
'_op_mod'
}
else {
'bad op $name'}
}
}
2020-03-22 13:55:39 +01:00
fn comp_if_to_ifdef(name string) string {
match name {
'windows' {
return '_WIN32'
}
'mac' {
return '__APPLE__'
}
'macos' {
return '__APPLE__'
}
'linux' {
return '__linux__'
}
'freebsd' {
return '__FreeBSD__'
}
'openbsd' {
return '__OpenBSD__'
}
'netbsd' {
return '__NetBSD__'
}
'dragonfly' {
return '__DragonFly__'
}
'msvc' {
return '_MSC_VER'
}
'android' {
return '__ANDROID__'
}
'js' {
return '_VJS'
}
'solaris' {
return '__sun'
}
'haiku' {
return '__haiku__'
}
'tinyc' {
return 'tinyc'
}
'debug' {
return '_VDEBUG'
}
'linux_or_macos' {
return ''
}
2020-03-22 14:54:31 +01:00
'mingw' {
return '__MINGW32__'
}
'glibc' {
return '__GLIBC__'
}
2020-03-24 14:46:00 +01:00
'prealloc' {
return 'VPREALLOC'
}
2020-03-22 13:55:39 +01:00
'no_bounds_checking' {
return 'NO_BOUNDS_CHECK'
}
else {
verror('bad os ifdef name "$name"')
}
}
// verror('bad os ifdef name "$name"')
return ''
}
[inline]
fn c_name(name_ string) string {
name := name_.replace('.', '__')
if name in c_reserved {
return 'v_$name'
}
return name
}
fn (g &Gen) type_default(typ table.Type) string {
sym := g.table.get_type_symbol(typ)
if sym.kind == .array {
2020-03-24 03:31:16 +01:00
elem_sym := g.table.get_type_symbol(sym.array_info().elem_type)
elem_type_str := elem_sym.name.replace('.', '__')
return 'new_array(0, 1, sizeof($elem_type_str))'
}
if sym.kind == .map {
value_sym := g.table.get_type_symbol(sym.map_info().value_type)
2020-03-24 03:31:16 +01:00
value_type_str := value_sym.name.replace('.', '__')
return 'new_map(1, sizeof($value_type_str))'
}
// Always set pointers to 0
if table.type_is_ptr(typ) {
return '0'
}
// User struct defined in another module.
// if typ.contains('__') {
if sym.kind == .struct_ {
return '{0}'
}
// if typ.ends_with('Fn') { // TODO
// return '0'
// }
// Default values for other types are not needed because of mandatory initialization
2020-03-23 09:17:32 +01:00
idx := int(typ)
if idx >= 1 && idx <= 17 {
return '0'
}
/*
match idx {
table.bool_type_idx {
return '0'
}
2020-03-23 09:17:32 +01:00
else {}
}
*/
match sym.name {
'string' {
return 'tos3("")'
}
'rune' {
return '0'
}
else {}
}
return '{0}'
// TODO this results in
// error: expected a field designator, such as '.field = 4'
// - Empty ee= (Empty) { . = {0} } ;
/*
return match typ {
'bool'{ '0'}
'string'{ 'tos3("")'}
'i8'{ '0'}
'i16'{ '0'}
'i64'{ '0'}
'u16'{ '0'}
'u32'{ '0'}
'u64'{ '0'}
'byte'{ '0'}
'int'{ '0'}
'rune'{ '0'}
'f32'{ '0.0'}
'f64'{ '0.0'}
'byteptr'{ '0'}
'voidptr'{ '0'}
else { '{0} '}
}
*/
}
pub fn (g mut Gen) write_tests_main() {
g.definitions.writeln('int g_test_oks = 0;')
g.definitions.writeln('int g_test_fails = 0;')
g.writeln('int main() {')
2020-03-25 00:17:39 +01:00
g.writeln('\t_vinit();')
2020-03-30 17:21:32 +02:00
g.writeln('')
all_tfuncs := g.get_all_test_function_names()
if g.pref.is_stats {
g.writeln('\tBenchedTests bt = start_testing(${all_tfuncs.len}, tos3("$g.pref.path"));')
}
for t in all_tfuncs {
if g.pref.is_stats {
g.writeln('\tBenchedTests_testing_step_start(&bt, tos3("$t"));')
}
g.writeln('\t${t}();')
if g.pref.is_stats {
g.writeln('\tBenchedTests_testing_step_end(&bt);')
}
}
if g.pref.is_stats {
g.writeln('\tBenchedTests_end_testing(&bt);')
}
g.writeln('')
2020-03-31 16:47:55 +02:00
g.writeln('\t_vcleanup();')
2020-03-30 17:21:32 +02:00
g.writeln('\treturn g_test_fails > 0;')
g.writeln('}')
}
fn (g &Gen) get_all_test_function_names() []string {
mut tfuncs := []string
mut tsuite_begin := ''
mut tsuite_end := ''
for _, f in g.table.fns {
if f.name == 'testsuite_begin' {
tsuite_begin = f.name
}
if f.name == 'testsuite_end' {
tsuite_end = f.name
}
if !f.name.starts_with('test_') {
continue
}
tfuncs << f.name
}
2020-03-30 17:21:32 +02:00
mut all_tfuncs := []string
if tsuite_begin.len > 0 {
2020-03-30 17:21:32 +02:00
all_tfuncs << tsuite_begin
}
2020-03-30 17:21:32 +02:00
all_tfuncs << tfuncs
if tsuite_end.len > 0 {
2020-03-30 17:21:32 +02:00
all_tfuncs << tsuite_end
}
2020-03-30 17:21:32 +02:00
return all_tfuncs
}
fn (g &Gen) is_importing_os() bool {
return 'os' in g.table.imports
}
2020-03-27 14:44:30 +01:00
fn (g mut Gen) comp_if(it ast.CompIf) {
ifdef := comp_if_to_ifdef(it.val)
if it.is_not {
g.writeln('\n#ifndef ' + ifdef)
g.writeln('// #if not $it.val')
}
else {
g.writeln('\n#ifdef ' + ifdef)
g.writeln('// #if $it.val')
}
// NOTE: g.defer_ifdef is needed for defers called witin an ifdef
// in v1 this code would be completely excluded
g.defer_ifdef = if it.is_not { '#ifndef ' + ifdef } else { '#ifdef ' + ifdef }
// println('comp if stmts $g.file.path:$it.pos.line_nr')
g.stmts(it.stmts)
g.defer_ifdef = ''
if it.has_else {
g.writeln('#else')
g.defer_ifdef = if it.is_not { '#ifdef ' + ifdef } else { '#ifndef ' + ifdef }
g.stmts(it.else_stmts)
g.defer_ifdef = ''
}
g.writeln('#endif')
}