Use Nom for Rust parsing

This commit is contained in:
George Thomas 2025-12-10 12:39:12 +00:00
parent fa6f734286
commit 415ed20dd5
4 changed files with 77 additions and 29 deletions

18
rust/Cargo.lock generated
View File

@ -5,3 +5,21 @@ version = 4
[[package]] [[package]]
name = "aoc" name = "aoc"
version = "0.1.0" version = "0.1.0"
dependencies = [
"nom",
]
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]

View File

@ -8,3 +8,4 @@ path = "main.rs"
name = "aoc" name = "aoc"
[dependencies] [dependencies]
nom = "8.0.0"

View File

@ -1,21 +1,36 @@
use crate::puzzle::Puzzle; use crate::puzzle::Puzzle;
use nom::{
Parser,
branch::alt,
character::complete::{char, digit1, newline},
combinator::{eof, map_res, value},
error::Error,
multi::separated_list1,
sequence::{pair, terminated},
};
pub const PUZZLE: Puzzle<Vec<(Direction, i32)>, i32, 2> = Puzzle { pub const PUZZLE: Puzzle<Vec<(Direction, i32)>, i32, 2> = Puzzle {
number: 1, number: 1,
parser: |input| { parser: |input| {
input terminated(
.lines() terminated(
.filter(|line| !line.is_empty()) separated_list1(
.map(|line| { newline,
let d = match line.chars().next().unwrap() { pair(
'L' => Direction::L, alt((
'R' => Direction::R, value(Direction::L, char('L')),
c => panic!("Unknown direction: {}", c), value(Direction::R, char('R')),
}; )),
let i: i32 = line[1..].parse().unwrap(); parse_int(),
(d, i) ),
}) ),
.collect() newline,
),
eof,
)
.parse(input)
.unwrap()
.1
}, },
parts: [ parts: [
|instructions| { |instructions| {
@ -68,3 +83,7 @@ fn step(i: i32, d: Direction, p: i32) -> (i32, i32) {
}; };
(p1.div_euclid(100), p1.rem_euclid(100)) (p1.div_euclid(100), p1.rem_euclid(100))
} }
fn parse_int<'a>() -> impl Parser<&'a str, Output = i32, Error = Error<&'a str>> {
map_res(digit1, |s: &str| s.parse::<i32>())
}

View File

@ -1,23 +1,29 @@
use crate::puzzle::Puzzle; use crate::puzzle::Puzzle;
use nom::{
Parser,
character::complete::{char, digit1, newline},
combinator::{eof, map_res},
error::Error,
multi::separated_list1,
sequence::{separated_pair, terminated},
};
pub const PUZZLE: Puzzle<Vec<(usize, usize)>, usize, 2> = Puzzle { pub const PUZZLE: Puzzle<Vec<(usize, usize)>, usize, 2> = Puzzle {
number: 2, number: 2,
parser: |input| { parser: |input| -> Vec<(usize, usize)> {
input terminated(
.strip_suffix("\n") terminated(
separated_list1(
char(','),
separated_pair(parse_int(), char('-'), parse_int()),
),
newline,
),
eof,
)
.parse(input)
.unwrap() .unwrap()
.split(',') .1
.map(|range| {
match range
.split('-')
.map(|n| n.parse().unwrap())
.collect::<Vec<usize>>()[..]
{
[a, b] => (a, b),
_ => panic!(),
}
})
.collect()
}, },
parts: [ parts: [
|input| { |input| {
@ -62,3 +68,7 @@ fn equal_chunks(n: &String, i: usize) -> bool {
Some(x) => chunks.all(|y| y == x), Some(x) => chunks.all(|y| y == x),
} }
} }
fn parse_int<'a>() -> impl Parser<&'a str, Output = usize, Error = Error<&'a str>> {
map_res(digit1, |s: &str| s.parse::<usize>())
}