strings.textscanner: add .current/0, .peek_back/0, .peek_back_n/1 and .goto_end/1 methods

pull/10530/head
Delyan Angelov 2021-06-20 12:38:14 +03:00
parent ce3681ee8f
commit a21ee1abd4
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
2 changed files with 105 additions and 1 deletions

View File

@ -35,6 +35,7 @@ pub fn (ss &TextScanner) remaining() int {
// next returns the next character code from the input text. // next returns the next character code from the input text.
// next returns `-1` if it can't reach the next character. // next returns `-1` if it can't reach the next character.
// next advances the scanner position.
[direct_array_access; inline] [direct_array_access; inline]
pub fn (mut ss TextScanner) next() int { pub fn (mut ss TextScanner) next() int {
if ss.pos < ss.ilen { if ss.pos < ss.ilen {
@ -76,6 +77,8 @@ pub fn (ss &TextScanner) peek() int {
// peek_n returns the character code from the input text at position + `n`. // peek_n returns the character code from the input text at position + `n`.
// peek_n returns `-1` if it can't peek `n` characters ahead. // peek_n returns `-1` if it can't peek `n` characters ahead.
// ts.peek_n(0) == ts.current() .
// ts.peek_n(1) == ts.peek() .
[direct_array_access; inline] [direct_array_access; inline]
pub fn (ss &TextScanner) peek_n(n int) int { pub fn (ss &TextScanner) peek_n(n int) int {
if ss.pos + n < ss.ilen { if ss.pos + n < ss.ilen {
@ -103,7 +106,49 @@ pub fn (mut ss TextScanner) back_n(n int) {
} }
} }
// reset resets the internal state of the scanner. // peek_back returns the *previous* character code from the input text.
// peek_back returns `-1` if it can't peek the previous character.
// unlike `back()`, `peek_back()` does not change the state of the scanner.
[direct_array_access; inline]
pub fn (ss &TextScanner) peek_back() int {
return ss.peek_back_n(1)
}
// peek_back_n returns the character code from the input text at position - `n`.
// peek_back_n returns `-1` if it can't peek `n` characters back.
// ts.peek_back_n(0) == ts.current()
// ts.peek_back_n(1) == ts.peek_back()
[direct_array_access; inline]
pub fn (ss &TextScanner) peek_back_n(n int) int {
offset := n + 1
if ss.pos >= offset {
return ss.input[ss.pos - offset]
}
return -1
}
// current returns the current character code from the input text.
// current returns `-1` at the start of the input text.
// NB: after `c := ts.next()`, `ts.current()` will also return `c`.
[direct_array_access; inline]
pub fn (mut ss TextScanner) current() int {
if ss.pos > 0 {
return ss.input[ss.pos - 1]
}
return -1
}
// reset resets the internal state of the scanner
// After calling .reset(), .next() will start reading
// again from the start of the input text.
pub fn (mut ss TextScanner) reset() { pub fn (mut ss TextScanner) reset() {
ss.pos = 0 ss.pos = 0
} }
// goto_end has the same effect as `for ts.next() != -1 {}`
// i.e. after calling .goto_end(), the scanner will be at
// the end of the input text. Further .next() calls will
// return -1, unless you go back.
pub fn (mut ss TextScanner) goto_end() {
ss.pos = ss.ilen
}

View File

@ -89,6 +89,36 @@ fn test_back_n() {
assert s.next() == `b` assert s.next() == `b`
} }
fn test_peek_back() {
mut s := textscanner.new('abc')
assert s.next() == `a`
assert s.next() == `b`
// check that calling .peek_back() multiple times
// does not change the state:
assert s.peek_back() == `a`
assert s.peek_back() == `a`
assert s.peek_back() == `a`
// advance, then peek_back again:
assert s.next() == `c`
assert s.peek_back() == `b`
// peeking before the start:
s.reset()
assert s.peek_back() == -1
// peeking right at the end:
s.goto_end()
assert s.peek_back() == `b`
}
fn test_peek_back_n() {
mut s := textscanner.new('abc')
s.goto_end()
assert s.peek_back_n(0) == `c`
assert s.peek_back_n(1) == `b`
assert s.peek_back_n(2) == `a`
assert s.peek_back_n(3) == -1
assert s.peek_back_n(4) == -1
}
fn test_reset() { fn test_reset() {
mut s := textscanner.new('abc') mut s := textscanner.new('abc')
assert s.next() == `a` assert s.next() == `a`
@ -98,3 +128,32 @@ fn test_reset() {
s.reset() s.reset()
assert s.next() == `a` assert s.next() == `a`
} }
fn test_current() {
mut s := textscanner.new('abc')
assert s.current() == -1
assert s.next() == `a`
assert s.current() == `a`
assert s.current() == `a`
assert s.peek_back() == -1
assert s.next() == `b`
assert s.current() == `b`
assert s.current() == `b`
assert s.peek_back() == `a`
assert s.next() == `c`
assert s.current() == `c`
assert s.next() == -1
assert s.current() == `c`
assert s.next() == -1
assert s.current() == `c`
s.reset()
assert s.current() == -1
assert s.next() == `a`
assert s.current() == `a`
}
fn test_goto_end() {
mut s := textscanner.new('abc')
s.goto_end()
assert s.current() == `c`
}