Skip to content

Commit

Permalink
Merge pull request #50 from statiolake/feature/dynamic-readable
Browse files Browse the repository at this point in the history
Add run-time version of Readable
  • Loading branch information
statiolake committed Apr 29, 2024
2 parents 1ce1cc0 + c5784bd commit cedb4ec
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 7 deletions.
107 changes: 100 additions & 7 deletions proconio/src/lib.rs
Expand Up @@ -596,7 +596,18 @@ macro_rules! input {
}
};

// parse kind (type)
// parse `with` (the marker of RuntimeReadable type)
(@from [$source:expr] @mut [$($mut:tt)?] @var $var:tt @kind [] @rest with $($rest:tt)*) => {
$crate::input! {
@from [$source]
@mut [$($mut)*]
@var $var
@dyn_kind []
@rest $($rest)*
}
};

// parse kind (Readable type)
(@from [$source:expr] @mut [$($mut:tt)?] @var $var:tt @kind [$($kind:tt)*] @rest) => {
let $($mut)* $var = $crate::read_value!(@source [$source] @kind [$($kind)*]);
};
Expand All @@ -617,6 +628,30 @@ macro_rules! input {
$crate::input!(@from [$source] @mut [$($mut)*] @var $var @kind [$ty] @rest);
};

// parse runtime kind (RuntimeReadable type)
(@from [$source:expr] @mut [$($mut:tt)?] @var $var:tt @dyn_kind [$($dyn_kind:tt)*] @rest) => {
let $($mut)* $var = $crate::read_value!(@source [$source] @dyn_kind [$($dyn_kind)*]);
};
(@from [$source:expr] @mut [$($mut:tt)?] @var $var:tt @dyn_kind [$($dyn_kind:tt)*] @rest, $($rest:tt)*) => {
$crate::input!(@from [$source] @mut [$($mut)*] @var $var @dyn_kind [$($dyn_kind)*] @rest);
$crate::input!(@from [$source] @rest $($rest)*);
};
(@from [$source:expr] @mut [$($mut:tt)?] @var $var:tt @dyn_kind [$($dyn_kind:tt)*] @rest $dyn_readable:expr) => {
$crate::input!(@from [$source] @mut [$($mut)*] @var $var @dyn_kind [$($dyn_kind)* $dyn_readable] @rest);
};
(@from [$source:expr] @mut [$($mut:tt)?] @var $var:tt @dyn_kind [$($dyn_kind:tt)*] @rest $dyn_readable:expr, $($rest:tt)*) => {
$crate::input!(@from [$source] @mut [$($mut)*] @var $var @dyn_kind [$($dyn_kind)* $dyn_readable] @rest , $($rest)*);
};
(@from $($tt:tt)*) => {
compile_error!(concat!(
"Reached unreachable statement while parsing macro input. ",
"This is a bug in `proconio`. ",
"Please report this issue from ",
"<https://github.com/statiolake/proconio-rs/issues>."
));
};

// interface
(from $source:expr, $($rest:tt)*) => {
#[allow(unused_variables, unused_mut)]
let mut s = $source;
Expand Down Expand Up @@ -726,15 +761,25 @@ macro_rules! read_value {
$crate::read_value!(@tuple @source [$source] @kinds [$($kinds)*] @current [$($curr)* $tt] @rest $($rest)*)
};

// unreachable
(@source [$source:expr] @kind []) => {
compile_error!(concat!("Reached unreachable statement while parsing macro input. ", "This is a bug in `proconio`. ", "Please report this issue from ", "<https://github.com/statiolake/proconio-rs/issues>."));
};

// normal other
(@source [$source:expr] @kind [$kind:ty]) => {
<$kind as $crate::__Readable>::read($source)
}
};

// runtime readable
(@source [$source:expr] @dyn_kind [$dyn_kind:expr]) => {
$crate::source::RuntimeReadable::read($dyn_kind, $source)
};

// unreachable
(@source [$source:expr] @kind []) => {
compile_error!(concat!(
"Reached unreachable statement while parsing macro input. ",
"This is a bug in `proconio`. ",
"Please report this issue from ",
"<https://github.com/statiolake/proconio-rs/issues>."
));
};
}

/// Checks if some of tokens are left on stdin.
Expand Down Expand Up @@ -771,6 +816,8 @@ pub fn is_stdin_empty() -> bool {

#[cfg(test)]
mod tests {
use std::marker::PhantomData;

use crate::source::auto::AutoSource;

#[test]
Expand Down Expand Up @@ -1020,6 +1067,52 @@ mod tests {
assert_eq!(b, [6, 7, 8]);
}

#[test]
fn input_runtime_readable() {
// VecReadable<T> replicates the built-in Vec reader `input!(v: [T])` in user-land.
struct VecReadable<T> {
n: usize,
_marker: PhantomData<T>,
}

impl<T> VecReadable<T> {
pub fn new(n: usize) -> Self {
VecReadable {
n,
_marker: PhantomData,
}
}
}

impl<T: crate::source::Readable> crate::source::RuntimeReadable for VecReadable<T> {
type Output = Vec<T::Output>;

fn read<R: std::io::BufRead, S: crate::source::Source<R>>(
self,
source: &mut S,
) -> Self::Output {
let mut res = vec![];
for _ in 0..self.n {
input! {
from &mut *source,
v: T,
}
res.push(v);
}
res
}
}

let mut source = AutoSource::from("4 1 2 3 4");
input! {
from &mut source,
n: usize,
v: with VecReadable::<crate::marker::Usize1>::new(n),
}

assert_eq!(v, [0, 1, 2, 3]);
}

#[test]
#[should_panic]
fn input_err_different_type() {
Expand Down
43 changes: 43 additions & 0 deletions proconio/src/source/mod.rs
Expand Up @@ -138,3 +138,46 @@ where
}
}
}

/// A trait that specifies how to read a value of some type from `Source` in such a way that is only
/// known at runtime, such as reading a vector or graph whose size is specified at runtime.
///
/// ```
/// # use proconio::source::RuntimeReadable;
/// # use proconio::source::auto::AutoSource;
/// # use proconio::source::Source;
/// # use proconio::input;
/// # use proconio::marker::Usize1;
/// # use std::io::BufRead;
/// struct DirectedGraph(usize, usize);
/// impl RuntimeReadable for DirectedGraph {
/// type Output = Vec<Vec<usize>>;
///
/// fn read<R: BufRead, S: Source<R>>(self, source: &mut S) -> Self::Output {
/// let DirectedGraph(n, m) = self;
///
/// input! {
/// from source,
/// edges: [(Usize1, Usize1); m],
/// };
///
/// let mut g = vec![vec![]; n];
/// for e in edges {
/// g[e.0].push(e.1);
/// }
/// g
/// }
/// }
///
/// # let mut source = AutoSource::from("2 3\n1 1\n1 2\n2 2\n");
/// input! {
/// # from source,
/// n: usize,
/// m: usize,
/// g: with DirectedGraph(n, m),
/// }
/// ```
pub trait RuntimeReadable {
type Output;
fn read<R: BufRead, S: Source<R>>(self, source: &mut S) -> Self::Output;
}

0 comments on commit cedb4ec

Please sign in to comment.