cgen: lots of fixes

pull/3949/head
Alexander Medvednikov 2020-03-10 23:21:26 +01:00
parent 1143320b8b
commit de55a26cfe
12 changed files with 214 additions and 94 deletions

View File

@ -255,7 +255,7 @@ fn (a array) slice2(start, _end int, end_max bool) array {
}
// array.clone returns an independent copy of a given array
pub fn (a array) clone() array {
pub fn (a &array) clone() array {
mut size := a.cap * a.element_size
if size == 0 {
size++
@ -270,7 +270,7 @@ pub fn (a array) clone() array {
return arr
}
fn (a array) slice_clone(start, _end int) array {
fn (a &array) slice_clone(start, _end int) array {
mut end := _end
$if !no_bounds_checking? {
if start > end {
@ -316,8 +316,6 @@ pub fn (a3 mut array) push_many(val voidptr, size int) {
// handle `arr << arr`
copy := a3.clone()
a3.ensure_cap(a3.len + size)
C.printf("%d", a3.len*2)
println(a3.len*2)
//C.memcpy(a.data, copy.data, copy.element_size * copy.len)
C.memcpy(a3.data + a3.element_size * a3.len, copy.data, a3.element_size * size)
} else {

View File

@ -190,7 +190,7 @@ pub fn (c rune) str() string {
for i in 0..len {
str.str[i] = int(c)>>8 * (3 - i) & 0xff
}
str[len] = `\0`
str.str[len] = `\0`
return str
}

View File

@ -40,6 +40,11 @@ pub fn (b mut Builder) write(s string) {
b.len += s.len
}
pub fn (b mut Builder) go_back(n int) {
b.buf.trim(b.buf.len-n)
//b.len -= n
}
pub fn (b mut Builder) writeln(s string) {
// for c in s {
// b.buf << c

View File

@ -10,15 +10,15 @@ import (
pub type TypeDecl = AliasTypeDecl | SumTypeDecl
pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral |
FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr |
AssignExpr | PrefixExpr | MethodCallExpr | IndexExpr | RangeExpr | MatchExpr |
CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr |
pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral |
FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr |
AssignExpr | PrefixExpr | MethodCallExpr | IndexExpr | RangeExpr | MatchExpr |
CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr |
ConcatExpr | Type | AsCast
pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt |
ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt |
HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt |
pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt |
ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt |
HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt |
LineComment | MultiLineComment | AssertStmt | UnsafeStmt
// pub type Type = StructType | ArrayType
// pub struct StructType {
@ -156,28 +156,31 @@ pub:
pub struct CallExpr {
pub:
// tok token.Token
pos token.Position
pos token.Position
mut:
// func Expr
name string
args []Expr
is_c bool
muts []bool
or_block OrExpr
name string
args []Expr
arg_types []table.Type
is_c bool
muts []bool
or_block OrExpr
typ table.Type
}
pub struct MethodCallExpr {
pub:
// tok token.Token
pos token.Position
expr Expr
name string
args []Expr
muts []bool
or_block OrExpr
pos token.Position
expr Expr // `user` in `user.register()`
name string
args []Expr
muts []bool
or_block OrExpr
mut:
typ table.Type
expr_type table.Type // type of `user`
receiver_type table.Type // User
typ table.Type
}
pub struct Return {

View File

@ -22,6 +22,7 @@ mut:
errors []string
expected_type table.Type
fn_return_type table.Type // current function's return type
// is_amp bool
}
pub fn new_checker(table &table.Table) Checker {
@ -250,7 +251,7 @@ pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type {
// TODO: clean this up, remove dupe code & consider merging method/fn call everywhere
pub fn (c mut Checker) method_call_expr(method_call_expr mut ast.MethodCallExpr) table.Type {
typ := c.expr(method_call_expr.expr)
method_call_expr.typ = typ
method_call_expr.expr_type = typ
typ_sym := c.table.get_type_symbol(typ)
name := method_call_expr.name
if typ_sym.kind == .array && name in ['filter', 'clone'] {
@ -281,6 +282,10 @@ pub fn (c mut Checker) method_call_expr(method_call_expr mut ast.MethodCallExpr)
return typ
}
if method := typ_sym.find_method(name) {
if name == 'clone' {
println('CLONE nr args=$method.args.len')
}
method_call_expr.receiver_type = method.args[0].typ
for i, arg_expr in method_call_expr.args {
c.expected_type = method.args[i].typ
c.expr(arg_expr)
@ -382,7 +387,7 @@ pub fn (c mut Checker) assign_stmt(assign_stmt mut ast.AssignStmt) {
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
for i, _ in assign_stmt.left {
mut ident := assign_stmt.left[i]
val_type := mr_info.types[i]
val_type := mr_info.types[i]
mut var_info := ident.var_info()
var_info.typ = val_type
ident.info = var_info
@ -403,7 +408,7 @@ pub fn (c mut Checker) assign_stmt(assign_stmt mut ast.AssignStmt) {
}
// `a := 1` | `a,b := 1,2`
else {
if assign_stmt.left.len != assign_stmt.right.len {
if assign_stmt.left.len != assign_stmt.right.len {
c.error('wrong number of vars', assign_stmt.pos)
}
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)

View File

@ -19,6 +19,7 @@ mut:
is_c_call bool // e.g. `C.printf("v")`
is_assign_expr bool
is_array_set bool
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
}
pub fn cgen(files []ast.File, table &table.Table) string {
@ -42,6 +43,7 @@ pub fn (g mut Gen) init() {
g.definitions.writeln('#include <inttypes.h>') // int64_t etc
g.definitions.writeln(c_builtin_types)
g.definitions.writeln(c_headers)
g.write_builtin_types()
g.write_array_types()
g.write_sorted_types()
g.write_multi_return_types()
@ -211,7 +213,12 @@ fn (g mut Gen) stmt(node ast.Stmt) {
}
ast.ForStmt {
g.write('while (')
g.expr(it.cond)
if it.is_inf {
g.write('1')
}
else {
g.expr(it.cond)
}
g.writeln(') {')
for stmt in it.stmts {
g.stmt(stmt)
@ -288,20 +295,20 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
else {
panic('expected call')
}
}
mr_typ_sym := g.table.get_type_symbol(return_type)
}
mr_var_name := 'mr_$assign_stmt.pos.pos'
g.write('$mr_typ_sym.name $mr_var_name = ')
mr_typ_str := g.typ(return_type)
g.write('$mr_typ_str $mr_var_name = ')
g.expr(assign_stmt.right[0])
g.writeln(';')
for i, ident in assign_stmt.left {
ident_var_info := ident.var_info()
var_type_sym := g.table.get_type_symbol(ident_var_info.typ)
styp := g.typ(ident_var_info.typ)
if ident.kind == .blank_ident {
g.writeln('{$var_type_sym.name _ = $mr_var_name->arg[$i]};')
g.writeln('{ $styp _ = ${mr_var_name}.arg$i};')
}
else {
g.writeln('$var_type_sym.name $ident.name = $mr_var_name->arg[$i];')
g.writeln('$styp $ident.name = ${mr_var_name}.arg$i;')
}
}
}
@ -310,24 +317,29 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
for i, ident in assign_stmt.left {
val := assign_stmt.right[i]
ident_var_info := ident.var_info()
var_type_sym := g.table.get_type_symbol(ident_var_info.typ)
styp := g.typ(ident_var_info.typ)
if ident.kind == .blank_ident {
is_call := match val {
ast.CallExpr { true }
ast.MethodCallExpr { true }
else { false }
}
ast.CallExpr{
true
}
ast.MethodCallExpr{
true
}
else {
false}
}
if is_call {
g.expr(val)
g.expr(val)
}
else {
g.write('{$var_type_sym.name _ = ')
g.write('{$styp _ = ')
g.expr(val)
g.write('}')
g.write('}')
}
}
else {
g.write('$var_type_sym.name $ident.name = ')
g.write('$styp $ident.name = ')
g.expr(val)
}
g.writeln(';')
@ -359,13 +371,14 @@ fn (g mut Gen) gen_fn_decl(it ast.FnDecl) {
g.definitions.write('$type_name ${name}(')
}
// Receiver is the first argument
/*
if it.is_method {
mut styp := g.typ(it.receiver.typ)
// if table.type_nr_muls(it.receiver.typ) > 0 {
// if it.rec_mut {
// styp += '*'
// }
g.write('$styp $it.receiver.name')
g.write('$styp $it.receiver.name ')
// TODO mut
g.definitions.write('$styp $it.receiver.name')
if it.args.len > 0 {
@ -373,6 +386,7 @@ fn (g mut Gen) gen_fn_decl(it ast.FnDecl) {
g.definitions.write(', ')
}
}
*/
//
no_names := it.args.len > 0 && it.args[0].name == 'arg_1'
for i, arg in it.args {
@ -459,13 +473,24 @@ fn (g mut Gen) expr(node ast.Expr) {
name = name[3..]
}
g.write('${name}(')
g.call_args(it.args)
g.call_args(it.args, it.muts)
g.write(')')
g.is_c_call = false
}
ast.CastExpr {
// g.write('/*cast*/')
if g.is_amp {
// &Foo(0) => ((Foo*)0)
g.out.go_back(1)
}
if it.typ == table.string_type_idx {
g.write('tos(')
// tos(str, len), tos2(str)
if it.has_arg {
g.write('tos(')
}
else {
g.write('tos2(')
}
g.expr(it.expr)
if it.has_arg {
g.write(',')
@ -474,10 +499,16 @@ fn (g mut Gen) expr(node ast.Expr) {
g.write(')')
}
else {
styp := g.table.type_to_str(it.typ)
g.write('($styp)(')
// 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(')(')
g.expr(it.expr)
g.write(')')
g.write('))')
}
}
ast.CharLiteral {
@ -491,7 +522,12 @@ fn (g mut Gen) expr(node ast.Expr) {
}
ast.Ident {
name := it.name.replace('.', '__')
g.write(name)
if name.starts_with('C__') {
g.write(name[3..])
}
else {
g.write(name)
}
}
ast.IfExpr {
// If expression? Assign the value to a temp var.
@ -544,9 +580,11 @@ fn (g mut Gen) expr(node ast.Expr) {
g.write('/* guard */')
}
ast.IndexExpr {
//
g.index_expr(it)
}
ast.InfixExpr {
// sdf
g.infix_expr(it)
}
ast.IntegerLiteral {
@ -614,20 +652,30 @@ fn (g mut Gen) expr(node ast.Expr) {
ast.MethodCallExpr {
mut receiver_name := 'TODO'
// TODO: there are still due to unchecked exprs (opt/some fn arg)
if it.typ != 0 {
typ_sym := g.table.get_type_symbol(it.typ)
if it.expr_type != 0 {
typ_sym := g.table.get_type_symbol(it.expr_type)
receiver_name = typ_sym.name
// if typ_sym.kind == .array {
// receiver_name = 'array'
// }
if typ_sym.kind == .array && receiver_name.starts_with('array_') {
// array_byte_clone => array_clone
receiver_name = 'array'
}
}
name := '${receiver_name}_$it.name'.replace('.', '__')
// if it.receiver_type != 0 {
// g.write('/*${g.typ(it.receiver_type)}*/')
// g.write('/*expr_type=${g.typ(it.expr_type)} rec type=${g.typ(it.receiver_type)}*/')
// }
g.write('${name}(')
if table.type_is_ptr(it.receiver_type) && !table.type_is_ptr(it.expr_type) {
// The receiver is a reference, but the caller provided a value
// Add `&` automatically.
g.write('&')
}
g.expr(it.expr)
if it.args.len > 0 {
g.write(', ')
}
g.call_args(it.args)
g.call_args(it.args, it.muts)
g.write(')')
}
ast.None {
@ -643,9 +691,13 @@ fn (g mut Gen) expr(node ast.Expr) {
g.write(it.op.str())
}
ast.PrefixExpr {
g.write(it.op.str())
if it.op == .amp {
g.is_amp = true
}
// g.write('/*pref*/')
g.write(it.op.str())
g.expr(it.right)
g.is_amp = false
}
/*
ast.UnaryExpr {
@ -677,13 +729,22 @@ fn (g mut Gen) expr(node ast.Expr) {
// `user := User{name: 'Bob'}`
ast.StructInit {
styp := g.typ(it.typ)
g.writeln('($styp){')
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){')
}
for i, field in it.fields {
g.write('\t.$field = ')
g.expr(it.exprs[i])
g.writeln(', ')
}
g.write('}')
if g.is_amp {
g.write(', sizeof($styp))')
}
}
ast.SelectorExpr {
g.expr(it.expr)
@ -731,14 +792,27 @@ fn (g mut Gen) infix_expr(it ast.InfixExpr) {
g.expr(it.right)
g.write(')')
}
// arr << val
else if it.op == .left_shift && g.table.get_type_symbol(it.left_type).kind == .array {
g.write('array_push(')
else if it.op == .key_in {
styp := g.typ(it.left_type)
g.write('_IN($styp, ')
g.expr(it.left)
g.write(', ')
g.expr(it.right)
g.write(')')
}
// arr << val
else if it.op == .left_shift && g.table.get_type_symbol(it.left_type).kind == .array {
sym := g.table.get_type_symbol(it.left_type)
info := sym.info as table.Array
elem_type_str := g.typ(info.elem_type)
// g.write('array_push(&')
tmp := g.new_tmp_var()
g.write('_PUSH(&')
g.expr(it.left)
g.write(', ')
g.expr(it.right)
g.write(', $tmp, $elem_type_str)')
}
else {
// if it.op == .dot {
// println('!! dot')
@ -847,8 +921,11 @@ fn (g mut Gen) const_decl(node ast.ConstDecl) {
}
}
fn (g mut Gen) call_args(args []ast.Expr) {
fn (g mut Gen) call_args(args []ast.Expr, muts []bool) {
for i, expr in args {
if muts[i] {
g.write('&/*mut*/')
}
g.expr(expr)
if i != args.len - 1 {
g.write(', ')
@ -860,19 +937,26 @@ fn verror(s string) {
println('cgen error: $s')
// exit(1)
}
const (
builtins = ['string', 'array', 'KeyValue', 'map', 'Option']
)
fn (g mut Gen) write_builtin_types() {
mut builtin_types := []table.TypeSymbol // builtin types
// builtin types need to be on top
// everything except builtin will get sorted
for builtin_name in builtins {
builtin_types << g.table.types[g.table.type_idxs[builtin_name]]
}
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
// builtin_types := [
mut builtin_types := []table.TypeSymbol // builtin types
// builtin types need to be on top
builtins := ['string', 'array', 'KeyValue', 'map', 'Option']
// everything except builtin will get sorted
for builtin_name in builtins {
builtin_types << g.table.types[g.table.type_idxs[builtin_name]]
}
for typ in g.table.types {
if !(typ.name in builtins) {
types << typ
@ -882,7 +966,7 @@ fn (g mut Gen) write_sorted_types() {
types_sorted := g.sort_structs(types)
// Generate C code
g.definitions.writeln('// builtin types:')
g.write_types(builtin_types)
// g.write_types(builtin_types)
g.definitions.writeln('//------------------ #endbuiltin')
g.write_types(types_sorted)
}

View File

@ -7,14 +7,14 @@ import (
const (
nr_tests = 4
term_ok = term.ok_message('OK')
term_fail = term.fail_message('FAIL')
)
fn test_c_files() {
println('Running V => C tests')
vexe := os.getenv('VEXE')
vroot := os.dir(vexe)
term_ok := term.ok_message('OK')
term_fail := term.fail_message('FAIL')
for i in 1 .. (nr_tests + 1) {
path := '$vroot/vlib/v/gen/tests/${i}.vv'
mut ctext := os.read_file('$vroot/vlib/v/gen/tests/${i}.c') or {
@ -24,30 +24,31 @@ fn test_c_files() {
mut b := builder.new_builder(pref.Preferences{})
b.module_search_paths = ['$vroot/vlib/v/gen/tests/']
res := b.gen_c([path]).after('#endbuiltin')
if compare_texts(res, ctext) {
eprintln('${term_ok} ${i}')
if compare_texts(res, ctext, path) {
println('${term_ok} ${i}')
}
else {
eprintln('${term_fail} ${i}')
eprintln('${path}: got\n$res')
assert false
}
}
}
fn compare_texts(a, b string) bool {
fn compare_texts(a, b, path string) bool {
lines_a_ := a.trim_space().split_into_lines()
lines_b_ := b.trim_space().split_into_lines()
lines_a := lines_a_.filter(it != '')
lines_b := lines_b_.filter(it != '')
if lines_a.len != lines_b.len {
println(term.red('different len'))
// return false
println('${path}: got\n$a')
return false
}
for i, line_a in lines_a {
line_b := lines_b[i]
if line_a.trim_space() != line_b.trim_space() {
println(term.red('i=$i V="$line_a" C="$line_b"'))
println('${path}: got\n$a')
println('${term_fail} ${i}')
println(term.red('i=$i "$line_a" expected="$line_b"'))
// exit(1)
return false
}

View File

@ -294,6 +294,7 @@ typedef array array_u32;
typedef array array_u64;
typedef map map_int;
typedef map map_string;
typedef byte array_fixed_byte_300 [300];
#ifndef bool
typedef int bool;
#define true 1

View File

@ -52,7 +52,9 @@ int main() {
localmod__pub_foo();
int ten = localmod__get_int_10();
println(localmod__pub_int_const);
int g = (int)(3.0);
int g = ((int)(3.0));
byte* bytes = ((byte*)(0));
User user_ptr = (User*)memdup(&(User){}, sizeof(User));
return 0;
}

View File

@ -34,6 +34,8 @@ fn main() {
ten := localmod.get_int_10()
println(localmod.pub_int_const)
g := int(3.0)
bytes := &byte(0)
user_ptr := &User{}
}
/*
user := User{}

View File

@ -75,6 +75,8 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
mut is_method := false
mut rec_type := table.void_type
mut rec_mut := false
mut args := []table.Var
mut ast_args := []ast.Arg
if p.tok.kind == .lpar {
is_method = true
p.next()
@ -84,6 +86,15 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
rec_mut = true
}
rec_type = p.parse_type()
args << table.Var{
// Receiver is the first arg
typ: rec_type
name: rec_name
}
ast_args << ast.Arg{
name: rec_name
typ: rec_type
}
// p.table.register_var(table.Var{
// name: rec_name
// typ: rec_type
@ -111,8 +122,8 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
}
// println('fn decl $name')
// Args
mut args := []table.Var
ast_args,is_variadic := p.fn_args()
ast_args2,is_variadic := p.fn_args()
ast_args << ast_args2
for ast_arg in ast_args {
var := table.Var{
name: ast_arg.name

View File

@ -44,6 +44,7 @@ mut:
scope &ast.Scope
imports map[string]string
ast_imports []ast.Import
is_amp bool
}
// for tests
@ -55,7 +56,7 @@ pub fn parse_stmt(text string, table &table.Table, scope &ast.Scope) ast.Stmt {
pref: &pref.Preferences{}
scope: scope
// scope: &ast.Scope{start_pos: 0, parent: 0}
}
p.init_parse_fns()
p.read_first_token()
@ -79,7 +80,7 @@ pub fn parse_file(path string, table &table.Table, comments_mode scanner.Comment
parent: 0
}
// comments_mode: comments_mode
}
p.read_first_token()
// p.scope = &ast.Scope{start_pos: p.tok.position(), parent: 0}
@ -558,7 +559,11 @@ pub fn (p mut Parser) name_expr() ast.Expr {
// if name in table.builtin_type_names {
if (name in p.table.type_idxs || name_w_mod in p.table.type_idxs) && !(name in ['C.stat', 'C.sigaction']) {
// TODO handle C.stat()
to_typ := p.parse_type()
mut to_typ := p.parse_type()
if p.is_amp {
// Handle `&Foo(0)`
to_typ = table.type_to_ptr(to_typ)
}
p.check(.lpar)
mut expr := ast.Expr{}
mut arg := ast.Expr{}
@ -613,7 +618,7 @@ pub fn (p mut Parser) name_expr() ast.Expr {
p.expr_mod = ''
return ast.EnumVal{
enum_name: enum_name // lp.prepend_mod(enum_name)
val: val
pos: p.tok.position()
}
@ -810,8 +815,12 @@ pub fn (p mut Parser) expr(precedence int) ast.Expr {
fn (p mut Parser) prefix_expr() ast.PrefixExpr {
op := p.tok.kind
if op == .amp {
p.is_amp = true
}
p.next()
right := p.expr(1)
p.is_amp = false
return ast.PrefixExpr{
op: op
right: right
@ -940,7 +949,7 @@ fn (p mut Parser) infix_expr(left ast.Expr) ast.Expr {
left: left
right: right
// right_type: typ
op: op
pos: pos
}
@ -1051,7 +1060,7 @@ fn (p mut Parser) for_statement() ast.Stmt {
p.scope.register_var(ast.Var{
name: var_name
// expr: cond
})
stmts := p.parse_block()
// println('nr stmts=$stmts.len')
@ -1146,11 +1155,11 @@ fn (p mut Parser) if_expr() ast.Expr {
stmts: stmts
else_stmts: else_stmts
// typ: typ
pos: pos
has_else: has_else
// left: left
}
return node
}
@ -1324,7 +1333,7 @@ fn (p mut Parser) const_decl() ast.ConstDecl {
fields << ast.Field{
name: name
// typ: typ
}
exprs << expr
// TODO: once consts are fixed reg here & update in checker
@ -1715,7 +1724,6 @@ fn (p mut Parser) type_decl() ast.TypeDecl {
sub_types: sum_variants
}
}
// type MyType int
parent_type := p.parse_type()
pid := table.type_idx(parent_type)