all: add string range OrExpr (#13189)
parent
d1ac22e3bb
commit
727c9fb4a1
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
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 {
|
||||
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('*')
|
||||
}
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
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: .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
|
||||
}
|
||||
}
|
||||
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
|
||||
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: .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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue