switch to nom

This commit is contained in:
Rüdiger Ludwig 2023-07-30 16:42:17 +02:00
parent a5f19ecae1
commit b00835c25e
7 changed files with 288 additions and 181 deletions

View file

@ -1,37 +1,44 @@
use super::template::{DayTrait, ResultType};
use crate::common::{file::split_lines, pos::Pos};
use crate::common::{
parser::{eol_terminated, extract_result, ignore, trim0},
pos::Pos,
};
use itertools::Itertools;
use once_cell::sync::Lazy;
use regex::Regex;
use std::{collections::HashSet, num::ParseIntError};
use nom::{
bytes::complete::tag,
character::complete::{char, i64},
error::Error,
multi::many0,
sequence::{separated_pair, tuple},
Err, IResult,
};
use std::collections::HashSet;
use thiserror::Error;
const DAY_NUMBER: usize = 15;
pub struct Day;
fn parse_row(input: &str) -> IResult<&str, i64> {
eol_terminated(i64)(input)
}
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let lines = split_lines(lines).collect_vec();
if lines.len() < 2 {
Err(SensorError::ToFewLines)?;
}
let row = lines[0].parse()?;
let (sensors, beacons) = Day::parse_all(&lines[1..])?;
let (lines, row) = parse_row(lines).map_err(|_| SensorError::RowLineNotFound)?;
let (sensors, beacons) = Day::parse_all(lines)?;
let result = Day::count_coverage_at(&sensors, &beacons, row);
Ok(ResultType::Integer(result))
}
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let lines = split_lines(lines).collect_vec();
if lines.len() < 2 {
Err(SensorError::ToFewLines)?;
}
let (sensors, _) = Day::parse_all(&lines[1..])?;
let (lines, _) = parse_row(lines).map_err(|_| SensorError::RowLineNotFound)?;
let (sensors, _) = Day::parse_all(lines)?;
let mid_lines = Day::dividing_lines(&sensors);
let points = mid_lines
.iter()
@ -51,11 +58,12 @@ impl DayTrait for Day {
}
impl Day {
fn parse_all(lines: &[&str]) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> {
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> {
let data = extract_result(many0(eol_terminated(Sensor::parse)))(lines)?;
let mut sensors = HashSet::new();
let mut beacons = HashSet::new();
for line in lines {
let (sensor, beacon) = Sensor::parse(line)?;
for (sensor, beacon) in data {
sensors.insert(sensor);
beacons.insert(beacon);
}
@ -91,17 +99,20 @@ impl Day {
#[derive(Debug, Error)]
enum SensorError {
#[error("Not an Integer")]
NotAnInt(#[from] ParseIntError),
#[error("Unknown line: {0}")]
UnknownLine(String),
#[error("Error Parsing Input: {0}")]
ParsingError(String),
#[error("Did not find exactly one point for the sensor: {0}")]
NotExactlyOneResult(usize),
#[error("Too few lines in input.txt")]
ToFewLines,
#[error("Row line not found at start of Input")]
RowLineNotFound,
}
impl From<Err<Error<&str>>> for SensorError {
fn from(error: Err<Error<&str>>) -> Self {
SensorError::ParsingError(error.to_string())
}
}
#[derive(Debug)]
@ -220,28 +231,30 @@ struct Sensor {
radius: i64,
}
static SENSOR: Lazy<Regex> =
Lazy::new(|| Regex::new(r"x=(-?\d+), y=(-?\d+).*x=(-?\d+), y=(-?\d+)").unwrap());
impl Sensor {
pub fn parse(line: &str) -> Result<(Sensor, Pos<i64>), SensorError> {
let caps = SENSOR
.captures(line)
.ok_or(SensorError::UnknownLine(line.to_owned()))?;
let sensor_x = caps.get(1).unwrap().as_str().parse()?;
let sensor_y = caps.get(2).unwrap().as_str().parse()?;
let beacon_x = caps.get(3).unwrap().as_str().parse()?;
let beacon_y = caps.get(4).unwrap().as_str().parse()?;
let sensor = Pos::new(sensor_x, sensor_y);
let beacon = Pos::new(beacon_x, beacon_y);
let radius = sensor.taxicab(&beacon);
Ok((
Sensor {
pos: sensor,
radius,
},
beacon,
))
fn component<'a>(name: &'a str) -> impl FnMut(&'a str) -> IResult<&'a str, i64> {
move |input: &str| {
let input = ignore(tuple((tag(name), char('='))))(input)?;
trim0(i64)(input)
}
}
fn parse_pos(input: &str) -> IResult<&str, Pos<i64>> {
let (input, (x, y)) = separated_pair(
Sensor::component("x"),
trim0(char(',')),
Sensor::component("y"),
)(input)?;
Ok((input, Pos::new(x, y)))
}
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos<i64>)> {
let input = ignore(tag("Sensor at"))(input)?;
let (input, pos) = trim0(Sensor::parse_pos)(input)?;
let input = ignore(tag(": closest beacon is at"))(input)?;
let (input, beacon) = trim0(Sensor::parse_pos)(input)?;
let radius = pos.taxicab(&beacon);
Ok((input, (Sensor { pos, radius }, beacon)))
}
pub fn range_at(&self, y: i64) -> Option<Range> {
@ -279,7 +292,7 @@ mod test {
},
Pos::new(-2, 15),
);
let result = Sensor::parse(input)?;
let result = extract_result(Sensor::parse)(input)?;
assert_eq!(result, expected);
Ok(())