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
|
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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue