println: optimize and remove memory leaks

pull/1049/head
Alexander Medvednikov 2019-07-03 23:53:48 +02:00
parent 5d4d3b838b
commit 1e32a4cec4
3 changed files with 37 additions and 18 deletions

View File

@ -670,7 +670,7 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn {
if i == f.args.len - 1 && arg.name == '..' { if i == f.args.len - 1 && arg.name == '..' {
break break
} }
amp_ph := p.cgen.add_placeholder() ph := p.cgen.add_placeholder()
// ) here means not enough args were supplied // ) here means not enough args were supplied
if p.tok == RPAR { if p.tok == RPAR {
str_args := f.str_args(p.table)// TODO this is C args str_args := f.str_args(p.table)// TODO this is C args
@ -688,18 +688,21 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn {
p.check(MUT) p.check(MUT)
} }
typ := p.bool_expression() typ := p.bool_expression()
// TODO temporary hack to allow println(777) // Optimize `println`: replace it with `printf` to avoid extra allocations and
// function calls. `println(777)` => `printf("%d\n", 777)`
// (If we don't check for void, then V will compile `println(func())`)
if i == 0 && f.name == 'println' && typ != 'string' && typ != 'void' { if i == 0 && f.name == 'println' && typ != 'string' && typ != 'void' {
// If we dont check for void, then V will compile "println(procedure())"
T := p.table.find_type(typ) T := p.table.find_type(typ)
if typ == 'u8' { fmt := p.typ_to_fmt(typ)
p.cgen.set_placeholder(amp_ph, 'u8_str(') if fmt != '' {
} p.cgen.cur_line = p.cgen.cur_line.replace('println (', '/*opt*/printf ("' + fmt + '", ')
else if T.parent == 'int' { continue
p.cgen.set_placeholder(amp_ph, 'int_str(') }
if T.parent == 'int' {
p.cgen.set_placeholder(ph, 'int_str(')
} }
else if typ.ends_with('*') { else if typ.ends_with('*') {
p.cgen.set_placeholder(amp_ph, 'ptr_str(') p.cgen.set_placeholder(ph, 'ptr_str(')
} }
else { else {
// Make sure this type has a `str()` method // Make sure this type has a `str()` method
@ -719,7 +722,7 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn {
} }
p.error('`$typ` needs to have method `str() string` to be printable') p.error('`$typ` needs to have method `str() string` to be printable')
} }
p.cgen.set_placeholder(amp_ph, '${typ}_str(') p.cgen.set_placeholder(ph, '${typ}_str(')
} }
p.gen(')') p.gen(')')
continue continue
@ -737,7 +740,7 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn {
if !is_interface { if !is_interface {
// Dereference // Dereference
if got.contains('*') && !expected.contains('*') { if got.contains('*') && !expected.contains('*') {
p.cgen.set_placeholder(amp_ph, '*') p.cgen.set_placeholder(ph, '*')
} }
// Reference // Reference
// TODO ptr hacks. DOOM hacks, fix please. // TODO ptr hacks. DOOM hacks, fix please.
@ -746,14 +749,14 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn {
if ! (expected == 'void*' && got == 'int') && if ! (expected == 'void*' && got == 'int') &&
! (expected == 'byte*' && got.contains(']byte')) && ! (expected == 'byte*' && got.contains(']byte')) &&
! (expected == 'byte*' && got == 'string') { ! (expected == 'byte*' && got == 'string') {
p.cgen.set_placeholder(amp_ph, '& /*11 EXP:"$expected" GOT:"$got" */') p.cgen.set_placeholder(ph, '& /*11 EXP:"$expected" GOT:"$got" */')
} }
} }
} }
// interface? // interface?
if is_interface { if is_interface {
if !got.contains('*') { if !got.contains('*') {
p.cgen.set_placeholder(amp_ph, '&') p.cgen.set_placeholder(ph, '&')
} }
// Pass all interface methods // Pass all interface methods
interface_type := p.table.find_type(arg.typ) interface_type := p.table.find_type(arg.typ)

View File

@ -2149,7 +2149,6 @@ fn (p mut Parser) typ_to_fmt(typ string) string {
if typ.ends_with('*') { if typ.ends_with('*') {
return '%p' return '%p'
} }
p.error('unhandled sprintf format "$typ" ')
} }
return '' return ''
} }
@ -2214,17 +2213,22 @@ fn (p mut Parser) string_expr() {
p.next() p.next()
} }
else { else {
format += p.typ_to_fmt(typ) f := p.typ_to_fmt(typ)
if f == '' {
p.error('unhandled sprintf format "$typ" ')
}
format += f
} }
} }
// println("hello %d", num) optimization. // println("hello %d", num) optimization.
if p.cgen.nogen { if p.cgen.nogen {
return return
} }
// Don't allocate a new string, just print it. TODO HACK PRINT OPT // println: don't allocate a new string, just print it.
cur_line := p.cgen.cur_line.trim_space() cur_line := p.cgen.cur_line.trim_space()
if cur_line.contains('println(') && p.tok != PLUS && !p.pref.is_prod && !cur_line.contains('string_add') { if cur_line.contains('println (') && p.tok != PLUS &&
p.cgen.cur_line = cur_line.replace('println(', 'printf(') !cur_line.contains('string_add') && !cur_line.contains('eprintln') {
p.cgen.cur_line = cur_line.replace('println (', 'printf(')
p.gen('$format\\n$args') p.gen('$format\\n$args')
return return
} }

View File

@ -8,6 +8,7 @@ struct B{
mut: mut:
a A a A
} }
struct C { struct C {
mut: mut:
b B b B
@ -15,6 +16,11 @@ mut:
as []A as []A
num int num int
} }
struct User {
name string
age int
}
fn test_struct_levels() { fn test_struct_levels() {
mut c := C{} mut c := C{}
@ -42,3 +48,9 @@ fn test_struct_levels() {
c.as[0].val = 10 c.as[0].val = 10
assert c.as[0].val == 10 assert c.as[0].val == 10
} }
fn test_struct_str() {
u := User{'Bob', 30}
println(u) // make sure the struct is printable
// assert u.str() == '{name:"Bob", age:30}' // TODO
}