all: add string range OrExpr (#13189)

pull/13200/head
trueFireblade 2022-01-17 11:03:10 +01:00 committed by GitHub
parent d1ac22e3bb
commit 727c9fb4a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 183 additions and 8 deletions

View File

@ -818,6 +818,32 @@ pub fn (s string) substr(start int, end int) string {
return res 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 // substr_ni returns the string between index positions `start` and `end` allowing negative indexes
// This function always return a valid string. // This function always return a valid string.
[direct_array_access] [direct_array_access]

View File

@ -120,6 +120,34 @@ fn test_sort_reverse() {
assert vals[3] == 'arr' 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() { fn test_split_nth() {
a := '1,2,3' a := '1,2,3'
assert a.split(',').len == 3 assert a.split(',').len == 3

View File

@ -3569,6 +3569,10 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
} }
.array { .array {
node.is_array = true 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 break
} }
.array_fixed { .array_fixed {

View File

@ -59,12 +59,24 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) { fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
sym := g.table.final_sym(node.left_type) 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 sym.kind == .string {
if node.is_gated { if node.is_gated {
g.write('string_substr_ni(') g.write('string_substr_ni(')
} else {
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 { } else {
g.write('string_substr(') g.write('string_substr(')
} }
}
if node.left_type.is_ptr() { if node.left_type.is_ptr() {
g.write('*') g.write('*')
} }
@ -131,6 +143,14 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
g.write('.len') g.write('.len')
} }
g.write(')') 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) { fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {

View File

@ -2378,44 +2378,141 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr {
high = p.expr(0) high = p.expr(0)
has_high = true has_high = true
} }
pos := start_pos.extend(p.tok.position())
pos_high := start_pos.extend(p.tok.position())
p.check(.rsbr) 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{ return ast.IndexExpr{
left: left left: left
pos: pos pos: pos_high
index: ast.RangeExpr{ index: ast.RangeExpr{
low: ast.empty_expr() low: ast.empty_expr()
high: high high: high
has_high: has_high has_high: has_high
pos: pos pos: pos_high
is_gated: is_gated 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_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: or_kind_high
stmts: or_stmts_high
pos: or_pos_high
}
is_gated: is_gated is_gated: is_gated
} }
} }
expr := p.expr(0) // `[expr]` or `[expr..` expr := p.expr(0) // `[expr]` or `[expr..`
mut has_high := false mut has_high := false
if p.tok.kind == .dotdot { if p.tok.kind == .dotdot {
// [start..end] or [start..] // either [start..end] or [start..]
p.next() p.next()
mut high := ast.empty_expr() mut high := ast.empty_expr()
if p.tok.kind != .rsbr { if p.tok.kind != .rsbr {
has_high = true has_high = true
high = p.expr(0) high = p.expr(0)
} }
pos := start_pos.extend(p.tok.position()) pos_low := start_pos.extend(p.tok.position())
p.check(.rsbr) 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{ return ast.IndexExpr{
left: left left: left
pos: pos pos: pos_low
index: ast.RangeExpr{ index: ast.RangeExpr{
low: expr low: expr
high: high high: high
has_high: has_high has_high: has_high
has_low: has_low has_low: has_low
pos: pos pos: pos_low
is_gated: is_gated 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_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: or_kind_low
stmts: or_stmts_low
pos: or_pos_low
}
is_gated: is_gated is_gated: is_gated
} }
} }