Skip to content

Commit 727c9fb

Browse files
all: add string range OrExpr (#13189)
1 parent d1ac22e commit 727c9fb

File tree

5 files changed

+183
-8
lines changed

5 files changed

+183
-8
lines changed

vlib/builtin/string.v

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,32 @@ pub fn (s string) substr(start int, end int) string {
818818
return res
819819
}
820820

821+
// version of `substr()` that is used in `a[start..end] or {`
822+
// return an error when the index is out of range
823+
[direct_array_access]
824+
pub fn (s string) substr_with_check(start int, end int) ?string {
825+
if start > end || start > s.len || end > s.len || start < 0 || end < 0 {
826+
return error('substr($start, $end) out of bounds (len=$s.len)')
827+
}
828+
len := end - start
829+
if len == s.len {
830+
return s.clone()
831+
}
832+
mut res := string{
833+
str: unsafe { malloc_noscan(len + 1) }
834+
len: len
835+
}
836+
for i in 0 .. len {
837+
unsafe {
838+
res.str[i] = s.str[start + i]
839+
}
840+
}
841+
unsafe {
842+
res.str[len] = 0
843+
}
844+
return res
845+
}
846+
821847
// substr_ni returns the string between index positions `start` and `end` allowing negative indexes
822848
// This function always return a valid string.
823849
[direct_array_access]

vlib/builtin/string_test.v

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,34 @@ fn test_sort_reverse() {
120120
assert vals[3] == 'arr'
121121
}
122122

123+
fn test_ranges() {
124+
s := 'test'
125+
s1 := s[0..20] or { 'both' }
126+
s2 := s[..20] or { 'last' }
127+
s3 := s[10..] or { 'first' }
128+
s4 := ranges_propagate_both(s) or { 'both' }
129+
s5 := ranges_propagate_last(s) or { 'last' }
130+
s6 := ranges_propagate_first(s) or { 'first' }
131+
assert s1 == 'both'
132+
assert s2 == 'last'
133+
assert s3 == 'first'
134+
assert s4 == 'both'
135+
assert s5 == 'last'
136+
assert s6 == 'first'
137+
}
138+
139+
fn ranges_propagate_first(s string) ?string {
140+
return s[10..] ?
141+
}
142+
143+
fn ranges_propagate_last(s string) ?string {
144+
return s[..20] ?
145+
}
146+
147+
fn ranges_propagate_both(s string) ?string {
148+
return s[1..20] ?
149+
}
150+
123151
fn test_split_nth() {
124152
a := '1,2,3'
125153
assert a.split(',').len == 3

vlib/v/checker/checker.v

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3569,6 +3569,10 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
35693569
}
35703570
.array {
35713571
node.is_array = true
3572+
if node.or_expr.kind != .absent && node.index is ast.RangeExpr {
3573+
c.error('custom error handling on range expressions for arrays is not supported yet.',
3574+
node.or_expr.pos)
3575+
}
35723576
break
35733577
}
35743578
.array_fixed {

vlib/v/gen/c/index.v

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,23 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
5959

6060
fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
6161
sym := g.table.final_sym(node.left_type)
62+
mut tmp_opt := ''
63+
mut cur_line := ''
64+
mut gen_or := node.or_expr.kind != .absent || node.is_option
65+
6266
if sym.kind == .string {
6367
if node.is_gated {
6468
g.write('string_substr_ni(')
6569
} else {
66-
g.write('string_substr(')
70+
if gen_or {
71+
tmp_opt = g.new_tmp_var()
72+
cur_line = g.go_before_stmt(0)
73+
g.out.write_string(util.tabs(g.indent))
74+
opt_elem_type := g.typ(ast.string_type.set_flag(.optional))
75+
g.write('$opt_elem_type $tmp_opt = string_substr_with_check(')
76+
} else {
77+
g.write('string_substr(')
78+
}
6779
}
6880
if node.left_type.is_ptr() {
6981
g.write('*')
@@ -131,6 +143,14 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
131143
g.write('.len')
132144
}
133145
g.write(')')
146+
147+
if gen_or {
148+
if !node.is_option {
149+
g.or_block(tmp_opt, node.or_expr, ast.string_type)
150+
}
151+
152+
g.write('\n$cur_line*(string*)&${tmp_opt}.data')
153+
}
134154
}
135155

136156
fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {

vlib/v/parser/parser.v

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2378,44 +2378,141 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr {
23782378
high = p.expr(0)
23792379
has_high = true
23802380
}
2381-
pos := start_pos.extend(p.tok.position())
2381+
2382+
pos_high := start_pos.extend(p.tok.position())
23822383
p.check(.rsbr)
2384+
mut or_kind_high := ast.OrKind.absent
2385+
mut or_stmts_high := []ast.Stmt{}
2386+
mut or_pos_high := token.Position{}
2387+
2388+
if !p.or_is_handled {
2389+
// a[..end] or {...}
2390+
if p.tok.kind == .key_orelse {
2391+
was_inside_or_expr := p.inside_or_expr
2392+
p.inside_or_expr = true
2393+
or_pos_high = p.tok.position()
2394+
p.next()
2395+
p.open_scope()
2396+
or_stmts_high = p.parse_block_no_scope(false)
2397+
or_pos_high = or_pos_high.extend(p.prev_tok.position())
2398+
p.close_scope()
2399+
p.inside_or_expr = was_inside_or_expr
2400+
return ast.IndexExpr{
2401+
left: left
2402+
pos: pos_high
2403+
index: ast.RangeExpr{
2404+
low: ast.empty_expr()
2405+
high: high
2406+
has_high: has_high
2407+
pos: pos_high
2408+
is_gated: is_gated
2409+
}
2410+
or_expr: ast.OrExpr{
2411+
kind: .block
2412+
stmts: or_stmts_high
2413+
pos: or_pos_high
2414+
}
2415+
is_gated: is_gated
2416+
}
2417+
}
2418+
// `a[start..end] ?`
2419+
if p.tok.kind == .question {
2420+
or_pos_high = p.tok.position()
2421+
or_kind_high = .propagate
2422+
p.next()
2423+
}
2424+
}
2425+
23832426
return ast.IndexExpr{
23842427
left: left
2385-
pos: pos
2428+
pos: pos_high
23862429
index: ast.RangeExpr{
23872430
low: ast.empty_expr()
23882431
high: high
23892432
has_high: has_high
2390-
pos: pos
2433+
pos: pos_high
23912434
is_gated: is_gated
23922435
}
2436+
or_expr: ast.OrExpr{
2437+
kind: or_kind_high
2438+
stmts: or_stmts_high
2439+
pos: or_pos_high
2440+
}
23932441
is_gated: is_gated
23942442
}
23952443
}
23962444
expr := p.expr(0) // `[expr]` or `[expr..`
23972445
mut has_high := false
2446+
23982447
if p.tok.kind == .dotdot {
2399-
// [start..end] or [start..]
2448+
// either [start..end] or [start..]
24002449
p.next()
24012450
mut high := ast.empty_expr()
24022451
if p.tok.kind != .rsbr {
24032452
has_high = true
24042453
high = p.expr(0)
24052454
}
2406-
pos := start_pos.extend(p.tok.position())
2455+
pos_low := start_pos.extend(p.tok.position())
24072456
p.check(.rsbr)
2457+
mut or_kind_low := ast.OrKind.absent
2458+
mut or_stmts_low := []ast.Stmt{}
2459+
mut or_pos_low := token.Position{}
2460+
2461+
if !p.or_is_handled {
2462+
// a[start..end] or {...}
2463+
if p.tok.kind == .key_orelse {
2464+
was_inside_or_expr := p.inside_or_expr
2465+
p.inside_or_expr = true
2466+
or_pos_low = p.tok.position()
2467+
p.next()
2468+
p.open_scope()
2469+
or_stmts_low = p.parse_block_no_scope(false)
2470+
or_pos_low = or_pos_low.extend(p.prev_tok.position())
2471+
p.close_scope()
2472+
p.inside_or_expr = was_inside_or_expr
2473+
return ast.IndexExpr{
2474+
left: left
2475+
pos: pos_low
2476+
index: ast.RangeExpr{
2477+
low: expr
2478+
high: high
2479+
has_high: has_high
2480+
has_low: has_low
2481+
pos: pos_low
2482+
is_gated: is_gated
2483+
}
2484+
or_expr: ast.OrExpr{
2485+
kind: .block
2486+
stmts: or_stmts_low
2487+
pos: or_pos_low
2488+
}
2489+
is_gated: is_gated
2490+
}
2491+
}
2492+
// `a[start..end] ?`
2493+
if p.tok.kind == .question {
2494+
or_pos_low = p.tok.position()
2495+
or_kind_low = .propagate
2496+
p.next()
2497+
}
2498+
}
2499+
24082500
return ast.IndexExpr{
24092501
left: left
2410-
pos: pos
2502+
pos: pos_low
24112503
index: ast.RangeExpr{
24122504
low: expr
24132505
high: high
24142506
has_high: has_high
24152507
has_low: has_low
2416-
pos: pos
2508+
pos: pos_low
24172509
is_gated: is_gated
24182510
}
2511+
or_expr: ast.OrExpr{
2512+
kind: or_kind_low
2513+
stmts: or_stmts_low
2514+
pos: or_pos_low
2515+
}
24192516
is_gated: is_gated
24202517
}
24212518
}

0 commit comments

Comments
 (0)