1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

"none" keyword for optionals + more memory fixes

This commit is contained in:
Alexander Medvednikov 2019-09-17 22:41:58 +03:00
parent e40ab547ba
commit d1500511e6
12 changed files with 273 additions and 204 deletions

View File

@ -315,8 +315,9 @@ fn (v &V) type_definitions() string {
// sort structs // sort structs
types_sorted := sort_structs(types) types_sorted := sort_structs(types)
// Generate C code // Generate C code
return types_to_c(builtin_types,v.table) + '\n//----\n' + res := types_to_c(builtin_types,v.table) + '\n//----\n' +
types_to_c(types_sorted, v.table) types_to_c(types_sorted, v.table)
return res
} }
// sort structs by dependant fields // sort structs by dependant fields

View File

@ -116,13 +116,10 @@ typedef map map_string;
#endif #endif
//============================== HELPER C MACROS =============================*/ //============================== HELPER C MACROS =============================*/
#define _PUSH(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array__push(arr, &tmp);} #define _PUSH(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array__push(arr, &tmp);}
#define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array__push_many(arr, tmp.data, tmp.len);} #define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array__push_many(arr, tmp.data, tmp.len);}
#define _IN(typ, val, arr) array_##typ##_contains(arr, val) #define _IN(typ, val, arr) array_##typ##_contains(arr, val)
#define _IN_MAP(val, m) map__exists(m, val) #define _IN_MAP(val, m) map__exists(m, val)
//#define ALLOC_INIT(type, ...) (type *)memdup((type[]){ __VA_ARGS__ }, sizeof(type))
//================================== GLOBALS =================================*/ //================================== GLOBALS =================================*/
byteptr g_str_buf; byteptr g_str_buf;
int load_so(byteptr); int load_so(byteptr);

View File

@ -33,7 +33,34 @@ mut:
//gen_types []string //gen_types []string
} }
fn (f &Fn) find_var(name string) Var { fn (f &Fn) find_var(name string) ?Var {
for i in 0 .. f.var_idx {
if f.local_vars[i].name == name {
return f.local_vars[i]
}
}
return none
}
fn (p &Parser) find_var_check_new_var(name string) ?Var {
for i in 0 .. p.cur_fn.var_idx {
if p.cur_fn.local_vars[i].name == name {
return p.cur_fn.local_vars[i]
}
}
// A hack to allow `newvar := Foo{ field: newvar }`
// Declare the variable so that it can be used in the initialization
if name == 'main__' + p.var_decl_name {
return Var{
name : p.var_decl_name
typ : 'voidptr'
is_mut : true
}
}
return none
}
fn (f &Fn) find_var2(name string) Var {
for i in 0 .. f.var_idx { for i in 0 .. f.var_idx {
if f.local_vars[i].name == name { if f.local_vars[i].name == name {
return f.local_vars[i] return f.local_vars[i]
@ -73,8 +100,10 @@ fn (p mut Parser) mark_var_changed(v Var) {
} }
fn (f mut Fn) known_var(name string) bool { fn (f mut Fn) known_var(name string) bool {
v := f.find_var(name) _ := f.find_var(name) or {
return v.name.len > 0 return false
}
return true
} }
fn (f mut Fn) register_var(v Var) { fn (f mut Fn) register_var(v Var) {
@ -819,9 +848,9 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
} }
p.check(.key_mut) p.check(.key_mut)
var_name := p.lit var_name := p.lit
v := p.cur_fn.find_var(var_name) v := p.cur_fn.find_var(var_name) or {
if v.name == '' {
p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`') p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`')
exit(1)
} }
if !v.is_changed { if !v.is_changed {
p.mark_var_changed(v) p.mark_var_changed(v)

View File

@ -184,6 +184,17 @@ fn main() {
v.run_compiled_executable_and_exit() v.run_compiled_executable_and_exit()
} }
// TODO remove
if v.pref.autofree {
println('started freeing v struct')
v.table.fns.free()
v.table.typesmap.free()
v.table.obf_ids.free()
v.cgen.lines.free()
free(v.cgen)
free(v.table)
println('done!')
}
} }
fn (v mut V) compile() { fn (v mut V) compile() {
@ -833,7 +844,7 @@ fn new_v(args[]string) &V {
show_c_cmd: '-show_c_cmd' in args show_c_cmd: '-show_c_cmd' in args
translated: 'translated' in args translated: 'translated' in args
is_run: 'run' in args is_run: 'run' in args
autofree: 'autofree' in args autofree: '-autofree' in args
is_repl: is_repl is_repl: is_repl
build_mode: build_mode build_mode: build_mode
cflags: cflags cflags: cflags

View File

@ -1267,12 +1267,11 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) {
//p.warn('expecting array got $expr_type') //p.warn('expecting array got $expr_type')
//} //}
// Allow `num = 4` where `num` is an `?int` // Allow `num = 4` where `num` is an `?int`
if p.assigned_type.starts_with('Option_') && expr_type == p.assigned_type.right('Option_'.len) { if p.assigned_type.starts_with('Option_') &&
println('allowing option asss') expr_type == p.assigned_type.right('Option_'.len) {
expr := p.cgen.cur_line.right(pos) expr := p.cgen.cur_line.right(pos)
left := p.cgen.cur_line.left(pos) left := p.cgen.cur_line.left(pos)
typ := expr_type.replace('Option_', '') typ := expr_type.replace('Option_', '')
//p.cgen.cur_line = left + 'opt_ok($expr)'
p.cgen.resetln(left + 'opt_ok($expr, sizeof($typ))') p.cgen.resetln(left + 'opt_ok($expr, sizeof($typ))')
} }
else if !p.builtin_mod && !p.check_types_no_throw(expr_type, p.assigned_type) { else if !p.builtin_mod && !p.check_types_no_throw(expr_type, p.assigned_type) {
@ -1319,7 +1318,7 @@ fn (p mut Parser) var_decl() {
name: name name: name
typ: typ typ: typ
is_mut: is_mut is_mut: is_mut
is_alloc: p.is_alloc is_alloc: p.is_alloc || typ.starts_with('array_')
}) })
//if p.is_alloc { println('REG VAR IS ALLOC $name') } //if p.is_alloc { println('REG VAR IS ALLOC $name') }
p.var_decl_name = '' p.var_decl_name = ''
@ -1486,44 +1485,37 @@ fn (p mut Parser) name_expr() string {
name = p.prepend_mod(name) name = p.prepend_mod(name)
} }
// Variable // Variable
mut v := p.cur_fn.find_var(name) for { // TODO remove
// A hack to allow `newvar := Foo{ field: newvar }` mut v := p.find_var_check_new_var(name) or { break }
// Declare the variable so that it can be used in the initialization if ptr {
if name == 'main__' + p.var_decl_name { p.gen('& /*vvar*/ ')
v.name = p.var_decl_name
v.typ = 'voidptr'
v.is_mut = true
} }
if v.name.len != 0 { else if deref {
if ptr { p.gen('*')
p.gen('& /*vvar*/ ')
}
else if deref {
p.gen('*')
}
mut typ := p.var_expr(v)
// *var
if deref {
if !typ.contains('*') && !typ.ends_with('ptr') {
println('name="$name", t=$v.typ')
p.error('dereferencing requires a pointer, but got `$typ`')
}
typ = typ.replace('ptr', '')// TODO
typ = typ.replace('*', '')// TODO
}
// &var
else if ptr {
typ += '*'
}
if p.inside_return_expr {
//println('marking $v.name returned')
p.mark_var_returned(v)
// v.is_returned = true // TODO modifying a local variable
// that's not used afterwards, this should be a compilation
// error
}
return typ
} }
mut typ := p.var_expr(v)
// *var
if deref {
if !typ.contains('*') && !typ.ends_with('ptr') {
println('name="$name", t=$v.typ')
p.error('dereferencing requires a pointer, but got `$typ`')
}
typ = typ.replace('ptr', '')// TODO
typ = typ.replace('*', '')// TODO
}
// &var
else if ptr {
typ += '*'
}
if p.inside_return_expr {
//println('marking $v.name returned')
p.mark_var_returned(v)
// v.is_returned = true // TODO modifying a local variable
// that's not used afterwards, this should be a compilation
// error
}
return typ
} // TODO REMOVE for{}
// if known_type || is_c_struct_init || (p.first_pass() && p.peek() == .lcbr) { // if known_type || is_c_struct_init || (p.first_pass() && p.peek() == .lcbr) {
// known type? int(4.5) or Color.green (enum) // known type? int(4.5) or Color.green (enum)
if p.table.known_type(name) { if p.table.known_type(name) {
@ -1881,7 +1873,7 @@ struct $f.parent_fn {
} }
enum IndexType { enum IndexType {
none noindex
str str
map map
array array
@ -1898,7 +1890,7 @@ fn get_index_type(typ string) IndexType {
return IndexType.ptr return IndexType.ptr
} }
if typ[0] == `[` { return IndexType.fixed_array } if typ[0] == `[` { return IndexType.fixed_array }
return IndexType.none return IndexType.noindex
} }
fn (p mut Parser) index_expr(typ_ string, fn_ph int) string { fn (p mut Parser) index_expr(typ_ string, fn_ph int) string {
@ -2239,7 +2231,14 @@ fn (p mut Parser) factor() string {
mut typ := '' mut typ := ''
tok := p.tok tok := p.tok
switch tok { switch tok {
case .number: case .key_none:
if !p.expected_type.starts_with('Option_') {
p.error('need "$p.expected_type" got none')
}
p.gen('opt_none()')
p.check(.key_none)
return p.expected_type
case Token.number:
typ = 'int' typ = 'int'
// Check if float (`1.0`, `1e+3`) but not if is hexa // Check if float (`1.0`, `1e+3`) but not if is hexa
if (p.lit.contains('.') || (p.lit.contains('e') || p.lit.contains('E'))) && if (p.lit.contains('.') || (p.lit.contains('e') || p.lit.contains('E'))) &&
@ -2367,10 +2366,10 @@ fn (p mut Parser) assoc() string {
// println('assoc()') // println('assoc()')
p.next() p.next()
name := p.check_name() name := p.check_name()
if !p.cur_fn.known_var(name) { var := p.cur_fn.find_var(name) or {
p.error('unknown variable `$name`') p.error('unknown variable `$name`')
} exit(1)
var := p.cur_fn.find_var(name) }
p.check(.pipe) p.check(.pipe)
p.gen('($var.typ){') p.gen('($var.typ){')
mut fields := []string// track the fields user is setting, the rest will be copied from the old object mut fields := []string// track the fields user is setting, the rest will be copied from the old object
@ -3463,10 +3462,14 @@ fn (p mut Parser) return_st() {
else { else {
ph := p.cgen.add_placeholder() ph := p.cgen.add_placeholder()
p.inside_return_expr = true p.inside_return_expr = true
is_none := p.tok == .key_none
p.expected_type = p.cur_fn.typ
expr_type := p.bool_expression() expr_type := p.bool_expression()
p.inside_return_expr = false p.inside_return_expr = false
// Automatically wrap an object inside an option if the function returns an option // Automatically wrap an object inside an option if the function
if p.cur_fn.typ.ends_with(expr_type) && p.cur_fn.typ.starts_with('Option_') { // returns an option
if p.cur_fn.typ.ends_with(expr_type) && !is_none &&
p.cur_fn.typ.starts_with('Option_') {
tmp := p.get_tmp() tmp := p.get_tmp()
ret := p.cgen.cur_line.right(ph) ret := p.cgen.cur_line.right(ph)
typ := expr_type.replace('Option_', '') typ := expr_type.replace('Option_', '')
@ -3537,7 +3540,7 @@ fn (p mut Parser) go_statement() {
// Method // Method
if p.peek() == .dot { if p.peek() == .dot {
var_name := p.lit var_name := p.lit
v := p.cur_fn.find_var(var_name) v := p.cur_fn.find_var(var_name) or { return }
p.mark_var_used(v) p.mark_var_used(v)
p.next() p.next()
p.check(.dot) p.check(.dot)

View File

@ -2,9 +2,9 @@
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module main module main
import strings import strings
fn sql_params2params_gen(sql_params []string, sql_types []string, qprefix string) string { fn sql_params2params_gen(sql_params []string, sql_types []string, qprefix string) string {
mut params_gen := '' mut params_gen := ''
@ -32,7 +32,7 @@ fn sql_params2params_gen(sql_params []string, sql_types []string, qprefix string
return params_gen return params_gen
} }
// `db.select from User where id == 1 && nr_bookings > 0` // `db.select from User where id == 1 && nr_bookings > 0`
fn (p mut Parser) select_query(fn_ph int) string { fn (p mut Parser) select_query(fn_ph int) string {
// NB: qprefix and { p.sql_i, p.sql_params, p.sql_types } SHOULD be reset for each query, // NB: qprefix and { p.sql_i, p.sql_params, p.sql_types } SHOULD be reset for each query,
// because we can have many queries in the _same_ scope. // because we can have many queries in the _same_ scope.
@ -40,96 +40,96 @@ fn (p mut Parser) select_query(fn_ph int) string {
p.sql_i = 0 p.sql_i = 0
p.sql_params = []string p.sql_params = []string
p.sql_types = []string p.sql_types = []string
mut q := 'select ' mut q := 'select '
p.check(.key_select) p.check(.key_select)
n := p.check_name() n := p.check_name()
if n == 'count' { if n == 'count' {
q += 'count(*) from ' q += 'count(*) from '
p.check_name() p.check_name()
} }
table_name := p.check_name() table_name := p.check_name()
// Register this type's fields as variables so they can be used in where expressions // Register this type's fields as variables so they can be used in where expressions
typ := p.table.find_type(table_name) typ := p.table.find_type(table_name)
if typ.name == '' { if typ.name == '' {
p.error('unknown type `$table_name`') p.error('unknown type `$table_name`')
} }
//fields := typ.fields.filter(typ == 'string' || typ == 'int') //fields := typ.fields.filter(typ == 'string' || typ == 'int')
// get only string and int fields // get only string and int fields
mut fields := []Var mut fields := []Var
for i, field in typ.fields { for i, field in typ.fields {
if field.typ != 'string' && field.typ != 'int' { if field.typ != 'string' && field.typ != 'int' {
continue continue
} }
fields << field fields << field
} }
if fields.len == 0 { if fields.len == 0 {
p.error('V orm: select: empty fields in `$table_name`') p.error('V orm: select: empty fields in `$table_name`')
} }
if fields[0].name != 'id' { if fields[0].name != 'id' {
p.error('V orm: `id int` must be the first field in `$table_name`') p.error('V orm: `id int` must be the first field in `$table_name`')
} }
// 'select id, name, age from...' // 'select id, name, age from...'
if n == 'from' { if n == 'from' {
for i, field in fields { for i, field in fields {
q += field.name q += field.name
if i < fields.len - 1 { if i < fields.len - 1 {
q += ', ' q += ', '
} }
} }
q += ' from ' q += ' from '
} }
for field in fields { for field in fields {
//println('registering sql field var $field.name') //println('registering sql field var $field.name')
if field.typ != 'string' && field.typ != 'int' { if field.typ != 'string' && field.typ != 'int' {
continue continue
} }
p.cur_fn.register_var({ field | is_used:true }) p.cur_fn.register_var({ field | is_used:true })
} }
q += table_name q += table_name
// `where` statement // `where` statement
if p.tok == .name && p.lit == 'where' { if p.tok == .name && p.lit == 'where' {
p.next() p.next()
p.cgen.start_tmp() p.cgen.start_tmp()
p.is_sql = true p.is_sql = true
p.bool_expression() p.bool_expression()
p.is_sql = false p.is_sql = false
q += ' where ' + p.cgen.end_tmp() q += ' where ' + p.cgen.end_tmp()
} }
// limit? // limit?
mut query_one := false mut query_one := false
if p.tok == .name && p.lit == 'limit' { if p.tok == .name && p.lit == 'limit' {
p.next() p.next()
p.cgen.start_tmp() p.cgen.start_tmp()
p.is_sql = true p.is_sql = true
p.bool_expression() p.bool_expression()
p.is_sql = false p.is_sql = false
limit := p.cgen.end_tmp() limit := p.cgen.end_tmp()
q += ' limit ' + limit q += ' limit ' + limit
// `limit 1` means we are getting `?User`, not `[]User` // `limit 1` means we are getting `?User`, not `[]User`
if limit.trim_space() == '1' { if limit.trim_space() == '1' {
query_one = true query_one = true
} }
} }
println('sql query="$q"') println('sql query="$q"')
p.cgen.insert_before('// DEBUG_SQL prefix: $qprefix | fn_ph: $fn_ph | query: "$q" ') p.cgen.insert_before('// DEBUG_SQL prefix: $qprefix | fn_ph: $fn_ph | query: "$q" ')
if n == 'count' { if n == 'count' {
p.cgen.set_placeholder(fn_ph, 'pg__DB_q_int(') p.cgen.set_placeholder(fn_ph, 'pg__DB_q_int(')
p.gen(', tos2("$q"))') p.gen(', tos2("$q"))')
} else { } else {
// Build an object, assign each field. // Build an object, assign each field.
tmp := p.get_tmp() tmp := p.get_tmp()
mut obj_gen := strings.new_builder(300) mut obj_gen := strings.new_builder(300)
for i, field in fields { for i, field in fields {
mut cast := '' mut cast := ''
if field.typ == 'int' { if field.typ == 'int' {
cast = 'v_string_int' cast = 'v_string_int'
} }
obj_gen.writeln('${qprefix}$tmp . $field.name = $cast( *(string*)array__get(${qprefix}row.vals, $i) );') obj_gen.writeln('${qprefix}$tmp . $field.name = $cast( *(string*)array__get(${qprefix}row.vals, $i) );')
} }
// One object // One object
if query_one { if query_one {
mut params_gen := sql_params2params_gen( p.sql_params, p.sql_types, qprefix ) mut params_gen := sql_params2params_gen( p.sql_params, p.sql_types, qprefix )
p.cgen.insert_before(' p.cgen.insert_before('
@ -144,99 +144,99 @@ if (! opt_${qprefix}row . ok ) {
opt_${qprefix}$tmp = v_error( opt_${qprefix}row . error ); opt_${qprefix}$tmp = v_error( opt_${qprefix}row . error );
}else{ }else{
$table_name ${qprefix}$tmp; $table_name ${qprefix}$tmp;
pg__Row ${qprefix}row = *(pg__Row*) opt_${qprefix}row . data; pg__Row ${qprefix}row = *(pg__Row*) opt_${qprefix}row . data;
${obj_gen.str()} ${obj_gen.str()}
opt_${qprefix}$tmp = opt_ok( & ${qprefix}$tmp, sizeof($table_name) ); opt_${qprefix}$tmp = opt_ok( & ${qprefix}$tmp, sizeof($table_name) );
} }
') ')
p.cgen.resetln('opt_${qprefix}$tmp') p.cgen.resetln('opt_${qprefix}$tmp')
} }
// Array // Array
else { else {
q += ' order by id' q += ' order by id'
params_gen := sql_params2params_gen( p.sql_params, p.sql_types, qprefix ) params_gen := sql_params2params_gen( p.sql_params, p.sql_types, qprefix )
p.cgen.insert_before('char* ${qprefix}params[$p.sql_i]; p.cgen.insert_before('char* ${qprefix}params[$p.sql_i];
$params_gen $params_gen
void* ${qprefix}res = PQexecParams(db.conn, "$q", $p.sql_i, 0, ${qprefix}params, 0, 0, 0) ; void* ${qprefix}res = PQexecParams(db.conn, "$q", $p.sql_i, 0, ${qprefix}params, 0, 0, 0) ;
array_pg__Row ${qprefix}rows = pg__res_to_rows(${qprefix}res); array_pg__Row ${qprefix}rows = pg__res_to_rows(${qprefix}res);
// TODO preallocate // TODO preallocate
array ${qprefix}arr_$tmp = new_array(0, 0, sizeof($table_name)); array ${qprefix}arr_$tmp = new_array(0, 0, sizeof($table_name));
for (int i = 0; i < ${qprefix}rows.len; i++) { for (int i = 0; i < ${qprefix}rows.len; i++) {
pg__Row ${qprefix}row = *(pg__Row*)array__get(${qprefix}rows, i); pg__Row ${qprefix}row = *(pg__Row*)array__get(${qprefix}rows, i);
$table_name ${qprefix}$tmp; $table_name ${qprefix}$tmp;
${obj_gen.str()} ${obj_gen.str()}
_PUSH(&${qprefix}arr_$tmp, ${qprefix}$tmp, ${tmp}2, $table_name); _PUSH(&${qprefix}arr_$tmp, ${qprefix}$tmp, ${tmp}2, $table_name);
} }
') ')
p.cgen.resetln('${qprefix}arr_$tmp') p.cgen.resetln('${qprefix}arr_$tmp')
} }
} }
if n == 'count' { if n == 'count' {
return 'int' return 'int'
} else if query_one { } else if query_one {
opt_type := 'Option_$table_name' opt_type := 'Option_$table_name'
p.cgen.typedefs << 'typedef Option $opt_type;' p.cgen.typedefs << 'typedef Option $opt_type;'
p.table.register_type( opt_type ) p.table.register_type( opt_type )
return opt_type return opt_type
} else { } else {
p.register_array('array_$table_name') p.register_array('array_$table_name')
return 'array_$table_name' return 'array_$table_name'
} }
} }
// `db.insert(user)` // `db.insert(user)`
fn (p mut Parser) insert_query(fn_ph int) { fn (p mut Parser) insert_query(fn_ph int) {
p.check_name() p.check_name()
p.check(.lpar) p.check(.lpar)
var_name := p.check_name() var_name := p.check_name()
p.check(.rpar) p.check(.rpar)
var := p.cur_fn.find_var(var_name) var := p.cur_fn.find_var(var_name) or { return }
typ := p.table.find_type(var.typ) typ := p.table.find_type(var.typ)
mut fields := []Var mut fields := []Var
for i, field in typ.fields { for i, field in typ.fields {
if field.typ != 'string' && field.typ != 'int' { if field.typ != 'string' && field.typ != 'int' {
continue continue
} }
fields << field fields << field
} }
if fields.len == 0 { if fields.len == 0 {
p.error('V orm: insert: empty fields in `$var.typ`') p.error('V orm: insert: empty fields in `$var.typ`')
} }
if fields[0].name != 'id' { if fields[0].name != 'id' {
p.error('V orm: `id int` must be the first field in `$var.typ`') p.error('V orm: `id int` must be the first field in `$var.typ`')
} }
table_name := var.typ table_name := var.typ
mut sfields := '' // 'name, city, country' mut sfields := '' // 'name, city, country'
mut params := '' // params[0] = 'bob'; params[1] = 'Vienna'; mut params := '' // params[0] = 'bob'; params[1] = 'Vienna';
mut vals := '' // $1, $2, $3... mut vals := '' // $1, $2, $3...
mut nr_vals := 0 mut nr_vals := 0
for i, field in fields { for i, field in fields {
if field.name == 'id' { if field.name == 'id' {
continue continue
} }
sfields += field.name sfields += field.name
vals += '$' + i.str() vals += '$' + i.str()
nr_vals++ nr_vals++
params += 'params[${i-1}] = ' params += 'params[${i-1}] = '
if field.typ == 'string' { if field.typ == 'string' {
params += '$var_name . $field.name .str;\n' params += '$var_name . $field.name .str;\n'
} else if field.typ == 'int' { } else if field.typ == 'int' {
params += 'int_str($var_name . $field.name).str;\n' params += 'int_str($var_name . $field.name).str;\n'
} else { } else {
p.error('V ORM: unsupported type `$field.typ`') p.error('V ORM: unsupported type `$field.typ`')
} }
if i < fields.len - 1 { if i < fields.len - 1 {
sfields += ', ' sfields += ', '
vals += ', ' vals += ', '
} }
} }
p.cgen.insert_before('char* params[$nr_vals];' + params) p.cgen.insert_before('char* params[$nr_vals];' + params)
p.cgen.set_placeholder(fn_ph, 'PQexecParams( ') p.cgen.set_placeholder(fn_ph, 'PQexecParams( ')
p.genln('.conn, "insert into $table_name ($sfields) values ($vals)", $nr_vals, p.genln('.conn, "insert into $table_name ($sfields) values ($vals)", $nr_vals,
0, params, 0, 0, 0)') 0, params, 0, 0, 0)')
} }

View File

@ -78,6 +78,7 @@ mut:
is_changed bool is_changed bool
scope_level int scope_level int
is_c bool // todo remove once `typ` is `Type`, not string is_c bool // todo remove once `typ` is `Type`, not string
moved bool
} }
struct Type { struct Type {
@ -322,6 +323,7 @@ fn (t &Table) find_fn(name string) Fn {
if !isnil(f.name.str) { if !isnil(f.name.str) {
return f return f
} }
//println('ret find fn')
return Fn{} return Fn{}
} }
@ -412,6 +414,7 @@ fn (t &Type) find_field(name string) Var {
return field return field
} }
} }
//println('ret Var{}')
return Var{} return Var{}
} }
@ -455,11 +458,16 @@ fn (table &Table) find_method(typ &Type, name string) Fn {
// method := typ.find_method(name) // method := typ.find_method(name)
t := table.typesmap[typ.name] t := table.typesmap[typ.name]
method := t.find_method(name) method := t.find_method(name)
if method.name.len == 0 && typ.parent.len > 0 {
for method in t.methods {
if method.name == name {
return method
}
}
if typ.parent != '' {
parent := table.find_type(typ.parent) parent := table.find_type(typ.parent)
return parent.find_method(name) return parent.find_method(name)
// println('parent = $parent.name $res')
// return res
} }
return method return method
} }
@ -472,7 +480,9 @@ fn (t &Type) find_method(name string) Fn {
return method return method
} }
} }
//println('ret Fn{}')
return Fn{} return Fn{}
//return none
} }
/* /*

View File

@ -16,9 +16,21 @@ fn err_call(ok bool) ?int {
return 42 return 42
} }
fn ret_none() ?int {
//return error('wtf') //none
return none
}
fn test_option_for_base_type_without_variable() { fn test_option_for_base_type_without_variable() {
val := err_call(true) or { val := err_call(true) or {
panic(err) panic(err)
} }
assert val == 42 assert val == 42
println('hm')
val2 := ret_none() or {
println('yep')
return
}
println('nice')
println(val2)
} }

View File

@ -96,6 +96,7 @@ enum Token {
key_match key_match
key_module key_module
key_mut key_mut
key_none
key_return key_return
key_select key_select
key_sizeof key_sizeof
@ -222,6 +223,7 @@ fn build_token_str() []string {
s[Token.key_defer] = 'defer' s[Token.key_defer] = 'defer'
s[Token.key_match] = 'match' s[Token.key_match] = 'match'
s[Token.key_select] = 'select' s[Token.key_select] = 'select'
s[Token.key_none] = 'none'
return s return s
} }

View File

@ -4,10 +4,13 @@
- wrap up orm - wrap up orm
- fix vorum, migrate to orm - fix vorum, migrate to orm
- wrap up memory management - wrap up memory management
- remove all compiler memory leaks
- fix child <T> function calls - fix child <T> function calls
+ fix non-ascii rendering in gg (ä, å, etc) + fix non-ascii rendering in gg (ä, å, etc)
- cache all tokens once - cache all tokens once
- enable vfmt - enable vfmt
- optimize the parser (reduce map lookups)
- cache vlib (right now it's re-compiled every time)
- fix openssl on older linux distros - fix openssl on older linux distros
- chat.vlang.io - chat.vlang.io
- rewrite objective c code in v (ui_mac.m) - rewrite objective c code in v (ui_mac.m)
@ -26,9 +29,6 @@
+ javascript backend + javascript backend
- new playground with a v compiler running in the browser - new playground with a v compiler running in the browser
+ o(log n) type lookup + o(log n) type lookup
- remove all compiler memory leaks
- optimize the parser (reduce map lookups)
- prebuilt binaries for all platforms - prebuilt binaries for all platforms
- cache vlib (right now it's re-compiled every time)
- fix interfaces - fix interfaces

View File

@ -155,8 +155,7 @@ fn (m map) bs(query string, start, end int, out voidptr) {
fn preorder_keys(node &mapnode, keys mut []string, key_i int) int { fn preorder_keys(node &mapnode, keys mut []string, key_i int) int {
mut i := key_i mut i := key_i
if !node.is_empty { if !node.is_empty {
mut a := *keys keys[i] = node.key
a[i] = node.key
i++ i++
} }
if !isnil(node.left) { if !isnil(node.left) {

View File

@ -5,21 +5,26 @@
module builtin module builtin
struct Option { struct Option {
data [255]byte data [255]byte
error string error string
ok bool ok bool
is_none bool
} }
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }` // `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
fn opt_ok(data voidptr, size int) Option { fn opt_ok(data voidptr, size int) Option {
if size >= 255 { if size >= 255 {
panic('option size too big: $size (max is 255), this is a temporary limit') panic('option size too big: $size (max is 255), this is a temporary limit')
} }
res := Option { res := Option {
ok: true ok: true
} }
C.memcpy(res.data, data, size) C.memcpy(res.data, data, size)
return res return res
}
fn opt_none() Option {
return Option{ is_none: true }
} }
pub fn error(s string) Option { pub fn error(s string) Option {