Skip to content

Commit 7f06134

Browse files
committed
scanner
1 parent 702bf8a commit 7f06134

File tree

2 files changed

+213
-0
lines changed

2 files changed

+213
-0
lines changed

libs/scanner/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "scanner"
3+
version = "0.1.0"
4+
authors = ["ia7ck <[email protected]>"]
5+
edition = "2021"
6+
license = "CC0-1.0"
7+
8+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9+
10+
[dependencies]

libs/scanner/src/lib.rs

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
use std::{
2+
fmt,
3+
io::{self, BufReader, Cursor},
4+
str,
5+
};
6+
7+
pub struct Scanner<R>
8+
where
9+
R: io::BufRead,
10+
{
11+
reader: R,
12+
buf: String,
13+
pos: usize,
14+
}
15+
16+
impl Scanner<BufReader<io::StdinLock<'static>>> {
17+
/// Creates a scanner that reads from standard input.
18+
pub fn stdin_lock() -> Self {
19+
Self {
20+
reader: BufReader::new(io::stdin().lock()),
21+
buf: String::new(),
22+
pos: 0,
23+
}
24+
}
25+
}
26+
27+
impl<T> Scanner<Cursor<T>>
28+
where
29+
T: AsRef<[u8]>,
30+
{
31+
/// Creates a scanner that reads from a string or byte slice.
32+
pub fn cursor(inner: T) -> Self {
33+
Self {
34+
reader: Cursor::new(inner),
35+
buf: String::new(),
36+
pos: 0,
37+
}
38+
}
39+
}
40+
41+
impl<R> Scanner<R>
42+
where
43+
R: io::BufRead,
44+
{
45+
/// Scans and parses the next token from the input.
46+
///
47+
/// For more convenient input scanning with variable declarations, see [`scan!`].
48+
///
49+
/// # Examples
50+
///
51+
/// ```
52+
/// use scanner::Scanner;
53+
///
54+
/// let mut scanner = Scanner::cursor("-10 20");
55+
///
56+
/// let x = scanner.scan::<i32>();
57+
/// let y = scanner.scan::<i32>();
58+
///
59+
/// assert_eq!(x, -10);
60+
/// assert_eq!(y, 20);
61+
/// ```
62+
pub fn scan<T>(&mut self) -> T
63+
where
64+
T: str::FromStr,
65+
T::Err: fmt::Debug,
66+
{
67+
// skip whitespace
68+
loop {
69+
match self.buf[self.pos..].find(|ch| !char::is_ascii_whitespace(&ch)) {
70+
Some(j) => {
71+
self.pos += j;
72+
break;
73+
}
74+
None => {
75+
let num_bytes = self
76+
.reader
77+
.read_line(&mut self.buf)
78+
.unwrap_or_else(|_| panic!("invalid UTF-8"));
79+
assert!(num_bytes > 0, "reached EOF :(");
80+
}
81+
}
82+
}
83+
84+
let rest = &self.buf[self.pos..];
85+
let token_len = rest
86+
.find(|ch| char::is_ascii_whitespace(&ch))
87+
.unwrap_or(rest.len());
88+
let value = rest[..token_len]
89+
.parse()
90+
.unwrap_or_else(|e| panic!("{:?}, attempt to read `{}`", e, rest));
91+
self.pos += token_len;
92+
93+
value
94+
}
95+
}
96+
97+
/// Macro for convenient input scanning with variable declarations.
98+
///
99+
/// For direct token scanning, see [`Scanner::scan()`].
100+
///
101+
/// # Examples
102+
///
103+
/// ```
104+
/// use scanner::{Scanner, scan};
105+
///
106+
/// let mut scanner = Scanner::cursor("3 10\n1 2 3");
107+
///
108+
/// scan! {
109+
/// via scanner,
110+
/// (n, k): (usize, usize),
111+
/// a: [i32; n],
112+
/// };
113+
///
114+
/// assert_eq!((n, k), (3, 10));
115+
/// assert_eq!(a, vec![1, 2, 3]);
116+
/// ```
117+
#[macro_export]
118+
macro_rules! scan {
119+
(via $scanner:expr, $($rest:tt)*) => {
120+
$crate::scan!(@via [$scanner] @rest $($rest)*);
121+
};
122+
123+
(@via [$via:expr] @rest) => {};
124+
(@via [$via:expr] @rest ,) => {};
125+
126+
(@via [$via:expr] @rest mut $($rest:tt)*) => {
127+
$crate::scan!(@via [$via] @mut [mut] @rest $($rest)*);
128+
};
129+
(@via [$via:expr] @rest $($rest:tt)*) => {
130+
$crate::scan!(@via [$via] @mut [] @rest $($rest)*);
131+
};
132+
133+
(@via [$via:expr] @mut [$($mut:tt)?] @rest $var:tt : $t:tt) => {
134+
let $($mut)? $var = $crate::scan_inner!(via $via, $t);
135+
};
136+
(@via [$via:expr] @mut [$($mut:tt)?] @rest $var:tt : $t:tt , $($rest:tt)*) => {
137+
$crate::scan!(@via [$via] @mut [$($mut)?] @rest $var : $t);
138+
$crate::scan!(@via [$via] @rest $($rest)*);
139+
};
140+
}
141+
142+
#[doc(hidden)]
143+
#[macro_export]
144+
macro_rules! scan_inner {
145+
// (i32, i32)
146+
(via $scanner:expr, ( $($t:tt),* )) => {
147+
( $($crate::scan_inner!(via $scanner, $t)),* )
148+
};
149+
150+
// [i32; n]
151+
(via $scanner:expr, [ $t:tt ; $len:expr ]) => {
152+
::std::iter::repeat_with(|| $crate::scan_inner!(via $scanner, $t)).take($len).collect::<Vec<_>>()
153+
};
154+
155+
// i32
156+
(via $scanner:expr, $ty:ty) => {
157+
$scanner.scan::<$ty>()
158+
};
159+
}
160+
161+
#[cfg(test)]
162+
mod tests {
163+
use crate::Scanner;
164+
165+
#[test]
166+
fn scan_test() {
167+
let mut scanner = Scanner::cursor("42 123\n456\r\nABC");
168+
assert_eq!(scanner.scan::<i32>(), 42);
169+
assert_eq!(scanner.scan::<i32>(), 123);
170+
assert_eq!(scanner.scan::<i32>(), 456);
171+
assert_eq!(scanner.scan::<String>(), String::from("ABC"));
172+
}
173+
174+
#[test]
175+
fn scan_macro_test() {
176+
let mut scanner = Scanner::cursor(
177+
r#"
178+
3 10
179+
4
180+
1 2 3
181+
a 1
182+
b 2
183+
c 3
184+
d 4
185+
"#,
186+
);
187+
scan! {
188+
via scanner,
189+
(n, k): (usize, usize),
190+
mut q: usize,
191+
a: [i32; n],
192+
queries: [(char, i32); q],
193+
};
194+
195+
assert_eq!((n, k), (3, 10));
196+
assert_eq!(q, 4);
197+
// test mutable
198+
q += 1;
199+
assert_eq!(q, 5);
200+
assert_eq!(a, vec![1, 2, 3]);
201+
assert_eq!(queries, vec![('a', 1), ('b', 2), ('c', 3), ('d', 4)]);
202+
}
203+
}

0 commit comments

Comments
 (0)