strings: replace_each([]); orm: update

pull/3051/head
Alexander Medvednikov 2019-12-10 14:32:12 +03:00
parent 4e69c40e12
commit cdfbb2978d
5 changed files with 177 additions and 1 deletions

View File

@ -190,6 +190,101 @@ pub fn (s string) replace(rep, with string) string {
return tos(b, new_len)
}
struct RepIndex {
idx int
val_idx int
}
fn (a mut []RepIndex) sort() {
a.sort_with_compare(compare_rep_index)
}
// TODO
/*
fn (a RepIndex) < (b RepIndex) bool {
return a.idx < b.idx
}
*/
fn compare_rep_index(a, b &RepIndex) int {
if a.idx < b.idx {
return -1
}
if a.idx > b.idx {
return 1
}
return 0
}
pub fn (s string) replace_each(vals []string) string {
if s.len == 0 || vals.len == 0 {
return s
}
if vals.len % 2 != 0 {
println('string.replace_many(): odd number of strings')
return s
}
// `rep` - string to replace
// `with` - string to replace with
// Remember positions of all rep strings, and calculate the length
// of the new string to do just one allocation.
mut new_len := s.len
mut idxs := []RepIndex
mut idx := 0
for rep_i := 0; rep_i < vals.len; rep_i+=2 {
// vals: ['rep1, 'with1', 'rep2', 'with2']
rep := vals[rep_i]
with := vals[rep_i+1]
for {
idx = s.index_after(rep, idx)
if idx == -1 {
break
}
// We need to remember both the position in the string,
// and which rep/with pair it refers to.
idxs << RepIndex{idx, rep_i}
idx++
new_len += with.len - rep.len
}
}
// Dont change the string if there's nothing to replace
if idxs.len == 0 {
return s
}
idxs.sort()
mut b := malloc(new_len + 1)// add a \0 just in case
// Fill the new string
mut idx_pos := 0
mut cur_idx := idxs[idx_pos]
mut b_i := 0
for i := 0; i < s.len; i++ {
// Reached the location of rep, replace it with "with"
if i == cur_idx.idx {
rep := vals[cur_idx.val_idx]
with := vals[cur_idx.val_idx+1]
for j := 0; j < with.len; j++ {
b[b_i] = with[j]
b_i++
}
// Skip the length of rep, since we just replaced it with "with"
i += rep.len - 1
// Go to the next index
idx_pos++
if idx_pos < idxs.len {
cur_idx = idxs[idx_pos]
}
}
// Rep doesnt start here, just copy
else {
b[b_i] = s[i]
b_i++
}
}
b[new_len] = `\0`
return tos(b, new_len)
}
pub fn (s string) bool() bool {
return s == 'true' || s == 't' // TODO t for pg, remove
}

View File

@ -219,6 +219,22 @@ fn test_replace() {
assert c.replace('','-') == c
}
fn test_replace_each() {
s := 'hello man man :)'
q := s.replace_each([
'man', 'dude',
'hello', 'hey'
])
assert q == 'hey dude dude :)'
bb := '[b]bold[/b] [code]code[/code]'
assert bb.replace_each([
'[b]', '<b>',
'[/b]', '</b>',
'[code]', '<code>',
'[/code]', '</code>'
]) == '<b>bold</b> <code>code</code>'
}
fn test_itoa() {
num := 777
assert num.str() == '777'

View File

@ -1764,7 +1764,11 @@ fn (p mut Parser) var_expr(v Var) string {
name := p.tokens[p.token_idx].lit
if !name.contains('exec') && !name.starts_with('q_') {
p.next()
p.insert_query(fn_ph)
if name == 'insert' {
p.insert_query(fn_ph)
} else if name == 'update' {
p.update_query(fn_ph)
}
return 'void'
}
}

View File

@ -245,3 +245,63 @@ fn (p mut Parser) insert_query(fn_ph int) {
0, params, 0, 0, 0)')
}
// `db.update User set nr_orders=nr_orders+1`
fn (p mut Parser) update_query(fn_ph int) {
println('update query')
p.check_name()
table_name := p.check_name()
typ := p.table.find_type(table_name)
if typ.name == '' {
p.error('unknown type `$table_name`')
}
set := p.check_name()
if set != 'set' {
p.error('expected `set`')
}
if typ.fields.len == 0 {
p.error('V orm: update: empty fields in `$typ.name`')
}
if typ.fields[0].name != 'id' {
p.error('V orm: `id int` must be the first field in `$typ.name`')
}
field := p.check_name()
p.check(.assign)
for f in typ.fields {
if !(f.typ in ['string', 'int', 'bool']) {
println('orm: skipping $f.name')
continue
}
p.register_var({ f | is_mut: true, is_used:true, is_changed:true })
}
mut q := 'update ${typ.name}s set $field='
p.is_sql = true
set_typ, expr := p.tmp_expr()
p.is_sql = false
// TODO this hack should not be necessary
if set_typ == 'bool' {
if expr.trim_space() == '1' {
q += 'true'
}
else {
q += 'false'
}
} else {
q += expr
}
// where
if p.tok == .name && p.lit == 'where' {
p.next()
p.is_sql = true
_, wexpr := p.tmp_expr()
p.is_sql = false
q += ' where ' + wexpr
}
nr_vals := 0
p.cgen.insert_before('char* params[$nr_vals];')// + params)
p.cgen.set_placeholder(fn_ph, 'PQexecParams( ')
println('update q="$q"')
p.genln('.conn, "$q", $nr_vals, 0, params, 0, 0, 0)')
}

View File

@ -196,6 +196,7 @@ pub fn run<T>(app mut T, port int) {
// }
// Call the right action
println('action=$action')
app.$action() or {
conn.write(HTTP_404) or {}
}