aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/vlib/strings
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/old/vlib/strings
downloadcli-tools-windows-master.tar.gz
cli-tools-windows-master.tar.bz2
cli-tools-windows-master.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/old/vlib/strings')
-rw-r--r--v_windows/v/old/vlib/strings/builder.js.v51
-rw-r--r--v_windows/v/old/vlib/strings/builder.v140
-rw-r--r--v_windows/v/old/vlib/strings/builder_test.v96
-rw-r--r--v_windows/v/old/vlib/strings/similarity.v69
-rw-r--r--v_windows/v/old/vlib/strings/similarity_test.v13
-rw-r--r--v_windows/v/old/vlib/strings/strings.c.v38
-rw-r--r--v_windows/v/old/vlib/strings/strings.js.v17
-rw-r--r--v_windows/v/old/vlib/strings/strings.v13
-rw-r--r--v_windows/v/old/vlib/strings/strings_test.v14
-rw-r--r--v_windows/v/old/vlib/strings/textscanner/textscanner.v154
-rw-r--r--v_windows/v/old/vlib/strings/textscanner/textscanner_test.v159
11 files changed, 764 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/strings/builder.js.v b/v_windows/v/old/vlib/strings/builder.js.v
new file mode 100644
index 0000000..e445804
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/builder.js.v
@@ -0,0 +1,51 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module strings
+
+pub struct Builder {
+mut:
+ buf []byte
+pub mut:
+ len int
+ initial_size int = 1
+}
+
+pub fn new_builder(initial_size int) Builder {
+ return Builder{
+ buf: make(0, initial_size, sizeof(byte))
+ initial_size: initial_size
+ }
+}
+
+pub fn (mut b Builder) write_b(data byte) {
+ b.buf << data
+ b.len++
+}
+
+pub fn (mut b Builder) write_string(s string) {
+ b.buf.push_many(s.str, s.len)
+ // b.buf << []byte(s) // TODO
+ b.len += s.len
+}
+
+pub fn (mut b Builder) writeln(s string) {
+ b.buf.push_many(s.str, s.len)
+ // b.buf << []byte(s) // TODO
+ b.buf << `\n`
+ b.len += s.len + 1
+}
+
+pub fn (b Builder) str() string {
+ x := &byte(b.buf.data)
+ return unsafe { x.vstring_with_len(b.len) }
+}
+
+pub fn (mut b Builder) cut(n int) {
+ b.len -= n
+}
+
+pub fn (mut b Builder) free() {
+ b.buf = make(0, b.initial_size, 1)
+ b.len = 0
+}
diff --git a/v_windows/v/old/vlib/strings/builder.v b/v_windows/v/old/vlib/strings/builder.v
new file mode 100644
index 0000000..cec4f6c
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/builder.v
@@ -0,0 +1,140 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module strings
+
+// strings.Builder is used to efficiently append many strings to a large
+// dynamically growing buffer, then use the resulting large string. Using
+// a string builder is much better for performance/memory usage than doing
+// constantly string concatenation.
+pub type Builder = []byte
+
+// new_builder returns a new string builder, with an initial capacity of `initial_size`
+pub fn new_builder(initial_size int) Builder {
+ return Builder([]byte{cap: initial_size})
+}
+
+// write_ptr writes `len` bytes provided byteptr to the accumulated buffer
+[unsafe]
+pub fn (mut b Builder) write_ptr(ptr &byte, len int) {
+ if len == 0 {
+ return
+ }
+ unsafe { b.push_many(ptr, len) }
+}
+
+// write_b appends a single `data` byte to the accumulated buffer
+pub fn (mut b Builder) write_b(data byte) {
+ b << data
+}
+
+// write implements the Writer interface
+pub fn (mut b Builder) write(data []byte) ?int {
+ if data.len == 0 {
+ return 0
+ }
+ b << data
+ return data.len
+}
+
+[inline]
+pub fn (b &Builder) byte_at(n int) byte {
+ return unsafe { (&[]byte(b))[n] }
+}
+
+// write appends the string `s` to the buffer
+[inline]
+pub fn (mut b Builder) write_string(s string) {
+ if s.len == 0 {
+ return
+ }
+ unsafe { b.push_many(s.str, s.len) }
+ // for c in s {
+ // b.buf << c
+ // }
+ // b.buf << []byte(s) // TODO
+}
+
+// go_back discards the last `n` bytes from the buffer
+pub fn (mut b Builder) go_back(n int) {
+ b.trim(b.len - n)
+}
+
+// cut_last cuts the last `n` bytes from the buffer and returns them
+pub fn (mut b Builder) cut_last(n int) string {
+ cut_pos := b.len - n
+ x := unsafe { (&[]byte(b))[cut_pos..] }
+ res := x.bytestr()
+ b.trim(cut_pos)
+ return res
+}
+
+// cut_to cuts the string after `pos` and returns it.
+// if `pos` is superior to builder length, returns an empty string
+// and cancel further operations
+pub fn (mut b Builder) cut_to(pos int) string {
+ if pos > b.len {
+ return ''
+ }
+ return b.cut_last(b.len - pos)
+}
+
+// go_back_to resets the buffer to the given position `pos`
+// NB: pos should be < than the existing buffer length.
+pub fn (mut b Builder) go_back_to(pos int) {
+ b.trim(pos)
+}
+
+// writeln appends the string `s`, and then a newline character.
+[inline]
+pub fn (mut b Builder) writeln(s string) {
+ // for c in s {
+ // b.buf << c
+ // }
+ if s.len > 0 {
+ unsafe { b.push_many(s.str, s.len) }
+ }
+ // b.buf << []byte(s) // TODO
+ b << byte(`\n`)
+}
+
+// last_n(5) returns 'world'
+// buf == 'hello world'
+pub fn (b &Builder) last_n(n int) string {
+ if n > b.len {
+ return ''
+ }
+ x := unsafe { (&[]byte(b))[b.len - n..] }
+ return x.bytestr()
+}
+
+// after(6) returns 'world'
+// buf == 'hello world'
+pub fn (b &Builder) after(n int) string {
+ if n >= b.len {
+ return ''
+ }
+ x := unsafe { (&[]byte(b))[n..] }
+ return x.bytestr()
+}
+
+// str returns a copy of all of the accumulated buffer content.
+// NB: after a call to b.str(), the builder b should not be
+// used again, you need to call b.free() first, or just leave
+// it to be freed by -autofree when it goes out of scope.
+// The returned string *owns* its own separate copy of the
+// accumulated data that was in the string builder, before the
+// .str() call.
+pub fn (mut b Builder) str() string {
+ b << byte(0)
+ bcopy := unsafe { &byte(memdup(b.data, b.len)) }
+ s := unsafe { bcopy.vstring_with_len(b.len - 1) }
+ b.trim(0)
+ return s
+}
+
+// free is for manually freeing the contents of the buffer
+[unsafe]
+pub fn (mut b Builder) free() {
+ unsafe { free(b.data) }
+}
diff --git a/v_windows/v/old/vlib/strings/builder_test.v b/v_windows/v/old/vlib/strings/builder_test.v
new file mode 100644
index 0000000..a714722
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/builder_test.v
@@ -0,0 +1,96 @@
+import strings
+
+type MyInt = int
+
+fn test_sb() {
+ mut sb := strings.new_builder(100)
+ sb.write_string('hi')
+ sb.write_string('!')
+ sb.write_string('hello')
+ assert sb.len == 8
+ sb_end := sb.str()
+ assert sb_end == 'hi!hello'
+ assert sb.len == 0
+ ///
+ sb = strings.new_builder(10)
+ sb.write_string('a')
+ sb.write_string('b')
+ assert sb.len == 2
+ assert sb.str() == 'ab'
+ // Test interpolation optimization
+ sb = strings.new_builder(10)
+ x := 10
+ y := MyInt(20)
+ sb.writeln('x = $x y = $y')
+ res := sb.str()
+ assert res[res.len - 1] == `\n`
+ println('"$res"')
+ assert res.trim_space() == 'x = 10 y = 20'
+ //
+ sb = strings.new_builder(10)
+ sb.write_string('x = $x y = $y')
+ assert sb.str() == 'x = 10 y = 20'
+ //$if !windows {
+ sb = strings.new_builder(10)
+ sb.write_string('123456')
+ last_2 := sb.cut_last(2)
+ assert last_2 == '56'
+ final_sb := sb.str()
+ assert final_sb == '1234'
+ //}
+}
+
+const (
+ maxn = 100000
+)
+
+fn test_big_sb() {
+ mut sb := strings.new_builder(100)
+ mut sb2 := strings.new_builder(10000)
+ for i in 0 .. maxn {
+ sb.writeln(i.str())
+ sb2.write_string('+')
+ }
+ s := sb.str()
+ lines := s.split_into_lines()
+ assert lines.len == maxn
+ assert lines[0] == '0'
+ assert lines[1] == '1'
+ assert lines[777] == '777'
+ assert lines[98765] == '98765'
+ println(sb2.len)
+ assert sb2.len == maxn
+}
+
+fn test_byte_write() {
+ mut sb := strings.new_builder(100)
+ temp_str := 'byte testing'
+ mut count := 0
+ for word in temp_str {
+ sb.write_b(word)
+ count++
+ assert count == sb.len
+ }
+ sb_final := sb.str()
+ assert sb_final == temp_str
+}
+
+fn test_strings_builder_reuse() {
+ mut sb := strings.new_builder(256)
+ sb.write_string('world')
+ assert sb.str() == 'world'
+ sb.write_string('hello')
+ assert sb.str() == 'hello'
+}
+
+fn test_cut_to() {
+ mut sb := strings.new_builder(16)
+ sb.write_string('hello')
+ assert sb.cut_to(3) == 'lo'
+ assert sb.len == 3
+ assert sb.cut_to(3) == ''
+ assert sb.len == 3
+ assert sb.cut_to(0) == 'hel'
+ assert sb.cut_to(32) == ''
+ assert sb.len == 0
+}
diff --git a/v_windows/v/old/vlib/strings/similarity.v b/v_windows/v/old/vlib/strings/similarity.v
new file mode 100644
index 0000000..8d8de95
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/similarity.v
@@ -0,0 +1,69 @@
+module strings
+
+// #-js
+// use levenshtein distance algorithm to calculate
+// the distance between between two strings (lower is closer)
+pub fn levenshtein_distance(a string, b string) int {
+ mut f := [0].repeat(b.len + 1)
+ for j in 0 .. f.len {
+ f[j] = j
+ }
+ for ca in a {
+ mut j := 1
+ mut fj1 := f[0]
+ f[0]++
+ for cb in b {
+ mut mn := if f[j] + 1 <= f[j - 1] + 1 { f[j] + 1 } else { f[j - 1] + 1 }
+ if cb != ca {
+ mn = if mn <= fj1 + 1 { mn } else { fj1 + 1 }
+ } else {
+ mn = if mn <= fj1 { mn } else { fj1 }
+ }
+ fj1 = f[j]
+ f[j] = mn
+ j++
+ }
+ }
+ return f[f.len - 1]
+}
+
+// use levenshtein distance algorithm to calculate
+// how similar two strings are as a percentage (higher is closer)
+pub fn levenshtein_distance_percentage(a string, b string) f32 {
+ d := levenshtein_distance(a, b)
+ l := if a.len >= b.len { a.len } else { b.len }
+ return (1.00 - f32(d) / f32(l)) * 100.00
+}
+
+// implementation of Sørensen–Dice coefficient.
+// find the similarity between two strings.
+// returns coefficient between 0.0 (not similar) and 1.0 (exact match).
+pub fn dice_coefficient(s1 string, s2 string) f32 {
+ if s1.len == 0 || s2.len == 0 {
+ return 0.0
+ }
+ if s1 == s2 {
+ return 1.0
+ }
+ if s1.len < 2 || s2.len < 2 {
+ return 0.0
+ }
+ a := if s1.len > s2.len { s1 } else { s2 }
+ b := if a == s1 { s2 } else { s1 }
+ mut first_bigrams := map[string]int{}
+ for i in 0 .. a.len - 1 {
+ bigram := a[i..i + 2]
+ q := if bigram in first_bigrams { first_bigrams[bigram] + 1 } else { 1 }
+ first_bigrams[bigram] = q
+ }
+ mut intersection_size := 0
+ for i in 0 .. b.len - 1 {
+ bigram := b[i..i + 2]
+ count := if bigram in first_bigrams { first_bigrams[bigram] } else { 0 }
+ if count > 0 {
+ first_bigrams[bigram] = count - 1
+ intersection_size++
+ }
+ }
+ return (2.0 * f32(intersection_size)) / (f32(a.len) + f32(b.len) - 2)
+}
diff --git a/v_windows/v/old/vlib/strings/similarity_test.v b/v_windows/v/old/vlib/strings/similarity_test.v
new file mode 100644
index 0000000..965da45
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/similarity_test.v
@@ -0,0 +1,13 @@
+import strings
+
+fn test_levenshtein_distance() {
+ assert strings.levenshtein_distance('', '') == 0
+ assert strings.levenshtein_distance('one', 'one') == 0
+ assert strings.levenshtein_distance('', 'two') == 3
+ assert strings.levenshtein_distance('three', '') == 5
+ assert strings.levenshtein_distance('bananna', '') == 7
+ assert strings.levenshtein_distance('cats', 'hats') == 1
+ assert strings.levenshtein_distance('hugs', 'shrugs') == 2
+ assert strings.levenshtein_distance('broom', 'shroom') == 2
+ assert strings.levenshtein_distance('flomax', 'volmax') == 3
+}
diff --git a/v_windows/v/old/vlib/strings/strings.c.v b/v_windows/v/old/vlib/strings/strings.c.v
new file mode 100644
index 0000000..c020f5b
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/strings.c.v
@@ -0,0 +1,38 @@
+module strings
+
+// strings.repeat - fill a string with `n` repetitions of the character `c`
+pub fn repeat(c byte, n int) string {
+ if n <= 0 {
+ return ''
+ }
+ mut bytes := unsafe { malloc_noscan(n + 1) }
+ unsafe {
+ C.memset(bytes, c, n)
+ bytes[n] = `0`
+ }
+ return unsafe { bytes.vstring_with_len(n) }
+}
+
+// strings.repeat_string - gives you `n` repetitions of the substring `s`
+// NB: strings.repeat, that repeats a single byte, is between 2x
+// and 24x faster than strings.repeat_string called for a 1 char string.
+pub fn repeat_string(s string, n int) string {
+ if n <= 0 || s.len == 0 {
+ return ''
+ }
+ slen := s.len
+ blen := slen * n
+ mut bytes := unsafe { malloc_noscan(blen + 1) }
+ for bi in 0 .. n {
+ bislen := bi * slen
+ for si in 0 .. slen {
+ unsafe {
+ bytes[bislen + si] = s[si]
+ }
+ }
+ }
+ unsafe {
+ bytes[blen] = `0`
+ }
+ return unsafe { bytes.vstring_with_len(blen) }
+}
diff --git a/v_windows/v/old/vlib/strings/strings.js.v b/v_windows/v/old/vlib/strings/strings.js.v
new file mode 100644
index 0000000..2681b94
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/strings.js.v
@@ -0,0 +1,17 @@
+module strings
+
+pub fn repeat(c byte, n int) string {
+ if n <= 0 {
+ return ''
+ }
+ arr := [c].repeat(n)
+ return arr.bytestr()
+}
+
+pub fn repeat_string(s string, n int) string {
+ /*
+ // TODO: uncomment this. It is commented for now, so that `v doc strings` works
+ res := # s.repeat(n)
+ return res
+ */
+}
diff --git a/v_windows/v/old/vlib/strings/strings.v b/v_windows/v/old/vlib/strings/strings.v
new file mode 100644
index 0000000..c01dd90
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/strings.v
@@ -0,0 +1,13 @@
+module strings
+
+// import rand
+// random returns a random string with `n` characters
+/*
+pub fn random(n int) string {
+ buf := vmalloc(n)
+ for i in 0..n {
+ buf[i] = rand.next()
+ }
+ return tos(buf)
+}
+*/
diff --git a/v_windows/v/old/vlib/strings/strings_test.v b/v_windows/v/old/vlib/strings/strings_test.v
new file mode 100644
index 0000000..ff5ddf5
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/strings_test.v
@@ -0,0 +1,14 @@
+import strings
+
+fn test_repeat() {
+ assert strings.repeat(`x`, 10) == 'xxxxxxxxxx'
+ assert strings.repeat(`a`, 1) == 'a'
+ assert strings.repeat(`a`, 0) == ''
+}
+
+fn test_repeat_string() {
+ assert strings.repeat_string('abc', 3) == 'abcabcabc'
+ assert strings.repeat_string('abc', 1) == 'abc'
+ assert strings.repeat_string('abc', 0) == ''
+ assert strings.repeat_string('', 200) == ''
+}
diff --git a/v_windows/v/old/vlib/strings/textscanner/textscanner.v b/v_windows/v/old/vlib/strings/textscanner/textscanner.v
new file mode 100644
index 0000000..5525137
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/textscanner/textscanner.v
@@ -0,0 +1,154 @@
+module textscanner
+
+// TextScanner simplifies writing small scanners/parsers
+// by providing safe methods to scan texts character by
+// character, peek for the next characters, go back, etc.
+pub struct TextScanner {
+pub:
+ input string
+ ilen int
+mut:
+ pos int // current position; pos is *always* kept in [0,ilen]
+}
+
+// new returns a stack allocated instance of TextScanner.
+pub fn new(input string) TextScanner {
+ return TextScanner{
+ input: input
+ ilen: input.len
+ }
+}
+
+// free frees all allocated resources.
+[unsafe]
+pub fn (mut ss TextScanner) free() {
+ unsafe {
+ ss.input.free()
+ }
+}
+
+// remaining returns how many characters remain from current position.
+[inline]
+pub fn (ss &TextScanner) remaining() int {
+ return ss.ilen - ss.pos
+}
+
+// next returns the next character code from the input text.
+// next returns `-1` if it can't reach the next character.
+// next advances the scanner position.
+[direct_array_access; inline]
+pub fn (mut ss TextScanner) next() int {
+ if ss.pos < ss.ilen {
+ opos := ss.pos
+ ss.pos++
+ return ss.input[opos]
+ }
+ return -1
+}
+
+// skip skips one character ahead; `skip()` is slightly faster than `.next()`.
+// `skip()` does not return a result.
+[inline]
+pub fn (mut ss TextScanner) skip() {
+ if ss.pos + 1 < ss.ilen {
+ ss.pos++
+ }
+}
+
+// skip_n skips ahead `n` characters, stopping at the end of the input.
+[inline]
+pub fn (mut ss TextScanner) skip_n(n int) {
+ ss.pos += n
+ if ss.pos > ss.ilen {
+ ss.pos = ss.ilen
+ }
+}
+
+// peek returns the *next* character code from the input text.
+// peek returns `-1` if it can't peek the next character.
+// unlike `next()`, `peek()` does not change the state of the scanner.
+[direct_array_access; inline]
+pub fn (ss &TextScanner) peek() int {
+ if ss.pos < ss.ilen {
+ return ss.input[ss.pos]
+ }
+ return -1
+}
+
+// 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.
+// ts.peek_n(0) == ts.current() .
+// ts.peek_n(1) == ts.peek() .
+[direct_array_access; inline]
+pub fn (ss &TextScanner) peek_n(n int) int {
+ if ss.pos + n < ss.ilen {
+ return ss.input[ss.pos + n]
+ }
+ return -1
+}
+
+// back goes back one character from the current scanner position.
+[inline]
+pub fn (mut ss TextScanner) back() {
+ if ss.pos > 0 {
+ ss.pos--
+ }
+}
+
+// back_n goes back `n` characters from the current scanner position.
+pub fn (mut ss TextScanner) back_n(n int) {
+ ss.pos -= n
+ if ss.pos < 0 {
+ ss.pos = 0
+ }
+ if ss.pos > ss.ilen {
+ ss.pos = ss.ilen
+ }
+}
+
+// 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() {
+ 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
+}
diff --git a/v_windows/v/old/vlib/strings/textscanner/textscanner_test.v b/v_windows/v/old/vlib/strings/textscanner/textscanner_test.v
new file mode 100644
index 0000000..e9d2487
--- /dev/null
+++ b/v_windows/v/old/vlib/strings/textscanner/textscanner_test.v
@@ -0,0 +1,159 @@
+import strings.textscanner
+
+fn test_remaining() {
+ mut s := textscanner.new('abc')
+ assert s.remaining() == 3
+ s.next()
+ s.next()
+ assert s.remaining() == 1
+ s.next()
+ assert s.remaining() == 0
+ s.next()
+ s.next()
+ assert s.remaining() == 0
+ s.reset()
+ assert s.remaining() == 3
+}
+
+fn test_next() {
+ mut s := textscanner.new('abc')
+ assert s.next() == `a`
+ assert s.next() == `b`
+ assert s.next() == `c`
+ assert s.next() == -1
+ assert s.next() == -1
+ assert s.next() == -1
+}
+
+fn test_skip() {
+ mut s := textscanner.new('abc')
+ assert s.next() == `a`
+ s.skip()
+ assert s.next() == `c`
+ assert s.next() == -1
+}
+
+fn test_skip_n() {
+ mut s := textscanner.new('abc')
+ s.skip_n(2)
+ assert s.next() == `c`
+ assert s.next() == -1
+}
+
+fn test_peek() {
+ mut s := textscanner.new('abc')
+ assert s.peek() == `a`
+ assert s.peek() == `a`
+ assert s.peek() == `a`
+ //
+ assert s.next() == `a`
+ assert s.next() == `b`
+ assert s.next() == `c`
+ assert s.next() == -1
+}
+
+fn test_peek_n() {
+ mut s := textscanner.new('abc')
+ assert s.peek_n(0) == `a`
+ assert s.peek_n(1) == `b`
+ assert s.peek_n(2) == `c`
+ assert s.peek_n(3) == -1
+ assert s.peek_n(4) == -1
+ //
+ assert s.next() == `a`
+ assert s.next() == `b`
+ assert s.next() == `c`
+ assert s.next() == -1
+}
+
+fn test_back() {
+ mut s := textscanner.new('abc')
+ assert s.next() == `a`
+ s.back()
+ assert s.next() == `a`
+ assert s.next() == `b`
+ s.back()
+ assert s.next() == `b`
+ assert s.next() == `c`
+ assert s.next() == -1
+}
+
+fn test_back_n() {
+ mut s := textscanner.new('abc')
+ assert s.next() == `a`
+ s.back_n(10)
+ assert s.next() == `a`
+ assert s.next() == `b`
+ assert s.next() == `c`
+ s.back_n(2)
+ 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() {
+ mut s := textscanner.new('abc')
+ assert s.next() == `a`
+ s.next()
+ s.next()
+ assert s.next() == -1
+ s.reset()
+ 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`
+}