From 8f10e37370c661c75163ac6a70ee315853264a24 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 4 Jul 2019 01:56:18 +0200 Subject: [PATCH] make << work with appending arrays; PostgreSQL driver --- compiler/main.v | 1 + compiler/parser.v | 49 ++++++---------- compiler/table.v | 3 + vlib/builtin/array.v | 8 +-- vlib/builtin/array_test.v | 2 +- vlib/gg/gg.v | 2 +- vlib/os/os.v | 38 ++----------- vlib/pg/pg.v | 115 ++++++++++++++++++++++++++++++++++++++ vlib/pg/pg_lin.v | 4 ++ vlib/pg/pg_mac.v | 2 + vlib/strings/builder.v | 2 + 11 files changed, 151 insertions(+), 75 deletions(-) create mode 100644 vlib/pg/pg.v create mode 100644 vlib/pg/pg_lin.v create mode 100644 vlib/pg/pg_mac.v diff --git a/compiler/main.v b/compiler/main.v index 2d104c31ac..f7b2e9e203 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -236,6 +236,7 @@ typedef map map_string; //============================== HELPER C MACROS =============================*/ #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 _IN(typ, val, arr) array_##typ##_contains(arr, val) #define ALLOC_INIT(type, ...) (type *)memdup((type[]){ __VA_ARGS__ }, sizeof(type)) #define UTF8_CHAR_LEN( byte ) (( 0xE5000000 >> (( byte >> 3 ) & 0x1e )) & 3 ) + 1 diff --git a/compiler/parser.v b/compiler/parser.v index 748da98218..fed5f5f34b 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -48,21 +48,9 @@ mut: assigned_type string left_type string tmp_cnt int - // TODO all these options are copy-pasted from the V struct. Create a Settings struct instead? - // is_test bool is_script bool pref *Preferences // Setting and Preferences shared from V struct - // is_live bool - // is_so bool - // is_prof bool - // translated bool - // is_prod bool - // is_verbose bool - // obfuscate bool - // is_play bool - // is_repl bool builtin_pkg bool - // build_mode BuildMode vh_lines []string inside_if_expr bool is_struct_init bool @@ -92,19 +80,9 @@ fn (c mut V) new_parser(path string, run Pass) Parser { table: c.table cur_fn: EmptyFn cgen: c.cgen - // is_test: c.pref.is_test is_script: (c.pref.is_script && path == c.dir) pref: c.pref - // is_so: c.is_so os: c.os - // is_prof: c.is_prof - // is_prod: c.is_prod - // is_play: c.is_play - // translated: c.translated - // obfuscate: c.obfuscate - // is_verbose: c.is_verbose - // build_mode: c.build_mode - // is_repl: c.is_repl run: run vroot: c.vroot } @@ -148,7 +126,7 @@ fn (p mut Parser) parse() { p.fgenln('\n') p.builtin_pkg = p.pkg == 'builtin' p.can_chash = p.pkg == 'gg' || p.pkg == 'glm' || p.pkg == 'gl' || - p.pkg == 'http' || p.pkg == 'glfw' // TODO tmp remove + p.pkg == 'http' || p.pkg == 'glfw' || p.pkg=='ui' // TODO tmp remove // Import pass - the first and the smallest pass that only analyzes imports p.table.register_package(p.pkg) if p.run == RUN_IMPORTS { @@ -1151,8 +1129,7 @@ fn (p mut Parser) var_decl() { p.statements() p.genln('$typ $name = *($typ*) $tmp . data;') if !p.returns && p.prev_tok2 != CONTINUE && p.prev_tok2 != BREAK { - println(p.prev_tok2) - p.error('`or` statement must return/continue/break') + p.error('`or` block must return/continue/break/panic') } } p.register_var(Var { @@ -1820,13 +1797,21 @@ fn (p mut Parser) expression() string { if !p.expr_var.is_mut && !p.pref.translated { p.error('`$p.expr_var.name` is immutable (can\'t <<)') } - p.check_types(p.expression(), tmp_typ) - // Pass tmp var info to the _PUSH macro - p.gen('), $tmp, $tmp_typ)') - // Prepend tmp initialisation and push call - // Don't dereference if it's already a mutable array argument (`fn foo(mut []int)`) - push_call := if typ.contains('*'){'_PUSH('} else { '_PUSH(&'} // p.cgen.set_placeholder(ph, '_PUSH(&') - p.cgen.set_placeholder(ph, push_call) + expr_type := p.expression() + // Two arrays of the same type? + push_array := typ == expr_type + if push_array { + p.cgen.set_placeholder(ph, '_PUSH_MANY(&' ) + p.gen('), $tmp, $typ)') + } else { + p.check_types(expr_type, tmp_typ) + // Pass tmp var info to the _PUSH macro + // Prepend tmp initialisation and push call + // Don't dereference if it's already a mutable array argument (`fn foo(mut []int)`) + push_call := if typ.contains('*'){'_PUSH('} else { '_PUSH(&'} + p.cgen.set_placeholder(ph, push_call) + p.gen('), $tmp, $tmp_typ)') + } return 'void' } else { diff --git a/compiler/table.v b/compiler/table.v index 9a5d727b4c..1de6cbcfb5 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -437,6 +437,9 @@ fn (p mut Parser) _check_types(got, expected string, throw bool) bool { if got=='byteptr' && expected=='byte*' { return true } + if got=='byte*' && expected=='byteptr' { + return true + } if got=='int' && expected=='byte*' { return true } diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 0848addcf4..cec68385e6 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -69,13 +69,6 @@ fn array_repeat(val voidptr, nr_repeats, elm_size int) array { return arr } -pub fn (a mut array) append_array(b array) { - for i := 0; i < b.len; i++ { - val := b[i] - a._push(val) - } -} - pub fn (a mut array) sort_with_compare(compare voidptr) { C.qsort(a.data, a.len, a.element_size, compare) } @@ -178,6 +171,7 @@ fn (arr mut array) _push(val voidptr) { arr.len++ } +// `val` is array.data pub fn (arr mut array) _push_many(val voidptr, size int) { if arr.len >= arr.cap - size { cap := (arr.len + size) * 2 diff --git a/vlib/builtin/array_test.v b/vlib/builtin/array_test.v index 31b29c43cd..84eaca3824 100644 --- a/vlib/builtin/array_test.v +++ b/vlib/builtin/array_test.v @@ -141,7 +141,7 @@ fn test_slice() { fn test_push_many() { mut a := [1, 2, 3] b := [4, 5, 6] - a._push_many(b.data, b.len) + a << b assert a.len == 6 assert a[0] == 1 assert a[3] == 4 diff --git a/vlib/gg/gg.v b/vlib/gg/gg.v index ea02de9545..9d1802db35 100644 --- a/vlib/gg/gg.v +++ b/vlib/gg/gg.v @@ -567,7 +567,7 @@ fn (ctx &GG) _draw_text(_x, _y int, utext ustring, cfg gx.TextCfg) { // #free(runes.data); } -fn (ctx &GG) draw_text_def(x, y int, text string) { +pub fn (ctx &GG) draw_text_def(x, y int, text string) { cfg := gx.TextCfg { color: gx.Black, size: DEFAULT_FONT_SIZE, diff --git a/vlib/os/os.v b/vlib/os/os.v index de6fcad0c6..33af8e9a64 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -108,12 +108,7 @@ fn parse_windows_cmd_line(cmd byteptr) []string { //pub fn read_file(path string) ?string { pub fn read_file(path string) ?string { mut res := '' - mut mode := 'r' - 777 // TODO - // Need 'rb' on windows to avoid the \r\n mess. - $if windows { - mode = 'rb' - } + mut mode := 'rb' cpath := path.cstr() fp := C.fopen(cpath, mode.cstr()) if isnil(fp) { @@ -132,31 +127,6 @@ pub fn read_file(path string) ?string { return res } -pub fn read_file_opt(path string) ?string { - mut res := '' - mut mode := 'r' - 777 // TODO - // Need 'rb' on windows to avoid the \r\n mess. - $if windows { - mode = 'rb' - } - cpath := path.cstr() - fp := C.fopen(cpath, mode.cstr()) - if isnil(fp) { - return error('failed to open file "$path"') - } - C.fseek(fp, 0, SEEK_END) - fsize := C.ftell(fp) - // C.fseek(fp, 0, SEEK_SET) // same as C.rewind(fp) below - C.rewind(fp) - mut str := malloc(fsize + 1) - C.fread(str, fsize, 1, fp) - C.fclose(fp) - str[fsize] = 0 - res = tos(str, fsize) - return res -} - // file_size returns the size of the file located in `path`. pub fn file_size(path string) int { s := C.stat{} @@ -208,7 +178,7 @@ fn read_ulines(path string) []ustring { pub fn open(path string) ?File { cpath := path.cstr() file := File { - cfile: C.fopen(cpath, 'r') + cfile: C.fopen(cpath, 'rb') } if isnil(file.cfile) { return error('failed to open file "$path"') @@ -220,7 +190,7 @@ pub fn open(path string) ?File { pub fn create(path string) ?File { cpath := path.cstr() file := File { - cfile: C.fopen(cpath, 'w') + cfile: C.fopen(cpath, 'wb') } if isnil(file.cfile) { return error('failed to create file "$path"') @@ -231,7 +201,7 @@ pub fn create(path string) ?File { pub fn open_append(path string) ?File { cpath := path.cstr() file := File { - cfile: C.fopen(cpath, 'a') + cfile: C.fopen(cpath, 'ab') } if isnil(file.cfile) { return error('failed to create file "$path"') diff --git a/vlib/pg/pg.v b/vlib/pg/pg.v new file mode 100644 index 0000000000..3e7d27cf02 --- /dev/null +++ b/vlib/pg/pg.v @@ -0,0 +1,115 @@ +module pg + +import os +import time + +#flag -lpq +#flag linux -I/usr/include/postgresql +#include + +struct DB { +mut: + conn *C.PGconn +} + +struct Row { +pub: + vals []string +} + +import const ( + CONNECTION_OK +) + +struct C.PGResult { } + +fn C.PQconnectdb(a byteptr) *C.PGconn +fn C.PQerrorMessage(voidptr) byteptr + +pub fn connect(dbname, user string) DB { + conninfo := 'host=localhost user=$user dbname=$dbname' + conn:=C.PQconnectdb(conninfo.cstr()) + status := C.PQstatus(conn) + if status != CONNECTION_OK { + error_msg := C.PQerrorMessage(conn) + eprintln('Connection to a PG database failed: ' + string(error_msg)) + exit(1) + } + return DB {conn: conn} +} + +fn res_to_rows(res voidptr) []pg.Row { + nr_rows := C.PQntuples(res) + nr_cols := C.PQnfields(res) + mut rows := []pg.Row + for i := 0; i < nr_rows; i++ { + mut row := Row{} + for j := 0; j < nr_cols; j++ { + val := C.PQgetvalue(res, i, j) + row.vals << string(val) + } + rows << row + } + return rows +} + +pub fn (db DB) q_int(query string) int { + rows := db.exec(query) + if rows.len == 0 { + println('q_int "$query" not found') + return 0 + } + row := rows[0] + if row.vals.len == 0 { + return 0 + } + val := row.vals[0] + return val.int() +} + +pub fn (db DB) q_string(query string) string { + rows := db.exec(query) + if rows.len == 0 { + println('q_string "$query" not found') + return '' + } + row := rows[0] + if row.vals.len == 0 { + return '' + } + val := row.vals[0] + return val +} + +pub fn (db DB) q_strings(query string) []pg.Row { + return db.exec(query) +} + +pub fn (db DB) exec(query string) []pg.Row { + res := C.PQexec(db.conn, query.cstr()) + e := string(C.PQerrorMessage(db.conn)) + if e != '' { + println('pg exec error:') + println(e) + return res_to_rows(res) + } + return res_to_rows(res) +} + + +// +pub fn (db DB) exec_param2(query string, param, param2 string) []pg.Row { + mut param_vals := [2]byteptr + param_vals[0] = param.str + param_vals[1] = param2.str + res := C.PQexecParams(db.conn, query.str, 2, 0, param_vals, 0, 0, 0) + return res_to_rows(res) +} + +pub fn (db DB) exec_param(query string, param string) []pg.Row { + mut param_vals := [1]byteptr + param_vals[0] = param.str + res := C.PQexecParams(db.conn, query.str, 1, 0, param_vals, 0, 0, 0) + return res_to_rows(res) +} + diff --git a/vlib/pg/pg_lin.v b/vlib/pg/pg_lin.v new file mode 100644 index 0000000000..965aac0d24 --- /dev/null +++ b/vlib/pg/pg_lin.v @@ -0,0 +1,4 @@ +module pg + +#flag -I/usr/include/postgresql +// #flag `pkg-config --cflags libpq` diff --git a/vlib/pg/pg_mac.v b/vlib/pg/pg_mac.v new file mode 100644 index 0000000000..65c15291c5 --- /dev/null +++ b/vlib/pg/pg_mac.v @@ -0,0 +1,2 @@ +module pg + diff --git a/vlib/strings/builder.v b/vlib/strings/builder.v index 4cc0e8a7e7..11f34c6127 100644 --- a/vlib/strings/builder.v +++ b/vlib/strings/builder.v @@ -18,11 +18,13 @@ pub fn new_builder(initial_size int) Builder { pub fn (b mut Builder) write(s string) { b.buf._push_many(s.str, s.len) + //b.buf << []byte(s) // TODO b.len += s.len } pub fn (b mut Builder) writeln(s string) { b.buf._push_many(s.str, s.len) + //b.buf << []byte(s) // TODO b.buf << `\n` b.len += s.len + 1 }