diff options
Diffstat (limited to 'v_windows/v/old/vlib/strings/textscanner')
-rw-r--r-- | v_windows/v/old/vlib/strings/textscanner/textscanner.v | 154 | ||||
-rw-r--r-- | v_windows/v/old/vlib/strings/textscanner/textscanner_test.v | 159 |
2 files changed, 313 insertions, 0 deletions
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` +} |