Skip to content

Commit

Permalink
all: add string range OrExpr (#13189)
Browse files Browse the repository at this point in the history
  • Loading branch information
trueFireblade committed Jan 17, 2022
1 parent d1ac22e commit 727c9fb
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 8 deletions.
26 changes: 26 additions & 0 deletions vlib/builtin/string.v
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,32 @@ pub fn (s string) substr(start int, end int) string {
return res
}

// version of `substr()` that is used in `a[start..end] or {`
// return an error when the index is out of range
[direct_array_access]
pub fn (s string) substr_with_check(start int, end int) ?string {
if start > end || start > s.len || end > s.len || start < 0 || end < 0 {
return error('substr($start, $end) out of bounds (len=$s.len)')
}
len := end - start
if len == s.len {
return s.clone()
}
mut res := string{
str: unsafe { malloc_noscan(len + 1) }
len: len
}
for i in 0 .. len {
unsafe {
res.str[i] = s.str[start + i]
}
}
unsafe {
res.str[len] = 0
}
return res
}

// substr_ni returns the string between index positions `start` and `end` allowing negative indexes
// This function always return a valid string.
[direct_array_access]
Expand Down
28 changes: 28 additions & 0 deletions vlib/builtin/string_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,34 @@ fn test_sort_reverse() {
assert vals[3] == 'arr'
}

fn test_ranges() {
s := 'test'
s1 := s[0..20] or { 'both' }
s2 := s[..20] or { 'last' }
s3 := s[10..] or { 'first' }
s4 := ranges_propagate_both(s) or { 'both' }
s5 := ranges_propagate_last(s) or { 'last' }
s6 := ranges_propagate_first(s) or { 'first' }
assert s1 == 'both'
assert s2 == 'last'
assert s3 == 'first'
assert s4 == 'both'
assert s5 == 'last'
assert s6 == 'first'
}

fn ranges_propagate_first(s string) ?string {
return s[10..] ?
}

fn ranges_propagate_last(s string) ?string {
return s[..20] ?
}

fn ranges_propagate_both(s string) ?string {
return s[1..20] ?
}

fn test_split_nth() {
a := '1,2,3'
assert a.split(',').len == 3
Expand Down
4 changes: 4 additions & 0 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -3569,6 +3569,10 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
}
.array {
node.is_array = true
if node.or_expr.kind != .absent && node.index is ast.RangeExpr {
c.error('custom error handling on range expressions for arrays is not supported yet.',
node.or_expr.pos)
}
break
}
.array_fixed {
Expand Down
22 changes: 21 additions & 1 deletion vlib/v/gen/c/index.v
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,23 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {

fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
sym := g.table.final_sym(node.left_type)
mut tmp_opt := ''
mut cur_line := ''
mut gen_or := node.or_expr.kind != .absent || node.is_option

if sym.kind == .string {
if node.is_gated {
g.write('string_substr_ni(')
} else {
g.write('string_substr(')
if gen_or {
tmp_opt = g.new_tmp_var()
cur_line = g.go_before_stmt(0)
g.out.write_string(util.tabs(g.indent))
opt_elem_type := g.typ(ast.string_type.set_flag(.optional))
g.write('$opt_elem_type $tmp_opt = string_substr_with_check(')
} else {
g.write('string_substr(')
}
}
if node.left_type.is_ptr() {
g.write('*')
Expand Down Expand Up @@ -131,6 +143,14 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
g.write('.len')
}
g.write(')')

if gen_or {
if !node.is_option {
g.or_block(tmp_opt, node.or_expr, ast.string_type)
}

g.write('\n$cur_line*(string*)&${tmp_opt}.data')
}
}

fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
Expand Down
111 changes: 104 additions & 7 deletions vlib/v/parser/parser.v
Original file line number Diff line number Diff line change
Expand Up @@ -2378,44 +2378,141 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr {
high = p.expr(0)
has_high = true
}
pos := start_pos.extend(p.tok.position())

pos_high := start_pos.extend(p.tok.position())
p.check(.rsbr)
mut or_kind_high := ast.OrKind.absent
mut or_stmts_high := []ast.Stmt{}
mut or_pos_high := token.Position{}

if !p.or_is_handled {
// a[..end] or {...}
if p.tok.kind == .key_orelse {
was_inside_or_expr := p.inside_or_expr
p.inside_or_expr = true
or_pos_high = p.tok.position()
p.next()
p.open_scope()
or_stmts_high = p.parse_block_no_scope(false)
or_pos_high = or_pos_high.extend(p.prev_tok.position())
p.close_scope()
p.inside_or_expr = was_inside_or_expr
return ast.IndexExpr{
left: left
pos: pos_high
index: ast.RangeExpr{
low: ast.empty_expr()
high: high
has_high: has_high
pos: pos_high
is_gated: is_gated
}
or_expr: ast.OrExpr{
kind: .block
stmts: or_stmts_high
pos: or_pos_high
}
is_gated: is_gated
}
}
// `a[start..end] ?`
if p.tok.kind == .question {
or_pos_high = p.tok.position()
or_kind_high = .propagate
p.next()
}
}

return ast.IndexExpr{
left: left
pos: pos
pos: pos_high
index: ast.RangeExpr{
low: ast.empty_expr()
high: high
has_high: has_high
pos: pos
pos: pos_high
is_gated: is_gated
}
or_expr: ast.OrExpr{
kind: or_kind_high
stmts: or_stmts_high
pos: or_pos_high
}
is_gated: is_gated
}
}
expr := p.expr(0) // `[expr]` or `[expr..`
mut has_high := false

if p.tok.kind == .dotdot {
// [start..end] or [start..]
// either [start..end] or [start..]
p.next()
mut high := ast.empty_expr()
if p.tok.kind != .rsbr {
has_high = true
high = p.expr(0)
}
pos := start_pos.extend(p.tok.position())
pos_low := start_pos.extend(p.tok.position())
p.check(.rsbr)
mut or_kind_low := ast.OrKind.absent
mut or_stmts_low := []ast.Stmt{}
mut or_pos_low := token.Position{}

if !p.or_is_handled {
// a[start..end] or {...}
if p.tok.kind == .key_orelse {
was_inside_or_expr := p.inside_or_expr
p.inside_or_expr = true
or_pos_low = p.tok.position()
p.next()
p.open_scope()
or_stmts_low = p.parse_block_no_scope(false)
or_pos_low = or_pos_low.extend(p.prev_tok.position())
p.close_scope()
p.inside_or_expr = was_inside_or_expr
return ast.IndexExpr{
left: left
pos: pos_low
index: ast.RangeExpr{
low: expr
high: high
has_high: has_high
has_low: has_low
pos: pos_low
is_gated: is_gated
}
or_expr: ast.OrExpr{
kind: .block
stmts: or_stmts_low
pos: or_pos_low
}
is_gated: is_gated
}
}
// `a[start..end] ?`
if p.tok.kind == .question {
or_pos_low = p.tok.position()
or_kind_low = .propagate
p.next()
}
}

return ast.IndexExpr{
left: left
pos: pos
pos: pos_low
index: ast.RangeExpr{
low: expr
high: high
has_high: has_high
has_low: has_low
pos: pos
pos: pos_low
is_gated: is_gated
}
or_expr: ast.OrExpr{
kind: or_kind_low
stmts: or_stmts_low
pos: or_pos_low
}
is_gated: is_gated
}
}
Expand Down

0 comments on commit 727c9fb

Please sign in to comment.