Skip to content

Commit cdfbb29

Browse files
committed
strings: replace_each([]); orm: update
1 parent 4e69c40 commit cdfbb29

5 files changed

Lines changed: 177 additions & 1 deletion

File tree

vlib/builtin/string.v

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,101 @@ pub fn (s string) replace(rep, with string) string {
190190
return tos(b, new_len)
191191
}
192192

193+
struct RepIndex {
194+
idx int
195+
val_idx int
196+
}
197+
198+
fn (a mut []RepIndex) sort() {
199+
a.sort_with_compare(compare_rep_index)
200+
}
201+
202+
203+
// TODO
204+
/*
205+
fn (a RepIndex) < (b RepIndex) bool {
206+
return a.idx < b.idx
207+
}
208+
*/
209+
210+
fn compare_rep_index(a, b &RepIndex) int {
211+
if a.idx < b.idx {
212+
return -1
213+
}
214+
if a.idx > b.idx {
215+
return 1
216+
}
217+
return 0
218+
}
219+
220+
pub fn (s string) replace_each(vals []string) string {
221+
if s.len == 0 || vals.len == 0 {
222+
return s
223+
}
224+
if vals.len % 2 != 0 {
225+
println('string.replace_many(): odd number of strings')
226+
return s
227+
}
228+
// `rep` - string to replace
229+
// `with` - string to replace with
230+
// Remember positions of all rep strings, and calculate the length
231+
// of the new string to do just one allocation.
232+
mut new_len := s.len
233+
mut idxs := []RepIndex
234+
mut idx := 0
235+
for rep_i := 0; rep_i < vals.len; rep_i+=2 {
236+
// vals: ['rep1, 'with1', 'rep2', 'with2']
237+
rep := vals[rep_i]
238+
with := vals[rep_i+1]
239+
for {
240+
idx = s.index_after(rep, idx)
241+
if idx == -1 {
242+
break
243+
}
244+
// We need to remember both the position in the string,
245+
// and which rep/with pair it refers to.
246+
idxs << RepIndex{idx, rep_i}
247+
idx++
248+
new_len += with.len - rep.len
249+
}
250+
}
251+
// Dont change the string if there's nothing to replace
252+
if idxs.len == 0 {
253+
return s
254+
}
255+
idxs.sort()
256+
mut b := malloc(new_len + 1)// add a \0 just in case
257+
// Fill the new string
258+
mut idx_pos := 0
259+
mut cur_idx := idxs[idx_pos]
260+
mut b_i := 0
261+
for i := 0; i < s.len; i++ {
262+
// Reached the location of rep, replace it with "with"
263+
if i == cur_idx.idx {
264+
rep := vals[cur_idx.val_idx]
265+
with := vals[cur_idx.val_idx+1]
266+
for j := 0; j < with.len; j++ {
267+
b[b_i] = with[j]
268+
b_i++
269+
}
270+
// Skip the length of rep, since we just replaced it with "with"
271+
i += rep.len - 1
272+
// Go to the next index
273+
idx_pos++
274+
if idx_pos < idxs.len {
275+
cur_idx = idxs[idx_pos]
276+
}
277+
}
278+
// Rep doesnt start here, just copy
279+
else {
280+
b[b_i] = s[i]
281+
b_i++
282+
}
283+
}
284+
b[new_len] = `\0`
285+
return tos(b, new_len)
286+
}
287+
193288
pub fn (s string) bool() bool {
194289
return s == 'true' || s == 't' // TODO t for pg, remove
195290
}

vlib/builtin/string_test.v

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,22 @@ fn test_replace() {
219219
assert c.replace('','-') == c
220220
}
221221

222+
fn test_replace_each() {
223+
s := 'hello man man :)'
224+
q := s.replace_each([
225+
'man', 'dude',
226+
'hello', 'hey'
227+
])
228+
assert q == 'hey dude dude :)'
229+
bb := '[b]bold[/b] [code]code[/code]'
230+
assert bb.replace_each([
231+
'[b]', '<b>',
232+
'[/b]', '</b>',
233+
'[code]', '<code>',
234+
'[/code]', '</code>'
235+
]) == '<b>bold</b> <code>code</code>'
236+
}
237+
222238
fn test_itoa() {
223239
num := 777
224240
assert num.str() == '777'

vlib/compiler/parser.v

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1764,7 +1764,11 @@ fn (p mut Parser) var_expr(v Var) string {
17641764
name := p.tokens[p.token_idx].lit
17651765
if !name.contains('exec') && !name.starts_with('q_') {
17661766
p.next()
1767-
p.insert_query(fn_ph)
1767+
if name == 'insert' {
1768+
p.insert_query(fn_ph)
1769+
} else if name == 'update' {
1770+
p.update_query(fn_ph)
1771+
}
17681772
return 'void'
17691773
}
17701774
}

vlib/compiler/query.v

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,63 @@ fn (p mut Parser) insert_query(fn_ph int) {
245245
0, params, 0, 0, 0)')
246246
}
247247

248+
// `db.update User set nr_orders=nr_orders+1`
249+
fn (p mut Parser) update_query(fn_ph int) {
250+
println('update query')
251+
p.check_name()
252+
table_name := p.check_name()
253+
typ := p.table.find_type(table_name)
254+
if typ.name == '' {
255+
p.error('unknown type `$table_name`')
256+
}
257+
set := p.check_name()
258+
if set != 'set' {
259+
p.error('expected `set`')
260+
}
261+
if typ.fields.len == 0 {
262+
p.error('V orm: update: empty fields in `$typ.name`')
263+
}
264+
if typ.fields[0].name != 'id' {
265+
p.error('V orm: `id int` must be the first field in `$typ.name`')
266+
}
267+
field := p.check_name()
268+
p.check(.assign)
269+
for f in typ.fields {
270+
if !(f.typ in ['string', 'int', 'bool']) {
271+
println('orm: skipping $f.name')
272+
continue
273+
}
274+
p.register_var({ f | is_mut: true, is_used:true, is_changed:true })
275+
}
276+
mut q := 'update ${typ.name}s set $field='
277+
p.is_sql = true
278+
set_typ, expr := p.tmp_expr()
279+
p.is_sql = false
280+
// TODO this hack should not be necessary
281+
if set_typ == 'bool' {
282+
if expr.trim_space() == '1' {
283+
q += 'true'
284+
}
285+
else {
286+
q += 'false'
287+
}
288+
} else {
289+
q += expr
290+
}
291+
// where
292+
if p.tok == .name && p.lit == 'where' {
293+
p.next()
294+
p.is_sql = true
295+
_, wexpr := p.tmp_expr()
296+
p.is_sql = false
297+
q += ' where ' + wexpr
298+
}
299+
300+
301+
nr_vals := 0
302+
p.cgen.insert_before('char* params[$nr_vals];')// + params)
303+
p.cgen.set_placeholder(fn_ph, 'PQexecParams( ')
304+
println('update q="$q"')
305+
p.genln('.conn, "$q", $nr_vals, 0, params, 0, 0, 0)')
306+
}
307+

vlib/vweb/vweb.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ pub fn run<T>(app mut T, port int) {
196196
// }
197197

198198
// Call the right action
199+
println('action=$action')
199200
app.$action() or {
200201
conn.write(HTTP_404) or {}
201202
}

0 commit comments

Comments
 (0)