From a21ee1abd4a88d76f2b9164e965fd4aac83b7b1a Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 20 Jun 2021 12:38:14 +0300 Subject: [PATCH] strings.textscanner: add .current/0, .peek_back/0, .peek_back_n/1 and .goto_end/1 methods --- vlib/strings/textscanner/textscanner.v | 47 +++++++++++++++- vlib/strings/textscanner/textscanner_test.v | 59 +++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/vlib/strings/textscanner/textscanner.v b/vlib/strings/textscanner/textscanner.v index 7f2dc010a0..55251373b4 100644 --- a/vlib/strings/textscanner/textscanner.v +++ b/vlib/strings/textscanner/textscanner.v @@ -35,6 +35,7 @@ pub fn (ss &TextScanner) remaining() int { // 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 { @@ -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 `-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 { @@ -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() { 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/vlib/strings/textscanner/textscanner_test.v b/vlib/strings/textscanner/textscanner_test.v index a672204f56..e9d248769a 100644 --- a/vlib/strings/textscanner/textscanner_test.v +++ b/vlib/strings/textscanner/textscanner_test.v @@ -89,6 +89,36 @@ fn test_back_n() { 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` @@ -98,3 +128,32 @@ fn test_reset() { 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` +}