Skip to content

Commit 28fbf14

Browse files
committed
2017 day 11
Also optimize parsing of literals and lists with separators which take the vast majority of the time for this solution
1 parent 559e05d commit 28fbf14

File tree

15 files changed

+252
-114
lines changed

15 files changed

+252
-114
lines changed

crates/utils/src/parser/base.rs

Lines changed: 41 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use crate::parser::combinator::{
33
Map, MapResult, Optional, Or, RepeatN, RepeatVec, WithPrefix, WithSuffix,
44
};
55
use crate::parser::error::WithErrorMsg;
6-
use crate::parser::simple::Eol;
7-
use crate::parser::then::Then2;
6+
use crate::parser::simple::{Constant, Eol};
7+
use crate::parser::then::{Then2, Unimplemented};
88
use crate::parser::ParseError;
99

1010
/// [`Result`] type returned by [`Parser::parse`].
@@ -182,42 +182,18 @@ pub trait Parser: Sized {
182182
/// If the number of items is constant and known in advance, prefer [`repeat_n`](Self::repeat_n)
183183
/// as it avoids allocating.
184184
///
185-
/// See also [`repeat_min`](Self::repeat_min), which ensures at least N items are parsed.
186-
///
187-
/// # Examples
188-
/// ```
189-
/// # use utils::parser::{self, Parser};
190-
/// assert_eq!(
191-
/// parser::u32()
192-
/// .with_suffix(",".optional())
193-
/// .repeat()
194-
/// .parse(b"12,34,56,78"),
195-
/// Ok((vec![12, 34, 56, 78], &b""[..]))
196-
/// );
197-
/// ```
198-
fn repeat(self) -> RepeatVec<Self> {
199-
RepeatVec {
200-
parser: self,
201-
min_elements: 0,
202-
}
203-
}
204-
205-
/// Repeat this parser at least N times, returning a [`Vec`].
206-
///
207-
/// See also [`repeat`](Self::repeat).
208-
///
209185
/// # Examples
210186
/// ```
211187
/// # use utils::parser::{self, Parser};
212188
/// let parser = parser::u32()
213-
/// .with_suffix(",".optional())
214-
/// .repeat_min(3);
189+
/// .repeat(",", 3);
215190
/// assert_eq!(parser.parse(b"12,34,56,78"), Ok((vec![12, 34, 56, 78], &b""[..])));
216191
/// assert!(parser.parse(b"12,34").is_err());
217192
/// ```
218-
fn repeat_min(self, min_elements: usize) -> RepeatVec<Self> {
193+
fn repeat<S: Parser>(self, separator: S, min_elements: usize) -> RepeatVec<Self, S> {
219194
RepeatVec {
220195
parser: self,
196+
separator,
221197
min_elements,
222198
}
223199
}
@@ -287,8 +263,25 @@ pub trait Parser: Sized {
287263
}
288264
}
289265

266+
/// Apply this parser once, checking the provided input is fully consumed.
267+
///
268+
/// # Examples
269+
/// ```
270+
/// # use utils::parser::{self, Parser};
271+
/// assert_eq!(parser::u32().parse_complete("1234").unwrap(), 1234);
272+
/// assert!(parser::u32().parse_complete("1234abc").is_err());
273+
/// ```
274+
fn parse_complete<'i>(&self, input: &'i str) -> Result<Self::Output<'i>, InputError> {
275+
match self.parse(input.as_bytes()).map_with_input(input)? {
276+
(v, []) => Ok(v),
277+
(_, remaining) => Err(InputError::new(input, remaining, "expected end of input")),
278+
}
279+
}
280+
290281
/// Apply this parser repeatedly until the provided input is fully consumed.
291282
///
283+
/// Equivalent to `parser.repeat(parser::noop(), 0).parse_complete(input)`.
284+
///
292285
/// # Examples
293286
/// ```
294287
/// # use utils::parser::{self, Parser};
@@ -306,20 +299,9 @@ pub trait Parser: Sized {
306299
/// );
307300
/// ```
308301
fn parse_all<'i>(&self, input: &'i str) -> Result<Vec<Self::Output<'i>>, InputError> {
309-
let mut results = Vec::new();
310-
let mut remaining = input.as_bytes();
311-
while !remaining.is_empty() {
312-
let (v, new_remaining) = self.parse(remaining).map_with_input(input)?;
313-
314-
if results.is_empty() {
315-
let length = remaining.len() - new_remaining.len();
316-
results.reserve(2 + ((remaining.len() / length) * 6 / 5));
317-
}
318-
319-
remaining = new_remaining;
320-
results.push(v);
321-
}
322-
Ok(results)
302+
ParserRef(self)
303+
.repeat(Constant(()), 0)
304+
.parse_complete(input)
323305
}
324306

325307
/// Similar to [`parse_all`](Self::parse_all) but expects a newline after each item.
@@ -342,44 +324,26 @@ pub trait Parser: Sized {
342324
/// );
343325
/// ```
344326
fn parse_lines<'i>(&self, input: &'i str) -> Result<Vec<Self::Output<'i>>, InputError> {
345-
// Can't use WithSuffix as it consumes the input parser
346-
struct LineParser<'a, P>(&'a P);
347-
impl<'a, P: Parser> Parser for LineParser<'a, P> {
348-
type Output<'i> = P::Output<'i>;
349-
type Then<T: Parser> = Then2<Self, T>;
327+
ParserRef(self)
328+
.with_suffix(Eol())
329+
.repeat(Constant(()), 0)
330+
.parse_complete(input)
331+
}
332+
}
350333

351-
#[inline]
352-
fn parse<'i>(&self, input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> {
353-
match self.0.parse(input) {
354-
Ok((v, remaining)) => match Eol().parse(remaining) {
355-
Ok(((), remaining)) => Ok((v, remaining)),
356-
Err(e) => Err(e),
357-
},
358-
Err(e) => Err(e),
359-
}
360-
}
334+
// Workaround to allow using methods which consume a parser in methods which take references.
335+
struct ParserRef<'a, P>(&'a P);
336+
impl<'a, P: Parser> Parser for ParserRef<'a, P> {
337+
type Output<'i> = P::Output<'i>;
338+
type Then<T: Parser> = Unimplemented;
361339

362-
fn then<T: Parser>(self, _: T) -> Self::Then<T> {
363-
unreachable!();
364-
}
365-
}
366-
367-
LineParser(self).parse_all(input)
340+
#[inline]
341+
fn parse<'i>(&self, input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> {
342+
self.0.parse(input)
368343
}
369344

370-
/// Apply this parser once, checking the provided input is fully consumed.
371-
///
372-
/// # Examples
373-
/// ```
374-
/// # use utils::parser::{self, Parser};
375-
/// assert_eq!(parser::u32().parse_complete("1234").unwrap(), 1234);
376-
/// assert!(parser::u32().parse_complete("1234abc").is_err());
377-
/// ```
378-
fn parse_complete<'i>(&self, input: &'i str) -> Result<Self::Output<'i>, InputError> {
379-
match self.parse(input.as_bytes()).map_with_input(input)? {
380-
(v, []) => Ok(v),
381-
(_, remaining) => Err(InputError::new(input, remaining, "expected end of input")),
382-
}
345+
fn then<T: Parser>(self, _: T) -> Self::Then<T> {
346+
unreachable!();
383347
}
384348
}
385349

crates/utils/src/parser/combinator.rs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::input::{InputError, MapWithInputExt};
12
use crate::parser::then::Then2;
23
use crate::parser::{ParseError, ParseResult, Parser};
34

@@ -102,31 +103,67 @@ impl<const N: usize, P: for<'i> Parser<Output<'i>: Copy + Default>> Parser for R
102103
}
103104

104105
#[derive(Copy, Clone)]
105-
pub struct RepeatVec<P> {
106+
pub struct RepeatVec<P, S> {
106107
pub(super) parser: P,
108+
pub(super) separator: S,
107109
pub(super) min_elements: usize,
108110
}
109-
impl<P: Parser> Parser for RepeatVec<P> {
110-
type Output<'i> = Vec<P::Output<'i>>;
111-
type Then<T: Parser> = Then2<Self, T>;
112-
111+
impl<P: Parser, S: Parser> RepeatVec<P, S> {
113112
#[inline]
114-
fn parse<'i>(&self, mut input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> {
113+
fn helper<'i>(
114+
&self,
115+
mut input: &'i [u8],
116+
consume_all: bool,
117+
) -> ParseResult<'i, Vec<P::Output<'i>>> {
115118
let mut output = Vec::new();
119+
116120
while let Ok((v, remaining)) = self.parser.parse(input) {
121+
let consumed = input.len() - remaining.len();
122+
assert!(consumed > 0, "parsing item consumed no input");
123+
124+
// When parsing the complete input, after parsing the first item use the proportion of
125+
// consumed bytes for one item to reserve capacity for the output vec
126+
if consume_all && output.is_empty() {
127+
output.reserve(((remaining.len() / consumed) * 7 / 5) + 2);
128+
}
129+
117130
output.push(v);
118-
input = remaining;
131+
132+
if let Ok((_, remaining)) = self.separator.parse(remaining) {
133+
input = remaining;
134+
} else {
135+
input = remaining;
136+
break;
137+
}
119138
}
139+
120140
if output.len() >= self.min_elements {
121141
Ok((output, input))
122142
} else {
123143
Err((ParseError::ExpectedMatches(self.min_elements), input))
124144
}
125145
}
146+
}
147+
impl<P: Parser, S: Parser> Parser for RepeatVec<P, S> {
148+
type Output<'i> = Vec<P::Output<'i>>;
149+
type Then<T: Parser> = Then2<Self, T>;
150+
151+
#[inline]
152+
fn parse<'i>(&self, input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> {
153+
self.helper(input, false)
154+
}
126155

127156
fn then<T: Parser>(self, next: T) -> Self::Then<T> {
128157
Then2::new(self, next)
129158
}
159+
160+
// Override the default implementation to set consume_all to true
161+
fn parse_complete<'i>(&self, input: &'i str) -> Result<Self::Output<'i>, InputError> {
162+
match self.helper(input.as_bytes(), true).map_with_input(input)? {
163+
(v, []) => Ok(v),
164+
(_, remaining) => Err(InputError::new(input, remaining, "expected end of input")),
165+
}
166+
}
130167
}
131168

132169
#[derive(Copy, Clone)]

crates/utils/src/parser/macros.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/// Parse one or more string literals, mapping the results.
2+
///
3+
/// This is a replacement for
4+
/// [`parser::one_of`](crate::parser::one_of())`(("a".map(|_| Enum::A), "b".map(|_| Enum::b)))`
5+
/// which produces more optimized assembly and is easier to read and write.
6+
///
7+
/// The string patterns are matched in the order provided, so strings should be ordered by length.
8+
///
9+
/// Using this makes [2017 day 11](../../year2017/struct.Day11.html), which parses a sequence of
10+
/// literals separated by commas, over 2x faster.
11+
///
12+
/// # Examples
13+
/// ```
14+
/// # use utils::parser::{Parser, self};
15+
///
16+
/// #[derive(Debug, PartialEq)]
17+
/// enum Example {
18+
/// A,
19+
/// B,
20+
/// C,
21+
/// }
22+
///
23+
/// let parser = parser::literal_map!(
24+
/// "A" | "a" => Example::A,
25+
/// "B" => Example::B,
26+
/// "C" => Example::C,
27+
/// );
28+
/// assert_eq!(parser.parse(b"A"), Ok((Example::A, &b""[..])));
29+
/// assert_eq!(parser.parse(b"a"), Ok((Example::A, &b""[..])));
30+
/// assert_eq!(parser.parse(b"B"), Ok((Example::B, &b""[..])));
31+
/// assert_eq!(parser.parse(b"C"), Ok((Example::C, &b""[..])));
32+
/// assert!(parser.parse(b"D").is_err());
33+
/// ```
34+
#[macro_export]
35+
macro_rules! parser_literal_map {
36+
(
37+
$($($l:literal)|+ => $e:expr),+$(,)?
38+
) => {{
39+
fn coerce_to_parser<F: Fn(&[u8]) -> $crate::parser::ParseResult<'_, O>, O>(f: F) -> F { f }
40+
41+
coerce_to_parser(|input| {
42+
$($(
43+
if input.len() >= const { $l.len() } && const { $l.as_bytes() } == &input[..const { $l.len() }] {
44+
return Ok((($e), &input[const { $l.len() }..]));
45+
}
46+
)+)*
47+
48+
Err(($crate::parser_literal_map!(@error $($($l)+)+), input))
49+
})
50+
}};
51+
(@error $first:literal $($l:literal)+) => {
52+
$crate::parser::ParseError::Custom(concat!("expected one of '", $first, "'", $(", '", $l, "'",)+))
53+
};
54+
(@error $first:literal) => {
55+
$crate::parser::ParseError::ExpectedLiteral($first)
56+
};
57+
}

crates/utils/src/parser/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod base;
44
mod combinator;
55
mod error;
6+
mod macros;
67
mod number;
78
mod one_of;
89
mod simple;
@@ -12,4 +13,6 @@ pub use base::*;
1213
pub use error::ParseError;
1314
pub use number::{i128, i16, i32, i64, i8, number_range, u128, u16, u32, u64, u8};
1415
pub use one_of::one_of;
15-
pub use simple::{byte, byte_range, constant, eol, take_while, take_while1};
16+
pub use simple::{byte, byte_range, constant, eol, noop, take_while, take_while1};
17+
18+
pub use crate::parser_literal_map as literal_map;

crates/utils/src/parser/one_of.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ impl<L: ParserList> Parser for OneOf<L> {
7070
/// Similar to [`Parser::or`], each parser will be tried in order until one succeeds. If no parsers
7171
/// succeed, the error from the parser furthest into the input is returned.
7272
///
73+
/// Prefer [`parser::literal_map`](super::literal_map) if all the parsers are string literals.
74+
///
7375
/// # Examples
7476
/// ```
7577
/// # use utils::input::InputError;

crates/utils/src/parser/simple.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub fn byte_range(range: RangeInclusive<u8>) -> ByteRange {
9191
}
9292

9393
#[derive(Copy, Clone)]
94-
pub struct Constant<V: Copy>(V);
94+
pub struct Constant<V: Copy>(pub(super) V);
9595
impl<V: Copy> Parser for Constant<V> {
9696
type Output<'i> = V;
9797
type Then<T: Parser> = Then2<Self, T>;
@@ -121,6 +121,24 @@ pub fn constant<T: Copy>(v: T) -> Constant<T> {
121121
Constant(v)
122122
}
123123

124+
/// Parser that consumes no input and always succeeds, returning [`()`](unit).
125+
///
126+
/// # Examples
127+
/// ```
128+
/// # use utils::parser::{self, Parser};
129+
/// assert_eq!(
130+
/// parser::noop().parse(b"abc"),
131+
/// Ok(((), &b"abc"[..]))
132+
/// );
133+
/// ```
134+
#[must_use]
135+
pub fn noop() -> Constant<()> {
136+
const {
137+
assert!(size_of::<Constant<()>>() == 0);
138+
}
139+
Constant(())
140+
}
141+
124142
#[derive(Copy, Clone)]
125143
pub struct Eol();
126144
impl Parser for Eol {

crates/year2015/src/day06.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ enum Action {
3535

3636
impl Day06 {
3737
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
38-
let mut instructions = parser::one_of((
39-
"turn off ".map(|_| Action::TurnOff),
40-
"turn on ".map(|_| Action::TurnOn),
41-
"toggle ".map(|_| Action::Toggle),
42-
))
38+
let mut instructions = parser::literal_map!(
39+
"turn off " => Action::TurnOff,
40+
"turn on " => Action::TurnOn,
41+
"toggle " => Action::Toggle,
42+
)
4343
.then(parser::u16())
4444
.then(parser::u16().with_prefix(b','))
4545
.then(parser::u16().with_prefix(" through "))

crates/year2015/src/day23.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ enum Instruction {
2424

2525
impl Day23 {
2626
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
27-
let register = b'a'.map(|_| Register::A).or(b'b'.map(|_| Register::B));
27+
let register = parser::literal_map!("a" => Register::A, "b" => Register::B);
2828

2929
Ok(Self {
3030
instructions: parser::one_of((

0 commit comments

Comments
 (0)