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:
parent
e40ab547ba
commit
d1500511e6
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
258
compiler/query.v
258
compiler/query.v
@ -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)')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user