Skip to content

Commit 31431a6

Browse files
committed
feat: add next_token, and make next_number generic
1 parent c4a33bd commit 31431a6

File tree

2 files changed

+110
-72
lines changed

2 files changed

+110
-72
lines changed

src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ use scanner::scanner::Scanner;
22

33
// Example
44
fn main() {
5-
let input = "8 + 9";
5+
let input = "-8 + 9";
66

77
let mut scanner = Scanner::new(&input);
88

9-
let x = scanner.next_number().expect("Expecting number");
9+
let x: isize = scanner.next_number().expect("Expecting number");
1010
let op = scanner.next_word().expect("Expecting word");
11-
let y = scanner.next_number().expect("Expecting number");
11+
let y: isize = scanner.next_number().expect("Expecting number");
1212

1313
let result = match op {
1414
"+" => x + y,

src/scanner.rs

Lines changed: 107 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::str::FromStr;
2+
13
/// A `Scanner` is a simple utility for parsing strings, allowing access to words,
24
/// numbers, and lines from an input string.
35
///
@@ -19,47 +21,103 @@ impl<'a> Scanner<'a> {
1921
/// # Examples
2022
///
2123
/// ```
24+
/// use scanner::scanner::Scanner;
2225
/// let scanner = Scanner::new("Hello, world!");
2326
/// ```
2427
pub fn new(input: &'a str) -> Self {
2528
Scanner { input, position: 0 }
2629
}
2730

28-
/// Scans for the next number in the input string.
31+
/// Scans for the next token in the input string based on a provided predicate.
2932
///
30-
/// Parses a contiguous sequence of digits, including an optional leading
31-
/// minus sign for negative numbers. Consumes the number from the input
32-
/// and updates the scanner's position.
33+
/// A token is defined as a contiguous sequence of characters that satisfy the
34+
/// condition defined by the `predicate` closure. This method will consume the
35+
/// characters matching the predicate from the input string and update the scanner's position.
36+
///
37+
/// # Arguments
38+
///
39+
/// * `predicate`: A closure that takes a character as input and returns a boolean,
40+
/// determining whether the character should be considered part of the token.
3341
///
3442
/// # Returns
3543
///
36-
/// * `Some(i32)` if a valid number is found.
37-
/// * `None` if no valid number is found.
44+
/// * `Option<&'a str>` if a valid token is found.
45+
/// * `None` if no valid token can be found.
3846
///
3947
/// # Examples
4048
///
4149
/// ```
42-
/// let mut scanner = Scanner::new("42 is the answer");
43-
/// assert_eq!(scanner.next_number(), Some(42));
50+
/// use scanner::scanner::Scanner;
51+
/// let mut scanner = Scanner::new("Hello, world!");
52+
/// // Scan for words (non-whitespace characters)
53+
/// assert_eq!(scanner.next_token(|c, _| !c.is_whitespace()), Some("Hello,"));
54+
///
55+
/// let mut scanner = Scanner::new("123 456 789");
56+
/// // Scan for numbers (digits)
57+
/// assert_eq!(scanner.next_token(|c, _| c.is_digit(10)), Some("123"));
4458
/// ```
45-
pub fn next_number(&mut self) -> Option<i32> {
59+
pub fn next_token<F>(&mut self, predicate: F) -> Option<&'a str>
60+
where
61+
F: Fn(char, usize) -> bool,
62+
{
4663
let remaining = self.get_remaining();
4764

48-
let (number_len, valid_chars_count): (usize, usize) = remaining
49-
.trim_start()
50-
.char_indices()
51-
.take_while(|&(_, c)| c.is_digit(10) || c == '-')
52-
.fold((0, 0), |(_, count), (i, _)| (i + 1, count + 1));
53-
let number_len = number_len + remaining.len() - remaining.trim_start().len();
65+
let mut token_len: usize = 0;
66+
let mut valid_chars_count: usize = 0;
67+
68+
for (i, c) in remaining.char_indices() {
69+
if predicate(c, i) {
70+
valid_chars_count += 1;
71+
token_len = i + 1;
72+
} else {
73+
if valid_chars_count > 0 {
74+
break;
75+
}
76+
token_len = i; // update token length to exclude this character
77+
}
78+
}
5479

5580
if valid_chars_count > 0 {
56-
self.position += number_len;
57-
Some(remaining[..number_len].trim_start().parse::<i32>().ok()?)
81+
self.position += token_len;
82+
Some(remaining[..token_len].trim_start())
5883
} else {
5984
None
6085
}
6186
}
6287

88+
/// Scans for the next number in the input string.
89+
///
90+
/// Parses a contiguous sequence of digits, including an optional leading
91+
/// minus sign for negative numbers. Consumes the number from the input
92+
/// and updates the scanner's position.
93+
///
94+
/// # Returns
95+
///
96+
/// * `Some(T)` if a valid number is found.
97+
/// * `None` if no valid number is found.
98+
///
99+
/// # Examples
100+
///
101+
/// ```
102+
/// use scanner::scanner::Scanner;
103+
/// let mut scanner = Scanner::new("42 is the answer");
104+
/// assert_eq!(scanner.next_number(), Some(42));
105+
/// ```
106+
pub fn next_number<T>(&mut self) -> Option<T>
107+
where
108+
T: FromStr,
109+
{
110+
let position = self.position;
111+
self.next_token(|c, i| c.is_digit(10) || (c == '-' && i == 0))
112+
.and_then(|token| match token.parse::<T>() {
113+
Ok(number) => Some(number),
114+
Err(_) => {
115+
self.position = position;
116+
None
117+
}
118+
})
119+
}
120+
63121
/// Scans for the next word in the input string.
64122
///
65123
/// A word is defined as a contiguous sequence of non-whitespace characters.
@@ -73,34 +131,12 @@ impl<'a> Scanner<'a> {
73131
/// # Examples
74132
///
75133
/// ```
134+
/// use scanner::scanner::Scanner;
76135
/// let mut scanner = Scanner::new("Hello, world!");
77136
/// assert_eq!(scanner.next_word(), Some("Hello,"));
78137
/// ```
79138
pub fn next_word(&mut self) -> Option<&'a str> {
80-
let remaining = self.get_remaining();
81-
82-
let mut word_len: usize = 0;
83-
let mut valid_chars_count: usize = 0;
84-
85-
for (i, c) in remaining.char_indices() {
86-
if !c.is_whitespace() {
87-
valid_chars_count += 1;
88-
word_len = i + 1;
89-
} else {
90-
word_len = i;
91-
92-
if valid_chars_count > 0 {
93-
break;
94-
}
95-
}
96-
}
97-
98-
if valid_chars_count > 0 {
99-
self.position += word_len;
100-
Some(remaining[..word_len].trim_start())
101-
} else {
102-
None
103-
}
139+
self.next_token(|c, _| !c.is_whitespace())
104140
}
105141

106142
/// Scans for the next line from the input string.
@@ -117,6 +153,7 @@ impl<'a> Scanner<'a> {
117153
/// # Examples
118154
///
119155
/// ```
156+
/// use scanner::scanner::Scanner;
120157
/// let mut scanner = Scanner::new("First line\nSecond line");
121158
/// assert_eq!(scanner.next_line(), Some("First line"));
122159
/// ```
@@ -146,9 +183,10 @@ impl<'a> Scanner<'a> {
146183
/// # Examples
147184
///
148185
/// ```
186+
/// use scanner::scanner::Scanner;
149187
/// let mut scanner = Scanner::new("Hello world!");
150188
/// scanner.next_word();
151-
/// assert_eq!(scanner.get_remaining(), "world!");
189+
/// assert_eq!(scanner.get_remaining(), " world!");
152190
/// ```
153191
pub fn get_remaining(&self) -> &'a str {
154192
&self.input[self.position..]
@@ -162,7 +200,7 @@ mod tests {
162200
#[test]
163201
fn test_empty() {
164202
let mut scanner = Scanner::new("");
165-
assert_eq!(scanner.next_number(), None);
203+
assert_eq!(scanner.next_number::<i32>(), None);
166204
}
167205

168206
#[test]
@@ -199,13 +237,13 @@ mod tests {
199237
assert_eq!(scanner.next_number(), Some(-30));
200238
assert_eq!(scanner.next_number(), Some(33));
201239
assert_eq!(scanner.next_number(), Some(85));
202-
assert_eq!(scanner.next_number(), None);
240+
assert_eq!(scanner.next_number::<i32>(), None);
203241
}
204242

205243
#[test]
206244
fn test_next_number_no_digits() {
207245
let mut scanner = Scanner::new("no numbers here");
208-
assert_eq!(scanner.next_number(), None);
246+
assert_eq!(scanner.next_number::<i32>(), None);
209247
assert_eq!(scanner.get_remaining(), "no numbers here");
210248
}
211249

@@ -215,56 +253,56 @@ mod tests {
215253
assert_eq!(scanner.next_number(), Some(55));
216254
assert_eq!(scanner.get_remaining(), " 88");
217255
assert_eq!(scanner.next_number(), Some(88));
218-
assert_eq!(scanner.next_number(), None);
256+
assert_eq!(scanner.next_number::<i32>(), None);
219257
}
220258

221259
#[test]
222260
fn test_get_remaining() {
223261
let mut scanner = Scanner::new("123 456");
224262
assert_eq!(scanner.get_remaining(), "123 456");
225-
scanner.next_number();
263+
scanner.next_number::<i32>();
226264
assert_eq!(scanner.get_remaining(), " 456");
227-
scanner.next_number();
265+
scanner.next_number::<i32>();
228266
assert_eq!(scanner.get_remaining(), "");
229267
}
230268

231269
#[test]
232270
fn test_next_number_non_digit_characters() {
233271
let mut scanner = Scanner::new("abc 123 xyz 456");
234272

235-
assert_eq!(scanner.next_number(), None);
273+
assert_eq!(scanner.next_number::<i32>(), None);
236274
assert_eq!(scanner.get_remaining(), "abc 123 xyz 456");
237275
assert_eq!(scanner.next_word(), Some("abc"));
238276
assert_eq!(scanner.get_remaining(), " 123 xyz 456");
239277
assert_eq!(scanner.next_word(), Some("123"));
240278
assert_eq!(scanner.get_remaining(), " xyz 456");
241-
assert_eq!(scanner.next_number(), None);
279+
assert_eq!(scanner.next_number::<i32>(), None);
242280
assert_eq!(scanner.get_remaining(), " xyz 456");
243281
assert_eq!(scanner.next_word(), Some("xyz"));
244282
assert_eq!(scanner.get_remaining(), " 456");
245283
assert_eq!(scanner.next_word(), Some("456"));
246284
assert_eq!(scanner.get_remaining(), "");
247285
}
248286

249-
//#[test]
250-
//fn test_negative_sign_between_numbers() {
251-
// let mut scanner = Scanner::new("5-3");
252-
//
253-
// assert_eq!(scanner.next_number(), Some(5));
254-
// assert_eq!(scanner.get_remaining(), "-3");
255-
// assert_eq!(scanner.next_number(), Some(-3));
256-
//
257-
// let mut scanner = Scanner::new("5 - 3");
258-
//
259-
// assert_eq!(scanner.next_number(), Some(5));
260-
// assert_eq!(scanner.get_remaining(), " - 3");
261-
// assert_eq!(scanner.next_number(), None);
262-
// assert_eq!(scanner.get_remaining(), " - 3");
263-
// assert_eq!(scanner.next_word(), Some("-"));
264-
// assert_eq!(scanner.get_remaining(), " 3");
265-
// assert_eq!(scanner.next_number(), Some(3));
266-
// assert_eq!(scanner.get_remaining(), "");
267-
//}
287+
#[test]
288+
fn test_negative_sign_between_numbers() {
289+
let mut scanner = Scanner::new("5-3");
290+
291+
assert_eq!(scanner.next_number(), Some(5));
292+
assert_eq!(scanner.get_remaining(), "-3");
293+
assert_eq!(scanner.next_number(), Some(-3));
294+
295+
let mut scanner = Scanner::new("5 - 3");
296+
297+
assert_eq!(scanner.next_number(), Some(5));
298+
assert_eq!(scanner.get_remaining(), " - 3");
299+
assert_eq!(scanner.next_number::<i32>(), None);
300+
assert_eq!(scanner.get_remaining(), " - 3");
301+
assert_eq!(scanner.next_word(), Some("-"));
302+
assert_eq!(scanner.get_remaining(), " 3");
303+
assert_eq!(scanner.next_number(), Some(3));
304+
assert_eq!(scanner.get_remaining(), "");
305+
}
268306

269307
#[test]
270308
fn test_next_line() {

0 commit comments

Comments
 (0)