diff options
Diffstat (limited to 'challenge-287/ppentchev/rust/src/bin/ch-2.rs')
| -rw-r--r-- | challenge-287/ppentchev/rust/src/bin/ch-2.rs | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/challenge-287/ppentchev/rust/src/bin/ch-2.rs b/challenge-287/ppentchev/rust/src/bin/ch-2.rs new file mode 100644 index 0000000000..ed383ae837 --- /dev/null +++ b/challenge-287/ppentchev/rust/src/bin/ch-2.rs @@ -0,0 +1,117 @@ +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +// SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net> +// SPDX-License-Identifier: BSD-2-Clause +//! PWC 287 task 2: Valid Number + +use std::env; +use std::io; +use std::process::{ExitCode, Termination}; + +use anyhow::{Context, Result}; +use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::one_of, + combinator::{all_consuming, opt}, + multi::{many0, many1}, + sequence::tuple, + IResult, +}; +use tracing::debug; + +/// The examples from the problem. +const TEST_NUMBERS: [&str; 7] = ["1", "a", ".", "1.2e4.2", "-1.", "+1E-8", ".44"]; + +/// The result of the main program or an invoked subcommand. +enum MainResult { + /// Everything seems fine. + Ok, +} + +impl Termination for MainResult { + fn report(self) -> ExitCode { + match self { + Self::Ok => ExitCode::SUCCESS, + } + } +} + +/// Recognize a plus or minus sign at the start of the mantissa or the exponent. +fn _p_sign(input: &str) -> IResult<&str, ()> { + let (r_input, _) = one_of("+-")(input)?; + Ok((r_input, ())) +} + +/// Recognize an ASCII decimal digit. +fn _p_digit(input: &str) -> IResult<&str, ()> { + let (r_input, _) = one_of("0123456789")(input)?; + Ok((r_input, ())) +} + +/// Recognize a decimal dot maybe followed by some digits. +fn _p_large_fractional_part(input: &str) -> IResult<&str, ()> { + let (r_input, _) = tuple((tag("."), many0(_p_digit)))(input)?; + Ok((r_input, ())) +} + +/// Recognize a number with some digits before the optional decimal dot, etc. +fn _p_large_number(input: &str) -> IResult<&str, ()> { + let (r_input, _) = tuple((many1(_p_digit), opt(_p_large_fractional_part)))(input)?; + Ok((r_input, ())) +} + +/// Recognize a number between 0 and 1: a decimal dot and some digits. +fn _p_small_number(input: &str) -> IResult<&str, ()> { + let (r_input, _) = tuple((tag("."), many1(_p_digit)))(input)?; + Ok((r_input, ())) +} + +/// Recognize a valid mantissa: sign, digits, decimal dot, etc. +fn _p_mantissa(input: &str) -> IResult<&str, ()> { + let (r_input, _) = tuple((opt(_p_sign), alt((_p_large_number, _p_small_number))))(input)?; + Ok((r_input, ())) +} + +/// Recognize a valid `[Ee][+-]?{digits}` exponent, return the unit value on success. +fn _p_exponent(input: &str) -> IResult<&str, ()> { + let (r_input, _) = tuple((one_of("Ee"), opt(_p_sign), many1(_p_digit)))(input)?; + Ok((r_input, ())) +} + +/// Recognize a valid number, return the unit value on success. +fn _p_valid_number(input: &str) -> IResult<&str, ()> { + let (r_input, _) = all_consuming(tuple((_p_mantissa, opt(_p_exponent))))(input)?; + Ok((r_input, ())) +} + +/// Recognize a valid number, return "true" or "false" as a string. +#[tracing::instrument] +fn valid_number(input: &str) -> Result<&'static str> { + debug!("Checking whether '{input}' is a valid number"); + if _p_valid_number(input).is_ok() { + Ok("true") + } else { + Ok("false") + } +} + +/// The main program - parse the environment settings, do something about it. +#[allow(clippy::print_stdout)] +fn main() -> Result<MainResult> { + if env::var("PWC_FROM_STDIN").unwrap_or_else(|_| String::new()) == "1" { + let mut line = String::new(); + io::stdin() + .read_line(&mut line) + .context("Could not read a line from the standard input stream")?; + println!( + "{res}", + res = valid_number(line.trim_end_matches(&['\r', '\n']))? + ); + } else { + for test_str in TEST_NUMBERS { + println!("{res}", res = valid_number(test_str)?); + } + } + Ok(MainResult::Ok) +} |
