builtin: string.index_after() ?int

This commit is contained in:
Alexander Medvednikov 2025-03-12 23:02:51 +03:00
parent 85973b9cca
commit 951d30405f
12 changed files with 94 additions and 44 deletions

1
.gitignore vendored
View File

@ -155,3 +155,4 @@ bench/vectors/obj
autofuzz.log
.project.gf
.aider*

View File

@ -311,7 +311,7 @@ fn (l Line) write_at_category(txt string) ?string {
title := category_map[l.category]
title_pos := txt.index(title)?
// Find the position of the ### category title
pos := txt.index_after('\n', title_pos + 1)
pos := txt.index_after('\n', title_pos + 1) or { return none }
first_half := txt[..pos]
second_half := txt[pos..]
if txt.contains(l.text) {

View File

@ -104,24 +104,18 @@ fn get_all_modules() []string {
mut start_token := "<a href='/mod"
end_token := '</a>'
// get the start index of the module entry
mut start_index := s.index_after(start_token, read_len)
mut start_index := s.index_after(start_token, read_len) or { -1 }
if start_index == -1 {
start_token = '<a href="/mod'
start_index = s.index_after(start_token, read_len)
if start_index == -1 {
break
}
start_index = s.index_after(start_token, read_len) or { break }
}
// get the index of the end of anchor (a) opening tag
// we use the previous start_index to make sure we are getting a module and not just a random 'a' tag
start_token = '>'
start_index = s.index_after(start_token, start_index) + start_token.len
start_index = s.index_after(start_token, start_index) or { break } + start_token.len
// get the index of the end of module entry
end_index := s.index_after(end_token, start_index)
if end_index == -1 {
break
}
end_index := s.index_after(end_token, start_index) or { break }
modules << s[start_index..end_index]
read_len = end_index
if read_len >= s.len {

View File

@ -4,11 +4,8 @@ fn main() {
html := http.get_text('https://news.ycombinator.com')
mut pos := 0
for {
pos = html.index_after('https://', pos + 1)
if pos == -1 {
break
}
end := html.index_after('"', pos)
pos = html.index_after('https://', pos + 1) or { break }
end := html.index_after('"', pos) or { break }
println(html[pos..end])
}
}

View File

@ -707,7 +707,7 @@ pub fn (s string) replace_each(vals []string) string {
with_ = with_
for {
idx = s_.index_after(rep, idx)
idx = s_.index_after_(rep, idx)
if idx == -1 {
break
}
@ -817,7 +817,37 @@ pub fn (s string) trim_space() string {
return res
}
pub fn (s string) index_after(p string, start int) int {
pub fn (s string) index_after(p string, start int) ?int {
if p.len > s.len {
return none
}
mut strt := start
if start < 0 {
strt = 0
}
if start >= s.len {
return none
}
mut i := strt
for i < s.len {
mut j := 0
mut ii := i
for j < p.len && s[ii] == p[j] {
j++
ii++
}
if j == p.len {
return i
}
i++
}
return none
}
pub fn (s string) index_after_(p string, start int) int {
if p.len > s.len {
return -1
}

View File

@ -392,7 +392,7 @@ pub fn (s string) replace(rep string, with string) string {
}
mut idx := 0
for {
idx = s.index_after(rep, idx)
idx = s.index_after_(rep, idx)
if idx == -1 {
break
}
@ -463,7 +463,7 @@ pub fn (s string) replace_each(vals []string) string {
with := vals[rep_i + 1]
for {
idx = s_.index_after(rep, idx)
idx = s_.index_after_(rep, idx)
if idx == -1 {
break
}
@ -1352,7 +1352,36 @@ fn (s string) index_last_(p string) int {
// index_after returns the position of the input string, starting search from `start` position.
@[direct_array_access]
pub fn (s string) index_after(p string, start int) int {
pub fn (s string) index_after(p string, start int) ?int {
if p.len > s.len {
return none
}
mut strt := start
if start < 0 {
strt = 0
}
if start >= s.len {
return none
}
mut i := strt
for i < s.len {
mut j := 0
mut ii := i
for j < p.len && unsafe { s.str[ii] == p.str[j] } {
j++
ii++
}
if j == p.len {
return i
}
i++
}
return none
}
// index_after_ returns the position of the input string, starting search from `start` position.
@[direct_array_access]
pub fn (s string) index_after_(p string, start int) int {
if p.len > s.len {
return -1
}
@ -1429,7 +1458,7 @@ pub fn (s string) count(substr string) int {
mut i := 0
for {
i = s.index_after(substr, i)
i = s.index_after_(substr, i)
if i == -1 {
return n
}

View File

@ -95,11 +95,11 @@ fn (mut r Reader) read_line() !string {
return &EndOfFileError{}
}
le := if r.is_mac_pre_osx_le { '\r' } else { '\n' }
mut i := r.data.index_after(le, r.row_pos)
mut i := r.data.index_after(le, r.row_pos) or { -1 }
if i == -1 {
if r.row_pos == 0 {
// check for pre osx mac line endings
i = r.data.index_after('\r', r.row_pos)
i = r.data.index_after('\r', r.row_pos) or { -1 }
if i != -1 {
r.is_mac_pre_osx_le = true
} else {

View File

@ -8,9 +8,6 @@ module http
// "GET / HTTP/1.1" => ["GET" "/" "HTTP/1.1"]
fn fast_request_words(line string) (int, int) {
space1 := line.index(' ') or { return 0, 0 }
space2 := line.index_after(' ', space1 + 1)
if space2 == -1 {
return 0, 0
}
space2 := line.index_after(' ', space1 + 1) or { return 0, 0 }
return space1, space2
}

View File

@ -64,7 +64,7 @@ pub fn (mut t Table) parse_cflag(cflg string, mod string, ctimedefines []string)
if has_next {
break
}
index = flag.index_after(' -', index + 1)
index = flag.index_after(' -', index + 1) or { -1 }
}
if index == -1 {
value = flag.trim_space()

View File

@ -3367,15 +3367,17 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
}
c.check_any_type(to_type, to_sym, node.pos)
if (to_sym.is_number() && from_sym.name == 'JS.Number')
|| (to_sym.is_number() && from_sym.name == 'JS.BigInt')
|| (to_sym.is_string() && from_sym.name == 'JS.String')
|| (to_type.is_bool() && from_sym.name == 'JS.Boolean')
|| (from_type.is_bool() && to_sym.name == 'JS.Boolean')
|| (from_sym.is_number() && to_sym.name == 'JS.Number')
|| (from_sym.is_number() && to_sym.name == 'JS.BigInt')
|| (from_sym.is_string() && to_sym.name == 'JS.String') {
return to_type
if c.pref.backend.is_js() {
if (to_sym.is_number() && from_sym.name == 'JS.Number')
|| (to_sym.is_number() && from_sym.name == 'JS.BigInt')
|| (to_sym.is_string() && from_sym.name == 'JS.String')
|| (to_type.is_bool() && from_sym.name == 'JS.Boolean')
|| (from_type.is_bool() && to_sym.name == 'JS.Boolean')
|| (from_sym.is_number() && to_sym.name == 'JS.Number')
|| (from_sym.is_number() && to_sym.name == 'JS.BigInt')
|| (from_sym.is_string() && to_sym.name == 'JS.String') {
return to_type
}
}
if !c.expected_type.has_flag(.generic) && to_sym.name.len == 1

View File

@ -579,12 +579,15 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
}
e.error('unhandled index expression ${left}[ ${index} ]')
}
ast.OrExpr {
e.error('unhandled expression ${typeof(expr).name}')
}
ast.AnonFn, ast.ArrayDecompose, ast.AsCast, ast.Assoc, ast.AtExpr, ast.CTempVar,
ast.ChanInit, ast.Comment, ast.ComptimeCall, ast.ComptimeSelector, ast.ComptimeType,
ast.ConcatExpr, ast.DumpExpr, ast.EmptyExpr, ast.EnumVal, ast.GoExpr, ast.SpawnExpr,
ast.IfGuardExpr, ast.IsRefType, ast.Likely, ast.LockExpr, ast.MapInit, ast.MatchExpr,
ast.Nil, ast.None, ast.OffsetOf, ast.OrExpr, ast.RangeExpr, ast.SelectExpr, ast.SqlExpr,
ast.TypeNode, ast.TypeOf, ast.LambdaExpr {
ast.Nil, ast.None, ast.OffsetOf, ast.RangeExpr, ast.SelectExpr, ast.SqlExpr, ast.TypeNode,
ast.TypeOf, ast.LambdaExpr {
e.error('unhandled expression ${typeof(expr).name}')
}
}

View File

@ -1506,10 +1506,7 @@ fn trim_slash_line_break(s string) string {
mut ret_str := s
for {
// find the position of the first `\` followed by a newline, after `start`:
idx := ret_str.index_after('\\\n', start)
if idx == -1 {
break
}
idx := ret_str.index_after('\\\n', start) or { break }
start = idx
// Here, ret_str[idx] is \, and ret_str[idx+1] is newline.
// Depending on the number of backslashes before the newline, we should either