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 autofuzz.log
.project.gf .project.gf
.aider*

View File

@ -311,7 +311,7 @@ fn (l Line) write_at_category(txt string) ?string {
title := category_map[l.category] title := category_map[l.category]
title_pos := txt.index(title)? title_pos := txt.index(title)?
// Find the position of the ### category 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] first_half := txt[..pos]
second_half := txt[pos..] second_half := txt[pos..]
if txt.contains(l.text) { if txt.contains(l.text) {

View File

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

View File

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

View File

@ -707,7 +707,7 @@ pub fn (s string) replace_each(vals []string) string {
with_ = with_ with_ = with_
for { for {
idx = s_.index_after(rep, idx) idx = s_.index_after_(rep, idx)
if idx == -1 { if idx == -1 {
break break
} }
@ -817,7 +817,37 @@ pub fn (s string) trim_space() string {
return res 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 { if p.len > s.len {
return -1 return -1
} }

View File

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

View File

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

View File

@ -8,9 +8,6 @@ module http
// "GET / HTTP/1.1" => ["GET" "/" "HTTP/1.1"] // "GET / HTTP/1.1" => ["GET" "/" "HTTP/1.1"]
fn fast_request_words(line string) (int, int) { fn fast_request_words(line string) (int, int) {
space1 := line.index(' ') or { return 0, 0 } space1 := line.index(' ') or { return 0, 0 }
space2 := line.index_after(' ', space1 + 1) space2 := line.index_after(' ', space1 + 1) or { return 0, 0 }
if space2 == -1 {
return 0, 0
}
return space1, space2 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 { if has_next {
break break
} }
index = flag.index_after(' -', index + 1) index = flag.index_after(' -', index + 1) or { -1 }
} }
if index == -1 { if index == -1 {
value = flag.trim_space() value = flag.trim_space()

View File

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

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} ]') 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.AnonFn, ast.ArrayDecompose, ast.AsCast, ast.Assoc, ast.AtExpr, ast.CTempVar,
ast.ChanInit, ast.Comment, ast.ComptimeCall, ast.ComptimeSelector, ast.ComptimeType, ast.ChanInit, ast.Comment, ast.ComptimeCall, ast.ComptimeSelector, ast.ComptimeType,
ast.ConcatExpr, ast.DumpExpr, ast.EmptyExpr, ast.EnumVal, ast.GoExpr, ast.SpawnExpr, 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.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.Nil, ast.None, ast.OffsetOf, ast.RangeExpr, ast.SelectExpr, ast.SqlExpr, ast.TypeNode,
ast.TypeNode, ast.TypeOf, ast.LambdaExpr { ast.TypeOf, ast.LambdaExpr {
e.error('unhandled expression ${typeof(expr).name}') 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 mut ret_str := s
for { for {
// find the position of the first `\` followed by a newline, after `start`: // find the position of the first `\` followed by a newline, after `start`:
idx := ret_str.index_after('\\\n', start) idx := ret_str.index_after('\\\n', start) or { break }
if idx == -1 {
break
}
start = idx start = idx
// Here, ret_str[idx] is \, and ret_str[idx+1] is newline. // Here, ret_str[idx] is \, and ret_str[idx+1] is newline.
// Depending on the number of backslashes before the newline, we should either // Depending on the number of backslashes before the newline, we should either