switch to nom
This commit is contained in:
parent
a5f19ecae1
commit
b00835c25e
7 changed files with 288 additions and 181 deletions
|
|
@ -1,17 +1,20 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::parser::{eol_terminated, extract_result, ignore, trim0, trim1, trim_left1};
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete::{alpha1, i64},
|
||||
error::Error,
|
||||
multi::{many1, separated_list1},
|
||||
Err, IResult,
|
||||
};
|
||||
use std::collections::BinaryHeap;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::hash::Hash;
|
||||
use std::iter::Sum;
|
||||
use std::ops::{Add, Deref, Mul, Sub};
|
||||
use std::{collections::BinaryHeap, num::ParseIntError};
|
||||
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use const_format::concatcp;
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 16;
|
||||
|
|
@ -129,11 +132,8 @@ impl Deref for Index {
|
|||
|
||||
#[derive(Debug, Error)]
|
||||
enum ValveError {
|
||||
#[error("Not an Integer")]
|
||||
NotAnInt(#[from] ParseIntError),
|
||||
|
||||
#[error("Not a valid description: {0}")]
|
||||
CouldNotParse(String),
|
||||
ParsingError(String),
|
||||
|
||||
#[error("Not a valid valve: {0}")]
|
||||
ValveNotFound(String),
|
||||
|
|
@ -142,17 +142,11 @@ enum ValveError {
|
|||
NoOptimumFound,
|
||||
}
|
||||
|
||||
const ID: &str = "[[:alpha:]]+";
|
||||
const COMMON: &str = concatcp!("^Valve (?<id>", ID, r") has flow rate=(?<rate>\d+); ");
|
||||
const PLURAL_STR: &str = concatcp!(
|
||||
COMMON,
|
||||
"tunnels lead to valves (?<exits>",
|
||||
ID,
|
||||
"(?:, ",
|
||||
ID,
|
||||
")+)$"
|
||||
);
|
||||
const SINGULAR_STR: &str = concatcp!(COMMON, "tunnel leads to valve (?<exits>", ID, ")$");
|
||||
impl From<Err<Error<&str>>> for ValveError {
|
||||
fn from(error: Err<Error<&str>>) -> Self {
|
||||
ValveError::ParsingError(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct RawValve<'a> {
|
||||
|
|
@ -170,29 +164,45 @@ impl<'a> RawValve<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_regex(regex: &Regex, line: &'a str) -> Option<Result<RawValve<'a>, ValveError>> {
|
||||
regex.captures(line).map(|caps| {
|
||||
match (caps.name("id"), caps.name("rate"), caps.name("exits")) {
|
||||
(Some(id), Some(rate), Some(exits)) => {
|
||||
let rate = rate.as_str().parse()?;
|
||||
let neighbors = exits.as_str().split(",").map(|s| s.trim_start()).collect();
|
||||
Ok(RawValve::create(id.as_str(), Flow(rate), neighbors))
|
||||
}
|
||||
_ => Err(ValveError::CouldNotParse(line.to_string())),
|
||||
}
|
||||
})
|
||||
fn parse_start(input: &str) -> IResult<&str, (&str, Flow)> {
|
||||
let input = ignore(tag("Valve"))(input)?;
|
||||
let (input, valve) = trim1(alpha1)(input)?;
|
||||
let input = ignore(tag("has flow rate="))(input)?;
|
||||
let (input, flow) = trim0(i64)(input)?;
|
||||
let input = ignore(tag(";"))(input)?;
|
||||
Ok((input, (valve, Flow(flow))))
|
||||
}
|
||||
|
||||
fn parse_singular(input: &str) -> IResult<&str, Vec<&str>> {
|
||||
let input = ignore(tag("tunnel leads to valve "))(input)?;
|
||||
let (input, valve) = alpha1(input)?;
|
||||
|
||||
Ok((input, vec![valve]))
|
||||
}
|
||||
|
||||
fn parse_plural(input: &str) -> IResult<&str, Vec<&str>> {
|
||||
let input = ignore(tag("tunnels lead to valves "))(input)?;
|
||||
let (input, valve) = separated_list1(tag(","), trim0(alpha1))(input)?;
|
||||
|
||||
Ok((input, valve))
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> IResult<&str, RawValve> {
|
||||
let (input, (id, flow)) = RawValve::parse_start(input)?;
|
||||
let (input, neighbors) = trim_left1(eol_terminated(alt((
|
||||
RawValve::parse_plural,
|
||||
RawValve::parse_singular,
|
||||
))))(input)?;
|
||||
|
||||
Ok((input, RawValve::create(id, flow, neighbors)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for RawValve<'a> {
|
||||
type Error = ValveError;
|
||||
|
||||
fn try_from(value: &'a str) -> Result<RawValve<'a>, Self::Error> {
|
||||
static PLURAL: Lazy<Regex> = Lazy::new(|| Regex::new(PLURAL_STR).unwrap());
|
||||
static SINGULAR: Lazy<Regex> = Lazy::new(|| Regex::new(SINGULAR_STR).unwrap());
|
||||
RawValve::from_regex(&PLURAL, value)
|
||||
.or_else(|| RawValve::from_regex(&SINGULAR, value))
|
||||
.unwrap_or_else(|| Err(ValveError::CouldNotParse(value.to_string())))
|
||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||
Ok(extract_result(RawValve::parse)(value)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -267,9 +277,7 @@ struct ValveSystem {
|
|||
|
||||
impl ValveSystem {
|
||||
fn build(lines: &str, start: &str) -> Result<ValveSystem, ValveError> {
|
||||
let mut raw = split_lines(lines)
|
||||
.map(|line| RawValve::try_from(line))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mut raw = extract_result(many1(RawValve::parse))(lines)?;
|
||||
raw.sort_unstable_by_key(|valve| valve.id);
|
||||
|
||||
let start_idx = raw
|
||||
|
|
@ -931,7 +939,7 @@ mod test {
|
|||
fn parse_plural() -> Result<()> {
|
||||
let line = "Valve BB has flow rate=13; tunnels lead to valves CC, AA";
|
||||
let expected = RawValve::create("BB", Flow(13), vec!["CC", "AA"]);
|
||||
let result = RawValve::try_from(line)?;
|
||||
let result = line.try_into()?;
|
||||
assert_eq!(expected, result);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -941,7 +949,7 @@ mod test {
|
|||
fn parse_singular() -> Result<()> {
|
||||
let line = "Valve HH has flow rate=22; tunnel leads to valve GG";
|
||||
let expected = RawValve::create("HH", Flow(22), vec!["GG"]);
|
||||
let result = RawValve::try_from(line)?;
|
||||
let result = line.try_into()?;
|
||||
assert_eq!(expected, result);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue